Skip to content

Commit

Permalink
Merge branch 'dev' into 219/CarbonAwareNullable
Browse files Browse the repository at this point in the history
  • Loading branch information
vaughanknight authored Jan 10, 2023
2 parents 0ad3b4b + 23d2e63 commit c22c748
Show file tree
Hide file tree
Showing 61 changed files with 1,223 additions and 316 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/verify-azure-function-with-packages.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Verify Sample Azure Functions with GSF Packages

on:
push:
branches: [ dev, main ]
pull_request:
branches: [ dev, main ]
paths:
- 'src/**'
- '.github/workflows/**'
- 'samples/azure-function/**'

env:
DOCKERFILE_PATH: samples/azure-function/Dockerfile
CONTAINER_IMAGE_NAME: runnable-container
CONTAINER_RUNTIME_NAME: az-func

jobs:
container_azure_function:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Docker RM container name
continue-on-error: true
run: docker rm -f ${{ env.CONTAINER_RUNTIME_NAME }}

- name: Docker Target Final
run: |
docker build --no-cache . -f ${{ env.DOCKERFILE_PATH }} -t ${{ env.CONTAINER_IMAGE_NAME }}
docker rmi -f $(docker images -f "dangling=true" -q)
- name: Docker Run Container
run: docker run -d --name ${{ env.CONTAINER_RUNTIME_NAME }} -p 8080:80 ${{ env.CONTAINER_IMAGE_NAME }}

# Request fails with authentication error. Expected
- name: Get Average Carbon Intensity
run: |
set +e
ret_code=$(wget -S -t 5 --waitretry=5 "http://0.0.0.0:8080/api/GetAverageCarbonIntensity?startDate=2022-03-01T15:30:00Z&endDate=2022-03-01T18:30:00Z&location=eastus" 2>&1 | grep "HTTP/" | awk '{print $2}')
set -e
[ "401" == $ret_code ]
# Request fails with authentication error. Expected
- name: Get Current Forecast
run: |
set +e
ret_code=$(wget -S -t 5 --waitretry=5 "http://0.0.0.0:8080/api/GetCurrentForecast" --header "Content-Type: application/json" --post-data '{"startDate":"2022-11-02T15:30:00Z","endDate":"2022-11-02T18:30:00Z","location":"eastus","duration":"15"}' 2>&1 | grep "HTTP/" | awk '{print $2}')
set -e
[ "401" == $ret_code ]
File renamed without changes.
71 changes: 53 additions & 18 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

