diff --git a/.editorconfig b/.editorconfig index 60dc40f..11c25d4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ -root = true +root = true [*] -charset = utf-8-bom +charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space @@ -9,6 +9,10 @@ insert_final_newline = true trim_trailing_whitespace = true max_line_length = 180 -[md] +# Workflow files are read by tools that refuse a byte order mark. +[.github/workflows/*.yml] +charset = utf-8 + +[*.md] trim_trailing_whitespace = false max_line_length = 240 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b35c2a..7f37053 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI +name: CI # title: CI (Continuous Integration) pipeline on: @@ -21,66 +21,140 @@ env: CHARTS_DIRECTORIES_ARG: "--chart-dirs charts" jobs: - build: + markup-lint: + name: Markup + uses: devpro/github-workflow-parts/.github/workflows/reusable-markup-lint.yml@591b0c7bfba6fbc7b0cab7d896447e0732b819de + + docs: + name: Docs + uses: devpro/github-workflow-parts/.github/workflows/reusable-pages-deployment.yml@591b0c7bfba6fbc7b0cab7d896447e0732b819de + with: + # VitePress writes the site the GitLab pipeline publishes, which this job only builds. + deploy: false + job-name: Docs + site-path: public + workflow-parts-version: 591b0c7bfba6fbc7b0cab7d896447e0732b819de + + charts: + name: Charts runs-on: ubuntu-latest + timeout-minutes: 30 + outputs: + changed: ${{ steps.list-changed.outputs.changed }} + changed-charts: ${{ steps.list-changed.outputs.changed_charts }} + env: + # Read from the environment rather than pasted into the scripts by GitHub. + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} steps: - name: Checkout repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # note: important to have chart-testing work (see https://github.com/helm/chart-testing/issues/186) fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v7 - with: - node-version: 22 - cache: npm - - name: Install Node.js dependencies - run: npm install - - name: Build with VitePress - run: npm run docs:build - name: Install Helm - uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 - with: - version: latest + uses: devpro/github-workflow-parts/actions/helm/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Install kube-linter - env: - KUBE_LINTER_VERSION: "0.8.3" - KUBE_LINTER_SHA256: "1a6d8419b11971372971fdbc22682b684ebfb7cf1c39591662d1b6ca736c41df" - run: | - curl -sSLo kube-linter.tar.gz "https://github.com/stackrox/kube-linter/releases/download/v${KUBE_LINTER_VERSION}/kube-linter-linux.tar.gz" - echo "${KUBE_LINTER_SHA256} kube-linter.tar.gz" | sha256sum -c - - tar -xzf kube-linter.tar.gz kube-linter - sudo mv kube-linter /usr/local/bin/ - rm kube-linter.tar.gz + uses: devpro/github-workflow-parts/actions/kube-linter/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Run kube-linter run: kube-linter lint charts --config .kube-linter.yaml - name: Set up chart-testing - uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f + uses: devpro/github-workflow-parts/actions/chart-testing/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: List changed charts id: list-changed run: | - changed=$(ct list-changed ${CHARTS_DIRECTORIES_ARG} --target-branch ${{ github.event.repository.default_branch }}) + changed=$(ct list-changed ${CHARTS_DIRECTORIES_ARG} --target-branch "$DEFAULT_BRANCH") charts=$(echo "$changed" | tr '\n' ' ' | xargs) if [[ -n "$changed" ]]; then - echo "changed=true" >> $GITHUB_OUTPUT - echo "changed_charts=$charts" >> $GITHUB_OUTPUT + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "changed_charts=$charts" >> "$GITHUB_OUTPUT" fi - name: Lint charts if: steps.list-changed.outputs.changed == 'true' - run: ct lint $CHARTS_DIRECTORIES_ARG --target-branch ${{ github.event.repository.default_branch }} - # TODO: Enable when https://github.com/stackrox/kube-linter/issues/575 is fixed - # - name: Scan yamls - # id: kube-lint-scan - # uses: stackrox/kube-linter-action@v1 - # with: - # directory: charts - # config: .kube-linter.yaml - # TODO: debug & fix - # - name: Create kind cluster - # uses: helm/kind-action@v1.4.0 - # if: steps.list-changed.outputs.changed == 'true' - # - name: Run chart-testing (install) - # run: ct install $CHARTS_DIRECTORIES_ARG --target-branch ${{ github.event.repository.default_branch }} - # if: steps.list-changed.outputs.changed == 'true' + run: ct lint $CHARTS_DIRECTORIES_ARG --target-branch "$DEFAULT_BRANCH" + - name: Install kubeconform + uses: devpro/github-workflow-parts/actions/kubeconform/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de + - name: Validate manifests against the Kubernetes schemas + run: ./scripts/validate_charts.sh + # The standalone binary of the helm-unittest plugin, so no plugin is installed into Helm. + - name: Install helm-unittest + uses: devpro/github-workflow-parts/actions/helm-unittest/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de + - name: Run chart unit tests + run: | + charts=$(dirname charts/*/tests | sort -u) + untt $charts + + # A job of its own, which IstarCI leaves out of a local run: the kind node runs as a privileged container on the Docker it is given. + charts-install: + name: Charts install + needs: charts + if: needs.charts.outputs.changed == 'true' + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - name: Install Helm + uses: devpro/github-workflow-parts/actions/helm/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de + - name: Set up chart-testing + uses: devpro/github-workflow-parts/actions/chart-testing/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de + - name: Add dependency chart repositories + run: ./scripts/add_helm_repo.sh + # kind and kubectl are installed by an action of the workflow parts, as helm/kind-action is a JavaScript action, and a container image has neither preinstalled. + - name: Install kind and kubectl + uses: devpro/github-workflow-parts/actions/kind/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de + # The cluster name carries the run, so two runs on one Docker daemon never share a cluster. + - name: Create kind cluster + id: create-cluster + run: kind create cluster --name "ct-${GITHUB_RUN_ID}" --image kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed --wait 120s + - name: Install charts + env: + # Charts that cannot reach a ready state on a bare cluster, given here rather than in ct.yaml so that ct lint still covers them. + # Custom resources only, whose CRDs a bare cluster lacks: capi-templates, gitops-bootstrap, letsencrypt, rancher-cluster-templates. + # Cloud providers and storage drivers: azure-storage, outscale. + # Private images, which the cluster cannot pull: fallstar, liveship, sidelab, trackboard. + # Default images no longer published: wordpress. + EXCLUDED_CHARTS: >- + capi-templates,gitops-bootstrap,letsencrypt,rancher-cluster-templates, + azure-storage,outscale, + fallstar,liveship,sidelab,trackboard, + wordpress + # Installs side by side on the one cluster, each chart in a release and a namespace of its own. + PARALLEL_INSTALLS: "3" + CHANGED_CHARTS: ${{ needs.charts.outputs.changed-charts }} + run: | + excluded=",${EXCLUDED_CHARTS// /}," + charts=() + for path in $CHANGED_CHARTS; do + [[ "$excluded" == *",$(basename "$path"),"* ]] || charts+=("$path") + done + if [ "${#charts[@]}" -eq 0 ]; then + echo "No changed chart to install" + exit 0 + fi + # Each install writes a log of its own, shown chart by chart once all are done, rather than interleaved. + logs="$(mktemp -d)" + printf '%s\n' "${charts[@]}" | xargs -P "$PARALLEL_INSTALLS" -I{} bash -c ' + name="$(basename "$1")" + if ct install --charts "$1" --helm-extra-args "--timeout 300s" > "$2/$name.log" 2>&1; then + echo "$name: installed" + else + echo "$name: failed" + echo "$name" >> "$2/failed" + fi' _ {} "$logs" + for path in "${charts[@]}"; do + echo "::group::$(basename "$path")" + cat "$logs/$(basename "$path").log" + echo "::endgroup::" + done + if [ -s "$logs/failed" ]; then + echo "::error::Charts that did not install: $(tr '\n' ' ' < "$logs/failed")" + exit 1 + fi + # Only a cluster this run created, since a failure before it leaves neither kind nor a cluster. + - name: Delete kind cluster + if: always() && steps.create-cluster.outcome != 'skipped' && steps.create-cluster.outcome != '' + run: kind delete cluster --name "ct-${GITHUB_RUN_ID}" diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index e432f7e..c30f913 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -1,4 +1,4 @@ -# title: Continuous Delivery (Packaging) +# title: Continuous Delivery (Packaging) # requirements: # - Create manually "gh-pages" branch # - Maintain pages files in "gh-pages" branch: index.tpl, placeholder.png @@ -29,14 +29,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout git repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v7 - with: - node-version: 22 - cache: npm # ref. https://vitepress.dev/guide/deploy#github-pages # - name: Install Node.js dependencies # run: npm install @@ -53,9 +48,7 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Install Helm - uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 - with: - version: latest + uses: devpro/github-workflow-parts/actions/helm/setup@591b0c7bfba6fbc7b0cab7d896447e0732b819de - name: Add dependency chart repositories run: ./scripts/add_helm_repo.sh - name: Host charts repository on GitHub Pages @@ -65,23 +58,27 @@ jobs: # important: it needs to be done in the same pipeline as another pages workflow won't be triggered by the pust on the gh-pages branch # (tried with a PAT and several options but didn't work) - name: Checkout gh-pages branch - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: gh-pages # - uses: actions/download-artifact@v4 # with: # name: docs # path: docs + # A Go of its own rather than the one of the runner image, which changes with it. - name: Setup Go environment - uses: actions/setup-go@v7 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: "1.27.3" + cache: false - name: Generate index.html run: | go install github.com/halkeye/helm-repo-html@v0.2.1 - /home/runner/go/bin/helm-repo-html + "$(go env GOPATH)/bin/helm-repo-html" - name: Setup Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 - name: Build with Jekyll - uses: actions/jekyll-build-pages@v1 + uses: actions/jekyll-build-pages@44a6e6beabd48582f863aeeb6cb2151cc1716697 # v1.0.13 with: source: ./ destination: ./_site @@ -92,7 +89,7 @@ jobs: # sudo mkdir -p ./_site/docs # sudo cp -r docs/* ./_site/docs/ - name: Upload artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 deploy: runs-on: ubuntu-latest needs: build @@ -102,4 +99,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 diff --git a/.gitignore b/.gitignore index 3b53792..5caf2f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# local downloaded chart compressed files +# local downloaded chart compressed files /**/charts/*.tgz # local temporary files diff --git a/.istarci.yml b/.istarci.yml new file mode 100644 index 0000000..79caf93 --- /dev/null +++ b/.istarci.yml @@ -0,0 +1,3 @@ +workflow: + # ct install runs a kind node, a privileged container, on the Docker of the machine, so it runs on GitHub only, on a runner thrown away after the job. + skipJobs: [charts-install] diff --git a/.kube-linter.yaml b/.kube-linter.yaml index bd59dc1..fc03d30 100644 --- a/.kube-linter.yaml +++ b/.kube-linter.yaml @@ -1,4 +1,4 @@ -checks: +checks: addAllBuiltIn: true # ref. https://docs.kubelinter.io/#/generated/checks exclude: diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 690956f..5dd1d21 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -1,4 +1,4 @@ -gitignore: true +gitignore: true ignores: - "**/node_modules/**" - "CLAUDE.md" diff --git a/.yamllint.yaml b/.yamllint.yaml index 226c5d6..4aed556 100644 --- a/.yamllint.yaml +++ b/.yamllint.yaml @@ -1,7 +1,15 @@ extends: default +# Helm templates are Go templates that render to YAML, not YAML themselves. +ignore: + - "charts/*/templates/**" + - "samples/charts/*/templates/**" + rules: line-length: level: warning max: 180 truthy: disable + # The Fallstar recipes and Fleet patches write their flow mappings with a space inside the braces. + braces: + max-spaces-inside: 1 diff --git a/AGENTS.md b/AGENTS.md index 8844788..f039b54 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# helm-charts: agent context +# helm-charts: agent context ## What this is @@ -22,6 +22,8 @@ helm lint charts/ # lint helm template myapp charts/ -f values.yaml --namespace myns # render the manifests helm upgrade --install myapp charts/ -f values.yaml --namespace myns --create-namespace kube-linter lint charts --config .kube-linter.yaml # the check CI runs +scripts/validate_charts.sh [charts/] # validate the rendered manifests with kubeconform +untt charts/ # run the chart unit tests (helm-unittest binary) npm run docs:dev # serve the documentation site ``` @@ -36,6 +38,8 @@ Where a chart pins an image tag in `values.yaml`, that tag and `appVersion` must - Chart values are documented with `# --` comments above the key, which is the helm-docs annotation form. - A chart that packages a devpro application keeps its values structure close to the application's configuration keys, so that a setting can be traced from `values.yaml` to the environment variable it becomes. - CI runs `kube-linter` over `charts/` with `.kube-linter.yaml`, and `ct lint` over the charts changed against `main`. +- CI renders every chart with each of its `ci/*-values.yaml` files and validates the manifests with kubeconform, so a chart that needs values to render has a `ci/` directory. +- Unit tests of a chart live in its `tests/` directory as helm-unittest suites, listed in its `.helmignore` so they are not packaged. Exclusions belong in `.kube-linter.yaml` with a comment saying why. - Markdown and YAML are linted in CI through `.markdownlint-cli2.yaml` and `.yamllint.yaml`. - Do not run markdownlint: linting is run manually by the maintainer and in CI. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb2359a..dbe5947 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing +# Contributing ## File organization @@ -140,3 +140,8 @@ This is a details block. ``` - [Markdown Extensions](https://vitepress.dev/guide/markdown) + +## Run the pipeline locally + +[IstarCI](https://github.com/devpro/istarci) runs the GitHub Actions pipeline in Docker before the push. +`istarci add` and `istarci install-hook` set it up for this repository. diff --git a/README.md b/README.md index 7608036..1532a2b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Devpro Helm Charts +# Devpro Helm Charts [![CI](https://github.com/devpro/helm-charts/actions/workflows/ci.yml/badge.svg)](https://github.com/devpro/helm-charts/actions/workflows/ci.yml) [![PKG](https://github.com/devpro/helm-charts/actions/workflows/pkg.yml/badge.svg)](https://github.com/devpro/helm-charts/actions/workflows/pkg.yml) diff --git a/charts/capi-templates/Chart.yaml b/charts/capi-templates/Chart.yaml index f6ddb16..bc1b560 100644 --- a/charts/capi-templates/Chart.yaml +++ b/charts/capi-templates/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: capi-templates description: Helm chart for Cluster API (CAPI) Templates type: application -version: 0.1.2 +version: 0.1.3 appVersion: "1.0.0" home: https://github.com/devpro/helm-charts/tree/main/charts/capi-templates maintainers: diff --git a/charts/capi-templates/ci/ci-values.yaml b/charts/capi-templates/ci/ci-values.yaml new file mode 100644 index 0000000..af58d81 --- /dev/null +++ b/charts/capi-templates/ci/ci-values.yaml @@ -0,0 +1,9 @@ +name: ci-cluster +type: gke +googlecloud: + project: ci-project + region: europe-west1 + zone: europe-west1-b + vpc: ci-vpc + subnet: + name: ci-subnet diff --git a/charts/cow-demo/Chart.yaml b/charts/cow-demo/Chart.yaml index 279290a..1106c88 100644 --- a/charts/cow-demo/Chart.yaml +++ b/charts/cow-demo/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: cow-demo description: Helm chart for Cow Demo Application type: application -version: 0.2.3 +version: 0.2.4 appVersion: "2.0.0" maintainers: - name: devpro diff --git a/charts/cow-demo/values.yaml b/charts/cow-demo/values.yaml index 8875120..0ffeb43 100644 --- a/charts/cow-demo/values.yaml +++ b/charts/cow-demo/values.yaml @@ -1,4 +1,4 @@ -name: cow-demo +name: cow-demo image: devprofr/cow-demo # images are hosted on DockerHub: https://hub.docker.com/r/devprofr/cow-demo/tags tag: 1.0.23271988100 diff --git a/charts/devpro-salesportal/Chart.yaml b/charts/devpro-salesportal/Chart.yaml index 3e200cb..45e1e61 100644 --- a/charts/devpro-salesportal/Chart.yaml +++ b/charts/devpro-salesportal/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: devpro-salesportal description: Helm chart for Devpro Sales Portal type: application -version: 0.2.8 +version: 0.2.9 appVersion: "1.1.18203306613" dependencies: - name: mongodb diff --git a/charts/devpro-salesportal/templates/deployment.yaml b/charts/devpro-salesportal/templates/deployment.yaml index af34d03..7cc38f9 100644 --- a/charts/devpro-salesportal/templates/deployment.yaml +++ b/charts/devpro-salesportal/templates/deployment.yaml @@ -92,8 +92,16 @@ spec: value: "http://$(HOST_IP):4317" {{- end }} {{- if .db }} + {{- if .db.connectionStringSecretKeyRef }} + - name: ConnectionStrings__MongoDb + valueFrom: + secretKeyRef: + name: {{ .db.connectionStringSecretKeyRef.name }} + key: {{ .db.connectionStringSecretKeyRef.key }} + {{- else }} - name: ConnectionStrings__MongoDb value: {{ .db.connectionString }} + {{- end }} - name: MongoDb__ConnectionStringName value: "MongoDb" - name: MongoDb__DatabaseName diff --git a/charts/devpro-salesportal/values.yaml b/charts/devpro-salesportal/values.yaml index a792109..941cc54 100644 --- a/charts/devpro-salesportal/values.yaml +++ b/charts/devpro-salesportal/values.yaml @@ -51,7 +51,12 @@ data: port: 8080 healthEndpoint: /health db: + # -- MongoDB connection string, ignored when connectionStringSecretKeyRef is set connectionString: "someconnstring" + # -- Secret key holding the connection string, which keeps its password out of the values + connectionStringSecretKeyRef: {} + # name: salesportal-db + # key: connectionString databaseName: "somedb" extraEnv: [] additionalPodLabels: {} diff --git a/charts/drupal/.helmignore b/charts/drupal/.helmignore index 1b59443..4f3ddb6 100644 --- a/charts/drupal/.helmignore +++ b/charts/drupal/.helmignore @@ -1,4 +1,4 @@ -.DS_Store +.DS_Store .git .gitignore README.md diff --git a/charts/drupal/CONTRIBUTING.md b/charts/drupal/CONTRIBUTING.md index 3d476d7..e91679d 100644 --- a/charts/drupal/CONTRIBUTING.md +++ b/charts/drupal/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/drupal/Chart.yaml b/charts/drupal/Chart.yaml index 4ced60f..962c415 100644 --- a/charts/drupal/Chart.yaml +++ b/charts/drupal/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: drupal description: Drupal Web Application - for container security workshops type: application -version: 1.0.1 +version: 1.0.2 appVersion: "8.5.0" keywords: - security diff --git a/charts/drupal/README.md b/charts/drupal/README.md index ac1eec5..51b34a9 100644 --- a/charts/drupal/README.md +++ b/charts/drupal/README.md @@ -1,4 +1,4 @@ -# Drupal Helm Chart +# Drupal Helm Chart Helm chart for Drupal — designed for container security workshops. diff --git a/charts/drupal/templates/NOTES.txt b/charts/drupal/templates/NOTES.txt index 867f87e..5f6039a 100644 --- a/charts/drupal/templates/NOTES.txt +++ b/charts/drupal/templates/NOTES.txt @@ -1,3 +1,3 @@ -🎯 Drupal deployed! +🎯 Drupal deployed! ⚠️ FOR WORKSHOP USE ONLY — never expose to the internet long-term! diff --git a/charts/drupal/templates/deployment.yaml b/charts/drupal/templates/deployment.yaml index 465825c..29efe8e 100644 --- a/charts/drupal/templates/deployment.yaml +++ b/charts/drupal/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: drupal diff --git a/charts/drupal/templates/ingress.yaml b/charts/drupal/templates/ingress.yaml index 164ba3a..791f01b 100644 --- a/charts/drupal/templates/ingress.yaml +++ b/charts/drupal/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/drupal/templates/pvc.yaml b/charts/drupal/templates/pvc.yaml index bfac030..9b3d960 100644 --- a/charts/drupal/templates/pvc.yaml +++ b/charts/drupal/templates/pvc.yaml @@ -1,4 +1,4 @@ -{{- if .Values.persistence.enabled }} +{{- if .Values.persistence.enabled }} apiVersion: v1 kind: PersistentVolumeClaim metadata: diff --git a/charts/drupal/templates/service.yaml b/charts/drupal/templates/service.yaml index cbc0f20..b5b99d5 100644 --- a/charts/drupal/templates/service.yaml +++ b/charts/drupal/templates/service.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: Service metadata: name: drupal diff --git a/charts/drupal/values.yaml b/charts/drupal/values.yaml index a5a2267..a2630ce 100644 --- a/charts/drupal/values.yaml +++ b/charts/drupal/values.yaml @@ -1,4 +1,4 @@ -replicaCount: 1 +replicaCount: 1 image: vulhub/drupal tag: 8.5.0 diff --git a/charts/dvwa/.helmignore b/charts/dvwa/.helmignore index 1b59443..4f3ddb6 100644 --- a/charts/dvwa/.helmignore +++ b/charts/dvwa/.helmignore @@ -1,4 +1,4 @@ -.DS_Store +.DS_Store .git .gitignore README.md diff --git a/charts/dvwa/CONTRIBUTING.md b/charts/dvwa/CONTRIBUTING.md index 46b5eb1..d7efddc 100644 --- a/charts/dvwa/CONTRIBUTING.md +++ b/charts/dvwa/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/dvwa/Chart.yaml b/charts/dvwa/Chart.yaml index b539004..a404967 100644 --- a/charts/dvwa/Chart.yaml +++ b/charts/dvwa/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: dvwa description: Damn Vulnerable Web Application - for container security workshops type: application -version: 1.0.1 +version: 1.0.3 appVersion: "1.0.0" keywords: - security diff --git a/charts/dvwa/README.md b/charts/dvwa/README.md index 114e2cb..1831505 100644 --- a/charts/dvwa/README.md +++ b/charts/dvwa/README.md @@ -1,4 +1,4 @@ -# DVWA Helm Chart +# DVWA Helm Chart Helm chart for [Damn Vulnerable Web Application](https://github.com/digininja/DVWA) — designed for container security workshops. diff --git a/charts/dvwa/templates/deployment.yaml b/charts/dvwa/templates/deployment.yaml index 4f8098c..5789ce1 100644 --- a/charts/dvwa/templates/deployment.yaml +++ b/charts/dvwa/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "dvwa.fullname" . }} @@ -103,7 +103,7 @@ spec: command: - sh - -c - - mysqladmin ping -u root -p${MARIADB_ROOT_PASSWORD} + - mariadb-admin ping -u root -p"${MARIADB_ROOT_PASSWORD}" initialDelaySeconds: 10 periodSeconds: 5 livenessProbe: @@ -111,7 +111,7 @@ spec: command: - sh - -c - - mysqladmin ping -u root -p${MARIADB_ROOT_PASSWORD} + - mariadb-admin ping -u root -p"${MARIADB_ROOT_PASSWORD}" initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 diff --git a/charts/dvwa/templates/ingress.yaml b/charts/dvwa/templates/ingress.yaml index 4e18eeb..5bcfa24 100644 --- a/charts/dvwa/templates/ingress.yaml +++ b/charts/dvwa/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} {{- if not .Values.ingress.domain }} {{- fail "ingress.domain is required when ingress.enabled=true. Set it with --set ingress.domain=dvwa.yourdomain.com" }} {{- end }} diff --git a/charts/dvwa/values.yaml b/charts/dvwa/values.yaml index 6c4ca19..4d6e584 100644 --- a/charts/dvwa/values.yaml +++ b/charts/dvwa/values.yaml @@ -1,4 +1,4 @@ -replicaCount: 1 +replicaCount: 1 image: repository: ghcr.io/digininja/dvwa diff --git a/charts/fallstar/.helmignore b/charts/fallstar/.helmignore new file mode 100644 index 0000000..2f957f7 --- /dev/null +++ b/charts/fallstar/.helmignore @@ -0,0 +1,11 @@ +.DS_Store +.git/ +.gitignore +.helmignore +*.orig +*.swp +*.bak +values.mine.yaml +temp*.yaml +kubeconfig +tests/ diff --git a/charts/fallstar/Chart.yaml b/charts/fallstar/Chart.yaml new file mode 100644 index 0000000..58c40fa --- /dev/null +++ b/charts/fallstar/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +name: fallstar +description: > + GitOps made easy. + Deploys the Fallstar server, a catalog of recipes rendered into Flux, Argo CD or Fleet manifests on git branches. +type: application +version: 0.1.0 +keywords: + - fallstar + - gitops + - flux +home: https://github.com/devpro/helm-charts +sources: + - https://github.com/devpro/helm-charts +maintainers: + - name: devpro + email: bertrand@devpro.fr diff --git a/charts/fallstar/README.md b/charts/fallstar/README.md new file mode 100644 index 0000000..474862e --- /dev/null +++ b/charts/fallstar/README.md @@ -0,0 +1,41 @@ +# Fallstar Helm Chart + +Helm chart for [Fallstar](https://github.com/devpro/fallstar), GitOps made easy. + +## Quick Start + +```bash +helm repo add devpro https://devpro.github.io/helm-charts +helm repo update +helm upgrade --install fallstar devpro/fallstar \ + --set image.repository=/fallstar \ + --set image.tag= \ + --namespace fallstar --create-namespace +``` + +The image is built from the `Dockerfile` of the Fallstar repository. +`ghcr.io/devpro/fallstar` is private, so its pull needs a Secret of type `kubernetes.io/dockerconfigjson` holding a token with `read:packages`, named in `imagePullSecrets`. + +> **Tip:** Every option is documented in [values.yaml](values.yaml). + +## Lost password + +The reset command of the image writes a new generated password to the database and prints it. + +```bash +kubectl -n fallstar exec deploy/fallstar -- node --import tsx packages/server/src/reset-password.ts +``` + +## Uninstall + +```bash +helm uninstall fallstar -n fallstar +kubectl delete namespace fallstar +``` + +> **Note:** Deleting the namespace deletes the generated `-auth` Secret and the data volume. + +## Fallstar + +`fallstar.yaml` describes the choices of a deployment (database, secrets, image pull, TLS) and the values each one implies, for [Fallstar](https://github.com/devpro/fallstar) itself. +This Helm repository added as a Fallstar recipe source shows the chart in its store with these choices. diff --git a/charts/fallstar/ci/autoscaling-values.yaml b/charts/fallstar/ci/autoscaling-values.yaml new file mode 100644 index 0000000..80ea66a --- /dev/null +++ b/charts/fallstar/ci/autoscaling-values.yaml @@ -0,0 +1,19 @@ +# Autoscaling on MongoDB: no fixed replicas on the Deployment, an HPA instead. +image: + repository: ci-dummy-registry/fallstar + tag: ci + +database: + backend: mongo + mongo: + url: mongodb://ci-dummy-host:27017/fallstar + +persistence: + enabled: false + +resources: + requests: + cpu: 100m + +autoscaling: + enabled: true diff --git a/charts/fallstar/ci/ci-values.yaml b/charts/fallstar/ci/ci-values.yaml new file mode 100644 index 0000000..79002ec --- /dev/null +++ b/charts/fallstar/ci/ci-values.yaml @@ -0,0 +1,3 @@ +image: + repository: ci-dummy-registry/fallstar + tag: ci diff --git a/charts/fallstar/ci/mongo-ingress-values.yaml b/charts/fallstar/ci/mongo-ingress-values.yaml new file mode 100644 index 0000000..980da9c --- /dev/null +++ b/charts/fallstar/ci/mongo-ingress-values.yaml @@ -0,0 +1,15 @@ +image: + repository: ci-dummy-registry/fallstar + tag: ci + +database: + backend: mongo + mongo: + url: mongodb://ci-dummy-host:27017/fallstar + +ingress: + enabled: true + className: traefik + host: fallstar.example.com + tls: + enabled: true diff --git a/charts/fallstar/ci/scaleout-values.yaml b/charts/fallstar/ci/scaleout-values.yaml new file mode 100644 index 0000000..64800d1 --- /dev/null +++ b/charts/fallstar/ci/scaleout-values.yaml @@ -0,0 +1,18 @@ +# Scale-out: MongoDB, no volume, several replicas. +# Guards the RollingUpdate branch of the Deployment strategy and the replica validation in _helpers.tpl. +image: + repository: ci-dummy-registry/fallstar + tag: ci + +replicaCount: 3 + +database: + backend: mongo + mongo: + url: mongodb://ci-dummy-host:27017/fallstar + +persistence: + enabled: false + +podDisruptionBudget: + enabled: true diff --git a/charts/fallstar/fallstar.yaml b/charts/fallstar/fallstar.yaml new file mode 100644 index 0000000..c64da63 --- /dev/null +++ b/charts/fallstar/fallstar.yaml @@ -0,0 +1,230 @@ +# Recipe of this chart for Fallstar: the choices a Fallstar deployment makes, and the values each one implies. +# Format: https://github.com/devpro/fallstar/blob/main/docs/sources.md +title: Fallstar +description: GitOps made easy, a catalog of recipes rendered into Flux, Argo CD or Fleet manifests on git branches. +category: application +namespace: fallstar +layer: apps +# The namespace is a resource of the infra layer, so the ExternalSecrets of the apps layer find it before the release exists. +createNamespace: false + +options: + - key: host + title: Host + type: string + default: fallstar.${{ cluster.domain }} + value: ingress.host + - key: tag + title: Image tag + description: A pkg.yml run of the Fallstar repository, 1.0.. + type: string + value: image.tag + - key: privateImages + title: Image pulled with a ghcr.io token + group: Image + type: boolean + default: true + + - key: database + title: Database + description: SQLite is one file on one volume, MongoDB allows several replicas. + group: Database + type: enum + values: [sqlite, mongo] + default: sqlite + - key: storageSize + title: Volume size + description: The SQLite file and the mirrors of the repositories. + group: Database + type: string + default: 2Gi + value: persistence.size + when: { equals: { database: sqlite } } + - key: storageClass + title: Storage class + description: Empty for the cluster default. + group: Database + type: string + value: persistence.storageClass + when: { equals: { database: sqlite } } + - key: replicas + title: Replicas + group: Database + type: number + default: 1 + bind: { chart: fallstar, path: [values, replicaCount] } + when: { equals: { database: mongo } } + + - key: ingressClass + title: Ingress class + group: Access + type: string + default: ${{ cluster.uses.ingress }} + value: ingress.className + - key: clusterIssuer + title: Certificate issuer + description: A cert-manager ClusterIssuer ordering a certificate for the host, the ingress default certificate when empty. + group: Access + type: string + bind: { chart: fallstar, path: [values, ingress, annotations, cert-manager.io/cluster-issuer] } + +values: + image: + repository: ghcr.io/devpro/fallstar + ingress: + enabled: true + tls: + enabled: true + +patches: + - when: { secretsManaged: true } + chart: fallstar + patch: + values: + auth: + existingSecret: fallstar-auth + - when: { equals: { database: sqlite } } + chart: fallstar + patch: + values: + database: + backend: sqlite + persistence: + enabled: true + - when: { equals: { database: mongo } } + chart: fallstar + notes: + - path: [values, persistence] + text: The mirrors of the repositories are re-created from their remotes, so no Pod needs a volume. + patch: + values: + replicaCount: ${{ options.replicas }} + database: + backend: mongo + mongo: + existingSecret: fallstar-db + persistence: + enabled: false + - when: { option: privateImages } + chart: fallstar + patch: + values: + imagePullSecrets: + - name: ghcr-pull + - when: { option: clusterIssuer } + chart: fallstar + patch: + values: + ingress: + annotations: + cert-manager.io/cluster-issuer: ${{ options.clusterIssuer }} + +resources: + - file: infra/namespaces/fallstar.yaml + shared: true + object: + apiVersion: v1 + kind: Namespace + metadata: { name: fallstar } + - file: apps/externalsecrets/fallstar.yaml + when: { secretStore: true } + notes: + - path: [] + text: Held in the secret store rather than generated by the chart, so a rebuilt cluster keeps the stored repository tokens readable and the same admin password. + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: fallstar-auth, namespace: fallstar } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: fallstar-auth + creationPolicy: Owner + template: + data: + FALLSTAR_SECRET: "{{ .secret }}" + ADMIN_USERNAME: admin + ADMIN_PASSWORD: "{{ .adminPassword }}" + data: + - secretKey: secret + remoteRef: + key: fallstar-secret + - secretKey: adminPassword + remoteRef: + key: fallstar-admin-password + - file: apps/externalsecrets/fallstar.yaml + when: { secretStore: true, equals: { database: mongo } } + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: fallstar-db, namespace: fallstar } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: fallstar-db + creationPolicy: Owner + data: + - secretKey: DB_URL + remoteRef: + key: fallstar-mongodb-url + - file: apps/externalsecrets/fallstar.yaml + when: { secretStore: true, option: privateImages } + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: ghcr-pull, namespace: fallstar } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: ghcr-pull + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: '{"auths":{"ghcr.io":{"username":"{{ .username }}","password":"{{ .password }}","auth":"{{ printf "%s:%s" .username .password | b64enc }}"}}}' + data: + - secretKey: username + remoteRef: + key: ghcr-username + - secretKey: password + remoteRef: + key: ghcr-token + +secrets: + - { name: fallstar-secret, description: "Master secret, at least 32 characters: it signs the sessions and encrypts the stored repository tokens.", generate: hex32, when: { secretsManaged: true }, target: { secret: fallstar-auth, key: FALLSTAR_SECRET, namespace: fallstar } } + - { name: fallstar-admin-username, description: Name of the admin account., value: admin, when: { secretsManaged: true, secretStore: false }, target: { secret: fallstar-auth, key: ADMIN_USERNAME, namespace: fallstar } } + - { name: fallstar-admin-password, description: Password of the admin account., when: { secretsManaged: true }, target: { secret: fallstar-auth, key: ADMIN_PASSWORD, namespace: fallstar } } + - { name: fallstar-mongodb-url, description: "MongoDB connection string, mongodb+srv://:@/fallstar.", when: { secretsManaged: true, equals: { database: mongo } }, target: { secret: fallstar-db, key: DB_URL, namespace: fallstar } } + - { name: ghcr-username, description: GitHub user pulling the private image., when: { secretStore: true, option: privateImages } } + - { name: ghcr-token, description: "GitHub token with read:packages on ghcr.io/devpro.", when: { secretStore: true, option: privateImages } } + +warnings: + - when: { secretsManaged: false, equals: { database: mongo } } + text: reads its database from Secret fallstar-db in namespace fallstar, which neither a secret store nor SOPS creates on this cluster + +guide: + prerequisites: + - A DNS record of the host to the ingress load balancer IP. + - "With the private image and SOPS: a ghcr-pull Secret of type kubernetes.io/dockerconfigjson in namespace fallstar." + verify: + - kubectl get pods -n fallstar + - kubectl logs -n fallstar deploy/fallstar --tail=30 + gotchas: + - title: Pod ImagePullBackOff + cause: ghcr-pull missing, or a token without read:packages. + fix: kubectl get secret -n fallstar ghcr-pull + - title: Second replica fails to start + cause: SQLite is one file on a ReadWriteOnce volume. + fix: Choose MongoDB before raising the replicas. + - title: Stored repository tokens no longer readable + cause: FALLSTAR_SECRET changed, it encrypts them. + fix: Restore the previous secret, or enter the tokens again. diff --git a/charts/fallstar/templates/NOTES.txt b/charts/fallstar/templates/NOTES.txt new file mode 100644 index 0000000..b561dbf --- /dev/null +++ b/charts/fallstar/templates/NOTES.txt @@ -0,0 +1,25 @@ +Fallstar has been deployed. + +{{- if .Values.ingress.enabled }} + +URL: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.host }} +{{- else }} + +Port-forward to open it locally: + + kubectl port-forward -n {{ .Release.Namespace }} svc/{{ include "fallstar.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }} + +Then open http://localhost:{{ .Values.service.port }} +{{- end }} + +Sign in as {{ .Values.auth.adminUsername }}. +{{- if and (not .Values.auth.existingSecret) (not .Values.auth.adminPassword) }} +The generated password is read with: + + kubectl get secret -n {{ .Release.Namespace }} {{ include "fallstar.authSecretName" . }} -o jsonpath='{.data.ADMIN_PASSWORD}' | base64 -d; echo +{{- end }} + +Database: {{ .Values.database.backend }}, replicas: {{ if .Values.autoscaling.enabled }}{{ .Values.autoscaling.minReplicas }}-{{ .Values.autoscaling.maxReplicas }} (autoscaled){{ else }}{{ .Values.replicaCount }}{{ end }}. +{{- if include "fallstar.singleWriter" . }} +A single Pod owns the data, so an upgrade restarts it. More replicas need database.backend=mongo and persistence.enabled=false. +{{- end }} diff --git a/charts/fallstar/templates/_helpers.tpl b/charts/fallstar/templates/_helpers.tpl new file mode 100644 index 0000000..5595d10 --- /dev/null +++ b/charts/fallstar/templates/_helpers.tpl @@ -0,0 +1,96 @@ +{{- define "fallstar.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "fallstar.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "fallstar.labels" -}} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{ include "fallstar.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "fallstar.selectorLabels" -}} +app.kubernetes.io/name: {{ include "fallstar.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "fallstar.authSecretName" -}} +{{- default (printf "%s-auth" (include "fallstar.fullname" .)) .Values.auth.existingSecret }} +{{- end }} + +{{- define "fallstar.mongoSecretName" -}} +{{- default (printf "%s-mongo" (include "fallstar.fullname" .)) .Values.database.mongo.existingSecret }} +{{- end }} + +{{/* +A generated value is read back from the existing Secret on upgrade, so the master secret never changes by itself. +`lookup` returns nothing outside a cluster, so `helm template` generates a fresh one. +*/}} +{{- define "fallstar.generated" -}} +{{- $existing := lookup "v1" "Secret" .root.Release.Namespace (include "fallstar.authSecretName" .root) -}} +{{- if and $existing $existing.data (hasKey $existing.data .key) -}} +{{- index $existing.data .key | b64dec -}} +{{- else -}} +{{- randAlphaNum .length -}} +{{- end -}} +{{- end }} + +{{/* +Whether the configuration holds state only one Pod may own: a SQLite file, or a ReadWriteOnce volume. +Returns "true" or "" (empty is false in an `if`). +*/}} +{{- define "fallstar.singleWriter" -}} +{{- if or (eq .Values.database.backend "sqlite") .Values.persistence.enabled -}} +true +{{- end -}} +{{- end }} + +{{/* +Highest number of Pods this release can run, autoscaling included. +*/}} +{{- define "fallstar.maxReplicas" -}} +{{- if .Values.autoscaling.enabled -}} +{{- max (.Values.replicaCount | int) (.Values.autoscaling.maxReplicas | int) -}} +{{- else -}} +{{- .Values.replicaCount | int -}} +{{- end -}} +{{- end }} + +{{- define "fallstar.validateValues" -}} +{{- if not .Values.image.repository }} +{{- fail "image.repository is required." }} +{{- end }} +{{- if not .Values.image.tag }} +{{- fail "image.tag is required." }} +{{- end }} +{{- if and (eq .Values.database.backend "mongo") (not .Values.database.mongo.url) (not .Values.database.mongo.existingSecret) }} +{{- fail "database.backend=mongo needs database.mongo.url or database.mongo.existingSecret." }} +{{- end }} +{{- if and .Values.auth.secret (lt (len .Values.auth.secret) 32) }} +{{- fail "auth.secret needs at least 32 characters." }} +{{- end }} +{{- if and (gt (int (include "fallstar.maxReplicas" .)) 1) (include "fallstar.singleWriter" .) }} +{{- fail "More than one replica requires database.backend=mongo and persistence.enabled=false: SQLite is a local file, and a ReadWriteOnce volume cannot be mounted by two Pods." }} +{{- end }} +{{- if and .Values.autoscaling.enabled (not .Values.resources.requests) }} +{{- fail "autoscaling.enabled needs resources.requests.cpu, the target being a share of it." }} +{{- end }} +{{- if and .Values.ingress.enabled (not .Values.ingress.host) }} +{{- fail "ingress.host is required when ingress.enabled=true." }} +{{- end }} +{{- end }} diff --git a/charts/fallstar/templates/deployment.yaml b/charts/fallstar/templates/deployment.yaml new file mode 100644 index 0000000..454224b --- /dev/null +++ b/charts/fallstar/templates/deployment.yaml @@ -0,0 +1,106 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + strategy: + {{- if include "fallstar.singleWriter" . }} + # The SQLite file or the volume belongs to one Pod, so the old Pod must be gone before the new one starts. + type: Recreate + {{- else }} + # Nothing is local to a Pod: roll without downtime. + type: RollingUpdate + {{- end }} + selector: + matchLabels: + {{- include "fallstar.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "fallstar.selectorLabels" . | nindent 8 }} + annotations: + # Environment variables are read at start, so an edited Secret needs a rollout. + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + # A Service named fallstar would inject FALLSTAR_PORT=tcp://..., which the server reads as its port. + enableServiceLinks: false + automountServiceAccountToken: false + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + restartPolicy: Always + containers: + - name: fallstar + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 4500 + protocol: TCP + envFrom: + - secretRef: + name: {{ include "fallstar.authSecretName" . }} + {{- if eq .Values.database.backend "mongo" }} + - secretRef: + name: {{ include "fallstar.mongoSecretName" . }} + {{- end }} + env: + - name: DB_BACKEND + value: {{ .Values.database.backend | quote }} + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim | default (printf "%s-data" (include "fallstar.fullname" .)) }} + {{- else }} + emptyDir: {} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/fallstar/templates/hpa.yaml b/charts/fallstar/templates/hpa.yaml new file mode 100644 index 0000000..d408ece --- /dev/null +++ b/charts/fallstar/templates/hpa.yaml @@ -0,0 +1,23 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "fallstar.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} +{{- end }} diff --git a/charts/fallstar/templates/ingress.yaml b/charts/fallstar/templates/ingress.yaml new file mode 100644 index 0000000..a07eeae --- /dev/null +++ b/charts/fallstar/templates/ingress.yaml @@ -0,0 +1,34 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls.enabled }} + tls: + - hosts: + - {{ .Values.ingress.host | quote }} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end }} + rules: + - host: {{ .Values.ingress.host | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "fallstar.fullname" . }} + port: + name: http +{{- end }} diff --git a/charts/fallstar/templates/pdb.yaml b/charts/fallstar/templates/pdb.yaml new file mode 100644 index 0000000..b85e545 --- /dev/null +++ b/charts/fallstar/templates/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.podDisruptionBudget.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} + selector: + matchLabels: + {{- include "fallstar.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/fallstar/templates/pvc.yaml b/charts/fallstar/templates/pvc.yaml new file mode 100644 index 0000000..2a9ca9e --- /dev/null +++ b/charts/fallstar/templates/pvc.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "fallstar.fullname" . }}-data + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- end }} diff --git a/charts/fallstar/templates/secret.yaml b/charts/fallstar/templates/secret.yaml new file mode 100644 index 0000000..8230aae --- /dev/null +++ b/charts/fallstar/templates/secret.yaml @@ -0,0 +1,31 @@ +{{- include "fallstar.validateValues" . }} +{{- if not .Values.auth.existingSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "fallstar.authSecretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +type: Opaque +stringData: + FALLSTAR_SECRET: {{ .Values.auth.secret | default (include "fallstar.generated" (dict "root" . "key" "FALLSTAR_SECRET" "length" 48)) | quote }} + ADMIN_USERNAME: {{ .Values.auth.adminUsername | quote }} + ADMIN_PASSWORD: {{ .Values.auth.adminPassword | default (include "fallstar.generated" (dict "root" . "key" "ADMIN_PASSWORD" "length" 20)) | quote }} + {{- with .Values.auth.adminEmail }} + ADMIN_EMAIL: {{ . | quote }} + {{- end }} +{{- end }} +{{- if and (eq .Values.database.backend "mongo") (not .Values.database.mongo.existingSecret) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "fallstar.mongoSecretName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +type: Opaque +stringData: + DB_URL: {{ .Values.database.mongo.url | quote }} +{{- end }} diff --git a/charts/fallstar/templates/service.yaml b/charts/fallstar/templates/service.yaml new file mode 100644 index 0000000..356eaff --- /dev/null +++ b/charts/fallstar/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "fallstar.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fallstar.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "fallstar.selectorLabels" . | nindent 4 }} diff --git a/charts/fallstar/tests/deployment_test.yaml b/charts/fallstar/tests/deployment_test.yaml new file mode 100644 index 0000000..fbc7bf0 --- /dev/null +++ b/charts/fallstar/tests/deployment_test.yaml @@ -0,0 +1,113 @@ +suite: deployment +# secret.yaml is rendered too, the Deployment carrying its checksum. +templates: + - deployment.yaml + - secret.yaml +set: + image.repository: registry.example.com/fallstar + image.tag: "1.0.1" +release: + name: fallstar +tests: + - it: runs the pinned image + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].image + value: registry.example.com/fallstar:1.0.1 + - it: keeps service links off, so no FALLSTAR_PORT reaches the server + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.enableServiceLinks + value: false + - it: probes the health endpoint, which reads the database + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.path + value: /healthz + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: /healthz + - it: recreates the Pod on SQLite + asserts: + - template: deployment.yaml + equal: + path: spec.strategy.type + value: Recreate + - template: deployment.yaml + equal: + path: spec.template.spec.volumes[0].persistentVolumeClaim.claimName + value: fallstar-data + - it: rolls on MongoDB without a volume + set: + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + persistence.enabled: false + replicaCount: 3 + asserts: + - template: deployment.yaml + equal: + path: spec.strategy.type + value: RollingUpdate + - template: deployment.yaml + equal: + path: spec.replicas + value: 3 + - template: deployment.yaml + isNotNull: + path: spec.template.spec.volumes[0].emptyDir + - it: reads the backend and the Secrets as environment variables + set: + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + asserts: + - template: deployment.yaml + contains: + path: spec.template.spec.containers[0].env + content: + name: DB_BACKEND + value: mongo + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].envFrom + value: + - secretRef: + name: fallstar-auth + - secretRef: + name: fallstar-mongo + - it: reads existing Secrets when named + set: + auth.existingSecret: my-auth + database.backend: mongo + database.mongo.existingSecret: my-db + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.containers[0].envFrom + value: + - secretRef: + name: my-auth + - secretRef: + name: my-db + - it: leaves the replicas to the autoscaler + set: + autoscaling.enabled: true + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + persistence.enabled: false + resources.requests.cpu: 100m + asserts: + - template: deployment.yaml + notExists: + path: spec.replicas + - it: mounts an existing claim when named + set: + persistence.existingClaim: my-data + asserts: + - template: deployment.yaml + equal: + path: spec.template.spec.volumes[0].persistentVolumeClaim.claimName + value: my-data diff --git a/charts/fallstar/tests/resources_test.yaml b/charts/fallstar/tests/resources_test.yaml new file mode 100644 index 0000000..9c87f61 --- /dev/null +++ b/charts/fallstar/tests/resources_test.yaml @@ -0,0 +1,111 @@ +# The optional resources, each rendered only when its values ask for it. +suite: optional resources +set: + image.repository: registry.example.com/fallstar + image.tag: "1.0.1" +release: + name: fallstar +tests: + - it: creates a claim by default + template: pvc.yaml + asserts: + - hasDocuments: + count: 1 + - equal: + path: spec.resources.requests.storage + value: 2Gi + - it: creates no claim for an existing one + template: pvc.yaml + set: + persistence.existingClaim: my-data + asserts: + - hasDocuments: + count: 0 + - it: creates the auth and MongoDB Secrets + template: secret.yaml + set: + auth.secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + auth.adminPassword: s3cret + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + asserts: + - hasDocuments: + count: 2 + - equal: + path: stringData + value: + FALLSTAR_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ADMIN_USERNAME: admin + ADMIN_PASSWORD: s3cret + documentIndex: 0 + - equal: + path: stringData.DB_URL + value: mongodb://db:27017/fallstar + documentIndex: 1 + - it: names the admin email only when set + template: secret.yaml + set: + auth.adminEmail: admin@example.com + asserts: + - equal: + path: stringData.ADMIN_EMAIL + value: admin@example.com + - it: generates the master secret and the admin password when empty + template: secret.yaml + asserts: + - matchRegex: + path: stringData.FALLSTAR_SECRET + pattern: ^[A-Za-z0-9]{48}$ + - matchRegex: + path: stringData.ADMIN_PASSWORD + pattern: ^[A-Za-z0-9]{20}$ + - it: creates no Secret when existing ones are named + template: secret.yaml + set: + auth.existingSecret: my-auth + asserts: + - hasDocuments: + count: 0 + - it: scales on CPU when autoscaling + template: hpa.yaml + set: + autoscaling.enabled: true + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + persistence.enabled: false + resources.requests.cpu: 100m + asserts: + - equal: + path: spec.maxReplicas + value: 4 + - equal: + path: spec.metrics[0].resource.target.averageUtilization + value: 80 + - it: creates the disruption budget when enabled + template: pdb.yaml + set: + podDisruptionBudget.enabled: true + asserts: + - equal: + path: spec.minAvailable + value: 1 + - it: serves the host over TLS + template: ingress.yaml + set: + ingress.enabled: true + ingress.className: traefik + ingress.host: fallstar.example.com + ingress.tls.enabled: true + asserts: + - equal: + path: spec.ingressClassName + value: traefik + - equal: + path: spec.tls[0] + value: + hosts: + - fallstar.example.com + secretName: fallstar-tls + - equal: + path: spec.rules[0].host + value: fallstar.example.com diff --git a/charts/fallstar/tests/validation_test.yaml b/charts/fallstar/tests/validation_test.yaml new file mode 100644 index 0000000..6a3b816 --- /dev/null +++ b/charts/fallstar/tests/validation_test.yaml @@ -0,0 +1,77 @@ +# The rules of fallstar.validateValues, which run from secret.yaml. +suite: values validation +templates: + - secret.yaml +set: + image.repository: registry.example.com/fallstar + image.tag: "1.0.1" +tests: + - it: renders with the defaults and an image + asserts: + - notFailedTemplate: {} + - it: requires the image repository + set: + image.repository: "" + asserts: + - failedTemplate: + errorMessage: image.repository is required. + - it: requires the image tag + set: + image.tag: "" + asserts: + - failedTemplate: + errorMessage: image.tag is required. + - it: requires a connection string on MongoDB + set: + database.backend: mongo + asserts: + - failedTemplate: + errorMessage: database.backend=mongo needs database.mongo.url or database.mongo.existingSecret. + - it: accepts an existing Secret instead of a connection string + set: + database.backend: mongo + database.mongo.existingSecret: fallstar-db + asserts: + - notFailedTemplate: {} + - it: rejects a short master secret + set: + auth.secret: too-short + asserts: + - failedTemplate: + errorMessage: auth.secret needs at least 32 characters. + - it: rejects several replicas on SQLite + set: + replicaCount: 2 + asserts: + - failedTemplate: + errorPattern: ^More than one replica requires + - it: rejects several replicas on a volume, even on MongoDB + set: + replicaCount: 2 + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + asserts: + - failedTemplate: + errorPattern: ^More than one replica requires + - it: rejects autoscaling on SQLite, its maximum being above one + set: + autoscaling.enabled: true + resources.requests.cpu: 100m + asserts: + - failedTemplate: + errorPattern: ^More than one replica requires + - it: requires a CPU request for autoscaling + set: + autoscaling.enabled: true + database.backend: mongo + database.mongo.url: mongodb://db:27017/fallstar + persistence.enabled: false + asserts: + - failedTemplate: + errorMessage: autoscaling.enabled needs resources.requests.cpu, the target being a share of it. + - it: requires a host for the ingress + set: + ingress.enabled: true + asserts: + - failedTemplate: + errorMessage: ingress.host is required when ingress.enabled=true. diff --git a/charts/fallstar/values.yaml b/charts/fallstar/values.yaml new file mode 100644 index 0000000..d198461 --- /dev/null +++ b/charts/fallstar/values.yaml @@ -0,0 +1,102 @@ +image: + # -- Server image repository, required. + repository: + pullPolicy: IfNotPresent + # -- Server image tag, required: a pkg.yml run of the Fallstar repository, 1.0.. + tag: "" + +imagePullSecrets: [] + # - name: "" + +nameOverride: "" +fullnameOverride: "" + +# -- More than one replica requires database.backend=mongo and persistence.enabled=false. +replicaCount: 1 + +# Same requirements as replicaCount, and resources.requests.cpu must be set for the CPU target. +autoscaling: + enabled: false + minReplicas: 2 + maxReplicas: 4 + targetCPUUtilizationPercentage: 80 + +# Only useful with two or more replicas: with one, it blocks node drains. +podDisruptionBudget: + enabled: false + minAvailable: 1 + +auth: + # -- Master secret of at least 32 characters, generated on first install when empty and kept across upgrades. + # Changing it signs everyone out and makes the stored repository tokens unreadable. + secret: "" + # -- Initial admin account, only used while the database is empty. + adminUsername: "admin" + # -- Generated on first install when empty, the install notes show how to read it. + adminPassword: "" + # -- Email of the initial admin, the author of the commits it makes, none when empty. + adminEmail: "" + # -- Existing Secret with the keys FALLSTAR_SECRET, ADMIN_USERNAME, ADMIN_PASSWORD and optionally ADMIN_EMAIL, replacing the four values above. + existingSecret: "" + +database: + # -- sqlite or mongo. + backend: "sqlite" + mongo: + # -- Connection string, e.g. mongodb+srv://:@/fallstar + url: "" + # -- Existing Secret with the key DB_URL, taking priority over url. + existingSecret: "" + +persistence: + # -- Volume for /data: the SQLite file and the mirrors of the repositories, which MongoDB re-creates when missing. + enabled: true + # -- Empty for the cluster default. + storageClass: "" + size: 2Gi + # -- Existing PersistentVolumeClaim, instead of creating one. + existingClaim: "" + +service: + type: ClusterIP + port: 4500 + +ingress: + enabled: false + # -- e.g. "traefik" or "nginx". + className: "" + # -- Required when enabled. + host: "" + annotations: {} + # cert-manager.io/cluster-issuer: "letsencrypt-prod" + tls: + enabled: false + secretName: "fallstar-tls" + +# -- Pod user of the image, which owns /data. +podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + +containerSecurityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + +resources: {} + # requests: + # cpu: 100m + # memory: 256Mi + +extraEnv: [] + # - name: "" + # value: "" + +nodeSelector: {} +tolerations: [] +affinity: {} diff --git a/charts/gitops-bootstrap/Chart.yaml b/charts/gitops-bootstrap/Chart.yaml index 2f20eda..74d5d2b 100644 --- a/charts/gitops-bootstrap/Chart.yaml +++ b/charts/gitops-bootstrap/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: gitops-bootstrap description: Helm chart for managing bootstraping GitOps type: application -version: "0.1.0" +version: 0.1.1 appVersion: "1.0.0" maintainers: - name: devpro diff --git a/charts/gitops-bootstrap/README.md b/charts/gitops-bootstrap/README.md index 32ee484..0497da5 100644 --- a/charts/gitops-bootstrap/README.md +++ b/charts/gitops-bootstrap/README.md @@ -1,4 +1,4 @@ -# GitOps Bootstrap +# GitOps Bootstrap This Helm chart will install the Kubernetes objects to bootstrap GitOps in an infrastructure. Originally created to bypass issues with Kubernetes Terraform provider with manifests at init. diff --git a/charts/gitops-bootstrap/templates/argocd-application.yaml b/charts/gitops-bootstrap/templates/argocd-application.yaml index dcafe50..172ad00 100644 --- a/charts/gitops-bootstrap/templates/argocd-application.yaml +++ b/charts/gitops-bootstrap/templates/argocd-application.yaml @@ -1,4 +1,4 @@ -{{ range .Values.argocd.applications }} +{{ range .Values.argocd.applications }} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: diff --git a/charts/gitops-bootstrap/values.yaml b/charts/gitops-bootstrap/values.yaml index 47faee5..78d191b 100644 --- a/charts/gitops-bootstrap/values.yaml +++ b/charts/gitops-bootstrap/values.yaml @@ -1,4 +1,4 @@ -argocd: +argocd: applications: [] # - name: root-apps # namespace: argocd diff --git a/charts/grafana-stack/Chart.yaml b/charts/grafana-stack/Chart.yaml index 72cf62d..0ff8bc0 100644 --- a/charts/grafana-stack/Chart.yaml +++ b/charts/grafana-stack/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: grafana-stack description: Helm chart for managing Grafana Stack type: application -version: "0.2.2" +version: "0.2.3" appVersion: "10.3.1" dependencies: - name: grafana diff --git a/charts/grafana-stack/README.md b/charts/grafana-stack/README.md index 5952c7a..055152a 100644 --- a/charts/grafana-stack/README.md +++ b/charts/grafana-stack/README.md @@ -12,7 +12,7 @@ Stack components: See also: -- [CNCF Live Webinar: Intro to open source observability with Grafana, Prometheus, Loki, and Tempo](https://community.cncf.io/events/details/cncf-cncf-online-programs-presents-cncf-live-webinar-intro-to-open-source-observability-with-grafana-prometheus-loki-and-tempo/) - November 16, 2021 +- [CNCF: Intro to open source observability](https://community.cncf.io/events/details/cncf-cncf-online-programs-presents-cncf-live-webinar-intro-to-open-source-observability-with-grafana-prometheus-loki-and-tempo/) - November 16, 2021 ## How to use diff --git a/charts/grafana-stack/ci/ci-values.yaml b/charts/grafana-stack/ci/ci-values.yaml new file mode 100644 index 0000000..bc04844 --- /dev/null +++ b/charts/grafana-stack/ci/ci-values.yaml @@ -0,0 +1,26 @@ +# Loki as a single binary on the filesystem, since its scalable mode spreads three replicas of each target over distinct nodes and needs object storage. +loki: + loki: + auth_enabled: false + commonConfig: + replication_factor: 1 + storage: + type: filesystem + singleBinary: + replicas: 1 + write: + replicas: 0 + read: + replicas: 0 + backend: + replicas: 0 + # The self-monitoring and its test rely on the Grafana Agent operator, which a bare cluster lacks. + monitoring: + selfMonitoring: + enabled: false + grafanaAgent: + installOperator: false + lokiCanary: + enabled: false + test: + enabled: false diff --git a/charts/keeptrack/.helmignore b/charts/keeptrack/.helmignore index 0e8a0eb..faeb926 100644 --- a/charts/keeptrack/.helmignore +++ b/charts/keeptrack/.helmignore @@ -21,3 +21,4 @@ .idea/ *.tmproj .vscode/ +tests/ diff --git a/charts/keeptrack/CONTRIBUTING.md b/charts/keeptrack/CONTRIBUTING.md index 5af6d3f..5bf9634 100644 --- a/charts/keeptrack/CONTRIBUTING.md +++ b/charts/keeptrack/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Update chart dependencies diff --git a/charts/keeptrack/Chart.yaml b/charts/keeptrack/Chart.yaml index 83a044d..f4a5741 100644 --- a/charts/keeptrack/Chart.yaml +++ b/charts/keeptrack/Chart.yaml @@ -1,9 +1,9 @@ -apiVersion: v2 +apiVersion: v2 name: keeptrack description: Helm chart for Keeptrack type: application -version: 0.2.6 -appVersion: "2.4.1" +version: 0.2.7 +appVersion: "2.4.6" home: https://github.com/devpro/keeptrack sources: - https://github.com/devpro/keeptrack @@ -11,7 +11,7 @@ keywords: - keeptrack dependencies: - name: mongodb - version: 18.6.16 # MongoDB 8.2.6 + version: 18.6.16 # MongoDB 8.2.6 repository: https://charts.bitnami.com/bitnami alias: mongodb condition: mongodb.enabled diff --git a/charts/keeptrack/README.md b/charts/keeptrack/README.md index 918a95c..45e1ee2 100644 --- a/charts/keeptrack/README.md +++ b/charts/keeptrack/README.md @@ -1,4 +1,4 @@ -# Helm chart for Devpro Keeptrack +# Helm chart for Devpro Keeptrack This is the official Helm chart to install [Keeptrack](https://github.com/devpro/keeptrack) on a Kubernetes cluster. @@ -39,6 +39,7 @@ Notable ones: - `webapi.db`: MongoDB connection (mandatory). - `firebase`: authentication configuration (mandatory). - `webapi.referenceData`: TMDB/RAWG/Discogs API keys and the Open Library book provider override, used to enrich movies/TV shows/video games/books/albums with posters, synopsis, cast and episode data (optional). +- `opentelemetry.endpoint`: an OTLP endpoint, such as an OpenTelemetry Collector, receiving the traces, metrics and logs of both applications (optional). - `security.hardened.enabled`: runs containers with a read-only root filesystem, all Linux capabilities dropped, and the default seccomp profile, for platforms that mandate the Kubernetes Pod Security Standards "restricted" profile. Disabled by default; validate in a non-production environment first. diff --git a/charts/keeptrack/ci/ci-values.yaml b/charts/keeptrack/ci/ci-values.yaml new file mode 100644 index 0000000..906a11a --- /dev/null +++ b/charts/keeptrack/ci/ci-values.yaml @@ -0,0 +1,12 @@ +# The bundled MongoDB under a fixed name, so that the connection string does not depend on the release name. +webapi: + db: + connectionString: mongodb://root:ci-only-password@ci-mongodb:27017/?authSource=admin +mongodb: + enabled: true + fullnameOverride: ci-mongodb + auth: + rootPassword: ci-only-password +# The web app parses a Firebase service account private key at startup, and no key belongs in the repository. +blazorapp: + enabled: false diff --git a/charts/keeptrack/templates/_helpers.tpl b/charts/keeptrack/templates/_helpers.tpl index 97994f7..5f01a41 100644 --- a/charts/keeptrack/templates/_helpers.tpl +++ b/charts/keeptrack/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Recommended Kubernetes labels (https://helm.sh/docs/chart_best_practices/labels/), added on top of the app-specific `app` / `app.kubernetes.io/name` labels every template already sets directly. These are informational only: never add them to a Deployment's spec.selector.matchLabels, which is immutable and would break `helm upgrade` for existing releases. Call with a dict: (dict "app" . "root" $) where "." is the current blazorapp/webapi values entry. diff --git a/charts/keeptrack/templates/deployment.yaml b/charts/keeptrack/templates/deployment.yaml index 2f368fb..c70bba3 100644 --- a/charts/keeptrack/templates/deployment.yaml +++ b/charts/keeptrack/templates/deployment.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.blazorapp .Values.webapi -}} +{{- $applications := list .Values.blazorapp .Values.webapi -}} {{ range $applications }} {{- if .enabled -}} --- @@ -101,6 +101,21 @@ spec: value: {{ $.Values.dotnet.httpsRedirectionEnabled | quote }} - name: Features__IsScalarEnabled value: {{ $.Values.dotnet.scalarEnabled | quote }} + {{- if $.Values.opentelemetry.endpoint }} + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: {{ $.Values.opentelemetry.endpoint | quote }} + - name: OTEL_EXPORTER_OTLP_PROTOCOL + value: {{ $.Values.opentelemetry.protocol | quote }} + - name: OTEL_SERVICE_NAME + value: {{ .name | quote }} + # declared before OTEL_RESOURCE_ATTRIBUTES, since $(VAR) only expands a variable defined above it + - name: K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES + value: "service.namespace=keeptrack,service.version={{ .tag }},service.instance.id=$(K8S_POD_NAME),k8s.pod.name=$(K8S_POD_NAME),k8s.namespace.name={{ $.Release.Namespace }},deployment.environment.name={{ $.Values.dotnet.environment }}" + {{- end }} {{- if eq .role "frontend" }} - name: WebApi__BaseUrl value: "http://{{ $.Values.webapi.name }}" diff --git a/charts/keeptrack/templates/ingress.yaml b/charts/keeptrack/templates/ingress.yaml index bf660e6..971291f 100644 --- a/charts/keeptrack/templates/ingress.yaml +++ b/charts/keeptrack/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.blazorapp .Values.webapi -}} +{{- $applications := list .Values.blazorapp .Values.webapi -}} {{ range $applications }} {{- if and $.Values.ingress.enabled .enabled .host -}} --- diff --git a/charts/keeptrack/templates/service.yaml b/charts/keeptrack/templates/service.yaml index cb93f70..0237a4e 100644 --- a/charts/keeptrack/templates/service.yaml +++ b/charts/keeptrack/templates/service.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.blazorapp .Values.webapi -}} +{{- $applications := list .Values.blazorapp .Values.webapi -}} {{ range $applications }} {{- if .enabled -}} --- diff --git a/charts/keeptrack/templates/serviceaccount.yaml b/charts/keeptrack/templates/serviceaccount.yaml index 935ffa6..4c0f030 100644 --- a/charts/keeptrack/templates/serviceaccount.yaml +++ b/charts/keeptrack/templates/serviceaccount.yaml @@ -1,4 +1,4 @@ -{{- if .Values.security.serviceAccount.create }} +{{- if .Values.security.serviceAccount.create }} {{- $applications := list .Values.blazorapp .Values.webapi -}} {{ range $applications }} apiVersion: v1 diff --git a/charts/keeptrack/tests/opentelemetry_test.yaml b/charts/keeptrack/tests/opentelemetry_test.yaml new file mode 100644 index 0000000..4deec6b --- /dev/null +++ b/charts/keeptrack/tests/opentelemetry_test.yaml @@ -0,0 +1,68 @@ +suite: opentelemetry +templates: + - deployment.yaml +release: + name: keeptrack + namespace: keeptrack +tests: + - it: exports nothing without an endpoint, the applications then registering no exporter + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_EXPORTER_OTLP_ENDPOINT + any: true + - it: sends both applications to the endpoint, each under its own service name + set: + opentelemetry.endpoint: http://otel-collector.observability:4317 + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://otel-collector.observability:4317 + - contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_EXPORTER_OTLP_PROTOCOL + value: grpc + - documentIndex: 0 + contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_SERVICE_NAME + value: keeptrack-blazorapp + - documentIndex: 1 + contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_SERVICE_NAME + value: keeptrack-webapi + - it: tells the pod, namespace and version in the resource attributes, read from the downward API + set: + opentelemetry.endpoint: http://otel-collector.observability:4317 + webapi.tag: 2.4.5 + blazorapp.enabled: false + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_RESOURCE_ATTRIBUTES + value: service.namespace=keeptrack,service.version=2.4.5,service.instance.id=$(K8S_POD_NAME),k8s.pod.name=$(K8S_POD_NAME),k8s.namespace.name=keeptrack,deployment.environment.name=Production + - it: sends over HTTP when asked, for a collector listening on 4318 only + set: + opentelemetry.endpoint: http://otel-collector.observability:4318 + opentelemetry.protocol: http/protobuf + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: OTEL_EXPORTER_OTLP_PROTOCOL + value: http/protobuf diff --git a/charts/keeptrack/values.yaml b/charts/keeptrack/values.yaml index 40ee318..4c9152c 100644 --- a/charts/keeptrack/values.yaml +++ b/charts/keeptrack/values.yaml @@ -1,4 +1,4 @@ -blazorapp: +blazorapp: enabled: true role: frontend name: keeptrack-blazorapp @@ -8,7 +8,7 @@ healthEndpoint: /healthz tls: secretName: keeptrack-blazorapp-tls - tag: 2.4.30663981662 # ref. https://hub.docker.com/r/devprofr/keeptrack-blazorapp/tags + tag: 2.4.40771568745 # https://hub.docker.com/r/devprofr/keeptrack-blazorapp/tags replicaCount: 1 resources: {} # limits: @@ -22,14 +22,13 @@ # value: "yyyy" additionalPodLabels: {} host: "" - # Blazor Server scale-out support - see README "Scaling & high availability". - # Persists the app's Data Protection key ring (which encrypts the auth cookie and antiforgery tokens) - # in MongoDB, so every replica can decrypt what any other replica issued. REQUIRED before setting replicaCount > 1; - # recommended even at 1 replica (sessions then survive pod restarts). + # Blazor Server scale-out support (see README "Scaling & high availability"). + # Persists the app's Data Protection key ring (which encrypts the auth cookie and antiforgery tokens) in MongoDB, + # so every replica can decrypt what any other replica issued. + # REQUIRED before setting replicaCount > 1; recommended even at 1 replica (sessions then survive pod restarts). dataProtection: enabled: false - # important when enabled: connectionString or connectionStringSecretKeyRef must be provided - # (typically the same secret as webapi.db) + # important when enabled: connectionString or connectionStringSecretKeyRef must be provided (typically the same secret as webapi.db) # connectionStringSecretKeyRef: # name: "" # key: "" @@ -48,12 +47,12 @@ webapi: secretName: keeptrack-webapi-tls host: "" replicaCount: 1 - tag: 2.4.30663981662 # ref. https://hub.docker.com/r/devprofr/keeptrack-webapi/tags + tag: 2.4.40771568745 # https://hub.docker.com/r/devprofr/keeptrack-webapi/tags resources: {} extraEnv: [] additionalPodLabels: {} # how many items a signed-in account WITHOUT the Firebase "member"/"admin" role may create per free-tier collection (movies, TV shows); - # such accounts have no access to the other collections at all - + # such accounts have no access to the other collections at all # grant `role: member` via the Firebase Admin SDK to unlock everything (see the app's CONTRIBUTING.md) freeTierItemLimit: 20 db: @@ -120,6 +119,14 @@ dotnet: imagePullSecrets: [] # - name: my-registry-secret +# optional: traces, metrics and logs of both applications sent over OTLP, to an OpenTelemetry Collector for instance; +# nothing is exported while endpoint is empty, and an image older than the OpenTelemetry support ignores these variables +opentelemetry: + # OTEL_EXPORTER_OTLP_ENDPOINT, e.g. http://otel-collector.observability:4317 + endpoint: "" + # OTEL_EXPORTER_OTLP_PROTOCOL: grpc (port 4317) or http/protobuf (port 4318) + protocol: grpc + security: serviceAccount: create: false diff --git a/charts/letsencrypt/Chart.yaml b/charts/letsencrypt/Chart.yaml index d1cfa2e..2c8d70c 100644 --- a/charts/letsencrypt/Chart.yaml +++ b/charts/letsencrypt/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: letsencrypt description: Helm chart for managing Let's Encrypt certificate issuers type: application -version: "0.1.4" +version: "0.1.5" appVersion: "1.0.0" maintainers: - name: devpro diff --git a/charts/letsencrypt/ci/ci-values.yaml b/charts/letsencrypt/ci/ci-values.yaml new file mode 100644 index 0000000..9f353af --- /dev/null +++ b/charts/letsencrypt/ci/ci-values.yaml @@ -0,0 +1,6 @@ +registration: + emailAddress: admin@example.com +ingress: + className: nginx +selfsigned: + enabled: true diff --git a/charts/nextportal/CONTRIBUTING.md b/charts/nextportal/CONTRIBUTING.md index e0613e3..7595a3a 100644 --- a/charts/nextportal/CONTRIBUTING.md +++ b/charts/nextportal/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/nextportal/Chart.yaml b/charts/nextportal/Chart.yaml index cd17079..319b29f 100644 --- a/charts/nextportal/Chart.yaml +++ b/charts/nextportal/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: nextportal description: NextPortal web application - intentionally vulnerable by default for security workshops type: application -version: 1.0.0 +version: 1.0.1 appVersion: "0.1.0" keywords: - security diff --git a/charts/nextportal/README.md b/charts/nextportal/README.md index c896379..de2e488 100644 --- a/charts/nextportal/README.md +++ b/charts/nextportal/README.md @@ -1,4 +1,4 @@ -# NextPortal Helm Chart +# NextPortal Helm Chart Helm chart for [NextPortal Web Application](https://github.com/devpro/container-images/tree/main/src/nextportal) — designed for container security workshops. diff --git a/charts/nextportal/templates/NOTES.txt b/charts/nextportal/templates/NOTES.txt index 1a7b9b5..83fd112 100644 --- a/charts/nextportal/templates/NOTES.txt +++ b/charts/nextportal/templates/NOTES.txt @@ -1,4 +1,4 @@ -1. NextPortal web application has been deployed. +1. NextPortal web application has been deployed. {{- if .Values.ingress.enabled }} URL: https://{{ .Values.ingress.domain }} diff --git a/charts/nextportal/templates/_helpers.tpl b/charts/nextportal/templates/_helpers.tpl index 8f1b1ac..5634107 100644 --- a/charts/nextportal/templates/_helpers.tpl +++ b/charts/nextportal/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Expand the name of the chart. */}} {{- define "nextportal.name" -}} diff --git a/charts/nextportal/templates/deployment.yaml b/charts/nextportal/templates/deployment.yaml index e3c60f1..7b3406a 100644 --- a/charts/nextportal/templates/deployment.yaml +++ b/charts/nextportal/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "nextportal.fullname" . }} diff --git a/charts/nextportal/templates/hpa.yaml b/charts/nextportal/templates/hpa.yaml index f5dbebf..33bc3b2 100644 --- a/charts/nextportal/templates/hpa.yaml +++ b/charts/nextportal/templates/hpa.yaml @@ -1,4 +1,4 @@ -{{- if .Values.autoscaling.enabled }} +{{- if .Values.autoscaling.enabled }} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: diff --git a/charts/nextportal/templates/ingress.yaml b/charts/nextportal/templates/ingress.yaml index 0d8be98..e17bcf2 100644 --- a/charts/nextportal/templates/ingress.yaml +++ b/charts/nextportal/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/nextportal/templates/networkpolicy.yaml b/charts/nextportal/templates/networkpolicy.yaml index 7a6c1de..5bca7d9 100644 --- a/charts/nextportal/templates/networkpolicy.yaml +++ b/charts/nextportal/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkpolicy.enabled -}} +{{- if .Values.networkpolicy.enabled -}} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/nextportal/templates/service.yaml b/charts/nextportal/templates/service.yaml index 6283c1c..9e42d70 100644 --- a/charts/nextportal/templates/service.yaml +++ b/charts/nextportal/templates/service.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: Service metadata: name: {{ include "nextportal.fullname" . }} diff --git a/charts/nextportal/values.yaml b/charts/nextportal/values.yaml index 3770fdd..9242416 100644 --- a/charts/nextportal/values.yaml +++ b/charts/nextportal/values.yaml @@ -1,4 +1,4 @@ -image: +image: repository: ghcr.io/devpro/nextportal tag: "0.1.0" pullPolicy: IfNotPresent diff --git a/charts/rancher-cluster-templates/Chart.yaml b/charts/rancher-cluster-templates/Chart.yaml index 4c78518..a4b7cc8 100644 --- a/charts/rancher-cluster-templates/Chart.yaml +++ b/charts/rancher-cluster-templates/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: rancher-cluster-templates description: Helm chart for managing Rancher cluster templates type: application -version: "0.1.6" +version: "0.1.7" appVersion: "0.1.0" annotations: catalog.cattle.io/type: cluster-template diff --git a/charts/rancher-cluster-templates/ci/aws-values.yaml b/charts/rancher-cluster-templates/ci/aws-values.yaml new file mode 100644 index 0000000..b0bbcf4 --- /dev/null +++ b/charts/rancher-cluster-templates/ci/aws-values.yaml @@ -0,0 +1,39 @@ +cluster: + name: ci-cluster + +cloudprovider: aws + +cloudCredentialSecretName: cattle-global-data:cc-ci + +nodepools: + - etcd: true + controlplane: true + worker: true + labels: {} + quantity: 1 + paused: false + name: nodepool-1 + deviceName: "/dev/sda1" + encryptEbsVolume: false + endpoint: "" + iamInstanceProfile: "" + insecureTransport: false + instanceType: t3a.medium + region: us-west-2 + createSecurityGroup: true + keypairName: "" + securityGroupReadonly: false + sshKeyContents: "" + subnetId: "" + monitoring: false + privateAddressOnly: false + requestSpotInstance: false + retries: 5 + rootSize: 16 + spotPrice: 0.5 + sshUser: ubuntu + storageType: gp2 + useEbsOptimizedInstance: false + usePrivateAddress: false + userdata: "" + zone: a diff --git a/charts/sidelab/.helmignore b/charts/sidelab/.helmignore index d13bc8e..517eba1 100644 --- a/charts/sidelab/.helmignore +++ b/charts/sidelab/.helmignore @@ -1,4 +1,4 @@ -.DS_Store +.DS_Store .git/ .gitignore .helmignore diff --git a/charts/sidelab/Chart.yaml b/charts/sidelab/Chart.yaml index 9409a2a..cd897e6 100644 --- a/charts/sidelab/Chart.yaml +++ b/charts/sidelab/Chart.yaml @@ -1,10 +1,10 @@ -apiVersion: v2 +apiVersion: v2 name: sidelab description: > Self-hosted interactive lab platform. Deploys the Sidelab launcher, which manages lab sessions as ephemeral Kubernetes Pods inside tenant-scoped namespaces. type: application -version: 0.1.6 +version: 0.1.11 appVersion: "0.1.0" keywords: - sidelab diff --git a/charts/sidelab/README.md b/charts/sidelab/README.md index b95765a..59f6c90 100644 --- a/charts/sidelab/README.md +++ b/charts/sidelab/README.md @@ -1,4 +1,4 @@ -# Sidelab Helm Chart +# Sidelab Helm Chart Helm chart for Sidelab, a self-hosted interactive lab platform. @@ -22,3 +22,8 @@ kubectl delete namespace sidelab ``` > **Note:** Deleting the namespace deletes the generated `-auth` Secret and the data volume. + +## Fallstar + +`fallstar.yaml` describes the choices of a deployment (database, secrets, lab access, TLS) and the values each one implies, for [Fallstar](https://github.com/devpro/fallstar). +This Helm repository added as a Fallstar recipe source shows the chart in its store with these choices. diff --git a/charts/sidelab/ci/ci-values.yaml b/charts/sidelab/ci/ci-values.yaml index 4ffe840..c05c07b 100644 --- a/charts/sidelab/ci/ci-values.yaml +++ b/charts/sidelab/ci/ci-values.yaml @@ -1,2 +1,2 @@ -image: +image: repository: ci-dummy-registry/app diff --git a/charts/sidelab/fallstar.yaml b/charts/sidelab/fallstar.yaml new file mode 100644 index 0000000..9392296 --- /dev/null +++ b/charts/sidelab/fallstar.yaml @@ -0,0 +1,349 @@ +# Recipe of this chart for Fallstar: the choices a Sidelab deployment makes, and the values each one implies. +# Format: https://github.com/devpro/fallstar/blob/main/docs/sources.md +title: Sidelab +description: Interactive lab platform, one pod per lab session, each published on its own hostname. +category: application +namespace: sidelab +layer: apps +# The namespace is a resource of the infra layer, so the ExternalSecrets of the apps layer find it before the release exists. +createNamespace: false + +options: + - key: domain + title: Domain + description: The dashboard is sidelab., lab sessions lab-.labs.. + type: string + default: ${{ cluster.domain }} + value: domain + - key: tag + title: Image tag + description: The same pkg.yml run for the launcher and the lab image, .. + type: string + value: image.tag + - key: privateImages + title: Images pulled with a ghcr.io token + group: Images + type: boolean + default: true + + - key: database + title: Database + description: SQLite is one file on one volume, MongoDB allows several replicas, the bundled one is for a disposable demo. + group: Database + type: enum + values: [sqlite, mongo, mongo-bundled] + default: sqlite + - key: storageSize + title: SQLite volume size + group: Database + type: string + default: 2Gi + value: persistence.size + when: { equals: { database: sqlite } } + - key: storageClass + title: Storage class + description: Empty for the cluster default. + group: Database + type: string + value: persistence.storageClass + when: { equals: { database: sqlite } } + - key: replicas + title: Replicas + description: More than one needs MongoDB. + group: Database + type: number + default: 1 + bind: { chart: sidelab, path: [values, replicaCount] } + when: { equals: { database: [mongo, mongo-bundled] } } + + - key: labAccess + title: Lab access + description: Ingress gives each session its own hostname, NodePort its own port. + group: Access + type: enum + values: [ingress, nodeport] + default: ingress + value: launcher.labAccess + - key: ingressClass + title: Ingress class + group: Access + type: string + default: traefik + bind: { chart: sidelab, path: [values, ingress, className] } + when: { equals: { labAccess: ingress } } + - key: tls + title: TLS + description: A wildcard certificate served by the ingress controller, a certificate per hostname from cert-manager, or none. + group: Access + type: enum + values: [wildcard, cert-manager, none] + default: wildcard + when: { equals: { labAccess: ingress } } + - key: clusterIssuer + title: cert-manager ClusterIssuer + group: Access + type: string + default: letsencrypt-dns + when: { equals: { labAccess: ingress, tls: cert-manager } } + - key: trustProxy + title: Trust the ingress controller as a proxy + description: Keeps the client IP, which the login throttle counts. + group: Access + type: boolean + default: true + when: { equals: { labAccess: ingress } } + + - key: sessionDefaultDuration + title: Default session duration, in minutes + group: Sessions + type: number + default: 30 + value: launcher.sessionDefaultDuration + - key: sessionMaxDuration + title: Maximum session duration, in minutes + group: Sessions + type: number + default: 60 + value: launcher.sessionMaxDuration + +values: + image: + repository: ghcr.io/devpro/sidelab-launcher + launcher: + labImage: ghcr.io/devpro/sidelab-app:${{ options.tag }} + +patches: + - when: { secretsManaged: true } + chart: sidelab + patch: + values: + auth: + existingSecret: sidelab-auth + - when: { equals: { database: sqlite } } + chart: sidelab + patch: + values: + database: + backend: sqlite + persistence: + enabled: true + - when: { equals: { database: [mongo, mongo-bundled] } } + chart: sidelab + patch: + values: + replicaCount: ${{ options.replicas }} + database: + backend: mongo + mongo: + existingSecret: sidelab-db + persistence: + enabled: false + - when: { equals: { database: mongo-bundled } } + chart: sidelab + notes: + - path: [values, mongodb, auth, existingSecret] + text: The root password comes from the same Secret as DB_URL, so it is never written in the values. + patch: + values: + mongodb: + enabled: true + auth: + existingSecret: sidelab-db + - when: { option: privateImages } + chart: sidelab + patch: + values: + imagePullSecrets: + - name: ghcr-pull + launcher: + labImagePullSecret: ghcr-pull + - when: { equals: { labAccess: ingress } } + chart: sidelab + patch: + values: + ingress: + enabled: true + className: ${{ options.ingressClass }} + - when: { equals: { labAccess: ingress, tls: wildcard } } + chart: sidelab + notes: + - path: [values, ingress, tls] + text: No cert-manager annotation, the ingress controller serves its wildcard certificate to every lab hostname. + patch: + values: + ingress: + tls: + enabled: true + - when: { equals: { labAccess: ingress, tls: cert-manager } } + chart: sidelab + notes: + - path: [values, ingress, annotations] + text: Copied onto every lab session Ingress, so each session orders its own certificate. + patch: + values: + ingress: + annotations: + cert-manager.io/cluster-issuer: ${{ options.clusterIssuer }} + tls: + enabled: true + - when: { equals: { labAccess: ingress }, option: trustProxy } + chart: sidelab + patch: + values: + extraEnv: + - name: TRUST_PROXY + value: "1" + - when: { equals: { labAccess: nodeport } } + chart: sidelab + patch: + values: + service: + type: NodePort + +resources: + - file: infra/namespaces/sidelab.yaml + shared: true + object: + apiVersion: v1 + kind: Namespace + metadata: { name: sidelab } + - file: apps/externalsecrets/sidelab.yaml + when: { secretStore: true } + notes: + - path: [] + text: Held in the secret store rather than generated by the chart, so a rebuilt cluster keeps the same signing secret and admin password. + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: sidelab-auth, namespace: sidelab } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: sidelab-auth + creationPolicy: Owner + template: + data: + JWT_SECRET: "{{ .jwtSecret }}" + ADMIN_USERNAME: admin + ADMIN_PASSWORD: "{{ .adminPassword }}" + data: + - secretKey: jwtSecret + remoteRef: + key: sidelab-jwt-secret + - secretKey: adminPassword + remoteRef: + key: sidelab-admin-password + - file: apps/externalsecrets/sidelab.yaml + when: { secretStore: true, equals: { database: mongo } } + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: sidelab-db, namespace: sidelab } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: sidelab-db + creationPolicy: Owner + data: + - secretKey: DB_URL + remoteRef: + key: sidelab-mongodb-url + - file: apps/externalsecrets/sidelab.yaml + when: { secretStore: true, equals: { database: mongo-bundled } } + notes: + - path: [] + text: One password for the bundled MongoDB and for the connection string the launcher uses, the Service being -mongodb. + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: sidelab-db, namespace: sidelab } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: sidelab-db + creationPolicy: Owner + template: + data: + DB_URL: "mongodb://root:{{ .password }}@sidelab-mongodb:27017/sidelab?authSource=admin" + mongodb-root-password: "{{ .password }}" + data: + - secretKey: password + remoteRef: + key: sidelab-mongodb-root-password + - file: apps/externalsecrets/sidelab.yaml + when: { secretStore: true, option: privateImages } + notes: + - path: [] + text: The launcher copies it into every tenant namespace, for the lab image. + object: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: { name: ghcr-pull, namespace: sidelab } + spec: + refreshInterval: 1h + secretStoreRef: + name: ${{ cluster.secretStore }} + kind: ClusterSecretStore + target: + name: ghcr-pull + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + .dockerconfigjson: '{"auths":{"ghcr.io":{"username":"{{ .username }}","password":"{{ .password }}","auth":"{{ printf "%s:%s" .username .password | b64enc }}"}}}' + data: + - secretKey: username + remoteRef: + key: ghcr-username + - secretKey: password + remoteRef: + key: ghcr-token + +secrets: + - { name: sidelab-jwt-secret, description: Signing key of the launcher tokens., generate: hex32, when: { secretsManaged: true }, target: { secret: sidelab-auth, key: JWT_SECRET, namespace: sidelab } } + - { name: sidelab-admin-username, description: Name of the admin account., value: admin, when: { secretsManaged: true, secretStore: false }, target: { secret: sidelab-auth, key: ADMIN_USERNAME, namespace: sidelab } } + - { name: sidelab-admin-password, description: Password of the admin account., when: { secretsManaged: true }, target: { secret: sidelab-auth, key: ADMIN_PASSWORD, namespace: sidelab } } + - { name: sidelab-mongodb-url, description: "MongoDB connection string, mongodb+srv://:@/sidelab.", when: { secretsManaged: true, equals: { database: mongo } }, target: { secret: sidelab-db, key: DB_URL, namespace: sidelab } } + - { name: sidelab-mongodb-root-password, description: Root password of the bundled MongoDB., when: { secretsManaged: true, equals: { database: mongo-bundled } }, target: { secret: sidelab-db, key: mongodb-root-password, namespace: sidelab } } + - { name: sidelab-mongodb-url, description: "mongodb://root:@sidelab-mongodb:27017/sidelab?authSource=admin, with the root password above.", when: { secretsManaged: true, secretStore: false, equals: { database: mongo-bundled } }, target: { secret: sidelab-db, key: DB_URL, namespace: sidelab } } + - { name: ghcr-username, description: GitHub user pulling the private images., when: { secretStore: true, option: privateImages } } + - { name: ghcr-token, description: "GitHub token with read:packages on ghcr.io/devpro.", when: { secretStore: true, option: privateImages } } + +warnings: + - when: { secretsManaged: false, equals: { database: [mongo, mongo-bundled] } } + text: reads its database from Secret sidelab-db in namespace sidelab, which neither a secret store nor SOPS creates on this cluster + +guide: + prerequisites: + - "With ingress: two DNS records to the ingress load balancer IP, DNS only, sidelab. and *.labs.." + - "With a wildcard certificate: the ingress controller serves one for *.labs. by default." + - "With private images and SOPS: a ghcr-pull Secret of type kubernetes.io/dockerconfigjson in namespace sidelab." + verify: + - kubectl get pods -n sidelab + - kubectl logs -n sidelab deploy/sidelab --tail=30 + - kubectl get ns -l app.kubernetes.io/managed-by=sidelab + gotchas: + - title: Every lab orders its own certificate, rate limit hit + cause: TLS from cert-manager, whose annotation the chart copies onto every session Ingress. + fix: TLS from a wildcard certificate served by the ingress controller. + - title: Lab pod ImagePullBackOff + cause: ghcr-pull missing, or a token without read:packages. + fix: kubectl get secret -n sidelab ghcr-pull; the launcher copies it into sidelab- at every session start. + - title: Second replica fails to start + cause: SQLite is one file on a ReadWriteOnce volume. + fix: Choose MongoDB before raising the replicas. + - title: Lab stuck on starting, pod Pending, Insufficient cpu + cause: Nodes already full, and a lab requests 250m CPU and 512Mi. + fix: Wait for the autoscaler, or raise the node pool minimum before a demo. + - title: Tenant namespaces left after a teardown + cause: The launcher creates them, not Flux. + fix: Delete the namespaces labelled app.kubernetes.io/managed-by=sidelab by hand. diff --git a/charts/sidelab/templates/NOTES.txt b/charts/sidelab/templates/NOTES.txt index 2ad68bd..65c9007 100644 --- a/charts/sidelab/templates/NOTES.txt +++ b/charts/sidelab/templates/NOTES.txt @@ -1,4 +1,4 @@ -🎉 Sidelab has been deployed! +🎉 Sidelab has been deployed! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Launcher URL diff --git a/charts/sidelab/templates/_helpers.tpl b/charts/sidelab/templates/_helpers.tpl index 262d504..0a9b0c0 100644 --- a/charts/sidelab/templates/_helpers.tpl +++ b/charts/sidelab/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Expand the name of the chart. */}} {{- define "sidelab.name" -}} diff --git a/charts/sidelab/templates/configmap.yaml b/charts/sidelab/templates/configmap.yaml index 091f786..449cdf6 100644 --- a/charts/sidelab/templates/configmap.yaml +++ b/charts/sidelab/templates/configmap.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: {{ include "sidelab.fullname" . }}-config @@ -29,6 +29,18 @@ data: {{- if .Values.launcher.labImagePullSecret }} K8S_LAB_IMAGE_PULL_SECRET: {{ .Values.launcher.labImagePullSecret | quote }} {{- end }} + {{- with .Values.launcher.labEgressCidrs }} + K8S_LAB_EGRESS_CIDRS: {{ join "," . | quote }} + {{- end }} + K8S_VM_IMAGE: {{ .Values.launcher.vm.image | quote }} + K8S_VM_KVM_RESOURCE: {{ .Values.launcher.vm.kvmResource | quote }} + K8S_VM_TUN_RESOURCE: {{ .Values.launcher.vm.tunResource | quote }} + {{- with .Values.launcher.vm.runtimeClass }} + K8S_VM_RUNTIME_CLASS: {{ . | quote }} + {{- end }} + {{- with .Values.launcher.terraform.moduleSource }} + TERRAFORM_MODULE_SOURCE: {{ . | quote }} + {{- end }} # The admin tenant's namespace is the default, as a fallback when namespaces cannot be listed. K8S_KNOWN_NAMESPACES: {{ .Values.launcher.knownNamespaces | default (printf "%s-admin" .Values.launcher.namespacePrefix) | quote }} --- diff --git a/charts/sidelab/templates/deployment.yaml b/charts/sidelab/templates/deployment.yaml index 0341d72..ad8929c 100644 --- a/charts/sidelab/templates/deployment.yaml +++ b/charts/sidelab/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "sidelab.fullname" . }} @@ -69,6 +69,10 @@ spec: - secretRef: name: {{ include "sidelab.mongoSecretName" . }} {{- end }} + {{- with .Values.launcher.terraform.credentialsSecret }} + - secretRef: + name: {{ . }} + {{- end }} env: # The Service lab Pods call back. - name: K8S_LAUNCHER_NAMESPACE diff --git a/charts/sidelab/templates/networkpolicy.yaml b/charts/sidelab/templates/networkpolicy.yaml index 5d1b93f..d9cbaad 100644 --- a/charts/sidelab/templates/networkpolicy.yaml +++ b/charts/sidelab/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkPolicy.enabled }} +{{- if .Values.networkPolicy.enabled }} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/sidelab/templates/rbac.yaml b/charts/sidelab/templates/rbac.yaml index e595dbe..27d53a7 100644 --- a/charts/sidelab/templates/rbac.yaml +++ b/charts/sidelab/templates/rbac.yaml @@ -1,4 +1,4 @@ -{{- if .Values.rbac.create }} +{{- if .Values.rbac.create }} # Cluster-scoped because tenant namespaces are created on demand. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -32,6 +32,11 @@ rules: resources: ["ingresses"] verbs: ["get", "list", "create", "delete"] + # Per-session isolation; a session does not start without its NetworkPolicies. + - apiGroups: ["networking.k8s.io"] + resources: ["networkpolicies"] + verbs: ["get", "list", "create", "delete"] + # Attaches launcher.labImagePullSecret to the default ServiceAccount of tenant namespaces. - apiGroups: [""] resources: ["serviceaccounts"] diff --git a/charts/sidelab/templates/secret.yaml b/charts/sidelab/templates/secret.yaml index 482e2fe..b7173e6 100644 --- a/charts/sidelab/templates/secret.yaml +++ b/charts/sidelab/templates/secret.yaml @@ -1,4 +1,4 @@ -{{- include "sidelab.validateValues" . }} +{{- include "sidelab.validateValues" . }} {{- if not .Values.auth.existingSecret }} apiVersion: v1 kind: Secret diff --git a/charts/sidelab/values.yaml b/charts/sidelab/values.yaml index 3703d99..0bdbc1b 100644 --- a/charts/sidelab/values.yaml +++ b/charts/sidelab/values.yaml @@ -1,4 +1,4 @@ -# -- Base domain: the dashboard defaults to sidelab. and lab sessions to *.labs.. +# -- Base domain: the dashboard defaults to sidelab. and lab sessions to *.labs.. domain: "" image: @@ -98,6 +98,28 @@ launcher: # -- dockerconfigjson Secret in the release namespace used to pull labImage, copied into every tenant namespace. labImagePullSecret: "" + # -- CIDRs a lab may reach beyond the internet, for a course that needs a private network; every private, shared and link-local range is closed otherwise. + labEgressCidrs: [] + + # A course host declared runtime: vm runs as a pod booting a Firecracker guest, on nodes whose device plugin offers /dev/kvm (docs/kubernetes-vm.md in sidelab). + vm: + # -- Image of the VM pods, built from sidelab's packages/vm. + image: "sidelab-vm:latest" + # -- Extended resource that brings /dev/kvm; devices.kubevirt.io/kvm on a cluster running KubeVirt. + kvmResource: "devic.es/kvm" + # -- Extended resource that brings /dev/net/tun; devices.kubevirt.io/tun on a cluster running KubeVirt. + tunResource: "devic.es/tun" + # -- RuntimeClass of the VM pods, which the cluster exempts from a baseline Pod Security level, since their prepare step adds NET_ADMIN. + runtimeClass: "" + + # A course host declared runtime: terraform is a machine an OpenTofu module provisions, which downloads its agent from the lab and dials it at the lab's URL, so that URL must be reachable from where the machine runs (docs/terraform.md in sidelab). + # Its state is kept under /app/data/terraform, so persistence.enabled keeps a launcher restart from losing track of machines that bill. + terraform: + # -- Module of a host whose lab.yaml names none. + moduleSource: "" + # -- Secret in the release namespace holding the provider's credentials as its standard variables (AWS_ACCESS_KEY_ID, ARM_CLIENT_SECRET, KUBE_TOKEN, ...), passed to the launcher and never to a lab. + credentialsSecret: "" + service: # -- ClusterIP, NodePort or LoadBalancer. type: ClusterIP diff --git a/charts/terraform-backend-mongodb/Chart.yaml b/charts/terraform-backend-mongodb/Chart.yaml index e54f42d..ccea1b2 100644 --- a/charts/terraform-backend-mongodb/Chart.yaml +++ b/charts/terraform-backend-mongodb/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: terraform-backend-mongodb description: Helm chart for Terraform Backend MongoDB type: application -version: 0.3.0 +version: 0.3.2 appVersion: "1.3.0" home: https://github.com/devpro/terraform-backend-mongodb sources: diff --git a/charts/terraform-backend-mongodb/README.md b/charts/terraform-backend-mongodb/README.md index 3c9914d..183da1c 100644 --- a/charts/terraform-backend-mongodb/README.md +++ b/charts/terraform-backend-mongodb/README.md @@ -94,7 +94,8 @@ The defaults under `authentication` in [values.yaml](values.yaml) suit most depl The setting that does need a decision is `network.trustAllProxies`, because the other two depend on it. Terraform's `http` backend sends a Basic credential on every single request, so the server verifies the same password over and over and every failed guess costs it a BCrypt verify. -The lockout puts a ceiling on that, and it is scoped to the username and the caller's source address together rather than to the username alone: a lockout on the username alone would let anybody on the internet lock the real operator out of the backend. +The lockout puts a ceiling on that, and it is scoped to the username and the caller's source address together rather than to the username alone. +A lockout on the username alone would let anybody on the internet lock the real operator out of the backend. Behind an ingress, an application that does not trust the proxy sees the proxy's address on every request. Every caller then shares one lockout bucket, which turns the protection into the denial of service it was designed to avoid. diff --git a/charts/terraform-backend-mongodb/ci/ci-values.yaml b/charts/terraform-backend-mongodb/ci/ci-values.yaml new file mode 100644 index 0000000..0a41b35 --- /dev/null +++ b/charts/terraform-backend-mongodb/ci/ci-values.yaml @@ -0,0 +1,5 @@ +# The bundled MongoDB, so that the server reaches a database and its probes pass. +mongodb: + enabled: true + auth: + rootPassword: ci-only-password diff --git a/charts/terraform-backend-mongodb/templates/_helpers.tpl b/charts/terraform-backend-mongodb/templates/_helpers.tpl index 2c25f0f..e948dba 100644 --- a/charts/terraform-backend-mongodb/templates/_helpers.tpl +++ b/charts/terraform-backend-mongodb/templates/_helpers.tpl @@ -34,15 +34,31 @@ Call with a dict: (dict "app" . "root" $) where "." is the current webapi values {{/* MongoDB connection string. webapi.db.connectionString wins; otherwise, if the bundled mongodb subchart is enabled, derived from -its default standalone Service name ("-mongodb", per the alias in Chart.yaml and the -Bitnami chart's own naming convention) and mongodb.auth.rootPassword. +its standalone Service name and mongodb.auth.rootPassword. Only called from deployment.yaml when webapi.db.connectionStringSecretKeyRef is not set. */}} {{- define "terraform-backend-mongodb.mongoConnectionString" -}} {{- if .Values.webapi.db.connectionString -}} {{- .Values.webapi.db.connectionString -}} {{- else if .Values.mongodb.enabled -}} -{{- printf "mongodb://root:%s@%s-mongodb:27017/?authSource=admin" .Values.mongodb.auth.rootPassword .Release.Name -}} +{{- printf "mongodb://root:%s@%s:27017/?authSource=admin" .Values.mongodb.auth.rootPassword (include "terraform-backend-mongodb.mongoServiceName" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Service name of the bundled mongodb subchart, following the Bitnami common.names.fullname rule: +a release name that already contains the chart name is used alone, so the release "terraform-backend-mongodb" gets the Service "terraform-backend-mongodb". +*/}} +{{- define "terraform-backend-mongodb.mongoServiceName" -}} +{{- if .Values.mongodb.fullnameOverride -}} +{{- .Values.mongodb.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default "mongodb" .Values.mongodb.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} {{- end -}} {{- end -}} diff --git a/charts/terraform-backend-mongodb/templates/deployment.yaml b/charts/terraform-backend-mongodb/templates/deployment.yaml index 33d1bcd..f87388a 100644 --- a/charts/terraform-backend-mongodb/templates/deployment.yaml +++ b/charts/terraform-backend-mongodb/templates/deployment.yaml @@ -55,9 +55,9 @@ spec: - name: http containerPort: {{ .containerPort }} protocol: TCP + # The health endpoint pings MongoDB, so a database outage would restart the server rather than take it out of the Service. livenessProbe: - httpGet: - path: {{ .healthEndpoint }} + tcpSocket: port: http readinessProbe: httpGet: diff --git a/charts/terraform-backend-mongodb/values.yaml b/charts/terraform-backend-mongodb/values.yaml index 7721af6..cade6a6 100644 --- a/charts/terraform-backend-mongodb/values.yaml +++ b/charts/terraform-backend-mongodb/values.yaml @@ -4,7 +4,7 @@ webapi: name: tfbackend image: docker.io/devprofr/terraform-backend-mongodb # images are hosted on DockerHub: https://hub.docker.com/r/devprofr/terraform-backend-mongodb/tags - tag: "1.3.0" # pins to Chart.yaml appVersion - avoid floating tags (e.g. latest) in Production + tag: "1.3.36770971345" # application version 1.3.0 (Chart.yaml appVersion), built by its pipeline run - avoid floating tags (e.g. latest) in Production replicaCount: 1 port: 80 containerPort: 8080 @@ -43,9 +43,9 @@ webapi: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: "kubernetes.io/arch" - operator: In - values: ["amd64"] + - key: "kubernetes.io/arch" + operator: In + values: ["amd64"] dotnet: environment: Production diff --git a/charts/tinyfilemanager/CONTRIBUTING.md b/charts/tinyfilemanager/CONTRIBUTING.md index 0c66f90..b4faea0 100644 --- a/charts/tinyfilemanager/CONTRIBUTING.md +++ b/charts/tinyfilemanager/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/tinyfilemanager/Chart.yaml b/charts/tinyfilemanager/Chart.yaml index ba12974..02b5bb2 100644 --- a/charts/tinyfilemanager/Chart.yaml +++ b/charts/tinyfilemanager/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: tinyfilemanager description: Tiny File Manager - intentionally vulnerable by default for security workshops type: application -version: 1.0.2 +version: 1.0.3 appVersion: "2.4.6" keywords: - security diff --git a/charts/tinyfilemanager/README.md b/charts/tinyfilemanager/README.md index f592dc1..1d3285d 100644 --- a/charts/tinyfilemanager/README.md +++ b/charts/tinyfilemanager/README.md @@ -1,4 +1,4 @@ -# Tiny File Manager Helm Chart +# Tiny File Manager Helm Chart Helm chart for [Tiny File Manager](https://tinyfilemanager.github.io/) — designed for container security workshops. diff --git a/charts/tinyfilemanager/templates/NOTES.txt b/charts/tinyfilemanager/templates/NOTES.txt index 75ea141..875d8cc 100644 --- a/charts/tinyfilemanager/templates/NOTES.txt +++ b/charts/tinyfilemanager/templates/NOTES.txt @@ -1,4 +1,4 @@ -1. Tiny File Manager web application has been deployed. +1. Tiny File Manager web application has been deployed. {{- if .Values.ingress.enabled }} URL: https://{{ .Values.ingress.domain }} diff --git a/charts/tinyfilemanager/templates/_helpers.tpl b/charts/tinyfilemanager/templates/_helpers.tpl index 09da1b5..f2c02f9 100644 --- a/charts/tinyfilemanager/templates/_helpers.tpl +++ b/charts/tinyfilemanager/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Expand the name of the chart. */}} {{- define "tinyfilemanager.name" -}} diff --git a/charts/tinyfilemanager/templates/deployment.yaml b/charts/tinyfilemanager/templates/deployment.yaml index 24c0e0a..e77a7a3 100644 --- a/charts/tinyfilemanager/templates/deployment.yaml +++ b/charts/tinyfilemanager/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "tinyfilemanager.fullname" . }} diff --git a/charts/tinyfilemanager/templates/hpa.yaml b/charts/tinyfilemanager/templates/hpa.yaml index 0f1106b..0d09530 100644 --- a/charts/tinyfilemanager/templates/hpa.yaml +++ b/charts/tinyfilemanager/templates/hpa.yaml @@ -1,4 +1,4 @@ -{{- if .Values.autoscaling.enabled }} +{{- if .Values.autoscaling.enabled }} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: diff --git a/charts/tinyfilemanager/templates/ingress.yaml b/charts/tinyfilemanager/templates/ingress.yaml index 5894081..f37740f 100644 --- a/charts/tinyfilemanager/templates/ingress.yaml +++ b/charts/tinyfilemanager/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/tinyfilemanager/templates/networkpolicy.yaml b/charts/tinyfilemanager/templates/networkpolicy.yaml index 4650615..2839435 100644 --- a/charts/tinyfilemanager/templates/networkpolicy.yaml +++ b/charts/tinyfilemanager/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkpolicy.enabled -}} +{{- if .Values.networkpolicy.enabled -}} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/tinyfilemanager/templates/service.yaml b/charts/tinyfilemanager/templates/service.yaml index fd28b34..3c93b5a 100644 --- a/charts/tinyfilemanager/templates/service.yaml +++ b/charts/tinyfilemanager/templates/service.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: Service metadata: name: {{ include "tinyfilemanager.fullname" . }} diff --git a/charts/tinyfilemanager/values.yaml b/charts/tinyfilemanager/values.yaml index 6bef7c2..3a16f16 100644 --- a/charts/tinyfilemanager/values.yaml +++ b/charts/tinyfilemanager/values.yaml @@ -1,4 +1,4 @@ -image: +image: repository: ghcr.io/devpro/tinyfilemanager tag: "2.4.6" pullPolicy: IfNotPresent diff --git a/charts/todoblazor/.helmignore b/charts/todoblazor/.helmignore index 59a8c7d..0e8a0eb 100644 --- a/charts/todoblazor/.helmignore +++ b/charts/todoblazor/.helmignore @@ -1,4 +1,4 @@ -# Patterns to ignore when building packages. +# Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and # negation (prefixed with !). Only one pattern per line. .DS_Store diff --git a/charts/todoblazor/CONTRIBUTING.md b/charts/todoblazor/CONTRIBUTING.md index 13f79e5..7ee9439 100644 --- a/charts/todoblazor/CONTRIBUTING.md +++ b/charts/todoblazor/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Update chart dependencies diff --git a/charts/todoblazor/Chart.yaml b/charts/todoblazor/Chart.yaml index 286a625..259bafe 100644 --- a/charts/todoblazor/Chart.yaml +++ b/charts/todoblazor/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: todoblazor description: Helm chart for Todo Blazor web application type: application -version: 0.1.3 +version: 0.1.5 appVersion: "1.0.0" dependencies: - name: mongodb diff --git a/charts/todoblazor/README.md b/charts/todoblazor/README.md index 33866d6..565b4b0 100644 --- a/charts/todoblazor/README.md +++ b/charts/todoblazor/README.md @@ -1,4 +1,4 @@ -# Helm chart for Todo Blazor +# Helm chart for Todo Blazor This Helm chart will deploy [Todo Blazor](https://github.com/devpro/todo-blazor) on a Kubernetes cluster. diff --git a/charts/todoblazor/ci/ci-values.yaml b/charts/todoblazor/ci/ci-values.yaml new file mode 100644 index 0000000..0407340 --- /dev/null +++ b/charts/todoblazor/ci/ci-values.yaml @@ -0,0 +1,9 @@ +# The bundled MongoDB under a fixed name, so that the connection string does not depend on the release name. +webapp: + db: + connectionString: mongodb://root:ci-only-password@ci-mongodb:27017/?authSource=admin +mongodb: + enabled: true + fullnameOverride: ci-mongodb + auth: + rootPassword: ci-only-password diff --git a/charts/todoblazor/templates/NOTES.txt b/charts/todoblazor/templates/NOTES.txt index 5f28270..e69de29 100644 --- a/charts/todoblazor/templates/NOTES.txt +++ b/charts/todoblazor/templates/NOTES.txt @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/charts/todoblazor/templates/_helpers.tpl b/charts/todoblazor/templates/_helpers.tpl index 5f28270..e69de29 100644 --- a/charts/todoblazor/templates/_helpers.tpl +++ b/charts/todoblazor/templates/_helpers.tpl @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/charts/todoblazor/templates/clusterrolebinding.yaml b/charts/todoblazor/templates/clusterrolebinding.yaml index 0ed9e73..5c6c7ef 100644 --- a/charts/todoblazor/templates/clusterrolebinding.yaml +++ b/charts/todoblazor/templates/clusterrolebinding.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.security.rbac.giveClusterAdmin .Values.security.serviceAccount.create }} +{{- if and .Values.security.rbac.giveClusterAdmin .Values.security.serviceAccount.create }} {{- $namespace := .Release.Namespace }} {{- $applications := list .Values.webapp -}} {{ range $applications }} diff --git a/charts/todoblazor/templates/deployment.yaml b/charts/todoblazor/templates/deployment.yaml index 46e152b..287a154 100644 --- a/charts/todoblazor/templates/deployment.yaml +++ b/charts/todoblazor/templates/deployment.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.webapp -}} +{{- $applications := list .Values.webapp -}} {{ range $applications }} {{- if .enabled -}} --- diff --git a/charts/todoblazor/templates/ingress.yaml b/charts/todoblazor/templates/ingress.yaml index 532e36b..84cf988 100644 --- a/charts/todoblazor/templates/ingress.yaml +++ b/charts/todoblazor/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.webapp -}} +{{- $applications := list .Values.webapp -}} {{ range $applications }} {{- if and $.Values.ingress.enabled .enabled .host -}} --- diff --git a/charts/todoblazor/templates/service.yaml b/charts/todoblazor/templates/service.yaml index 55ae081..d5ac563 100644 --- a/charts/todoblazor/templates/service.yaml +++ b/charts/todoblazor/templates/service.yaml @@ -1,4 +1,4 @@ -{{- $applications := list .Values.webapp -}} +{{- $applications := list .Values.webapp -}} {{ range $applications }} {{- if .enabled -}} --- diff --git a/charts/todoblazor/templates/serviceaccount.yaml b/charts/todoblazor/templates/serviceaccount.yaml index c70b502..0c705ad 100644 --- a/charts/todoblazor/templates/serviceaccount.yaml +++ b/charts/todoblazor/templates/serviceaccount.yaml @@ -1,4 +1,4 @@ -{{- if .Values.security.serviceAccount.create }} +{{- if .Values.security.serviceAccount.create }} {{- $applications := list .Values.webapp -}} {{ range $applications }} apiVersion: v1 diff --git a/charts/todoblazor/values.yaml b/charts/todoblazor/values.yaml index 8087a61..aa90fd4 100644 --- a/charts/todoblazor/values.yaml +++ b/charts/todoblazor/values.yaml @@ -1,4 +1,4 @@ -webapp: +webapp: enabled: true name: todoblazor image: docker.io/devprofr/todoblazor diff --git a/charts/trackboard/Chart.yaml b/charts/trackboard/Chart.yaml index 154aae9..ed28073 100644 --- a/charts/trackboard/Chart.yaml +++ b/charts/trackboard/Chart.yaml @@ -1,8 +1,8 @@ -apiVersion: v2 +apiVersion: v2 name: trackboard description: Trackboard web application type: application -version: 0.1.0 +version: 0.1.1 appVersion: "0.1.0" keywords: - event diff --git a/charts/trackboard/README.md b/charts/trackboard/README.md index 944e5d4..bef48b7 100644 --- a/charts/trackboard/README.md +++ b/charts/trackboard/README.md @@ -1,4 +1,4 @@ -# Trackboard Helm Chart +# Trackboard Helm Chart Helm chart for [Trackboard Web Application](https://github.com/devpro/container-images/tree/main/src/trackboard) — designed for container security workshops. diff --git a/charts/trackboard/templates/NOTES.txt b/charts/trackboard/templates/NOTES.txt index 567b9b9..ed61d0c 100644 --- a/charts/trackboard/templates/NOTES.txt +++ b/charts/trackboard/templates/NOTES.txt @@ -1,4 +1,4 @@ -1. Trackboard web application has been deployed. +1. Trackboard web application has been deployed. {{- if .Values.ingress.enabled }} URL: https://{{ .Values.ingress.domain }} diff --git a/charts/trackboard/templates/_helpers.tpl b/charts/trackboard/templates/_helpers.tpl index 4379db8..620ad51 100644 --- a/charts/trackboard/templates/_helpers.tpl +++ b/charts/trackboard/templates/_helpers.tpl @@ -1,4 +1,4 @@ -{{/* +{{/* Expand the name of the chart. */}} {{- define "trackboard.name" -}} diff --git a/charts/trackboard/templates/deployment.yaml b/charts/trackboard/templates/deployment.yaml index 18756e7..53a40a7 100644 --- a/charts/trackboard/templates/deployment.yaml +++ b/charts/trackboard/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "trackboard.fullname" . }} diff --git a/charts/trackboard/templates/ingress.yaml b/charts/trackboard/templates/ingress.yaml index 204a49b..b843a2f 100644 --- a/charts/trackboard/templates/ingress.yaml +++ b/charts/trackboard/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/charts/trackboard/templates/networkpolicy.yaml b/charts/trackboard/templates/networkpolicy.yaml index 8e9ccc1..a08e761 100644 --- a/charts/trackboard/templates/networkpolicy.yaml +++ b/charts/trackboard/templates/networkpolicy.yaml @@ -1,4 +1,4 @@ -{{- if .Values.networkpolicy.enabled -}} +{{- if .Values.networkpolicy.enabled -}} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/trackboard/templates/pvc.yaml b/charts/trackboard/templates/pvc.yaml index 22e5938..9522e71 100644 --- a/charts/trackboard/templates/pvc.yaml +++ b/charts/trackboard/templates/pvc.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: PersistentVolumeClaim metadata: name: {{ include "trackboard.fullname" . }} diff --git a/charts/trackboard/templates/secret.yaml b/charts/trackboard/templates/secret.yaml index 5b603ca..0d8ce35 100644 --- a/charts/trackboard/templates/secret.yaml +++ b/charts/trackboard/templates/secret.yaml @@ -1,4 +1,4 @@ -{{- if .Values.admin.password -}} +{{- if .Values.admin.password -}} apiVersion: v1 kind: Secret metadata: diff --git a/charts/trackboard/templates/service.yaml b/charts/trackboard/templates/service.yaml index ac38825..be652ff 100644 --- a/charts/trackboard/templates/service.yaml +++ b/charts/trackboard/templates/service.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v1 kind: Service metadata: name: {{ include "trackboard.fullname" . }} diff --git a/charts/trackboard/values.yaml b/charts/trackboard/values.yaml index d2eac54..46b695d 100644 --- a/charts/trackboard/values.yaml +++ b/charts/trackboard/values.yaml @@ -1,4 +1,4 @@ -image: +image: repository: ghcr.io/devpro/trackboard tag: "1.0.0" pullPolicy: IfNotPresent diff --git a/charts/webgoat/CONTRIBUTING.md b/charts/webgoat/CONTRIBUTING.md index 95f8338..5e2a133 100644 --- a/charts/webgoat/CONTRIBUTING.md +++ b/charts/webgoat/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contribution guide +# Contribution guide ## Validate on a test cluster diff --git a/charts/webgoat/Chart.yaml b/charts/webgoat/Chart.yaml index 1a8e9e6..8536dbc 100644 --- a/charts/webgoat/Chart.yaml +++ b/charts/webgoat/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: webgoat description: OWASP WebGoat — intentionally vulnerable Java/Spring app for security workshops type: application -version: 1.0.0 +version: 1.0.1 appVersion: "2023.8" keywords: - security diff --git a/charts/webgoat/README.md b/charts/webgoat/README.md index 67e2bc1..ac185f5 100644 --- a/charts/webgoat/README.md +++ b/charts/webgoat/README.md @@ -1,4 +1,4 @@ -# OWASP WebGoat Helm Chart +# OWASP WebGoat Helm Chart Helm chart for [WebGoat Web Application](https://owasp.org/www-project-webgoat/) — designed for container security workshops. diff --git a/charts/webgoat/templates/NOTES.txt b/charts/webgoat/templates/NOTES.txt index 8c05230..20df4ad 100644 --- a/charts/webgoat/templates/NOTES.txt +++ b/charts/webgoat/templates/NOTES.txt @@ -1,4 +1,4 @@ -🎯 WebGoat + WebWolf deployed! +🎯 WebGoat + WebWolf deployed! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ACCESS diff --git a/charts/webgoat/templates/deployment.yaml b/charts/webgoat/templates/deployment.yaml index d62af02..ce6e4f0 100644 --- a/charts/webgoat/templates/deployment.yaml +++ b/charts/webgoat/templates/deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "webgoat.fullname" . }} diff --git a/charts/webgoat/templates/ingress.yaml b/charts/webgoat/templates/ingress.yaml index fa8176b..f859ead 100644 --- a/charts/webgoat/templates/ingress.yaml +++ b/charts/webgoat/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled -}} {{- if not .Values.ingress.domain }} {{- fail "ingress.domain is required when ingress.enabled=true. Set it with --set ingress.domain=workshop.example.com" }} {{- end }} diff --git a/charts/webgoat/values.yaml b/charts/webgoat/values.yaml index 5f638f8..535ba12 100644 --- a/charts/webgoat/values.yaml +++ b/charts/webgoat/values.yaml @@ -1,4 +1,4 @@ -replicaCount: 1 +replicaCount: 1 webgoat: image: diff --git a/docs/application-guides/databases/mysql.md b/docs/application-guides/databases/mysql.md index 76ed303..c14c76c 100644 --- a/docs/application-guides/databases/mysql.md +++ b/docs/application-guides/databases/mysql.md @@ -40,7 +40,8 @@ kubectl delete ns mysql 3. Connect using the mysql cli, then provide your password: `$ mysql -h mysql-xxxxxxx -p` - To connect to your database directly from outside the K8s cluster: MYSQL_HOST=127.0.0.1, MYSQL_PORT=3306. Execute the following command to route the connection: `kubectl port-forward svc/mysql-xxxxxxx 3306` and `mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}`. + To connect to your database directly from outside the K8s cluster: MYSQL_HOST=127.0.0.1, MYSQL_PORT=3306. + Execute the following command to route the connection: `kubectl port-forward svc/mysql-xxxxxxx 3306` and `mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}`. As usual, look at the progress with `kubectl get pods` ("STATUS" column). diff --git a/docs/application-guides/developer-platforms/drupal.md b/docs/application-guides/developer-platforms/drupal.md index 8fa8467..93ab9a3 100644 --- a/docs/application-guides/developer-platforms/drupal.md +++ b/docs/application-guides/developer-platforms/drupal.md @@ -1,4 +1,4 @@ -# Drupal +# Drupal ## Stable repository diff --git a/docs/application-guides/pipeline-orchestration/concourse.md b/docs/application-guides/pipeline-orchestration/concourse.md index 1bf0f4b..5bef52a 100644 --- a/docs/application-guides/pipeline-orchestration/concourse.md +++ b/docs/application-guides/pipeline-orchestration/concourse.md @@ -91,7 +91,12 @@ spec: kubectl apply -f ingress.yaml # activate ingress -helm install my-name stable/concourse --set concourse.web.externalUrl=https://my-domain.com/ --set web.ingress.enabled=true --set web.ingress.hosts[0]=my-domain.com --set web.ingress.tls[0].secretName=domain-web-tls --set web.ingress.tls[0].hosts[0]=my-domain.com --set secrets.localUsers="test:mysecretpassword" --set web.resources.requests.cpu="200m" --set web.resources.requests.memory="256Mi" +helm install my-name stable/concourse \ + --set concourse.web.externalUrl=https://my-domain.com/ \ + --set web.ingress.enabled=true --set web.ingress.hosts[0]=my-domain.com \ + --set web.ingress.tls[0].secretName=domain-web-tls --set web.ingress.tls[0].hosts[0]=my-domain.com \ + --set secrets.localUsers="test:mysecretpassword" \ + --set web.resources.requests.cpu="200m" --set web.resources.requests.memory="256Mi" ``` ## Known issues diff --git a/docs/custom-charts/index.md b/docs/custom-charts/index.md index 8f99bc2..7cd2b89 100644 --- a/docs/custom-charts/index.md +++ b/docs/custom-charts/index.md @@ -1,4 +1,4 @@ ---- +--- order: 2 --- @@ -7,6 +7,7 @@ order: 2 ## Applications * [Keeptrack](keeptrack.md) +* Fallstar * Sidelab ## Databases diff --git a/docs/custom-charts/keeptrack.md b/docs/custom-charts/keeptrack.md index cfa61e1..5460e31 100644 --- a/docs/custom-charts/keeptrack.md +++ b/docs/custom-charts/keeptrack.md @@ -1,4 +1,4 @@ -# Keeptrack +# Keeptrack Let's see how to deploy [Keeptrack](https://github.com/devpro/keeptrack) on a Kubernetes cluster. diff --git a/docs/custom-charts/terraform-backend-mongodb.md b/docs/custom-charts/terraform-backend-mongodb.md index e760889..78b2e9a 100644 --- a/docs/custom-charts/terraform-backend-mongodb.md +++ b/docs/custom-charts/terraform-backend-mongodb.md @@ -1,4 +1,4 @@ -# Terraform Backend MongoDB +# Terraform Backend MongoDB Let's see how to deploy [Terraform Backend MongoDB](https://github.com/devpro/terraform-backend-mongodb) on a Kubernetes cluster. diff --git a/docs/examples/api-examples.md b/docs/examples/api-examples.md index 691df9c..18ce89c 100644 --- a/docs/examples/api-examples.md +++ b/docs/examples/api-examples.md @@ -2,6 +2,8 @@ outline: deep --- + + # Runtime API Examples This page demonstrates usage of some of the runtime APIs provided by VitePress. diff --git a/docs/examples/markdown-examples.md b/docs/examples/markdown-examples.md index f9258a5..619f69a 100644 --- a/docs/examples/markdown-examples.md +++ b/docs/examples/markdown-examples.md @@ -1,3 +1,5 @@ + + # Markdown Extension Examples This page demonstrates some of the built-in markdown extensions provided by VitePress. diff --git a/package.json b/package.json index 6d3ff85..4ef5b14 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "vitepress-sidebar": "^1.33.0" }, "scripts": { + "build": "vitepress build docs", "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs" diff --git a/samples/charts/hobbyfarm/README.md b/samples/charts/hobbyfarm/README.md index 9c07605..05b2f1a 100644 --- a/samples/charts/hobbyfarm/README.md +++ b/samples/charts/hobbyfarm/README.md @@ -22,8 +22,8 @@ kubectl delete ns hobbyfarm ## How to start once the application is running -- Open HobbyFarm Admin UI, for example "https://admin.hobbyfarm.${NGINX_PUBLIC_IP}.sslip.io/" -- Open HobbyFarm UI, for example "https://learn.hobbyfarm.${NGINX_PUBLIC_IP}.sslip.io/" +- Open HobbyFarm Admin UI, for example `https://admin.hobbyfarm.${NGINX_PUBLIC_IP}.sslip.io/` +- Open HobbyFarm UI, for example `https://learn.hobbyfarm.${NGINX_PUBLIC_IP}.sslip.io/` ## How to create or update the chart diff --git a/samples/gitops/devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml b/samples/gitops/devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml index 86d7e1f..9265a43 100644 --- a/samples/gitops/devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml +++ b/samples/gitops/devopsdays-geneva-2023/fleet/crds/cert-manager.crds.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 The cert-manager Authors. +# Copyright 2022 The cert-manager Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/gitops/suse-exchange-paris-2023/fleet/repo-content/above-crds/cert-manager.crds.yaml b/samples/gitops/suse-exchange-paris-2023/fleet/repo-content/above-crds/cert-manager.crds.yaml index 86d7e1f..9265a43 100644 --- a/samples/gitops/suse-exchange-paris-2023/fleet/repo-content/above-crds/cert-manager.crds.yaml +++ b/samples/gitops/suse-exchange-paris-2023/fleet/repo-content/above-crds/cert-manager.crds.yaml @@ -1,4 +1,4 @@ -# Copyright 2022 The cert-manager Authors. +# Copyright 2022 The cert-manager Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/scripts/validate_charts.sh b/scripts/validate_charts.sh new file mode 100755 index 0000000..11fd79d --- /dev/null +++ b/scripts/validate_charts.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Renders each chart and validates the manifests against the Kubernetes API schemas, which catches a wrong apiVersion or a misspelled field that helm lint lets through. +# A chart is rendered once per values file in its ci directory, as ct does, and with its default values when it has none. +# Usage: scripts/validate_charts.sh [chart directory...], every chart under charts when none is given. + +set -euo pipefail + +kubernetes_version="${KUBERNETES_VERSION:-1.36.5}" +# Custom resources are read from the CRDs catalog, pinned so that a schema change upstream never breaks an unrelated commit. +# A kind it does not hold, such as the Rancher node driver configurations, is counted as skipped rather than failed. +crds_catalog="https://raw.githubusercontent.com/datreeio/CRDs-catalog/501527ab9c40c827144c8f45945b25a023e344ce/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json" + +if [[ $# -gt 0 ]]; then + charts=("$@") +else + charts=(charts/*/) +fi + +failed=() +for chart in "${charts[@]}"; do + chart="${chart%/}" + [[ -f "$chart/Chart.yaml" ]] || continue + + if grep -q "^dependencies:" "$chart/Chart.yaml"; then + helm dependency build "$chart" > /dev/null + fi + + values_files=("$chart"/ci/*-values.yaml) + [[ -e "${values_files[0]}" ]] || values_files=("") + + for values_file in "${values_files[@]}"; do + echo "--- $chart ${values_file:+with $(basename "$values_file")}" + if ! helm template ci "$chart" ${values_file:+--values "$values_file"} --namespace ci --kube-version "$kubernetes_version" \ + | kubeconform -strict -summary -ignore-missing-schemas -kubernetes-version "$kubernetes_version" \ + -schema-location default -schema-location "$crds_catalog"; then + failed+=("$chart ${values_file:+($(basename "$values_file"))}") + fi + done +done + +if [[ ${#failed[@]} -gt 0 ]]; then + echo "Invalid charts:" + printf ' %s\n' "${failed[@]}" + exit 1 +fi