Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement get emissions data and carbon intensity #4

Merged
merged 2 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/create-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: write
packages: write
Expand Down
64 changes: 64 additions & 0 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Run e2e test
on:
push:
branches:
- main
pull_request:
branches:
- main

env:
REGISTRY: ghcr.io
# Common versions
GO_VERSION: '1.19'

SERVER_IMG_TAG : '0.1.0'
EXPORTER_IMG_TAG: '0.1.0'

jobs:
export-registry:
runs-on: ubuntu-20.04
outputs:
registry: ${{ steps.export.outputs.registry }}
steps:
- id: export
run: |
# registry must be in lowercase
echo "registry=$(echo "${{ env.REGISTRY }}/${{ github.repository }}" | tr [:upper:] [:lower:])" >> $GITHUB_OUTPUT

e2e-test:
runs-on: ubuntu-20.04
needs:
- export-registry
env:
REGISTRY: ${{ needs.export-registry.outputs.registry }}
steps:
- uses: actions/checkout@v3
with:
submodules: true
fetch-depth: 0
- name: Login to ${{ env.REGISTRY }}
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: install kubectl
uses: azure/setup-kubectl@v3
- name: Create k8s Kind Cluster
uses: helm/kind-action@v1.4.0
with:
cluster_name: "carbon"
- name: Run e2e test
run: |
make docker-build-server-image docker-build-exporter-image
kind load docker-image -n carbon ${{ env.REGISTRY }}/server:${{ env.SERVER_IMG_TAG }}
kind load docker-image -n carbon ${{ env.REGISTRY }}/exporter:${{ env.EXPORTER_IMG_TAG }}
kubectl apply -f deploy/carbon-aware.yaml
sleep 20
kubectl wait --for=condition=available deploy carbon-aware --timeout=300s
sleep 20
kubectl logs $(echo $(kubectl get pods --output=jsonpath={.items..metadata.name})) -c carbon-data-exporter
env:
SERVER_IMG_TAG: ${{ env.SERVER_IMG_TAG }}
EXPORTER_IMG_TAG: ${{ env.EXPORTER_IMG_TAG }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ _testmain.go
*.travis.yml
*git_push.sh
_output

.idea
4 changes: 4 additions & 0 deletions cmd/exporter/app/config/config.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
MIT License
Copyright (c) Microsoft Corporation.
*/
package config

import (
Expand Down
4 changes: 4 additions & 0 deletions cmd/exporter/app/options/options.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
MIT License
Copyright (c) Microsoft Corporation.
*/
package options

import (
Expand Down
9 changes: 8 additions & 1 deletion cmd/exporter/app/server.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
MIT License
Copyright (c) Microsoft Corporation.
*/
package app

import (
Expand All @@ -7,6 +11,7 @@ import (
_ "net/http/pprof" // enable pprof in the server
"os"

"github.com/Azure/kubernetes-carbon-intensity-exporter/pkg/sdk/client"
"github.com/spf13/cobra"
"k8s.io/apiserver/pkg/server/healthz"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
Expand Down Expand Up @@ -72,7 +77,9 @@ func NewExporterCommand(stopChan <-chan struct{}) *cobra.Command {
}

func Run(cc *exporterconfig.CompletedConfig, stopCh <-chan struct{}) error {
p, err := exporter.New(cc.ClusterClient, cc.Recorder)

apiClient := client.NewAPIClient(client.NewConfiguration())
p, err := exporter.New(cc.ClusterClient, apiClient, cc.Recorder)

if err != nil {
return fmt.Errorf("new syncer: %v", err)
Expand Down
6 changes: 6 additions & 0 deletions cmd/exporter/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
MIT License
Copyright (c) Microsoft Corporation.
*/
package main

import (
Expand All @@ -23,6 +27,8 @@ func mainMethod() error {
}

func main() {

time.Sleep(10 * time.Second)
if mainMethod() != nil {
os.Exit(1)
}
Expand Down
62 changes: 62 additions & 0 deletions deploy/carbon-aware.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: carbon-aware-sa
---
apiVersion: "rbac.authorization.k8s.io/v1"
kind: ClusterRoleBinding
metadata:
name: carbon-aware-binding
subjects:
- kind: ServiceAccount
name: carbon-aware-sa
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: carbon-aware
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: carbon-aware
template:
metadata:
labels:
app: carbon-aware
spec:
serviceAccountName: carbon-aware-sa
containers:
- name: api-server
image: ghcr.io/azure/kubernetes-carbon-intensity-exporter/server:0.1.0
imagePullPolicy: IfNotPresent
command:
- sh
- -c
- cd /app && sed "s/username/wibuchan/" -i appsettings.json && sed "s/password/greenAI/" -i appsettings.json && dotnet CarbonAware.WebApi.dll
ports:
- name: api-server-port
containerPort: 7031
- name: carbon-data-exporter
image: ghcr.io/azure/kubernetes-carbon-intensity-exporter/exporter:0.1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9090
---
apiVersion: v1
kind: Service
metadata:
name: api-server-svc
spec:
selector:
app: carbon-aware
ports:
- protocol: TCP
port: 80
targetPort: api-server-port
11 changes: 0 additions & 11 deletions deploy/exporter.yaml

This file was deleted.

16 changes: 0 additions & 16 deletions deploy/server.yaml

This file was deleted.

7 changes: 6 additions & 1 deletion docker/server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app

# Copy everything from source
RUN git clone https://github.com/helayoty/carbon-aware-sdk.git && cd carbon-aware-sdk && git checkout 159f99c
RUN git clone https://github.com/helayoty/carbon-aware-sdk.git && cd carbon-aware-sdk && git checkout 70568de
# Use implicit restore to build and publish
RUN dotnet publish carbon-aware-sdk/src/CarbonAware.WebApi/src/CarbonAware.WebApi.csproj -c Release -o publish
# Generate OpenAPI spec
WORKDIR carbon-aware-sdk/src/CarbonAware.WebApi/src
RUN dotnet tool restore && \
mkdir -p /app/publish/wwwroot/api/v1 && \
dotnet tool run swagger tofile --output /app/publish/wwwroot/api/v1/swagger.yaml --yaml /app/publish/CarbonAware.WebApi.dll v1

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
Expand Down
89 changes: 76 additions & 13 deletions pkg/exporter/exporter.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
/*
MIT License
Copyright (c) Microsoft Corporation.
*/
package exporter

import (
"time"

"github.com/Azure/kubernetes-carbon-intensity-exporter/pkg/sdk/client"
"github.com/antihax/optional"
"golang.org/x/net/context"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record"
"k8s.io/klog/v2"
)

const (
Expand All @@ -16,32 +24,87 @@ const (

type Exporter struct {
clusterClient clientset.Interface

recorder record.EventRecorder
apiClient *client.APIClient
recorder record.EventRecorder
}

func New(clusterClient clientset.Interface, recorder record.EventRecorder) (*Exporter, error) {
func New(clusterClient clientset.Interface, apiClient *client.APIClient, recorder record.EventRecorder) (*Exporter, error) {
b := &Exporter{
clusterClient: clusterClient,
apiClient: apiClient,
recorder: recorder,
}
return b, nil
}

func (b *Exporter) Run(stopChan <-chan struct{}) {
go wait.Until(b.Patrol, patrolInterval, stopChan)
func (e *Exporter) Run(stopChan <-chan struct{}) {
go wait.Until(e.Patrol, patrolInterval, stopChan)
}

func (b *Exporter) Patrol() {

/*
Calling SDK to get 24 hours foreceast data,
*/

b.recorder.Eventf(&corev1.ObjectReference{
func (e *Exporter) Patrol() {
ctx := context.Background()
//e.getEmissionData(ctx, "eastus")
e.getCarbonIntensity(ctx, "eastus")
e.getCurrentForecastData(ctx, []string{"eastus"})
e.recorder.Eventf(&corev1.ObjectReference{
Kind: "Pod",
Namespace: "kube-system",
Namespace: "",
Name: "carbon-data-exporter", // TODO: replace this with the actual Pod name, passed through the downward API.
}, corev1.EventTypeNormal, "Exporter results", "Done retrieve data")

}

func (e *Exporter) getEmissionData(ctx context.Context, region string) {
opt := &client.CarbonAwareApiGetEmissionsDataForLocationByTimeOpts{
StartTime: optional.NewTime(time.Now().AddDate(0, 0, -1)),
EndTime: optional.NewTime(time.Now()),
}
emissionsData, _, err := e.apiClient.CarbonAwareApi.GetEmissionsDataForLocationByTime(ctx,
region, opt)
if err != nil {
klog.ErrorS(err, "error while getting emissions data")
return
}

klog.Infof("emissionsData for %s region is: \n", region)
for i := range emissionsData {
index := i
helayoty marked this conversation as resolved.
Show resolved Hide resolved
index++
klog.Infof("%d. Location: %s {Time: %s, Duration: %s, Rating: %f}\n",
index, emissionsData[i].Location, emissionsData[i].Time.String(), emissionsData[i].Duration, emissionsData[i].Rating)
}
}

func (e *Exporter) getCarbonIntensity(ctx context.Context, region string) {
intensity, _, err := e.apiClient.CarbonAwareApi.GetAverageCarbonIntensity(ctx,
region,
time.Now().AddDate(0, 0, -1),
time.Now())
if err != nil {
klog.ErrorS(err, "error while getting carbon intensity")
return
}

klog.Infof("carbon intensity for %s region is %f", region, intensity.CarbonIntensity)
}

func (e *Exporter) getCurrentForecastData(ctx context.Context, region []string) {
opt := &client.CarbonAwareApiGetCurrentForecastDataOpts{
DataStartAt: optional.EmptyTime(),
DataEndAt: optional.EmptyTime(),
}
forecast, _, err := e.apiClient.CarbonAwareApi.GetCurrentForecastData(ctx,
region, opt)
if err != nil {
klog.ErrorS(err, "error while getting current forecast data")
return
}

klog.Infof("current forecast data for %s region is: \n", region)
for i := range forecast {
index := i
index++
klog.Infof("%d. Location: %s {DataStartAt: %s, DataEndAt: %s, ForecastData: %v, OptimalDataPoints: %v}\n",
index, forecast[i].Location, forecast[i].DataStartAt.String(), forecast[i].DataEndAt.String(), forecast[i].ForecastData, forecast[i].OptimalDataPoints)
}
}
5 changes: 2 additions & 3 deletions pkg/sdk/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ info:
title: "CarbonAware.WebApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
version: "1.0"
servers:
- url: https://virtserver.swaggerhub.com/Microsoft-hela/carbonaware/1.0.0
description: SwaggerHub API Auto Mocking
- url: https://carbonaware.swagger.io/v2
- url: http://api-server-svc.default.svc.cluster.local
description: API Server URL
paths:
/emissions/bylocations/best:
get:
Expand Down