diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 000000000..1326700af
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,3 @@
+{
+ "image": "mcr.microsoft.com/dotnet/sdk:8.0"
+}
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index 9345e1b0b..9a3d9c40d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -44,4 +44,21 @@ csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_members_in_anonymous_types = true
\ No newline at end of file
+csharp_new_line_before_members_in_anonymous_types = true
+
+# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/unnecessary-code-rules
+
+# Avoid unused private fields
+dotnet_diagnostic.CA1823.severity = error
+
+# Use string.Contains(char) instead of string.Contains(string) with single characters
+dotnet_diagnostic.CA1847.severity = error
+
+# Remove unnecessary import
+dotnet_diagnostic.IDE0005.severity = error
+
+# Private member is unused
+dotnet_diagnostic.IDE0051.severity = error
+
+# Private member is unread
+dotnet_diagnostic.IDE0052.severity = error
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..841fcae61
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,37 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**Kubernetes C# SDK Client Version**
+e.g. `9.0.1`
+
+**Server Kubernetes Version**
+e.g. `1.22.3`
+
+**Dotnet Runtime Version**
+e.g. net6
+
+**To Reproduce**
+Steps to reproduce the behavior:
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**KubeConfig**
+If applicable, add a KubeConfig file with secrets redacted.
+
+**Where do you run your app with Kubernetes SDK (please complete the following information):**
+ - OS: [e.g. Linux]
+ - Environment [e.g. container]
+ - Cloud [e.g. Azure]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..2fb1795e4
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,31 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+
+ # Maintain dependencies for GitHub Actions
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ # Allow up to 5 open pull requests for GitHub Actions dependencies
+ open-pull-requests-limit: 5
+ labels:
+ - "dependencies"
+ # Rebase open pull requests when changes are detected
+ rebase-strategy: "auto"
+
+ # Maintain dependencies for NuGet packages
+ - package-ecosystem: "nuget"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ # Allow up to 10 open pull requests for NuGet dependencies
+ open-pull-requests-limit: 10
+ labels:
+ - "dependencies"
+ # Rebase open pull requests when changes are detected
+ rebase-strategy: "auto"
diff --git a/.github/workflows/buildtest.yaml b/.github/workflows/buildtest.yaml
index 081a1066d..26d585ec7 100644
--- a/.github/workflows/buildtest.yaml
+++ b/.github/workflows/buildtest.yaml
@@ -8,63 +8,82 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest]
name: Dotnet build
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
- - name: Setup dotnet SDK 3.1
- uses: actions/setup-dotnet@v1
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: '3.1.x'
- - name: Setup dotnet SDK 5
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: '5.0.x'
- - name: Setup dotnet SDK 6
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: '6.0.x'
- - name: Check Format
- # don't check formatting on Windows b/c of CRLF issues.
- if: matrix.os == 'ubuntu-latest'
- run: dotnet format --severity error --verify-no-changes --exclude ./src/KubernetesClient/generated/
+ dotnet-version: |
+ 8.0.x
+ 9.0.x
- name: Build
- run: dotnet build --configuration Release
+ run: dotnet build --configuration Release
- name: Test
- run: dotnet test /p:CollectCoverage=true /p:ExcludeByFile=\"**/KubernetesClient/generated/**/*.cs\" /p:CoverletOutputFormat="cobertura"
- # - uses: 5monkeys/cobertura-action@master
- # with:
- # path: tests/KubernetesClient.Tests/coverage.netcoreapp2.1.cobertura.xml
- # repo_token: ${{ secrets.GITHUB_TOKEN }}
- # minimum_coverage: 0
- e2e:
- runs-on: ubuntu-latest
+ run: dotnet test --configuration Release --collect:"Code Coverage;Format=Cobertura" --logger trx --results-directory TestResults --settings CodeCoverage.runsettings --no-build
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v5
+ with:
+ directory: ./TestResults
+ files: '*.cobertura.xml'
+ - name: Upload test results
+ uses: actions/upload-artifact@v5
+ with:
+ name: test-results-${{ matrix.os }}
+ path: ./TestResults
+ if: ${{ always() }} # Always run this step even on failure
+
+ # Test code gen for visual studio compatibility >> https://github.com/kubernetes-client/csharp/pull/1008
+ codgen:
+ runs-on: windows-latest
+ name: MSBuild build
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
- - name: Setup dotnet SDK 3.1
- uses: actions/setup-dotnet@v1
+ - name: Add msbuild to PATH
+ uses: microsoft/setup-msbuild@v2
+ - name: Setup dotnet SDK
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: '3.1.x'
- - name: Setup dotnet SDK 5
- uses: actions/setup-dotnet@v1
+ dotnet-version: '9.0.x'
+ - name: Restore nugets (msbuild)
+ run: msbuild .\src\KubernetesClient\ -t:restore -p:RestorePackagesConfig=true
+ - name: Build (msbuild)
+ run: msbuild .\src\KubernetesClient\
+
+ e2e:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v5
with:
- dotnet-version: '5.0.x'
- - name: Setup dotnet SDK 6
- uses: actions/setup-dotnet@v1
+ fetch-depth: 0
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: '6.0.x'
+ dotnet-version: |
+ 8.0.x
+ 9.0.x
- name: Minikube
run: minikube start
- name: Test
run: |
true > skip.log
- env K8S_E2E_MINIKUBE=1 dotnet test tests/E2E.Tests --logger "SkipTestLogger;file=$PWD/skip.log"
+ env K8S_E2E_MINIKUBE=1 dotnet test tests/E2E.Tests --logger "SkipTestLogger;file=$PWD/skip.log" -p:BuildInParallel=false
+ if [ -s skip.log ]; then
+ cat skip.log
+ echo "CASES MUST NOT BE SKIPPED"
+ exit 1
+ fi
+ - name: AOT Test
+ run: |
+ true > skip.log
+ env K8S_E2E_MINIKUBE=1 dotnet test tests/E2E.Aot.Tests --logger "SkipTestLogger;file=$PWD/skip.log" -p:BuildInParallel=false
if [ -s skip.log ]; then
cat skip.log
echo "CASES MUST NOT BE SKIPPED"
exit 1
- fi
+ fi
on:
pull_request:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 4ae4d4eff..6355396eb 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,55 +1,62 @@
-name: "CodeQL"
-
-on:
- push:
- branches: [ master ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ master ]
- schedule:
- - cron: '15 23 * * 1'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: windows-latest
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'csharp' ]
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v2
- with:
- fetch-depth: 0
-
- - name: Setup dotnet SDK 3.1
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: '3.1.x'
- - name: Setup dotnet SDK 5
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: '5.0.x'
- - name: Setup dotnet SDK 6
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: '6.0.x'
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v1
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
- - name: Autobuild
- uses: github/codeql-action/autobuild@v1
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+name: "CodeQL"
+
+permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ master ]
+ schedule:
+ - cron: '15 23 * * 1'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: windows-2022
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'csharp' ]
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: |
+ 8.0.x
+ 9.0.x
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v4
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ # Currently .NET8.0 isn't supported
+ # - name: Autobuild
+ # uses: github/codeql-action/autobuild@v2
+
+ - name: Restore dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build --configuration Debug --no-restore
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v4
diff --git a/.github/workflows/docfx.yaml b/.github/workflows/docfx.yaml
new file mode 100644
index 000000000..3eec06ec3
--- /dev/null
+++ b/.github/workflows/docfx.yaml
@@ -0,0 +1,56 @@
+name: Docfx
+
+on:
+ push:
+ branches: [ master ]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# Allow one concurrent deployment
+concurrency:
+ group: "pages"
+ cancel-in-progress: true
+
+jobs:
+ docfx:
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: |
+ 8.0.x
+ 9.0.x
+
+ - name: Build
+ run: dotnet build -c Release
+
+ - uses: nunit/docfx-action@v4.1.0
+ name: Build Documentation
+ with:
+ args: doc/docfx.json
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v5
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v4
+ with:
+ # Upload entire repository
+ path: doc/_site
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/draft.yaml b/.github/workflows/draft.yaml
new file mode 100644
index 000000000..01b098518
--- /dev/null
+++ b/.github/workflows/draft.yaml
@@ -0,0 +1,42 @@
+name: Draft Release
+
+permissions:
+ contents: write
+
+on:
+ push:
+ branches: [ master ]
+
+jobs:
+ draft:
+
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: |
+ 8.0.x
+ 9.0.x
+
+ - name: dotnet restore
+ run: dotnet restore --verbosity minimal --configfile nuget.config
+
+ - name: dotnet test
+ run: dotnet test
+
+ - uses: dotnet/nbgv@master
+ with:
+ setAllVars: true
+
+ - name: create release
+ shell: pwsh
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh release create -d --generate-notes v$env:NBGV_NuGetPackageVersion
diff --git a/.github/workflows/nuget.yaml b/.github/workflows/nuget.yaml
index 5808d472b..fa654822f 100644
--- a/.github/workflows/nuget.yaml
+++ b/.github/workflows/nuget.yaml
@@ -1,33 +1,25 @@
name: Nuget
on:
- push:
- branches: [ master ]
+ release:
+ types: [ released ]
jobs:
- build:
+ nuget:
runs-on: windows-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
- - name: .NET Core 3.1.x SDK
- uses: actions/setup-dotnet@v1
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: 3.1.x
-
- - name: .NET 5.x SDK
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: 5.0.x
-
- - name: .NET 6.x SDK
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: 6.0.x
+ dotnet-version: |
+ 8.0.x
+ 9.0.x
- name: dotnet restore
run: dotnet restore --verbosity minimal --configfile nuget.config
@@ -36,7 +28,33 @@ jobs:
run: dotnet test
- name: dotnet pack
- run: dotnet pack -c Release src/KubernetesClient -o pkg --include-symbols
+ run: dotnet pack -c Release src/nuget.proj -o pkg --include-symbols
- name: dotnet nuget push
- run: dotnet nuget push pkg\*.nupkg -s https://www.nuget.org/ -k ${{ secrets.nuget_api_key }}
+ run: |
+ dotnet nuget push pkg\*.nupkg -s https://nuget.pkg.github.com/$env:GITHUB_REPOSITORY_OWNER -k ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
+ dotnet nuget push pkg\*.nupkg -s https://www.nuget.org/ -k ${{ secrets.nuget_api_key }} --skip-duplicate
+
+
+ ## Remove old versions of NuGet packages form github NuGet feed
+ nuget-delete-old-packages:
+ name: "Delete Old NuGet"
+ needs: [nuget]
+ strategy:
+ matrix:
+ nuget-package:
+ - "KubernetesClient"
+ - "KubernetesClient.Classic"
+ runs-on: ubuntu-latest
+ permissions:
+ packages: write
+
+ steps:
+ - name: Delete old NuGet packages
+ uses: actions/delete-package-versions@v5
+ with:
+ owner: ${{ env.GITHUB_REPOSITORY_OWNER }}
+ token: ${{ secrets.GITHUB_TOKEN }}
+ package-name: ${{ matrix.nuget-package }}
+ package-type: nuget
+ min-versions-to-keep: 10
diff --git a/.gitignore b/.gitignore
index d24a2eae2..46bc886d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,6 @@ bin/
*.sln.iml
launchSettings.json
-*.DotSettings
\ No newline at end of file
+*.DotSettings
+
+*.sln
\ No newline at end of file
diff --git a/CodeCoverage.runsettings b/CodeCoverage.runsettings
new file mode 100644
index 000000000..acc025c10
--- /dev/null
+++ b/CodeCoverage.runsettings
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+ .*KubernetesClient\..*\.dll$
+
+
+ .*tests\.dll$
+ .*xunit.*dll$
+ .*moq\.dll$
+ .*System\.Reactive\.dll$
+ .*BouncyCastle\.Crypto\.dll$
+ .*IdentityModel\.OidcClient\.dll$
+
+
+
+ True
+
+ True
+
+ True
+
+
+ ^System.ObsoleteAttribute$
+ ^System.CodeDom.Compiler.GeneratedCodeAttribute$
+ ^System.Diagnostics.DebuggerHiddenAttribute$
+ ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$
+
+
+
+
+
+
+
+
diff --git a/Directory.Build.props b/Directory.Build.props
index bfa551f49..3d3e1cfce 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,6 +1,39 @@
-
-
-
- $(MSBuildThisFileDirectory)\kubernetes-client.ruleset
-
-
+
+
+
+ $(MSBuildThisFileDirectory)\kubernetes-client.ruleset
+ true
+ true
+
+
+
+ The Kubernetes Project Authors
+ 2017 The Kubernetes Project Authors
+ Client library for the Kubernetes open source container orchestrator.
+
+ Apache-2.0
+ https://github.com/kubernetes-client/csharp
+ https://raw.githubusercontent.com/kubernetes/kubernetes/master/logo/logo.png
+ logo.png
+ kubernetes;docker;containers;
+ true
+
+
+ true
+
+
+ true
+ snupkg
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+ 13.0
+
+
+
+ true
+
+
+
+
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 5556d70a5..517121e49 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -1,15 +1,5 @@
-
-
- All
-
-
-
- All
-
-
-
-
-
-
+
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 000000000..27783a77c
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,54 @@
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OWNERS b/OWNERS
index 055985bd8..ef8b7dd29 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,8 +2,9 @@
approvers:
- brendandburns
-- krabhishek8260
-reviewer:
+- tg123
+reviewers:
- brendandburns
-- krabhishek8260
-
+- tg123
+emeritus_approvers:
+- krabhishek8260 # 4/4/2022
diff --git a/README.md b/README.md
index a89adc07c..c8eb91626 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,38 @@
# Kubernetes C# Client
-[](https://travis-ci.org/kubernetes-client/csharp)
+
+[](https://github.com/kubernetes-client/csharp/actions/workflows/buildtest.yaml)
[](http://bit.ly/kubernetes-client-capabilities-badge)
[](http://bit.ly/kubernetes-client-support-badge)
# Usage
-[Nuget Package](https://www.nuget.org/packages/KubernetesClient/)
+
+[](https://www.nuget.org/packages/KubernetesClient/)
```sh
dotnet add package KubernetesClient
```
+## Generate with Visual Studio
+
+```
+dotnet msbuild /Restore /t:SlnGen kubernetes-client.proj
+```
+
## Authentication/Configuration
You should be able to use a standard KubeConfig file with this library,
see the `BuildConfigFromConfigFile` function below. Most authentication
-methods are currently supported, but a few are not, see the
+methods are currently supported, but a few are not, see the
[known-issues](https://github.com/kubernetes-client/csharp#known-issues).
You should also be able to authenticate with the in-cluster service
account using the `InClusterConfig` function shown below.
## Monitoring
-There is optional built-in metric generation for prometheus client metrics.
-The exported metrics are:
-
-* `k8s_dotnet_request_total` - Counter of request, broken down by HTTP Method
-* `k8s_dotnet_response_code_total` - Counter of responses, broken down by HTTP Method and response code
-* `k8s_request_latency_seconds` - Latency histograms broken down by method, api group, api version and resource kind
+Metrics are built in to HttpClient using System.Diagnostics.DiagnosticsSource.
+https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-system-net
-There is an example integrating these monitors in the examples/prometheus directory.
+There are many ways these metrics can be consumed/exposed but that decision is up to the application, not KubernetesClient itself.
+https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics-collection
## Sample Code
@@ -48,10 +53,10 @@ var client = new Kubernetes(config);
### Listing Objects
```c#
-var namespaces = client.ListNamespace();
+var namespaces = client.CoreV1.ListNamespace();
foreach (var ns in namespaces.Items) {
Console.WriteLine(ns.Metadata.Name);
- var list = client.ListNamespacedPod(ns.Metadata.Name);
+ var list = client.CoreV1.ListNamespacedPod(ns.Metadata.Name);
foreach (var item in list.Items)
{
Console.WriteLine(item.Metadata.Name);
@@ -69,10 +74,10 @@ var ns = new V1Namespace
}
};
-var result = client.CreateNamespace(ns);
+var result = client.CoreV1.CreateNamespace(ns);
Console.WriteLine(result);
-var status = client.DeleteNamespace(ns.Metadata.Name, new V1DeleteOptions());
+var status = client.CoreV1.DeleteNamespace(ns.Metadata.Name, new V1DeleteOptions());
```
## Examples
@@ -96,7 +101,7 @@ var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
var client = new Kubernetes(config);
```
-Not all auth providers are supported at moment [#91](https://github.com/kubernetes-client/csharp/issues/91#issuecomment-362920478). You can still connect to a cluster by starting the proxy command:
+Not all auth providers are supported at the moment [#91](https://github.com/kubernetes-client/csharp/issues/91#issuecomment-362920478). You can still connect to a cluster by starting the proxy command:
```bash
$ kubectl proxy
@@ -113,7 +118,7 @@ Notice that this is a workaround and is not recommended for production use.
## Testing
-The project uses [XUnit](https://xunit.github.io) as unit testing framework.
+The project uses [XUnit](https://github.com/xunit/xunit) as unit testing framework.
To run the tests:
@@ -123,14 +128,12 @@ dotnet restore
dotnet test
```
-# Generating the Client Code
+# Update the API model
## Prerequisites
You'll need a Linux machine with Docker.
-The generated code works on all platforms supported by .NET or .NET Core.
-
Check out the generator project into some other directory
(henceforth `$GEN_DIR`).
@@ -139,30 +142,53 @@ cd $GEN_DIR/..
git clone https://github.com/kubernetes-client/gen
```
-## Generating code
+## Generating new swagger.json
```bash
# Where REPO_DIR points to the root of the csharp repository
-cd ${REPO_DIR}/csharp/src/KubernetesClient
-${GEN_DIR}/openapi/csharp.sh generated ../csharp.settings
+cd
+${GEN_DIR}/openapi/csharp.sh ${REPO_DIR}/src/KubernetesClient ${REPO_DIR}/csharp.settings
```
-# Version Compatibility
+# Version Compatibility
+
+| SDK Version | Kubernetes Version | .NET Targeting |
+|-------------|--------------------|-----------------------------------------------------|
+| 18.0 | 1.34 | net8.0;net9.0;net48*;netstandard2.0* |
+| 17.0 | 1.33 | net8.0;net9.0;net48*;netstandard2.0* |
+| 16.0 | 1.32 | net8.0;net9.0;net48*;netstandard2.0* |
+| 15.0 | 1.31 | net6.0;net8.0;net48*;netstandard2.0* |
+| 14.0 | 1.30 | net6.0;net8.0;net48*;netstandard2.0* |
+| 13.0 | 1.29 | net6.0;net7.0;net8.0;net48*;netstandard2.0* |
+| 12.0 | 1.28 | net6.0;net7.0;net48*;netstandard2.0* |
+| 11.0 | 1.27 | net6.0;net7.0;net48*;netstandard2.0* |
+| 10.0 | 1.26 | net6.0;net7.0;net48*;netstandard2.0* |
+| 9.1 | 1.25 | netstandard2.1;net6.0;net7.0;net48*;netstandard2.0* |
+| 9.0 | 1.25 | netstandard2.1;net5.0;net6.0;net48*;netstandard2.0* |
+| 8.0 | 1.24 | netstandard2.1;net5.0;net6.0;net48*;netstandard2.0* |
+| 7.2 | 1.23 | netstandard2.1;net5.0;net6.0;net48*;netstandard2.0* |
+| 7.0 | 1.23 | netstandard2.1;net5.0;net6.0 |
+| 6.0 | 1.22 | netstandard2.1;net5.0 |
+| 5.0 | 1.21 | netstandard2.1;net5 |
+| 4.0 | 1.20 | netstandard2.0;netstandard2.1 |
+| 3.0 | 1.19 | netstandard2.0;net452 |
+| 2.0 | 1.18 | netstandard2.0;net452 |
+| 1.6 | 1.16 | netstandard1.4;netstandard2.0;net452; |
+| 1.4 | 1.13 | netstandard1.4;net451 |
+| 1.3 | 1.12 | netstandard1.4;net452 |
+
+ * Starting from `2.0`, [dotnet sdk versioning](https://github.com/kubernetes-client/csharp/issues/400) adopted
+ * `Kubernetes Version` here means the version sdk models and apis were generated from
+ * Kubernetes api server guarantees the compatibility with `n-2` (`n-3` after 1.28) version. for example:
+ - 1.19 based sdk should work with 1.21 cluster, but not guaranteed to work with 1.22 cluster.
-| SDK Version | Kubernetes Version | .NET Targeting |
-|-------------|--------------------|---------------------------------------|
-| 6.0 | 1.22 | netstandard2.1;net5 |
-| 5.0 | 1.21 | netstandard2.1;net5 |
-| 4.0 | 1.20 | netstandard2.0;netstandard2.1 |
-| 3.0 | 1.19 | netstandard2.0;net452 |
-| 2.0 | 1.18 | netstandard2.0;net453 |
-| 1.6 | 1.16 | netstandard1.4;netstandard2.0;net452; |
-| 1.4 | 1.13 | netstandard1.4;net451 |
-| 1.3 | 1.12 | netstandard1.4;net452 |
+ and vice versa:
+ - 1.21 based sdk should work with 1.19 cluster, but not guaranteed to work with 1.18 cluster.
+Note: in practice, the sdk might work with much older clusters, at least for the more stable functionality. However, it is not guaranteed past the `n-2` (or `n-3` after 1.28 ) version. See [#1511](https://github.com/kubernetes-client/csharp/issues/1511) for additional details.
- * Starting form `2.0`, [dotnet sdk versioning](https://github.com/kubernetes-client/csharp/issues/400) adopted
- * `Kubernetes Version` here means the version sdk models and apis were generated from
- * Kubernetes api server guarantees the compatibility with `n-2` version. for exmaple, 1.19 based sdk should work with 1.21 cluster, but no guarantee works with 1.22 cluster. see also
+ see also
+ * Fixes (including security fixes) are not back-ported automatically to older sdk versions. However, contributions from the community are welcomed 😊; See [Contributing](#contributing) for instructions on how to contribute.
+ * `*` `KubernetesClient.Classic`: netstandard2.0 and net48 are supported with limited features
## Contributing
diff --git a/SECURITY_CONTACTS b/SECURITY_CONTACTS
index d22538052..df0df1c5f 100644
--- a/SECURITY_CONTACTS
+++ b/SECURITY_CONTACTS
@@ -11,3 +11,4 @@
# INSTRUCTIONS AT https://kubernetes.io/security/
brendandburns
+tg123
diff --git a/csharp.settings b/csharp.settings
index 07a2b4e55..0110958c8 100644
--- a/csharp.settings
+++ b/csharp.settings
@@ -1,3 +1,3 @@
-export KUBERNETES_BRANCH=v1.22.0
+export KUBERNETES_BRANCH=v1.34.0
export CLIENT_VERSION=0.0.1
export PACKAGE_NAME=k8s
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 000000000..2f16432e9
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,11 @@
+###############
+# folder #
+###############
+/**/DROP/
+/**/TEMP/
+/**/packages/
+/**/bin/
+/**/obj/
+_site
+
+api
\ No newline at end of file
diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md
new file mode 120000
index 000000000..44fcc6343
--- /dev/null
+++ b/doc/CONTRIBUTING.md
@@ -0,0 +1 @@
+../CONTRIBUTING.md
\ No newline at end of file
diff --git a/doc/docfx.json b/doc/docfx.json
new file mode 100644
index 000000000..2917802e6
--- /dev/null
+++ b/doc/docfx.json
@@ -0,0 +1,41 @@
+{
+ "metadata": [
+ {
+ "src": [
+ {
+ "files": [
+ "KubernetesClient/bin/Release/net8.0/KubernetesClient.dll"
+ ],
+ "src": "../src"
+ }
+ ],
+ "dest": "api",
+ "disableGitFeatures": false,
+ "disableDefaultFilter": false
+ }
+ ],
+ "build": {
+ "content": [
+ {
+ "files": [
+ "api/**.yml",
+ "index.md",
+ "CONTRIBUTING.md",
+ "toc.yml"
+ ]
+ }
+ ],
+ "dest": "_site",
+ "globalMetadataFiles": [],
+ "fileMetadataFiles": [],
+ "template": [
+ "default"
+ ],
+ "postProcessors": [],
+ "markdownEngineName": "markdig",
+ "noLangKeyword": false,
+ "keepFileLink": false,
+ "cleanupCacheHistory": false,
+ "disableGitFeatures": false
+ }
+}
\ No newline at end of file
diff --git a/doc/index.md b/doc/index.md
new file mode 120000
index 000000000..32d46ee88
--- /dev/null
+++ b/doc/index.md
@@ -0,0 +1 @@
+../README.md
\ No newline at end of file
diff --git a/doc/toc.yml b/doc/toc.yml
new file mode 100644
index 000000000..8bf2c8ed1
--- /dev/null
+++ b/doc/toc.yml
@@ -0,0 +1,2 @@
+- name: API Documentation
+ href: api/k8s.yml
diff --git a/examples/Directory.Build.props b/examples/Directory.Build.props
new file mode 100644
index 000000000..b87fe6aaa
--- /dev/null
+++ b/examples/Directory.Build.props
@@ -0,0 +1,7 @@
+
+
+
+
+ net9.0
+
+
diff --git a/examples/Directory.Build.targets b/examples/Directory.Build.targets
new file mode 100644
index 000000000..bf5f5ee49
--- /dev/null
+++ b/examples/Directory.Build.targets
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/examples/GenericKubernetesApi/Program.cs b/examples/GenericKubernetesApi/Program.cs
deleted file mode 100644
index 5a093fad1..000000000
--- a/examples/GenericKubernetesApi/Program.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using k8s;
-using k8s.Models;
-using k8s.Util.Common;
-using k8s.Util.Common.Generic;
-
-namespace GenericKubernetesApiExample
-{
- public class Program
- {
- private static GenericKubernetesApi _genericKubernetesApi;
-
- public static void Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildDefaultConfig();
- IKubernetes client = new Kubernetes(config);
- var cts = new CancellationTokenSource();
-
- _genericKubernetesApi = new GenericKubernetesApi(
- apiGroup: "pod",
- apiVersion: "v1",
- resourcePlural: "pods",
- apiClient: client);
-
- var aPod = GetNamespacedPod(Namespaces.NamespaceDefault, "my-pod-name", cts.Token);
- var aListOfPods = ListPodsInNamespace(Namespaces.NamespaceDefault, cts.Token);
-
- // Watch for pod actions in a namespsace
- using var watch = _genericKubernetesApi.Watch(
- Namespaces.NamespaceDefault,
- (eventType, pod) => { Console.WriteLine("The event {0} happened on pod named {1}", eventType, pod.Metadata.Name); },
- exception => { Console.WriteLine("Oh no! An exception happened while watching pods. The message was '{0}'.", exception.Message); },
- () => { Console.WriteLine("The server closed the connection."); });
-
- Console.WriteLine("press ctrl + c to stop watching");
-
- var ctrlc = new ManualResetEventSlim(false);
- Console.CancelKeyPress += (sender, eventArgs) => ctrlc.Set();
- ctrlc.Wait();
- cts.Cancel();
- }
-
- private static V1Pod GetNamespacedPod(string @namespace, string podName, CancellationToken cancellationToken)
- {
- var resp = Task.Run(
- async () => await _genericKubernetesApi.GetAsync(@namespace, podName, cancellationToken).ConfigureAwait(false), cancellationToken);
-
- return resp.Result;
- }
-
- private static V1PodList ListPodsInNamespace(string @namespace, CancellationToken cancellationToken)
- {
- var resp = Task.Run(
- async () => await _genericKubernetesApi.ListAsync(@namespace, cancellationToken).ConfigureAwait(false), cancellationToken);
-
- return resp.Result;
- }
- }
-}
diff --git a/examples/aks-kubelogin/Program.cs b/examples/aks-kubelogin/Program.cs
new file mode 100644
index 000000000..cdee0cf10
--- /dev/null
+++ b/examples/aks-kubelogin/Program.cs
@@ -0,0 +1,49 @@
+using k8s;
+using System;
+using System.IO;
+using System.Text;
+
+var server = "https://example.hcp.eastus.azmk8s.io/"; // the server url of your aks
+var clientid = "00000000-0000-0000-0000-000000000000"; // the client id of the your msi
+var kubelogin = @"C:\bin\kubelogin.exe"; // the path to the kubelogin.exe
+
+using var configstream = new MemoryStream(Encoding.ASCII.GetBytes($"""
+apiVersion: v1
+clusters:
+- cluster:
+ insecure-skip-tls-verify: true
+ server: {server}
+ name: aks
+contexts:
+- context:
+ cluster: aks
+ user: msi
+ name: aks
+current-context: aks
+kind: Config
+users:
+- name: msi
+ user:
+ exec:
+ apiVersion: client.authentication.k8s.io/v1beta1
+ args:
+ - get-token
+ - --login
+ - msi
+ - --server-id
+ - 6dae42f8-4368-4678-94ff-3960e28e3630
+ - --client-id
+ - {clientid}
+ command: {kubelogin}
+ env: null
+"""));
+
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile(configstream);
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
+
+var list = client.CoreV1.ListNamespacedPod("default");
+foreach (var item in list.Items)
+{
+ Console.WriteLine(item.Metadata.Name);
+}
diff --git a/examples/aks-kubelogin/README.md b/examples/aks-kubelogin/README.md
new file mode 100644
index 000000000..ab71071b0
--- /dev/null
+++ b/examples/aks-kubelogin/README.md
@@ -0,0 +1,24 @@
+# AKS C# example using kubelogin + MSI
+
+This example shows how to use the [kubelogin](https://github.com/Azure/kubelogin) to authenticate using [managed identities](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) with Azure Kubernetes Service (AKS) using the C# SDK.
+
+
+## Prerequisites
+
+ - turn on AAD support for AKS, see [here](https://docs.microsoft.com/en-us/azure/aks/managed-aad)
+ - create a managed identity for the AKS cluster
+ - assign the managed identity the `Azure Kubernetes Service RBAC Cluster Admin` (or other RBAC permission) on the AKS cluster
+ - assign the managed identity to the VM, see [here](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm)
+ - install the [kubelogin](https://github.com/Azure/kubelogin) to your machine
+
+## Running the code
+
+ *You must the the code on VM with MSI*
+
+ - Replace `server` with the address of your AKS cluster
+ - Replace `clientid` with the client id of the managed identity
+ - Replace `kubelogin` with the path to the kubelogin executable
+
+```
+dotnet run
+```
\ No newline at end of file
diff --git a/examples/aks-kubelogin/aks-kubelogin.csproj b/examples/aks-kubelogin/aks-kubelogin.csproj
new file mode 100644
index 000000000..11afe8d56
--- /dev/null
+++ b/examples/aks-kubelogin/aks-kubelogin.csproj
@@ -0,0 +1,5 @@
+
+
+ Exe
+
+
\ No newline at end of file
diff --git a/examples/aot/Program.cs b/examples/aot/Program.cs
new file mode 100644
index 000000000..d5125c0ff
--- /dev/null
+++ b/examples/aot/Program.cs
@@ -0,0 +1,16 @@
+using k8s;
+
+var config = KubernetesClientConfiguration.BuildDefaultConfig();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
+
+var list = client.CoreV1.ListNamespacedPod("default");
+foreach (var item in list.Items)
+{
+ Console.WriteLine(item.Metadata.Name);
+}
+
+if (list.Items.Count == 0)
+{
+ Console.WriteLine("Empty!");
+}
\ No newline at end of file
diff --git a/examples/aot/aot.csproj b/examples/aot/aot.csproj
new file mode 100644
index 000000000..28741906d
--- /dev/null
+++ b/examples/aot/aot.csproj
@@ -0,0 +1,11 @@
+
+
+ Exe
+ enable
+ enable
+ true
+
+
+
+
+
diff --git a/examples/attach/Attach.cs b/examples/attach/Attach.cs
index 2542e1c90..cfdce7d8e 100755
--- a/examples/attach/Attach.cs
+++ b/examples/attach/Attach.cs
@@ -1,43 +1,31 @@
-using System;
-using System.Threading.Tasks;
using k8s;
using k8s.Models;
-using Microsoft.Rest;
-
-namespace attach
-{
- internal class Attach
- {
- private static async Task Main(string[] args)
- {
- ServiceClientTracing.IsEnabled = true;
+using System;
+using System.Threading.Tasks;
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(config);
- Console.WriteLine("Starting Request!");
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
- var list = client.ListNamespacedPod("default");
- var pod = list.Items[0];
- await AttachToPod(client, pod).ConfigureAwait(false);
- }
+var list = client.CoreV1.ListNamespacedPod("default");
+var pod = list.Items[0];
+await AttachToPod(client, pod).ConfigureAwait(false);
- private async static Task AttachToPod(IKubernetes client, V1Pod pod)
- {
- var webSocket =
- await client.WebSocketNamespacedPodAttachAsync(pod.Metadata.Name, "default",
- pod.Spec.Containers[0].Name).ConfigureAwait(false);
+async Task AttachToPod(IKubernetes client, V1Pod pod)
+{
+ var webSocket =
+ await client.WebSocketNamespacedPodAttachAsync(pod.Metadata.Name, "default",
+ pod.Spec.Containers[0].Name).ConfigureAwait(false);
- var demux = new StreamDemuxer(webSocket);
- demux.Start();
+ var demux = new StreamDemuxer(webSocket);
+ demux.Start();
- var buff = new byte[4096];
- var stream = demux.GetStream(1, 1);
- while (true)
- {
- var read = stream.Read(buff, 0, 4096);
- var str = System.Text.Encoding.Default.GetString(buff);
- Console.WriteLine(str);
- }
- }
+ var buff = new byte[4096];
+ var stream = demux.GetStream(1, 1);
+ while (true)
+ {
+ var read = stream.Read(buff, 0, 4096);
+ var str = System.Text.Encoding.Default.GetString(buff);
+ Console.WriteLine(str);
}
}
diff --git a/examples/attach/attach.csproj b/examples/attach/attach.csproj
index 4f6e2ae17..5644cab8a 100755
--- a/examples/attach/attach.csproj
+++ b/examples/attach/attach.csproj
@@ -1,12 +1,7 @@
-
-
-
-
Exe
- net5
diff --git a/examples/clientset/Program.cs b/examples/clientset/Program.cs
new file mode 100644
index 000000000..a1b74e0f8
--- /dev/null
+++ b/examples/clientset/Program.cs
@@ -0,0 +1,32 @@
+using k8s;
+using k8s.Models;
+using k8s.ClientSets;
+using System.Threading.Tasks;
+
+namespace clientset
+{
+ internal class Program
+ {
+ private static async Task Main(string[] args)
+ {
+ var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+ var client = new Kubernetes(config);
+
+ var clientSet = new ClientSet(client);
+ var list = await clientSet.CoreV1.Pod.ListAsync("default").ConfigureAwait(false);
+ foreach (var item in list)
+ {
+ System.Console.WriteLine(item.Metadata.Name);
+ }
+
+ var pod = await clientSet.CoreV1.Pod.GetAsync("test", "default").ConfigureAwait(false);
+ System.Console.WriteLine(pod?.Metadata?.Name);
+
+ var watch = clientSet.CoreV1.Pod.WatchListAsync("default");
+ await foreach (var (_, item) in watch.ConfigureAwait(false))
+ {
+ System.Console.WriteLine(item.Metadata.Name);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/clientset/clientset.csproj b/examples/clientset/clientset.csproj
new file mode 100644
index 000000000..4274ceb02
--- /dev/null
+++ b/examples/clientset/clientset.csproj
@@ -0,0 +1,6 @@
+
+
+ Exe
+
+
+
diff --git a/examples/cp/Cp.cs b/examples/cp/Cp.cs
new file mode 100644
index 000000000..43e769490
--- /dev/null
+++ b/examples/cp/Cp.cs
@@ -0,0 +1,110 @@
+using ICSharpCode.SharpZipLib.Tar;
+using k8s;
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace cp;
+
+internal class Cp
+{
+ private static IKubernetes client;
+
+ private static async Task Main(string[] args)
+ {
+ var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+ client = new Kubernetes(config);
+
+
+ var pods = client.CoreV1.ListNamespacedPod("default", null, null, null, $"job-name=upload-demo");
+ var pod = pods.Items.First();
+
+ await CopyFileToPodAsync(pod.Metadata.Name, "default", "upload-demo", args[0], $"home/{args[1]}").ConfigureAwait(false);
+ }
+
+
+
+
+ private static void ValidatePathParameters(string sourcePath, string destinationPath)
+ {
+ if (string.IsNullOrWhiteSpace(sourcePath))
+ {
+ throw new ArgumentException($"{nameof(sourcePath)} cannot be null or whitespace");
+ }
+
+ if (string.IsNullOrWhiteSpace(destinationPath))
+ {
+ throw new ArgumentException($"{nameof(destinationPath)} cannot be null or whitespace");
+ }
+ }
+
+ public static async Task CopyFileToPodAsync(string name, string @namespace, string container, string sourceFilePath, string destinationFilePath, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ // All other parameters are being validated by MuxedStreamNamespacedPodExecAsync called by NamespacedPodExecAsync
+ ValidatePathParameters(sourceFilePath, destinationFilePath);
+
+ // The callback which processes the standard input, standard output and standard error of exec method
+ var handler = new ExecAsyncCallback(async (stdIn, stdOut, stdError) =>
+ {
+ var fileInfo = new FileInfo(destinationFilePath);
+ try
+ {
+ using (var memoryStream = new MemoryStream())
+ {
+ using (var inputFileStream = File.OpenRead(sourceFilePath))
+ using (var tarOutputStream = new TarOutputStream(memoryStream, Encoding.Default))
+ {
+ tarOutputStream.IsStreamOwner = false;
+
+ var fileSize = inputFileStream.Length;
+ var entry = TarEntry.CreateTarEntry(fileInfo.Name);
+
+ entry.Size = fileSize;
+
+ tarOutputStream.PutNextEntry(entry);
+ await inputFileStream.CopyToAsync(tarOutputStream).ConfigureAwait(false);
+ tarOutputStream.CloseEntry();
+ }
+
+ memoryStream.Position = 0;
+
+ await memoryStream.CopyToAsync(stdIn).ConfigureAwait(false);
+ await stdIn.FlushAsync().ConfigureAwait(false);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException($"Copy command failed: {ex.Message}");
+ }
+
+ using StreamReader streamReader = new StreamReader(stdError);
+ while (streamReader.EndOfStream == false)
+ {
+ string error = await streamReader.ReadToEndAsync().ConfigureAwait(false);
+ throw new IOException($"Copy command failed: {error}");
+ }
+ });
+
+ string destinationFolder = GetFolderName(destinationFilePath);
+
+ return await client.NamespacedPodExecAsync(
+ name,
+ @namespace,
+ container,
+ new string[] { "sh", "-c", $"tar xmf - -C {destinationFolder}" },
+ false,
+ handler,
+ cancellationToken).ConfigureAwait(false);
+ }
+
+
+ private static string GetFolderName(string filePath)
+ {
+ var folderName = Path.GetDirectoryName(filePath);
+
+ return string.IsNullOrEmpty(folderName) ? "." : folderName;
+ }
+}
diff --git a/examples/cp/cp.csproj b/examples/cp/cp.csproj
new file mode 100644
index 000000000..cde0f0fca
--- /dev/null
+++ b/examples/cp/cp.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+
+
+
+
+
+
+
diff --git a/examples/csrApproval/Program.cs b/examples/csrApproval/Program.cs
new file mode 100644
index 000000000..6c374105b
--- /dev/null
+++ b/examples/csrApproval/Program.cs
@@ -0,0 +1,85 @@
+using Json.Patch;
+using k8s;
+using k8s.Models;
+using System.Net;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Text.Json;
+
+string GenerateCertificate(string name)
+{
+ var sanBuilder = new SubjectAlternativeNameBuilder();
+ sanBuilder.AddIpAddress(IPAddress.Loopback);
+ sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
+ sanBuilder.AddDnsName("localhost");
+ sanBuilder.AddDnsName(Environment.MachineName);
+
+ var distinguishedName = new X500DistinguishedName(name);
+
+ using var rsa = RSA.Create(4096);
+ var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
+
+ request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
+ request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension([new ("1.3.6.1.5.5.7.3.1")], false));
+ request.CertificateExtensions.Add(sanBuilder.Build());
+ var csr = request.CreateSigningRequest();
+ var pemKey = "-----BEGIN CERTIFICATE REQUEST-----\r\n" +
+ Convert.ToBase64String(csr) +
+ "\r\n-----END CERTIFICATE REQUEST-----";
+
+ return pemKey;
+}
+
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
+var name = "demo";
+var x509 = GenerateCertificate(name);
+var encodedCsr = Encoding.UTF8.GetBytes(x509);
+
+var request = new V1CertificateSigningRequest
+{
+ ApiVersion = "certificates.k8s.io/v1",
+ Kind = "CertificateSigningRequest",
+ Metadata = new V1ObjectMeta
+ {
+ Name = name,
+ },
+ Spec = new V1CertificateSigningRequestSpec
+ {
+ Request = encodedCsr,
+ SignerName = "kubernetes.io/kube-apiserver-client",
+ Usages = new List { "client auth" },
+ ExpirationSeconds = 600, // minimum should be 10 minutes
+ },
+};
+
+await client.CertificatesV1.CreateCertificateSigningRequestAsync(request).ConfigureAwait(false);
+
+var serializeOptions = new JsonSerializerOptions
+{
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = true,
+};
+var readCert = await client.CertificatesV1.ReadCertificateSigningRequestAsync(name).ConfigureAwait(false);
+var old = JsonSerializer.SerializeToDocument(readCert, serializeOptions);
+
+var replace = new List
+{
+ new V1CertificateSigningRequestCondition
+ {
+ Type = "Approved",
+ Status = "True",
+ Reason = "Approve",
+ Message = "This certificate was approved by k8s client",
+ LastUpdateTime = DateTime.UtcNow,
+ LastTransitionTime = DateTime.UtcNow,
+ },
+};
+readCert.Status.Conditions = replace;
+
+var expected = JsonSerializer.SerializeToDocument(readCert, serializeOptions);
+
+var patch = old.CreatePatch(expected);
+await client.CertificatesV1.PatchCertificateSigningRequestApprovalAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name).ConfigureAwait(false);
diff --git a/examples/csrApproval/csrApproval.csproj b/examples/csrApproval/csrApproval.csproj
new file mode 100644
index 000000000..f67de4fbc
--- /dev/null
+++ b/examples/csrApproval/csrApproval.csproj
@@ -0,0 +1,12 @@
+
+
+
+ Exe
+ enable
+ enable
+
+
+
+
+
+
diff --git a/examples/customResource/CustomResourceDefinition.cs b/examples/customResource/CustomResourceDefinition.cs
index 302d22f6d..ad1b7f9c4 100644
--- a/examples/customResource/CustomResourceDefinition.cs
+++ b/examples/customResource/CustomResourceDefinition.cs
@@ -1,7 +1,7 @@
using k8s;
using k8s.Models;
-using Newtonsoft.Json;
using System.Collections.Generic;
+using System.Text.Json.Serialization;
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "CA1724:TypeNamesShouldNotMatchNamespaces", Justification = "This is just an example.")]
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "This is just an example.")]
@@ -21,19 +21,19 @@ public class CustomResourceDefinition
public string Namespace { get; set; }
}
- public abstract class CustomResource : KubernetesObject
+ public abstract class CustomResource : KubernetesObject, IMetadata
{
- [JsonProperty(PropertyName = "metadata")]
+ [JsonPropertyName("metadata")]
public V1ObjectMeta Metadata { get; set; }
}
public abstract class CustomResource : CustomResource
{
- [JsonProperty(PropertyName = "spec")]
+ [JsonPropertyName("spec")]
public TSpec Spec { get; set; }
- [JsonProperty(PropertyName = "CStatus")]
- public TStatus CStatus { get; set; }
+ [JsonPropertyName("status")]
+ public TStatus Status { get; set; }
}
public class CustomResourceList : KubernetesObject
diff --git a/examples/customResource/Program.cs b/examples/customResource/Program.cs
index 0be25601d..726852a7f 100644
--- a/examples/customResource/Program.cs
+++ b/examples/customResource/Program.cs
@@ -1,8 +1,10 @@
+using Json.Patch;
using k8s;
+using k8s.Autorest;
using k8s.Models;
-using Microsoft.AspNetCore.JsonPatch;
using System;
using System.Collections.Generic;
+using System.Text.Json;
using System.Threading.Tasks;
@@ -29,20 +31,20 @@ private static async Task Main(string[] args)
try
{
Console.WriteLine("creating CR {0}", myCr.Metadata.Name);
- var response = await client.CreateNamespacedCustomObjectWithHttpMessagesAsync(
+ var response = await client.CustomObjects.CreateNamespacedCustomObjectWithHttpMessagesAsync(
myCr,
myCRD.Group, myCRD.Version,
myCr.Metadata.NamespaceProperty ?? "default",
myCRD.PluralName).ConfigureAwait(false);
}
- catch (Microsoft.Rest.HttpOperationException httpOperationException) when (httpOperationException.Message.Contains("422"))
+ catch (HttpOperationException httpOperationException) when (httpOperationException.Message.Contains("422"))
{
var phase = httpOperationException.Response.ReasonPhrase;
var content = httpOperationException.Response.Content;
Console.WriteLine("response content: {0}", content);
Console.WriteLine("response phase: {0}", phase);
}
- catch (Microsoft.Rest.HttpOperationException)
+ catch (HttpOperationException)
{
}
@@ -54,15 +56,17 @@ private static async Task Main(string[] args)
Console.WriteLine("- CR Item {0} = {1}", crs.Items.IndexOf(cr), cr.Metadata.Name);
}
- // updating the custom resource
+ var old = JsonSerializer.SerializeToDocument(myCr);
myCr.Metadata.Labels.TryAdd("newKey", "newValue");
- var patch = new JsonPatchDocument();
- patch.Replace(x => x.Metadata.Labels, myCr.Metadata.Labels);
- patch.Operations.ForEach(x => x.path = x.path.ToLower());
+
+ var expected = JsonSerializer.SerializeToDocument(myCr);
+ var patch = old.CreatePatch(expected);
+
+ // updating the custom resource
var crPatch = new V1Patch(patch, V1Patch.PatchType.JsonPatch);
try
{
- var patchResponse = await client.PatchNamespacedCustomObjectAsync(
+ var patchResponse = await client.CustomObjects.PatchNamespacedCustomObjectAsync(
crPatch,
myCRD.Group,
myCRD.Version,
@@ -70,7 +74,7 @@ private static async Task Main(string[] args)
myCRD.PluralName,
myCr.Metadata.Name).ConfigureAwait(false);
}
- catch (Microsoft.Rest.HttpOperationException httpOperationException)
+ catch (HttpOperationException httpOperationException)
{
var phase = httpOperationException.Response.ReasonPhrase;
var content = httpOperationException.Response.Content;
@@ -88,11 +92,11 @@ private static async Task Main(string[] args)
// deleting the custom resource
try
{
- myCr = await generic.DeleteNamespacedAsync(
+ var status = await generic.DeleteNamespacedAsync(
myCr.Metadata.NamespaceProperty ?? "default",
myCr.Metadata.Name).ConfigureAwait(false);
- Console.WriteLine("Deleted the CR");
+ Console.WriteLine($"Deleted the CR status: {status}");
}
catch (Exception exception)
{
diff --git a/examples/customResource/README.md b/examples/customResource/README.md
index 8ea908495..2fdb11703 100644
--- a/examples/customResource/README.md
+++ b/examples/customResource/README.md
@@ -15,7 +15,7 @@ dotnet add package KubernetesClient
Make sure the [CRD](./config/crd.yaml) is created, in order to create an instance of it after.
```shell
-kubectl create -f ./crd.yaml
+kubectl create -f ./config/crd.yaml
```
You can test that the CRD is successfully added, by creating an [instance](./config/yaml-cr-instance.yaml) of it using kubectl:
diff --git a/examples/customResource/cResource.cs b/examples/customResource/cResource.cs
index 188ee841b..67440aee9 100644
--- a/examples/customResource/cResource.cs
+++ b/examples/customResource/cResource.cs
@@ -1,5 +1,5 @@
using k8s.Models;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace customResource
{
@@ -19,15 +19,15 @@ public override string ToString()
}
}
- public class CResourceSpec
+ public record CResourceSpec
{
- [JsonProperty(PropertyName = "cityName")]
+ [JsonPropertyName("cityName")]
public string CityName { get; set; }
}
- public class CResourceStatus : V1Status
+ public record CResourceStatus : V1Status
{
- [JsonProperty(PropertyName = "temperature", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonPropertyName("temperature")]
public string Temperature { get; set; }
}
}
diff --git a/examples/customResource/customResource.csproj b/examples/customResource/customResource.csproj
index 15c3cfb32..ad2bdd739 100644
--- a/examples/customResource/customResource.csproj
+++ b/examples/customResource/customResource.csproj
@@ -2,14 +2,10 @@
Exe
- net5.0
-
-
-
-
+
diff --git a/examples/exec/Exec.cs b/examples/exec/Exec.cs
index fe796ae21..20bbd2125 100755
--- a/examples/exec/Exec.cs
+++ b/examples/exec/Exec.cs
@@ -1,37 +1,28 @@
-using System;
-using System.Threading.Tasks;
using k8s;
using k8s.Models;
+using System;
+using System.Threading.Tasks;
-namespace exec
-{
- internal class Exec
- {
- private static async Task Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(config);
- Console.WriteLine("Starting Request!");
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
- var list = client.ListNamespacedPod("default");
- var pod = list.Items[0];
- await ExecInPod(client, pod).ConfigureAwait(false);
- }
+var list = client.CoreV1.ListNamespacedPod("default");
+var pod = list.Items[0];
+await ExecInPod(client, pod).ConfigureAwait(false);
- private async static Task ExecInPod(IKubernetes client, V1Pod pod)
- {
- var webSocket =
- await client.WebSocketNamespacedPodExecAsync(pod.Metadata.Name, "default", "ls",
- pod.Spec.Containers[0].Name).ConfigureAwait(false);
+async Task ExecInPod(IKubernetes client, V1Pod pod)
+{
+ var webSocket =
+ await client.WebSocketNamespacedPodExecAsync(pod.Metadata.Name, "default", "ls",
+ pod.Spec.Containers[0].Name).ConfigureAwait(false);
- var demux = new StreamDemuxer(webSocket);
- demux.Start();
+ var demux = new StreamDemuxer(webSocket);
+ demux.Start();
- var buff = new byte[4096];
- var stream = demux.GetStream(1, 1);
- var read = stream.Read(buff, 0, 4096);
- var str = System.Text.Encoding.Default.GetString(buff);
- Console.WriteLine(str);
- }
- }
+ var buff = new byte[4096];
+ var stream = demux.GetStream(1, 1);
+ var read = stream.Read(buff, 0, 4096);
+ var str = System.Text.Encoding.Default.GetString(buff);
+ Console.WriteLine(str);
}
diff --git a/examples/exec/exec.csproj b/examples/exec/exec.csproj
index ecfb5feaf..52e6553de 100755
--- a/examples/exec/exec.csproj
+++ b/examples/exec/exec.csproj
@@ -1,12 +1,7 @@
-
-
-
-
Exe
- net5
diff --git a/examples/generic/Generic.cs b/examples/generic/Generic.cs
index de4525caf..f65fb944d 100644
--- a/examples/generic/Generic.cs
+++ b/examples/generic/Generic.cs
@@ -1,26 +1,16 @@
-using System;
-using System.Threading.Tasks;
using k8s;
using k8s.Models;
+using System;
-namespace exec
-{
- internal class Generic
- {
- private static async Task Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(config);
- var generic = new GenericClient(client, "", "v1", "nodes");
- var node = await generic.ReadAsync("kube0").ConfigureAwait(false);
- Console.WriteLine(node.Metadata.Name);
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+var generic = new GenericClient(client, "", "v1", "nodes");
+var node = await generic.ReadAsync("kube0").ConfigureAwait(false);
+Console.WriteLine(node.Metadata.Name);
- var genericPods = new GenericClient(client, "", "v1", "pods");
- var pods = await genericPods.ListNamespacedAsync("default").ConfigureAwait(false);
- foreach (var pod in pods.Items)
- {
- Console.WriteLine(pod.Metadata.Name);
- }
- }
- }
+var genericPods = new GenericClient(client, "", "v1", "pods");
+var pods = await genericPods.ListNamespacedAsync("default").ConfigureAwait(false);
+foreach (var pod in pods.Items)
+{
+ Console.WriteLine(pod.Metadata.Name);
}
diff --git a/examples/generic/generic.csproj b/examples/generic/generic.csproj
index 651bf9989..52e6553de 100644
--- a/examples/generic/generic.csproj
+++ b/examples/generic/generic.csproj
@@ -1,13 +1,7 @@
-
-
-
-
Exe
- net5
- 7.1
diff --git a/examples/httpClientFactory/PodListHostedService.cs b/examples/httpClientFactory/PodListHostedService.cs
deleted file mode 100644
index c104fc8c8..000000000
--- a/examples/httpClientFactory/PodListHostedService.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using k8s;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace httpClientFactory
-{
- // Learn more about IHostedServices at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio
- public class PodListHostedService : IHostedService
- {
- private readonly IKubernetes _kubernetesClient;
- private readonly ILogger _logger;
-
- public PodListHostedService(IKubernetes kubernetesClient, ILogger logger)
- {
- _kubernetesClient = kubernetesClient ?? throw new ArgumentNullException(nameof(kubernetesClient));
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task StartAsync(CancellationToken cancellationToken)
- {
- _logger.LogInformation("Starting Request!");
-
- var list = await _kubernetesClient.ListNamespacedPodAsync("default", cancellationToken: cancellationToken).ConfigureAwait(false);
- foreach (var item in list.Items)
- {
- _logger.LogInformation(item.Metadata.Name);
- }
-
- if (list.Items.Count == 0)
- {
- _logger.LogInformation("Empty!");
- }
- }
-
- public Task StopAsync(CancellationToken cancellationToken)
- {
- // Nothing to stop
- return Task.CompletedTask;
- }
- }
-}
diff --git a/examples/httpClientFactory/Program.cs b/examples/httpClientFactory/Program.cs
deleted file mode 100644
index e3c332dc0..000000000
--- a/examples/httpClientFactory/Program.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using k8s;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.DependencyInjection;
-using System.Threading.Tasks;
-
-namespace httpClientFactory
-{
- internal class Program
- {
- public static async Task Main(string[] args)
- {
- // Learn more about generic hosts at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host
- using (var host = new HostBuilder()
- .ConfigureLogging((logging) => { logging.AddConsole(); })
- .ConfigureServices((hostBuilderContext, services) =>
- {
- // Ideally this config would be read from the .net core config constructs,
- // but that has not been implemented in the KubernetesClient library at
- // the time this sample was created.
- var config = KubernetesClientConfiguration.BuildDefaultConfig();
- services.AddSingleton(config);
-
- // Setup the http client
- services.AddHttpClient("K8s")
- .AddTypedClient((httpClient, serviceProvider) =>
- {
- return new Kubernetes(
- serviceProvider.GetRequiredService(),
- httpClient);
- })
- .ConfigurePrimaryHttpMessageHandler(config.CreateDefaultHttpClientHandler);
-
- // Add the class that uses the client
- services.AddHostedService();
- })
- .Build())
- {
- await host.StartAsync().ConfigureAwait(false);
- await host.StopAsync().ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/examples/httpClientFactory/httpClientFactory.csproj b/examples/httpClientFactory/httpClientFactory.csproj
deleted file mode 100644
index 0394190f1..000000000
--- a/examples/httpClientFactory/httpClientFactory.csproj
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- Exe
- net5
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/labels/PodList.cs b/examples/labels/PodList.cs
index 89d701ff0..0c5df001d 100755
--- a/examples/labels/PodList.cs
+++ b/examples/labels/PodList.cs
@@ -1,48 +1,39 @@
+using k8s;
using System;
using System.Collections.Generic;
-using k8s;
-namespace simple
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
+
+var list = client.CoreV1.ListNamespacedService("default");
+foreach (var item in list.Items)
{
- internal class PodList
+ Console.WriteLine("Pods for service: " + item.Metadata.Name);
+ Console.WriteLine("=-=-=-=-=-=-=-=-=-=-=");
+ if (item.Spec == null || item.Spec.Selector == null)
{
- private static void Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(config);
- Console.WriteLine("Starting Request!");
-
- var list = client.ListNamespacedService("default");
- foreach (var item in list.Items)
- {
- Console.WriteLine("Pods for service: " + item.Metadata.Name);
- Console.WriteLine("=-=-=-=-=-=-=-=-=-=-=");
- if (item.Spec == null || item.Spec.Selector == null)
- {
- continue;
- }
-
- var labels = new List();
- foreach (var key in item.Spec.Selector)
- {
- labels.Add(key.Key + "=" + key.Value);
- }
+ continue;
+ }
- var labelStr = string.Join(",", labels.ToArray());
- Console.WriteLine(labelStr);
- var podList = client.ListNamespacedPod("default", labelSelector: labelStr);
- foreach (var pod in podList.Items)
- {
- Console.WriteLine(pod.Metadata.Name);
- }
+ var labels = new List();
+ foreach (var key in item.Spec.Selector)
+ {
+ labels.Add(key.Key + "=" + key.Value);
+ }
- if (podList.Items.Count == 0)
- {
- Console.WriteLine("Empty!");
- }
+ var labelStr = string.Join(",", labels.ToArray());
+ Console.WriteLine(labelStr);
+ var podList = client.CoreV1.ListNamespacedPod("default", labelSelector: labelStr);
+ foreach (var pod in podList.Items)
+ {
+ Console.WriteLine(pod.Metadata.Name);
+ }
- Console.WriteLine();
- }
- }
+ if (podList.Items.Count == 0)
+ {
+ Console.WriteLine("Empty!");
}
+
+ Console.WriteLine();
}
diff --git a/examples/labels/labels.csproj b/examples/labels/labels.csproj
index 028b76a31..52e6553de 100755
--- a/examples/labels/labels.csproj
+++ b/examples/labels/labels.csproj
@@ -1,12 +1,7 @@
-
-
-
-
-
+
Exe
- net5
diff --git a/examples/logs/Logs.cs b/examples/logs/Logs.cs
index cdc21c8bc..5293de579 100755
--- a/examples/logs/Logs.cs
+++ b/examples/logs/Logs.cs
@@ -1,31 +1,21 @@
-using System;
-using System.Threading.Tasks;
using k8s;
+using System;
-namespace logs
-{
- internal class Logs
- {
- private static async Task Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(config);
- Console.WriteLine("Starting Request!");
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
- var list = client.ListNamespacedPod("default");
- if (list.Items.Count == 0)
- {
- Console.WriteLine("No pods!");
- return;
- }
+var list = client.CoreV1.ListNamespacedPod("default");
+if (list.Items.Count == 0)
+{
+ Console.WriteLine("No pods!");
+ return;
+}
- var pod = list.Items[0];
+var pod = list.Items[0];
- var response = await client.ReadNamespacedPodLogWithHttpMessagesAsync(
- pod.Metadata.Name,
- pod.Metadata.NamespaceProperty, follow: true).ConfigureAwait(false);
- var stream = response.Body;
- stream.CopyTo(Console.OpenStandardOutput());
- }
- }
-}
+var response = await client.CoreV1.ReadNamespacedPodLogWithHttpMessagesAsync(
+ pod.Metadata.Name,
+ pod.Metadata.NamespaceProperty, container: pod.Spec.Containers[0].Name, follow: true).ConfigureAwait(false);
+var stream = response.Body;
+stream.CopyTo(Console.OpenStandardOutput());
diff --git a/examples/logs/logs.csproj b/examples/logs/logs.csproj
index 028b76a31..52e6553de 100755
--- a/examples/logs/logs.csproj
+++ b/examples/logs/logs.csproj
@@ -1,12 +1,7 @@
-
-
-
-
-
+
Exe
- net5
diff --git a/examples/metrics/Program.cs b/examples/metrics/Program.cs
index 33a779f09..f823bf54d 100644
--- a/examples/metrics/Program.cs
+++ b/examples/metrics/Program.cs
@@ -3,58 +3,49 @@
using System.Linq;
using System.Threading.Tasks;
-namespace metrics
+async Task NodesMetrics(IKubernetes client)
{
- internal class Program
+ var nodesMetrics = await client.GetKubernetesNodesMetricsAsync().ConfigureAwait(false);
+
+ foreach (var item in nodesMetrics.Items)
{
- private static async Task NodesMetrics(IKubernetes client)
+ Console.WriteLine(item.Metadata.Name);
+
+ foreach (var metric in item.Usage)
{
- var nodesMetrics = await client.GetKubernetesNodesMetricsAsync().ConfigureAwait(false);
+ Console.WriteLine($"{metric.Key}: {metric.Value}");
+ }
+ }
+}
- foreach (var item in nodesMetrics.Items)
- {
- Console.WriteLine(item.Metadata.Name);
+async Task PodsMetrics(IKubernetes client)
+{
+ var podsMetrics = await client.GetKubernetesPodsMetricsAsync().ConfigureAwait(false);
- foreach (var metric in item.Usage)
- {
- Console.WriteLine($"{metric.Key}: {metric.Value}");
- }
- }
- }
+ if (!podsMetrics.Items.Any())
+ {
+ Console.WriteLine("Empty");
+ }
- private static async Task PodsMetrics(IKubernetes client)
+ foreach (var item in podsMetrics.Items)
+ {
+ foreach (var container in item.Containers)
{
- var podsMetrics = await client.GetKubernetesPodsMetricsAsync().ConfigureAwait(false);
-
- if (!podsMetrics.Items.Any())
- {
- Console.WriteLine("Empty");
- }
+ Console.WriteLine(container.Name);
- foreach (var item in podsMetrics.Items)
+ foreach (var metric in container.Usage)
{
- foreach (var container in item.Containers)
- {
- Console.WriteLine(container.Name);
-
- foreach (var metric in container.Usage)
- {
- Console.WriteLine($"{metric.Key}: {metric.Value}");
- }
- }
-
- Console.Write(Environment.NewLine);
+ Console.WriteLine($"{metric.Key}: {metric.Value}");
}
}
- private static async Task Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- var client = new Kubernetes(config);
-
- await NodesMetrics(client).ConfigureAwait(false);
- Console.WriteLine(Environment.NewLine);
- await PodsMetrics(client).ConfigureAwait(false);
- }
+ Console.Write(Environment.NewLine);
}
}
+
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+var client = new Kubernetes(config);
+
+await NodesMetrics(client).ConfigureAwait(false);
+Console.WriteLine(Environment.NewLine);
+await PodsMetrics(client).ConfigureAwait(false);
diff --git a/examples/metrics/metrics.csproj b/examples/metrics/metrics.csproj
index 098194a46..52e6553de 100644
--- a/examples/metrics/metrics.csproj
+++ b/examples/metrics/metrics.csproj
@@ -1,13 +1,7 @@
-
+
Exe
- net5
-
-
-
-
-
diff --git a/examples/namespace/NamespaceExample.cs b/examples/namespace/NamespaceExample.cs
index 83fd3c870..06e8757a4 100644
--- a/examples/namespace/NamespaceExample.cs
+++ b/examples/namespace/NamespaceExample.cs
@@ -1,55 +1,40 @@
+using k8s;
+using k8s.Models;
using System;
using System.Net;
using System.Threading.Tasks;
-using k8s;
-using k8s.Models;
-namespace @namespace
+void ListNamespaces(IKubernetes client)
{
- internal class NamespaceExample
+ var list = client.CoreV1.ListNamespace();
+ foreach (var item in list.Items)
{
- private static void ListNamespaces(IKubernetes client)
- {
- var list = client.ListNamespace();
- foreach (var item in list.Items)
- {
- Console.WriteLine(item.Metadata.Name);
- }
+ Console.WriteLine(item.Metadata.Name);
+ }
- if (list.Items.Count == 0)
- {
- Console.WriteLine("Empty!");
- }
- }
+ if (list.Items.Count == 0)
+ {
+ Console.WriteLine("Empty!");
+ }
+}
- private static async Task DeleteAsync(IKubernetes client, string name, int delayMillis)
+async Task DeleteAsync(IKubernetes client, string name, int delayMillis)
+{
+ while (true)
+ {
+ await Task.Delay(delayMillis).ConfigureAwait(false);
+ try
{
- while (true)
+ await client.CoreV1.ReadNamespaceAsync(name).ConfigureAwait(false);
+ }
+ catch (AggregateException ex)
+ {
+ foreach (var innerEx in ex.InnerExceptions)
{
- await Task.Delay(delayMillis).ConfigureAwait(false);
- try
- {
- await client.ReadNamespaceAsync(name).ConfigureAwait(false);
- }
- catch (AggregateException ex)
- {
- foreach (var innerEx in ex.InnerExceptions)
- {
- if (innerEx is Microsoft.Rest.HttpOperationException)
- {
- var code = ((Microsoft.Rest.HttpOperationException)innerEx).Response.StatusCode;
- if (code == HttpStatusCode.NotFound)
- {
- return;
- }
-
- throw;
- }
- }
- }
- catch (Microsoft.Rest.HttpOperationException ex)
+ if (innerEx is k8s.Autorest.HttpOperationException exception)
{
- if (ex.Response.StatusCode == HttpStatusCode.NotFound)
+ var code = exception.Response.StatusCode;
+ if (code == HttpStatusCode.NotFound)
{
return;
}
@@ -58,41 +43,47 @@ private static async Task DeleteAsync(IKubernetes client, string name, int delay
}
}
}
-
- private static void Delete(IKubernetes client, string name, int delayMillis)
+ catch (k8s.Autorest.HttpOperationException ex)
{
- DeleteAsync(client, name, delayMillis).Wait();
+ if (ex.Response.StatusCode == HttpStatusCode.NotFound)
+ {
+ return;
+ }
+
+ throw;
}
+ }
+}
- private static void Main(string[] args)
- {
- var k8SClientConfig = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(k8SClientConfig);
+void Delete(IKubernetes client, string name, int delayMillis)
+{
+ DeleteAsync(client, name, delayMillis).Wait();
+}
- ListNamespaces(client);
+var k8SClientConfig = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(k8SClientConfig);
- var ns = new V1Namespace { Metadata = new V1ObjectMeta { Name = "test" } };
+ListNamespaces(client);
- var result = client.CreateNamespace(ns);
- Console.WriteLine(result);
+var ns = new V1Namespace { Metadata = new V1ObjectMeta { Name = "test" } };
- ListNamespaces(client);
+var result = client.CoreV1.CreateNamespace(ns);
+Console.WriteLine(result);
- var status = client.DeleteNamespace(ns.Metadata.Name, new V1DeleteOptions());
+ListNamespaces(client);
- if (status.HasObject)
- {
- var obj = status.ObjectView();
- Console.WriteLine(obj.Status.Phase);
+var status = client.CoreV1.DeleteNamespace(ns.Metadata.Name, new V1DeleteOptions());
- Delete(client, ns.Metadata.Name, 3 * 1000);
- }
- else
- {
- Console.WriteLine(status.Message);
- }
+if (status.HasObject)
+{
+ var obj = status.ObjectView();
+ Console.WriteLine(obj.Status.Phase);
- ListNamespaces(client);
- }
- }
+ Delete(client, ns.Metadata.Name, 3 * 1000);
}
+else
+{
+ Console.WriteLine(status.Message);
+}
+
+ListNamespaces(client);
diff --git a/examples/namespace/namespace.csproj b/examples/namespace/namespace.csproj
index 29e6232c7..f850aa91d 100644
--- a/examples/namespace/namespace.csproj
+++ b/examples/namespace/namespace.csproj
@@ -1,12 +1,7 @@
-
-
-
-
-
+
Exe
- net5
diff --git a/examples/nginx.yml b/examples/nginx.yml
deleted file mode 100644
index b92a713ba..000000000
--- a/examples/nginx.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: v1
-kind: Pod
-metadata:
- name: nginx
-spec:
- containers:
- - name: nginx
- image: nginx:1.7.9
- ports:
- - containerPort: 80
\ No newline at end of file
diff --git a/examples/openTelemetryConsole/Program.cs b/examples/openTelemetryConsole/Program.cs
new file mode 100644
index 000000000..4b7406be3
--- /dev/null
+++ b/examples/openTelemetryConsole/Program.cs
@@ -0,0 +1,37 @@
+using k8s;
+using OpenTelemetry;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+
+var serviceName = "MyCompany.MyProduct.MyService";
+var serviceVersion = "1.0.0";
+
+// Create the OpenTelemetry TraceProvide with HttpClient instrumentation enabled
+// NOTE: for this example telemetry will be exported to console
+using var tracerProvider = Sdk.CreateTracerProviderBuilder()
+ .AddSource(serviceName)
+ .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName: serviceName, serviceVersion: serviceVersion))
+ .AddHttpClientInstrumentation()
+ .AddConsoleExporter()
+ .Build();
+
+// Load kubernetes configuration
+var config = KubernetesClientConfiguration.BuildDefaultConfig();
+
+// Create an istance of Kubernetes client
+IKubernetes client = new Kubernetes(config);
+
+// Read the list of pods contained in default namespace
+var list = client.CoreV1.ListNamespacedPod("default");
+
+// Print the name of pods
+foreach (var item in list.Items)
+{
+ Console.WriteLine(item.Metadata.Name);
+}
+
+// Or empty if there are no pods
+if (list.Items.Count == 0)
+{
+ Console.WriteLine("Empty!");
+}
diff --git a/examples/openTelemetryConsole/openTelemetryConsole.csproj b/examples/openTelemetryConsole/openTelemetryConsole.csproj
new file mode 100644
index 000000000..ff48d4450
--- /dev/null
+++ b/examples/openTelemetryConsole/openTelemetryConsole.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/examples/patch-aot/Program.cs b/examples/patch-aot/Program.cs
new file mode 100644
index 000000000..e72f6a4d2
--- /dev/null
+++ b/examples/patch-aot/Program.cs
@@ -0,0 +1,33 @@
+using k8s;
+using k8s.Models;
+
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
+
+var pod = client.CoreV1.ListNamespacedPod("default").Items.First();
+var name = pod.Metadata.Name;
+PrintLabels(pod);
+
+var patchStr = @"
+{
+ ""metadata"": {
+ ""labels"": {
+ ""test"": ""test""
+ }
+ }
+}";
+
+client.CoreV1.PatchNamespacedPod(new V1Patch(patchStr, V1Patch.PatchType.MergePatch), name, "default");
+PrintLabels(client.CoreV1.ReadNamespacedPod(name, "default"));
+
+static void PrintLabels(V1Pod pod)
+{
+ Console.WriteLine($"Labels: for {pod.Metadata.Name}");
+ foreach (var (k, v) in pod.Metadata.Labels)
+ {
+ Console.WriteLine($"{k} : {v}");
+ }
+
+ Console.WriteLine("=-=-=-=-=-=-=-=-=-=-=");
+}
diff --git a/examples/patch-aot/patch-aot.csproj b/examples/patch-aot/patch-aot.csproj
new file mode 100644
index 000000000..c2c806215
--- /dev/null
+++ b/examples/patch-aot/patch-aot.csproj
@@ -0,0 +1,11 @@
+
+
+ Exe
+ enable
+ enable
+ true
+
+
+
+
+
diff --git a/examples/patch/Program.cs b/examples/patch/Program.cs
index 8d7a4a271..f8cefa67c 100644
--- a/examples/patch/Program.cs
+++ b/examples/patch/Program.cs
@@ -1,23 +1,17 @@
-using System;
-using System.Linq;
using k8s;
using k8s.Models;
+using System;
+using System.Linq;
-namespace patch
-{
- internal class Program
- {
- private static void Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(config);
- Console.WriteLine("Starting Request!");
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
- var pod = client.ListNamespacedPod("default").Items.First();
- var name = pod.Metadata.Name;
- PrintLabels(pod);
+var pod = client.CoreV1.ListNamespacedPod("default").Items.First();
+var name = pod.Metadata.Name;
+PrintLabels(pod);
- var patchStr = @"
+var patchStr = @"
{
""metadata"": {
""labels"": {
@@ -26,19 +20,16 @@ private static void Main(string[] args)
}
}";
- client.PatchNamespacedPod(new V1Patch(patchStr, V1Patch.PatchType.MergePatch), name, "default");
- PrintLabels(client.ReadNamespacedPod(name, "default"));
- }
-
- private static void PrintLabels(V1Pod pod)
- {
- Console.WriteLine($"Labels: for {pod.Metadata.Name}");
- foreach (var (k, v) in pod.Metadata.Labels)
- {
- Console.WriteLine($"{k} : {v}");
- }
+client.CoreV1.PatchNamespacedPod(new V1Patch(patchStr, V1Patch.PatchType.MergePatch), name, "default");
+PrintLabels(client.CoreV1.ReadNamespacedPod(name, "default"));
- Console.WriteLine("=-=-=-=-=-=-=-=-=-=-=");
- }
+void PrintLabels(V1Pod pod)
+{
+ Console.WriteLine($"Labels: for {pod.Metadata.Name}");
+ foreach (var (k, v) in pod.Metadata.Labels)
+ {
+ Console.WriteLine($"{k} : {v}");
}
+
+ Console.WriteLine("=-=-=-=-=-=-=-=-=-=-=");
}
diff --git a/examples/patch/patch.csproj b/examples/patch/patch.csproj
index 131fa5d70..f850aa91d 100644
--- a/examples/patch/patch.csproj
+++ b/examples/patch/patch.csproj
@@ -1,12 +1,7 @@
-
+
Exe
- net5
-
-
-
-
diff --git a/examples/portforward/PortForward.cs b/examples/portforward/PortForward.cs
index 710f36ad7..ee095e073 100644
--- a/examples/portforward/PortForward.cs
+++ b/examples/portforward/PortForward.cs
@@ -1,71 +1,71 @@
+using k8s;
+using k8s.Models;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
-using k8s;
-using k8s.Models;
-namespace portforward
-{
- internal class Portforward
- {
- private static async Task Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(config);
- Console.WriteLine("Starting port forward!");
-
- var list = client.ListNamespacedPod("default");
- var pod = list.Items[0];
- await Forward(client, pod);
- }
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting port forward!");
- private async static Task Forward(IKubernetes client, V1Pod pod) {
- // Note this is single-threaded, it won't handle concurrent requests well...
- var webSocket = await client.WebSocketNamespacedPodPortForwardAsync(pod.Metadata.Name, "default", new int[] {80}, "v4.channel.k8s.io");
- var demux = new StreamDemuxer(webSocket, StreamType.PortForward);
- demux.Start();
+var list = client.CoreV1.ListNamespacedPod("default");
+var pod = list.Items[0];
+await Forward(client, pod).ConfigureAwait(false);
- var stream = demux.GetStream((byte?)0, (byte?)0);
+async Task Forward(IKubernetes client, V1Pod pod)
+{
+ // Note this is single-threaded, it won't handle concurrent requests well...
+ var webSocket = await client.WebSocketNamespacedPodPortForwardAsync(pod.Metadata.Name, "default", new int[] { 80 }, "v4.channel.k8s.io").ConfigureAwait(false);
+ var demux = new StreamDemuxer(webSocket, StreamType.PortForward);
+ demux.Start();
- IPAddress ipAddress = IPAddress.Loopback;
- IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 8080);
- Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
- listener.Bind(localEndPoint);
- listener.Listen(100);
+ var stream = demux.GetStream((byte?)0, (byte?)0);
- Socket handler = null;
+ IPAddress ipAddress = IPAddress.Loopback;
+ IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 8080);
+ Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+ listener.Bind(localEndPoint);
+ listener.Listen(100);
- // Note this will only accept a single connection
- var accept = Task.Run(() => {
- while (true) {
- handler = listener.Accept();
- var bytes = new byte[4096];
- while (true) {
- int bytesRec = handler.Receive(bytes);
- stream.Write(bytes, 0, bytesRec);
- if (bytesRec == 0 || Encoding.ASCII.GetString(bytes,0,bytesRec).IndexOf("") > -1) {
- break;
- }
- }
- }
- });
+ Socket handler = null;
- var copy = Task.Run(() => {
- var buff = new byte[4096];
- while (true) {
- var read = stream.Read(buff, 0, 4096);
- handler.Send(buff, read, 0);
+ // Note this will only accept a single connection
+ var accept = Task.Run(() =>
+ {
+ while (true)
+ {
+ handler = listener.Accept();
+ var bytes = new byte[4096];
+ while (true)
+ {
+ int bytesRec = handler.Receive(bytes);
+ stream.Write(bytes, 0, bytesRec);
+ if (bytesRec == 0 || Encoding.ASCII.GetString(bytes, 0, bytesRec).IndexOf("") > -1)
+ {
+ break;
}
- });
-
- await accept;
- await copy;
- if (handler != null) {
- handler.Close();
}
- listener.Close();
}
+ });
+
+ var copy = Task.Run(() =>
+ {
+ var buff = new byte[4096];
+ while (true)
+ {
+ var read = stream.Read(buff, 0, 4096);
+ handler.Send(buff, read, 0);
+ }
+ });
+
+ await accept.ConfigureAwait(false);
+ await copy.ConfigureAwait(false);
+ if (handler != null)
+ {
+ handler.Close();
}
+
+ listener.Close();
}
diff --git a/examples/portforward/portforward.csproj b/examples/portforward/portforward.csproj
index 2f90dd432..e3b6154bb 100644
--- a/examples/portforward/portforward.csproj
+++ b/examples/portforward/portforward.csproj
@@ -1,13 +1,7 @@
-
-
-
-
Exe
- netcoreapp2.1
- 7.1
\ No newline at end of file
diff --git a/examples/prometheus/Prometheus.cs b/examples/prometheus/Prometheus.cs
deleted file mode 100755
index b15f3618e..000000000
--- a/examples/prometheus/Prometheus.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-using System.Net.Http;
-using System.Threading;
-using k8s;
-using k8s.Monitoring;
-using Prometheus;
-
-namespace prom
-{
- internal class Prometheus
- {
- private static void Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildDefaultConfig();
- var handler = new PrometheusHandler();
- IKubernetes client = new Kubernetes(config, new DelegatingHandler[] { handler });
-
- var server = new MetricServer(hostname: "localhost", port: 1234);
- server.Start();
-
- Console.WriteLine("Making requests!");
- while (true)
- {
- client.ListNamespacedPod("default");
- client.ListNode();
- client.ListNamespacedDeployment("default");
- Thread.Sleep(1000);
- }
- }
- }
-}
diff --git a/examples/prometheus/prometheus.csproj b/examples/prometheus/prometheus.csproj
deleted file mode 100755
index 028b76a31..000000000
--- a/examples/prometheus/prometheus.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
- Exe
- net5
-
-
-
diff --git a/examples/resize/Program.cs b/examples/resize/Program.cs
new file mode 100644
index 000000000..85fbeb9b2
--- /dev/null
+++ b/examples/resize/Program.cs
@@ -0,0 +1,63 @@
+using k8s;
+using k8s.Models;
+using System;
+using System.Collections.Generic;
+
+
+var config = KubernetesClientConfiguration.BuildDefaultConfig();
+var client = new Kubernetes(config);
+
+
+var pod = new V1Pod
+{
+ Metadata = new V1ObjectMeta { Name = "nginx-pod" },
+ Spec = new V1PodSpec
+ {
+ Containers =
+ [
+ new V1Container
+ {
+ Name = "nginx",
+ Image = "nginx",
+ Resources = new V1ResourceRequirements
+ {
+ Requests = new Dictionary()
+ {
+ ["cpu"] = "100m",
+ },
+ },
+ },
+ ],
+ },
+};
+{
+ var created = await client.CoreV1.CreateNamespacedPodAsync(pod, "default").ConfigureAwait(false);
+ Console.WriteLine($"Created pod: {created.Metadata.Name}");
+}
+
+{
+ var patchStr = @"
+ {
+ ""spec"": {
+ ""containers"": [
+ {
+ ""name"": ""nginx"",
+ ""resources"": {
+ ""requests"": {
+ ""cpu"": ""200m""
+ }
+ }
+ }
+ ]
+ }
+ }";
+
+ var patch = await client.CoreV1.PatchNamespacedPodResizeAsync(new V1Patch(patchStr, V1Patch.PatchType.MergePatch), "nginx-pod", "default").ConfigureAwait(false);
+
+ if (patch?.Spec?.Containers?.Count > 0 &&
+ patch.Spec.Containers[0].Resources?.Requests != null &&
+ patch.Spec.Containers[0].Resources.Requests.TryGetValue("cpu", out var cpuQty))
+ {
+ Console.WriteLine($"CPU request: {cpuQty}");
+ }
+}
diff --git a/examples/resize/resize.csproj b/examples/resize/resize.csproj
new file mode 100644
index 000000000..d1e5b4724
--- /dev/null
+++ b/examples/resize/resize.csproj
@@ -0,0 +1,5 @@
+
+
+ Exe
+
+
\ No newline at end of file
diff --git a/examples/restart/Program.cs b/examples/restart/Program.cs
new file mode 100644
index 000000000..894e305a6
--- /dev/null
+++ b/examples/restart/Program.cs
@@ -0,0 +1,68 @@
+using Json.Patch;
+using k8s;
+using k8s.Models;
+using System.Text.Json;
+
+async Task RestartDaemonSetAsync(string name, string @namespace, IKubernetes client)
+{
+ var daemonSet = await client.AppsV1.ReadNamespacedDaemonSetAsync(name, @namespace).ConfigureAwait(false);
+ var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
+ var old = JsonSerializer.SerializeToDocument(daemonSet, options);
+ var now = DateTimeOffset.Now.ToUnixTimeSeconds();
+ var restart = new Dictionary
+ {
+ ["date"] = now.ToString(),
+ };
+
+ daemonSet.Spec.Template.Metadata.Annotations = restart;
+
+ var expected = JsonSerializer.SerializeToDocument(daemonSet);
+
+ var patch = old.CreatePatch(expected);
+ await client.AppsV1.PatchNamespacedDaemonSetAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace).ConfigureAwait(false);
+}
+
+async Task RestartDeploymentAsync(string name, string @namespace, IKubernetes client)
+{
+ var deployment = await client.AppsV1.ReadNamespacedDeploymentAsync(name, @namespace).ConfigureAwait(false);
+ var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
+ var old = JsonSerializer.SerializeToDocument(deployment, options);
+ var now = DateTimeOffset.Now.ToUnixTimeSeconds();
+ var restart = new Dictionary
+ {
+ ["date"] = now.ToString(),
+ };
+
+ deployment.Spec.Template.Metadata.Annotations = restart;
+
+ var expected = JsonSerializer.SerializeToDocument(deployment);
+
+ var patch = old.CreatePatch(expected);
+ await client.AppsV1.PatchNamespacedDeploymentAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace).ConfigureAwait(false);
+}
+
+async Task RestartStatefulSetAsync(string name, string @namespace, IKubernetes client)
+{
+ var deployment = await client.AppsV1.ReadNamespacedStatefulSetAsync(name, @namespace).ConfigureAwait(false);
+ var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
+ var old = JsonSerializer.SerializeToDocument(deployment, options);
+ var now = DateTimeOffset.Now.ToUnixTimeSeconds();
+ var restart = new Dictionary
+ {
+ ["date"] = now.ToString(),
+ };
+
+ deployment.Spec.Template.Metadata.Annotations = restart;
+
+ var expected = JsonSerializer.SerializeToDocument(deployment);
+
+ var patch = old.CreatePatch(expected);
+ await client.AppsV1.PatchNamespacedStatefulSetAsync(new V1Patch(patch, V1Patch.PatchType.JsonPatch), name, @namespace).ConfigureAwait(false);
+}
+
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+IKubernetes client = new Kubernetes(config);
+
+await RestartDeploymentAsync("event-exporter", "monitoring", client).ConfigureAwait(false);
+await RestartDaemonSetAsync("prometheus-exporter", "monitoring", client).ConfigureAwait(false);
+await RestartStatefulSetAsync("argocd-application-controlle", "argocd", client).ConfigureAwait(false);
diff --git a/examples/GenericKubernetesApi/GenericKubernetesApi.csproj b/examples/restart/restart.csproj
similarity index 53%
rename from examples/GenericKubernetesApi/GenericKubernetesApi.csproj
rename to examples/restart/restart.csproj
index d9f686266..0d5a49c4c 100644
--- a/examples/GenericKubernetesApi/GenericKubernetesApi.csproj
+++ b/examples/restart/restart.csproj
@@ -2,11 +2,12 @@
Exe
- net5.0
+ enable
+ enable
-
+
diff --git a/examples/simple/PodList.cs b/examples/simple/PodList.cs
index 54f36e91d..751622c16 100755
--- a/examples/simple/PodList.cs
+++ b/examples/simple/PodList.cs
@@ -1,26 +1,17 @@
-using System;
using k8s;
+using System;
-namespace simple
-{
- internal class PodList
- {
- private static void Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildDefaultConfig();
- IKubernetes client = new Kubernetes(config);
- Console.WriteLine("Starting Request!");
+var config = KubernetesClientConfiguration.BuildDefaultConfig();
+IKubernetes client = new Kubernetes(config);
+Console.WriteLine("Starting Request!");
- var list = client.ListNamespacedPod("default");
- foreach (var item in list.Items)
- {
- Console.WriteLine(item.Metadata.Name);
- }
+var list = client.CoreV1.ListNamespacedPod("default");
+foreach (var item in list.Items)
+{
+ Console.WriteLine(item.Metadata.Name);
+}
- if (list.Items.Count == 0)
- {
- Console.WriteLine("Empty!");
- }
- }
- }
+if (list.Items.Count == 0)
+{
+ Console.WriteLine("Empty!");
}
diff --git a/examples/simple/simple.csproj b/examples/simple/simple.csproj
index 028b76a31..52e6553de 100755
--- a/examples/simple/simple.csproj
+++ b/examples/simple/simple.csproj
@@ -1,12 +1,7 @@
-
-
-
-
-
+
Exe
- net5
diff --git a/examples/watch/Program.cs b/examples/watch/Program.cs
index 8b8092275..1aff65883 100644
--- a/examples/watch/Program.cs
+++ b/examples/watch/Program.cs
@@ -1,51 +1,39 @@
+using k8s;
using System;
using System.Threading;
using System.Threading.Tasks;
-using k8s;
-using k8s.Models;
-using Microsoft.Rest;
-namespace watch
-{
- internal class Program
- {
- private async static Task Main(string[] args)
- {
- var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
+var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();
- IKubernetes client = new Kubernetes(config);
+IKubernetes client = new Kubernetes(config);
- var podlistResp = client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
- // C# 8 required https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/november/csharp-iterating-with-async-enumerables-in-csharp-8
- await foreach (var (type, item) in podlistResp.WatchAsync())
- {
- Console.WriteLine("==on watch event==");
- Console.WriteLine(type);
- Console.WriteLine(item.Metadata.Name);
- Console.WriteLine("==on watch event==");
- }
+var podlistResp = client.CoreV1.WatchListNamespacedPodAsync("default");
- // uncomment if you prefer callback api
- // WatchUsingCallback(client);
- }
+// C# 8 required https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/november/csharp-iterating-with-async-enumerables-in-csharp-8
+await foreach (var (type, item) in podlistResp.ConfigureAwait(false))
+{
+ Console.WriteLine("==on watch event==");
+ Console.WriteLine(type);
+ Console.WriteLine(item.Metadata.Name);
+ Console.WriteLine("==on watch event==");
+}
- private static void WatchUsingCallback(IKubernetes client)
- {
- var podlistResp = client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
- using (podlistResp.Watch((type, item) =>
- {
- Console.WriteLine("==on watch event==");
- Console.WriteLine(type);
- Console.WriteLine(item.Metadata.Name);
- Console.WriteLine("==on watch event==");
- }))
- {
- Console.WriteLine("press ctrl + c to stop watching");
+#pragma warning disable CS8321 // Remove unused private members
+void WatchUsingCallback(IKubernetes client)
+#pragma warning restore CS8321 // Remove unused private members
+{
+ using (var podlistResp = client.CoreV1.WatchListNamespacedPod("default", onEvent: (type, item) =>
+ {
+ Console.WriteLine("==on watch event==");
+ Console.WriteLine(type);
+ Console.WriteLine(item.Metadata.Name);
+ Console.WriteLine("==on watch event==");
+ }))
+ {
+ Console.WriteLine("press ctrl + c to stop watching");
- var ctrlc = new ManualResetEventSlim(false);
- Console.CancelKeyPress += (sender, eventArgs) => ctrlc.Set();
- ctrlc.Wait();
- }
- }
+ var ctrlc = new ManualResetEventSlim(false);
+ Console.CancelKeyPress += (sender, eventArgs) => ctrlc.Set();
+ ctrlc.Wait();
}
-}
+}
\ No newline at end of file
diff --git a/examples/watch/watch.csproj b/examples/watch/watch.csproj
index 89f728c57..f850aa91d 100644
--- a/examples/watch/watch.csproj
+++ b/examples/watch/watch.csproj
@@ -1,12 +1,7 @@
-
+
Exe
- net5
-
-
-
-
diff --git a/examples/webApiDependencyInjection/Controllers/ExampleDependencyInjectionOnConstructorController.cs b/examples/webApiDependencyInjection/Controllers/ExampleDependencyInjectionOnConstructorController.cs
new file mode 100644
index 000000000..6bff6df0d
--- /dev/null
+++ b/examples/webApiDependencyInjection/Controllers/ExampleDependencyInjectionOnConstructorController.cs
@@ -0,0 +1,36 @@
+using k8s;
+using Microsoft.AspNetCore.Mvc;
+
+namespace webApiDependencyInjection.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class ExampleDependencyInjectionOnConstructorController : ControllerBase
+ {
+ private readonly IKubernetes kubernetesClient;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Injects the Kubernetes client into the controller.
+ ///
+ /// The Kubernetes client to interact with the Kubernetes API.
+ public ExampleDependencyInjectionOnConstructorController(IKubernetes kubernetesClient)
+ {
+ this.kubernetesClient = kubernetesClient;
+ }
+
+ ///
+ /// Retrieves the names of all pods in the default namespace using the injected Kubernetes client.
+ ///
+ /// A collection of pod names in the default namespace.
+ [HttpGet]
+ public IEnumerable GetPods()
+ {
+ // Read the list of pods contained in the default namespace
+ var podList = this.kubernetesClient.CoreV1.ListNamespacedPod("default");
+
+ // Return names of pods
+ return podList.Items.Select(pod => pod.Metadata.Name);
+ }
+ }
+}
diff --git a/examples/webApiDependencyInjection/Controllers/ExampleDependencyInjectionOnMethodController.cs b/examples/webApiDependencyInjection/Controllers/ExampleDependencyInjectionOnMethodController.cs
new file mode 100644
index 000000000..84427f5e2
--- /dev/null
+++ b/examples/webApiDependencyInjection/Controllers/ExampleDependencyInjectionOnMethodController.cs
@@ -0,0 +1,27 @@
+using k8s;
+using Microsoft.AspNetCore.Mvc;
+
+namespace webApiDependencyInjection.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class ExampleDependencyInjectionOnMethodController : ControllerBase
+ {
+ ///
+ /// Example using the kubernetes client injected directly into the method ([FromServices] IKubernetes kubernetesClient).
+ ///
+ /// The Kubernetes client instance injected via dependency injection.
+ /// A collection of pod names in the default namespace.
+ [HttpGet]
+ public IEnumerable GetPods([FromServices] IKubernetes kubernetesClient)
+ {
+ ArgumentNullException.ThrowIfNull(kubernetesClient);
+
+ // Read the list of pods contained in default namespace
+ var podList = kubernetesClient.CoreV1.ListNamespacedPod("default");
+
+ // Return names of pods
+ return podList.Items.Select(pod => pod.Metadata.Name);
+ }
+ }
+}
diff --git a/examples/webApiDependencyInjection/Program.cs b/examples/webApiDependencyInjection/Program.cs
new file mode 100644
index 000000000..3ec631b65
--- /dev/null
+++ b/examples/webApiDependencyInjection/Program.cs
@@ -0,0 +1,34 @@
+using k8s;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Load kubernetes configuration
+var kubernetesClientConfig = KubernetesClientConfiguration.BuildDefaultConfig();
+
+// Register Kubernetes client interface as sigleton
+builder.Services.AddSingleton(_ => new Kubernetes(kubernetesClientConfig));
+
+// Add services to the container.
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+builder.Services.AddControllers();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseAuthorization();
+
+app.MapControllers();
+
+// Start the service
+app.Run();
+
+
+// Swagger ui can be accesse at: http://localhost:/swagger
diff --git a/examples/webApiDependencyInjection/appsettings.Development.json b/examples/webApiDependencyInjection/appsettings.Development.json
new file mode 100644
index 000000000..0c208ae91
--- /dev/null
+++ b/examples/webApiDependencyInjection/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/examples/webApiDependencyInjection/appsettings.json b/examples/webApiDependencyInjection/appsettings.json
new file mode 100644
index 000000000..10f68b8c8
--- /dev/null
+++ b/examples/webApiDependencyInjection/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/examples/webApiDependencyInjection/webApiDependencyInjection.csproj b/examples/webApiDependencyInjection/webApiDependencyInjection.csproj
new file mode 100644
index 000000000..23e466d7e
--- /dev/null
+++ b/examples/webApiDependencyInjection/webApiDependencyInjection.csproj
@@ -0,0 +1,12 @@
+
+
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/examples/workerServiceDependencyInjection/Program.cs b/examples/workerServiceDependencyInjection/Program.cs
new file mode 100644
index 000000000..a894a33fe
--- /dev/null
+++ b/examples/workerServiceDependencyInjection/Program.cs
@@ -0,0 +1,17 @@
+using k8s;
+using workerServiceDependencyInjection;
+
+IHost host = Host.CreateDefaultBuilder(args)
+ .ConfigureServices(services =>
+ {
+ // Load kubernetes configuration
+ var kubernetesClientConfig = KubernetesClientConfiguration.BuildDefaultConfig();
+
+ // Register Kubernetes client interface as sigleton
+ services.AddSingleton(_ => new Kubernetes(kubernetesClientConfig));
+
+ services.AddHostedService();
+ })
+ .Build();
+
+await host.RunAsync().ConfigureAwait(false);
diff --git a/examples/workerServiceDependencyInjection/Worker.cs b/examples/workerServiceDependencyInjection/Worker.cs
new file mode 100644
index 000000000..cb2f82386
--- /dev/null
+++ b/examples/workerServiceDependencyInjection/Worker.cs
@@ -0,0 +1,41 @@
+using k8s;
+
+namespace workerServiceDependencyInjection
+{
+ public class Worker : BackgroundService
+ {
+ private readonly ILogger logger;
+ private readonly IKubernetes kubernetesClient;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Inject in the constructor the IKubernetes interface.
+ ///
+ /// The logger instance used for logging information.
+ /// The Kubernetes client used to interact with the Kubernetes API.
+ public Worker(ILogger logger, IKubernetes kubernetesClient)
+ {
+ this.logger = logger;
+ this.kubernetesClient = kubernetesClient;
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
+
+ // Read the list of pods contained in default namespace
+ var podList = kubernetesClient.CoreV1.ListNamespacedPod("default");
+
+ // Print pods names
+ foreach (var pod in podList.Items)
+ {
+ Console.WriteLine(pod.Metadata.Name);
+ }
+
+ await Task.Delay(1000, stoppingToken).ConfigureAwait(false);
+ }
+ }
+ }
+}
diff --git a/examples/workerServiceDependencyInjection/appsettings.Development.json b/examples/workerServiceDependencyInjection/appsettings.Development.json
new file mode 100644
index 000000000..b2dcdb674
--- /dev/null
+++ b/examples/workerServiceDependencyInjection/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/examples/workerServiceDependencyInjection/appsettings.json b/examples/workerServiceDependencyInjection/appsettings.json
new file mode 100644
index 000000000..b2dcdb674
--- /dev/null
+++ b/examples/workerServiceDependencyInjection/appsettings.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/examples/workerServiceDependencyInjection/workerServiceDependencyInjection.csproj b/examples/workerServiceDependencyInjection/workerServiceDependencyInjection.csproj
new file mode 100644
index 000000000..84522ab7c
--- /dev/null
+++ b/examples/workerServiceDependencyInjection/workerServiceDependencyInjection.csproj
@@ -0,0 +1,11 @@
+
+
+
+ enable
+ enable
+
+
+
+
+
+
diff --git a/examples/yaml/Program.cs b/examples/yaml/Program.cs
index 88fc4ac12..47b70bdfe 100644
--- a/examples/yaml/Program.cs
+++ b/examples/yaml/Program.cs
@@ -1,26 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
using k8s;
using k8s.Models;
+using System;
+using System.Collections.Generic;
-namespace yaml
+var typeMap = new Dictionary
{
- internal class Program
- {
- private async static Task Main(string[] args)
- {
- var typeMap = new Dictionary();
- typeMap.Add("v1/Pod", typeof(V1Pod));
- typeMap.Add("v1/Service", typeof(V1Service));
- typeMap.Add("apps/v1beta1/Deployment", typeof(Appsv1beta1Deployment));
+ { "v1/Pod", typeof(V1Pod) },
+ { "v1/Service", typeof(V1Service) },
+ { "apps/v1/Deployment", typeof(V1Deployment) },
+};
- var objects = await Yaml.LoadAllFromFileAsync(args[0], typeMap);
+var objects = await KubernetesYaml.LoadAllFromFileAsync(args[0], typeMap).ConfigureAwait(false);
- foreach (var obj in objects) {
- Console.WriteLine(obj);
- }
- }
- }
+foreach (var obj in objects)
+{
+ Console.WriteLine(obj);
}
diff --git a/examples/yaml/yaml.csproj b/examples/yaml/yaml.csproj
index a6cdcb9a5..d1e5b4724 100644
--- a/examples/yaml/yaml.csproj
+++ b/examples/yaml/yaml.csproj
@@ -1,12 +1,5 @@
-
-
-
-
-
Exe
- netcoreapp2.0
-
\ No newline at end of file
diff --git a/gen/KubernetesGenerator/ApiGenerator.cs b/gen/KubernetesGenerator/ApiGenerator.cs
deleted file mode 100644
index 4603ad04e..000000000
--- a/gen/KubernetesGenerator/ApiGenerator.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using NSwag;
-using Nustache.Core;
-
-namespace KubernetesGenerator
-{
- internal class ApiGenerator
- {
- public void Generate(OpenApiDocument swagger, string outputDirectory)
- {
- var data = swagger.Operations
- .Where(o => o.Method != OpenApiOperationMethod.Options)
- .GroupBy(o => o.Operation.OperationId)
- .Select(g =>
- {
- var gs = g.ToArray();
-
- for (var i = 1; i < g.Count(); i++)
- {
- gs[i].Operation.OperationId += i;
- }
-
- return gs;
- })
- .SelectMany(g => g)
- .Select(o =>
- {
- var ps = o.Operation.ActualParameters.OrderBy(p => !p.IsRequired).ToArray();
-
- o.Operation.Parameters.Clear();
-
- var name = new HashSet();
-
- var i = 1;
- foreach (var p in ps)
- {
- if (name.Contains(p.Name))
- {
- p.Name = p.Name + i++;
- }
-
- o.Operation.Parameters.Add(p);
- name.Add(p.Name);
- }
-
- return o;
- })
- .Select(o =>
- {
- o.Path = o.Path.TrimStart('/');
- o.Method = char.ToUpper(o.Method[0]) + o.Method.Substring(1);
- return o;
- })
- .ToArray();
-
- Render.FileToFile(Path.Combine("templates", "IKubernetes.cs.template"), data,
- Path.Combine(outputDirectory, "IKubernetes.cs"));
- Render.FileToFile(Path.Combine("templates", "Kubernetes.cs.template"), data,
- Path.Combine(outputDirectory, "Kubernetes.cs"));
- Render.FileToFile(Path.Combine("templates", "KubernetesExtensions.cs.template"), data,
- Path.Combine(outputDirectory, "KubernetesExtensions.cs"));
- }
- }
-}
diff --git a/gen/KubernetesGenerator/ClassNameHelper.cs b/gen/KubernetesGenerator/ClassNameHelper.cs
deleted file mode 100644
index 9ed87cb56..000000000
--- a/gen/KubernetesGenerator/ClassNameHelper.cs
+++ /dev/null
@@ -1,152 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using CaseExtensions;
-using NJsonSchema;
-using NSwag;
-using Nustache.Core;
-
-namespace KubernetesGenerator
-{
- internal class ClassNameHelper : INustacheHelper
- {
- private readonly Dictionary classNameMap;
- private readonly HashSet schemaDefinitionsInMultipleGroups;
- private readonly Dictionary schemaToNameMapCooked;
- private readonly Dictionary schemaToNameMapUnprocessed;
-
- public ClassNameHelper(OpenApiDocument swaggerCooked, OpenApiDocument swaggerUnprocessed)
- {
- classNameMap = InitClassNameMap(swaggerCooked);
-
- schemaToNameMapCooked = GenerateSchemaToNameMapCooked(swaggerCooked);
- schemaToNameMapUnprocessed = GenerateSchemaToNameMapUnprocessed(swaggerUnprocessed);
- schemaDefinitionsInMultipleGroups = InitSchemaDefinitionsInMultipleGroups(schemaToNameMapUnprocessed);
- }
-
- public void RegisterHelper()
- {
- Helpers.Register(nameof(GetClassName), GetClassName);
- }
-
- private static Dictionary GenerateSchemaToNameMapUnprocessed(
- OpenApiDocument swaggerUnprocessed)
- {
- return swaggerUnprocessed.Definitions.ToDictionary(x => x.Value, x => x.Key);
- }
-
- private static Dictionary GenerateSchemaToNameMapCooked(OpenApiDocument swaggerCooked)
- {
- return swaggerCooked.Definitions.ToDictionary(x => x.Value, x => x.Key.Replace(".", "").ToPascalCase());
- }
-
- private static HashSet InitSchemaDefinitionsInMultipleGroups(
- Dictionary schemaToNameMap)
- {
- return schemaToNameMap.Values.Select(x =>
- {
- var parts = x.Split(".");
- return new
- {
- FullName = x,
- Name = parts[parts.Length - 1],
- Version = parts[parts.Length - 2],
- Group = parts[parts.Length - 3],
- };
- })
- .GroupBy(x => new { x.Name, x.Version })
- .Where(x => x.Count() > 1)
- .SelectMany(x => x)
- .Select(x => x.FullName)
- .ToHashSet();
- }
-
- private Dictionary InitClassNameMap(OpenApiDocument doc)
- {
- var map = new Dictionary();
- foreach (var (k, v) in doc.Definitions)
- {
- if (v.ExtensionData?.TryGetValue("x-kubernetes-group-version-kind", out _) == true)
- {
- var groupVersionKindElements = (object[])v.ExtensionData["x-kubernetes-group-version-kind"];
- var groupVersionKind = (Dictionary)groupVersionKindElements[0];
-
- var group = (string)groupVersionKind["group"];
- var kind = (string)groupVersionKind["kind"];
- var version = (string)groupVersionKind["version"];
- map[$"{group}_{kind}_{version}"] = k.Replace(".", "").ToPascalCase();
- }
- }
-
- return map;
- }
-
- public void GetClassName(RenderContext context, IList