- [Configuration](#configuration)
- [Logging](#logging)
- [DataSources](#datasources)
Expand All @@ -10,9 +9,11 @@
- [WattTime Caching BalancingAuthority](#watttime-caching-balancingauthority)
- [Json Configuration](#json-configuration)
- [ElectricityMaps Configuration](#electricitymaps-configuration)
- [ApiTokenHeader](#api-token-header)
- [ApiToken](#api-token)
- [baseUrl](#baseurl)
- [API Token Header](#api-token-header)
- [API Token](#api-token)
- [BaseUrl](#baseurl-1)
- [Emission Factor Type](#emission-factor-type)
- [Disable Estimations](#disable-estimations)
- [CarbonAwareVars](#carbonawarevars)
- [Tracing and Monitoring Configuration](#tracing-and-monitoring-configuration)
- [Verbosity](#verbosity)
Expand Down Expand Up @@ -69,10 +70,11 @@ Logging__LogLevel__Default="Debug" dotnet run

## DataSources

The SDK supports multiple data sources for getting carbon data. At this time, only a JSON file and [WattTime](https://www.watttime.org/) are supported.
The SDK supports multiple data sources for getting carbon data. At this time, only a JSON file, [WattTime](https://www.watttime.org/) and [ElectricityMaps](https://www.electricitymaps.com/) are supported.

Each data source interface is configured with a specific data source implementation.

If set to `WattTime`, WattTime configuration must also be supplied.
If set to `WattTime` or `ElectricityMaps`, the configuration specific to that data provider must also be supplied.

`JSON` will result in the data being loaded from the file specified in the `DataFileLocation` property

Expand All @@ -93,6 +95,12 @@ If set to `WattTime`, WattTime configuration must also be supplied.
"username": "proxyUsername",
"password": "proxyPassword"
}
},
"ElectricityMaps": {
"Type": "ElectricityMaps",
"APITokenHeader": "auth-token",
"APIToken": "myAwesomeToken",
"BaseURL": "https://api.electricitymap.org/v3/"
},
"Json": {
"Type": "Json",
Expand All @@ -115,7 +123,7 @@ If using the WattTime data source, WattTime configuration is required.
}
```

> **Sign up for a test account:** To create an account, follow these steps : https://www.watttime.org/api-documentation/#best-practices-for-api-usage
> **Sign up for a test account:** To create an account, follow these steps [from the WattTime documentation](https://www.watttime.org/api-documentation/#best-practices-for-api-usage)
#### username

Expand Down Expand Up @@ -170,7 +178,17 @@ info: CarbonAware.DataSources.Json.JsonDataSource[0]

If using the ElectricityMaps data source, ElectricityMaps configuration is required.

__With an account token:__
> **NOTE**
> The ElectricityMaps API does not currently support access to historical forecasts.
> This means that functionality such as the CLI `emissions-forecasts` `--requested-at` flag
> and the API `/forecasts/batch` `requestedAt` input will respond with a `NotImplemented` error.
>
> Depending on the goal, the historical measured `emissions` commands may be a reasonable workaround.
> This would treat the measured emissions as a "perfect historical forecast" effectively.
> Otherwise, use a data source that has support for historical forecasts, such as [WattTime](#watttime-configuration).
**With an account token:**

```json
{
"APITokenHeader": "auth-token",
Expand All @@ -179,7 +197,8 @@ __With an account token:__
}
```

__With a free trial token:__
**With a free trial token:**

```json
{
"APITokenHeader": "X-BLOBR-KEY",
Expand All @@ -188,20 +207,32 @@ __With a free trial token:__
}
```

> **Sign up for a free trial:** To get a free trial: https://api-portal.electricitymaps.com/
> **Sign up for a free trial:** Select the free trial product from [the ElectricityMaps catalog](https://api-portal.electricitymaps.com/)
#### API Token Header

The API Token Header for ElectricityMaps. If you have a paid account, the header is "auth-token". If you're using the free trial, the header is "X-BLOB-KEY"
The API Token Header for ElectricityMaps. If you have a paid account, the header is "auth-token". If you're using the free trial, the header is "X-BLOBR-KEY"

#### API Token

The ElectricityMaps token you receive with your account or free trial.

#### baseUrl
#### BaseUrl

The url to use when connecting to ElectricityMaps. Defaults to "https://api.electricitymap.org/v3/" but can be overridden in the config if needed (such as for free-trial users or enable integration testing scenarios).

#### Emission Factor Type

String value for the optional `emissionFactorType` parameter to be sent on every ElectricityMaps API request that accepts this parameter.

See the [ElectricityMaps API Documentation](https://static.electricitymaps.com/api/docs/index.html#emission-factors) for more details and valid values.

#### Disable Estimations

Boolean value for the optional `disableEstimations` parameter to be sent on every ElectricityMaps API request that accepts this parameter.

See the [ElectricityMaps API Documentation](https://static.electricitymaps.com/api/docs/index.html#estimations) for more details.

## CarbonAwareVars

This section contains the global settings for the SDK. The configuration looks like this:
Expand Down Expand Up @@ -230,7 +261,7 @@ This application is integrated with Application Insights for monitoring purposes
ApplicationInsights_Connection_String="AppInsightsConnectionString"
```

You can alternatively configure using Instrumentation Key by setting the `AppInsights_InstrumentationKey` variable. However, Microsoft is ending technical support for instrumentation key�based configuration of the Application Insights feature soon. ConnectionString-based configuration should be used over InstrumentationKey. For more details, please refer to https://docs.microsoft.com/en-us/azure/azure-monitor/app/sdk-connection-string?tabs=net.
You can alternatively configure using Instrumentation Key by setting the `AppInsights_InstrumentationKey` variable. However, Microsoft is ending technical support for instrumentation key�based configuration of the Application Insights feature soon. ConnectionString-based configuration should be used over InstrumentationKey. For more details, please refer to [the documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/sdk-connection-string?tabs=net).

```bash
AppInsights_InstrumentationKey="AppInsightsInstrumentationKey"
Expand Down Expand Up @@ -335,6 +366,7 @@ DataSources__Configurations__WattTime__Password="wattTimePassword"
```

## Configuration for Forecast data Using ElectricityMaps

```json
{
"DataSources": {
Expand All @@ -343,18 +375,20 @@ DataSources__Configurations__WattTime__Password="wattTimePassword"
"ElectricityMaps": {
"Type": "ElectricityMaps",
"APITokenHeader": "auth-token",
"APIToken": "token"
"APIToken": "token",
"BaseURL": "https://api.electricitymap.org/v3/"
}
}
}
}
```

## Configuration for Emissions data Using WattTime and Forecast data Using ElectricityMaps
## Configuration for Emissions data using ElectricityMaps and Forecast data using WattTime

```json
"DataSources": {
"EmissionsDataSource": "WattTime",
"ForecastDataSource": "ElectricityMaps",
"EmissionsDataSource": "ElectricityMaps",
"ForecastDataSource": "WattTime",
"Configurations": {
"WattTime": {
"Type": "WattTime",
Expand All @@ -365,7 +399,8 @@ DataSources__Configurations__WattTime__Password="wattTimePassword"
"ElectricityMaps": {
"Type": "ElectricityMaps",
"APITokenHeader": "auth-token",
"APIToken": "token"
"APIToken": "token",
"BaseURL": "https://api.electricitymap.org/v3/"
}
}
}
Expand Down
1 change: 0 additions & 1 deletion docs/packaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ The current package include 8 projects from the SDK:
6. "CarbonAware.DataSources.Registration"
7. "CarbonAware.DataSources.WattTime"
8. "CarbonAware.LocationSources.Azure"
9. "CarbonAware.Tools.WattTimeClient"

These 8 projects enable users of the library to consume the current endpoints exposed by the library. The package that needs to be added to a new C# project is `GSF.CarbonAware`.

Expand Down
37 changes: 37 additions & 0 deletions docs/selecting-a-data-source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Selecting a Data Source

The Carbon Aware SDK includes access to various data sources of carbon aware data, including WattTime, ElectricityMaps, and a custom JSON source. These matrices are an attempt to track what features of the Carbon Aware SDK are enabled for which data sources.

## Contents

- [Type of Data Sources and Configuration](#type-of-data-sources-and-configuration)
- [Data Source Methods Available](#data-source-methods-available)
- [Location Coverage](#location-coverage)

## Type of Data Sources and Configuration

In the CarbonAware SDK configuration, you can set what data source to use as the `EmissionsDataSource` and the `ForecastDataSource`. There are also certain configuration fields that must be set in order to access the raw data.
| Type | WattTime | ElectricityMaps | JSON |
|--------------------------|-----------|-----------------|------|
| Is Emissions DataSource | ✅ | ✅ | ✅ |
| Is Forecast DataSource | ✅ | ✅ | ❌ |
| Makes HTTP(s) call | ✅ | ✅ | ❌ |
| Can Use Custom Data | ❌ | ❌ | ✅ |
| Supports Trial + Full Account | ✅ | ✅ (*[different config required](./configuration.md#electricitymaps-configuration)) | N/A |

## Data Source Methods Available

Not all data sources support all the routes provided in the interfaces (`IEmissionsDataSource`/`IForecastDataSource`).

| Methods | WattTime | ElectricityMaps | JSON | CLI Usage | Web Api Usage | SDK Usage
|--------------|:-----------:|:-----------------:|:------:|:-:|:-:|:-:|
| GetCarbonIntensityAsync | ✅ | ✅ | ✅ |`emissions`|`emissions/bylocation` or `emissions/bylocations` or `emissions/bylocations/best` or `emissions/average`‑`carbon`‑`intensity` or `emissions/average`‑`carbon`‑`intensity/batch`|`GetEmissionsDataAsync(...)` or `GetBestEmissionsDataAsync(...)` or `GetAverageCarbonIntensityDataAsync(...)`|
| GetCurrentForecastAsync | ✅ | ✅ | ❌ |`emissions`‑`forecasts`|`forecasts/current`|`GetCurrentForecastAsync(...)`|
| GetForecastByDateAsync | ✅ | ❌ | ❌ |`emissions`‑`forecasts` ‑‑`requested`‑`at`|`forecasts/batch` with `requestedAt` field|`GetForecastByDateAsync(...)`|

## Location Coverage

Different data sources provide both different features (as outlined above) but also coverage of different geographic areas. It's important to note that each data source may have different region names, which are handled through the location config.

- For `WattTime`, see their [interactive coverage map](https://www.watttime.org/explorer) to find the relevant zone.
- For `ElectricityMaps`, see their [live map app](https://app.electricitymaps.com/map?utm_source=electricitymaps.com&utm_medium=website&utm_campaign=banner) to find the relevant zone and see current data coming in.
5 changes: 5 additions & 0 deletions samples/azure-function/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Azure Functions & C# - .NET 6 (In-Process)",
"dockerFile": "../Dockerfile",
"forwardPorts": [ 7071 ]
}
23 changes: 23 additions & 0 deletions samples/azure-function/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Find the Dockerfile at this URL
# https://github.com/Azure/azure-functions-docker/blob/dev/host/4/bullseye/amd64/dotnet/dotnet-inproc/dotnet.Dockerfile

FROM mcr.microsoft.com/azure-functions/dotnet:4.0 AS base
WORKDIR /home/site/wwwroot

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
COPY ["src/", "data/src/"]
COPY ["scripts/", "data/scripts/"]
COPY ["samples/", "data/samples/"]

WORKDIR /data
RUN scripts/package/create_packages.sh src/CarbonAwareSDK.sln /packages && \
dotnet restore "samples/azure-function/function.csproj" && \
scripts/package/add_packages.sh samples/azure-function/function.csproj /packages && \
dotnet build "samples/azure-function/function.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "samples/azure-function/function.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /home/site/wwwroot
COPY --from=publish /app/publish .
66 changes: 66 additions & 0 deletions samples/azure-function/GetCarbonIntensity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using GSF.CarbonAware.Handlers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Text.Json;

namespace function
{
public class GetCarbonIntensity
{
private readonly IEmissionsHandler _handler;

public GetCarbonIntensity(IEmissionsHandler handler)
{
this._handler = handler;
}


[FunctionName("GetAverageCarbonIntensity")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
//Get the startDate, endDate, and location from the request query if the values are present in the query
string startDate = req.Query["startdate"];
string endDate = req.Query["enddate"];
string location = req.Query["location"];

//If the parameters are includes in the body of the request, read the values from the request body
string requestBody = String.Empty;
using (StreamReader streamReader = new(req.Body))
{
requestBody = await streamReader.ReadToEndAsync();
}
dynamic data = JsonSerializer.Deserialize<object>(requestBody);

startDate ??= data?.startDate;
endDate ??= data?.endDate;
location ??= data?.location;


try
{
var result = await _handler.GetAverageCarbonIntensityAsync(location, DateTimeOffset.Parse(startDate), DateTimeOffset.Parse(endDate));
log.LogInformation($"For location {location} Starting at: {startDate} Ending at: {endDate} the Average Emissions Rating is: {result}.");

return new OkObjectResult(result);
}
catch (Exception e)
{
//Messages related to incorrect parameter values (ie dates outside of range) are returned in the data section
//Otherwise send the returned error messages
if (e.Data.Count>0)
return new BadRequestObjectResult(e.Data);
else
return new BadRequestObjectResult(e.Message);
}

}
}
}
Loading

0 comments on commit c22c748

Please sign in to comment.