diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..38ad1963 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +debug/ +target/ +**/*.rs.bk + +.idea/ +*.iws + +# We do NOT want to ignore .git because we use the `built` crate to gather the current git commit hash at built time +# This means we need the .git directory in our Docker image, it will be thrown away and won't be included in the final image diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..50eaadb2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,30 @@ +# ============= +# This file is automatically generated from the templates in stackabletech/operator-templating +# DON'T MANUALLY EDIT THIS FILE +# ============= +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "type/dependencies" + reviewers: + - "stackabletech/developers" + + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" + labels: + - "type/dependencies" + reviewers: + - "stackabletech/rust-developers" + ignore: + # We never want to be notified about a kube-rs update. + # It often contains breaking changes so it has to be updated manually anyway + # and it needs to be updated together with kube-runtime, kube-derive etc. + - dependency-name: "kube*" + - dependency-name: "k8s-openapi" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..14b10276 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,14 @@ +## Description + +*Please add a description here. This will become the commit message of the merge request later.* + + + +## Review Checklist +- [ ] Code contains useful comments +- [ ] (Integration-)Test cases added (or not applicable) +- [ ] Documentation added (or not applicable) +- [ ] Changelog updated (or not applicable) +- [ ] Cargo.toml only contains references to git tags (not specific commits or branches) + +Once the review is done, comment `bors r+` (or `bors merge`) to merge. [Further information](https://bors.tech/documentation/getting-started/#reviewing-pull-requests) diff --git a/.github/workflows/daily_security.yml b/.github/workflows/daily_security.yml index f4627093..850a3f4d 100644 --- a/.github/workflows/daily_security.yml +++ b/.github/workflows/daily_security.yml @@ -2,11 +2,12 @@ # This file is automatically generated from the templates in stackabletech/operator-templating # DON'T MANUALLY EDIT THIS FILE # ============= +--- name: Security audit on: schedule: - - cron: '0 0 * * *' + - cron: '15 4 * * *' workflow_dispatch: jobs: diff --git a/.github/workflows/helm_tests.yml b/.github/workflows/helm_tests.yml new file mode 100644 index 00000000..017edeef --- /dev/null +++ b/.github/workflows/helm_tests.yml @@ -0,0 +1,53 @@ +name: Lint and Test Helm Charts + +on: + push: + branches: ["main"] + pull_request: + +env: + CT_CONFIG: deploy/helm/ct.yaml + +jobs: + lint-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.4.0 + with: + fetch-depth: 0 + submodules: true + + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: v3.7.2 + + - uses: actions/setup-python@v2 + with: + python-version: 3.7 + + - name: Compile chart + run: make compile-chart + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.2.0 + + - name: Run chart-testing (lint) + run: ct lint --config "${CT_CONFIG}" + + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --config "${CT_CONFIG}") + if [[ -n "$changed" ]]; then + echo "::set-output name=changed::true" + fi + + - name: Create kind cluster + uses: helm/kind-action@v1.2.0 + if: steps.list-changed.outputs.changed == 'true' + + - name: Run chart-testing (install) + run: ct install --config "${CT_CONFIG}" + if: steps.list-changed.outputs.changed == 'true' diff --git a/.github/workflows/pr_generate_manifests.yml b/.github/workflows/pr_generate_manifests.yml new file mode 100644 index 00000000..c480eedf --- /dev/null +++ b/.github/workflows/pr_generate_manifests.yml @@ -0,0 +1,39 @@ +# ============= +# This file is automatically generated from the templates in stackabletech/operator-templating +# DON'T MANUALLY EDIT THIS FILE +# ============= +name: Update Manifest files + +on: + pull_request: + +jobs: + manifests: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + token: ${{ secrets.STACKY_MC_STACKFACE_TOKEN }} + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: v3.6.2 + - name: update manifests + env: + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + if: env.NEXUS_PASSWORD != null + run: make generate-manifests + - name: Add & Commit + env: + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + if: env.NEXUS_PASSWORD != null + uses: EndBug/add-and-commit@v7 + with: + default_author: user_info + author_name: Stacky McStackface + author_email: stackable-bot@users.noreply.github.com + pathspec_error_handling: exitImmediately + pull: NO-PULL + add: 'deploy' + message: 'Github Actions: Generated k8s manifest files' diff --git a/.github/workflows/publish_main_artifacts.yml b/.github/workflows/publish_main_artifacts.yml new file mode 100644 index 00000000..6001d61e --- /dev/null +++ b/.github/workflows/publish_main_artifacts.yml @@ -0,0 +1,60 @@ +# ============= +# This file is automatically generated from the templates in stackabletech/operator-templating +# DON'T MANUALLY EDIT THIS FILE +# ============= +--- +name: Publish nightly artifacts from main branch + +on: + push: + branches: + - main + schedule: + - cron: '30 4 * * *' + workflow_dispatch: + +env: + PRODUCT_NAME: secret-operator + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: '0' + CARGO_PROFILE_DEV_DEBUG: '0' + RUSTFLAGS: "-D warnings" + REPO_HELM_URL: https://repo.stackable.tech/repository/helm-dev + +jobs: + helm: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: true + + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: v3.6.2 + + - name: Build Docker image + env: + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + if: env.NEXUS_PASSWORD != null + run: make docker + + - name: Compile chart + run: make compile-chart + + - name: Package Chart + run: mkdir -p target/helm && helm package --destination target/helm deploy/helm/secret-operator + + - name: Publish Chart + env: + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + if: env.NEXUS_PASSWORD != null + run: >- + /usr/bin/curl + --fail + -u 'github:${{ secrets.NEXUS_PASSWORD }}' + --upload-file "./$(find target/helm/ -name '*.tgz')" + "${{ env.REPO_HELM_URL }}/" diff --git a/.github/workflows/publish_pr_artifacts.yml b/.github/workflows/publish_pr_artifacts.yml new file mode 100644 index 00000000..b0818303 --- /dev/null +++ b/.github/workflows/publish_pr_artifacts.yml @@ -0,0 +1,63 @@ +# ============= +# This file is automatically generated from the templates in stackabletech/operator-templating +# DON'T MANUALLY EDIT THIS FILE +# ============= +--- +name: Publish pull-request artifacts + +on: + pull_request: + +env: + PRODUCT_NAME: secret-operator + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: '0' + CARGO_PROFILE_DEV_DEBUG: '0' + RUSTFLAGS: "-D warnings" + REPO_HELM_URL: https://repo.stackable.tech/repository/helm-test + +jobs: + helm: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: true + + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: v3.6.2 + + - name: Set up Python and update cargo version. + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - run: pip install -r ./python/requirements.txt + - run: python ./python/cargo_version.py -m pr${{ github.event.number }} + + - name: Build Docker image + env: + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + if: env.NEXUS_PASSWORD != null + run: make docker + + - name: Compile chart + run: make compile-chart + + - name: Package Chart + run: mkdir -p target/helm && helm package --destination target/helm deploy/helm/secret-operator + + - name: Publish Chart + env: + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + if: env.NEXUS_PASSWORD != null + run: >- + /usr/bin/curl + --fail + -u 'github:${{ secrets.NEXUS_PASSWORD }}' + --upload-file "./$(find target/helm/ -name '*.tgz')" + "${{ env.REPO_HELM_URL }}/" diff --git a/.github/workflows/publish_release_artifacts.yml b/.github/workflows/publish_release_artifacts.yml new file mode 100644 index 00000000..781a8310 --- /dev/null +++ b/.github/workflows/publish_release_artifacts.yml @@ -0,0 +1,57 @@ +# ============= +# This file is automatically generated from the templates in stackabletech/operator-templating +# DON'T MANUALLY EDIT THIS FILE +# ============= +--- +name: Publish release artifacts + +on: + push: + tags: + - "*" + +env: + PRODUCT_NAME: secret-operator + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: '0' + CARGO_PROFILE_DEV_DEBUG: '0' + RUSTFLAGS: "-D warnings" + REPO_HELM_URL: https://repo.stackable.tech/repository/helm-stable + +jobs: + helm: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: true + + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: v3.6.2 + + - name: Build Docker image + env: + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + if: env.NEXUS_PASSWORD != null + run: make docker-release + + - name: Compile chart + run: make compile-chart + + - name: Package Chart + run: mkdir -p target/helm && helm package --destination target/helm deploy/helm/secret-operator + + - name: Publish Chart + env: + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + if: env.NEXUS_PASSWORD != null + run: >- + /usr/bin/curl + --fail + -u 'github:${{ secrets.NEXUS_PASSWORD }}' + --upload-file "./$(find target/helm/ -name '*.tgz')" + "${{ env.REPO_HELM_URL }}/" diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9c1afd3e..4d5b341e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,7 +2,8 @@ # This file is automatically generated from the templates in stackabletech/operator-templating # DON'T MANUALLY EDIT THIS FILE # ============= -name: Rust +--- +name: Rust checks on: push: @@ -16,7 +17,9 @@ env: CARGO_TERM_COLOR: always CARGO_INCREMENTAL: '0' CARGO_PROFILE_DEV_DEBUG: '0' - RUSTFLAGS: "-D warnings -W rust-2021-compatibility" + RUSTFLAGS: "-D warnings" + RUSTDOCFLAGS: "-D warnings" + RUST_LOG: "info" jobs: @@ -33,6 +36,8 @@ jobs: toolchain: stable override: true - uses: Swatinem/rust-cache@v1.3.0 + with: + key: test - uses: actions-rs/cargo@v1.0.3 with: command: test @@ -42,8 +47,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2.4.0 - with: - submodules: true - uses: actions-rs/toolchain@v1.0.7 with: profile: minimal @@ -69,6 +72,8 @@ jobs: components: rustfmt override: true - uses: Swatinem/rust-cache@v1.3.0 + with: + key: doc - uses: actions-rs/cargo@v1.0.3 with: command: doc @@ -83,11 +88,13 @@ jobs: submodules: true - uses: actions-rs/toolchain@v1.0.7 with: - profile: minimal - toolchain: stable - components: clippy - override: true + profile: minimal + toolchain: stable + components: clippy + override: true - uses: Swatinem/rust-cache@v1.3.0 + with: + key: clippy # We need this due to: https://github.com/actions-rs/clippy-check/issues/2 - name: Check workflow permissions id: check_permissions @@ -120,8 +127,6 @@ jobs: steps: - uses: actions/checkout@v2.4.0 - with: - submodules: true - uses: EmbarkStudios/cargo-deny-action@v1.2.6 with: command: check ${{ matrix.checks }} diff --git a/.gitignore b/.gitignore index 767605cf..4fd566b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,13 @@ -/target +debug/ +target/ +**/*.rs.bk -/result* -/Cargo.nix -/crate-hashes.json +.idea/ +*.iws +*.iml + +*.tgz + +Cargo.nix +crate-hashes.json +result* \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..5df27e82 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,9 @@ +[MESSAGES CONTROL] + +# These rules are for missing docstrings which doesn't matter much for most of our simple scripts +disable=C0114,C0115,C0116 + +[FORMAT] + +max-line-length=999 +indent-string=' ' diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 00000000..91a7fec6 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,10 @@ +--- +extends: default + +ignore: | + deploy/helm/**/templates + +rules: + line-length: disable + truthy: + check-keys: false diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..16e000d6 --- /dev/null +++ b/Makefile @@ -0,0 +1,59 @@ +# ============= +# This file is automatically generated from the templates in stackabletech/operator-templating +# DON'T MANUALLY EDIT THIS FILE +# ============= + +# This script requires https://github.com/mikefarah/yq (not to be confused with https://github.com/kislyuk/yq) +# It is available from Nixpkgs as `yq-go` (`nix shell nixpkgs#yq-go`) + +.PHONY: docker chart-lint compile-chart + +TAG := $(shell git rev-parse --short HEAD) + +VERSION := $(shell cargo metadata --format-version 1 | jq '.packages[] | select(.name=="stackable-secret-operator") | .version') + +## Docker related targets +docker-build: + docker build --force-rm -t "docker.stackable.tech/stackable/secret-operator:${VERSION}" -f docker/Dockerfile . + +docker-build-latest: docker-build + docker tag "docker.stackable.tech/stackable/secret-operator:${VERSION}" \ + "docker.stackable.tech/stackable/secret-operator:latest" + +docker-publish: + echo "${NEXUS_PASSWORD}" | docker login --username github --password-stdin docker.stackable.tech + docker push --all-tags docker.stackable.tech/stackable/secret-operator + +docker: docker-build docker-publish + +docker-release: docker-build-latest docker-publish + +## Chart related targets +compile-chart: version crds config + +chart-clean: + rm -rf deploy/helm/secret-operator/configs + rm -rf deploy/helm/secret-operator/crds + rm -rf deploy/helm/secret-operator/templates/crds.yaml + +version: + yq eval -i '.version = ${VERSION} | .appVersion = ${VERSION}' deploy/helm/secret-operator/Chart.yaml + +config: + +crds: deploy/helm/secret-operator/crds/crds.yaml + +deploy/helm/secret-operator/crds/crds.yaml: + mkdir -p deploy/helm/secret-operator/crds + cargo run crd | yq eval '.metadata.annotations["helm.sh/resource-policy"]="keep"' - > ${@} + +chart-lint: compile-chart + docker run -it -v $(shell pwd):/build/helm-charts -w /build/helm-charts quay.io/helmpack/chart-testing:v3.5.0 ct lint --config deploy/helm/ct.yaml + +## Manifest related targets +clean-manifests: + mkdir -p deploy/manifests + rm -rf $$(find deploy/manifests -maxdepth 1 -mindepth 1 -not -name Kustomization) + +generate-manifests: clean-manifests compile-chart + ./scripts/generate-manifests.sh diff --git a/Tiltfile b/Tiltfile index 841ef56e..f84eb354 100644 --- a/Tiltfile +++ b/Tiltfile @@ -9,4 +9,4 @@ k8s_yaml('provisioner.yaml') k8s_yaml('example-consumer-nginx.yaml') watch_file('result') if os.path.exists('result'): - k8s_yaml('result/crd.yaml') + k8s_yaml('result/crds.yaml') diff --git a/bors.toml b/bors.toml index 2ce6da66..4f305075 100644 --- a/bors.toml +++ b/bors.toml @@ -6,5 +6,7 @@ status = [ 'Run cargo deny (bans licenses sources)' ] delete_merged_branches = true +use_squash_merge = true pr_status = [ 'license/cla' ] -timeout_sec = 7200 \ No newline at end of file +timeout_sec = 7200 +cut_body_after = "" diff --git a/default.nix b/default.nix index 95675cb2..e5e571bc 100644 --- a/default.nix +++ b/default.nix @@ -18,6 +18,11 @@ }: rec { build = cargo.rootCrate.build; + crds = pkgs.runCommand "secret-provisioner-crds.yaml" {} + '' + ${build}/bin/stackable-secret-operator crd > $out + ''; + dockerImage = pkgs.dockerTools.streamLayeredImage { name = "docker.stackable.tech/teozkr/secret-provisioner"; tag = dockerTag; @@ -35,11 +40,8 @@ rec { path = pkgs.writeText "${dockerImage.name}-image-tag" "${dockerImage.imageName}:${dockerImage.imageTag}"; } { - name = "crd.yaml"; - path = pkgs.runCommand "secret-provisioner-crd.yaml" {} - '' - ${build}/bin/stackable-secret-operator crd > $out - ''; + name = "crds.yaml"; + path = crds; } ]; diff --git a/deny.toml b/deny.toml index 3b2ff45c..4f5ba74f 100644 --- a/deny.toml +++ b/deny.toml @@ -26,9 +26,6 @@ allow = [ "Apache-2.0", "BSD-3-Clause", "CC0-1.0", - "ISC", - "LicenseRef-ring", - "LicenseRef-webpki", "MIT", "Zlib" ] @@ -56,4 +53,4 @@ unknown-registry = "deny" unknown-git = "deny" [sources.allow-org] -github = ["stackabletech", "teozkr"] \ No newline at end of file +github = ["stackabletech", "teozkr"] diff --git a/deploy/DO_NOT_EDIT.md b/deploy/DO_NOT_EDIT.md new file mode 100644 index 00000000..da37bf18 --- /dev/null +++ b/deploy/DO_NOT_EDIT.md @@ -0,0 +1,4 @@ +These Helm charts and manifests are automatically generated. +Please do not edit anything in this directory manually. + +The details are in-motion but check this repository for a few details: https://github.com/stackabletech/operator-templating diff --git a/deploy/helm/ct.yaml b/deploy/helm/ct.yaml new file mode 100644 index 00000000..fbef6924 --- /dev/null +++ b/deploy/helm/ct.yaml @@ -0,0 +1,8 @@ +# This file is used for chart-testing (https://github.com/helm/chart-testing) +# The name "ct.yaml" is not very self-descriptive but it is the default that chart-testing is looking for +--- +remote: origin +target-branch: main +chart-dirs: + - deploy/helm +all: true diff --git a/deploy/helm/secret-operator/.helmignore b/deploy/helm/secret-operator/.helmignore new file mode 100644 index 00000000..fef44b7e --- /dev/null +++ b/deploy/helm/secret-operator/.helmignore @@ -0,0 +1,28 @@ +# ============= +# This file is automatically generated from the templates in stackabletech/operator-templating +# DON'T MANUALLY EDIT THIS FILE +# ============= + +# 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 +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/deploy/helm/secret-operator/Chart.yaml b/deploy/helm/secret-operator/Chart.yaml new file mode 100644 index 00000000..52561d43 --- /dev/null +++ b/deploy/helm/secret-operator/Chart.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: v2 +name: secret-operator +version: 0.1.0 +appVersion: "0.1.0" +description: The Stackable Operator for Stackable Secret Operator +home: https://github.com/stackabletech/secret-operator +maintainers: + - name: Stackable + url: https://www.stackable.tech diff --git a/deploy/helm/secret-operator/README.md b/deploy/helm/secret-operator/README.md new file mode 100644 index 00000000..cc0904a1 --- /dev/null +++ b/deploy/helm/secret-operator/README.md @@ -0,0 +1,33 @@ +# Helm Chart for Stackable Secret Operator + +This Helm Chart can be used to install Custom Resource Definitions and the Stackable Secret Operator. + + +## Requirements + +- Create a [Kubernetes Cluster](../Readme.md) +- Install [Helm](https://helm.sh/docs/intro/install/) + + +## Install the Stackable Secret Operator + +```bash +# From the root of the operator repository +make compile-chart + +helm install secret-operator deploy/helm/secret-operator +``` + + +## Usage of the CRDs + +The usage of this operator and its CRDs is described in the [documentation](https://docs.stackable.tech/secret-operator/index.html) + +The operator has example requests included in the [`/examples`](https://github.com/stackabletech/secret-operator/tree/main/examples) directory. + + +## Links + +https://github.com/stackabletech/secret-operator + + diff --git a/deploy/helm/secret-operator/crds/crds.yaml b/deploy/helm/secret-operator/crds/crds.yaml new file mode 100644 index 00000000..17f8afde --- /dev/null +++ b/deploy/helm/secret-operator/crds/crds.yaml @@ -0,0 +1,85 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: secretclasses.secrets.stackable.tech + annotations: + helm.sh/resource-policy: keep +spec: + group: secrets.stackable.tech + names: + categories: [] + kind: SecretClass + plural: secretclasses + shortNames: [] + singular: secretclass + scope: Cluster + versions: + - additionalPrinterColumns: [] + name: v1alpha1 + schema: + openAPIV3Schema: + description: "Auto-generated derived type for SecretClassSpec via `CustomResource`" + properties: + spec: + properties: + backend: + oneOf: + - required: + - k8sSearch + - required: + - autoTls + properties: + autoTls: + properties: + ca: + properties: + secret: + description: SecretReference represents a Secret Reference. It has enough information to retrieve secret in any namespace + properties: + name: + description: Name is unique within a namespace to reference a secret resource. + type: string + namespace: + description: Namespace defines the space within which the secret name must be unique. + type: string + type: object + required: + - secret + type: object + required: + - ca + type: object + k8sSearch: + properties: + searchNamespace: + oneOf: + - required: + - pod + - required: + - name + properties: + name: + type: string + pod: + type: object + type: object + secretLabels: + additionalProperties: + type: string + default: {} + type: object + required: + - searchNamespace + type: object + type: object + required: + - backend + type: object + required: + - spec + title: SecretClass + type: object + served: true + storage: true + subresources: {} diff --git a/deploy/helm/secret-operator/templates/_helpers.tpl b/deploy/helm/secret-operator/templates/_helpers.tpl new file mode 100644 index 00000000..840391ac --- /dev/null +++ b/deploy/helm/secret-operator/templates/_helpers.tpl @@ -0,0 +1,76 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "operator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-operator" }} +{{- end }} + +{{/* +Expand the name of the chart. +*/}} +{{- define "operator.appname" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "operator.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 }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "operator.labels" -}} +helm.sh/chart: {{ include "operator.chart" . }} +{{ include "operator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "operator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "operator.appname" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "operator.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "operator.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Labels for Kubernetes objects created by helm test +*/}} +{{- define "operator.testLabels" -}} +helm.sh/test: {{ include "operator.chart" . }} +{{- end }} \ No newline at end of file diff --git a/deploy/helm/secret-operator/templates/csidriver.yaml b/deploy/helm/secret-operator/templates/csidriver.yaml new file mode 100644 index 00000000..fd9840ca --- /dev/null +++ b/deploy/helm/secret-operator/templates/csidriver.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + name: secrets.stackable.tech +spec: + attachRequired: false + podInfoOnMount: true + volumeLifecycleModes: + - Ephemeral diff --git a/deploy/helm/secret-operator/templates/daemonset.yaml b/deploy/helm/secret-operator/templates/daemonset.yaml new file mode 100644 index 00000000..10154a14 --- /dev/null +++ b/deploy/helm/secret-operator/templates/daemonset.yaml @@ -0,0 +1,75 @@ +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ .Release.Name }}-daemonset + labels: + {{- include "operator.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "operator.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "operator.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ .Release.Name }}-serviceaccount + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "operator.appname" . }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: CSI_ENDPOINT + value: /csi/csi.sock + volumeMounts: + - name: csi + mountPath: /csi + - name: mountpoint + mountPath: /var/lib/kubelet/pods + - name: node-driver-registrar + image: k8s.gcr.io/sig-storage/csi-node-driver-registrar:v2.4.0 + args: + - --csi-address=/csi/csi.sock + - --kubelet-registration-path=/var/lib/kubelet/plugins/secrets.stackable.tech/csi.sock + volumeMounts: + - name: registration-sock + mountPath: /registration + - name: csi + mountPath: /csi + volumes: + - name: registration-sock + hostPath: + path: /var/lib/kubelet/plugins_registry/secrets.stackable.tech-reg.sock + - name: csi + hostPath: + path: /var/lib/kubelet/plugins/secrets.stackable.tech/ + - name: mountpoint + hostPath: + path: /var/lib/kubelet/pods/ + {{- 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/deploy/helm/secret-operator/templates/roles.yaml b/deploy/helm/secret-operator/templates/roles.yaml new file mode 100644 index 00000000..82fde6b8 --- /dev/null +++ b/deploy/helm/secret-operator/templates/roles.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Release.Name }}-clusterrole +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - list + - create + - get + - apiGroups: + - "" + resources: + - pods + - nodes + verbs: + - get + - apiGroups: + - secrets.stackable.tech + resources: + - secretclasses + verbs: + - get diff --git a/deploy/helm/secret-operator/templates/secretclasses.yaml b/deploy/helm/secret-operator/templates/secretclasses.yaml new file mode 100644 index 00000000..d0a0e306 --- /dev/null +++ b/deploy/helm/secret-operator/templates/secretclasses.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: secrets.stackable.tech/v1alpha1 +kind: SecretClass +metadata: + name: tls +spec: + backend: + autoTls: + ca: + secret: + name: secret-provisioner-tls-ca + namespace: default diff --git a/deploy/helm/secret-operator/templates/serviceaccount.yaml b/deploy/helm/secret-operator/templates/serviceaccount.yaml new file mode 100644 index 00000000..ff2b7f8e --- /dev/null +++ b/deploy/helm/secret-operator/templates/serviceaccount.yaml @@ -0,0 +1,29 @@ +--- +{{ if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-serviceaccount + labels: + {{- include "operator.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace. +kind: ClusterRoleBinding +metadata: + name: {{ .Release.Name }}-clusterrolebinding + labels: + {{- include "operator.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-serviceaccount + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Release.Name }}-clusterrole + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/deploy/helm/secret-operator/values.yaml b/deploy/helm/secret-operator/values.yaml new file mode 100644 index 00000000..c622d58a --- /dev/null +++ b/deploy/helm/secret-operator/values.yaml @@ -0,0 +1,51 @@ +# Default values for secret-operator. +--- +image: + repository: docker.stackable.tech/stackable/secret-operator + pullPolicy: IfNotPresent + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: + # secret-operator requires root permissions + runAsUser: 0 + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/deploy/manifests/Kustomization b/deploy/manifests/Kustomization new file mode 100644 index 00000000..0e1ce898 --- /dev/null +++ b/deploy/manifests/Kustomization @@ -0,0 +1,8 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - configmap.yaml + - deployment.yaml + - roles.yaml + - serviceaccount.yaml diff --git a/deploy/manifests/crds.yaml b/deploy/manifests/crds.yaml new file mode 100644 index 00000000..a34619e8 --- /dev/null +++ b/deploy/manifests/crds.yaml @@ -0,0 +1,86 @@ +--- +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: secretclasses.secrets.stackable.tech + annotations: + helm.sh/resource-policy: keep +spec: + group: secrets.stackable.tech + names: + categories: [] + kind: SecretClass + plural: secretclasses + shortNames: [] + singular: secretclass + scope: Cluster + versions: + - additionalPrinterColumns: [] + name: v1alpha1 + schema: + openAPIV3Schema: + description: "Auto-generated derived type for SecretClassSpec via `CustomResource`" + properties: + spec: + properties: + backend: + oneOf: + - required: + - k8sSearch + - required: + - autoTls + properties: + autoTls: + properties: + ca: + properties: + secret: + description: SecretReference represents a Secret Reference. It has enough information to retrieve secret in any namespace + properties: + name: + description: Name is unique within a namespace to reference a secret resource. + type: string + namespace: + description: Namespace defines the space within which the secret name must be unique. + type: string + type: object + required: + - secret + type: object + required: + - ca + type: object + k8sSearch: + properties: + searchNamespace: + oneOf: + - required: + - pod + - required: + - name + properties: + name: + type: string + pod: + type: object + type: object + secretLabels: + additionalProperties: + type: string + default: {} + type: object + required: + - searchNamespace + type: object + type: object + required: + - backend + type: object + required: + - spec + title: SecretClass + type: object + served: true + storage: true + subresources: {} diff --git a/deploy/manifests/csidriver.yaml b/deploy/manifests/csidriver.yaml new file mode 100644 index 00000000..70ac9980 --- /dev/null +++ b/deploy/manifests/csidriver.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + name: secrets.stackable.tech +spec: + attachRequired: false + podInfoOnMount: true + volumeLifecycleModes: + - Ephemeral diff --git a/deploy/manifests/daemonset.yaml b/deploy/manifests/daemonset.yaml new file mode 100644 index 00000000..b6570c56 --- /dev/null +++ b/deploy/manifests/daemonset.yaml @@ -0,0 +1,57 @@ +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: secret-operator-daemonset + labels: + app.kubernetes.io/name: secret-operator + app.kubernetes.io/instance: secret-operator + app.kubernetes.io/version: "0.1.0" +spec: + selector: + matchLabels: + app.kubernetes.io/name: secret-operator + app.kubernetes.io/instance: secret-operator + template: + metadata: + labels: + app.kubernetes.io/name: secret-operator + app.kubernetes.io/instance: secret-operator + spec: + serviceAccountName: secret-operator-serviceaccount + securityContext: {} + containers: + - name: secret-operator + securityContext: + runAsUser: 0 + image: "docker.stackable.tech/stackable/secret-operator:0.1.0" + imagePullPolicy: IfNotPresent + resources: {} + env: + - name: CSI_ENDPOINT + value: /csi/csi.sock + volumeMounts: + - name: csi + mountPath: /csi + - name: mountpoint + mountPath: /var/lib/kubelet/pods + - name: node-driver-registrar + image: k8s.gcr.io/sig-storage/csi-node-driver-registrar:v2.4.0 + args: + - --csi-address=/csi/csi.sock + - --kubelet-registration-path=/var/lib/kubelet/plugins/secrets.stackable.tech/csi.sock + volumeMounts: + - name: registration-sock + mountPath: /registration + - name: csi + mountPath: /csi + volumes: + - name: registration-sock + hostPath: + path: /var/lib/kubelet/plugins_registry/secrets.stackable.tech-reg.sock + - name: csi + hostPath: + path: /var/lib/kubelet/plugins/secrets.stackable.tech/ + - name: mountpoint + hostPath: + path: /var/lib/kubelet/pods/ diff --git a/deploy/manifests/roles.yaml b/deploy/manifests/roles.yaml new file mode 100644 index 00000000..f57e4294 --- /dev/null +++ b/deploy/manifests/roles.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: secret-operator-clusterrole +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - list + - create + - get + - apiGroups: + - "" + resources: + - pods + - nodes + verbs: + - get + - apiGroups: + - secrets.stackable.tech + resources: + - secretclasses + verbs: + - get diff --git a/deploy/manifests/secretclasses.yaml b/deploy/manifests/secretclasses.yaml new file mode 100644 index 00000000..d0a0e306 --- /dev/null +++ b/deploy/manifests/secretclasses.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: secrets.stackable.tech/v1alpha1 +kind: SecretClass +metadata: + name: tls +spec: + backend: + autoTls: + ca: + secret: + name: secret-provisioner-tls-ca + namespace: default diff --git a/deploy/manifests/serviceaccount.yaml b/deploy/manifests/serviceaccount.yaml new file mode 100644 index 00000000..d61b1132 --- /dev/null +++ b/deploy/manifests/serviceaccount.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: secret-operator-serviceaccount + labels: + app.kubernetes.io/name: secret-operator + app.kubernetes.io/instance: secret-operator + app.kubernetes.io/version: "0.1.0" +--- +apiVersion: rbac.authorization.k8s.io/v1 +# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace. +kind: ClusterRoleBinding +metadata: + name: secret-operator-clusterrolebinding + labels: + app.kubernetes.io/name: secret-operator + app.kubernetes.io/instance: secret-operator + app.kubernetes.io/version: "0.1.0" +subjects: + - kind: ServiceAccount + name: secret-operator-serviceaccount + namespace: default +roleRef: + kind: ClusterRole + name: secret-operator-clusterrole + apiGroup: rbac.authorization.k8s.io diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000..053b3341 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,23 @@ +# ============= +# This file is automatically generated from the templates in stackabletech/operator-templating +# DON'T MANUALLY EDIT THIS FILE +# ============= +FROM docker.stackable.tech/stackable/ubi8-rust-builder AS builder + +FROM registry.access.redhat.com/ubi8/ubi-minimal AS operator +LABEL maintainer="Stackable GmbH" + +# Update image +RUN microdnf update --disablerepo=* --enablerepo=ubi-8-baseos --enablerepo=ubi-8-baseos -y \ + && rm -rf /var/cache/yum \ + && microdnf install --disablerepo=* --enablerepo=ubi-8-baseos shadow-utils -y \ + && rm -rf /var/cache/yum + +COPY --from=builder /app/stackable-secret-operator / + +RUN groupadd -g 1000 stackable && adduser -u 1000 -g stackable -c 'Stackable Operator' stackable + +USER 1000:1000 + +ENTRYPOINT ["/stackable-secret-operator"] +CMD ["run"] diff --git a/python/cargo_version.py b/python/cargo_version.py new file mode 100755 index 00000000..c783ce58 --- /dev/null +++ b/python/cargo_version.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +# +# Utility for viewing and managing versions of cargo workspaces and crates. +# For workspaces, it assumes that all crate members use a single shared version. +# +# usage: cargo_version.py [-h] [-p PROJECT] [-r] [-n {major,minor,patch}] [-s SET] [-o] +# +# Change versions of cargo projects. +# +# optional arguments: +# -h, --help show this help message and exit +# -p PROJECT, --project PROJECT +# Project folder +# -r, --release Version +# -n {major,minor,patch}, --next {major,minor,patch} +# Version +# -s SET, --set SET Version +# -o, --show Version + +import argparse +import semver +import toml + + +class Crate: + def __init__(self, path, name, version, dependencies): + self.path = path + self.name = name + self.version = version + self.dependencies = dependencies + + def with_dependencies(self, names): + deps = {k: v for k, v in self.dependencies.items() if k in names} + return Crate(self.path, self.name, self.version, deps) + + @classmethod + def finalize(cls, version): + return str(semver.VersionInfo.parse(version).finalize_version()) + + @classmethod + def bump_level(cls, version, level): + v = semver.VersionInfo.parse(version) + if level == 'major': + return str(v.bump_major()) + if level == 'minor': + return str(v.bump_minor()) + if level == 'patch': + return str(v.bump_patch()) + else: + return str(v.bump_prerelease('nightly'))[:-2] ### remove the .1 suffix that semver always adds to the prererelease. + + @classmethod + def prerelease(cls, version, prerelease): + v = semver.VersionInfo.parse(version) + return str(semver.VersionInfo(v.major, v.minor, v.patch, prerelease)) + + def finalize_version(self): + return Crate(self.path, self.name, Crate.finalize(self.version), self.dependencies.copy()) + + def bump_version(self, level): + return Crate(self.path, self.name, Crate.bump_level(self.version, level), self.dependencies.copy()) + + def set_version(self, version): + return Crate(self.path, self.name, version, self.dependencies.copy()) + + def set_prerelease(self, prerelease): + return Crate(self.path, self.name, Crate.prerelease(self.version, prerelease), self.dependencies.copy()) + + def next_version(self): + return Crate(self.path, self.name, str(semver.VersionInfo.parse(self.version).next_version('patch')), self.dependencies.copy()) + + def show_version(self): + return self.version + + def save(self, previous): + contents = [] + cargo_file = f"{self.path}/Cargo.toml" + with open(cargo_file, 'r') as r: + for line in r.readlines(): + if line.startswith("version"): + line = line.replace(previous.version, self.version) + else: + for dname, dversion in self.dependencies.items(): + if line.startswith(dname): + line = line.replace(previous.dependencies[dname], dversion) + contents.append(line) + + with open(cargo_file, 'w') as w: + w.write(''.join(contents)) + + def __str__(self): + return f'Crate({self.path}, {self.name}, {self.version}, {self.dependencies})' + + +class Workspace: + def __init__(self, crates): + names = set([c.name for c in crates]) + self.crates = {c.name: c.with_dependencies(names) for c in crates} + + def finalize_version(self): + crates = {c.name: c.finalize_version() for c in self.crates.values()} + return Workspace(Workspace.update_dependencies(crates).values()) + + def bump_version(self, level): + crates = {c.name: c.bump_version(level) for c in self.crates.values()} + return Workspace(Workspace.update_dependencies(crates).values()) + + def set_version(self, version): + crates = {c.name: c.set_version(version) for c in self.crates.values()} + return Workspace(Workspace.update_dependencies(crates).values()) + + def set_prerelease(self, prerelease): + crates = {c.name: c.set_prerelease(prerelease) for c in self.crates.values()} + return Workspace(Workspace.update_dependencies(crates).values()) + + def next_version(self): + crates = {c.name: c.next_version() for c in self.crates.values()} + return Workspace(Workspace.update_dependencies(crates).values()) + + def show_version(self): + for c in self.crates.values(): + return c.show_version() + return "0.0.0" + + @classmethod + def update_dependencies(cls, crate_dict): + for crate in crate_dict.values(): + for dep in crate.dependencies.keys(): + crate.dependencies[dep] = crate_dict[dep].version + return crate_dict + + def __str__(self): + return f'Workspace({[str(c) for c in self.crates.values()]})' + + def save(self, previous): + for cn in self.crates.keys(): + self.crates[cn].save(previous.crates[cn]) + + +def load(root): + r = toml.load(f"{root}/Cargo.toml") + if "workspace" in r: + return Workspace([load(f"{root}/{path}") for path in r["workspace"]["members"]]) + + return Crate(path=root, name=r["package"]["name"], version=r["package"]["version"], dependencies={dn: r["dependencies"][dn]["version"] for dn in r["dependencies"] if "version" in r["dependencies"][dn]}) + + +def parse_args(): + parser = argparse.ArgumentParser(description="Change versions of cargo projects.") + parser.add_argument("-p", "--project", help="Project folder", default=".") + parser.add_argument("-r", "--release", help="Version", action="store_true") + parser.add_argument("-n", "--next", help="Version", choices=['major', 'minor', 'patch']) + parser.add_argument("-s", "--set", help="Version") + parser.add_argument("-o", "--show", help="Version", action="store_true") + parser.add_argument("-m", "--prerelease", help="Set pre-prelease string.") + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_args() + + old = load(args.project.rstrip('/')) + + if args.release: + new = old.finalize_version() + new.save(old) + elif args.next: + new = old.bump_version(args.next).bump_version("prerelease") + new.save(old) + elif args.set: + # sanity check + semver.VersionInfo.parse(args.set) + new = old.set_version(args.set) + new.save(old) + elif args.prerelease: + new = old.set_prerelease(args.prerelease) + new.save(old) + elif args.show: + print(old.show_version()) diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 00000000..143c5421 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1,2 @@ +semver==2.13.0 +toml==0.10.2 diff --git a/scripts/generate-manifests.sh b/scripts/generate-manifests.sh new file mode 100755 index 00000000..9c1ad0f3 --- /dev/null +++ b/scripts/generate-manifests.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# This script reads a Helm chart from deploy/helm/secret-operator-operator and +# generates manifest files into deploy/manifestss +set -e + +tmp=$(mktemp -d ./manifests-XXXXX) + +helm template --output-dir $tmp \ + --include-crds \ + --name-template secret-operator \ + deploy/helm/secret-operator + +for file in $(find $tmp -type f) +do + yq eval -i 'del(.. | select(has("app.kubernetes.io/managed-by")) | ."app.kubernetes.io/managed-by")' $file + yq eval -i 'del(.. | select(has("helm.sh/chart")) | ."helm.sh/chart")' $file + sed -i '/# Source: .*/d' $file +done + +cp -r $tmp/secret-operator/*/* deploy/manifests/ + +rm -rf $tmp