diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 78dfac3a7..1b69a04c8 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:6.0
+FROM mcr.microsoft.com/dotnet/sdk:8.0
RUN apt-get update
diff --git a/.github/ISSUE_TEMPLATE/agenda-template.md b/.github/ISSUE_TEMPLATE/agenda-template.md
index 41a9f8646..01114ccae 100644
--- a/.github/ISSUE_TEMPLATE/agenda-template.md
+++ b/.github/ISSUE_TEMPLATE/agenda-template.md
@@ -8,7 +8,7 @@ assignees: ''
---
## Date
-YYYY-MM-DD - 8am UTC
+YYYY-MM-DD - 7am UTC
## Meeting notices
@@ -18,7 +18,7 @@ Linux Foundation meetings involve participation by industry competitors, and it
Examples of types of actions that are prohibited at Linux Foundation meetings and in connection with Linux Foundation activities are described in the Linux Foundation Antitrust Policy available at http://www.linuxfoundation.org/antitrust-policy. If you have questions about these matters, please contact your company counsel, or if you are a member of the Linux Foundation, feel free to contact Andrew Updegrove of the firm of Gesmer Updegrove LLP, which provides legal counsel to the Linux Foundation.
### Recordings
-GSF project meetings may be recorded for use solely by the GSF team for administration purposes. In very limited instances, and with explicit approval, recordings may be made more widely available.
+We're happy to start recording these meetings if we receive any requests to record them.
### Roll Call
Please *add a comment* to this issue during the meeting to denote attendance.
diff --git a/.github/ISSUE_TEMPLATE/case-study-template.md b/.github/ISSUE_TEMPLATE/case-study-template.md
new file mode 100644
index 000000000..976c16d94
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/case-study-template.md
@@ -0,0 +1,62 @@
+---
+name: Case-study template
+about: Case-study submissions
+title: Case-study submissions
+labels: Case-study submissions
+assignees: vaughanknight
+
+---
+
+# Case Study Template
+
+*This is a template to use for case studies that are submitted that leverage the Carbon Aware SDK. These case studies will be published to the Carbon Aware SDK repository*
+
+> We will contact the person who created this issue to follow up prior to publishing any case studies for clarification and alignment
+
+*Please delete the text in italics and replace it with the appropriate information.*
+
+*For more information on any of the items, the final reference is the [SCI Specification](https://github.com/Green-Software-Foundation/software_carbon_intensity/blob/main/Software_Carbon_Intensity/Software_Carbon_Intensity_Specification.md)*
+
+*If you find errors, or have further questions, please feel free to raise an [issue](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues).*
+
+## Overview
+
+_Please provide information describing the use case in a few bullet points_
+
+## Describe the solution that implements the Carbon Aware SDK
+
+_A textual description of the solution that implements the Carbon Aware SDK_
+
+_A textual description of the action that is taken that reduces emissiosn due to leveraging the Carbon Aware SDK_
+
+## Architecture for the solution (if applicable)
+
+_An architecture diagram of the solution described in this use case_
+
+### Technical details of the components in the architecture
+
+_Textual description with technical details of each component provided in the architecture diagram_
+
+## Carbon Aware SDK impact (both emissions impact and business impact)
+
+_Textual description of the emissions impact and how it was measured_
+
+_Textual description of the business impact beyond the emissions impact (if applicable)_
+
+## Any public press release information that relates to the
+
+_Titles and links to press releases that can be viewed publicly online_
+
+## Any other notes of significance
+
+_For example_
+
+_Was the SCI used?_
+
+_Was this built in conjuction with another party that should be included in the case study?_
+
+_Was there a contribution back to the Carbon Aware SDK project as part of this?_
+
+_Are there any further plans to implement the Carbon Aware SDK across other solutions that can be discussed?_
+
+_Are there any further plans to leverage other aspects of the Green Software Foundation to drive positive impact with software?_
diff --git a/.github/workflows/1-pr.yaml b/.github/workflows/1-pr.yaml
index c6d8f9f44..b38b914d0 100644
--- a/.github/workflows/1-pr.yaml
+++ b/.github/workflows/1-pr.yaml
@@ -8,7 +8,7 @@ env:
# web app
DOCKERFILE_PATH: "CarbonAware.WebApi/src/Dockerfile"
HEALTH_ENDPOINT: "0.0.0.0:8080/health"
- DLL_FILE_PATH: "./bin/Release/net6.0/CarbonAware.WebApi.dll"
+ DLL_FILE_PATH: "./bin/Release/net8.0/CarbonAware.WebApi.dll"
DOTNET_SRC_DIR: "./src"
# console app packages
DOTNET_SOLUTION: "src/GSF.CarbonAware/src/GSF.CarbonAware.csproj"
@@ -40,9 +40,9 @@ jobs:
- uses: actions/checkout@v2
- name: Setup .NET
- uses: actions/setup-dotnet@v1
+ uses: actions/setup-dotnet@v3
with:
- dotnet-version: 6.0.x
+ dotnet-version: 8.0.x
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
@@ -86,14 +86,14 @@ jobs:
needs: sln-build-and-test
runs-on: ubuntu-latest
container:
- image: mcr.microsoft.com/dotnet/sdk:6.0
+ image: mcr.microsoft.com/dotnet/sdk:8.0
steps:
- uses: actions/checkout@v3
- - name: Setup .NET Core SDK 6
- uses: actions/setup-dotnet@v2
+ - name: Setup .NET Core SDK 8
+ uses: actions/setup-dotnet@v3
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
include-prerelease: false
- name: Install dependencies
@@ -123,6 +123,8 @@ jobs:
- name: Generate Open API
run: dotnet tool run swagger tofile --output ./wwwroot/api/v1/swagger.yaml --yaml ${{ env.DLL_FILE_PATH }} v1
+ env:
+ DOTNET_ROLL_FORWARD: LatestMajor
working-directory: ./src/CarbonAware.WebApi/src
- name: Upload swagger artifact
@@ -144,7 +146,7 @@ jobs:
- name: Docker Run Container
run: |
- docker run -d --name runnable-container -p 8080:80 ca-api
+ docker run -d --name runnable-container -p 8080:8080 ca-api
docker container ls
- name: Docker WGET Health Endpoint
@@ -164,10 +166,10 @@ jobs:
uses: actions/checkout@v3
with:
ref: dev
- - name: Setup .NET Core SDK 6
- uses: actions/setup-dotnet@v2
+ - name: Setup .NET Core SDK 8
+ uses: actions/setup-dotnet@v3
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
include-prerelease: false
- name: Install dependencies
run: dotnet restore
@@ -179,6 +181,8 @@ jobs:
working-directory: ${{ env.DOTNET_SRC_DIR }}
- name: Generate Open API
run: dotnet tool run swagger tofile --output ./wwwroot/api/v1/swagger.yaml --yaml ${{ env.DLL_FILE_PATH }} v1
+ env:
+ DOTNET_ROLL_FORWARD: LatestMajor
- name: Upload dev artifact
uses: actions/upload-artifact@v1
with:
@@ -199,10 +203,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- - name: Setup .NET Core SDK 6
- uses: actions/setup-dotnet@v2
+ - name: Setup .NET Core SDK 8
+ uses: actions/setup-dotnet@v3
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
include-prerelease: false
- name: Create packages
@@ -244,4 +248,4 @@ jobs:
command: config
globs: |
./custom.markdownlint.jsonc
- {"*[^.github]/**,*"}.md
\ No newline at end of file
+ {"*[^.github]/**,*"}.md
diff --git a/.github/workflows/2-pre-release.yaml b/.github/workflows/2-pre-release.yaml
index ddd6afe72..816cc7e66 100644
--- a/.github/workflows/2-pre-release.yaml
+++ b/.github/workflows/2-pre-release.yaml
@@ -23,8 +23,6 @@ jobs:
permissions:
packages: write
steps:
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry
diff --git a/.github/workflows/4-release.yaml b/.github/workflows/4-release.yaml
index b4eaa34d1..54fcc9deb 100644
--- a/.github/workflows/4-release.yaml
+++ b/.github/workflows/4-release.yaml
@@ -14,8 +14,6 @@ jobs:
permissions:
packages: write
steps:
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry
diff --git a/.github/workflows/dev_carbon-aware-api.yml b/.github/workflows/dev_carbon-aware-api.yml
index 643d58b8d..f1c4dc4ae 100644
--- a/.github/workflows/dev_carbon-aware-api.yml
+++ b/.github/workflows/dev_carbon-aware-api.yml
@@ -20,7 +20,7 @@ jobs:
- name: Set up .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
include-prerelease: true
- name: Build with dotnet
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 7df71697f..8921c133f 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -10,7 +10,7 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "buildCLI",
- "program": "${workspaceFolder}/src/CarbonAware.CLI/src/bin/Debug/net6.0/caw.dll",
+ "program": "${workspaceFolder}/src/CarbonAware.CLI/src/bin/Debug/net8.0/caw.dll",
"args": [
"emissions",
"--location", "${input:caw_location}"
@@ -27,7 +27,7 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "buildWebApi",
- "program": "${workspaceFolder}/src/CarbonAware.WebApi/src/bin/Debug/net6.0/CarbonAware.WebApi.dll",
+ "program": "${workspaceFolder}/src/CarbonAware.WebApi/src/bin/Debug/net8.0/CarbonAware.WebApi.dll",
"args": [],
"cwd": "${workspaceFolder}/src/CarbonAware.WebApi/src/",
"stopAtEntry": false,
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3863d0c3..d41501578 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,50 @@
All notable changes to the Carbon Aware SDK will be documented in this file.
+
+## [1.4.0] - 2024-05
+
+### Added
+
+-[#401] [Feature Contribution]: Upgrade .NET version to .NET 8 ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/401)
+-[#419] [Feature Contribution]: Migrate sample implementation of Azure Function to isolated worker model ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/419)
+-[PR #500] Up Helm chart version to 1.2.0 ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/pull/500)
+
+-[#397] [Feature Contribution]: Data caching in the SDK ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/397)
+
+### Fixed
+
+-[#505] [Bug]: Project Page wiki from GSF website still says it's in incubation ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/505)
+-[#496] [URGENT] WebAPI container has not built due to segmentation fault ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/496)
+-[#487] [Bug]: Getting started guide is lost ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/487)
+
+
+### Changed
+
+-[#477] [Bug]: Ensure the readme file shows as the project overview content on the documentation site ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/477)
+-[PR #485] Docs overview, disclaimer & pipeline updates for graduation ](https://github.com/Green-Software-Foundation/carbon-aware-sdk/pull/485)
+
+#### API
+
+-
+
+#### API Deployment
+
+-
+
+#### SDK
+
+-
+
+
+#### Other
+
+-
+
+
+For more details, checkout [https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/503](https://github.com/Green-Software-Foundation/carbon-aware-sdk/issues/503)
+
+
## [1.3.0] - 2024-02
### Added
diff --git a/README.md b/README.md
index ec7765990..5f8372fd1 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@ By knowing the carbon emissions of the energy that powers your applications, you
* Running software updates at greener energy time windows
* Using data to run hypothetical models to understand how you could start driving impact and reduce emissions, drive business cases for change, and create a greener future.
+
Within the [Green Software Foundations Theory of Change](https://greensoftware.foundation/articles/theory-of-change), we look at 3 pillars, that being **Knowledge**, **Tech Culture**, and **Tooling** as focus areas to drive this change. The Carbon Aware SDK at it's core sits firmly in the **Tooling** pillar, and also supports the other pillars, providing **Knowledge** through emissions data to inform change, and being core enabler for the **Tech Culture** for building carbon aware software.
Companies including UBS and Vestas have already deployed the Carbon Aware SDK to build greener software, and you can too!
@@ -102,7 +103,7 @@ centralised management, auditability and traceability, and more.
The Carbon Aware SDK is a collaborative effort between companies around the
world, with the intention of providing a platform that everyone can use. This
means the API will be striving towards what solves the highest impact issues
-with diverse perspectives from these organisation and contributors.
+with diverse perspectives from these organisations and contributors.
### Standardization
@@ -151,7 +152,7 @@ capability.
### Aggregated Sources
-A feature we have in the roadmap is the ability aggregate data sources across
+A feature we have in the roadmap is the ability to aggregate data sources across
multiple providers. Different data providers have different levels of
granularity depending on region, and it may be that data provider A is preferred
in Japan, while data provider B is preferred in US regions.
diff --git a/SECURITY.md b/SECURITY.md
index 468aa5c50..bba41e31f 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -75,11 +75,11 @@ We perform regular reviews inline with the information provided below. All rele
### Use basic good cryptographic practices
- https://www.bestpractices.dev/en/criteria/0#0.crypto_published - ✅ uses HTTPS for WebAPI, N/A for CLI
-- https://www.bestpractices.dev/en/criteria/0#0.crypto_floss - ✅ uses dotnet 6.0 implementations
-- https://www.bestpractices.dev/en/criteria/0#0.crypto_keylength - ✅ uses dotnet 6.0 implementations
-- https://www.bestpractices.dev/en/criteria/0#0.crypto_working - ✅ uses dotnet 6.0 implementations
-- https://www.bestpractices.dev/en/criteria/0#0.crypto_password_storage - ✅ ⚠️ uses dotnet 6.0 implementations
-- https://www.bestpractices.dev/en/criteria/0#0.crypto_random - ✅ uses dotnet 6.0 implementatons for HTTPS
+- https://www.bestpractices.dev/en/criteria/0#0.crypto_floss - ✅ uses dotnet 8.0 implementations
+- https://www.bestpractices.dev/en/criteria/0#0.crypto_keylength - ✅ uses dotnet 8.0 implementations
+- https://www.bestpractices.dev/en/criteria/0#0.crypto_working - ✅ uses dotnet 8.0 implementations
+- https://www.bestpractices.dev/en/criteria/0#0.crypto_password_storage - ✅ ⚠️ uses dotnet 8.0 implementations
+- https://www.bestpractices.dev/en/criteria/0#0.crypto_random - ✅ uses dotnet 8.0 implementatons for HTTPS
### Secured delivery against man-in-the-middle (MITM) attacks
- Delivery mechanisms that counters MITM - ✅ uses HTTPS
diff --git a/casdk-docs/docs/overview/adopters.md b/casdk-docs/docs/overview/adopters.md
index 8411426c5..c1ffbf8a5 100644
--- a/casdk-docs/docs/overview/adopters.md
+++ b/casdk-docs/docs/overview/adopters.md
@@ -4,7 +4,9 @@ sidebar_position: 7
# Carbon Aware SDK adopters
-We're sharing adopters of the Carbon Aware SDK with public evidence, although we know many others are also using the Carbon Aware SDK to reduce the carbon footprint their software.
+
+We're sharing adopters of the Carbon Aware SDK with public evidence, although we know many others are also using the Carbon Aware SDK to reduce the carbon footprint of their software.
+
If you're using the Carbon Aware SDK and can share evidence, we'd love to add you to this list.
Please reach out to carbon-aware-sdk@greensoftware.foundation or send a pull request.
diff --git a/casdk-docs/docs/overview/contributing.md b/casdk-docs/docs/overview/contributing.md
index 23b602d7a..afae220fa 100644
--- a/casdk-docs/docs/overview/contributing.md
+++ b/casdk-docs/docs/overview/contributing.md
@@ -22,13 +22,14 @@ We have opportunities for both code and non code contributors. We're currently l
| Contribution Areas | Description |
|----------|----------|
-|**Sample Creation** | These help adopters of the SDK learn how they can quick get started and build their own carbon aware solutions.|
+|**Sample Creation** | These help adopters of the SDK learn how they can quickly get started and build their own carbon aware solutions.|
|**Documentation Updates** | The documentation always can be improved to make the Carbon Aware SDK more accessible to everyone. Guides, SDK and API document, and more! |
-|**Video Content Creation (how to enable, demos etc)** | Quick videos help adopters undersatnd just how easy it is to get started in an easy to consume form.
+|**Video Content Creation (how to enable, demos etc)** | Quick videos help adopters understand just how easy it is to get started in an easy to consume form.
|**Slide Deck Creation
Available for presenter use, including real time video demo**| We get a lot of traction at conferences, and if we have a standard deck for anyone to present, it will enable those who might not be able to create a deck, but could easily present it, to also participate.
## How To Get Started
-Introduce yourself on on our [discussions page](https://github.com/orgs/Green-Software-Foundation/discussions/65) and let us know where you think you can help.
+Introduce yourself on our [discussions page](https://github.com/orgs/Green-Software-Foundation/discussions/65) and let us know where you think you can help.
+
Find the Project Key contacts in the [Confluence page](https://greensoftwarefoundation.atlassian.net/wiki/spaces/~612dd45e45cd76006a84071a/pages/17137665/Opensource+Carbon+Aware+SDK).
If you are a GSF member organisation employee, you should:
diff --git a/casdk-docs/docs/overview/enablement.md b/casdk-docs/docs/overview/enablement.md
index 2028bfdbe..969eb9c99 100644
--- a/casdk-docs/docs/overview/enablement.md
+++ b/casdk-docs/docs/overview/enablement.md
@@ -15,7 +15,7 @@ sidebar_position: 3
2. [How to Use Carbon Aware SDK](#2-how-to-use-carbon-aware-sdk)
- 2.1 [Pre-requisities](#21-pre-requisities)
+ 2.1 [Pre-requisites](#21-pre-requisites)
* Data sources
* System requirement
@@ -44,16 +44,16 @@ different endpoints to provide the most flexibility to integrate to your
environment:
* CLI
-You can run the application using the [CLI](../src/CarbonAware.CLI) and refer
+You can run the application using the [CLI](/src/CarbonAware.CLI) and refer
to more documentation [here](../tutorial-basics/carbon-aware-cli.md).
* WebAPI
-You can build a container containing the [WebAPI](../src/CarbonAware.WebApi)
+You can build a container containing the [WebAPI](/src/CarbonAware.WebApi)
and connect via REST requests and refer to more documentation
[here](../tutorial-basics/carbon-aware-webapi.md).
* SDK
-You can reference the [Carbon Aware C# Library](../src/GSF.CarbonAware) in your
+You can reference the [Carbon Aware C# Library](/src/GSF.CarbonAware) in your
projects and make use of its functionalities and features.
| ![Image 2](./images/readme/screenshot_cli.png) | ![Image 1](./images/readme/screenshot_web_api.png) |
@@ -67,7 +67,7 @@ we show some examples of the [use case](./adopters.md).
## 2. How to use Carbon Aware SDK?
-### 2.1 Pre-requisities
+### 2.1 Pre-requisites
#### Data sources
@@ -87,7 +87,7 @@ providers into the carbon aware SDK.
#### System requirement
* Command Line Interface (CLI)
- * .NET Core 6.0
+ * .NET 8.0
* Alternatively:
* Docker
* VSCode and its [Remote Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
@@ -588,7 +588,7 @@ using environment variables, you'd do this:
#### Local project settings
For local-only settings you can use environment variables,
-[the Secret Manager tool](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-6.0&tabs=windows#secret-manager)
+[the Secret Manager tool](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=windows#secret-manager)
, or an untracked Development appsettings file to override the default project
settings.
@@ -598,7 +598,7 @@ remove the first line of (invalid) comments. Then update any settings according
to your preferences.
> Wherever possible, the projects leverage the
-> [default .NET configuration](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0#default-application-configuration-sources)
+> [default .NET configuration](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?#default-application-configuration-sources)
> expectations. Thus, they can be configured using any file matching the format:
> `appsettings..json`. Where `` is the value of the
> `ASPNETCORE_ENVIRONMENT` environment variable. By convention projects tend to
diff --git a/casdk-docs/docs/overview/overview.md b/casdk-docs/docs/overview/overview.md
index ec7765990..cd628235f 100644
--- a/casdk-docs/docs/overview/overview.md
+++ b/casdk-docs/docs/overview/overview.md
@@ -17,13 +17,13 @@ By knowing the carbon emissions of the energy that powers your applications, you
* Running software updates at greener energy time windows
* Using data to run hypothetical models to understand how you could start driving impact and reduce emissions, drive business cases for change, and create a greener future.
-Within the [Green Software Foundations Theory of Change](https://greensoftware.foundation/articles/theory-of-change), we look at 3 pillars, that being **Knowledge**, **Tech Culture**, and **Tooling** as focus areas to drive this change. The Carbon Aware SDK at it's core sits firmly in the **Tooling** pillar, and also supports the other pillars, providing **Knowledge** through emissions data to inform change, and being core enabler for the **Tech Culture** for building carbon aware software.
+Within the [Green Software Foundations Theory of Change](https://greensoftware.foundation/articles/theory-of-change), we look at 3 pillars, that being **Knowledge**, **Tech Culture**, and **Tooling** as focus areas to drive this change. The Carbon Aware SDK at its core sits firmly in the **Tooling** pillar, and also supports the other pillars, providing **Knowledge** through emissions data to inform change, and being core enabler for the **Tech Culture** for building carbon aware software.
Companies including UBS and Vestas have already deployed the Carbon Aware SDK to build greener software, and you can too!
# Getting Started Overview
-Head on over to the [Getting Started Overview Guide](./casdk-docs/docs/overview/overview.md) to get up and running.
+Head on over to the [quickstart guide](../quickstart.md) to get up and running.
Get started on creating sustainable software innovation for a greener future
today!
@@ -83,7 +83,7 @@ deployment in the greenest location.
The Carbon Aware SDK is being used by large and small companies around the
world. Some of the world’s biggest enterprises and software companies, through
-to start-ups. Both UBS and Vestas have used the SDK, with further details over on the [adopters overview](./casdk-docs/docs/overview/adopters.md).
+to start-ups. Both UBS and Vestas have used the SDK, with further details over on the [adopters overview](./adopters.md).
Machine Learning (ML) workloads are a great example of long running compute
intensive workloads, that often are also not time critical. By moving these workloads to a different time, the carbon emissions from the ML training can be reduced by up to 15%, and by moving the location of the training this can be
@@ -102,7 +102,7 @@ centralised management, auditability and traceability, and more.
The Carbon Aware SDK is a collaborative effort between companies around the
world, with the intention of providing a platform that everyone can use. This
means the API will be striving towards what solves the highest impact issues
-with diverse perspectives from these organisation and contributors.
+with diverse perspectives from these organisations and contributors.
### Standardization
@@ -151,7 +151,7 @@ capability.
### Aggregated Sources
-A feature we have in the roadmap is the ability aggregate data sources across
+A feature we have in the roadmap is the ability to aggregate data sources across
multiple providers. Different data providers have different levels of
granularity depending on region, and it may be that data provider A is preferred
in Japan, while data provider B is preferred in US regions.
@@ -170,7 +170,7 @@ percentage information only at the moment.
## Contributing
The Carbon Aware SDK is open for contribution! Want to contribute? Check out the
-[contribution guide](./CONTRIBUTING.md).
+[contribution guide](./contributing.md).
## Green Software Foundation Project Summary
diff --git a/casdk-docs/docs/quickstart.md b/casdk-docs/docs/quickstart.md
index ab86b4d97..a500520f5 100644
--- a/casdk-docs/docs/quickstart.md
+++ b/casdk-docs/docs/quickstart.md
@@ -18,7 +18,7 @@ generated libraries for your language of choice!
Prerequisites:
-- .NET Core 6.0
+- .NET Core 8.0
- Alternatively:
- Docker
- VSCode (it is recommended to work in a Dev Container)
@@ -404,7 +404,7 @@ installation and example usage.
3. Install the Python client library using
[`setuptools`](http://pypi.python.org/pypi/setuptools)):
`python setup.py install --user`
-4. The library is now succesfully installed!
+4. The library is now successfully installed!
There should be an example script in the `README` file, but this guide suggests
trying the following example first:
diff --git a/casdk-docs/docs/tutorial-basics/carbon-aware-webapi.md b/casdk-docs/docs/tutorial-basics/carbon-aware-webapi.md
index f971f0714..028db6273 100644
--- a/casdk-docs/docs/tutorial-basics/carbon-aware-webapi.md
+++ b/casdk-docs/docs/tutorial-basics/carbon-aware-webapi.md
@@ -456,10 +456,10 @@ CarbonAware.LocationSources.LocationSource: Warning: New key swedencentral_1 gen
## Error Handling
The WebAPI leveraged the
-[.Net controller filter pipeline](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-6.0)
+[.Net controller filter pipeline](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-8.0)
to ensure that all requests respond with a consistent JSON schema.
-![.Net controller filter pipeline image](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters/_static/filter-pipeline-2.png?view=aspnetcore-6.0)
+![.Net controller filter pipeline image](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters/_static/filter-pipeline-2.png?view=aspnetcore-8.0)
Controllers are responsible for managing the "Success" responses. If an error
occurs in the WebAPI code and an unhandled exception is thrown, the
@@ -470,7 +470,7 @@ caught and handled by the WebAPI code, the controller will continue to manage
the response.
The .Net framework will automatically respond to validation errors with a
-[ValidationProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.validationproblemdetails?view=aspnetcore-6.0)
+[ValidationProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.validationproblemdetails?view=aspnetcore-8.0)
object. Using the Exception Filter class enables the WebAPI to consistently
respond with the `ValidationProblemDetails` error schema in all error cases and
take advantage of error handling automatically provided by the framework.
@@ -489,7 +489,7 @@ specification
cd CarbonAware.WebApi/src
dotnet tool restore
dotnet build --configuration Release --no-restore
- dotnet tool run swagger tofile --output ./wwwroot/api/v1/swagger.yaml --yaml bin/Release/net6.0/CarbonAware.WebApi.dll v1
+ dotnet tool run swagger tofile --output ./wwwroot/api/v1/swagger.yaml --yaml bin/Release/net8.0/CarbonAware.WebApi.dll v1
```
1. The `CarbonAware.WebApi/src/wwwroot/api/v1/swagger.yaml` file contains the supported
OpenApi specification.
diff --git a/casdk-docs/docs/tutorial-basics/containerization.md b/casdk-docs/docs/tutorial-basics/containerization.md
index 355a9a9b0..8f8211e05 100644
--- a/casdk-docs/docs/tutorial-basics/containerization.md
+++ b/casdk-docs/docs/tutorial-basics/containerization.md
@@ -25,11 +25,11 @@ carbon_aware v1 6293e2528bf2 About an hour ago 230MB
## Run WebApi Image
1. Run the image using `docker run` with host port 8000 mapped to the WebApi
- port 80 and configure environment variable settings for
+ port 8080 and configure environment variable settings for
[WattTime](https://www.watttime.org) provider.
```sh
- docker run --rm -p 8000:80 \
+ docker run --rm -p 8000:8080 \
> -e DataSources__EmissionsDataSource="WattTime" \
> -e DataSources__ForecastDataSource="WattTime" \
> -e DataSources__Configurations__WattTime__Type="WattTime" \
@@ -40,7 +40,7 @@ carbon_aware v1 6293e2528bf2 About an hour ago 230MB
or the [ElectricityMaps](https://www.electricitymaps.com) provider
```sh
- docker run --rm -p 8000:80 \
+ docker run --rm -p 8000:8080 \
> -e DataSources__EmissionsDataSource="ElectricityMaps" \
> -e DataSources__ForecastDataSource="ElectricityMaps" \
> -e DataSources__Configurations__ElectricityMaps__Type="ElectricityMaps" \
@@ -52,7 +52,7 @@ carbon_aware v1 6293e2528bf2 About an hour ago 230MB
or the [ElectricityMapsFree](https://www.co2signal.com/) provider
```sh
- docker run --rm -p 8000:80 \
+ docker run --rm -p 8000:8080 \
> -e DataSources__EmissionsDataSource="ElectricityMapsFree" \
> -e DataSources__Configurations__ElectricityMapsFree__Type="ElectricityMapsFree" \
> -e DataSources__Configurations__ElectricityMapsFree__token="" \
diff --git a/casdk-docs/docs/tutorial-extras/configuration.md b/casdk-docs/docs/tutorial-extras/configuration.md
index 7453b8e82..c65aa44f2 100644
--- a/casdk-docs/docs/tutorial-extras/configuration.md
+++ b/casdk-docs/docs/tutorial-extras/configuration.md
@@ -19,9 +19,11 @@
- [ElectricityMapsFree Configuration](#electricitymapsfree-configuration)
- [API Token](#api-token)
- [BaseUrl](#baseurl)
+ - [Cache](#cache)
- [CarbonAwareVars](#carbonawarevars)
- [Tracing and Monitoring Configuration](#tracing-and-monitoring-configuration)
- [Verbosity](#verbosity)
+ - [Prometheus exporter](#prometheus-exporter-for-emissions-data)
- [Web API Prefix](#web-api-prefix)
- [LocationDataSourcesConfiguration](#locationdatasourcesconfiguration)
- [Sample Configurations](#sample-configurations)
@@ -194,7 +196,7 @@ custom `EmissionsData` sets. The file should be located under the
`/src/data/data-sources/` directory that is part of the repository.
At build time, all the JSON files under `/src/data/data-sources/`
are copied over the destination directory
-`/src/CarbonAware.WebApi/src/bin/[Debug|Publish]/net6.0/data-sources/json`
+`/src/CarbonAware.WebApi/src/bin/[Debug|Publish]/net8.0/data-sources/json`
that is part of the `CarbonAware.WebApi` assembly. Also the file can be placed
where the assembly `CarbonAware.WebApi.dll` is located under `data-sources/json`
directory. For instance, if the application is installed under `/app`, copy the
@@ -327,6 +329,29 @@ The url to use when connecting to ElectricityMapsFree. Defaults to
"https://api.co2signal.com/v1/" but can be overridden in the config if needed
(such as to enable integration testing scenarios).
+## Cache
+
+Frequent access to data sources could cause problems such as performance trouble
+or exceed rate limit. To avoid them, you can configure data cache like this:
+
+```json
+{
+ "EmissionsDataCache": {
+ "Enabled": true,
+ "ExpirationMin": 30
+ }
+}
+```
+
+The behavior of current cache implementation:
+* Only emissions data are cached
+ * Forecast data are not stored
+* The result of the latest query to data sources is cached
+* Use cache rather than data sources if even one datum in cache match with the query
+ * Even though more data in data sources would be matched, they are not retrieved
+* Cached data are stored in memory
+ * They are cleard when the process of the SDK is down
+
## CarbonAwareVars
This section contains the global settings for the SDK. The configuration looks
@@ -379,6 +404,32 @@ InstrumentationKey. For more details, please refer to
AppInsights_InstrumentationKey="AppInsightsInstrumentationKey"
```
+### Prometheus exporter for emissions data
+
+> DISCLAIMER: The `/metrics` Prometheus exporter is currently unsupported, and is used for internal GSF needs, and may change in the future. It will retrieve _all_ emissions data and create heavy load on your data API's. It is turned off by default.
+
+In the WebApi project, this application can exporse latest carbon emissions data as a prometheus exporter.
+
+```bash
+CarbonAwareVars__EnableCarbonExporter="true"
+```
+The scraping endpoint is `/metrics` like this:
+
+```bash
+http://localhost/metrics
+```
+
+By default, the exposed data are latest ones within last 24 hours. If you would like to change the period
+in some reasones, you can configure the value like this:
+
+```json
+{
+ "CarbonExporter": {
+ "PeriodInHours": 48
+ }
+}
+```
+
### Verbosity
You can configure the verbosity of the application error messages by setting the
@@ -427,7 +478,7 @@ By setting `LocationDataSourcesConfiguration` property with one or more location
data sources, it is possible to load different `Location` data sets in order to
have more than one location. For instance by setting two location regions, the
property would be set as follow using
-[environment](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0#naming-of-environment-variables)
+[environment](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-8.0#naming-of-environment-variables)
variables:
```sh
@@ -458,7 +509,7 @@ curl "http://${IP_HOST}:${PORT}/emissions/bylocations/best?location=${REGION}&ti
At build time, all the JSON files under
`/src/data/location-sources` are copied over the destination
directory
-`/src/CarbonAware.WebApi/src/bin/[Debug|Publish]/net6.0/location-sources/json`
+`/src/CarbonAware.WebApi/src/bin/[Debug|Publish]/net8.0/location-sources/json`
that is part of the `CarbonAware.WebApi` assembly. Also the file can be placed
where the assembly `CarbonAware.WebApi.dll` is located under
`location-sources/json` directory. For instance, if the application is installed
diff --git a/casdk-docs/docs/tutorial-extras/packaging.md b/casdk-docs/docs/tutorial-extras/packaging.md
index ac8dac6d7..7aef80801 100644
--- a/casdk-docs/docs/tutorial-extras/packaging.md
+++ b/casdk-docs/docs/tutorial-extras/packaging.md
@@ -63,7 +63,7 @@ project. When running in the dev container you will need:
- [Remote Containers extension for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
Alternatively you can run in your local environment using the
-[.NET Core 6.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0).
+[.NET Core 8.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0).
## SDK Configuration
diff --git a/global.json b/global.json
index 1c64019b5..ad8ad01d1 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "6.0.418",
+ "version": "8.0.201",
"rollForward": "latestFeature"
}
}
\ No newline at end of file
diff --git a/helm-chart/Chart.yaml b/helm-chart/Chart.yaml
index fadfb8f70..5570e9904 100644
--- a/helm-chart/Chart.yaml
+++ b/helm-chart/Chart.yaml
@@ -15,10 +15,10 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.1.0
+version: 1.2.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
-appVersion: "v1.1.0"
+appVersion: "v1.4.0"
diff --git a/helm-chart/templates/deployment.yaml b/helm-chart/templates/deployment.yaml
index cec413296..5a21b6682 100644
--- a/helm-chart/templates/deployment.yaml
+++ b/helm-chart/templates/deployment.yaml
@@ -39,7 +39,7 @@ spec:
{{- end }}
ports:
- name: http
- containerPort: 80
+ containerPort: 8080
protocol: TCP
volumeMounts:
- name: appsettings
diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml
index 452ef6a21..1f5cdaf9e 100644
--- a/helm-chart/values.yaml
+++ b/helm-chart/values.yaml
@@ -37,7 +37,7 @@ securityContext: {}
service:
type: ClusterIP
- port: 80
+ port: 8080
ingress:
enabled: false
diff --git a/samples/azure/azure-function/Dockerfile b/samples/azure/azure-function/Dockerfile
index 53316cbf0..cc5a6d5f1 100644
--- a/samples/azure/azure-function/Dockerfile
+++ b/samples/azure/azure-function/Dockerfile
@@ -1,10 +1,10 @@
# 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
+FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0 AS base
WORKDIR /home/site/wwwroot
-FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
COPY ["src/", "data/src/"]
COPY ["scripts/", "data/scripts/"]
COPY ["samples/", "data/samples/"]
@@ -21,3 +21,4 @@ RUN dotnet publish "samples/azure/azure-function/function.csproj" -c Release -o
FROM base AS final
WORKDIR /home/site/wwwroot
COPY --from=publish /app/publish .
+ENV ASPNETCORE_CONTENTROOT=/home/site/wwwroot
diff --git a/samples/azure/azure-function/GetCarbonIntensity.cs b/samples/azure/azure-function/GetCarbonIntensity.cs
index c1e2a441e..7ca8db605 100644
--- a/samples/azure/azure-function/GetCarbonIntensity.cs
+++ b/samples/azure/azure-function/GetCarbonIntensity.cs
@@ -1,7 +1,7 @@
using GSF.CarbonAware.Handlers;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Azure.WebJobs;
-using Microsoft.Azure.WebJobs.Extensions.Http;
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
@@ -14,17 +14,18 @@ namespace function
public class GetCarbonIntensity
{
private readonly IEmissionsHandler _handler;
+ private readonly ILogger _log;
- public GetCarbonIntensity(IEmissionsHandler handler)
+ public GetCarbonIntensity(IEmissionsHandler handler, ILogger log)
{
this._handler = handler;
+ this._log = log;
}
- [FunctionName("GetAverageCarbonIntensity")]
+ [Function("GetAverageCarbonIntensity")]
public async Task Run(
- [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
- ILogger log)
+ [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
//Get the startDate, endDate, and location from the request query if the values are present in the query
string startDate = req.Query["startdate"];
@@ -47,7 +48,7 @@ public async Task Run(
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}.");
+ _log.LogInformation($"For location {location} Starting at: {startDate} Ending at: {endDate} the Average Emissions Rating is: {result}.");
return new OkObjectResult(result);
}
diff --git a/samples/azure/azure-function/GetForecast.cs b/samples/azure/azure-function/GetForecast.cs
index 564a0138e..eb3536620 100644
--- a/samples/azure/azure-function/GetForecast.cs
+++ b/samples/azure/azure-function/GetForecast.cs
@@ -1,7 +1,7 @@
using GSF.CarbonAware.Handlers;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Azure.WebJobs;
-using Microsoft.Azure.WebJobs.Extensions.Http;
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
@@ -15,16 +15,17 @@ namespace CarbonAwareFunctions
public class GetForecast
{
private readonly IForecastHandler _handler;
+ private readonly ILogger _log;
- public GetForecast(IForecastHandler handler)
+ public GetForecast(IForecastHandler handler, ILogger log)
{
this._handler = handler;
+ this._log = log;
}
- [FunctionName("GetCurrentForecast")]
+ [Function("GetCurrentForecast")]
public async Task Run(
- [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
- ILogger log)
+ [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
//Get the startDate, endDate, location, and duration from the request query if the values are present in the query
string startDate = req.Query["startdate"];
diff --git a/samples/azure/azure-function/Program.cs b/samples/azure/azure-function/Program.cs
new file mode 100644
index 000000000..0f64de4d5
--- /dev/null
+++ b/samples/azure/azure-function/Program.cs
@@ -0,0 +1,24 @@
+using GSF.CarbonAware.Configuration;
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Configuration;
+using System.IO;
+
+var host = new HostBuilder()
+ .ConfigureFunctionsWebApplication()
+ .ConfigureAppConfiguration((context, builder) => {
+ var env = context.HostingEnvironment;
+ builder.AddJsonFile(Path.Combine(env.ContentRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
+ .AddJsonFile(Path.Combine(env.ContentRootPath, $"appsettings.{env.EnvironmentName}.json"), optional: true, reloadOnChange: false)
+ .AddEnvironmentVariables();
+ })
+ .ConfigureServices((context,services) => {
+ services.AddApplicationInsightsTelemetryWorkerService();
+ services.ConfigureFunctionsApplicationInsights();
+ services.AddEmissionsServices(context.Configuration);
+ services.AddForecastServices(context.Configuration);
+ })
+ .Build();
+
+host.Run();
\ No newline at end of file
diff --git a/samples/azure/azure-function/README.md b/samples/azure/azure-function/README.md
index 0021630de..641919de4 100644
--- a/samples/azure/azure-function/README.md
+++ b/samples/azure/azure-function/README.md
@@ -19,29 +19,20 @@ will use.
The Carbon Aware SDK is included in the function .csproj file by
[creating and adding the SDK as a package](../../docs/packaging.md#included-scripts).
-The [Startup.cs](./Startup.cs) file uses dependency injection to access the
+The [Program.cs](./Program.cs) file uses dependency injection to access the
handlers in the library. The following code initializes the C# Library:
```C#
- public override void Configure(IFunctionsHostBuilder builder)
- {
- var configuration = builder.GetContext().Configuration;
- builder.Services
- .AddEmissionsServices(configuration)
- .AddForecastServices(configuration);
- }
+ // omitted
+ .ConfigureServices((context,services) => {
+ services.AddApplicationInsightsTelemetryWorkerService();
+ services.ConfigureFunctionsApplicationInsights();
+ services.AddEmissionsServices(context.Configuration);
+ services.AddForecastServices(context.Configuration);
+ })
+ // omitted
```
-> Note as the in-process
-> [Azure Function uses dependency injection](https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection)
-> though via
-> [Microsoft.Azure.Functions.Extensions](https://www.nuget.org/packages/Microsoft.Azure.Functions.Extensions/)
-> there is a version conflict of
-> [Microsoft.Extensions.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Configuration).
-> It is fixed adding a version specific project dependency (in .csproj) to the
-> same version as the Carbon Aware SDK. Microsoft.Extensions.Configuration is
-> backwards compatible.
-
## Run Function Locally
Both Azure Function apps can be
diff --git a/samples/azure/azure-function/Startup.cs b/samples/azure/azure-function/Startup.cs
deleted file mode 100644
index 1660065ba..000000000
--- a/samples/azure/azure-function/Startup.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using GSF.CarbonAware.Configuration;
-using Microsoft.Azure.Functions.Extensions.DependencyInjection;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using System.IO;
-
-[assembly: FunctionsStartup(typeof(CarbonAwareFunctions.Startup))]
-
-namespace CarbonAwareFunctions
-{
- public class Startup : FunctionsStartup
- {
- public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
- {
- FunctionsHostBuilderContext context = builder.GetContext();
-
- builder.ConfigurationBuilder
- .AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
- .AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
- .AddEnvironmentVariables();
- }
-
- public override void Configure(IFunctionsHostBuilder builder)
- {
- var configuration = builder.GetContext().Configuration;
- builder.Services
- .AddEmissionsServices(configuration)
- .AddForecastServices(configuration);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/azure/azure-function/function.csproj b/samples/azure/azure-function/function.csproj
index e48592f82..4dd9facd2 100644
--- a/samples/azure/azure-function/function.csproj
+++ b/samples/azure/azure-function/function.csproj
@@ -1,9 +1,16 @@
- net6.0
+ net8.0
v4
+ Exe
+
+
+
+
+
+
@@ -11,8 +18,6 @@
-
-
@@ -26,4 +31,7 @@
PreserveNewest
+
+
+
diff --git a/samples/lib-integration/ConsoleApp/ConsoleApp.csproj b/samples/lib-integration/ConsoleApp/ConsoleApp.csproj
index c688178ea..ffd7e1267 100644
--- a/samples/lib-integration/ConsoleApp/ConsoleApp.csproj
+++ b/samples/lib-integration/ConsoleApp/ConsoleApp.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net8.0
enable
enable
diff --git a/src/CarbonAware.CLI/src/CarbonAware.CLI.csproj b/src/CarbonAware.CLI/src/CarbonAware.CLI.csproj
index fcc1f9bfb..f097ae0c9 100644
--- a/src/CarbonAware.CLI/src/CarbonAware.CLI.csproj
+++ b/src/CarbonAware.CLI/src/CarbonAware.CLI.csproj
@@ -4,7 +4,7 @@
caw
win-x64;osx-x64;linux-x64
Exe
- net6.0
+ net8.0
enable
enable
34d82203-20b1-4fcd-9bd4-3b247f13bad7
diff --git a/src/CarbonAware.CLI/src/Dockerfile b/src/CarbonAware.CLI/src/Dockerfile
index 94c08a68e..8bd94af86 100644
--- a/src/CarbonAware.CLI/src/Dockerfile
+++ b/src/CarbonAware.CLI/src/Dockerfile
@@ -1,5 +1,5 @@
-# Set the base image as the .NET 6.0 SDK (this includes the runtime)
-FROM mcr.microsoft.com/dotnet/sdk:6.0 as build-env
+# Set the base image as the .NET 8.0 SDK (this includes the runtime)
+FROM mcr.microsoft.com/dotnet/sdk:8.0 as build-env
# Copy everything and publish the release (publish implicitly restores and builds)
COPY ./src/ ./
@@ -27,7 +27,7 @@ LABEL com.github.actions.icon="sliders"
LABEL com.github.actions.color="purple"
# Relayer the .NET SDK, anew with the build output
-FROM mcr.microsoft.com/dotnet/runtime:6.0
+FROM mcr.microsoft.com/dotnet/runtime:8.0
COPY --from=build-env /out .
RUN apt-get update && apt-get install jq -y
diff --git a/src/CarbonAware.CLI/test/integrationTests/CarbonAware.CLI.IntegrationTests.csproj b/src/CarbonAware.CLI/test/integrationTests/CarbonAware.CLI.IntegrationTests.csproj
index 9b0dd0efe..a79c9cdf0 100644
--- a/src/CarbonAware.CLI/test/integrationTests/CarbonAware.CLI.IntegrationTests.csproj
+++ b/src/CarbonAware.CLI/test/integrationTests/CarbonAware.CLI.IntegrationTests.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
enable
false
enable
diff --git a/src/CarbonAware.CLI/test/unitTests/CarbonAware.CLI.UnitTests.csproj b/src/CarbonAware.CLI/test/unitTests/CarbonAware.CLI.UnitTests.csproj
index a97048508..8c56dd8ba 100644
--- a/src/CarbonAware.CLI/test/unitTests/CarbonAware.CLI.UnitTests.csproj
+++ b/src/CarbonAware.CLI/test/unitTests/CarbonAware.CLI.UnitTests.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
enable
false
enable
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/mock/CarbonAware.DataSources.ElectricityMaps.Mocks.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/mock/CarbonAware.DataSources.ElectricityMaps.Mocks.csproj
index ac0346757..10277e544 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/mock/CarbonAware.DataSources.ElectricityMaps.Mocks.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/mock/CarbonAware.DataSources.ElectricityMaps.Mocks.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
false
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/src/CarbonAware.DataSources.ElectricityMaps.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/src/CarbonAware.DataSources.ElectricityMaps.csproj
index d9dab5e3e..1d3c1ad32 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/src/CarbonAware.DataSources.ElectricityMaps.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/src/CarbonAware.DataSources.ElectricityMaps.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
enable
enable
true
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/test/CarbonAware.DataSources.ElectricityMaps.Tests.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/test/CarbonAware.DataSources.ElectricityMaps.Tests.csproj
index 34e343371..b726b4773 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/test/CarbonAware.DataSources.ElectricityMaps.Tests.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMaps/test/CarbonAware.DataSources.ElectricityMaps.Tests.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
false
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/mock/CarbonAware.DataSources.ElectricityMapsFree.Mocks.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/mock/CarbonAware.DataSources.ElectricityMapsFree.Mocks.csproj
index 60a945672..a8878c6ae 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/mock/CarbonAware.DataSources.ElectricityMapsFree.Mocks.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/mock/CarbonAware.DataSources.ElectricityMapsFree.Mocks.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/src/CarbonAware.DataSources.ElectricityMapsFree.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/src/CarbonAware.DataSources.ElectricityMapsFree.csproj
index 8db8bc27b..310a6ed47 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/src/CarbonAware.DataSources.ElectricityMapsFree.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/src/CarbonAware.DataSources.ElectricityMapsFree.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
enable
enable
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/test/CarbonAware.DataSources.ElectricityMapsFree.Tests.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/test/CarbonAware.DataSources.ElectricityMapsFree.Tests.csproj
index 742632c0e..0375049af 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/test/CarbonAware.DataSources.ElectricityMapsFree.Tests.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.ElectricityMapsFree/test/CarbonAware.DataSources.ElectricityMapsFree.Tests.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
false
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/mock/CarbonAware.DataSources.Json.Mocks.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/mock/CarbonAware.DataSources.Json.Mocks.csproj
index a6f5e7724..898d7f162 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/mock/CarbonAware.DataSources.Json.Mocks.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/mock/CarbonAware.DataSources.Json.Mocks.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
false
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/src/CarbonAware.DataSources.Json.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/src/CarbonAware.DataSources.Json.csproj
index 7c9905d41..7bba6e61d 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/src/CarbonAware.DataSources.Json.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/src/CarbonAware.DataSources.Json.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
enable
enable
true
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/test/CarbonAware.DataSources.Json.Tests.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/test/CarbonAware.DataSources.Json.Tests.csproj
index 1addeb0dd..065ebeee5 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/test/CarbonAware.DataSources.Json.Tests.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.Json/test/CarbonAware.DataSources.Json.Tests.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
false
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.Registration/CarbonAware.DataSources.Registration.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.Registration/CarbonAware.DataSources.Registration.csproj
index d66d5e8c7..40ecc3ffe 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.Registration/CarbonAware.DataSources.Registration.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.Registration/CarbonAware.DataSources.Registration.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
true
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.Registration/Configuration/ServiceCollectionExtensions.cs b/src/CarbonAware.DataSources/CarbonAware.DataSources.Registration/Configuration/ServiceCollectionExtensions.cs
index 872ec4bae..7ceb5b012 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.Registration/Configuration/ServiceCollectionExtensions.cs
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.Registration/Configuration/ServiceCollectionExtensions.cs
@@ -5,6 +5,7 @@
using CarbonAware.DataSources.Json.Configuration;
using CarbonAware.DataSources.WattTime.Configuration;
using CarbonAware.Exceptions;
+using CarbonAware.Proxies.Cache;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -48,6 +49,8 @@ public static IServiceCollection AddDataSourceService(this IServiceCollection se
}
}
+ services.SetupCacheForEmissionsDataSource(configuration);
+
switch (forecastDataSource)
{
case DataSourceType.JSON:
@@ -96,4 +99,29 @@ private static DataSourceType GetDataSourceTypeFromValue(string? srcVal)
}
return result;
}
+
+ private static IServiceCollection SetupCacheForEmissionsDataSource(this IServiceCollection services, IConfiguration configuration)
+ {
+ var emissionsDataCache = configuration.EmissionsDataCache();
+ if(emissionsDataCache.Enabled){
+ var emissionsDataSourceDescriptor = services.SingleOrDefault(s => s.ServiceType == typeof(IEmissionsDataSource));
+ var type = emissionsDataSourceDescriptor!.ImplementationType;
+ if(type == null) return services;
+ services.Replace
+ (
+ ServiceDescriptor.Describe
+ (
+ typeof(IEmissionsDataSource),
+ serviceProvider =>
+ LatestEmissionsCache.CreateProxy
+ (
+ (IEmissionsDataSource)ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, type!),
+ emissionsDataCache
+ )!,
+ emissionsDataSourceDescriptor.Lifetime
+ )
+ );
+ }
+ return services;
+ }
}
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/mock/CarbonAware.DataSources.WattTime.Mocks.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/mock/CarbonAware.DataSources.WattTime.Mocks.csproj
index 1adda21a9..061c0cbab 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/mock/CarbonAware.DataSources.WattTime.Mocks.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/mock/CarbonAware.DataSources.WattTime.Mocks.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
false
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/CarbonAware.DataSources.WattTime.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/CarbonAware.DataSources.WattTime.csproj
index 20f6d7230..996b31ae4 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/CarbonAware.DataSources.WattTime.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/src/CarbonAware.DataSources.WattTime.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
true
diff --git a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/test/CarbonAware.DataSources.WattTime.Tests.csproj b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/test/CarbonAware.DataSources.WattTime.Tests.csproj
index 295fb782c..77f142861 100644
--- a/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/test/CarbonAware.DataSources.WattTime.Tests.csproj
+++ b/src/CarbonAware.DataSources/CarbonAware.DataSources.WattTime/test/CarbonAware.DataSources.WattTime.Tests.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
false
diff --git a/src/CarbonAware.LocationSources/src/CarbonAware.LocationSources.csproj b/src/CarbonAware.LocationSources/src/CarbonAware.LocationSources.csproj
index d05da871e..50211d65d 100644
--- a/src/CarbonAware.LocationSources/src/CarbonAware.LocationSources.csproj
+++ b/src/CarbonAware.LocationSources/src/CarbonAware.LocationSources.csproj
@@ -5,7 +5,7 @@
- net6.0
+ net8.0
enable
enable
true
diff --git a/src/CarbonAware.LocationSources/test/CarbonAware.LocationSources.Test.csproj b/src/CarbonAware.LocationSources/test/CarbonAware.LocationSources.Test.csproj
index 498ef5124..28dc668ef 100644
--- a/src/CarbonAware.LocationSources/test/CarbonAware.LocationSources.Test.csproj
+++ b/src/CarbonAware.LocationSources/test/CarbonAware.LocationSources.Test.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
false
enable
diff --git a/src/CarbonAware.Tools/CarbonAware.Tools.AWSRegionTestDataGenerator/CarbonAware.Tools.AWSRegionTestDataGenerator.csproj b/src/CarbonAware.Tools/CarbonAware.Tools.AWSRegionTestDataGenerator/CarbonAware.Tools.AWSRegionTestDataGenerator.csproj
index 203c50ed1..b5757b3b9 100644
--- a/src/CarbonAware.Tools/CarbonAware.Tools.AWSRegionTestDataGenerator/CarbonAware.Tools.AWSRegionTestDataGenerator.csproj
+++ b/src/CarbonAware.Tools/CarbonAware.Tools.AWSRegionTestDataGenerator/CarbonAware.Tools.AWSRegionTestDataGenerator.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net8.0
enable
enable
false
diff --git a/src/CarbonAware.Tools/CarbonAware.Tools.AzureRegionTestDataGenerator/CarbonAware.Tools.AzureRegionTestDataGenerator.csproj b/src/CarbonAware.Tools/CarbonAware.Tools.AzureRegionTestDataGenerator/CarbonAware.Tools.AzureRegionTestDataGenerator.csproj
index dc24429ba..ddfd8938d 100644
--- a/src/CarbonAware.Tools/CarbonAware.Tools.AzureRegionTestDataGenerator/CarbonAware.Tools.AzureRegionTestDataGenerator.csproj
+++ b/src/CarbonAware.Tools/CarbonAware.Tools.AzureRegionTestDataGenerator/CarbonAware.Tools.AzureRegionTestDataGenerator.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net8.0
enable
enable
false
diff --git a/src/CarbonAware.WebApi/src/.config/dotnet-tools.json b/src/CarbonAware.WebApi/src/.config/dotnet-tools.json
index 1a1607fe5..9a07f6919 100644
--- a/src/CarbonAware.WebApi/src/.config/dotnet-tools.json
+++ b/src/CarbonAware.WebApi/src/.config/dotnet-tools.json
@@ -3,10 +3,10 @@
"isRoot": true,
"tools": {
"swashbuckle.aspnetcore.cli": {
- "version": "6.2.3",
+ "version": "6.5.0",
"commands": [
"swagger"
]
}
}
-}
\ No newline at end of file
+}
diff --git a/src/CarbonAware.WebApi/src/CarbonAware.WebApi.csproj b/src/CarbonAware.WebApi/src/CarbonAware.WebApi.csproj
index 356e23088..de8904092 100644
--- a/src/CarbonAware.WebApi/src/CarbonAware.WebApi.csproj
+++ b/src/CarbonAware.WebApi/src/CarbonAware.WebApi.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
8d822819-8a1f-45e4-95fb-d4a9c3a9439f
@@ -15,13 +15,15 @@
+
+
-
-
-
-
-
+
+
+
+
+
@@ -45,4 +47,4 @@
-
\ No newline at end of file
+
diff --git a/src/CarbonAware.WebApi/src/Configuration/CarbonExporterConfiguration.cs b/src/CarbonAware.WebApi/src/Configuration/CarbonExporterConfiguration.cs
new file mode 100644
index 000000000..35f8a3bc1
--- /dev/null
+++ b/src/CarbonAware.WebApi/src/Configuration/CarbonExporterConfiguration.cs
@@ -0,0 +1,19 @@
+using Microsoft.Extensions.Configuration;
+
+namespace CarbonAware.WebApi.Configuration;
+
+
+internal class CarbonExporterConfiguration
+{
+ public const string Key = "CarbonExporter";
+
+ public int PeriodInHours { get; set; } = 24;
+
+ public void AssertValid()
+ {
+ if(PeriodInHours <= 0)
+ {
+ throw new ArgumentException($"The value of CarbonExporter.PeriodInHours must be greater than 0.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CarbonAware.WebApi/src/Configuration/ServiceCollectionExtensions.cs b/src/CarbonAware.WebApi/src/Configuration/ServiceCollectionExtensions.cs
index 48820a8bb..0c1458b23 100644
--- a/src/CarbonAware.WebApi/src/Configuration/ServiceCollectionExtensions.cs
+++ b/src/CarbonAware.WebApi/src/Configuration/ServiceCollectionExtensions.cs
@@ -1,4 +1,6 @@
using Microsoft.Extensions.Options;
+using CarbonAware.WebApi.Metrics;
+using OpenTelemetry.Metrics;
namespace CarbonAware.WebApi.Configuration;
@@ -30,6 +32,34 @@ public static void AddMonitoringAndTelemetry(this IServiceCollection services, I
// Can be extended in the future to support a different provider like Zipkin, Prometheus etc
}
+
+ }
+
+ public static IServiceCollection AddCarbonExporter(this IServiceCollection services, IConfiguration configuration)
+ {
+ var envVars = configuration?.GetSection(CarbonAwareVariablesConfiguration.Key).Get();
+ var enableCarbonExporter = envVars?.EnableCarbonExporter ?? false;
+ if(enableCarbonExporter){
+ var carbonExporter = configuration?.GetSection(CarbonExporterConfiguration.Key);
+ services.Configure(c =>
+ {
+ carbonExporter?.Bind(c);
+ });
+
+ services.AddOpenTelemetry()
+ .WithMetrics(meterProviderBuilder =>
+ meterProviderBuilder
+ .ConfigureServices(services =>
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ })
+ .ConfigureResource(rb => rb.AddDetector(sp => sp.GetRequiredService()))
+ .AddMeter(CarbonMetrics.MeterName)
+ .AddPrometheusExporter()
+ );
+ }
+ return services;
}
private static bool IsAppInsightsConfigured(IConfiguration? configuration, ILogger logger)
diff --git a/src/CarbonAware.WebApi/src/Dockerfile b/src/CarbonAware.WebApi/src/Dockerfile
index bc46714fe..068357d4a 100644
--- a/src/CarbonAware.WebApi/src/Dockerfile
+++ b/src/CarbonAware.WebApi/src/Dockerfile
@@ -1,22 +1,33 @@
-FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
+# For OpenAPI document
+FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS openapi-env
WORKDIR /app
+ENV DOTNET_ROLL_FORWARD LatestMajor
+COPY . ./
+RUN dotnet build CarbonAware.WebApi/src/CarbonAware.WebApi.csproj -o build
+WORKDIR /app/CarbonAware.WebApi/src
+RUN dotnet tool restore && \
+ dotnet tool run swagger tofile --output /app/build/swagger.yaml --yaml /app/build/CarbonAware.WebApi.dll v1
+
+# Builder
+FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
+ARG TARGETARCH
+WORKDIR /app
+ENV DOTNET_ROLL_FORWARD LatestMajor
# Copy everything from source
COPY . ./
# Use implicit restore to build and publish
-RUN dotnet publish CarbonAware.WebApi/src/CarbonAware.WebApi.csproj -c Release -o publish
-# Generate OpenAPI spec
-WORKDIR /app/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
+RUN dotnet publish CarbonAware.WebApi/src/CarbonAware.WebApi.csproj -a $TARGETARCH -o publish
+
# Build runtime image
-FROM mcr.microsoft.com/dotnet/aspnet:6.0
+FROM mcr.microsoft.com/dotnet/aspnet:8.0
# Install curl for health check
RUN apt-get update && \
apt-get install -y --no-install-recommends curl
-# Copy artifacts from build-env
+# Copy artifacts
WORKDIR /app
COPY --from=build-env /app/publish .
+RUN mkdir -p /app/wwwroot/api/v1
+COPY --from=openapi-env /app/build/swagger.yaml /app/wwwroot/api/v1/
ENTRYPOINT ["dotnet", "CarbonAware.WebApi.dll"]
diff --git a/src/CarbonAware.WebApi/src/Metrics/CarbonMetrics.cs b/src/CarbonAware.WebApi/src/Metrics/CarbonMetrics.cs
new file mode 100644
index 000000000..7efc244a1
--- /dev/null
+++ b/src/CarbonAware.WebApi/src/Metrics/CarbonMetrics.cs
@@ -0,0 +1,93 @@
+using CarbonAware.WebApi.Configuration;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Diagnostics.Metrics;
+using Microsoft.Extensions.Options;
+using GSF.CarbonAware.Handlers;
+using GSF.CarbonAware.Models;
+
+namespace CarbonAware.WebApi.Metrics;
+
+internal class CarbonMetrics : IDisposable
+{
+ private readonly ILogger _logger;
+
+ private readonly IOptionsMonitor _configurationMonitor;
+ private CarbonExporterConfiguration _configuration => this._configurationMonitor.CurrentValue;
+ internal const string MeterName = "Carbon.Aware.Metric";
+ internal const string ActivitySourceName = "Carbon.Aware.Metric";
+ internal const string GaugeName = "carbon.aware.intensity";
+
+ public ActivitySource ActivitySource { get; }
+
+ private readonly IEmissionsHandler _emissionsHandler;
+ private readonly ILocationHandler _locationHandler;
+
+ private readonly Meter _meter;
+
+ private readonly ConcurrentBag _locations = new ConcurrentBag();
+ private readonly IDictionary> _gauges = new ConcurrentDictionary>();
+
+ public CarbonMetrics(IMeterFactory meterFactory, IOptionsMonitor monitor, ILogger logger, IEmissionsHandler emissionsHandler, ILocationHandler locationHandler)
+ {
+ _emissionsHandler = emissionsHandler ?? throw new ArgumentNullException(nameof(emissionsHandler));
+ _locationHandler = locationHandler ?? throw new ArgumentNullException(nameof(locationHandler));
+ _configurationMonitor = monitor;
+ _logger = logger;
+ _configuration.AssertValid();
+
+ string? version = typeof(CarbonMetrics).Assembly.GetName().Version?.ToString();
+ ActivitySource = new ActivitySource(ActivitySourceName, version);
+ _meter = meterFactory.Create(MeterName, version);
+ InitLocations();
+ }
+
+ private void InitLocations()
+ {
+ // initialize locations and guages
+ _locations.Clear();
+ _gauges.Clear();
+
+ // load locations
+ Task> locationsTask = _locationHandler.GetLocationsAsync();
+ try
+ {
+ locationsTask.Result.Keys.ToList().ForEach(d => _locations.Add(d));
+ // create guages for each locaton
+ foreach(var loc in _locations){
+ _gauges[loc] = _meter.CreateObservableGauge(CarbonMetrics.GaugeName, () => GetIntensity(loc));
+ }
+ }
+ catch(Exception ex)
+ {
+ _logger.LogWarning(ex.Message);
+ _locations.Clear();
+ _gauges.Clear();
+ }
+ }
+
+ public void Dispose()
+ {
+ _meter.Dispose();
+ ActivitySource.Dispose();
+ }
+
+ private Measurement GetIntensity(string location){
+ try
+ {
+ var end = DateTimeOffset.UtcNow;
+ var start = end.AddHours(-1*_configuration.PeriodInHours);
+ var intensity = _emissionsHandler.GetEmissionsDataAsync(location, start, end)
+ .Result
+ .MaxBy(d => d.Time)!
+ .Rating;
+ var measurement = new Measurement(intensity, new TagList(){{"location", location}});
+ return measurement;
+ }
+ catch(Exception ex)
+ {
+ _logger.LogWarning(ex.Message);
+ return new Measurement(0, new TagList(){{"location", location}});
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CarbonAware.WebApi/src/Metrics/MetricsResourceDetector.cs b/src/CarbonAware.WebApi/src/Metrics/MetricsResourceDetector.cs
new file mode 100644
index 000000000..0f8398c09
--- /dev/null
+++ b/src/CarbonAware.WebApi/src/Metrics/MetricsResourceDetector.cs
@@ -0,0 +1,17 @@
+using CarbonAware.WebApi.Metrics;
+using OpenTelemetry.Resources;
+
+internal class MetricsResourceDetector : IResourceDetector
+{
+ private readonly CarbonMetrics _carbonMetrics;
+
+ public MetricsResourceDetector(CarbonMetrics carbonMetrics)
+ {
+ _carbonMetrics = carbonMetrics;
+ }
+
+ public Resource Detect()
+ {
+ return ResourceBuilder.CreateEmpty().Build();
+ }
+}
\ No newline at end of file
diff --git a/src/CarbonAware.WebApi/src/Program.cs b/src/CarbonAware.WebApi/src/Program.cs
index 9a70c15ab..b027ad12f 100644
--- a/src/CarbonAware.WebApi/src/Program.cs
+++ b/src/CarbonAware.WebApi/src/Program.cs
@@ -7,6 +7,7 @@
using Microsoft.OpenApi.Models;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
+using OpenTelemetry.Metrics;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
@@ -16,17 +17,16 @@
var builder = WebApplication.CreateBuilder(args);
-builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
-{
- tracerProviderBuilder
+builder.Services.AddOpenTelemetry()
+ .WithTracing(tracerProviderBuilder =>
+ tracerProviderBuilder
.AddConsoleExporter()
.AddSource(serviceName)
.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService(serviceName: serviceName, serviceVersion: serviceVersion))
.AddHttpClientInstrumentation()
- .AddAspNetCoreInstrumentation();
-});
+ .AddAspNetCoreInstrumentation());
// Add services to the container.
builder.Services.AddControllers(options =>
@@ -70,6 +70,8 @@
builder.Services.AddMonitoringAndTelemetry(builder.Configuration);
+builder.Services.AddCarbonExporter(builder.Configuration);
+
builder.Services.AddSwaggerGen(c => {
c.MapType(() => new OpenApiSchema { Type = "string", Format = "time-span" });
});
@@ -112,9 +114,14 @@
app.MapHealthChecks("/health");
+var enableCarbonExporter = config?.EnableCarbonExporter ?? false;
+if(enableCarbonExporter){
+ app.UseOpenTelemetryPrometheusScrapingEndpoint();
+}
+
app.Run();
-// Please view https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0#basic-tests-with-the-default-webapplicationfactory
+// Please view https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-8.0#basic-tests-with-the-default-webapplicationfactory
// This line is needed to allow for Integration Testing
public partial class Program { }
diff --git a/src/CarbonAware.WebApi/test/integrationTests/CarbonAware.WebApi.IntegrationTests.csproj b/src/CarbonAware.WebApi/test/integrationTests/CarbonAware.WebApi.IntegrationTests.csproj
index df1ed5ada..fd9179089 100644
--- a/src/CarbonAware.WebApi/test/integrationTests/CarbonAware.WebApi.IntegrationTests.csproj
+++ b/src/CarbonAware.WebApi/test/integrationTests/CarbonAware.WebApi.IntegrationTests.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
enable
false
enable
diff --git a/src/CarbonAware.WebApi/test/integrationTests/CarbonAwareControllerTests.cs b/src/CarbonAware.WebApi/test/integrationTests/CarbonAwareControllerTests.cs
index d4954bbd6..975d00d04 100644
--- a/src/CarbonAware.WebApi/test/integrationTests/CarbonAwareControllerTests.cs
+++ b/src/CarbonAware.WebApi/test/integrationTests/CarbonAwareControllerTests.cs
@@ -20,6 +20,7 @@ class CarbonAwareControllerTests : IntegrationTestingBase
{
private readonly string healthURI = "/health";
private readonly string fakeURI = "/fake-endpoint";
+ private readonly string metricsURI = "/metrics";
private readonly string bestLocationsURI = "/emissions/bylocations/best";
private readonly string bylocationsURI = "/emissions/bylocations";
private readonly string bylocationURI = "/emissions/bylocation";
@@ -47,6 +48,16 @@ public async Task FakeEndPoint_ReturnsNotFound()
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
+ [Test]
+ public async Task MetricsEndPoint_ReturnsOK()
+ {
+ //Use client to get endpoint
+ var result = await _client.GetAsync(metricsURI);
+ Assert.That(result, Is.Not.Null);
+ Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+ }
+
+
//ISO8601: YYYY-MM-DD
[TestCase("2022-1-1T04:05:06Z", "2022-1-2T04:05:06Z", "eastus", nameof(ByLocationURI_ReturnsOK) + "0")]
[TestCase("2021-12-25T00:00:00+06:00", "2021-12-26T00:00:00+06:00", "westus", nameof(ByLocationURI_ReturnsOK) + "1")]
diff --git a/src/CarbonAware.WebApi/test/integrationTests/IntegrationTestingBase.cs b/src/CarbonAware.WebApi/test/integrationTests/IntegrationTestingBase.cs
index 5dd7310b5..8d6d1c46c 100644
--- a/src/CarbonAware.WebApi/test/integrationTests/IntegrationTestingBase.cs
+++ b/src/CarbonAware.WebApi/test/integrationTests/IntegrationTestingBase.cs
@@ -21,6 +21,7 @@ internal abstract class IntegrationTestingBase
internal DataSourceType _dataSource;
internal string? _emissionsDataSourceEnv;
internal string? _forecastDataSourceEnv;
+ internal string? _enableCarbonExporterEnv;
internal WebApplicationFactory _factory;
protected HttpClient _client;
internal IDataSourceMocker _dataSourceMocker;
@@ -33,6 +34,7 @@ public IntegrationTestingBase(DataSourceType dataSource)
_dataSource = dataSource;
_emissionsDataSourceEnv = null;
_forecastDataSourceEnv = null;
+ _enableCarbonExporterEnv = null;
_factory = new WebApplicationFactory();
}
@@ -77,6 +79,8 @@ public void Setup()
{
_emissionsDataSourceEnv = Environment.GetEnvironmentVariable("DataSources__EmissionsDataSource");
_forecastDataSourceEnv = Environment.GetEnvironmentVariable("DataSources__ForecastDataSource");
+ _enableCarbonExporterEnv = Environment.GetEnvironmentVariable("CarbonAwareVars__EnableCarbonExporter");
+ Environment.SetEnvironmentVariable("CarbonAwareVars__EnableCarbonExporter", "true");
//Switch between different data sources as needed
//Each datasource should have an accompanying DataSourceMocker that will perform setup activities
switch (_dataSource)
@@ -153,5 +157,6 @@ public void TearDown()
_dataSourceMocker?.Dispose();
Environment.SetEnvironmentVariable("DataSources__EmissionsDataSource", _emissionsDataSourceEnv);
Environment.SetEnvironmentVariable("DataSources__ForecastDataSource", _forecastDataSourceEnv);
+ Environment.SetEnvironmentVariable("CarbonAwareVars__EnableCarbonMetrics", _enableCarbonExporterEnv);
}
}
\ No newline at end of file
diff --git a/src/CarbonAware.WebApi/test/unitTests/CarbonAware.WebApi.UnitTests.csproj b/src/CarbonAware.WebApi/test/unitTests/CarbonAware.WebApi.UnitTests.csproj
index a1859042d..f630cd0f8 100644
--- a/src/CarbonAware.WebApi/test/unitTests/CarbonAware.WebApi.UnitTests.csproj
+++ b/src/CarbonAware.WebApi/test/unitTests/CarbonAware.WebApi.UnitTests.csproj
@@ -1,6 +1,6 @@
- net6.0
+ net8.0
enable
false
enable
diff --git a/src/CarbonAware.WebApi/test/unitTests/Configuration/CarbonExporterConfigurationTests.cs b/src/CarbonAware.WebApi/test/unitTests/Configuration/CarbonExporterConfigurationTests.cs
new file mode 100644
index 000000000..af29280f0
--- /dev/null
+++ b/src/CarbonAware.WebApi/test/unitTests/Configuration/CarbonExporterConfigurationTests.cs
@@ -0,0 +1,30 @@
+using CarbonAware.WebApi.Configuration;
+using NUnit.Framework;
+
+namespace CarbonAware.WepApi.UnitTests;
+
+class CarbonExporterConfigurationTests
+{
+ [TestCase(12, TestName = "AssertValid: PeriodInHours greater than 0")]
+ [TestCase(1, TestName = "AssertValid: PeriodInHours equals to 1")]
+ public void AssertValid_PeriodInHoursGreaterThanZero_DoesNotThrowException(int periodInHours)
+ {
+ CarbonExporterConfiguration carbonExporterConfiguration = new CarbonExporterConfiguration()
+ {
+ PeriodInHours = periodInHours
+ };
+
+ Assert.DoesNotThrow(() => carbonExporterConfiguration.AssertValid());
+ }
+
+ [TestCase(0, TestName = "AssertValid: PeriodInHours equals to 0")]
+ public void AssertValid_PeriodInHoursGreaterThanZero_ThrowException(int periodInHours)
+ {
+ CarbonExporterConfiguration carbonExporterConfiguration = new CarbonExporterConfiguration()
+ {
+ PeriodInHours = periodInHours
+ };
+
+ Assert.Throws(() => carbonExporterConfiguration.AssertValid());
+ }
+}
\ No newline at end of file
diff --git a/src/CarbonAware.WebApi/test/unitTests/Configuration/ServiceCollectionExtensionsTests.cs b/src/CarbonAware.WebApi/test/unitTests/Configuration/ServiceCollectionExtensionsTests.cs
index 2e8edd87c..f675160a2 100644
--- a/src/CarbonAware.WebApi/test/unitTests/Configuration/ServiceCollectionExtensionsTests.cs
+++ b/src/CarbonAware.WebApi/test/unitTests/Configuration/ServiceCollectionExtensionsTests.cs
@@ -82,6 +82,61 @@ public void AddMonitoringAndTelemetry_DoesNotAddServices_WithoutTelemetryProvide
Assert.That(services.Count, Is.EqualTo(0));
}
+ [Test]
+ public void AddCarbonExporter_AddsServices_IsEnabledInConfiguration()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ var inMemorySettings = new Dictionary
+ {
+ { "CarbonAwareVars:EnableCarbonExporter", "true" }
+ };
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(inMemorySettings)
+ .Build();
+
+ // Act & Assert
+ Assert.DoesNotThrow(() => services.AddCarbonExporter(configuration));
+ Assert.That(services.Count, Is.GreaterThan(0));
+ }
+
+ [Test]
+ public void AddCarbonExporter_DoesNotAddServices_IsDisabledInConfiguration()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ var inMemorySettings = new Dictionary
+ {
+ { "CarbonAwareVars:EnableCarbonExporter", "false" }
+ };
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(inMemorySettings)
+ .Build();
+
+ // Act & Assert
+ Assert.DoesNotThrow(() => services.AddCarbonExporter(configuration));
+ Assert.That(services.Count, Is.EqualTo(0));
+ }
+
+
+ [Test]
+ public void AddCarbonExporter_DoesNotAddServices_WithoutConfiguration()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+
+ var inMemorySettings = new Dictionary{};
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(inMemorySettings)
+ .Build();
+
+ // Act & Assert
+ Assert.DoesNotThrow(() => services.AddCarbonExporter(configuration));
+ Assert.That(services.Count, Is.EqualTo(0));
+ }
+
[Test]
public void CreateConsoleLogger_ReturnsILogger()
{
diff --git a/src/CarbonAware/src/CarbonAware.csproj b/src/CarbonAware/src/CarbonAware.csproj
index d691e0f80..d04ce3a78 100644
--- a/src/CarbonAware/src/CarbonAware.csproj
+++ b/src/CarbonAware/src/CarbonAware.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
true
diff --git a/src/CarbonAware/src/CarbonAwareVariablesConfiguration.cs b/src/CarbonAware/src/CarbonAwareVariablesConfiguration.cs
index 13eea1dcb..ae37c78aa 100644
--- a/src/CarbonAware/src/CarbonAwareVariablesConfiguration.cs
+++ b/src/CarbonAware/src/CarbonAwareVariablesConfiguration.cs
@@ -36,6 +36,8 @@ internal class CarbonAwareVariablesConfiguration
public string TelemetryProvider { get; set; }
+ public Boolean EnableCarbonExporter { get;set; }
+
public Boolean VerboseApi {get; set;}
}
diff --git a/src/CarbonAware/src/Configuration/EmissionsDataCacheConfiguration.cs b/src/CarbonAware/src/Configuration/EmissionsDataCacheConfiguration.cs
new file mode 100644
index 000000000..9eec7a89b
--- /dev/null
+++ b/src/CarbonAware/src/Configuration/EmissionsDataCacheConfiguration.cs
@@ -0,0 +1,20 @@
+using Microsoft.Extensions.Configuration;
+
+namespace CarbonAware.Configuration;
+
+internal class EmissionsDataCacheConfiguration
+{
+ public const string Key = "EmissionsDataCache";
+
+ public bool Enabled { get; set; } = false;
+
+ public int ExpirationMin { get; set; } = 0;
+
+ public void AssertValid()
+ {
+ if(Enabled & ExpirationMin <= 0)
+ {
+ throw new ArgumentException($"Expiration period for data cache value must be greater than 0.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CarbonAware/src/Configuration/EmissionsDataCacheConfigurationExtensions.cs b/src/CarbonAware/src/Configuration/EmissionsDataCacheConfigurationExtensions.cs
new file mode 100644
index 000000000..76e1ed345
--- /dev/null
+++ b/src/CarbonAware/src/Configuration/EmissionsDataCacheConfigurationExtensions.cs
@@ -0,0 +1,14 @@
+using Microsoft.Extensions.Configuration;
+
+namespace CarbonAware.Configuration;
+
+internal static class EmissionsDataCacheConfigurationExtensions
+{
+ public static EmissionsDataCacheConfiguration EmissionsDataCache(this IConfiguration configuration)
+ {
+ var dataCache = configuration.GetSection(EmissionsDataCacheConfiguration.Key).Get() ?? new EmissionsDataCacheConfiguration();
+ dataCache.AssertValid();
+
+ return dataCache;
+ }
+}
\ No newline at end of file
diff --git a/src/CarbonAware/src/Proxies/Cache/LatestEmissionsCache.cs b/src/CarbonAware/src/Proxies/Cache/LatestEmissionsCache.cs
new file mode 100644
index 000000000..116f44db6
--- /dev/null
+++ b/src/CarbonAware/src/Proxies/Cache/LatestEmissionsCache.cs
@@ -0,0 +1,150 @@
+using CarbonAware.Configuration;
+using CarbonAware.Interfaces;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Reflection;
+
+namespace CarbonAware.Proxies.Cache;
+
+///
+/// A proxy class for IEmissionsDataSource to cache EmissionsData.
+/// This class caches data which is queried latestly for each Location.
+/// The cached value are used if it satisfies all of the conditions listed below.
+///
+/// -
+/// it is not exceeded the expiration period
+///
+/// -
+/// the name of the location is match with the query
+///
+/// -
+/// the time is match with the query
+///
+///
+///
+/// The target class of the proxy
+class LatestEmissionsCache : DispatchProxy where T : class, IEmissionsDataSource
+{
+
+ private IEmissionsDataSource? _target { get; set; }
+
+ readonly private ConcurrentDictionary)> _cache =
+ new ConcurrentDictionary)>();
+
+ private EmissionsDataCacheConfiguration? _config { get; set; }
+
+ public static T? CreateProxy(T target, EmissionsDataCacheConfiguration config)
+ {
+ var proxy = Create>() as LatestEmissionsCache;
+ proxy!._target = target;
+ proxy._config = config;
+ return proxy as T;
+ }
+
+ protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
+ {
+ if(targetMethod!.Name.Equals("GetCarbonIntensityAsync") && args![0]!.GetType() == typeof(Location))
+ {
+ var location = (Location?)args[0];
+ var start = (DateTimeOffset)args[1]!;
+ var end = (DateTimeOffset)args[2]!;
+ return ProxyGetCarbonIntensityAsync(targetMethod, location!, start, end);
+ } else if(targetMethod.Name.Equals("GetCarbonIntensityAsync") && args![0]!.GetType().GetInterfaces().Contains(typeof(IEnumerable)))
+ {
+ var locations = (IEnumerable?)args[0];
+ var start = (DateTimeOffset)args[1]!;
+ var end = (DateTimeOffset)args[2]!;
+ return ProxyGetCarbonIntensityAsync(targetMethod, locations!, start, end);
+ }
+ return targetMethod.Invoke(_target, args);
+ }
+
+ private Task> ProxyGetCarbonIntensityAsync(MethodInfo? original, Location location, DateTimeOffset periodStartTime, DateTimeOffset periodEndTime)
+ {
+ var cachedData = GetCachedData(location, periodStartTime, periodEndTime);
+ if(cachedData.Count() != 0)
+ {
+ return Task.FromResult(cachedData);
+ }
+
+ object[] args = {location, periodStartTime, periodEndTime};
+ var resultFromOriginal = (Task>?)original!.Invoke(_target, args);
+
+ if(string.IsNullOrEmpty(location.Name))
+ {
+ return resultFromOriginal!;
+ } else
+ {
+ var expiration = DateTimeOffset.UtcNow.AddMinutes(_config!.ExpirationMin);
+ var result = resultFromOriginal!.ContinueWith
+ (
+ c =>
+ {
+ _cache.AddOrUpdate(location.Name, (expiration, c.Result), (_, _) => (expiration, c.Result));
+ return c.Result;
+ }
+ );
+ return result;
+ }
+ }
+
+ private Task> ProxyGetCarbonIntensityAsync(MethodInfo? original, IEnumerable locations, DateTimeOffset periodStartTime, DateTimeOffset periodEndTime)
+ {
+ var cachedData = Enumerable.Empty();
+ var useCacheFor = Enumerable.Empty();
+ foreach (var location in locations)
+ {
+ var cachedDataForLocation = GetCachedData(location, periodStartTime, periodEndTime);
+ if(cachedDataForLocation.Count() != 0)
+ {
+ cachedData = cachedData.Union(cachedDataForLocation);
+ useCacheFor = useCacheFor.Append(location.Name);
+ }
+ }
+
+ var locationsForQuery = locations.Where(l => string.IsNullOrEmpty(l.Name) || !useCacheFor.Contains(l.Name));
+ if(locationsForQuery.Count() == 0){
+ return Task.FromResult(cachedData);
+ }
+
+ object[] args = {locationsForQuery, periodStartTime, periodEndTime};
+ var resultFromOriginal = (Task>?)original!.Invoke(_target, args);
+
+ var expiration = DateTimeOffset.UtcNow.AddMinutes(_config!.ExpirationMin);
+ var result = resultFromOriginal!.ContinueWith
+ (
+ c =>
+ {
+ foreach (var location in locationsForQuery)
+ {
+ if(!string.IsNullOrEmpty(location.Name))
+ {
+ var data = c.Result.Where(d => d.Location.Equals(location.Name));
+ _cache.AddOrUpdate(location.Name, (expiration, data), (_, _) => (expiration, data));
+ }
+ }
+ return c.Result.Union(cachedData);
+ }
+ );
+ return result;
+ }
+
+ private IEnumerable GetCachedData(Location location, DateTimeOffset periodStartTime, DateTimeOffset periodEndTime)
+ {
+ if(!string.IsNullOrEmpty(location.Name) && _cache.ContainsKey(location.Name))
+ {
+ var cachedValue = _cache.GetValueOrDefault(location.Name);
+
+ // check expiration
+ if(cachedValue.Item1.CompareTo(DateTimeOffset.UtcNow) > 0)
+ {
+ IEnumerable emissions = cachedValue.Item2.Where(d => d.TimeBetween(periodStartTime, periodEndTime));
+ if(emissions.Count() != 0)
+ {
+ return emissions;
+ }
+ }
+ }
+ return Enumerable.Empty();
+ }
+}
\ No newline at end of file
diff --git a/src/CarbonAware/test/CarbonAware.Tests.csproj b/src/CarbonAware/test/CarbonAware.Tests.csproj
index c11002e82..39071e6e9 100644
--- a/src/CarbonAware/test/CarbonAware.Tests.csproj
+++ b/src/CarbonAware/test/CarbonAware.Tests.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
false
@@ -13,6 +13,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/CarbonAware/test/Configuration/EmissionsDataCacheConfigurationTests.cs b/src/CarbonAware/test/Configuration/EmissionsDataCacheConfigurationTests.cs
new file mode 100644
index 000000000..5480204de
--- /dev/null
+++ b/src/CarbonAware/test/Configuration/EmissionsDataCacheConfigurationTests.cs
@@ -0,0 +1,31 @@
+using CarbonAware.Configuration;
+
+namespace CarbonAware.Tests.Configuration;
+
+class EmissionsDataCacheConfigurationTests
+{
+ [TestCase(10, TestName = "AssertValid: ExpirationMin greater than 0")]
+ public void AssertValid_ExpirationMinGreaterThanOrEqualsToZero_DoesNotThrowException(int expirationMin)
+ {
+ EmissionsDataCacheConfiguration emissionsDataCacheConfig = new EmissionsDataCacheConfiguration()
+ {
+ Enabled = true,
+ ExpirationMin = expirationMin
+ };
+
+ Assert.DoesNotThrow(() => emissionsDataCacheConfig.AssertValid());
+ }
+
+ [TestCase(0, TestName = "AssertValid: ExpirationMin equals to 0")]
+ [TestCase(-10, TestName = "AssertValid: ExpirationMin less than 0")]
+ public void AssertValid_ExpirationMinLessThanZero_ThrowException(int expirationMin)
+ {
+ EmissionsDataCacheConfiguration emissionsDataCacheConfig = new EmissionsDataCacheConfiguration()
+ {
+ Enabled = true,
+ ExpirationMin = expirationMin
+ };
+
+ Assert.Throws(() => emissionsDataCacheConfig.AssertValid());
+ }
+}
diff --git a/src/CarbonAware/test/Proxy/Cache/LatestEmissionsCacheTests.cs b/src/CarbonAware/test/Proxy/Cache/LatestEmissionsCacheTests.cs
new file mode 100644
index 000000000..cd806e91e
--- /dev/null
+++ b/src/CarbonAware/test/Proxy/Cache/LatestEmissionsCacheTests.cs
@@ -0,0 +1,231 @@
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using CarbonAware.Configuration;
+using CarbonAware.Interfaces;
+using CarbonAware.Model;
+using Moq;
+
+namespace CarbonAware.Proxies.Cache;
+
+class LatestEmissionsCacheTests
+{
+
+ Mock? _mock;
+
+ IEmissionsDataSource? _dataSource;
+
+ IDictionary? _dateTimes;
+
+ IDictionary? _emissions;
+
+ IDictionary? _locations;
+
+ [Test]
+ public void GetCarbonIntensityAsync_ForSingleLocation_UseCachedDataForSameQuery()
+ {
+ var config = new EmissionsDataCacheConfiguration();
+ config.Enabled = true;
+ config.ExpirationMin = 10;
+
+ var dataSource = LatestEmissionsCache.CreateProxy(_dataSource!, config);
+ var resultFromDataSource = dataSource!.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+ var resultFromCache = dataSource!.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+
+ // The results are same
+ Assert.AreEqual(resultFromDataSource, resultFromCache);
+ // The access to the data source occurs just once
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["firstDay"], _dateTimes["secondDay"]), Moq.Times.Once);
+ }
+
+ [Test]
+ public void GetCarbonIntensityAsync_ForSingleLocation_UsePartOfCachedData()
+ {
+ var config = new EmissionsDataCacheConfiguration();
+ config.Enabled = true;
+ config.ExpirationMin = 10;
+
+ var dataSource = LatestEmissionsCache.CreateProxy(_dataSource!, config);
+ var resultFromDataSource = dataSource!.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["firstDay"], _dateTimes["thirdDay"]).Result;
+ var resultFromCache = dataSource!.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+
+ // The result for the second query is a part of the first one
+ Assert.AreEqual(resultFromDataSource.Count(), 2);
+ Assert.AreEqual(resultFromCache.Count(), 1);
+ // The second query does not access to tha data source
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["firstDay"], _dateTimes["secondDay"]), Moq.Times.Never);
+ }
+
+ [Test]
+ public void GetCarbonIntensityAsync_ForSingleLocation_AccessToDataSourceIfCachedDataIsNotMached()
+ {
+ var config = new EmissionsDataCacheConfiguration();
+ config.Enabled = true;
+ config.ExpirationMin = 10;
+
+ var dataSource = LatestEmissionsCache.CreateProxy(_dataSource!, config);
+ var resultOfFirst = dataSource!.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+ var resultOfSecond = dataSource!.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["secondDay"], _dateTimes["thirdDay"]).Result;
+
+ Assert.Contains(_emissions!["eastus-firstDay"], resultOfFirst.ToList());
+ Assert.Contains(_emissions["eastus-secondDay"], resultOfSecond.ToList());
+ // Both queries access to tha data source
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["firstDay"], _dateTimes["secondDay"]), Moq.Times.Once);
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(_locations!["eastus"], _dateTimes!["secondDay"], _dateTimes["thirdDay"]), Moq.Times.Once);
+ }
+
+ [Test]
+ public void GetCarbonIntensityAsync_ForSingleLocation_AccessToDataSourceIfLocationNameIsEmpty()
+ {
+ var config = new EmissionsDataCacheConfiguration();
+ config.Enabled = true;
+ config.ExpirationMin = 10;
+
+ var dataSource = LatestEmissionsCache.CreateProxy(_dataSource!, config);
+ var resultOfFirst = dataSource!.GetCarbonIntensityAsync(_locations!["coordinate"], _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+ var resultOfSecond = dataSource!.GetCarbonIntensityAsync(_locations!["coordinate"], _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+
+ Assert.AreEqual(resultOfFirst, resultOfSecond);
+ // Both queries access to tha data source
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(_locations!["coordinate"], _dateTimes!["firstDay"], _dateTimes["secondDay"]), Moq.Times.Exactly(2));
+ }
+
+ [Test]
+ public void GetCarbonIntensityAsync_ForMultiLocation_UseCachedDataForSameQuery()
+ {
+ var config = new EmissionsDataCacheConfiguration();
+ config.Enabled = true;
+ config.ExpirationMin = 10;
+
+ var dataSource = LatestEmissionsCache.CreateProxy(_dataSource!, config);
+ var resultFromDataSource = dataSource!.GetCarbonIntensityAsync(new List{_locations!["eastus"], _locations["westus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+ var resultFromCache = dataSource!.GetCarbonIntensityAsync(new List{_locations!["eastus"], _locations["westus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+
+ // The results are same
+ Assert.AreEqual(resultFromDataSource, resultFromCache);
+ // The access to the data source occurs just once
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(new List{_locations!["eastus"], _locations["westus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]), Moq.Times.Once);
+ }
+
+ [Test]
+ public void GetCarbonIntensityAsync_ForMultiLocation_UsePartOfCachedData()
+ {
+ var config = new EmissionsDataCacheConfiguration();
+ config.Enabled = true;
+ config.ExpirationMin = 10;
+
+ var dataSource = LatestEmissionsCache.CreateProxy(_dataSource!, config);
+ var resultFromDataSource = dataSource!.GetCarbonIntensityAsync(new List{_locations!["eastus"], _locations["westus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+ var resultFromCache = dataSource!.GetCarbonIntensityAsync(new List{_locations!["eastus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+
+ // The result for the second query is a part of the first one
+ Assert.AreEqual(resultFromDataSource.Count(), 2);
+ Assert.AreEqual(resultFromCache.Count(), 1);
+ // The access to the data source occurs just once
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(new List{_locations!["eastus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]), Moq.Times.Never);
+ }
+
+ [Test]
+ public void GetCarbonIntensityAsync_ForMultiLocation_UsePartOfCachedData2()
+ {
+ var config = new EmissionsDataCacheConfiguration();
+ config.Enabled = true;
+ config.ExpirationMin = 10;
+
+ var dataSource = LatestEmissionsCache.CreateProxy(_dataSource!, config);
+ var resultForFirst = dataSource!.GetCarbonIntensityAsync(new List{_locations!["eastus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+ var resultForSecond = dataSource!.GetCarbonIntensityAsync(new List{_locations!["eastus"], _locations["westus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]).Result;
+
+ Assert.AreEqual(resultForFirst.Count(), 1);
+ Assert.AreEqual(resultForSecond.Count(), 2);
+ // the first query
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(new List{_locations!["eastus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]), Moq.Times.Once);
+ // the second query does not contain "eastus" because the data for "eastus" have been already cached
+ _mock!.Verify(ds => ds.GetCarbonIntensityAsync(new List{_locations!["westus"]}, _dateTimes!["firstDay"], _dateTimes["secondDay"]), Moq.Times.Once);
+ }
+
+ [SetUp]
+ public void Setup()
+ {
+ _mock!.Invocations.Clear();
+ }
+
+ [OneTimeSetUp]
+ public void SetupDataAndMock()
+ {
+ // Crate test data
+ _dateTimes = new Dictionary
+ {
+ {"firstDay", new DateTimeOffset(2021, 11, 16, 0, 0, 0, TimeSpan.Zero)},
+ {"secondDay", new DateTimeOffset(2021, 11, 17, 0, 0, 0, TimeSpan.Zero)},
+ {"thirdDay", new DateTimeOffset(2021, 11, 18, 0, 0, 0, TimeSpan.Zero)}
+ };
+
+ var location1 = new Location();
+ location1.Name = "eastus";
+ var location2 = new Location();
+ location2.Name = "westus";
+ var location3 = new Location();
+ location3.Latitude = 0;
+ location3.Longitude = 0;
+ _locations = new Dictionary
+ {
+ {"eastus", location1},
+ {"westus", location2},
+ {"coordinate", location3}
+ };
+
+ var emissions1 = new EmissionsData();
+ emissions1.Location = "eastus";
+ emissions1.Time = new DateTimeOffset(2021, 11, 16, 0, 55, 0, TimeSpan.Zero);
+ var emissions2 = new EmissionsData();
+ emissions2.Location = "eastus";
+ emissions2.Time = new DateTimeOffset(2021, 11, 17, 0, 55, 0, TimeSpan.Zero);
+ var emissions3 = new EmissionsData();
+ emissions3.Location = "westus";
+ emissions3.Time = new DateTimeOffset(2021, 11, 16, 0, 55, 0, TimeSpan.Zero);
+ var emissions4 = new EmissionsData();
+ emissions4.Location = "";
+ emissions4.Time = new DateTimeOffset(2021, 11, 16, 0, 55, 0, TimeSpan.Zero);
+ _emissions = new Dictionary
+ {
+ {"eastus-firstDay", emissions1},
+ {"eastus-secondDay", emissions2},
+ {"westus-firstDay", emissions3},
+ {"coordinate-firstDay", emissions4}
+ };
+
+ // setup a mock
+ _mock = new Mock();
+
+ // setup for GetCarbonIntensityAsync(Location, DateTimeOffset, DateTimeOffset)
+ _mock.Setup(ds =>
+ ds.GetCarbonIntensityAsync(_locations["eastus"], _dateTimes["firstDay"], _dateTimes["secondDay"]))
+ .Returns(Task.FromResult((IEnumerable)new List{_emissions["eastus-firstDay"]}));
+ _mock.Setup(ds =>
+ ds.GetCarbonIntensityAsync(_locations["eastus"], _dateTimes["secondDay"], _dateTimes["thirdDay"]))
+ .Returns(Task.FromResult((IEnumerable)new List{_emissions["eastus-secondDay"]}));
+ _mock.Setup(ds =>
+ ds.GetCarbonIntensityAsync(_locations["eastus"], _dateTimes["firstDay"], _dateTimes["thirdDay"]))
+ .Returns(Task.FromResult((IEnumerable)new List{_emissions["eastus-firstDay"], _emissions["eastus-secondDay"]}));
+ _mock.Setup(ds =>
+ ds.GetCarbonIntensityAsync(_locations["westus"], _dateTimes["firstDay"], _dateTimes["secondDay"]))
+ .Returns(Task.FromResult((IEnumerable)new List{_emissions["westus-firstDay"]}));
+ _mock.Setup(ds =>
+ ds.GetCarbonIntensityAsync(_locations["coordinate"], _dateTimes["firstDay"], _dateTimes["secondDay"]))
+ .Returns(Task.FromResult((IEnumerable)new List{_emissions["coordinate-firstDay"]}));
+
+ // setup for GetCarbonIntensityAsync(IEnumerable, DateTimeOffset, DateTimeOffset)
+ _mock.Setup(ds =>
+ ds.GetCarbonIntensityAsync(new List{_locations["eastus"], _locations["westus"]}, _dateTimes["firstDay"], _dateTimes["secondDay"]))
+ .Returns(Task.FromResult((IEnumerable)new List{_emissions["eastus-firstDay"], _emissions["westus-firstDay"]}));
+ _mock.Setup(ds =>
+ ds.GetCarbonIntensityAsync(new List{_locations["eastus"]}, _dateTimes["firstDay"], _dateTimes["secondDay"]))
+ .Returns(Task.FromResult((IEnumerable)new List{_emissions["eastus-firstDay"]}));
+ _mock.Setup(ds =>
+ ds.GetCarbonIntensityAsync(new List{_locations["westus"]}, _dateTimes["firstDay"], _dateTimes["secondDay"]))
+ .Returns(Task.FromResult((IEnumerable)new List{_emissions["westus-firstDay"]}));
+
+ _dataSource = _mock.Object;
+ }
+
+}
diff --git a/src/GSF.CarbonAware/src/Configuration/ServiceCollectionExtensions.cs b/src/GSF.CarbonAware/src/Configuration/ServiceCollectionExtensions.cs
index 459351ae6..440e657b4 100644
--- a/src/GSF.CarbonAware/src/Configuration/ServiceCollectionExtensions.cs
+++ b/src/GSF.CarbonAware/src/Configuration/ServiceCollectionExtensions.cs
@@ -11,15 +11,22 @@ namespace GSF.CarbonAware.Configuration;
public static class ServiceCollectionExtensions
{
- ///
- /// Add services needed in order to use an Emissions service.
- ///
- public static IServiceCollection AddEmissionsServices(this IServiceCollection services, IConfiguration configuration)
+
+ private static IServiceCollection ConfigureLocationDataSourcesConfiguration(this IServiceCollection services, IConfiguration configuration)
{
services.Configure(c =>
{
configuration.GetSection(LocationDataSourcesConfiguration.Key).Bind(c);
});
+ return services;
+ }
+
+ ///
+ /// Add services needed in order to use an Emissions service.
+ ///
+ public static IServiceCollection AddEmissionsServices(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.ConfigureLocationDataSourcesConfiguration(configuration);
services.TryAddSingleton();
services.AddDataSourceService(configuration);
services.TryAddSingleton();
@@ -32,10 +39,7 @@ public static IServiceCollection AddEmissionsServices(this IServiceCollection se
///
public static IServiceCollection AddForecastServices(this IServiceCollection services, IConfiguration configuration)
{
- services.Configure(c =>
- {
- configuration.GetSection(LocationDataSourcesConfiguration.Key).Bind(c);
- });
+ services.ConfigureLocationDataSourcesConfiguration(configuration);
services.TryAddSingleton();
services.AddDataSourceService(configuration);
services.TryAddSingleton();
diff --git a/src/GSF.CarbonAware/src/GSF.CarbonAware.csproj b/src/GSF.CarbonAware/src/GSF.CarbonAware.csproj
index f113e0b98..6bb9fac4b 100644
--- a/src/GSF.CarbonAware/src/GSF.CarbonAware.csproj
+++ b/src/GSF.CarbonAware/src/GSF.CarbonAware.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
true
@@ -37,6 +37,7 @@
+
@@ -48,13 +49,13 @@
-
+
-
+
-
\ No newline at end of file
+
diff --git a/src/GSF.CarbonAware/src/GSF.CarbonAware.targets b/src/GSF.CarbonAware/src/GSF.CarbonAware.targets
index 0e9a1ace2..752a668a6 100644
--- a/src/GSF.CarbonAware/src/GSF.CarbonAware.targets
+++ b/src/GSF.CarbonAware/src/GSF.CarbonAware.targets
@@ -16,7 +16,7 @@
-
+
diff --git a/src/GSF.CarbonAware/test/GSF.CarbonAware.Tests.csproj b/src/GSF.CarbonAware/test/GSF.CarbonAware.Tests.csproj
index 64e74590c..696005074 100644
--- a/src/GSF.CarbonAware/test/GSF.CarbonAware.Tests.csproj
+++ b/src/GSF.CarbonAware/test/GSF.CarbonAware.Tests.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
false
diff --git a/src/clients/docker-generate-clients.sh b/src/clients/docker-generate-clients.sh
index 142a64179..23478f2d4 100755
--- a/src/clients/docker-generate-clients.sh
+++ b/src/clients/docker-generate-clients.sh
@@ -45,7 +45,7 @@ docker run --rm \
-i http://$1/swagger/v1/swagger.json \
-g csharp-netcore \
-o /local/csharp \
- --additional-properties=targetFramework=net6.0
+ --additional-properties=targetFramework=net8.0
# golang
docker run --rm \
diff --git a/src/clients/generate-clients.sh b/src/clients/generate-clients.sh
index 529d78e7a..9677589b8 100755
--- a/src/clients/generate-clients.sh
+++ b/src/clients/generate-clients.sh
@@ -12,5 +12,5 @@ cd generated
openapi-generator-cli generate -i http://$1/swagger/v1/swagger.json -g java -o ./java
openapi-generator-cli generate -i http://$1/swagger/v1/swagger.json -g python -o ./python
openapi-generator-cli generate -i http://$1/swagger/v1/swagger.json -g javascript -o ./javascript
-openapi-generator-cli generate -i http://$1/swagger/v1/swagger.json -g csharp-netcore -o ./csharp --additional-properties=targetFramework=net6.0
+openapi-generator-cli generate -i http://$1/swagger/v1/swagger.json -g csharp-netcore -o ./csharp --additional-properties=targetFramework=net8.0
openapi-generator-cli generate -i http://$1/swagger/v1/swagger.json -g go -o ./golang
diff --git a/src/clients/tests/csharp/Dockerfile b/src/clients/tests/csharp/Dockerfile
index 56b1cffcd..4bb8ffe61 100644
--- a/src/clients/tests/csharp/Dockerfile
+++ b/src/clients/tests/csharp/Dockerfile
@@ -1,5 +1,5 @@
# https://hub.docker.com/_/microsoft-dotnet
-FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
# copy csharp client
WORKDIR /source/clients/csharp/src/Org.OpenAPITools
@@ -13,7 +13,7 @@ COPY tests/csharp .
RUN dotnet publish -c release -o /app
# final stage/image
-FROM mcr.microsoft.com/dotnet/aspnet:6.0
+FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY tests/temp.env /.env
COPY --from=build /app ./
diff --git a/src/clients/tests/csharp/csharp.csproj b/src/clients/tests/csharp/csharp.csproj
index 0976a8879..f620d14f3 100644
--- a/src/clients/tests/csharp/csharp.csproj
+++ b/src/clients/tests/csharp/csharp.csproj
@@ -1,15 +1,15 @@
- net6.0
+ net8.0
-
-
+
+