pkg/exporter/configmap.go (98 lines of code) (raw):
/*
MIT License
Copyright (c) Microsoft Corporation.
*/
package exporter
import (
"context"
"errors"
"fmt"
"sort"
"strconv"
"time"
"github.com/Azure/kubernetes-carbon-intensity-exporter/pkg/sdk/client"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/klog/v2"
)
var (
isImmutable = true
)
func (e *Exporter) CreateConfigMapFromEmissionForecast(ctx context.Context, configMapName string, emissionForecast []client.EmissionsForecastDto) error {
if emissionForecast == nil {
return errors.New("emission forecast cannot be nil")
}
forecast := emissionForecast[0]
binaryData, err := json.Marshal(forecast.ForecastData)
if err != nil {
return err
}
if forecast.ForecastData == nil || len(forecast.ForecastData) == 0 {
return errors.New("forecast data cannot be nil or empty")
}
minForecast, maxForeCast := getMinMaxForecast(ctx, forecast.ForecastData)
return e.CreateConfigMapFromProperties(ctx, configMapName,
map[string]string{
ConfigMapLastHeartbeatTime: time.Now().String(), // The latest time that the data exporter controller sends the data.
ConfigMapMessage: "", // Additional information for user notification, if any.
ConfigMapNumOfRecords: strconv.Itoa(len(forecast.ForecastData)), // The number can be any value between 0 (no records for the current location) and 24(hours) * 12(5 min interval per hour).
ConfigMapForecastDateTime: forecast.DataStartAt.String(), // The time when the data was started by the GSF SDK.
ConfigMapMinForecast: fmt.Sprintf("%f", minForecast), // min forecast in the forecastData.
ConfigMapMaxForecast: fmt.Sprintf("%f", maxForeCast), // max forecast in the forecastData.
}, binaryData)
}
func (e *Exporter) CreateConfigMapFromProperties(ctx context.Context, configMapName string, data map[string]string, binaryData []byte) error {
configMap := &corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{
Name: configMapName,
Namespace: client.Namespace,
},
Immutable: &isImmutable,
Data: data,
BinaryData: map[string][]byte{
BinaryData: binaryData, // json marshal of the EmissionsData array.
},
}
_, err := e.clusterClient.CoreV1().
ConfigMaps(client.Namespace).
Create(ctx, configMap, v1.CreateOptions{})
if err != nil {
return err
}
klog.Infof("configMap %s has been created", configMapName)
return nil
}
func (e *Exporter) DeleteConfigMap(ctx context.Context, configMapName string) error {
currentConfigMap, err := e.GetConfigMap(ctx, configMapName)
if err != nil {
return err
}
if currentConfigMap == nil {
return nil // configMap is not found, delete will not be called.
}
err = e.clusterClient.CoreV1().
ConfigMaps(client.Namespace).
Delete(ctx, configMapName, v1.DeleteOptions{})
if err != nil {
klog.Errorf("unable to delete configMap %s", configMapName)
return err
}
klog.Infof("configMap %s has been deleted", configMapName)
return nil
}
func (e *Exporter) GetConfigMap(ctx context.Context, configMapName string) (*corev1.ConfigMap, error) {
currentConfigMap, err := e.clusterClient.CoreV1().ConfigMaps(client.Namespace).Get(ctx, configMapName, v1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) { // if configMap is not found, no errors will be returned.
return nil, nil
}
return nil, err
}
return currentConfigMap, nil
}
func getMinMaxForecast(ctx context.Context, forecastData []client.EmissionsDataDto) (float64, float64) {
values := make([]float64, len(forecastData))
for index := range forecastData {
values[index] = forecastData[index].Value
}
// Sort values
sort.Float64s(values)
return values[0], values[len(values)-1]
}