From 41568461d40cabee5948f52126c5f025c05f65b8 Mon Sep 17 00:00:00 2001 From: Angel Barrera Sanchez Date: Sat, 11 Apr 2020 19:02:22 +0200 Subject: [PATCH] Hello World --- .github/workflows/ci.yml | 40 + .gitignore | 15 + Makefile | 74 + README.md | 97 + deploy/cert-manager/cert-manager.yaml | 7221 +++++++++++++++++ deploy/crds/organization.yaml | 56 + deploy/crds/space.yaml | 77 + deploy/crds/tenant.yaml | 56 + deploy/operator.yaml | 46 + deploy/roles/roles.yaml | 284 + deploy/webhook.yaml | 209 + dockerfiles/Dockerfile-operator | 12 + dockerfiles/Dockerfile-webhook | 16 + docs/README.md | 5 + docs/use-cases.md | 57 + example-cr/org-1.yaml | 14 + example-cr/roles/org.yaml | 12 + example-cr/roles/spaces.yaml | 40 + example-cr/roles/tenants.yaml | 30 + example-cr/space-1.yaml | 19 + example-cr/space-2.yaml | 29 + example-cr/space-bad.yaml | 5 + example-cr/tenant-1-bad.yaml | 11 + example-cr/tenant-1.yaml | 14 + k8spin_common/k8spin_common/__init__.py | 97 + k8spin_common/k8spin_common/helper.py | 46 + .../k8spin_common/resources/namespace.py | 19 + .../k8spin_common/resources/organization.py | 89 + .../k8spin_common/resources/quotas.py | 51 + k8spin_common/k8spin_common/resources/rbac.py | 51 + .../k8spin_common/resources/space.py | 145 + .../k8spin_common/resources/tenant.py | 100 + k8spin_common/requirements-dev.txt | 3 + k8spin_common/requirements.txt | 1 + k8spin_common/setup.py | 12 + k8spin_operator/__init__.py | 0 k8spin_operator/k8spin_operator/__init__.py | 0 .../k8spin_operator/handlers/__init__.py | 4 + .../k8spin_operator/handlers/organization.py | 9 + .../k8spin_operator/handlers/reconcile.py | 29 + .../k8spin_operator/handlers/space.py | 24 + .../k8spin_operator/handlers/tenant.py | 23 + k8spin_operator/operator.py | 7 + k8spin_operator/requirements-dev.txt | 3 + k8spin_operator/requirements.txt | 1 + k8spin_webhook/__init__.py | 0 k8spin_webhook/app.py | 22 + k8spin_webhook/mutator/__init__.py | 40 + k8spin_webhook/mutator/mutator_utils.py | 1 + k8spin_webhook/requirements-dev.txt | 3 + k8spin_webhook/requirements.txt | 5 + k8spin_webhook/utils.py | 0 k8spin_webhook/validator/__init__.py | 22 + k8spin_webhook/validator/validator_utils.py | 105 + tests/e2e/__init__.py | 0 tests/e2e/conftest.py | 73 + tests/e2e/test_basic.py | 179 + tests/e2e/utils.py | 55 + tests/requirements.txt | 6 + 59 files changed, 9664 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 deploy/cert-manager/cert-manager.yaml create mode 100644 deploy/crds/organization.yaml create mode 100644 deploy/crds/space.yaml create mode 100644 deploy/crds/tenant.yaml create mode 100644 deploy/operator.yaml create mode 100644 deploy/roles/roles.yaml create mode 100644 deploy/webhook.yaml create mode 100644 dockerfiles/Dockerfile-operator create mode 100644 dockerfiles/Dockerfile-webhook create mode 100644 docs/README.md create mode 100644 docs/use-cases.md create mode 100644 example-cr/org-1.yaml create mode 100644 example-cr/roles/org.yaml create mode 100644 example-cr/roles/spaces.yaml create mode 100644 example-cr/roles/tenants.yaml create mode 100644 example-cr/space-1.yaml create mode 100644 example-cr/space-2.yaml create mode 100644 example-cr/space-bad.yaml create mode 100644 example-cr/tenant-1-bad.yaml create mode 100644 example-cr/tenant-1.yaml create mode 100644 k8spin_common/k8spin_common/__init__.py create mode 100644 k8spin_common/k8spin_common/helper.py create mode 100644 k8spin_common/k8spin_common/resources/namespace.py create mode 100644 k8spin_common/k8spin_common/resources/organization.py create mode 100644 k8spin_common/k8spin_common/resources/quotas.py create mode 100644 k8spin_common/k8spin_common/resources/rbac.py create mode 100644 k8spin_common/k8spin_common/resources/space.py create mode 100644 k8spin_common/k8spin_common/resources/tenant.py create mode 100644 k8spin_common/requirements-dev.txt create mode 100644 k8spin_common/requirements.txt create mode 100644 k8spin_common/setup.py create mode 100644 k8spin_operator/__init__.py create mode 100644 k8spin_operator/k8spin_operator/__init__.py create mode 100644 k8spin_operator/k8spin_operator/handlers/__init__.py create mode 100644 k8spin_operator/k8spin_operator/handlers/organization.py create mode 100644 k8spin_operator/k8spin_operator/handlers/reconcile.py create mode 100644 k8spin_operator/k8spin_operator/handlers/space.py create mode 100644 k8spin_operator/k8spin_operator/handlers/tenant.py create mode 100644 k8spin_operator/operator.py create mode 100644 k8spin_operator/requirements-dev.txt create mode 100644 k8spin_operator/requirements.txt create mode 100644 k8spin_webhook/__init__.py create mode 100644 k8spin_webhook/app.py create mode 100644 k8spin_webhook/mutator/__init__.py create mode 100644 k8spin_webhook/mutator/mutator_utils.py create mode 100644 k8spin_webhook/requirements-dev.txt create mode 100644 k8spin_webhook/requirements.txt create mode 100644 k8spin_webhook/utils.py create mode 100644 k8spin_webhook/validator/__init__.py create mode 100644 k8spin_webhook/validator/validator_utils.py create mode 100644 tests/e2e/__init__.py create mode 100644 tests/e2e/conftest.py create mode 100644 tests/e2e/test_basic.py create mode 100644 tests/e2e/utils.py create mode 100644 tests/requirements.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c59734e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: "CI" + +on: + push: + branches: + - master + tags: + - v.* + +jobs: + e2e_and_publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: engineerd/setup-kind@v0.3.0 + with: + version: "v0.7.0" + skipClusterCreation: "true" + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: "3.8" + - name: Install dependencies + run: | + python -m pip install --upgrade virtualenv + - name: Execute E2E tests + run: | + make test-e2e + - name: Upload E2E logs + continue-on-error: true + uses: actions/upload-artifact@v1 + with: + name: e2elogs + path: e2elogs + - name: Publish image + run: | + docker login -u "${{ secrets.DOCKER_USERNAME }}" -p "${{ secrets.DOCKER_PASSWORD }}" + make TAG_VERSION=${GITHUB_REF##*/} publish_dockerhub + make TAG_VERSION=dev publish_dockerhub + make TAG_VERSION=latest publish_dockerhub diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d06185 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.venv-operator +.venv-validator +.venv-test +.venv +.kube +.pytest_cache +.pytest-kind +.vscode + +e2elogs + +# Byte-compiled / optimized / DLL files +**/__pycache__/ +*.py[cod] +**/*.egg-info diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4c246d6 --- /dev/null +++ b/Makefile @@ -0,0 +1,74 @@ +PROJECTNAME=$(shell basename "$(PWD)") +CLUSTER_VERSION="1.18.2" +KIND_CLUSTER_NAME="k8spin-operator" +PYTEST_PARAMS="" +TAG_VERSION="dev" + +.PHONY: help +all: help +help: Makefile + @echo + @echo " Choose a command run in "$(PROJECTNAME)":" + @echo + @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' + @echo + +## cluster-up: Creates the kind cluster +cluster-up: + @kind create cluster --name $(KIND_CLUSTER_NAME) --image kindest/node:v${CLUSTER_VERSION} && echo "Cluster created" || echo "Cluster already exists" + +## cluster-down: Teardown the kind cluster +cluster-down: + @kind delete cluster --name $(KIND_CLUSTER_NAME) -q && echo "Cluster deleted" || echo "Cluster does not exist exists" + +## build: Local build the operator +build: + @docker build -t k8spin/k8spin-operator:dev . -f dockerfiles/Dockerfile-operator + @docker build -t k8spin/k8spin-webhook:dev . -f dockerfiles/Dockerfile-webhook + +## deploy: Deploys the complete solution +deploy: load + @kubectl --context kind-$(KIND_CLUSTER_NAME) apply -f ./deploy/cert-manager + @kubectl --context kind-$(KIND_CLUSTER_NAME) wait --for=condition=Available deployment --timeout=2m -n cert-manager --all + @kubectl --context kind-$(KIND_CLUSTER_NAME) apply -f ./deploy/crds/ -n default + @kubectl --context kind-$(KIND_CLUSTER_NAME) apply -f ./deploy/ -n default + +## update: Update the complete solution +update: load + @kubectl --context kind-$(KIND_CLUSTER_NAME) delete -f ./deploy/ --wait=true -n default + @kubectl --context kind-$(KIND_CLUSTER_NAME) apply -f ./deploy/ -n default + +## test-e2e: End-to-End tests. Use `PYTEST_ADDOPTS=--keep-cluster make test-e2e` to keep cluster +## --workers auto could be added when we want multiple workers installing the package pytest-parallel +test-e2e: build + @virtualenv -p python3.8 .venv-test + source .venv-test/bin/activate; \ + pip install -r tests/requirements.txt; \ + pip install -e k8spin_common; \ + pytest -v -r=a \ + --log-cli-level info \ + --log-cli-format '%(asctime)s %(levelname)s %(message)s' \ + --cluster-name k8spin-operator \ + ${PYTEST_PARAMS} \ + tests/e2e; + +test-kubeconfig: + @export KUBECONFIG=.pytest-kind/k8spin-operator/kind-config-k8spin-operator + +load: cluster-up build + @kind load docker-image --name $(KIND_CLUSTER_NAME) k8spin/k8spin-operator:dev + @kind load docker-image --name $(KIND_CLUSTER_NAME) k8spin/k8spin-webhook:dev + +## kubie: Sets the kind cluster context +kubie: + @kubie ctx kind-$(KIND_CLUSTER_NAME) + +publish_dockerhub: + @docker tag k8spin/k8spin-operator:dev k8spin/k8spin-operator:$(TAG_VERSION) + @docker tag k8spin/k8spin-webhook:dev k8spin/k8spin-webhook:$(TAG_VERSION) + @docker push k8spin/k8spin-operator:$(TAG_VERSION) + @docker push k8spin/k8spin-webhook:$(TAG_VERSION) + +clean: + @rm -rf .kube .pytest_cache .pytest-kind .venv-test e2elogs + @find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete diff --git a/README.md b/README.md new file mode 100644 index 0000000..29bf478 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# K8Spin Operator + +Kubernetes multi-tenant operator. Enables multi-tenant capabilities in your Kubernetes Cluster. + +## Features + +The main features included in the Operator: + +- **Enable Multi-Tenant:** Adds three new hierarchy concepts *(Organizations, Tenants and Spaces)*. +- **Secure and scalable cluster management delegation:** Cluster Admins creates Organizations +then delegating its access to users and groups. +- **Cluster budget management:** Assignning resources in the organization definition makes possible to +understand how many resources are allocated to a user, team or the whole company. + +## Concepts + +K8Spin manages the multi-tenant feature with three simple concepts: + +- **Organization**: Created by a cluster administrator, hosts **tenants**. Cluster administrator +can set compute quotas for the whole Organization and grant permissions to users and/or groups. +- **Tenant**: A tenant can be created by an Organization administrator hosting **spaces**. The Tenant administrator +can fix compute quotas and assign roles to users and/or groups. Tenants resources should fit into +Organization resources. +- **Space**: Tenant administrator can create Spaces. An space is an abstraction layer on top of +a Namespace. A tenant administrator should assign quotas and roles to the Space. Space resources should fit +into Tenant resources. + +## TL;DR + +Clone this repo, cd into it and: + +```bash +# Create a local cluster +$ kind create cluster +# Deploy cert-manager +$ kubectl apply -f deploy/cert-manager/cert-manager.yaml +$ kubectl wait --for=condition=Available deployment --timeout=2m -n cert-manager --all +# Deploy K8Spin operator +$ kubectl apply -f ./deploy/crds/ -n default +$ kubectl apply -f ./deploy/roles/ -n default +$ kubectl apply -f ./deploy/ -n default +$ kubectl wait --for=condition=Available deployment --timeout=2m -n default --all +``` + +Now you are ready to use the operator + +```bash +$ kubectl apply -f example-cr/org-1.yaml +organization.k8spin.cloud/example created +$ kubectl apply -f example-cr/tenant-1.yaml +tenant.k8spin.cloud/crm created +$ kubectl apply -f example-cr/space-1.yaml +space.k8spin.cloud/dev created +``` + +As cluster admin check organizations: + +```bash +$ kubectl get org +NAME AGE +example 86s +``` + +As `example` organization admin get available tenants: + +```bash +kubectl get tenants -n org-example --as Angel --as-group "K8Spin.cloud" +NAME AGE +crm 7m31s +``` + +As `crm` tenant admin get spaces: + +```bash +$ kubectl get spaces -n org-example-tenant-crm --as Angel --as-group "K8Spin.cloud" +NAME AGE +dev 9m24s +``` + +Run a workload in the dev space: + +```bash +$ kubectl run nginx --image nginx --replicas=2 -n org-example-tenant-crm-space-dev --as Angel --as-group "K8Spin.cloud" +pod/nginx created +``` + +Dicover workloads in the dev space as space viewer: + +```bash +$ kubectl get pods -n org-example-tenant-crm-space-dev --as Pau +NAME READY STATUS RESTARTS AGE +nginx 1/1 Running 0 66s +``` + +## Documentation + +Discover all the power of this operator [reading all the documentation](docs) diff --git a/deploy/cert-manager/cert-manager.yaml b/deploy/cert-manager/cert-manager.yaml new file mode 100644 index 0000000..3bd7767 --- /dev/null +++ b/deploy/cert-manager/cert-manager.yaml @@ -0,0 +1,7221 @@ +# Copyright YEAR The Jetstack cert-manager contributors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +# Source: cert-manager/templates/templates.regular.out +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: certificaterequests.cert-manager.io + annotations: + cert-manager.io/inject-ca-from-secret: 'cert-manager/cert-manager-webhook-ca' + labels: + app: 'cert-manager' + app.kubernetes.io/name: 'cert-manager' + app.kubernetes.io/instance: 'cert-manager' + app.kubernetes.io/managed-by: 'Helm' + helm.sh/chart: 'cert-manager-v0.15.0' +spec: + additionalPrinterColumns: + - JSONPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - JSONPath: .spec.issuerRef.name + name: Issuer + priority: 1 + type: string + - JSONPath: .status.conditions[?(@.type=="Ready")].message + name: Status + priority: 1 + type: string + - JSONPath: .metadata.creationTimestamp + description: CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before order + across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. + name: Age + type: date + group: cert-manager.io + preserveUnknownFields: false + conversion: + # a Webhook strategy instruct API server to call an external webhook for any conversion between custom resources. + strategy: Webhook + # webhookClientConfig is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server. + webhookClientConfig: + service: + namespace: 'cert-manager' + name: 'cert-manager-webhook' + path: /convert + names: + kind: CertificateRequest + listKind: CertificateRequestList + plural: certificaterequests + shortNames: + - cr + - crs + singular: certificaterequest + scope: Namespaced + subresources: + status: {} + versions: + - name: v1alpha2 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + "validation": + "openAPIV3Schema": + description: CertificateRequest is a type to represent a Certificate Signing + Request + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: CertificateRequestSpec defines the desired state of CertificateRequest + type: object + required: + - csr + - issuerRef + properties: + csr: + description: Byte slice containing the PEM encoded CertificateSigningRequest + type: string + format: byte + duration: + description: Requested certificate default Duration + type: string + isCA: + description: IsCA will mark the resulting certificate as valid for signing. + This implies that the 'cert sign' usage is set + type: boolean + issuerRef: + description: IssuerRef is a reference to the issuer for this CertificateRequest. If + the 'kind' field is not set, or set to 'Issuer', an Issuer resource + with the given name in the same namespace as the CertificateRequest + will be used. If the 'kind' field is set to 'ClusterIssuer', a ClusterIssuer + with the provided name will be used. The 'name' field in this stanza + is required at all times. The group field refers to the API group + of the issuer which defaults to 'cert-manager.io' if empty. + type: object + required: + - name + properties: + group: + type: string + kind: + type: string + name: + type: string + usages: + description: Usages is the set of x509 actions that are enabled for + a given key. Defaults are ('digital signature', 'key encipherment') + if empty + type: array + items: + description: 'KeyUsage specifies valid usage contexts for keys. See: + https://tools.ietf.org/html/rfc5280#section-4.2.1.3 https://tools.ietf.org/html/rfc5280#section-4.2.1.12 + Valid KeyUsage values are as follows: "signing", "digital signature", + "content commitment", "key encipherment", "key agreement", "data + encipherment", "cert sign", "crl sign", "encipher only", "decipher + only", "any", "server auth", "client auth", "code signing", "email + protection", "s/mime", "ipsec end system", "ipsec tunnel", "ipsec + user", "timestamping", "ocsp signing", "microsoft sgc", "netscape + sgc"' + type: string + enum: + - signing + - digital signature + - content commitment + - key encipherment + - key agreement + - data encipherment + - cert sign + - crl sign + - encipher only + - decipher only + - any + - server auth + - client auth + - code signing + - email protection + - s/mime + - ipsec end system + - ipsec tunnel + - ipsec user + - timestamping + - ocsp signing + - microsoft sgc + - netscape sgc + status: + description: CertificateStatus defines the observed state of CertificateRequest + and resulting signed certificate. + type: object + properties: + ca: + description: Byte slice containing the PEM encoded certificate authority + of the signed certificate. + type: string + format: byte + certificate: + description: Byte slice containing a PEM encoded signed certificate + resulting from the given certificate signing request. + type: string + format: byte + conditions: + type: array + items: + description: CertificateRequestCondition contains condition information + for a CertificateRequest. + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: LastTransitionTime is the timestamp corresponding + to the last status change of this condition. + type: string + format: date-time + message: + description: Message is a human readable description of the details + of the last transition, complementing reason. + type: string + reason: + description: Reason is a brief machine readable explanation for + the condition's last transition. + type: string + status: + description: Status of the condition, one of ('True', 'False', + 'Unknown'). + type: string + enum: + - "True" + - "False" + - Unknown + type: + description: Type of the condition, currently ('Ready', 'InvalidRequest'). + type: string + failureTime: + description: FailureTime stores the time that this CertificateRequest + failed. This is used to influence garbage collection and back-off. + type: string + format: date-time +--- +# Source: cert-manager/templates/templates.regular.out +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: certificates.cert-manager.io + annotations: + cert-manager.io/inject-ca-from-secret: 'cert-manager/cert-manager-webhook-ca' + labels: + app: 'cert-manager' + app.kubernetes.io/name: 'cert-manager' + app.kubernetes.io/instance: 'cert-manager' + app.kubernetes.io/managed-by: 'Helm' + helm.sh/chart: 'cert-manager-v0.15.0' +spec: + additionalPrinterColumns: + - JSONPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - JSONPath: .spec.secretName + name: Secret + type: string + - JSONPath: .spec.issuerRef.name + name: Issuer + priority: 1 + type: string + - JSONPath: .status.conditions[?(@.type=="Ready")].message + name: Status + priority: 1 + type: string + - JSONPath: .metadata.creationTimestamp + description: CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before order + across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. + name: Age + type: date + group: cert-manager.io + preserveUnknownFields: false + conversion: + # a Webhook strategy instruct API server to call an external webhook for any conversion between custom resources. + strategy: Webhook + # webhookClientConfig is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server. + webhookClientConfig: + service: + namespace: 'cert-manager' + name: 'cert-manager-webhook' + path: /convert + names: + kind: Certificate + listKind: CertificateList + plural: certificates + shortNames: + - cert + - certs + singular: certificate + scope: Namespaced + subresources: + status: {} + versions: + - name: v1alpha2 + served: true + storage: true + "schema": + "openAPIV3Schema": + description: Certificate is a type to represent a Certificate from ACME + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: CertificateSpec defines the desired state of Certificate. + A valid Certificate requires at least one of a CommonName, DNSName, + or URISAN to be valid. + type: object + required: + - issuerRef + - secretName + properties: + commonName: + description: 'CommonName is a common name to be used on the Certificate. + The CommonName should have a length of 64 characters or fewer to + avoid generating invalid CSRs. This value is ignored by TLS clients + when any subject alt name is set. This is x509 behaviour: https://tools.ietf.org/html/rfc6125#section-6.4.4' + type: string + dnsNames: + description: DNSNames is a list of subject alt names to be used on + the Certificate. + type: array + items: + type: string + duration: + description: Certificate default Duration + type: string + emailSANs: + description: EmailSANs is a list of Email Subject Alternative Names + to be set on this Certificate. + type: array + items: + type: string + ipAddresses: + description: IPAddresses is a list of IP addresses to be used on the + Certificate + type: array + items: + type: string + isCA: + description: IsCA will mark this Certificate as valid for signing. + This implies that the 'cert sign' usage is set + type: boolean + issuerRef: + description: IssuerRef is a reference to the issuer for this certificate. + If the 'kind' field is not set, or set to 'Issuer', an Issuer resource + with the given name in the same namespace as the Certificate will + be used. If the 'kind' field is set to 'ClusterIssuer', a ClusterIssuer + with the provided name will be used. The 'name' field in this stanza + is required at all times. + type: object + required: + - name + properties: + group: + type: string + kind: + type: string + name: + type: string + keyAlgorithm: + description: KeyAlgorithm is the private key algorithm of the corresponding + private key for this certificate. If provided, allowed values are + either "rsa" or "ecdsa" If KeyAlgorithm is specified and KeySize + is not provided, key size of 256 will be used for "ecdsa" key algorithm + and key size of 2048 will be used for "rsa" key algorithm. + type: string + enum: + - rsa + - ecdsa + keyEncoding: + description: KeyEncoding is the private key cryptography standards + (PKCS) for this certificate's private key to be encoded in. If provided, + allowed values are "pkcs1" and "pkcs8" standing for PKCS#1 and PKCS#8, + respectively. If KeyEncoding is not specified, then PKCS#1 will + be used by default. + type: string + enum: + - pkcs1 + - pkcs8 + keySize: + description: KeySize is the key bit size of the corresponding private + key for this certificate. If provided, value must be between 2048 + and 8192 inclusive when KeyAlgorithm is empty or is set to "rsa", + and value must be one of (256, 384, 521) when KeyAlgorithm is set + to "ecdsa". + type: integer + maximum: 8192 + minimum: 0 + keystores: + description: Keystores configures additional keystore output formats + stored in the `secretName` Secret resource. + type: object + properties: + jks: + description: JKS configures options for storing a JKS keystore + in the `spec.secretName` Secret resource. + type: object + required: + - create + - passwordSecretRef + properties: + create: + description: Create enables JKS keystore creation for the + Certificate. If true, a file named `keystore.jks` will be + created in the target Secret resource, encrypted using the + password stored in `passwordSecretRef`. The keystore file + will only be updated upon re-issuance. + type: boolean + passwordSecretRef: + description: PasswordSecretRef is a reference to a key in + a Secret resource containing the password used to encrypt + the JKS keystore. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + pkcs12: + description: PKCS12 configures options for storing a PKCS12 keystore + in the `spec.secretName` Secret resource. + type: object + required: + - create + - passwordSecretRef + properties: + create: + description: Create enables PKCS12 keystore creation for the + Certificate. If true, a file named `keystore.p12` will be + created in the target Secret resource, encrypted using the + password stored in `passwordSecretRef`. The keystore file + will only be updated upon re-issuance. + type: boolean + passwordSecretRef: + description: PasswordSecretRef is a reference to a key in + a Secret resource containing the password used to encrypt + the PKCS12 keystore. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + organization: + description: Organization is the organization to be used on the Certificate + type: array + items: + type: string + privateKey: + description: Options to control private keys used for the Certificate. + type: object + properties: + rotationPolicy: + description: RotationPolicy controls how private keys should be + regenerated when a re-issuance is being processed. If set to + Never, a private key will only be generated if one does not + already exist in the target `spec.secretName`. If one does exists + but it does not have the correct algorithm or size, a warning + will be raised to await user intervention. If set to Always, + a private key matching the specified requirements will be generated + whenever a re-issuance occurs. Default is 'Never' for backward + compatibility. + type: string + renewBefore: + description: Certificate renew before expiration duration + type: string + secretName: + description: SecretName is the name of the secret resource to store + this secret in + type: string + subject: + description: Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + type: object + properties: + countries: + description: Countries to be used on the Certificate. + type: array + items: + type: string + localities: + description: Cities to be used on the Certificate. + type: array + items: + type: string + organizationalUnits: + description: Organizational Units to be used on the Certificate. + type: array + items: + type: string + postalCodes: + description: Postal codes to be used on the Certificate. + type: array + items: + type: string + provinces: + description: State/Provinces to be used on the Certificate. + type: array + items: + type: string + serialNumber: + description: Serial number to be used on the Certificate. + type: string + streetAddresses: + description: Street addresses to be used on the Certificate. + type: array + items: + type: string + uriSANs: + description: URISANs is a list of URI Subject Alternative Names to + be set on this Certificate. + type: array + items: + type: string + usages: + description: Usages is the set of x509 actions that are enabled for + a given key. Defaults are ('digital signature', 'key encipherment') + if empty + type: array + items: + description: 'KeyUsage specifies valid usage contexts for keys. + See: https://tools.ietf.org/html/rfc5280#section-4.2.1.3 https://tools.ietf.org/html/rfc5280#section-4.2.1.12 + Valid KeyUsage values are as follows: "signing", "digital signature", + "content commitment", "key encipherment", "key agreement", "data + encipherment", "cert sign", "crl sign", "encipher only", "decipher + only", "any", "server auth", "client auth", "code signing", "email + protection", "s/mime", "ipsec end system", "ipsec tunnel", "ipsec + user", "timestamping", "ocsp signing", "microsoft sgc", "netscape + sgc"' + type: string + enum: + - signing + - digital signature + - content commitment + - key encipherment + - key agreement + - data encipherment + - cert sign + - crl sign + - encipher only + - decipher only + - any + - server auth + - client auth + - code signing + - email protection + - s/mime + - ipsec end system + - ipsec tunnel + - ipsec user + - timestamping + - ocsp signing + - microsoft sgc + - netscape sgc + status: + description: CertificateStatus defines the observed state of Certificate + type: object + properties: + conditions: + type: array + items: + description: CertificateCondition contains condition information + for an Certificate. + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: LastTransitionTime is the timestamp corresponding + to the last status change of this condition. + type: string + format: date-time + message: + description: Message is a human readable description of the + details of the last transition, complementing reason. + type: string + reason: + description: Reason is a brief machine readable explanation + for the condition's last transition. + type: string + status: + description: Status of the condition, one of ('True', 'False', + 'Unknown'). + type: string + enum: + - "True" + - "False" + - Unknown + type: + description: Type of the condition, currently ('Ready'). + type: string + lastFailureTime: + type: string + format: date-time + nextPrivateKeySecretName: + description: The name of the Secret resource containing the private + key to be used for the next certificate iteration. The keymanager + controller will automatically set this field if the `Issuing` condition + is set to `True`. It will automatically unset this field when the + Issuing condition is not set or False. + type: string + notAfter: + description: The expiration time of the certificate stored in the + secret named by this resource in spec.secretName. + type: string + format: date-time + revision: + description: "The current 'revision' of the certificate as issued. + \n When a CertificateRequest resource is created, it will have the + `cert-manager.io/certificate-revision` set to one greater than the + current value of this field. \n Upon issuance, this field will be + set to the value of the annotation on the CertificateRequest resource + used to issue the certificate. \n Persisting the value on the CertificateRequest + resource allows the certificates controller to know whether a request + is part of an old issuance or if it is part of the ongoing revision's + issuance by checking if the revision value in the annotation is + greater than this field." + type: integer + - name: v1alpha3 + served: true + storage: false + "schema": + "openAPIV3Schema": + description: Certificate is a type to represent a Certificate from ACME + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: CertificateSpec defines the desired state of Certificate. + A valid Certificate requires at least one of a CommonName, DNSName, + or URISAN to be valid. + type: object + required: + - issuerRef + - secretName + properties: + commonName: + description: 'CommonName is a common name to be used on the Certificate. + The CommonName should have a length of 64 characters or fewer to + avoid generating invalid CSRs. This value is ignored by TLS clients + when any subject alt name is set. This is x509 behaviour: https://tools.ietf.org/html/rfc6125#section-6.4.4' + type: string + dnsNames: + description: DNSNames is a list of subject alt names to be used on + the Certificate. + type: array + items: + type: string + duration: + description: Certificate default Duration + type: string + emailSANs: + description: EmailSANs is a list of Email Subject Alternative Names + to be set on this Certificate. + type: array + items: + type: string + ipAddresses: + description: IPAddresses is a list of IP addresses to be used on the + Certificate + type: array + items: + type: string + isCA: + description: IsCA will mark this Certificate as valid for signing. + This implies that the 'cert sign' usage is set + type: boolean + issuerRef: + description: IssuerRef is a reference to the issuer for this certificate. + If the 'kind' field is not set, or set to 'Issuer', an Issuer resource + with the given name in the same namespace as the Certificate will + be used. If the 'kind' field is set to 'ClusterIssuer', a ClusterIssuer + with the provided name will be used. The 'name' field in this stanza + is required at all times. + type: object + required: + - name + properties: + group: + type: string + kind: + type: string + name: + type: string + keyAlgorithm: + description: KeyAlgorithm is the private key algorithm of the corresponding + private key for this certificate. If provided, allowed values are + either "rsa" or "ecdsa" If KeyAlgorithm is specified and KeySize + is not provided, key size of 256 will be used for "ecdsa" key algorithm + and key size of 2048 will be used for "rsa" key algorithm. + type: string + enum: + - rsa + - ecdsa + keyEncoding: + description: KeyEncoding is the private key cryptography standards + (PKCS) for this certificate's private key to be encoded in. If provided, + allowed values are "pkcs1" and "pkcs8" standing for PKCS#1 and PKCS#8, + respectively. If KeyEncoding is not specified, then PKCS#1 will + be used by default. + type: string + enum: + - pkcs1 + - pkcs8 + keySize: + description: KeySize is the key bit size of the corresponding private + key for this certificate. If provided, value must be between 2048 + and 8192 inclusive when KeyAlgorithm is empty or is set to "rsa", + and value must be one of (256, 384, 521) when KeyAlgorithm is set + to "ecdsa". + type: integer + maximum: 8192 + minimum: 0 + keystores: + description: Keystores configures additional keystore output formats + stored in the `secretName` Secret resource. + type: object + properties: + jks: + description: JKS configures options for storing a JKS keystore + in the `spec.secretName` Secret resource. + type: object + required: + - create + - passwordSecretRef + properties: + create: + description: Create enables JKS keystore creation for the + Certificate. If true, a file named `keystore.jks` will be + created in the target Secret resource, encrypted using the + password stored in `passwordSecretRef`. The keystore file + will only be updated upon re-issuance. + type: boolean + passwordSecretRef: + description: PasswordSecretRef is a reference to a key in + a Secret resource containing the password used to encrypt + the JKS keystore. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + pkcs12: + description: PKCS12 configures options for storing a PKCS12 keystore + in the `spec.secretName` Secret resource. + type: object + required: + - create + - passwordSecretRef + properties: + create: + description: Create enables PKCS12 keystore creation for the + Certificate. If true, a file named `keystore.p12` will be + created in the target Secret resource, encrypted using the + password stored in `passwordSecretRef`. The keystore file + will only be updated upon re-issuance. + type: boolean + passwordSecretRef: + description: PasswordSecretRef is a reference to a key in + a Secret resource containing the password used to encrypt + the PKCS12 keystore. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + privateKey: + description: Options to control private keys used for the Certificate. + type: object + properties: + rotationPolicy: + description: RotationPolicy controls how private keys should be + regenerated when a re-issuance is being processed. If set to + Never, a private key will only be generated if one does not + already exist in the target `spec.secretName`. If one does exists + but it does not have the correct algorithm or size, a warning + will be raised to await user intervention. If set to Always, + a private key matching the specified requirements will be generated + whenever a re-issuance occurs. Default is 'Never' for backward + compatibility. + type: string + renewBefore: + description: Certificate renew before expiration duration + type: string + secretName: + description: SecretName is the name of the secret resource to store + this secret in + type: string + subject: + description: Full X509 name specification (https://golang.org/pkg/crypto/x509/pkix/#Name). + type: object + properties: + countries: + description: Countries to be used on the Certificate. + type: array + items: + type: string + localities: + description: Cities to be used on the Certificate. + type: array + items: + type: string + organizationalUnits: + description: Organizational Units to be used on the Certificate. + type: array + items: + type: string + organizations: + description: Organizations to be used on the Certificate. + type: array + items: + type: string + postalCodes: + description: Postal codes to be used on the Certificate. + type: array + items: + type: string + provinces: + description: State/Provinces to be used on the Certificate. + type: array + items: + type: string + serialNumber: + description: Serial number to be used on the Certificate. + type: string + streetAddresses: + description: Street addresses to be used on the Certificate. + type: array + items: + type: string + uriSANs: + description: URISANs is a list of URI Subject Alternative Names to + be set on this Certificate. + type: array + items: + type: string + usages: + description: Usages is the set of x509 actions that are enabled for + a given key. Defaults are ('digital signature', 'key encipherment') + if empty + type: array + items: + description: 'KeyUsage specifies valid usage contexts for keys. + See: https://tools.ietf.org/html/rfc5280#section-4.2.1.3 https://tools.ietf.org/html/rfc5280#section-4.2.1.12 + Valid KeyUsage values are as follows: "signing", "digital signature", + "content commitment", "key encipherment", "key agreement", "data + encipherment", "cert sign", "crl sign", "encipher only", "decipher + only", "any", "server auth", "client auth", "code signing", "email + protection", "s/mime", "ipsec end system", "ipsec tunnel", "ipsec + user", "timestamping", "ocsp signing", "microsoft sgc", "netscape + sgc"' + type: string + enum: + - signing + - digital signature + - content commitment + - key encipherment + - key agreement + - data encipherment + - cert sign + - crl sign + - encipher only + - decipher only + - any + - server auth + - client auth + - code signing + - email protection + - s/mime + - ipsec end system + - ipsec tunnel + - ipsec user + - timestamping + - ocsp signing + - microsoft sgc + - netscape sgc + status: + description: CertificateStatus defines the observed state of Certificate + type: object + properties: + conditions: + type: array + items: + description: CertificateCondition contains condition information + for an Certificate. + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: LastTransitionTime is the timestamp corresponding + to the last status change of this condition. + type: string + format: date-time + message: + description: Message is a human readable description of the + details of the last transition, complementing reason. + type: string + reason: + description: Reason is a brief machine readable explanation + for the condition's last transition. + type: string + status: + description: Status of the condition, one of ('True', 'False', + 'Unknown'). + type: string + enum: + - "True" + - "False" + - Unknown + type: + description: Type of the condition, currently ('Ready'). + type: string + lastFailureTime: + type: string + format: date-time + nextPrivateKeySecretName: + description: The name of the Secret resource containing the private + key to be used for the next certificate iteration. The keymanager + controller will automatically set this field if the `Issuing` condition + is set to `True`. It will automatically unset this field when the + Issuing condition is not set or False. + type: string + notAfter: + description: The expiration time of the certificate stored in the + secret named by this resource in spec.secretName. + type: string + format: date-time + revision: + description: "The current 'revision' of the certificate as issued. + \n When a CertificateRequest resource is created, it will have the + `cert-manager.io/certificate-revision` set to one greater than the + current value of this field. \n Upon issuance, this field will be + set to the value of the annotation on the CertificateRequest resource + used to issue the certificate. \n Persisting the value on the CertificateRequest + resource allows the certificates controller to know whether a request + is part of an old issuance or if it is part of the ongoing revision's + issuance by checking if the revision value in the annotation is + greater than this field." + type: integer +--- +# Source: cert-manager/templates/templates.regular.out +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: challenges.acme.cert-manager.io + annotations: + cert-manager.io/inject-ca-from-secret: 'cert-manager/cert-manager-webhook-ca' + labels: + app: 'cert-manager' + app.kubernetes.io/name: 'cert-manager' + app.kubernetes.io/instance: 'cert-manager' + app.kubernetes.io/managed-by: 'Helm' + helm.sh/chart: 'cert-manager-v0.15.0' +spec: + additionalPrinterColumns: + - JSONPath: .status.state + name: State + type: string + - JSONPath: .spec.dnsName + name: Domain + type: string + - JSONPath: .status.reason + name: Reason + priority: 1 + type: string + - JSONPath: .metadata.creationTimestamp + description: CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before order + across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. + name: Age + type: date + group: acme.cert-manager.io + preserveUnknownFields: false + conversion: + # a Webhook strategy instruct API server to call an external webhook for any conversion between custom resources. + strategy: Webhook + # webhookClientConfig is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server. + webhookClientConfig: + service: + namespace: 'cert-manager' + name: 'cert-manager-webhook' + path: /convert + names: + kind: Challenge + listKind: ChallengeList + plural: challenges + singular: challenge + scope: Namespaced + subresources: + status: {} + versions: + - name: v1alpha2 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + "validation": + "openAPIV3Schema": + description: Challenge is a type to represent a Challenge request with an ACME + server + type: object + required: + - metadata + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + type: object + required: + - authzURL + - dnsName + - issuerRef + - key + - solver + - token + - type + - url + properties: + authzURL: + description: AuthzURL is the URL to the ACME Authorization resource + that this challenge is a part of. + type: string + dnsName: + description: DNSName is the identifier that this challenge is for, e.g. + example.com. If the requested DNSName is a 'wildcard', this field + MUST be set to the non-wildcard domain, e.g. for `*.example.com`, + it must be `example.com`. + type: string + issuerRef: + description: IssuerRef references a properly configured ACME-type Issuer + which should be used to create this Challenge. If the Issuer does + not exist, processing will be retried. If the Issuer is not an 'ACME' + Issuer, an error will be returned and the Challenge will be marked + as failed. + type: object + required: + - name + properties: + group: + type: string + kind: + type: string + name: + type: string + key: + description: 'Key is the ACME challenge key for this challenge For HTTP01 + challenges, this is the value that must be responded with to complete + the HTTP01 challenge in the format: `.`. For DNS01 challenges, this is the + base64 encoded SHA256 sum of the `.` text that must be set as the TXT + record content.' + type: string + solver: + description: Solver contains the domain solving configuration that should + be used to solve this challenge resource. + type: object + properties: + dns01: + type: object + properties: + acmedns: + description: ACMEIssuerDNS01ProviderAcmeDNS is a structure containing + the configuration for ACME-DNS servers + type: object + required: + - accountSecretRef + - host + properties: + accountSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + host: + type: string + akamai: + description: ACMEIssuerDNS01ProviderAkamai is a structure containing + the DNS configuration for Akamai DNS—Zone Record Management + API + type: object + required: + - accessTokenSecretRef + - clientSecretSecretRef + - clientTokenSecretRef + - serviceConsumerDomain + properties: + accessTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + clientSecretSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + clientTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + serviceConsumerDomain: + type: string + azuredns: + description: ACMEIssuerDNS01ProviderAzureDNS is a structure + containing the configuration for Azure DNS + type: object + required: + - resourceGroupName + - subscriptionID + properties: + clientID: + description: if both this and ClientSecret are left unset + MSI will be used + type: string + clientSecretSecretRef: + description: if both this and ClientID are left unset MSI + will be used + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + environment: + type: string + enum: + - AzurePublicCloud + - AzureChinaCloud + - AzureGermanCloud + - AzureUSGovernmentCloud + hostedZoneName: + type: string + resourceGroupName: + type: string + subscriptionID: + type: string + tenantID: + description: when specifying ClientID and ClientSecret then + this field is also needed + type: string + clouddns: + description: ACMEIssuerDNS01ProviderCloudDNS is a structure + containing the DNS configuration for Google Cloud DNS + type: object + required: + - project + properties: + project: + type: string + serviceAccountSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + cloudflare: + description: ACMEIssuerDNS01ProviderCloudflare is a structure + containing the DNS configuration for Cloudflare + type: object + required: + - email + properties: + apiKeySecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + apiTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + email: + type: string + cnameStrategy: + description: CNAMEStrategy configures how the DNS01 provider + should handle CNAME records when found in DNS zones. + type: string + enum: + - None + - Follow + digitalocean: + description: ACMEIssuerDNS01ProviderDigitalOcean is a structure + containing the DNS configuration for DigitalOcean Domains + type: object + required: + - tokenSecretRef + properties: + tokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + rfc2136: + description: ACMEIssuerDNS01ProviderRFC2136 is a structure containing + the configuration for RFC2136 DNS + type: object + required: + - nameserver + properties: + nameserver: + description: The IP address or hostname of an authoritative + DNS server supporting RFC2136 in the form host:port. If + the host is an IPv6 address it must be enclosed in square + brackets (e.g [2001:db8::1]) ; port is optional. This + field is required. + type: string + tsigAlgorithm: + description: 'The TSIG Algorithm configured in the DNS supporting + RFC2136. Used only when ``tsigSecretSecretRef`` and ``tsigKeyName`` + are defined. Supported values are (case-insensitive): + ``HMACMD5`` (default), ``HMACSHA1``, ``HMACSHA256`` or + ``HMACSHA512``.' + type: string + tsigKeyName: + description: The TSIG Key name configured in the DNS. If + ``tsigSecretSecretRef`` is defined, this field is required. + type: string + tsigSecretSecretRef: + description: The name of the secret containing the TSIG + value. If ``tsigKeyName`` is defined, this field is required. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + route53: + description: ACMEIssuerDNS01ProviderRoute53 is a structure containing + the Route 53 configuration for AWS + type: object + required: + - region + properties: + accessKeyID: + description: 'The AccessKeyID is used for authentication. + If not set we fall-back to using env vars, shared credentials + file or AWS Instance metadata see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + type: string + hostedZoneID: + description: If set, the provider will manage only this + zone in Route53 and will not do an lookup using the route53:ListHostedZonesByName + api call. + type: string + region: + description: Always set the region when using AccessKeyID + and SecretAccessKey + type: string + role: + description: Role is a Role ARN which the Route53 provider + will assume using either the explicit credentials AccessKeyID/SecretAccessKey + or the inferred credentials from environment variables, + shared credentials file or AWS Instance metadata + type: string + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication. + If not set we fall-back to using env vars, shared credentials + file or AWS Instance metadata https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + webhook: + description: ACMEIssuerDNS01ProviderWebhook specifies configuration + for a webhook DNS01 provider, including where to POST ChallengePayload + resources. + type: object + required: + - groupName + - solverName + properties: + config: + description: Additional configuration that should be passed + to the webhook apiserver when challenges are processed. + This can contain arbitrary JSON data. Secret values should + not be specified in this stanza. If secret values are + needed (e.g. credentials for a DNS service), you should + use a SecretKeySelector to reference a Secret resource. + For details on the schema of this field, consult the webhook + provider implementation's documentation. + x-kubernetes-preserve-unknown-fields: true + groupName: + description: The API group name that should be used when + POSTing ChallengePayload resources to the webhook apiserver. + This should be the same as the GroupName specified in + the webhook provider implementation. + type: string + solverName: + description: The name of the solver to use, as defined in + the webhook provider implementation. This will typically + be the name of the provider, e.g. 'cloudflare'. + type: string + http01: + description: ACMEChallengeSolverHTTP01 contains configuration detailing + how to solve HTTP01 challenges within a Kubernetes cluster. Typically + this is accomplished through creating 'routes' of some description + that configure ingress controllers to direct traffic to 'solver + pods', which are responsible for responding to the ACME server's + HTTP requests. + type: object + properties: + ingress: + description: The ingress based HTTP01 challenge solver will + solve challenges by creating or modifying Ingress resources + in order to route requests for '/.well-known/acme-challenge/XYZ' + to 'challenge solver' pods that are provisioned by cert-manager + for each Challenge to be completed. + type: object + properties: + class: + description: The ingress class to use when creating Ingress + resources to solve ACME challenges that use this challenge + solver. Only one of 'class' or 'name' may be specified. + type: string + ingressTemplate: + description: Optional ingress template used to configure + the ACME challenge solver ingress used for HTTP01 challenges + type: object + properties: + metadata: + description: ObjectMeta overrides for the ingress used + to solve HTTP01 challenges. Only the 'labels' and + 'annotations' fields may be set. If labels or annotations + overlap with in-built values, the values here will + override the in-built values. + type: object + properties: + annotations: + description: Annotations that should be added to + the created ACME HTTP01 solver ingress. + type: object + additionalProperties: + type: string + labels: + description: Labels that should be added to the + created ACME HTTP01 solver ingress. + type: object + additionalProperties: + type: string + name: + description: The name of the ingress resource that should + have ACME challenge solving routes inserted into it in + order to solve HTTP01 challenges. This is typically used + in conjunction with ingress controllers like ingress-gce, + which maintains a 1:1 mapping between external IPs and + ingress resources. + type: string + podTemplate: + description: Optional pod template used to configure the + ACME challenge solver pods used for HTTP01 challenges + type: object + properties: + metadata: + description: ObjectMeta overrides for the pod used to + solve HTTP01 challenges. Only the 'labels' and 'annotations' + fields may be set. If labels or annotations overlap + with in-built values, the values here will override + the in-built values. + type: object + properties: + annotations: + description: Annotations that should be added to + the create ACME HTTP01 solver pods. + type: object + additionalProperties: + type: string + labels: + description: Labels that should be added to the + created ACME HTTP01 solver pods. + type: object + additionalProperties: + type: string + spec: + description: PodSpec defines overrides for the HTTP01 + challenge solver pod. Only the 'nodeSelector', 'affinity' + and 'tolerations' fields are supported currently. + All other fields will be ignored. + type: object + properties: + affinity: + description: If specified, the pod's scheduling + constraints + type: object + properties: + nodeAffinity: + description: Describes node affinity scheduling + rules for the pod. + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to + schedule pods to nodes that satisfy the + affinity expressions specified by this + field, but it may choose a node that violates + one or more of the expressions. The node + that is most preferred is the one with + the greatest sum of weights, i.e. for + each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a + sum by iterating through the elements + of this field and adding "weight" to the + sum if the node matches the corresponding + matchExpressions; the node(s) with the + highest sum are the most preferred. + type: array + items: + description: An empty preferred scheduling + term matches all objects with implicit + weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no + objects (i.e. is also a no-op). + type: object + required: + - preference + - weight + properties: + preference: + description: A node selector term, + associated with the corresponding + weight. + type: object + properties: + matchExpressions: + description: A list of node selector + requirements by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + type: object + required: + - key + - operator + properties: + key: + description: The label key + that the selector applies + to. + type: string + operator: + description: Represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of + string values. If the + operator is In or NotIn, + the values array must + be non-empty. If the operator + is Exists or DoesNotExist, + the values array must + be empty. If the operator + is Gt or Lt, the values + array must have a single + element, which will be + interpreted as an integer. + This array is replaced + during a strategic merge + patch. + type: array + items: + type: string + matchFields: + description: A list of node selector + requirements by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + type: object + required: + - key + - operator + properties: + key: + description: The label key + that the selector applies + to. + type: string + operator: + description: Represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of + string values. If the + operator is In or NotIn, + the values array must + be non-empty. If the operator + is Exists or DoesNotExist, + the values array must + be empty. If the operator + is Gt or Lt, the values + array must have a single + element, which will be + interpreted as an integer. + This array is replaced + during a strategic merge + patch. + type: array + items: + type: string + weight: + description: Weight associated with + matching the corresponding nodeSelectorTerm, + in the range 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not met at + scheduling time, the pod will not be scheduled + onto the node. If the affinity requirements + specified by this field cease to be met + at some point during pod execution (e.g. + due to an update), the system may or may + not try to eventually evict the pod from + its node. + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + description: Required. A list of node + selector terms. The terms are ORed. + type: array + items: + description: A null or empty node + selector term matches no objects. + The requirements of them are ANDed. + The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + type: object + properties: + matchExpressions: + description: A list of node selector + requirements by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + type: object + required: + - key + - operator + properties: + key: + description: The label key + that the selector applies + to. + type: string + operator: + description: Represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of + string values. If the + operator is In or NotIn, + the values array must + be non-empty. If the operator + is Exists or DoesNotExist, + the values array must + be empty. If the operator + is Gt or Lt, the values + array must have a single + element, which will be + interpreted as an integer. + This array is replaced + during a strategic merge + patch. + type: array + items: + type: string + matchFields: + description: A list of node selector + requirements by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + type: object + required: + - key + - operator + properties: + key: + description: The label key + that the selector applies + to. + type: string + operator: + description: Represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of + string values. If the + operator is In or NotIn, + the values array must + be non-empty. If the operator + is Exists or DoesNotExist, + the values array must + be empty. If the operator + is Gt or Lt, the values + array must have a single + element, which will be + interpreted as an integer. + This array is replaced + during a strategic merge + patch. + type: array + items: + type: string + podAffinity: + description: Describes pod affinity scheduling + rules (e.g. co-locate this pod in the same + node, zone, etc. as some other pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to + schedule pods to nodes that satisfy the + affinity expressions specified by this + field, but it may choose a node that violates + one or more of the expressions. The node + that is most preferred is the one with + the greatest sum of weights, i.e. for + each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a + sum by iterating through the elements + of this field and adding "weight" to the + sum if the node has pods which matches + the corresponding podAffinityTerm; the + node(s) with the highest sum are the most + preferred. + type: array + items: + description: The weights of all of the + matched WeightedPodAffinityTerm fields + are added per-node to find the most + preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod affinity + term, associated with the corresponding + weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key and + values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to a + set of values. Valid + operators are In, + NotIn, Exists and + DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, the + values array must + be non-empty. If the + operator is Exists + or DoesNotExist, the + values array must + be empty. This array + is replaced during + a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is + a map of {key,value} pairs. + A single {key,value} in + the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", + the operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should be + co-located (affinity) or not + co-located (anti-affinity) with + the pods matching the labelSelector + in the specified namespaces, + where co-located is defined + as running on a node whose value + of the label with key topologyKey + matches that of any node on + which any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated with + matching the corresponding podAffinityTerm, + in the range 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not met at + scheduling time, the pod will not be scheduled + onto the node. If the affinity requirements + specified by this field cease to be met + at some point during pod execution (e.g. + due to a pod label update), the system + may or may not try to eventually evict + the pod from its node. When there are + multiple elements, the lists of nodes + corresponding to each podAffinityTerm + are intersected, i.e. all terms must be + satisfied. + type: array + items: + description: Defines a set of pods (namely + those matching the labelSelector relative + to the given namespace(s)) that this + pod should be co-located (affinity) + or not co-located (anti-affinity) with, + where co-located is defined as running + on a node whose value of the label with + key matches that of any + node on which a pod of the set of pods + is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a + set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the + label key that the selector + applies to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In + or NotIn, the values array + must be non-empty. If + the operator is Exists + or DoesNotExist, the values + array must be empty. This + array is replaced during + a strategic merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a + map of {key,value} pairs. A + single {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator + is "In", and the values array + contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); null + or empty list means "this pod's + namespace" + type: array + items: + type: string + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where + co-located is defined as running + on a node whose value of the label + with key topologyKey matches that + of any node on which any of the + selected pods is running. Empty + topologyKey is not allowed. + type: string + podAntiAffinity: + description: Describes pod anti-affinity scheduling + rules (e.g. avoid putting this pod in the + same node, zone, etc. as some other pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to + schedule pods to nodes that satisfy the + anti-affinity expressions specified by + this field, but it may choose a node that + violates one or more of the expressions. + The node that is most preferred is the + one with the greatest sum of weights, + i.e. for each node that meets all of the + scheduling requirements (resource request, + requiredDuringScheduling anti-affinity + expressions, etc.), compute a sum by iterating + through the elements of this field and + adding "weight" to the sum if the node + has pods which matches the corresponding + podAffinityTerm; the node(s) with the + highest sum are the most preferred. + type: array + items: + description: The weights of all of the + matched WeightedPodAffinityTerm fields + are added per-node to find the most + preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod affinity + term, associated with the corresponding + weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key and + values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to a + set of values. Valid + operators are In, + NotIn, Exists and + DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, the + values array must + be non-empty. If the + operator is Exists + or DoesNotExist, the + values array must + be empty. This array + is replaced during + a strategic merge + patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is + a map of {key,value} pairs. + A single {key,value} in + the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", + the operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should be + co-located (affinity) or not + co-located (anti-affinity) with + the pods matching the labelSelector + in the specified namespaces, + where co-located is defined + as running on a node whose value + of the label with key topologyKey + matches that of any node on + which any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated with + matching the corresponding podAffinityTerm, + in the range 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements + specified by this field are not met at + scheduling time, the pod will not be scheduled + onto the node. If the anti-affinity requirements + specified by this field cease to be met + at some point during pod execution (e.g. + due to a pod label update), the system + may or may not try to eventually evict + the pod from its node. When there are + multiple elements, the lists of nodes + corresponding to each podAffinityTerm + are intersected, i.e. all terms must be + satisfied. + type: array + items: + description: Defines a set of pods (namely + those matching the labelSelector relative + to the given namespace(s)) that this + pod should be co-located (affinity) + or not co-located (anti-affinity) with, + where co-located is defined as running + on a node whose value of the label with + key matches that of any + node on which a pod of the set of pods + is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over a + set of resources, in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, a key, + and an operator that relates + the key and values. + type: object + required: + - key + - operator + properties: + key: + description: key is the + label key that the selector + applies to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid + operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In + or NotIn, the values array + must be non-empty. If + the operator is Exists + or DoesNotExist, the values + array must be empty. This + array is replaced during + a strategic merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels is a + map of {key,value} pairs. A + single {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator + is "In", and the values array + contains only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); null + or empty list means "this pod's + namespace" + type: array + items: + type: string + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where + co-located is defined as running + on a node whose value of the label + with key topologyKey matches that + of any node on which any of the + selected pods is running. Empty + topologyKey is not allowed. + type: string + nodeSelector: + description: 'NodeSelector is a selector which must + be true for the pod to fit on a node. Selector + which must match a node''s labels for the pod + to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + tolerations: + description: If specified, the pod's tolerations. + type: array + items: + description: The pod this Toleration is attached + to tolerates any taint that matches the triple + using the matching operator + . + type: object + properties: + effect: + description: Effect indicates the taint effect + to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, + PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the + toleration applies to. Empty means match + all taint keys. If the key is empty, operator + must be Exists; this combination means to + match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship + to the value. Valid operators are Exists + and Equal. Defaults to Equal. Exists is + equivalent to wildcard for value, so that + a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents + the period of time the toleration (which + must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By + default, it is not set, which means tolerate + the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict + immediately) by the system. + type: integer + format: int64 + value: + description: Value is the taint value the + toleration matches to. If the operator is + Exists, the value should be empty, otherwise + just a regular string. + type: string + serviceType: + description: Optional service type for Kubernetes solver + service + type: string + selector: + description: Selector selects a set of DNSNames on the Certificate + resource that should be solved using this challenge solver. + type: object + properties: + dnsNames: + description: List of DNSNames that this solver will be used + to solve. If specified and a match is found, a dnsNames selector + will take precedence over a dnsZones selector. If multiple + solvers match with the same dnsNames value, the solver with + the most matching labels in matchLabels will be selected. + If neither has more matches, the solver defined earlier in + the list will be selected. + type: array + items: + type: string + dnsZones: + description: List of DNSZones that this solver will be used + to solve. The most specific DNS zone match specified here + will take precedence over other DNS zone matches, so a solver + specifying sys.example.com will be selected over one specifying + example.com for the domain www.sys.example.com. If multiple + solvers match with the same dnsZones value, the solver with + the most matching labels in matchLabels will be selected. + If neither has more matches, the solver defined earlier in + the list will be selected. + type: array + items: + type: string + matchLabels: + description: A label selector that is used to refine the set + of certificate's that this challenge solver will apply to. + type: object + additionalProperties: + type: string + token: + description: Token is the ACME challenge token for this challenge. This + is the raw value returned from the ACME server. + type: string + type: + description: Type is the type of ACME challenge this resource represents, + e.g. "dns01" or "http01". + type: string + url: + description: URL is the URL of the ACME Challenge resource for this + challenge. This can be used to lookup details about the status of + this challenge. + type: string + wildcard: + description: Wildcard will be true if this challenge is for a wildcard + identifier, for example '*.example.com'. + type: boolean + status: + type: object + properties: + presented: + description: Presented will be set to true if the challenge values for + this challenge are currently 'presented'. This *does not* imply the + self check is passing. Only that the values have been 'submitted' + for the appropriate challenge mechanism (i.e. the DNS01 TXT record + has been presented, or the HTTP01 configuration has been configured). + type: boolean + processing: + description: Processing is used to denote whether this challenge should + be processed or not. This field will only be set to true by the 'scheduling' + component. It will only be set to false by the 'challenges' controller, + after the challenge has reached a final state or timed out. If this + field is set to false, the challenge controller will not take any + more action. + type: boolean + reason: + description: Reason contains human readable information on why the Challenge + is in the current state. + type: string + state: + description: State contains the current 'state' of the challenge. If + not set, the state of the challenge is unknown. + type: string + enum: + - valid + - ready + - pending + - processing + - invalid + - expired + - errored +--- +# Source: cert-manager/templates/templates.regular.out +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: clusterissuers.cert-manager.io + annotations: + cert-manager.io/inject-ca-from-secret: 'cert-manager/cert-manager-webhook-ca' + labels: + app: 'cert-manager' + app.kubernetes.io/name: 'cert-manager' + app.kubernetes.io/instance: 'cert-manager' + app.kubernetes.io/managed-by: 'Helm' + helm.sh/chart: 'cert-manager-v0.15.0' +spec: + additionalPrinterColumns: + - JSONPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - JSONPath: .status.conditions[?(@.type=="Ready")].message + name: Status + priority: 1 + type: string + - JSONPath: .metadata.creationTimestamp + description: CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before order + across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. + name: Age + type: date + group: cert-manager.io + preserveUnknownFields: false + conversion: + # a Webhook strategy instruct API server to call an external webhook for any conversion between custom resources. + strategy: Webhook + # webhookClientConfig is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server. + webhookClientConfig: + service: + namespace: 'cert-manager' + name: 'cert-manager-webhook' + path: /convert + names: + kind: ClusterIssuer + listKind: ClusterIssuerList + plural: clusterissuers + singular: clusterissuer + scope: Cluster + subresources: + status: {} + versions: + - name: v1alpha2 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + "validation": + "openAPIV3Schema": + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IssuerSpec is the specification of an Issuer. This includes + any configuration required for the issuer. + type: object + properties: + acme: + description: ACMEIssuer contains the specification for an ACME issuer + type: object + required: + - privateKeySecretRef + - server + properties: + email: + description: Email is the email for this account + type: string + externalAccountBinding: + description: ExternalAccountBinding is a reference to a CA external + account of the ACME server. + type: object + required: + - keyAlgorithm + - keyID + - keySecretRef + properties: + keyAlgorithm: + description: keyAlgorithm is the MAC key algorithm that the + key is used for. Valid values are "HS256", "HS384" and "HS512". + type: string + enum: + - HS256 + - HS384 + - HS512 + keyID: + description: keyID is the ID of the CA key that the External + Account is bound to. + type: string + keySecretRef: + description: keySecretRef is a Secret Key Selector referencing + a data item in a Kubernetes Secret which holds the symmetric + MAC key of the External Account Binding. The `key` is the + index string that is paired with the key data in the Secret + and should not be confused with the key data itself, or indeed + with the External Account Binding keyID above. The secret + key stored in the Secret **must** be un-padded, base64 URL + encoded data. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + privateKeySecretRef: + description: PrivateKey is the name of a secret containing the private + key for this user account. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must be a + valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + server: + description: Server is the ACME server URL + type: string + skipTLSVerify: + description: If true, skip verifying the ACME server TLS certificate + type: boolean + solvers: + description: Solvers is a list of challenge solvers that will be + used to solve ACME challenges for the matching domains. + type: array + items: + type: object + properties: + dns01: + type: object + properties: + acmedns: + description: ACMEIssuerDNS01ProviderAcmeDNS is a structure + containing the configuration for ACME-DNS servers + type: object + required: + - accountSecretRef + - host + properties: + accountSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + host: + type: string + akamai: + description: ACMEIssuerDNS01ProviderAkamai is a structure + containing the DNS configuration for Akamai DNS—Zone + Record Management API + type: object + required: + - accessTokenSecretRef + - clientSecretSecretRef + - clientTokenSecretRef + - serviceConsumerDomain + properties: + accessTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + clientSecretSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + clientTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + serviceConsumerDomain: + type: string + azuredns: + description: ACMEIssuerDNS01ProviderAzureDNS is a structure + containing the configuration for Azure DNS + type: object + required: + - resourceGroupName + - subscriptionID + properties: + clientID: + description: if both this and ClientSecret are left + unset MSI will be used + type: string + clientSecretSecretRef: + description: if both this and ClientID are left unset + MSI will be used + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + environment: + type: string + enum: + - AzurePublicCloud + - AzureChinaCloud + - AzureGermanCloud + - AzureUSGovernmentCloud + hostedZoneName: + type: string + resourceGroupName: + type: string + subscriptionID: + type: string + tenantID: + description: when specifying ClientID and ClientSecret + then this field is also needed + type: string + clouddns: + description: ACMEIssuerDNS01ProviderCloudDNS is a structure + containing the DNS configuration for Google Cloud DNS + type: object + required: + - project + properties: + project: + type: string + serviceAccountSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + cloudflare: + description: ACMEIssuerDNS01ProviderCloudflare is a structure + containing the DNS configuration for Cloudflare + type: object + required: + - email + properties: + apiKeySecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + apiTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + email: + type: string + cnameStrategy: + description: CNAMEStrategy configures how the DNS01 provider + should handle CNAME records when found in DNS zones. + type: string + enum: + - None + - Follow + digitalocean: + description: ACMEIssuerDNS01ProviderDigitalOcean is a + structure containing the DNS configuration for DigitalOcean + Domains + type: object + required: + - tokenSecretRef + properties: + tokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + rfc2136: + description: ACMEIssuerDNS01ProviderRFC2136 is a structure + containing the configuration for RFC2136 DNS + type: object + required: + - nameserver + properties: + nameserver: + description: The IP address or hostname of an authoritative + DNS server supporting RFC2136 in the form host:port. + If the host is an IPv6 address it must be enclosed + in square brackets (e.g [2001:db8::1]) ; port is + optional. This field is required. + type: string + tsigAlgorithm: + description: 'The TSIG Algorithm configured in the + DNS supporting RFC2136. Used only when ``tsigSecretSecretRef`` + and ``tsigKeyName`` are defined. Supported values + are (case-insensitive): ``HMACMD5`` (default), ``HMACSHA1``, + ``HMACSHA256`` or ``HMACSHA512``.' + type: string + tsigKeyName: + description: The TSIG Key name configured in the DNS. + If ``tsigSecretSecretRef`` is defined, this field + is required. + type: string + tsigSecretSecretRef: + description: The name of the secret containing the + TSIG value. If ``tsigKeyName`` is defined, this + field is required. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + route53: + description: ACMEIssuerDNS01ProviderRoute53 is a structure + containing the Route 53 configuration for AWS + type: object + required: + - region + properties: + accessKeyID: + description: 'The AccessKeyID is used for authentication. + If not set we fall-back to using env vars, shared + credentials file or AWS Instance metadata see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + type: string + hostedZoneID: + description: If set, the provider will manage only + this zone in Route53 and will not do an lookup using + the route53:ListHostedZonesByName api call. + type: string + region: + description: Always set the region when using AccessKeyID + and SecretAccessKey + type: string + role: + description: Role is a Role ARN which the Route53 + provider will assume using either the explicit credentials + AccessKeyID/SecretAccessKey or the inferred credentials + from environment variables, shared credentials file + or AWS Instance metadata + type: string + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication. + If not set we fall-back to using env vars, shared + credentials file or AWS Instance metadata https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + webhook: + description: ACMEIssuerDNS01ProviderWebhook specifies + configuration for a webhook DNS01 provider, including + where to POST ChallengePayload resources. + type: object + required: + - groupName + - solverName + properties: + config: + description: Additional configuration that should + be passed to the webhook apiserver when challenges + are processed. This can contain arbitrary JSON data. + Secret values should not be specified in this stanza. + If secret values are needed (e.g. credentials for + a DNS service), you should use a SecretKeySelector + to reference a Secret resource. For details on the + schema of this field, consult the webhook provider + implementation's documentation. + x-kubernetes-preserve-unknown-fields: true + groupName: + description: The API group name that should be used + when POSTing ChallengePayload resources to the webhook + apiserver. This should be the same as the GroupName + specified in the webhook provider implementation. + type: string + solverName: + description: The name of the solver to use, as defined + in the webhook provider implementation. This will + typically be the name of the provider, e.g. 'cloudflare'. + type: string + http01: + description: ACMEChallengeSolverHTTP01 contains configuration + detailing how to solve HTTP01 challenges within a Kubernetes + cluster. Typically this is accomplished through creating + 'routes' of some description that configure ingress controllers + to direct traffic to 'solver pods', which are responsible + for responding to the ACME server's HTTP requests. + type: object + properties: + ingress: + description: The ingress based HTTP01 challenge solver + will solve challenges by creating or modifying Ingress + resources in order to route requests for '/.well-known/acme-challenge/XYZ' + to 'challenge solver' pods that are provisioned by cert-manager + for each Challenge to be completed. + type: object + properties: + class: + description: The ingress class to use when creating + Ingress resources to solve ACME challenges that + use this challenge solver. Only one of 'class' or + 'name' may be specified. + type: string + ingressTemplate: + description: Optional ingress template used to configure + the ACME challenge solver ingress used for HTTP01 + challenges + type: object + properties: + metadata: + description: ObjectMeta overrides for the ingress + used to solve HTTP01 challenges. Only the 'labels' + and 'annotations' fields may be set. If labels + or annotations overlap with in-built values, + the values here will override the in-built values. + type: object + properties: + annotations: + description: Annotations that should be added + to the created ACME HTTP01 solver ingress. + type: object + additionalProperties: + type: string + labels: + description: Labels that should be added to + the created ACME HTTP01 solver ingress. + type: object + additionalProperties: + type: string + name: + description: The name of the ingress resource that + should have ACME challenge solving routes inserted + into it in order to solve HTTP01 challenges. This + is typically used in conjunction with ingress controllers + like ingress-gce, which maintains a 1:1 mapping + between external IPs and ingress resources. + type: string + podTemplate: + description: Optional pod template used to configure + the ACME challenge solver pods used for HTTP01 challenges + type: object + properties: + metadata: + description: ObjectMeta overrides for the pod + used to solve HTTP01 challenges. Only the 'labels' + and 'annotations' fields may be set. If labels + or annotations overlap with in-built values, + the values here will override the in-built values. + type: object + properties: + annotations: + description: Annotations that should be added + to the create ACME HTTP01 solver pods. + type: object + additionalProperties: + type: string + labels: + description: Labels that should be added to + the created ACME HTTP01 solver pods. + type: object + additionalProperties: + type: string + spec: + description: PodSpec defines overrides for the + HTTP01 challenge solver pod. Only the 'nodeSelector', + 'affinity' and 'tolerations' fields are supported + currently. All other fields will be ignored. + type: object + properties: + affinity: + description: If specified, the pod's scheduling + constraints + type: object + properties: + nodeAffinity: + description: Describes node affinity scheduling + rules for the pod. + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + affinity expressions, etc.), compute + a sum by iterating through the elements + of this field and adding "weight" + to the sum if the node matches the + corresponding matchExpressions; + the node(s) with the highest sum + are the most preferred. + type: array + items: + description: An empty preferred + scheduling term matches all objects + with implicit weight 0 (i.e. it's + a no-op). A null preferred scheduling + term matches no objects (i.e. + is also a no-op). + type: object + required: + - preference + - weight + properties: + preference: + description: A node selector + term, associated with the + corresponding weight. + type: object + properties: + matchExpressions: + description: A list of node + selector requirements + by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchFields: + description: A list of node + selector requirements + by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + weight: + description: Weight associated + with matching the corresponding + nodeSelectorTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not + met at scheduling time, the pod + will not be scheduled onto the node. + If the affinity requirements specified + by this field cease to be met at + some point during pod execution + (e.g. due to an update), the system + may or may not try to eventually + evict the pod from its node. + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + description: Required. A list + of node selector terms. The + terms are ORed. + type: array + items: + description: A null or empty + node selector term matches + no objects. The requirements + of them are ANDed. The TopologySelectorTerm + type implements a subset of + the NodeSelectorTerm. + type: object + properties: + matchExpressions: + description: A list of node + selector requirements + by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchFields: + description: A list of node + selector requirements + by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + podAffinity: + description: Describes pod affinity scheduling + rules (e.g. co-locate this pod in the + same node, zone, etc. as some other + pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + affinity expressions, etc.), compute + a sum by iterating through the elements + of this field and adding "weight" + to the sum if the node has pods + which matches the corresponding + podAffinityTerm; the node(s) with + the highest sum are the most preferred. + type: array + items: + description: The weights of all + of the matched WeightedPodAffinityTerm + fields are added per-node to find + the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod + affinity term, associated + with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query + over a set of resources, + in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label + selector requirements. + The requirements are + ANDed. + type: array + items: + description: A label + selector requirement + is a selector that + contains values, + a key, and an operator + that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key + is the label + key that the + selector applies + to. + type: string + operator: + description: operator + represents a + key's relationship + to a set of + values. Valid + operators are + In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or + DoesNotExist, + the values array + must be empty. + This array is + replaced during + a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels + map is equivalent + to an element of matchExpressions, + whose key field is + "key", the operator + is "In", and the values + array contains only + "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces + specifies which namespaces + the labelSelector applies + to (matches against); + null or empty list means + "this pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) + or not co-located (anti-affinity) + with the pods matching + the labelSelector in the + specified namespaces, + where co-located is defined + as running on a node whose + value of the label with + key topologyKey matches + that of any node on which + any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated + with matching the corresponding + podAffinityTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not + met at scheduling time, the pod + will not be scheduled onto the node. + If the affinity requirements specified + by this field cease to be met at + some point during pod execution + (e.g. due to a pod label update), + the system may or may not try to + eventually evict the pod from its + node. When there are multiple elements, + the lists of nodes corresponding + to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + type: array + items: + description: Defines a set of pods + (namely those matching the labelSelector + relative to the given namespace(s)) + that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined + as running on a node whose value + of the label with key + matches that of any node on which + a pod of the set of pods is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to + a set of values. + Valid operators + are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels map + is equivalent to an element + of matchExpressions, whose + key field is "key", the + operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) or + not co-located (anti-affinity) + with the pods matching the + labelSelector in the specified + namespaces, where co-located + is defined as running on a + node whose value of the label + with key topologyKey matches + that of any node on which + any of the selected pods is + running. Empty topologyKey + is not allowed. + type: string + podAntiAffinity: + description: Describes pod anti-affinity + scheduling rules (e.g. avoid putting + this pod in the same node, zone, etc. + as some other pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the anti-affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + anti-affinity expressions, etc.), + compute a sum by iterating through + the elements of this field and adding + "weight" to the sum if the node + has pods which matches the corresponding + podAffinityTerm; the node(s) with + the highest sum are the most preferred. + type: array + items: + description: The weights of all + of the matched WeightedPodAffinityTerm + fields are added per-node to find + the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod + affinity term, associated + with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query + over a set of resources, + in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label + selector requirements. + The requirements are + ANDed. + type: array + items: + description: A label + selector requirement + is a selector that + contains values, + a key, and an operator + that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key + is the label + key that the + selector applies + to. + type: string + operator: + description: operator + represents a + key's relationship + to a set of + values. Valid + operators are + In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or + DoesNotExist, + the values array + must be empty. + This array is + replaced during + a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels + map is equivalent + to an element of matchExpressions, + whose key field is + "key", the operator + is "In", and the values + array contains only + "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces + specifies which namespaces + the labelSelector applies + to (matches against); + null or empty list means + "this pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) + or not co-located (anti-affinity) + with the pods matching + the labelSelector in the + specified namespaces, + where co-located is defined + as running on a node whose + value of the label with + key topologyKey matches + that of any node on which + any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated + with matching the corresponding + podAffinityTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity + requirements specified by this field + are not met at scheduling time, + the pod will not be scheduled onto + the node. If the anti-affinity requirements + specified by this field cease to + be met at some point during pod + execution (e.g. due to a pod label + update), the system may or may not + try to eventually evict the pod + from its node. When there are multiple + elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + type: array + items: + description: Defines a set of pods + (namely those matching the labelSelector + relative to the given namespace(s)) + that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined + as running on a node whose value + of the label with key + matches that of any node on which + a pod of the set of pods is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to + a set of values. + Valid operators + are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels map + is equivalent to an element + of matchExpressions, whose + key field is "key", the + operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) or + not co-located (anti-affinity) + with the pods matching the + labelSelector in the specified + namespaces, where co-located + is defined as running on a + node whose value of the label + with key topologyKey matches + that of any node on which + any of the selected pods is + running. Empty topologyKey + is not allowed. + type: string + nodeSelector: + description: 'NodeSelector is a selector which + must be true for the pod to fit on a node. + Selector which must match a node''s labels + for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + tolerations: + description: If specified, the pod's tolerations. + type: array + items: + description: The pod this Toleration is + attached to tolerates any taint that matches + the triple using the + matching operator . + type: object + properties: + effect: + description: Effect indicates the taint + effect to match. Empty means match + all taint effects. When specified, + allowed values are NoSchedule, PreferNoSchedule + and NoExecute. + type: string + key: + description: Key is the taint key that + the toleration applies to. Empty means + match all taint keys. If the key is + empty, operator must be Exists; this + combination means to match all values + and all keys. + type: string + operator: + description: Operator represents a key's + relationship to the value. Valid operators + are Exists and Equal. Defaults to + Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents + the period of time the toleration + (which must be of effect NoExecute, + otherwise this field is ignored) tolerates + the taint. By default, it is not set, + which means tolerate the taint forever + (do not evict). Zero and negative + values will be treated as 0 (evict + immediately) by the system. + type: integer + format: int64 + value: + description: Value is the taint value + the toleration matches to. If the + operator is Exists, the value should + be empty, otherwise just a regular + string. + type: string + serviceType: + description: Optional service type for Kubernetes + solver service + type: string + selector: + description: Selector selects a set of DNSNames on the Certificate + resource that should be solved using this challenge solver. + type: object + properties: + dnsNames: + description: List of DNSNames that this solver will be + used to solve. If specified and a match is found, a + dnsNames selector will take precedence over a dnsZones + selector. If multiple solvers match with the same dnsNames + value, the solver with the most matching labels in matchLabels + will be selected. If neither has more matches, the solver + defined earlier in the list will be selected. + type: array + items: + type: string + dnsZones: + description: List of DNSZones that this solver will be + used to solve. The most specific DNS zone match specified + here will take precedence over other DNS zone matches, + so a solver specifying sys.example.com will be selected + over one specifying example.com for the domain www.sys.example.com. + If multiple solvers match with the same dnsZones value, + the solver with the most matching labels in matchLabels + will be selected. If neither has more matches, the solver + defined earlier in the list will be selected. + type: array + items: + type: string + matchLabels: + description: A label selector that is used to refine the + set of certificate's that this challenge solver will + apply to. + type: object + additionalProperties: + type: string + ca: + type: object + required: + - secretName + properties: + crlDistributionPoints: + description: The CRL distribution points is an X.509 v3 certificate + extension which identifies the location of the CRL from which + the revocation of this certificate can be checked. If not set + certificate will be issued without CDP. Values are strings. + type: array + items: + type: string + secretName: + description: SecretName is the name of the secret used to sign Certificates + issued by this Issuer. + type: string + selfSigned: + type: object + properties: + crlDistributionPoints: + description: The CRL distribution points is an X.509 v3 certificate + extension which identifies the location of the CRL from which + the revocation of this certificate can be checked. If not set + certificate will be issued without CDP. Values are strings. + type: array + items: + type: string + vault: + type: object + required: + - auth + - path + - server + properties: + auth: + description: Vault authentication + type: object + properties: + appRole: + description: This Secret contains a AppRole and Secret + type: object + required: + - path + - roleId + - secretRef + properties: + path: + description: Where the authentication path is mounted in + Vault. + type: string + roleId: + type: string + secretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + kubernetes: + description: This contains a Role and Secret with a ServiceAccount + token to authenticate with vault. + type: object + required: + - role + - secretRef + properties: + mountPath: + description: The Vault mountPath here is the mount path + to use when authenticating with Vault. For example, setting + a value to `/v1/auth/foo`, will use the path `/v1/auth/foo/login` + to authenticate with Vault. If unspecified, the default + value "/v1/auth/kubernetes" will be used. + type: string + role: + description: A required field containing the Vault Role + to assume. A Role binds a Kubernetes ServiceAccount with + a set of Vault policies. + type: string + secretRef: + description: The required Secret field containing a Kubernetes + ServiceAccount JWT used for authenticating with Vault. + Use of 'ambient credentials' is not supported. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + tokenSecretRef: + description: This Secret contains the Vault token key + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + caBundle: + description: Base64 encoded CA bundle to validate Vault server certificate. + Only used if the Server URL is using HTTPS protocol. This parameter + is ignored for plain HTTP protocol connection. If not set the + system root certificates are used to validate the TLS connection. + type: string + format: byte + path: + description: Vault URL path to the certificate role + type: string + server: + description: Server is the vault connection address + type: string + venafi: + description: VenafiIssuer describes issuer configuration details for + Venafi Cloud. + type: object + required: + - zone + properties: + cloud: + description: Cloud specifies the Venafi cloud configuration settings. + Only one of TPP or Cloud may be specified. + type: object + required: + - apiTokenSecretRef + properties: + apiTokenSecretRef: + description: APITokenSecretRef is a secret key selector for + the Venafi Cloud API token. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + url: + description: URL is the base URL for Venafi Cloud + type: string + tpp: + description: TPP specifies Trust Protection Platform configuration + settings. Only one of TPP or Cloud may be specified. + type: object + required: + - credentialsRef + - url + properties: + caBundle: + description: CABundle is a PEM encoded TLS certificate to use + to verify connections to the TPP instance. If specified, system + roots will not be used and the issuing CA for the TPP instance + must be verifiable using the provided root. If not specified, + the connection will be verified using the cert-manager system + root certificates. + type: string + format: byte + credentialsRef: + description: CredentialsRef is a reference to a Secret containing + the username and password for the TPP server. The secret must + contain two keys, 'username' and 'password'. + type: object + required: + - name + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + url: + description: URL is the base URL for the Venafi TPP instance + type: string + zone: + description: Zone is the Venafi Policy Zone to use for this issuer. + All requests made to the Venafi platform will be restricted by + the named zone policy. This field is required. + type: string + status: + description: IssuerStatus contains status information about an Issuer + type: object + properties: + acme: + type: object + properties: + lastRegisteredEmail: + description: LastRegisteredEmail is the email associated with the + latest registered ACME account, in order to track changes made + to registered account associated with the Issuer + type: string + uri: + description: URI is the unique account identifier, which can also + be used to retrieve account details from the CA + type: string + conditions: + type: array + items: + description: IssuerCondition contains condition information for an + Issuer. + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: LastTransitionTime is the timestamp corresponding + to the last status change of this condition. + type: string + format: date-time + message: + description: Message is a human readable description of the details + of the last transition, complementing reason. + type: string + reason: + description: Reason is a brief machine readable explanation for + the condition's last transition. + type: string + status: + description: Status of the condition, one of ('True', 'False', + 'Unknown'). + type: string + enum: + - "True" + - "False" + - Unknown + type: + description: Type of the condition, currently ('Ready'). + type: string +--- +# Source: cert-manager/templates/templates.regular.out +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: issuers.cert-manager.io + annotations: + cert-manager.io/inject-ca-from-secret: 'cert-manager/cert-manager-webhook-ca' + labels: + app: 'cert-manager' + app.kubernetes.io/name: 'cert-manager' + app.kubernetes.io/instance: 'cert-manager' + app.kubernetes.io/managed-by: 'Helm' + helm.sh/chart: 'cert-manager-v0.15.0' +spec: + additionalPrinterColumns: + - JSONPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - JSONPath: .status.conditions[?(@.type=="Ready")].message + name: Status + priority: 1 + type: string + - JSONPath: .metadata.creationTimestamp + description: CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before order + across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. + name: Age + type: date + group: cert-manager.io + preserveUnknownFields: false + conversion: + # a Webhook strategy instruct API server to call an external webhook for any conversion between custom resources. + strategy: Webhook + # webhookClientConfig is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server. + webhookClientConfig: + service: + namespace: 'cert-manager' + name: 'cert-manager-webhook' + path: /convert + names: + kind: Issuer + listKind: IssuerList + plural: issuers + singular: issuer + scope: Namespaced + subresources: + status: {} + versions: + - name: v1alpha2 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + "validation": + "openAPIV3Schema": + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IssuerSpec is the specification of an Issuer. This includes + any configuration required for the issuer. + type: object + properties: + acme: + description: ACMEIssuer contains the specification for an ACME issuer + type: object + required: + - privateKeySecretRef + - server + properties: + email: + description: Email is the email for this account + type: string + externalAccountBinding: + description: ExternalAccountBinding is a reference to a CA external + account of the ACME server. + type: object + required: + - keyAlgorithm + - keyID + - keySecretRef + properties: + keyAlgorithm: + description: keyAlgorithm is the MAC key algorithm that the + key is used for. Valid values are "HS256", "HS384" and "HS512". + type: string + enum: + - HS256 + - HS384 + - HS512 + keyID: + description: keyID is the ID of the CA key that the External + Account is bound to. + type: string + keySecretRef: + description: keySecretRef is a Secret Key Selector referencing + a data item in a Kubernetes Secret which holds the symmetric + MAC key of the External Account Binding. The `key` is the + index string that is paired with the key data in the Secret + and should not be confused with the key data itself, or indeed + with the External Account Binding keyID above. The secret + key stored in the Secret **must** be un-padded, base64 URL + encoded data. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + privateKeySecretRef: + description: PrivateKey is the name of a secret containing the private + key for this user account. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must be a + valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + server: + description: Server is the ACME server URL + type: string + skipTLSVerify: + description: If true, skip verifying the ACME server TLS certificate + type: boolean + solvers: + description: Solvers is a list of challenge solvers that will be + used to solve ACME challenges for the matching domains. + type: array + items: + type: object + properties: + dns01: + type: object + properties: + acmedns: + description: ACMEIssuerDNS01ProviderAcmeDNS is a structure + containing the configuration for ACME-DNS servers + type: object + required: + - accountSecretRef + - host + properties: + accountSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + host: + type: string + akamai: + description: ACMEIssuerDNS01ProviderAkamai is a structure + containing the DNS configuration for Akamai DNS—Zone + Record Management API + type: object + required: + - accessTokenSecretRef + - clientSecretSecretRef + - clientTokenSecretRef + - serviceConsumerDomain + properties: + accessTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + clientSecretSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + clientTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + serviceConsumerDomain: + type: string + azuredns: + description: ACMEIssuerDNS01ProviderAzureDNS is a structure + containing the configuration for Azure DNS + type: object + required: + - resourceGroupName + - subscriptionID + properties: + clientID: + description: if both this and ClientSecret are left + unset MSI will be used + type: string + clientSecretSecretRef: + description: if both this and ClientID are left unset + MSI will be used + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + environment: + type: string + enum: + - AzurePublicCloud + - AzureChinaCloud + - AzureGermanCloud + - AzureUSGovernmentCloud + hostedZoneName: + type: string + resourceGroupName: + type: string + subscriptionID: + type: string + tenantID: + description: when specifying ClientID and ClientSecret + then this field is also needed + type: string + clouddns: + description: ACMEIssuerDNS01ProviderCloudDNS is a structure + containing the DNS configuration for Google Cloud DNS + type: object + required: + - project + properties: + project: + type: string + serviceAccountSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + cloudflare: + description: ACMEIssuerDNS01ProviderCloudflare is a structure + containing the DNS configuration for Cloudflare + type: object + required: + - email + properties: + apiKeySecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + apiTokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + email: + type: string + cnameStrategy: + description: CNAMEStrategy configures how the DNS01 provider + should handle CNAME records when found in DNS zones. + type: string + enum: + - None + - Follow + digitalocean: + description: ACMEIssuerDNS01ProviderDigitalOcean is a + structure containing the DNS configuration for DigitalOcean + Domains + type: object + required: + - tokenSecretRef + properties: + tokenSecretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + rfc2136: + description: ACMEIssuerDNS01ProviderRFC2136 is a structure + containing the configuration for RFC2136 DNS + type: object + required: + - nameserver + properties: + nameserver: + description: The IP address or hostname of an authoritative + DNS server supporting RFC2136 in the form host:port. + If the host is an IPv6 address it must be enclosed + in square brackets (e.g [2001:db8::1]) ; port is + optional. This field is required. + type: string + tsigAlgorithm: + description: 'The TSIG Algorithm configured in the + DNS supporting RFC2136. Used only when ``tsigSecretSecretRef`` + and ``tsigKeyName`` are defined. Supported values + are (case-insensitive): ``HMACMD5`` (default), ``HMACSHA1``, + ``HMACSHA256`` or ``HMACSHA512``.' + type: string + tsigKeyName: + description: The TSIG Key name configured in the DNS. + If ``tsigSecretSecretRef`` is defined, this field + is required. + type: string + tsigSecretSecretRef: + description: The name of the secret containing the + TSIG value. If ``tsigKeyName`` is defined, this + field is required. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + route53: + description: ACMEIssuerDNS01ProviderRoute53 is a structure + containing the Route 53 configuration for AWS + type: object + required: + - region + properties: + accessKeyID: + description: 'The AccessKeyID is used for authentication. + If not set we fall-back to using env vars, shared + credentials file or AWS Instance metadata see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials' + type: string + hostedZoneID: + description: If set, the provider will manage only + this zone in Route53 and will not do an lookup using + the route53:ListHostedZonesByName api call. + type: string + region: + description: Always set the region when using AccessKeyID + and SecretAccessKey + type: string + role: + description: Role is a Role ARN which the Route53 + provider will assume using either the explicit credentials + AccessKeyID/SecretAccessKey or the inferred credentials + from environment variables, shared credentials file + or AWS Instance metadata + type: string + secretAccessKeySecretRef: + description: The SecretAccessKey is used for authentication. + If not set we fall-back to using env vars, shared + credentials file or AWS Instance metadata https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. + Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + webhook: + description: ACMEIssuerDNS01ProviderWebhook specifies + configuration for a webhook DNS01 provider, including + where to POST ChallengePayload resources. + type: object + required: + - groupName + - solverName + properties: + config: + description: Additional configuration that should + be passed to the webhook apiserver when challenges + are processed. This can contain arbitrary JSON data. + Secret values should not be specified in this stanza. + If secret values are needed (e.g. credentials for + a DNS service), you should use a SecretKeySelector + to reference a Secret resource. For details on the + schema of this field, consult the webhook provider + implementation's documentation. + x-kubernetes-preserve-unknown-fields: true + groupName: + description: The API group name that should be used + when POSTing ChallengePayload resources to the webhook + apiserver. This should be the same as the GroupName + specified in the webhook provider implementation. + type: string + solverName: + description: The name of the solver to use, as defined + in the webhook provider implementation. This will + typically be the name of the provider, e.g. 'cloudflare'. + type: string + http01: + description: ACMEChallengeSolverHTTP01 contains configuration + detailing how to solve HTTP01 challenges within a Kubernetes + cluster. Typically this is accomplished through creating + 'routes' of some description that configure ingress controllers + to direct traffic to 'solver pods', which are responsible + for responding to the ACME server's HTTP requests. + type: object + properties: + ingress: + description: The ingress based HTTP01 challenge solver + will solve challenges by creating or modifying Ingress + resources in order to route requests for '/.well-known/acme-challenge/XYZ' + to 'challenge solver' pods that are provisioned by cert-manager + for each Challenge to be completed. + type: object + properties: + class: + description: The ingress class to use when creating + Ingress resources to solve ACME challenges that + use this challenge solver. Only one of 'class' or + 'name' may be specified. + type: string + ingressTemplate: + description: Optional ingress template used to configure + the ACME challenge solver ingress used for HTTP01 + challenges + type: object + properties: + metadata: + description: ObjectMeta overrides for the ingress + used to solve HTTP01 challenges. Only the 'labels' + and 'annotations' fields may be set. If labels + or annotations overlap with in-built values, + the values here will override the in-built values. + type: object + properties: + annotations: + description: Annotations that should be added + to the created ACME HTTP01 solver ingress. + type: object + additionalProperties: + type: string + labels: + description: Labels that should be added to + the created ACME HTTP01 solver ingress. + type: object + additionalProperties: + type: string + name: + description: The name of the ingress resource that + should have ACME challenge solving routes inserted + into it in order to solve HTTP01 challenges. This + is typically used in conjunction with ingress controllers + like ingress-gce, which maintains a 1:1 mapping + between external IPs and ingress resources. + type: string + podTemplate: + description: Optional pod template used to configure + the ACME challenge solver pods used for HTTP01 challenges + type: object + properties: + metadata: + description: ObjectMeta overrides for the pod + used to solve HTTP01 challenges. Only the 'labels' + and 'annotations' fields may be set. If labels + or annotations overlap with in-built values, + the values here will override the in-built values. + type: object + properties: + annotations: + description: Annotations that should be added + to the create ACME HTTP01 solver pods. + type: object + additionalProperties: + type: string + labels: + description: Labels that should be added to + the created ACME HTTP01 solver pods. + type: object + additionalProperties: + type: string + spec: + description: PodSpec defines overrides for the + HTTP01 challenge solver pod. Only the 'nodeSelector', + 'affinity' and 'tolerations' fields are supported + currently. All other fields will be ignored. + type: object + properties: + affinity: + description: If specified, the pod's scheduling + constraints + type: object + properties: + nodeAffinity: + description: Describes node affinity scheduling + rules for the pod. + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + affinity expressions, etc.), compute + a sum by iterating through the elements + of this field and adding "weight" + to the sum if the node matches the + corresponding matchExpressions; + the node(s) with the highest sum + are the most preferred. + type: array + items: + description: An empty preferred + scheduling term matches all objects + with implicit weight 0 (i.e. it's + a no-op). A null preferred scheduling + term matches no objects (i.e. + is also a no-op). + type: object + required: + - preference + - weight + properties: + preference: + description: A node selector + term, associated with the + corresponding weight. + type: object + properties: + matchExpressions: + description: A list of node + selector requirements + by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchFields: + description: A list of node + selector requirements + by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + weight: + description: Weight associated + with matching the corresponding + nodeSelectorTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not + met at scheduling time, the pod + will not be scheduled onto the node. + If the affinity requirements specified + by this field cease to be met at + some point during pod execution + (e.g. due to an update), the system + may or may not try to eventually + evict the pod from its node. + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + description: Required. A list + of node selector terms. The + terms are ORed. + type: array + items: + description: A null or empty + node selector term matches + no objects. The requirements + of them are ANDed. The TopologySelectorTerm + type implements a subset of + the NodeSelectorTerm. + type: object + properties: + matchExpressions: + description: A list of node + selector requirements + by node's labels. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchFields: + description: A list of node + selector requirements + by node's fields. + type: array + items: + description: A node selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: The label + key that the selector + applies to. + type: string + operator: + description: Represents + a key's relationship + to a set of values. + Valid operators + are In, NotIn, Exists, + DoesNotExist. Gt, + and Lt. + type: string + values: + description: An array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. If + the operator is + Gt or Lt, the values + array must have + a single element, + which will be interpreted + as an integer. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + podAffinity: + description: Describes pod affinity scheduling + rules (e.g. co-locate this pod in the + same node, zone, etc. as some other + pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + affinity expressions, etc.), compute + a sum by iterating through the elements + of this field and adding "weight" + to the sum if the node has pods + which matches the corresponding + podAffinityTerm; the node(s) with + the highest sum are the most preferred. + type: array + items: + description: The weights of all + of the matched WeightedPodAffinityTerm + fields are added per-node to find + the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod + affinity term, associated + with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query + over a set of resources, + in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label + selector requirements. + The requirements are + ANDed. + type: array + items: + description: A label + selector requirement + is a selector that + contains values, + a key, and an operator + that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key + is the label + key that the + selector applies + to. + type: string + operator: + description: operator + represents a + key's relationship + to a set of + values. Valid + operators are + In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or + DoesNotExist, + the values array + must be empty. + This array is + replaced during + a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels + map is equivalent + to an element of matchExpressions, + whose key field is + "key", the operator + is "In", and the values + array contains only + "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces + specifies which namespaces + the labelSelector applies + to (matches against); + null or empty list means + "this pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) + or not co-located (anti-affinity) + with the pods matching + the labelSelector in the + specified namespaces, + where co-located is defined + as running on a node whose + value of the label with + key topologyKey matches + that of any node on which + any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated + with matching the corresponding + podAffinityTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements + specified by this field are not + met at scheduling time, the pod + will not be scheduled onto the node. + If the affinity requirements specified + by this field cease to be met at + some point during pod execution + (e.g. due to a pod label update), + the system may or may not try to + eventually evict the pod from its + node. When there are multiple elements, + the lists of nodes corresponding + to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + type: array + items: + description: Defines a set of pods + (namely those matching the labelSelector + relative to the given namespace(s)) + that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined + as running on a node whose value + of the label with key + matches that of any node on which + a pod of the set of pods is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to + a set of values. + Valid operators + are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels map + is equivalent to an element + of matchExpressions, whose + key field is "key", the + operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) or + not co-located (anti-affinity) + with the pods matching the + labelSelector in the specified + namespaces, where co-located + is defined as running on a + node whose value of the label + with key topologyKey matches + that of any node on which + any of the selected pods is + running. Empty topologyKey + is not allowed. + type: string + podAntiAffinity: + description: Describes pod anti-affinity + scheduling rules (e.g. avoid putting + this pod in the same node, zone, etc. + as some other pod(s)). + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer + to schedule pods to nodes that satisfy + the anti-affinity expressions specified + by this field, but it may choose + a node that violates one or more + of the expressions. The node that + is most preferred is the one with + the greatest sum of weights, i.e. + for each node that meets all of + the scheduling requirements (resource + request, requiredDuringScheduling + anti-affinity expressions, etc.), + compute a sum by iterating through + the elements of this field and adding + "weight" to the sum if the node + has pods which matches the corresponding + podAffinityTerm; the node(s) with + the highest sum are the most preferred. + type: array + items: + description: The weights of all + of the matched WeightedPodAffinityTerm + fields are added per-node to find + the most preferred node(s) + type: object + required: + - podAffinityTerm + - weight + properties: + podAffinityTerm: + description: Required. A pod + affinity term, associated + with the corresponding weight. + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query + over a set of resources, + in this case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label + selector requirements. + The requirements are + ANDed. + type: array + items: + description: A label + selector requirement + is a selector that + contains values, + a key, and an operator + that relates the + key and values. + type: object + required: + - key + - operator + properties: + key: + description: key + is the label + key that the + selector applies + to. + type: string + operator: + description: operator + represents a + key's relationship + to a set of + values. Valid + operators are + In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array + of string values. + If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or + DoesNotExist, + the values array + must be empty. + This array is + replaced during + a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels + map is equivalent + to an element of matchExpressions, + whose key field is + "key", the operator + is "In", and the values + array contains only + "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces + specifies which namespaces + the labelSelector applies + to (matches against); + null or empty list means + "this pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) + or not co-located (anti-affinity) + with the pods matching + the labelSelector in the + specified namespaces, + where co-located is defined + as running on a node whose + value of the label with + key topologyKey matches + that of any node on which + any of the selected pods + is running. Empty topologyKey + is not allowed. + type: string + weight: + description: weight associated + with matching the corresponding + podAffinityTerm, in the range + 1-100. + type: integer + format: int32 + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity + requirements specified by this field + are not met at scheduling time, + the pod will not be scheduled onto + the node. If the anti-affinity requirements + specified by this field cease to + be met at some point during pod + execution (e.g. due to a pod label + update), the system may or may not + try to eventually evict the pod + from its node. When there are multiple + elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, + i.e. all terms must be satisfied. + type: array + items: + description: Defines a set of pods + (namely those matching the labelSelector + relative to the given namespace(s)) + that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined + as running on a node whose value + of the label with key + matches that of any node on which + a pod of the set of pods is running + type: object + required: + - topologyKey + properties: + labelSelector: + description: A label query over + a set of resources, in this + case pods. + type: object + properties: + matchExpressions: + description: matchExpressions + is a list of label selector + requirements. The requirements + are ANDed. + type: array + items: + description: A label selector + requirement is a selector + that contains values, + a key, and an operator + that relates the key + and values. + type: object + required: + - key + - operator + properties: + key: + description: key is + the label key that + the selector applies + to. + type: string + operator: + description: operator + represents a key's + relationship to + a set of values. + Valid operators + are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values + is an array of string + values. If the operator + is In or NotIn, + the values array + must be non-empty. + If the operator + is Exists or DoesNotExist, + the values array + must be empty. This + array is replaced + during a strategic + merge patch. + type: array + items: + type: string + matchLabels: + description: matchLabels + is a map of {key,value} + pairs. A single {key,value} + in the matchLabels map + is equivalent to an element + of matchExpressions, whose + key field is "key", the + operator is "In", and + the values array contains + only "value". The requirements + are ANDed. + type: object + additionalProperties: + type: string + namespaces: + description: namespaces specifies + which namespaces the labelSelector + applies to (matches against); + null or empty list means "this + pod's namespace" + type: array + items: + type: string + topologyKey: + description: This pod should + be co-located (affinity) or + not co-located (anti-affinity) + with the pods matching the + labelSelector in the specified + namespaces, where co-located + is defined as running on a + node whose value of the label + with key topologyKey matches + that of any node on which + any of the selected pods is + running. Empty topologyKey + is not allowed. + type: string + nodeSelector: + description: 'NodeSelector is a selector which + must be true for the pod to fit on a node. + Selector which must match a node''s labels + for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + additionalProperties: + type: string + tolerations: + description: If specified, the pod's tolerations. + type: array + items: + description: The pod this Toleration is + attached to tolerates any taint that matches + the triple using the + matching operator . + type: object + properties: + effect: + description: Effect indicates the taint + effect to match. Empty means match + all taint effects. When specified, + allowed values are NoSchedule, PreferNoSchedule + and NoExecute. + type: string + key: + description: Key is the taint key that + the toleration applies to. Empty means + match all taint keys. If the key is + empty, operator must be Exists; this + combination means to match all values + and all keys. + type: string + operator: + description: Operator represents a key's + relationship to the value. Valid operators + are Exists and Equal. Defaults to + Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents + the period of time the toleration + (which must be of effect NoExecute, + otherwise this field is ignored) tolerates + the taint. By default, it is not set, + which means tolerate the taint forever + (do not evict). Zero and negative + values will be treated as 0 (evict + immediately) by the system. + type: integer + format: int64 + value: + description: Value is the taint value + the toleration matches to. If the + operator is Exists, the value should + be empty, otherwise just a regular + string. + type: string + serviceType: + description: Optional service type for Kubernetes + solver service + type: string + selector: + description: Selector selects a set of DNSNames on the Certificate + resource that should be solved using this challenge solver. + type: object + properties: + dnsNames: + description: List of DNSNames that this solver will be + used to solve. If specified and a match is found, a + dnsNames selector will take precedence over a dnsZones + selector. If multiple solvers match with the same dnsNames + value, the solver with the most matching labels in matchLabels + will be selected. If neither has more matches, the solver + defined earlier in the list will be selected. + type: array + items: + type: string + dnsZones: + description: List of DNSZones that this solver will be + used to solve. The most specific DNS zone match specified + here will take precedence over other DNS zone matches, + so a solver specifying sys.example.com will be selected + over one specifying example.com for the domain www.sys.example.com. + If multiple solvers match with the same dnsZones value, + the solver with the most matching labels in matchLabels + will be selected. If neither has more matches, the solver + defined earlier in the list will be selected. + type: array + items: + type: string + matchLabels: + description: A label selector that is used to refine the + set of certificate's that this challenge solver will + apply to. + type: object + additionalProperties: + type: string + ca: + type: object + required: + - secretName + properties: + crlDistributionPoints: + description: The CRL distribution points is an X.509 v3 certificate + extension which identifies the location of the CRL from which + the revocation of this certificate can be checked. If not set + certificate will be issued without CDP. Values are strings. + type: array + items: + type: string + secretName: + description: SecretName is the name of the secret used to sign Certificates + issued by this Issuer. + type: string + selfSigned: + type: object + properties: + crlDistributionPoints: + description: The CRL distribution points is an X.509 v3 certificate + extension which identifies the location of the CRL from which + the revocation of this certificate can be checked. If not set + certificate will be issued without CDP. Values are strings. + type: array + items: + type: string + vault: + type: object + required: + - auth + - path + - server + properties: + auth: + description: Vault authentication + type: object + properties: + appRole: + description: This Secret contains a AppRole and Secret + type: object + required: + - path + - roleId + - secretRef + properties: + path: + description: Where the authentication path is mounted in + Vault. + type: string + roleId: + type: string + secretRef: + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + kubernetes: + description: This contains a Role and Secret with a ServiceAccount + token to authenticate with vault. + type: object + required: + - role + - secretRef + properties: + mountPath: + description: The Vault mountPath here is the mount path + to use when authenticating with Vault. For example, setting + a value to `/v1/auth/foo`, will use the path `/v1/auth/foo/login` + to authenticate with Vault. If unspecified, the default + value "/v1/auth/kubernetes" will be used. + type: string + role: + description: A required field containing the Vault Role + to assume. A Role binds a Kubernetes ServiceAccount with + a set of Vault policies. + type: string + secretRef: + description: The required Secret field containing a Kubernetes + ServiceAccount JWT used for authenticating with Vault. + Use of 'ambient credentials' is not supported. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + tokenSecretRef: + description: This Secret contains the Vault token key + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + caBundle: + description: Base64 encoded CA bundle to validate Vault server certificate. + Only used if the Server URL is using HTTPS protocol. This parameter + is ignored for plain HTTP protocol connection. If not set the + system root certificates are used to validate the TLS connection. + type: string + format: byte + path: + description: Vault URL path to the certificate role + type: string + server: + description: Server is the vault connection address + type: string + venafi: + description: VenafiIssuer describes issuer configuration details for + Venafi Cloud. + type: object + required: + - zone + properties: + cloud: + description: Cloud specifies the Venafi cloud configuration settings. + Only one of TPP or Cloud may be specified. + type: object + required: + - apiTokenSecretRef + properties: + apiTokenSecretRef: + description: APITokenSecretRef is a secret key selector for + the Venafi Cloud API token. + type: object + required: + - name + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + url: + description: URL is the base URL for Venafi Cloud + type: string + tpp: + description: TPP specifies Trust Protection Platform configuration + settings. Only one of TPP or Cloud may be specified. + type: object + required: + - credentialsRef + - url + properties: + caBundle: + description: CABundle is a PEM encoded TLS certificate to use + to verify connections to the TPP instance. If specified, system + roots will not be used and the issuing CA for the TPP instance + must be verifiable using the provided root. If not specified, + the connection will be verified using the cert-manager system + root certificates. + type: string + format: byte + credentialsRef: + description: CredentialsRef is a reference to a Secret containing + the username and password for the TPP server. The secret must + contain two keys, 'username' and 'password'. + type: object + required: + - name + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + url: + description: URL is the base URL for the Venafi TPP instance + type: string + zone: + description: Zone is the Venafi Policy Zone to use for this issuer. + All requests made to the Venafi platform will be restricted by + the named zone policy. This field is required. + type: string + status: + description: IssuerStatus contains status information about an Issuer + type: object + properties: + acme: + type: object + properties: + lastRegisteredEmail: + description: LastRegisteredEmail is the email associated with the + latest registered ACME account, in order to track changes made + to registered account associated with the Issuer + type: string + uri: + description: URI is the unique account identifier, which can also + be used to retrieve account details from the CA + type: string + conditions: + type: array + items: + description: IssuerCondition contains condition information for an + Issuer. + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: LastTransitionTime is the timestamp corresponding + to the last status change of this condition. + type: string + format: date-time + message: + description: Message is a human readable description of the details + of the last transition, complementing reason. + type: string + reason: + description: Reason is a brief machine readable explanation for + the condition's last transition. + type: string + status: + description: Status of the condition, one of ('True', 'False', + 'Unknown'). + type: string + enum: + - "True" + - "False" + - Unknown + type: + description: Type of the condition, currently ('Ready'). + type: string +--- +# Source: cert-manager/templates/templates.regular.out +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: orders.acme.cert-manager.io + annotations: + cert-manager.io/inject-ca-from-secret: 'cert-manager/cert-manager-webhook-ca' + labels: + app: 'cert-manager' + app.kubernetes.io/name: 'cert-manager' + app.kubernetes.io/instance: 'cert-manager' + app.kubernetes.io/managed-by: 'Helm' + helm.sh/chart: 'cert-manager-v0.15.0' +spec: + additionalPrinterColumns: + - JSONPath: .status.state + name: State + type: string + - JSONPath: .spec.issuerRef.name + name: Issuer + priority: 1 + type: string + - JSONPath: .status.reason + name: Reason + priority: 1 + type: string + - JSONPath: .metadata.creationTimestamp + description: CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before order + across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. + name: Age + type: date + group: acme.cert-manager.io + preserveUnknownFields: false + conversion: + # a Webhook strategy instruct API server to call an external webhook for any conversion between custom resources. + strategy: Webhook + # webhookClientConfig is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server. + webhookClientConfig: + service: + namespace: 'cert-manager' + name: 'cert-manager-webhook' + path: /convert + names: + kind: Order + listKind: OrderList + plural: orders + singular: order + scope: Namespaced + subresources: + status: {} + versions: + - name: v1alpha2 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + "validation": + "openAPIV3Schema": + description: Order is a type to represent an Order with an ACME server + type: object + required: + - metadata + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + type: object + required: + - csr + - issuerRef + properties: + commonName: + description: CommonName is the common name as specified on the DER encoded + CSR. If CommonName is not specified, the first DNSName specified will + be used as the CommonName. At least one of CommonName or a DNSNames + must be set. This field must match the corresponding field on the + DER encoded CSR. + type: string + csr: + description: Certificate signing request bytes in DER encoding. This + will be used when finalizing the order. This field must be set on + the order. + type: string + format: byte + dnsNames: + description: DNSNames is a list of DNS names that should be included + as part of the Order validation process. If CommonName is not specified, + the first DNSName specified will be used as the CommonName. At least + one of CommonName or a DNSNames must be set. This field must match + the corresponding field on the DER encoded CSR. + type: array + items: + type: string + issuerRef: + description: IssuerRef references a properly configured ACME-type Issuer + which should be used to create this Order. If the Issuer does not + exist, processing will be retried. If the Issuer is not an 'ACME' + Issuer, an error will be returned and the Order will be marked as + failed. + type: object + required: + - name + properties: + group: + type: string + kind: + type: string + name: + type: string + status: + type: object + properties: + authorizations: + description: Authorizations contains data returned from the ACME server + on what authorizations must be completed in order to validate the + DNS names specified on the Order. + type: array + items: + description: ACMEAuthorization contains data returned from the ACME + server on an authorization that must be completed in order validate + a DNS name on an ACME Order resource. + type: object + required: + - url + properties: + challenges: + description: Challenges specifies the challenge types offered + by the ACME server. One of these challenge types will be selected + when validating the DNS name and an appropriate Challenge resource + will be created to perform the ACME challenge process. + type: array + items: + description: Challenge specifies a challenge offered by the + ACME server for an Order. An appropriate Challenge resource + can be created to perform the ACME challenge process. + type: object + required: + - token + - type + - url + properties: + token: + description: Token is the token that must be presented for + this challenge. This is used to compute the 'key' that + must also be presented. + type: string + type: + description: Type is the type of challenge being offered, + e.g. http-01, dns-01 + type: string + url: + description: URL is the URL of this challenge. It can be + used to retrieve additional metadata about the Challenge + from the ACME server. + type: string + identifier: + description: Identifier is the DNS name to be validated as part + of this authorization + type: string + initialState: + description: InitialState is the initial state of the ACME authorization + when first fetched from the ACME server. If an Authorization + is already 'valid', the Order controller will not create a Challenge + resource for the authorization. This will occur when working + with an ACME server that enables 'authz reuse' (such as Let's + Encrypt's production endpoint). If not set and 'identifier' + is set, the state is assumed to be pending and a Challenge will + be created. + type: string + enum: + - valid + - ready + - pending + - processing + - invalid + - expired + - errored + url: + description: URL is the URL of the Authorization that must be + completed + type: string + wildcard: + description: Wildcard will be true if this authorization is for + a wildcard DNS name. If this is true, the identifier will be + the *non-wildcard* version of the DNS name. For example, if + '*.example.com' is the DNS name being validated, this field + will be 'true' and the 'identifier' field will be 'example.com'. + type: boolean + certificate: + description: Certificate is a copy of the PEM encoded certificate for + this Order. This field will be populated after the order has been + successfully finalized with the ACME server, and the order has transitioned + to the 'valid' state. + type: string + format: byte + failureTime: + description: FailureTime stores the time that this order failed. This + is used to influence garbage collection and back-off. + type: string + format: date-time + finalizeURL: + description: FinalizeURL of the Order. This is used to obtain certificates + for this order once it has been completed. + type: string + reason: + description: Reason optionally provides more information about a why + the order is in the current state. + type: string + state: + description: State contains the current state of this Order resource. + States 'success' and 'expired' are 'final' + type: string + enum: + - valid + - ready + - pending + - processing + - invalid + - expired + - errored + url: + description: URL of the Order. This will initially be empty when the + resource is first created. The Order controller will populate this + field when the Order is first processed. This field will be immutable + after it is initially set. + type: string +--- +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager +--- +# Source: cert-manager/templates/cainjector-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cert-manager-cainjector + namespace: "cert-manager" + labels: + app: cainjector + app.kubernetes.io/name: cainjector + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "cainjector" + helm.sh/chart: cert-manager-v0.15.0 +--- +# Source: cert-manager/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cert-manager + namespace: "cert-manager" + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +--- +# Source: cert-manager/templates/webhook-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cert-manager-webhook + namespace: "cert-manager" + labels: + app: webhook + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "webhook" + helm.sh/chart: cert-manager-v0.15.0 +--- +# Source: cert-manager/templates/cainjector-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: cert-manager-cainjector + labels: + app: cainjector + app.kubernetes.io/name: cainjector + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "cainjector" + helm.sh/chart: cert-manager-v0.15.0 +rules: + - apiGroups: ["cert-manager.io"] + resources: ["certificates"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["events"] + verbs: ["get", "create", "update", "patch"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["apiregistration.k8s.io"] + resources: ["apiservices"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["get", "list", "watch", "update"] + - apiGroups: ["auditregistration.k8s.io"] + resources: ["auditsinks"] + verbs: ["get", "list", "watch", "update"] +--- +# Source: cert-manager/templates/rbac.yaml +# ClusterIssuer controller role +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: cert-manager-controller-clusterissuers + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +rules: + - apiGroups: ["cert-manager.io"] + resources: ["clusterissuers", "clusterissuers/status"] + verbs: ["update"] + - apiGroups: ["cert-manager.io"] + resources: ["clusterissuers"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch", "create", "update", "delete"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +--- +# Source: cert-manager/templates/rbac.yaml +# Issuer controller role +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: cert-manager-controller-issuers + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +rules: + - apiGroups: ["cert-manager.io"] + resources: ["issuers", "issuers/status"] + verbs: ["update"] + - apiGroups: ["cert-manager.io"] + resources: ["issuers"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch", "create", "update", "delete"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +--- +# Source: cert-manager/templates/rbac.yaml +# Challenges controller role +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: cert-manager-controller-challenges + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +rules: + # Use to update challenge resource status + - apiGroups: ["acme.cert-manager.io"] + resources: ["challenges", "challenges/status"] + verbs: ["update"] + # Used to watch challenge resources + - apiGroups: ["acme.cert-manager.io"] + resources: ["challenges"] + verbs: ["get", "list", "watch"] + # Used to watch challenges, issuer and clusterissuer resources + - apiGroups: ["cert-manager.io"] + resources: ["issuers", "clusterissuers"] + verbs: ["get", "list", "watch"] + # Need to be able to retrieve ACME account private key to complete challenges + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch"] + # Used to create events + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] + # HTTP01 rules + - apiGroups: [""] + resources: ["pods", "services"] + verbs: ["get", "list", "watch", "create", "delete"] + - apiGroups: ["extensions"] + resources: ["ingresses"] + verbs: ["get", "list", "watch", "create", "delete", "update"] + # We require the ability to specify a custom hostname when we are creating + # new ingress resources. + # See: https://github.com/openshift/origin/blob/21f191775636f9acadb44fa42beeb4f75b255532/pkg/route/apiserver/admission/ingress_admission.go#L84-L148 + - apiGroups: ["route.openshift.io"] + resources: ["routes/custom-host"] + verbs: ["create"] + # We require these rules to support users with the OwnerReferencesPermissionEnforcement + # admission controller enabled: + # https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement + - apiGroups: ["acme.cert-manager.io"] + resources: ["challenges/finalizers"] + verbs: ["update"] + # DNS01 rules (duplicated above) + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch"] +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cert-manager-edit + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: ["cert-manager.io"] + resources: ["certificates", "certificaterequests", "issuers"] + verbs: ["create", "delete", "deletecollection", "patch", "update"] +--- +# Source: cert-manager/templates/rbac.yaml +# Orders controller role +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: cert-manager-controller-orders + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +rules: + - apiGroups: ["acme.cert-manager.io"] + resources: ["orders", "orders/status"] + verbs: ["update"] + - apiGroups: ["acme.cert-manager.io"] + resources: ["orders", "challenges"] + verbs: ["get", "list", "watch"] + - apiGroups: ["cert-manager.io"] + resources: ["clusterissuers", "issuers"] + verbs: ["get", "list", "watch"] + - apiGroups: ["acme.cert-manager.io"] + resources: ["challenges"] + verbs: ["create", "delete"] + # We require these rules to support users with the OwnerReferencesPermissionEnforcement + # admission controller enabled: + # https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement + - apiGroups: ["acme.cert-manager.io"] + resources: ["orders/finalizers"] + verbs: ["update"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +--- +# Source: cert-manager/templates/rbac.yaml +# ingress-shim controller role +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: cert-manager-controller-ingress-shim + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +rules: + - apiGroups: ["cert-manager.io"] + resources: ["certificates", "certificaterequests"] + verbs: ["create", "update", "delete"] + - apiGroups: ["cert-manager.io"] + resources: ["certificates", "certificaterequests", "issuers", "clusterissuers"] + verbs: ["get", "list", "watch"] + - apiGroups: ["extensions"] + resources: ["ingresses"] + verbs: ["get", "list", "watch"] + # We require these rules to support users with the OwnerReferencesPermissionEnforcement + # admission controller enabled: + # https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement + - apiGroups: ["extensions"] + resources: ["ingresses/finalizers"] + verbs: ["update"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +--- +# Source: cert-manager/templates/rbac.yaml +# Certificates controller role +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: cert-manager-controller-certificates + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +rules: + - apiGroups: ["cert-manager.io"] + resources: ["certificates", "certificates/status", "certificaterequests", "certificaterequests/status"] + verbs: ["update"] + - apiGroups: ["cert-manager.io"] + resources: ["certificates", "certificaterequests", "clusterissuers", "issuers"] + verbs: ["get", "list", "watch"] + # We require these rules to support users with the OwnerReferencesPermissionEnforcement + # admission controller enabled: + # https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement + - apiGroups: ["cert-manager.io"] + resources: ["certificates/finalizers", "certificaterequests/finalizers"] + verbs: ["update"] + - apiGroups: ["acme.cert-manager.io"] + resources: ["orders"] + verbs: ["create", "delete", "get", "list", "watch"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch", "create", "update", "delete"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cert-manager-view + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 + rbac.authorization.k8s.io/aggregate-to-view: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: + - apiGroups: ["cert-manager.io"] + resources: ["certificates", "certificaterequests", "issuers"] + verbs: ["get", "list", "watch"] +--- +# Source: cert-manager/templates/cainjector-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: cert-manager-cainjector + labels: + app: cainjector + app.kubernetes.io/name: cainjector + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "cainjector" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cert-manager-cainjector +subjects: + - name: cert-manager-cainjector + namespace: "cert-manager" + kind: ServiceAccount +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: cert-manager-controller-ingress-shim + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cert-manager-controller-ingress-shim +subjects: + - name: cert-manager + namespace: "cert-manager" + kind: ServiceAccount +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: cert-manager-controller-orders + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cert-manager-controller-orders +subjects: + - name: cert-manager + namespace: "cert-manager" + kind: ServiceAccount +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: cert-manager-controller-challenges + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cert-manager-controller-challenges +subjects: + - name: cert-manager + namespace: "cert-manager" + kind: ServiceAccount +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: cert-manager-controller-issuers + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cert-manager-controller-issuers +subjects: + - name: cert-manager + namespace: "cert-manager" + kind: ServiceAccount +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: cert-manager-controller-certificates + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cert-manager-controller-certificates +subjects: + - name: cert-manager + namespace: "cert-manager" + kind: ServiceAccount +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: cert-manager-controller-clusterissuers + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cert-manager-controller-clusterissuers +subjects: + - name: cert-manager + namespace: "cert-manager" + kind: ServiceAccount +--- +# Source: cert-manager/templates/cainjector-rbac.yaml +# leader election rules +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: Role +metadata: + name: cert-manager-cainjector:leaderelection + namespace: kube-system + labels: + app: cainjector + app.kubernetes.io/name: cainjector + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "cainjector" + helm.sh/chart: cert-manager-v0.15.0 +rules: + # Used for leader election by the controller + # cert-manager-cainjector-leader-election is used by the CertificateBased injector controller + # see cmd/cainjector/start.go#L113 + # cert-manager-cainjector-leader-election-core is used by the SecretBased injector controller + # see cmd/cainjector/start.go#L137 + - apiGroups: [""] + resources: ["configmaps"] + resourceNames: ["cert-manager-cainjector-leader-election", "cert-manager-cainjector-leader-election-core"] + verbs: ["get", "update", "patch"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create"] +--- +# Source: cert-manager/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: Role +metadata: + name: cert-manager:leaderelection + namespace: kube-system + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +rules: + # Used for leader election by the controller + - apiGroups: [""] + resources: ["configmaps"] + resourceNames: ["cert-manager-controller"] + verbs: ["get", "update", "patch"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create"] +--- +# Source: cert-manager/templates/webhook-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: Role +metadata: + name: cert-manager-webhook:dynamic-serving + namespace: "cert-manager" + labels: + app: webhook + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "webhook" + helm.sh/chart: cert-manager-v0.15.0 +rules: +- apiGroups: [""] + resources: ["secrets"] + resourceNames: + - 'cert-manager-webhook-ca' + verbs: ["get", "list", "watch", "update"] +# It's not possible to grant CREATE permission on a single resourceName. +- apiGroups: [""] + resources: ["secrets"] + verbs: ["create"] +--- +# Source: cert-manager/templates/cainjector-rbac.yaml +# grant cert-manager permission to manage the leaderelection configmap in the +# leader election namespace +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: cert-manager-cainjector:leaderelection + namespace: kube-system + labels: + app: cainjector + app.kubernetes.io/name: cainjector + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "cainjector" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cert-manager-cainjector:leaderelection +subjects: + - kind: ServiceAccount + name: cert-manager-cainjector + namespace: cert-manager +--- +# Source: cert-manager/templates/rbac.yaml +# grant cert-manager permission to manage the leaderelection configmap in the +# leader election namespace +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: cert-manager:leaderelection + namespace: kube-system + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cert-manager:leaderelection +subjects: + - apiGroup: "" + kind: ServiceAccount + name: cert-manager + namespace: cert-manager +--- +# Source: cert-manager/templates/webhook-rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: cert-manager-webhook:dynamic-serving + namespace: "cert-manager" + labels: + app: webhook + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "webhook" + helm.sh/chart: cert-manager-v0.15.0 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cert-manager-webhook:dynamic-serving +subjects: +- apiGroup: "" + kind: ServiceAccount + name: cert-manager-webhook + namespace: cert-manager +--- +# Source: cert-manager/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: cert-manager + namespace: "cert-manager" + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +spec: + type: ClusterIP + ports: + - protocol: TCP + port: 9402 + targetPort: 9402 + selector: + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/component: "controller" +--- +# Source: cert-manager/templates/webhook-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: cert-manager-webhook + namespace: "cert-manager" + labels: + app: webhook + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "webhook" + helm.sh/chart: cert-manager-v0.15.0 +spec: + type: ClusterIP + ports: + - name: https + port: 443 + targetPort: 10250 + selector: + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/component: "webhook" +--- +# Source: cert-manager/templates/cainjector-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cert-manager-cainjector + namespace: "cert-manager" + labels: + app: cainjector + app.kubernetes.io/name: cainjector + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "cainjector" + helm.sh/chart: cert-manager-v0.15.0 +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: cainjector + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/component: "cainjector" + template: + metadata: + labels: + app: cainjector + app.kubernetes.io/name: cainjector + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "cainjector" + helm.sh/chart: cert-manager-v0.15.0 + spec: + serviceAccountName: cert-manager-cainjector + containers: + - name: cert-manager + image: "quay.io/jetstack/cert-manager-cainjector:v0.15.0" + imagePullPolicy: IfNotPresent + args: + - --v=2 + - --leader-election-namespace=kube-system + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + {} +--- +# Source: cert-manager/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cert-manager + namespace: "cert-manager" + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "controller" + helm.sh/chart: cert-manager-v0.15.0 +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/component: "controller" + template: + metadata: + labels: + app: cert-manager + app.kubernetes.io/name: cert-manager + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/component: "controller" + app.kubernetes.io/managed-by: Helm + helm.sh/chart: cert-manager-v0.15.0 + annotations: + prometheus.io/path: "/metrics" + prometheus.io/scrape: 'true' + prometheus.io/port: '9402' + spec: + serviceAccountName: cert-manager + containers: + - name: cert-manager + image: "quay.io/jetstack/cert-manager-controller:v0.15.0" + imagePullPolicy: IfNotPresent + args: + - --v=2 + - --cluster-resource-namespace=$(POD_NAMESPACE) + - --leader-election-namespace=kube-system + ports: + - containerPort: 9402 + protocol: TCP + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + {} +--- +# Source: cert-manager/templates/webhook-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cert-manager-webhook + namespace: "cert-manager" + labels: + app: webhook + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "webhook" + helm.sh/chart: cert-manager-v0.15.0 +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/component: "webhook" + template: + metadata: + labels: + app: webhook + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "webhook" + helm.sh/chart: cert-manager-v0.15.0 + spec: + serviceAccountName: cert-manager-webhook + containers: + - name: cert-manager + image: "quay.io/jetstack/cert-manager-webhook:v0.15.0" + imagePullPolicy: IfNotPresent + args: + - --v=2 + - --secure-port=10250 + - --dynamic-serving-ca-secret-namespace=cert-manager + - --dynamic-serving-ca-secret-name=cert-manager-webhook-ca + - --dynamic-serving-dns-names=cert-manager-webhook,cert-manager-webhook.cert-manager,cert-manager-webhook.cert-manager.svc + ports: + - name: https + containerPort: 10250 + livenessProbe: + httpGet: + path: /livez + port: 6080 + scheme: HTTP + initialDelaySeconds: 60 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /healthz + port: 6080 + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 5 + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + {} +--- +# Source: cert-manager/templates/webhook-mutating-webhook.yaml +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: cert-manager-webhook + labels: + app: webhook + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "webhook" + helm.sh/chart: cert-manager-v0.15.0 + annotations: + cert-manager.io/inject-ca-from-secret: "cert-manager/cert-manager-webhook-ca" +webhooks: + - name: webhook.cert-manager.io + rules: + - apiGroups: + - "cert-manager.io" + - "acme.cert-manager.io" + apiVersions: + - v1alpha2 + - v1alpha3 + operations: + - CREATE + - UPDATE + resources: + - "*/*" + failurePolicy: Fail + # Only include 'sideEffects' field in Kubernetes 1.12+ + sideEffects: None + clientConfig: + service: + name: cert-manager-webhook + namespace: "cert-manager" + path: /mutate +--- +# Source: cert-manager/templates/webhook-validating-webhook.yaml +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: cert-manager-webhook + labels: + app: webhook + app.kubernetes.io/name: webhook + app.kubernetes.io/instance: cert-manager + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "webhook" + helm.sh/chart: cert-manager-v0.15.0 + annotations: + cert-manager.io/inject-ca-from-secret: "cert-manager/cert-manager-webhook-ca" +webhooks: + - name: webhook.cert-manager.io + namespaceSelector: + matchExpressions: + - key: "cert-manager.io/disable-validation" + operator: "NotIn" + values: + - "true" + - key: "name" + operator: "NotIn" + values: + - cert-manager + rules: + - apiGroups: + - "cert-manager.io" + - "acme.cert-manager.io" + apiVersions: + - v1alpha2 + - v1alpha3 + operations: + - CREATE + - UPDATE + resources: + - "*/*" + failurePolicy: Fail + # Only include 'sideEffects' field in Kubernetes 1.12+ + sideEffects: None + clientConfig: + service: + name: cert-manager-webhook + namespace: "cert-manager" + path: /validate diff --git a/deploy/crds/organization.yaml b/deploy/crds/organization.yaml new file mode 100644 index 0000000..3e1f9c4 --- /dev/null +++ b/deploy/crds/organization.yaml @@ -0,0 +1,56 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: organizations.k8spin.cloud +spec: + scope: Cluster + group: k8spin.cloud + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: + - spec + properties: + spec: + type: object + required: + - resources + properties: + roles: + type: array + items: + type: object + properties: + name: + type: string + groups: + type: array + items: + type: string + users: + type: array + items: + type: string + required: + - name + resources: + type: object + required: + - cpu + - memory + properties: + cpu: + type: string + memory: + type: string + names: + kind: Organization + plural: organizations + singular: organization + shortNames: + - orgs + - org \ No newline at end of file diff --git a/deploy/crds/space.yaml b/deploy/crds/space.yaml new file mode 100644 index 0000000..c74de29 --- /dev/null +++ b/deploy/crds/space.yaml @@ -0,0 +1,77 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: spaces.k8spin.cloud +spec: + scope: Namespaced + group: k8spin.cloud + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: + - spec + properties: + spec: + type: object + required: + - resources + - containers + properties: + roles: + type: array + items: + type: object + properties: + name: + type: string + groups: + type: array + items: + type: string + users: + type: array + items: + type: string + required: + - name + resources: + type: object + required: + - cpu + - memory + properties: + cpu: + type: string + memory: + type: string + containers: + type: object + required: + - defaults + properties: + defaults: + type: object + required: + - resources + properties: + resources: + type: object + required: + - cpu + - memory + properties: + cpu: + type: string + memory: + type: string + names: + kind: Space + plural: spaces + singular: space + shortNames: + - spcs + - sp \ No newline at end of file diff --git a/deploy/crds/tenant.yaml b/deploy/crds/tenant.yaml new file mode 100644 index 0000000..af68182 --- /dev/null +++ b/deploy/crds/tenant.yaml @@ -0,0 +1,56 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: tenants.k8spin.cloud +spec: + scope: Namespaced + group: k8spin.cloud + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + required: + - spec + properties: + spec: + type: object + required: + - resources + properties: + roles: + type: array + items: + type: object + properties: + name: + type: string + groups: + type: array + items: + type: string + users: + type: array + items: + type: string + required: + - name + resources: + type: object + required: + - cpu + - memory + properties: + cpu: + type: string + memory: + type: string + names: + kind: Tenant + plural: tenants + singular: tenant + shortNames: + - tens + - ten \ No newline at end of file diff --git a/deploy/operator.yaml b/deploy/operator.yaml new file mode 100644 index 0000000..6958f05 --- /dev/null +++ b/deploy/operator.yaml @@ -0,0 +1,46 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: k8spin-operator + namespace: default +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + application: k8spin-operator + template: + metadata: + labels: + application: k8spin-operator + spec: + serviceAccountName: k8spin-operator + containers: + - name: k8spin-operator + image: k8spin/k8spin-operator:dev + env: + - name: LOGGING_LEVEL + value: DEBUG + - name: RECONCILIATION_INTERVAL_SECONDS + value: "15" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: k8spin-operator + namespace: default +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: k8spin-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: k8spin-operator + namespace: default diff --git a/deploy/roles/roles.yaml b/deploy/roles/roles.yaml new file mode 100644 index 0000000..03dbc1e --- /dev/null +++ b/deploy/roles/roles.yaml @@ -0,0 +1,284 @@ +# K8Spin Included roles +## Namespaces +### namespace-viewer: Can query all namespaces +## Organizations +### organization-viewer: Can query tenants in a specific organization +### organization-admin: Can query and manage tenants in a specific organization +## Tenants +### tenant-viewer: Can query spaces in a specific tenant +### tenant-admin: Can query and manage spaces in a specific tenant +## Spaces +### space-viewer: Can readonly everything in a space +### space-admin: Can everything inside a space +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: namespace-viewer + labels: + application: k8spin-operator +rules: +- apiGroups: [""] + resources: + - namespaces + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: organization-viewer + labels: + application: k8spin-operator +rules: +- apiGroups: ["k8spin.cloud"] + resources: + - tenants + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: organization-admin + labels: + application: k8spin-operator +rules: +- apiGroups: ["k8spin.cloud"] + resources: + - tenants + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: tenant-viewer + labels: + application: k8spin-operator +rules: +- apiGroups: ["k8spin.cloud"] + resources: + - spaces + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: tenant-admin + labels: + application: k8spin-operator +rules: +- apiGroups: ["k8spin.cloud"] + resources: + - spaces + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: space-viewer + labels: + application: k8spin-operator +rules: +- apiGroups: [""] + resources: + - bindings + - configmaps + - endpoints + - events + - limitranges + - persistentvolumeclaims + - pods + - podtemplates + - replicationcontrollers + - resourcequotas + - secrets + - serviceaccounts + - services + verbs: + - get + - list + - watch +- apiGroups: ["acme.cert-manager.io"] + resources: + - challenges + - orders + verbs: + - get + - list + - watch +- apiGroups: ["apps"] + resources: + - controllerrevisions + - daemonsets + - deployments + - replicasets + - statefulsets + verbs: + - get + - list + - watch +- apiGroups: ["authorization.k8s.io"] + resources: + - localsubjectaccessreviews + verbs: + - get + - list + - watch +- apiGroups: ["autoscaling"] + resources: + - horizontalpodautoscalers + verbs: + - get + - list + - watch +- apiGroups: ["batch"] + resources: + - cronjobs + - jobs + verbs: + - get + - list + - watch +- apiGroups: ["cert-manager.io"] + resources: + - certificaterequests + - certificates + - issuers + verbs: + - get + - list + - watch +- apiGroups: ["coordination.k8s.io"] + resources: + - leases + verbs: + - get + - list + - watch +- apiGroups: ["discovery.k8s.io"] + resources: + - endpointslices + verbs: + - get + - list + - watch +- apiGroups: ["events.k8s.io"] + resources: + - events + verbs: + - get + - list + - watch +- apiGroups: ["extensions", "networking.k8s.io"] + resources: + - ingresses + - networkpolicies + verbs: + - get + - list + - watch +- apiGroups: ["policy"] + resources: + - poddisruptionbudgets + verbs: + - get + - list + - watch +- apiGroups: ["rbac.authorization.k8s.io"] + resources: + - rolebindings + - roles + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: space-admin + labels: + application: k8spin-operator +rules: +- apiGroups: [""] + resources: + - bindings + - configmaps + - endpoints + - events + - limitranges + - persistentvolumeclaims + - pods + - podtemplates + - replicationcontrollers + - resourcequotas + - secrets + - serviceaccounts + - services + verbs: ["*"] +- apiGroups: ["acme.cert-manager.io"] + resources: + - challenges + - orders + verbs: ["*"] +- apiGroups: ["apps"] + resources: + - controllerrevisions + - daemonsets + - deployments + - replicasets + - statefulsets + verbs: ["*"] +- apiGroups: ["authorization.k8s.io"] + resources: + - localsubjectaccessreviews + verbs: ["*"] +- apiGroups: ["autoscaling"] + resources: + - horizontalpodautoscalers + verbs: ["*"] +- apiGroups: ["batch"] + resources: + - cronjobs + - jobs + verbs: ["*"] +- apiGroups: ["cert-manager.io"] + resources: + - certificaterequests + - certificates + - issuers + verbs: ["*"] +- apiGroups: ["coordination.k8s.io"] + resources: + - leases + verbs: ["*"] +- apiGroups: ["discovery.k8s.io"] + resources: + - endpointslices + verbs: ["*"] +- apiGroups: ["events.k8s.io"] + resources: + - events + verbs: ["*"] +- apiGroups: ["extensions", "networking.k8s.io"] + resources: + - ingresses + - networkpolicies + verbs: ["*"] +- apiGroups: ["policy"] + resources: + - poddisruptionbudgets + verbs: ["*"] +- apiGroups: ["rbac.authorization.k8s.io"] + resources: + - rolebindings + - roles + verbs: ["*"] diff --git a/deploy/webhook.yaml b/deploy/webhook.yaml new file mode 100644 index 0000000..fab38ec --- /dev/null +++ b/deploy/webhook.yaml @@ -0,0 +1,209 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: k8spin-webhook + namespace: default +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + application: k8spin-webhook + template: + metadata: + labels: + application: k8spin-webhook + spec: + serviceAccountName: k8spin-webhook + containers: + - name: k8spin-webhook + image: k8spin/k8spin-webhook:dev + ports: + - containerPort: 443 + env: + - name: LOGGING_LEVEL + value: DEBUG + volumeMounts: + - name: tls + mountPath: "/certs" + readinessProbe: + httpGet: + path: /health + port: 443 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 3 + timeoutSeconds: 1 + successThreshold: 2 + failureThreshold: 1 + livenessProbe: + tcpSocket: + port: 443 + initialDelaySeconds: 5 + periodSeconds: 3 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 1 + volumes: + - name: tls + secret: + secretName: k8spin-webhook +--- +apiVersion: v1 +kind: Service +metadata: + name: k8spin-webhook + namespace: default +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 443 + selector: + application: k8spin-webhook +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: k8spin-webhook + namespace: default +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: k8spin-webhook +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: k8spin-webhook + namespace: default +--- +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: k8spin-webhook-cert + namespace: default +spec: + dnsNames: + - k8spin-webhook.default.svc + - k8spin-webhook.default.svc.cluster.local + issuerRef: + kind: Issuer + name: k8spin-webhook-issuer + secretName: k8spin-webhook +--- +apiVersion: cert-manager.io/v1alpha2 +kind: Issuer +metadata: + name: k8spin-webhook-issuer + namespace: default +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: default/k8spin-webhook-cert + name: validating-k8spin + namespace: default +webhooks: + - name: validating.tenants.k8spin.cloud + failurePolicy: Fail + clientConfig: + service: + name: k8spin-webhook + namespace: default + path: /validator/tenants + caBundle: Cg== + rules: + - apiGroups: ["k8spin.cloud"] + resources: + - "tenants" + apiVersions: + - "v1" + operations: + - CREATE + - UPDATE + - name: validating.spaces.k8spin.cloud + failurePolicy: Fail + clientConfig: + service: + name: k8spin-webhook + namespace: default + path: /validator/spaces + caBundle: Cg== + rules: + - apiGroups: ["k8spin.cloud"] + resources: + - "spaces" + apiVersions: + - "v1" + operations: + - CREATE + - UPDATE +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: default/k8spin-webhook-cert + name: mutating-k8spin + namespace: default +webhooks: + - name: mutating.organizations.k8spin.cloud + failurePolicy: Fail + clientConfig: + service: + name: k8spin-webhook + namespace: default + path: /mutator/organizations + caBundle: Cg== + rules: + - apiGroups: ["k8spin.cloud"] + resources: + - "organizations" + apiVersions: + - "v1" + operations: + - CREATE + - UPDATE + - name: mutating.tenants.k8spin.cloud + failurePolicy: Fail + clientConfig: + service: + name: k8spin-webhook + namespace: default + path: /mutator/tenants + caBundle: Cg== + rules: + - apiGroups: ["k8spin.cloud"] + resources: + - "tenants" + apiVersions: + - "v1" + operations: + - CREATE + - UPDATE + - name: mutating.spaces.k8spin.cloud + failurePolicy: Fail + clientConfig: + service: + name: k8spin-webhook + namespace: default + path: /mutator/spaces + caBundle: Cg== + rules: + - apiGroups: ["k8spin.cloud"] + resources: + - "spaces" + apiVersions: + - "v1" + operations: + - CREATE + - UPDATE \ No newline at end of file diff --git a/dockerfiles/Dockerfile-operator b/dockerfiles/Dockerfile-operator new file mode 100644 index 0000000..8a34f51 --- /dev/null +++ b/dockerfiles/Dockerfile-operator @@ -0,0 +1,12 @@ +FROM python:3.8 + +COPY k8spin_operator/requirements.txt /src/requirements.txt +RUN pip install -r /src/requirements.txt + +COPY k8spin_common /src/k8spin_common +RUN pip install -e /src/k8spin_common + +COPY k8spin_operator /app/k8spin_operator + +ENTRYPOINT [ "kopf", "run" ] +CMD ["/app/k8spin_operator/operator.py"] diff --git a/dockerfiles/Dockerfile-webhook b/dockerfiles/Dockerfile-webhook new file mode 100644 index 0000000..d96d69d --- /dev/null +++ b/dockerfiles/Dockerfile-webhook @@ -0,0 +1,16 @@ +FROM python:3.8 + +COPY k8spin_webhook/requirements.txt /src/requirements.txt +RUN pip install -r /src/requirements.txt + +COPY k8spin_common /src/k8spin_common +RUN pip install -e /src/k8spin_common + +COPY k8spin_webhook /app/k8spin_webhook + +EXPOSE 443 + +VOLUME ["/certs"] + +ENTRYPOINT [ "python" ] +CMD ["/app/k8spin_webhook/app.py"] diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..38b2e40 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,5 @@ +# Docs + +## Index + +- [use-cases](./use-cases.md) \ No newline at end of file diff --git a/docs/use-cases.md b/docs/use-cases.md new file mode 100644 index 0000000..1f68fd0 --- /dev/null +++ b/docs/use-cases.md @@ -0,0 +1,57 @@ +# Use cases + +## Single Organization cluster + +The is the example we had in K8Spin.cloud: + +Organizations: + - K8Spin.cloud + +Tenants at `K8Spin.cloud` **Organization**: + - angel@k8spin.cloud + - pau@k8spin.cloud + - bill@microsoft.com + +Spaces at `angel@k8spin.cloud` **tenant** under `K8Spin.cloud` **organization**: + - demo-app-1 + +Spaces at `pau@k8spin.cloud` **tenant** under `K8Spin.cloud` **organization**: + - single-project + + +## Multiple Organization cluster + +awesomevps.supercloud VPS provider wants to provide access to a managed Kubernetes cluster to multiple organizations. + +Organizations: + - prof-services-dot-com (10 cores, 10Gb) + - awesome-startup-dot-io (5 cores, 5Gb) + +Tenants at `prof-services-dot-com` **Organization**: + - team-red (3 cores, 5Gb) + - team-blue (3 cores, 3Gb) + +Tenants at `awesome-startup-dot-io` **Organization**: + - frontend-team (2 cores, 2Gb) + - backend-team (3 cores, 3Gb) + +Spaces at `team-red` **tenant** under `prof-services-dot-com` **organization**: + - demo-frontend-january (1 core, 1Gb) + - new-feature-frontend (1 core, 2Gb) + +Spaces at `backend-team` **tenant** under `awesome-startup-dot-io` **organization**: + - poc-kafka (2 cores, 2Gb) + - fixture-two (0.5 core, 0.5Gb) + +Some notes about this Cluster: + +- **awesome-startup-dot-io** organization admin can not create more tenants as cluster admin +defined the Organization with 5 cores and 5 Gb and it currently has 2 two tenants with 2+3 cores and 2+3 Gb. + +- **prof-services-dot-com** organization admin can create more tenants because the current +organization utilization is 2+3cores and 5+3 Gb. So the current utilization is: 6/10 cores and 8/10 Gb. + +- **team-red** tenant admin (in the **prof-services-dot-com** organization) can create more spaces because +tenant has 3 cores and 5Gb allocated, and the current usage is: 1+1 core and 1+2Gb. A new Space +up to 1 core and 2 Gb can be created under **team-red** tenant. + diff --git a/example-cr/org-1.yaml b/example-cr/org-1.yaml new file mode 100644 index 0000000..bf9d77f --- /dev/null +++ b/example-cr/org-1.yaml @@ -0,0 +1,14 @@ +apiVersion: k8spin.cloud/v1 +kind: Organization +metadata: + name: example +spec: + resources: + cpu: "10" + memory: "10G" + # By default the organization name could be used to access the organization if the certificate include the name in the certificate filed O= + roles: + - name: organization-admin # Cluster Role + groups: ["K8Spin.cloud"] # User Certificate O= + - name: organization-admin # Cluster Role + users: ["Angel", "Pau"] # User Certificate CN= diff --git a/example-cr/roles/org.yaml b/example-cr/roles/org.yaml new file mode 100644 index 0000000..300e6f0 --- /dev/null +++ b/example-cr/roles/org.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: k8spin.cloud/v1 +kind: Organization +metadata: + name: test-2 +spec: + roles: + - name: organization-admin + groups: ["k8spin"] + resources: + cpu: "10" + memory: "10G" diff --git a/example-cr/roles/spaces.yaml b/example-cr/roles/spaces.yaml new file mode 100644 index 0000000..2deb35f --- /dev/null +++ b/example-cr/roles/spaces.yaml @@ -0,0 +1,40 @@ +--- +apiVersion: k8spin.cloud/v1 +kind: Space +metadata: + namespace: org-test-2-tenant-las-rozas + name: app-1 +spec: + roles: + - name: space-admin + users: ["pau"] + - name: space-viewer + users: ["angel"] + resources: + cpu: "5" + memory: "5G" + containers: + defaults: + resources: + cpu: "10m" + memory: "32Mi" +--- +apiVersion: k8spin.cloud/v1 +kind: Space +metadata: + namespace: org-test-2-tenant-getafe + name: app-1 +spec: + roles: + - name: space-viewer + users: ["pau"] + - name: space-admin + users: ["angel"] + resources: + cpu: "5" + memory: "5G" + containers: + defaults: + resources: + cpu: "10m" + memory: "32Mi" \ No newline at end of file diff --git a/example-cr/roles/tenants.yaml b/example-cr/roles/tenants.yaml new file mode 100644 index 0000000..dd618da --- /dev/null +++ b/example-cr/roles/tenants.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: k8spin.cloud/v1 +kind: Tenant +metadata: + namespace: org-test-2 + name: las-rozas +spec: + roles: + - name: tenant-admin + users: ["pau"] + - name: tenant-viewer + users: ["angel"] + resources: + cpu: "5" + memory: "5G" +--- +apiVersion: k8spin.cloud/v1 +kind: Tenant +metadata: + namespace: org-test-2 + name: getafe +spec: + roles: + - name: tenant-viewer + users: ["pau"] + - name: tenant-admin + users: ["angel"] + resources: + cpu: "5" + memory: "5G" diff --git a/example-cr/space-1.yaml b/example-cr/space-1.yaml new file mode 100644 index 0000000..2fc14fa --- /dev/null +++ b/example-cr/space-1.yaml @@ -0,0 +1,19 @@ +apiVersion: k8spin.cloud/v1 +kind: Space +metadata: + namespace: org-example-tenant-crm + name: dev +spec: + resources: + cpu: "2" + memory: "1G" + roles: + - name: space-admin # Cluster Role + groups: ["K8Spin.cloud"] # User Certificate O= + - name: space-viewer # Cluster Role + users: ["Angel", "Pau"] # User Certificate CN= + containers: + defaults: + resources: + cpu: 10m + memory: 64Mi diff --git a/example-cr/space-2.yaml b/example-cr/space-2.yaml new file mode 100644 index 0000000..a89117b --- /dev/null +++ b/example-cr/space-2.yaml @@ -0,0 +1,29 @@ +apiVersion: k8spin.cloud/v1 +kind: Space +metadata: + namespace: org-example-tenant-crm + name: int +spec: + resources: + cpu: "2" + memory: "1G" + containers: + defaults: + resources: + cpu: 10m + memory: 32Mi +--- +apiVersion: k8spin.cloud/v1 +kind: Space +metadata: + namespace: org-example-tenant-crm + name: pro +spec: + resources: + cpu: "3" + memory: "2G" + containers: + defaults: + resources: + cpu: 10m + memory: 32Mi \ No newline at end of file diff --git a/example-cr/space-bad.yaml b/example-cr/space-bad.yaml new file mode 100644 index 0000000..4179d82 --- /dev/null +++ b/example-cr/space-bad.yaml @@ -0,0 +1,5 @@ +apiVersion: k8spin.cloud/v1 +kind: Space +metadata: + namespace: default + name: platform-pro diff --git a/example-cr/tenant-1-bad.yaml b/example-cr/tenant-1-bad.yaml new file mode 100644 index 0000000..aabb6d1 --- /dev/null +++ b/example-cr/tenant-1-bad.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: org-example-meh +--- +apiVersion: k8spin.cloud/v1 +kind: Tenant +metadata: + namespace: org-example-meh + name: example-crm diff --git a/example-cr/tenant-1.yaml b/example-cr/tenant-1.yaml new file mode 100644 index 0000000..60dab5b --- /dev/null +++ b/example-cr/tenant-1.yaml @@ -0,0 +1,14 @@ +apiVersion: k8spin.cloud/v1 +kind: Tenant +metadata: + namespace: org-example + name: crm +spec: + resources: + cpu: "3" + memory: "3G" + roles: + - name: tenant-admin # Cluster Role + groups: ["K8Spin.cloud"] # User Certificate O= + - name: tenant-viewer # Cluster Role + users: ["Angel", "Pau"] # User Certificate CN= diff --git a/k8spin_common/k8spin_common/__init__.py b/k8spin_common/k8spin_common/__init__.py new file mode 100644 index 0000000..3094ba0 --- /dev/null +++ b/k8spin_common/k8spin_common/__init__.py @@ -0,0 +1,97 @@ +from pykube.objects import APIObject, Namespace, NamespacedAPIObject + + +class Organization(APIObject): + + version = "k8spin.cloud/v1" + endpoint = "organizations" + kind = "Organization" + + @property + def organization_namespace(self) -> Namespace: + return Namespace.objects(self.api).get(name="org-"+self.name) + + @property + def roles(self) -> list: + return self.obj["spec"].get("roles", list()) + + @property + def tenants(self) -> list: + ns = self.organization_namespace + return Tenant.objects(self.api, ns.name).all() + + +class Tenant(NamespacedAPIObject): + + version = "k8spin.cloud/v1" + endpoint = "tenants" + kind = "Tenant" + + @property + def org(self) -> Organization: + namespace = Namespace.objects( + self.api).get(name=self.namespace) + org_name = namespace.labels["k8spin.cloud/org"] + return Organization.objects(self.api).get(name=org_name) + + @property + def tenant_namespace(self) -> Namespace: + namespaces = Namespace.objects(self.api).filter( + selector={"k8spin.cloud/org": self.org.name, "k8spin.cloud/type": "tenant"}) + for namespace in namespaces: + if any([owner.get("name") == self.name for owner in namespace.metadata.get("ownerReferences", list())]): + return namespace + # TODO CHANGE WITH K8SPIN EXCEPTIONS + raise Exception("Tenant namespace not found") + + @property + def roles(self) -> list: + return self.obj["spec"].get("roles", list()) + + @property + def spaces(self) -> list: + ns = self.tenant_namespace + return Space.objects(self.api, ns.name).all() + +class Space(NamespacedAPIObject): + + version = "k8spin.cloud/v1" + endpoint = "spaces" + kind = "Space" + + @property + def org(self) -> Organization: + namespace = Namespace.objects( + self.api).get(name=self.namespace) + org_name = namespace.labels["k8spin.cloud/org"] + return Organization.objects(self.api).get(name=org_name) + + @property + def tenant(self) -> Tenant: + namespace = Namespace.objects( + self.api).get(name=self.namespace) + tenant_name = namespace.labels["k8spin.cloud/tenant"] + tenant_namespace = self.org.organization_namespace + return Tenant.objects(self.api, tenant_namespace.name).get(name=tenant_name) + + @property + def space_namespace(self) -> Namespace: + namespaces = Namespace.objects(self.api).filter( + selector={"k8spin.cloud/org": self.org.name, "k8spin.cloud/tenant": self.tenant.name, "k8spin.cloud/type": "space"}) + for namespace in namespaces: + if any([owner.get("name") == self.name for owner in namespace.metadata.get("ownerReferences", list())]): + return namespace + # TODO CHANGE WITH K8SPIN EXCEPTIONS + raise Exception("Space namespace not found") + + @property + def roles(self) -> list: + return self.obj["spec"].get("roles", list()) + + @property + def resources(self) -> list: + return self.obj["spec"]["resources"] + + @property + def default_container_resources(self) -> list: + return self.obj["spec"]["containers"]["defaults"]["resources"] diff --git a/k8spin_common/k8spin_common/helper.py b/k8spin_common/k8spin_common/helper.py new file mode 100644 index 0000000..af6c5df --- /dev/null +++ b/k8spin_common/k8spin_common/helper.py @@ -0,0 +1,46 @@ +import os + +import pykube +from pykube.objects import APIObject, NamespacedAPIObject + + +def kubernetes_api(function): + def wrap_function(*args, **kwargs): + api = _get_kube_api() + res = function(api, *args, **kwargs) + api.session.close() + return res + return wrap_function + + +def adopt(owner: APIObject, children: APIObject) -> APIObject: + if not children.metadata.get("ownerReferences", None): + children.metadata["ownerReferences"] = list() + ownerReference = { + "apiVersion": owner.version, + "kind": owner.__class__.__name__, + "name": owner.name, + "uid": owner.metadata["uid"] + } + if not any(child_owner.get("uid") == owner.metadata["uid"] for child_owner in children.metadata["ownerReferences"]): + children.metadata["ownerReferences"].append(ownerReference) + return children + + +def _get_kube_api(): + try: + config = pykube.KubeConfig.from_service_account() + except FileNotFoundError: + config = pykube.KubeConfig.from_file( + os.getenv("KUBECONFIG", "~/.kube/config")) + api = pykube.HTTPClient(config) + return api + + +def ensure(resource: APIObject, owner: APIObject): + if not resource.exists(): + resource = adopt(owner, resource) + resource.create() + else: + resource.update() + return resource \ No newline at end of file diff --git a/k8spin_common/k8spin_common/resources/namespace.py b/k8spin_common/k8spin_common/resources/namespace.py new file mode 100644 index 0000000..e4ce9e3 --- /dev/null +++ b/k8spin_common/k8spin_common/resources/namespace.py @@ -0,0 +1,19 @@ +import pykube +import yaml + +from k8spin_common.helper import kubernetes_api + + +@kubernetes_api +def create_namespace(api, name: str, labels: dict) -> pykube.Namespace: + _obj = yaml.safe_load( + f""" + apiVersion: v1 + kind: Namespace + metadata: + name: {name} + """ + ) + metadata = _obj.get("metadata") + metadata["labels"] = labels + return pykube.Namespace(api, _obj) diff --git a/k8spin_common/k8spin_common/resources/organization.py b/k8spin_common/k8spin_common/resources/organization.py new file mode 100644 index 0000000..dc85142 --- /dev/null +++ b/k8spin_common/k8spin_common/resources/organization.py @@ -0,0 +1,89 @@ +import pykube + +import k8spin_common +from k8spin_common.helper import adopt, ensure, kubernetes_api +from k8spin_common.resources.namespace import create_namespace +from k8spin_common.resources.rbac import (create_cluster_role_binding, + create_role_binding) + +ORG_NAMESPACE_PREFIX = "org-" + + +def organization_namespacename_generator(organization_name): + return f"{ORG_NAMESPACE_PREFIX}{organization_name}" + + +def ensure_organization_resources(organization_name: str): + ensure_organization_namespace(organization_name) + ensure_organization_role_bindings(organization_name) + + +@kubernetes_api +def ensure_organization_namespace(api, organization_name: str): + org_namespace_name = organization_namespacename_generator( + organization_name=organization_name) + labels = { + "k8spin.cloud/type": "organization", + "k8spin.cloud/org": organization_name + } + owner = get_organization(organization_name) + org_namespace = create_namespace(org_namespace_name, labels) + org_namespace = ensure(org_namespace, owner) + return org_namespace + + +@kubernetes_api +def ensure_organization_role_bindings(api, organization_name: str): + organization = get_organization(organization_name) + namespace = organization.organization_namespace + roles = organization.roles + rolebindings_names = list() + # Organization level permissions + for role in roles: + # Cluster role to assign + name = role.get('name') + target_kind = "Group" + targets = role.get('groups', None) + if not targets: + target_kind = "User" + targets = role.get('users', list()) + for target in targets: + rolebinding_name = f"{organization_name}-{name}-{target_kind.lower()}-{target.lower()}" + labels = { + "k8spin.cloud/type": "role", + "k8spin.cloud/org": organization.name + } + role_binding = create_role_binding( + rolebinding_name, namespace.name, labels, name, target_kind, target) + ensure(role_binding, organization) + rolebindings_names.append(rolebinding_name) + # Create required binding to allow user query namespaces + cluster_rolebinding_name = f"{organization_name}-{name}-{target_kind.lower()}-{target.lower()}" + cluster_role_binding = create_cluster_role_binding( + cluster_rolebinding_name, labels, "namespace-viewer", target_kind, target) + ensure(cluster_role_binding, organization) + # Finally, cleanup + _clean_organization_roles(organization, rolebindings_names) + + +@kubernetes_api +def _clean_organization_roles(api, organization: k8spin_common.Organization, rolebindings_names: list): + namespace = organization.organization_namespace + org_role_bindings = pykube.RoleBinding.objects(api, namespace=namespace.name).filter( + selector={"k8spin.cloud/type": "role", "k8spin.cloud/org": organization.name}) + for org_role_binding in org_role_bindings: + if org_role_binding.name not in rolebindings_names: + org_role_binding.delete() + + +@kubernetes_api +def get_organization_from_namespace(api, organization_namespace_name: str): + organization_namespace = pykube.objects.Namespace.objects( + api).get(name=organization_namespace_name) + org_name = organization_namespace.labels["k8spin.cloud/org"] + return get_organization(organization_name=org_name) + + +@kubernetes_api +def get_organization(api, organization_name: str): + return k8spin_common.Organization.objects(api).get(name=organization_name) diff --git a/k8spin_common/k8spin_common/resources/quotas.py b/k8spin_common/k8spin_common/resources/quotas.py new file mode 100644 index 0000000..c2f0d46 --- /dev/null +++ b/k8spin_common/k8spin_common/resources/quotas.py @@ -0,0 +1,51 @@ +import pykube +import yaml + +from k8spin_common.helper import kubernetes_api + + +@kubernetes_api +def create_limit_range(api, name: str, namespace: str, labels: dict, cpu: str, memory: str) -> pykube.LimitRange: + _obj = yaml.safe_load( + f""" + apiVersion: v1 + kind: LimitRange + metadata: + name: {name} + namespace: {namespace} + spec: + limits: + - default: + cpu: {cpu} + memory: {memory} + defaultRequest: + cpu: {cpu} + memory: {memory} + type: Container + """ + ) + metadata = _obj.get("metadata") + metadata["labels"] = labels + return pykube.LimitRange(api, _obj) + + +@kubernetes_api +def create_resource_quota(api, name: str, namespace: str, labels: dict, cpu: str, memory: str) -> pykube.ResourceQuota: + _obj = yaml.safe_load( + f""" + apiVersion: v1 + kind: ResourceQuota + metadata: + name: {name} + namespace: {namespace} + spec: + hard: + requests.cpu: {cpu} + requests.memory: {memory} + limits.cpu: {cpu} + limits.memory: {memory} + """ + ) + metadata = _obj.get("metadata") + metadata["labels"] = labels + return pykube.ResourceQuota(api, _obj) diff --git a/k8spin_common/k8spin_common/resources/rbac.py b/k8spin_common/k8spin_common/resources/rbac.py new file mode 100644 index 0000000..f1361ee --- /dev/null +++ b/k8spin_common/k8spin_common/resources/rbac.py @@ -0,0 +1,51 @@ +import pykube +import yaml + +from k8spin_common.helper import kubernetes_api + + +@kubernetes_api +def create_role_binding(api, name: str, namespace: str, labels: dict, cluster_role: str, subject_kind: str, subject_name: str) -> pykube.RoleBinding: + _obj = yaml.safe_load( + f""" + apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: {name} + namespace: {namespace} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {cluster_role} + subjects: + - kind: {subject_kind} + name: {subject_name} + apiGroup: rbac.authorization.k8s.io + """ + ) + metadata = _obj.get("metadata") + metadata["labels"] = labels + return pykube.RoleBinding(api, _obj) + + +@kubernetes_api +def create_cluster_role_binding(api, name: str, labels: dict, cluster_role: str, subject_kind: str, subject_name: str) -> pykube.ClusterRoleBinding: + _obj = yaml.safe_load( + f""" + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: {name} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {cluster_role} + subjects: + - kind: {subject_kind} + name: {subject_name} + apiGroup: rbac.authorization.k8s.io + """ + ) + metadata = _obj.get("metadata") + metadata["labels"] = labels + return pykube.RoleBinding(api, _obj) diff --git a/k8spin_common/k8spin_common/resources/space.py b/k8spin_common/k8spin_common/resources/space.py new file mode 100644 index 0000000..b7a9489 --- /dev/null +++ b/k8spin_common/k8spin_common/resources/space.py @@ -0,0 +1,145 @@ +import pykube + +import k8spin_common +from k8spin_common.helper import adopt, ensure, kubernetes_api +from k8spin_common.resources.namespace import create_namespace +from k8spin_common.resources.quotas import (create_limit_range, + create_resource_quota) +from k8spin_common.resources.rbac import (create_cluster_role_binding, + create_role_binding) +from k8spin_common.resources.tenant import tenant_namespacename_generator + +SPACE_NAMESPACE_PREFIX = "space-" + + +def space_namespacename_generator(space_name, tenant_name, organization_name): + tenant_namespace = tenant_namespacename_generator( + organization_name=organization_name, tenant_name=tenant_name) + return f"{tenant_namespace}-{SPACE_NAMESPACE_PREFIX}{space_name}" + + +def ensure_space_resources(organization: k8spin_common.Organization, tenant: k8spin_common.Tenant, space_name: str): + ensure_space_namespace(organization=organization, + tenant=tenant, space_name=space_name) + ensure_space_role_bindings(organization=organization, + tenant=tenant, space_name=space_name) + ensure_space_resource_quota( + organization=organization, tenant=tenant, space_name=space_name) + ensure_space_limit_range( + organization=organization, tenant=tenant, space_name=space_name) + + +@kubernetes_api +def ensure_space_namespace(api, organization: k8spin_common.Organization, tenant: k8spin_common.Tenant, space_name: str): + space_namespace_name = space_namespacename_generator( + space_name=space_name, tenant_name=tenant.name, organization_name=organization.name) + labels = { + "k8spin.cloud/type": "space", + "k8spin.cloud/org": organization.name, + "k8spin.cloud/tenant": tenant.name, + "k8spin.cloud/space": space_name + } + owner = get_space(space_name, tenant.name, organization.name) + space_namespace = create_namespace(space_namespace_name, labels) + space_namespace = ensure(space_namespace, owner) + return space_namespace + + +@kubernetes_api +def ensure_space_resource_quota(api, organization: k8spin_common.Organization, tenant: k8spin_common.Tenant, space_name: str): + space = get_space(space_name, tenant.name, organization.name) + space_namespace = space.space_namespace + labels = { + "k8spin.cloud/type": "quotas", + "k8spin.cloud/org": organization.name, + "k8spin.cloud/tenant": tenant.name, + "k8spin.cloud/space": space_name + } + cpu = space.resources.get("cpu") + memory = space.resources.get("memory") + space_resource_quota = create_resource_quota( + "quotas", space_namespace.name, labels, cpu, memory) + space_resource_quota = ensure(space_resource_quota, space) + return space_resource_quota + + +@kubernetes_api +def ensure_space_limit_range(api, organization: k8spin_common.Organization, tenant: k8spin_common.Tenant, space_name: str): + space = get_space(space_name, tenant.name, organization.name) + space_namespace = space.space_namespace + labels = { + "k8spin.cloud/type": "defaults", + "k8spin.cloud/org": organization.name, + "k8spin.cloud/tenant": tenant.name, + "k8spin.cloud/space": space_name + } + cpu = space.default_container_resources.get("cpu") + memory = space.default_container_resources.get("memory") + space_limit_range = create_limit_range( + "defaults", space_namespace.name, labels, cpu, memory) + space_limit_range = ensure(space_limit_range, space) + return space_limit_range + + +@kubernetes_api +def ensure_space_role_bindings(api, organization: k8spin_common.Organization, tenant: k8spin_common.Tenant, space_name: str): + space = get_space(space_name=space_name, tenant_name=tenant.name, + organization_name=organization.name) + namespace = space.space_namespace + roles = space.roles + rolebindings_names = list() + # Space level permissions + for role in roles: + # Cluster role to assign + name = role.get('name') + target_kind = "Group" + targets = role.get('groups', None) + if not targets: + target_kind = "User" + targets = role.get('users', list()) + for target in targets: + rolebinding_name = f"{space_name}-{name}-{target_kind.lower()}-{target.lower()}" + labels = { + "k8spin.cloud/type": "role", + "k8spin.cloud/org": organization.name, + "k8spin.cloud/tenant": tenant.name, + "k8spin.cloud/space": space_name + } + role_binding = create_role_binding( + rolebinding_name, namespace.name, labels, name, target_kind, target) + ensure(role_binding, space) + rolebindings_names.append(rolebinding_name) + # Create required binding to allow user query namespaces + cluster_rolebinding_name = f"{space_name}-{name}-{target_kind.lower()}-{target.lower()}" + cluster_role_binding = create_cluster_role_binding( + cluster_rolebinding_name, labels, "namespace-viewer", target_kind, target) + ensure(cluster_role_binding, space) + # Finally, cleanup + _clean_space_roles(organization, tenant, space, rolebindings_names) + + +@kubernetes_api +def _clean_space_roles(api, organization: k8spin_common.Organization, tenant: k8spin_common.Tenant, space: k8spin_common.Space, rolebindings_names: list): + namespace = space.space_namespace + space_role_bindings = pykube.RoleBinding.objects(api, namespace=namespace.name).filter( + selector={"k8spin.cloud/type": "role", "k8spin.cloud/org": organization.name, "k8spin.cloud/tenant": tenant.name, "k8spin.cloud/space": space.name}) + for space_role_binding in space_role_bindings: + if space_role_binding.name not in rolebindings_names: + space_role_binding.delete() + + +@kubernetes_api +def get_space_from_namespace(api, space_namespace_name: str): + space_namespace = pykube.Namespace.objects( + api).get(name=space_namespace_name) + org_name = space_namespace.labels["k8spin.cloud/org"] + tenant_name = space_namespace.labels["k8spin.cloud/tenant"] + space_name = space_namespace.labels["k8spin.cloud/space"] + return get_space(space_name=space_name, tenant_name=tenant_name, organization_name=org_name) + + +@kubernetes_api +def get_space(api, space_name: str, tenant_name: str, organization_name: str): + tenant_namespace = tenant_namespacename_generator( + organization_name=organization_name, tenant_name=tenant_name) + return k8spin_common.Space.objects(api, namespace=tenant_namespace).get(name=space_name) diff --git a/k8spin_common/k8spin_common/resources/tenant.py b/k8spin_common/k8spin_common/resources/tenant.py new file mode 100644 index 0000000..7613628 --- /dev/null +++ b/k8spin_common/k8spin_common/resources/tenant.py @@ -0,0 +1,100 @@ +import pykube + +import k8spin_common +from k8spin_common.helper import adopt, ensure, kubernetes_api +from k8spin_common.resources.namespace import create_namespace +from k8spin_common.resources.organization import \ + organization_namespacename_generator +from k8spin_common.resources.rbac import (create_cluster_role_binding, + create_role_binding) + +TENANT_NAMESPACE_PREFIX = "tenant-" + + +def tenant_namespacename_generator(tenant_name, organization_name): + org_namespace = organization_namespacename_generator(organization_name) + return f"{org_namespace}-{TENANT_NAMESPACE_PREFIX}{tenant_name}" + + +def ensure_tenant_resources(organization: k8spin_common.Organization, tenant_name: str): + ensure_tenant_namespace( + organization=organization, tenant_name=tenant_name) + ensure_tenant_role_bindings( + organization=organization, tenant_name=tenant_name) + + +@kubernetes_api +def ensure_tenant_namespace(api, organization: k8spin_common.Organization, tenant_name: str): + tenant_namespace_name = tenant_namespacename_generator( + organization_name=organization.name, tenant_name=tenant_name) + labels = { + "k8spin.cloud/type": "tenant", + "k8spin.cloud/org": organization.name, + "k8spin.cloud/tenant": tenant_name + } + owner = get_tenant(tenant_name, organization.name) + tenant_namespace = create_namespace(tenant_namespace_name, labels) + tenant_namespace = ensure(tenant_namespace, owner) + return tenant_namespace + + +@kubernetes_api +def ensure_tenant_role_bindings(api, organization: k8spin_common.Organization, tenant_name: str): + tenant = get_tenant(tenant_name=tenant_name, + organization_name=organization.name) + namespace = tenant.tenant_namespace + roles = tenant.roles + rolebindings_names = list() + # Tenant level permissions + for role in roles: + # Cluster role to assign + name = role.get('name') + target_kind = "Group" + targets = role.get('groups', None) + if not targets: + target_kind = "User" + targets = role.get('users', list()) + for target in targets: + rolebinding_name = f"{tenant_name}-{name}-{target_kind.lower()}-{target.lower()}" + labels = { + "k8spin.cloud/type": "role", + "k8spin.cloud/org": organization.name, + "k8spin.cloud/tenant": tenant_name + } + role_binding = create_role_binding( + rolebinding_name, namespace.name, labels, name, target_kind, target) + ensure(role_binding, tenant) + rolebindings_names.append(rolebinding_name) + # Create required binding to allow user query namespaces + cluster_rolebinding_name = f"{tenant_name}-{name}-{target_kind.lower()}-{target.lower()}" + cluster_role_binding = create_cluster_role_binding( + cluster_rolebinding_name, labels, "namespace-viewer", target_kind, target) + ensure(cluster_role_binding, tenant) + # Finally, cleanup + _clean_tenant_roles(organization, tenant, rolebindings_names) + + +@kubernetes_api +def _clean_tenant_roles(api, organization: k8spin_common.Organization, tenant: k8spin_common.Tenant, rolebindings_names: list): + namespace = tenant.tenant_namespace + tenant_role_bindings = pykube.RoleBinding.objects(api, namespace=namespace.name).filter( + selector={"k8spin.cloud/type": "role", "k8spin.cloud/org": organization.name, "k8spin.cloud/tenant": tenant.name}) + for tenant_role_binding in tenant_role_bindings: + if tenant_role_binding.name not in rolebindings_names: + tenant_role_binding.delete() + + +@kubernetes_api +def get_tenant_from_namespace(api, tenant_namespace_name: str): + tenant_namespace = pykube.Namespace.objects( + api).get(name=tenant_namespace_name) + org_name = tenant_namespace.labels["k8spin.cloud/org"] + tenant_name = tenant_namespace.labels["k8spin.cloud/tenant"] + return get_tenant(tenant_name=tenant_name, organization_name=org_name) + + +@kubernetes_api +def get_tenant(api, tenant_name: str, organization_name: str): + organization_namespace_name = organization_namespacename_generator( + organization_name=organization_name) + return k8spin_common.Tenant.objects(api, namespace=organization_namespace_name).get(name=tenant_name) diff --git a/k8spin_common/requirements-dev.txt b/k8spin_common/requirements-dev.txt new file mode 100644 index 0000000..f3ff8f1 --- /dev/null +++ b/k8spin_common/requirements-dev.txt @@ -0,0 +1,3 @@ +-r requirements.txt +autopep8 +pylint \ No newline at end of file diff --git a/k8spin_common/requirements.txt b/k8spin_common/requirements.txt new file mode 100644 index 0000000..a75c4b0 --- /dev/null +++ b/k8spin_common/requirements.txt @@ -0,0 +1 @@ +pykube-ng \ No newline at end of file diff --git a/k8spin_common/setup.py b/k8spin_common/setup.py new file mode 100644 index 0000000..cf7fc66 --- /dev/null +++ b/k8spin_common/setup.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +from setuptools import setup, find_packages + +setup( + name="k8spin_common", + version="0.1", + packages=find_packages(), + install_requires=[ + 'kopf' + ] +) \ No newline at end of file diff --git a/k8spin_operator/__init__.py b/k8spin_operator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/k8spin_operator/k8spin_operator/__init__.py b/k8spin_operator/k8spin_operator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/k8spin_operator/k8spin_operator/handlers/__init__.py b/k8spin_operator/k8spin_operator/handlers/__init__.py new file mode 100644 index 0000000..0588fb2 --- /dev/null +++ b/k8spin_operator/k8spin_operator/handlers/__init__.py @@ -0,0 +1,4 @@ +from .organization import * +from .tenant import * +from .space import * +from .reconcile import * \ No newline at end of file diff --git a/k8spin_operator/k8spin_operator/handlers/organization.py b/k8spin_operator/k8spin_operator/handlers/organization.py new file mode 100644 index 0000000..aaaacbe --- /dev/null +++ b/k8spin_operator/k8spin_operator/handlers/organization.py @@ -0,0 +1,9 @@ +import kopf + +from k8spin_common.resources import organization + + +@kopf.on.create("k8spin.cloud", "v1", "organizations") +@kopf.on.update("k8spin.cloud", "v1", "organizations") +def create_organization(name, **kwargs): + organization.ensure_organization_resources(organization_name=name) diff --git a/k8spin_operator/k8spin_operator/handlers/reconcile.py b/k8spin_operator/k8spin_operator/handlers/reconcile.py new file mode 100644 index 0000000..83b06c3 --- /dev/null +++ b/k8spin_operator/k8spin_operator/handlers/reconcile.py @@ -0,0 +1,29 @@ +from os import getenv + +import kopf + +from k8spin_common.resources import organization, space, tenant + +RECONCILIATION_INTERVAL = int(getenv("RECONCILIATION_INTERVAL_SECONDS", "10")) + + +@kopf.on.delete("k8spin.cloud", "v1", "organizations") +def delete(name, **kwargs): + # Needed to fix a problem in the bellow reconciler. + # Seems like reconciler does not remove the object from its cache if its not + # explicitly declared this deletion handler. + pass + + +@kopf.timer("k8spin.cloud", "v1", "organizations", interval=RECONCILIATION_INTERVAL, idle=10) +def reconciler(name, **kwargs): + organization.ensure_organization_resources(organization_name=name) + org = organization.get_organization(name) + org_tenants = org.tenants + for org_tenant in org_tenants: + tenant.ensure_tenant_resources( + organization=org, tenant_name=org_tenant.name) + tenant_spaces = org_tenant.spaces + for tenant_space in tenant_spaces: + space.ensure_space_resources( + organization=org, tenant=org_tenant, space_name=tenant_space.name) diff --git a/k8spin_operator/k8spin_operator/handlers/space.py b/k8spin_operator/k8spin_operator/handlers/space.py new file mode 100644 index 0000000..e540a9d --- /dev/null +++ b/k8spin_operator/k8spin_operator/handlers/space.py @@ -0,0 +1,24 @@ +import kopf +import pykube + +from k8spin_common.helper import kubernetes_api +from k8spin_common.resources import space, tenant + + +@kubernetes_api +def lives_in_tenant_namespace(api, meta, namespace, **_): + parent_namespace = pykube.Namespace.objects(api).get(name=namespace) + if parent_namespace.labels.get("k8spin.cloud/type", "") == "tenant" and any( + [owner.get('kind') == 'Tenant' for owner in parent_namespace.metadata.get('ownerReferences', list())]): + return True + return False + + +@kopf.on.create("k8spin.cloud", "v1", "spaces", when=lives_in_tenant_namespace) +@kopf.on.update("k8spin.cloud", "v1", "spaces", when=lives_in_tenant_namespace) +def create_space(name, namespace, **kwargs): + parent_tenant = tenant.get_tenant_from_namespace( + tenant_namespace_name=namespace) + parent_organization = parent_tenant.org + space.ensure_space_resources( + organization=parent_organization, tenant=parent_tenant, space_name=name) diff --git a/k8spin_operator/k8spin_operator/handlers/tenant.py b/k8spin_operator/k8spin_operator/handlers/tenant.py new file mode 100644 index 0000000..9b496ed --- /dev/null +++ b/k8spin_operator/k8spin_operator/handlers/tenant.py @@ -0,0 +1,23 @@ +import kopf +import pykube + +from k8spin_common.helper import kubernetes_api +from k8spin_common.resources import organization, tenant + + +@kubernetes_api +def lives_in_org_namespace(api, namespace, **_): + parent_namespace = pykube.Namespace.objects(api).get(name=namespace) + if parent_namespace.labels.get("k8spin.cloud/type", "") == "organization" and any( + [owner.get('kind') == 'Organization' for owner in parent_namespace.metadata.get('ownerReferences', list())]): + return True + return False + + +@kopf.on.create("k8spin.cloud", "v1", "tenants", when=lives_in_org_namespace) +@kopf.on.update("k8spin.cloud", "v1", "tenants", when=lives_in_org_namespace) +def create_tenant(name, meta, **kwargs): + parent_organization = organization.get_organization( + organization_name=meta.labels.get("k8spin.cloud/org", None)) + tenant.ensure_tenant_resources( + organization=parent_organization, tenant_name=name) diff --git a/k8spin_operator/operator.py b/k8spin_operator/operator.py new file mode 100644 index 0000000..53c27e5 --- /dev/null +++ b/k8spin_operator/operator.py @@ -0,0 +1,7 @@ +import logging +from os import getenv + +from k8spin_operator.handlers import * + +logger = logging.getLogger() +logger.setLevel(getenv("LOGGING_LEVEL", "INFO")) diff --git a/k8spin_operator/requirements-dev.txt b/k8spin_operator/requirements-dev.txt new file mode 100644 index 0000000..f3ff8f1 --- /dev/null +++ b/k8spin_operator/requirements-dev.txt @@ -0,0 +1,3 @@ +-r requirements.txt +autopep8 +pylint \ No newline at end of file diff --git a/k8spin_operator/requirements.txt b/k8spin_operator/requirements.txt new file mode 100644 index 0000000..601aab0 --- /dev/null +++ b/k8spin_operator/requirements.txt @@ -0,0 +1 @@ +kopf==0.27rc6 diff --git a/k8spin_webhook/__init__.py b/k8spin_webhook/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/k8spin_webhook/app.py b/k8spin_webhook/app.py new file mode 100644 index 0000000..f824b0f --- /dev/null +++ b/k8spin_webhook/app.py @@ -0,0 +1,22 @@ +import logging +from os import getenv + +from flask import Flask +from healthcheck import HealthCheck + +import mutator +import validator + +logger = logging.getLogger() +logger.setLevel(getenv("LOGGING_LEVEL", "INFO")) + +app = Flask(__name__) + +health = HealthCheck(app, "/health") + +app.register_blueprint(validator.blueprint) +app.register_blueprint(mutator.blueprint) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=443, + ssl_context=("/certs/tls.crt", "/certs/tls.key")) diff --git a/k8spin_webhook/mutator/__init__.py b/k8spin_webhook/mutator/__init__.py new file mode 100644 index 0000000..b0d82d3 --- /dev/null +++ b/k8spin_webhook/mutator/__init__.py @@ -0,0 +1,40 @@ +from flask import Blueprint, request, jsonify +from loguru import logger +import base64 +import jsonpatch + +from k8spin_common.resources import tenant + +blueprint = Blueprint("mutator", __name__, url_prefix="/mutator") + +def mutator_response(allowed, message, json_patch): + base64_patch = None + if json_patch: + base64_patch = base64.b64encode( + json_patch.to_string().encode("utf-8")).decode("utf-8") + return jsonify({"response": {"allowed": allowed, + "status": {"message": message}, + "patchType": "JSONPatch", + "patch": base64_patch}}) + +@blueprint.route('/organizations', methods=['POST']) +def organization_mutator(): + request_info = request.get_json() + organization_name = request_info["request"]["object"]["metadata"]["name"] + patch = jsonpatch.JsonPatch([{"op": "add", "path": "/metadata/labels", "value": {"k8spin.cloud/org": organization_name}}]) + return mutator_response(True, "", patch) + +@blueprint.route('/tenants', methods=['POST']) +def tenant_mutator(): + request_info = request.get_json() + organization_name = request_info["request"]["namespace"].replace("org-", "") + patch = jsonpatch.JsonPatch([{"op": "add", "path": "/metadata/labels", "value": {"k8spin.cloud/org": organization_name, "k8spin.cloud/tenant": request_info["request"]["name"]}}]) + return mutator_response(True, "", patch) + +@blueprint.route('/spaces', methods=['POST']) +def space_mutator(): + request_info = request.get_json() + parent_tenant = tenant.get_tenant_from_namespace(tenant_namespace_name=request_info["request"]["namespace"]) + parent_organization = parent_tenant.org + patch = jsonpatch.JsonPatch([{"op": "add", "path": "/metadata/labels", "value": {"k8spin.cloud/org": parent_organization.metadata["name"], "k8spin.cloud/tenant": parent_tenant.metadata["name"], "k8spin.cloud/space": request_info["request"]["name"]}}]) + return mutator_response(True, "", patch) diff --git a/k8spin_webhook/mutator/mutator_utils.py b/k8spin_webhook/mutator/mutator_utils.py new file mode 100644 index 0000000..3ae1530 --- /dev/null +++ b/k8spin_webhook/mutator/mutator_utils.py @@ -0,0 +1 @@ +#Mutator utils \ No newline at end of file diff --git a/k8spin_webhook/requirements-dev.txt b/k8spin_webhook/requirements-dev.txt new file mode 100644 index 0000000..f3ff8f1 --- /dev/null +++ b/k8spin_webhook/requirements-dev.txt @@ -0,0 +1,3 @@ +-r requirements.txt +autopep8 +pylint \ No newline at end of file diff --git a/k8spin_webhook/requirements.txt b/k8spin_webhook/requirements.txt new file mode 100644 index 0000000..4fdb7bd --- /dev/null +++ b/k8spin_webhook/requirements.txt @@ -0,0 +1,5 @@ +flask +loguru +jsonpatch +six +healthcheck \ No newline at end of file diff --git a/k8spin_webhook/utils.py b/k8spin_webhook/utils.py new file mode 100644 index 0000000..e69de29 diff --git a/k8spin_webhook/validator/__init__.py b/k8spin_webhook/validator/__init__.py new file mode 100644 index 0000000..24966c2 --- /dev/null +++ b/k8spin_webhook/validator/__init__.py @@ -0,0 +1,22 @@ +from flask import Blueprint, request, jsonify + +from .validator_utils import check_space_quotas, check_tenant_quotas + +blueprint = Blueprint("validator", __name__, url_prefix="/validator") + +def validator_response(allowed, message): + return jsonify({"response": {"allowed": allowed, "status": {"message": message}}}) + +@blueprint.route('/tenants', methods=['POST']) +def tenant_validator(): + request_info = request.get_json() + resource_object = request_info["request"]["object"] + response, message = check_tenant_quotas(resource_object) + return validator_response(response, message) + +@blueprint.route('/spaces', methods=['POST']) +def space_validator(): + request_info = request.get_json() + resource_object = request_info["request"]["object"] + response, message = check_space_quotas(resource_object) + return validator_response(response, message) \ No newline at end of file diff --git a/k8spin_webhook/validator/validator_utils.py b/k8spin_webhook/validator/validator_utils.py new file mode 100644 index 0000000..52f7880 --- /dev/null +++ b/k8spin_webhook/validator/validator_utils.py @@ -0,0 +1,105 @@ +from typing import List, Tuple +from k8spin_common.helper import kubernetes_api +import k8spin_common +import pykube +from loguru import logger + +def memory_convert_unit(value: str) -> float: + #https://k8smeetup.github.io/docs/tasks/configure-pod-container/assign-cpu-ram-container/#cpu-and-ram-units + #E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki + value = value.replace("i", "") + scale_dict = { + "E": pow(10,18), + "P": pow(10,15), + "T": pow(10,12), + "G": pow(10,9), + "M": pow(10,6), + "K": pow(10,3) + } + for unit in scale_dict: + if unit in value: + value = value.replace(unit, "") + return float(value)*scale_dict[unit] + return float(value) + +def cpu_convert_unit(value: str) -> float: + #https://k8smeetup.github.io/docs/tasks/configure-pod-container/assign-cpu-ram-container/#cpu-and-ram-units + #0.1, 1, 100m + scale = 1000 + if "m" in value: + scale = 1 + value = value.replace("m", "") + return float(value)*scale + +def get_namespace_type(resource: object) -> str: + if "k8spin.cloud/type" in resource["metadata"]["labels"]: + return resource["metadata"]["labels"]["k8spin.cloud/type"] + else: + return None + +def get_cpu(resource: object) -> float: + return cpu_convert_unit(resource["spec"]["resources"]["cpu"]) + +def get_memory(resource: object) -> float: + return memory_convert_unit(resource["spec"]["resources"]["memory"]) + +def check_tenant_quotas(resource: object) -> Tuple[bool, str]: + tenant_name = resource["metadata"]["name"] + organization_name = resource["metadata"]["namespace"].replace("org-", "") + + logger.debug(f'Checking tenant quota for tenant {tenant_name} in organization {organization_name}') + + organization, tenants = get_organization_data(organization_name) + organization_cpu = get_cpu(organization.obj) + organization_memory = get_memory(organization.obj) + logger.debug("Organization found: " + organization_name + " CPU Limit: " + str(organization_cpu) + " Memory Limit: " + str(organization_memory)) + + incoming_cpu = get_cpu(resource) + incoming_memory = get_memory(resource) + logger.debug("Incoming tenant resources CPU Limit: " + str(incoming_cpu) + " Memory Limit: " + str(incoming_memory)) + + total_cpu , total_memory = get_total_resources([tenant for tenant in tenants if tenant.name != tenant_name]) + logger.debug("Total other tenants found for organization " + organization_name + " CPU: " + str(total_cpu) + " Memory: " + str(total_memory)) + + if (incoming_cpu+total_cpu)<=organization_cpu and (incoming_memory+total_memory)<=organization_memory: + return True, "" + else: + return False, "Resources exceeded" + +def check_space_quotas(resource: object) -> Tuple[bool, str]: + space_name = resource["metadata"]["name"] + organization_name = resource["metadata"]["labels"]["k8spin.cloud/org"] + tenant_name = resource["metadata"]["labels"]["k8spin.cloud/tenant"] + logger.debug(f'Checking space quota for space {space_name} in tenant {tenant_name}') + + tenant, spaces = get_tenant_data(organization=organization_name, tenant=tenant_name) + tenant_cpu = get_cpu(tenant.obj) + tenant_memory = get_memory(tenant.obj) + logger.debug("Tenant found: " + tenant_name + " CPU Limit: " + str(tenant_cpu) + " Memory Limit: " + str(tenant_memory)) + + incoming_cpu = get_cpu(resource) + incoming_memory = get_memory(resource) + logger.debug("Incoming space resources CPU Limit: " + str(incoming_cpu) + " Memory Limit: " + str(incoming_memory)) + + total_cpu , total_memory = get_total_resources([space for space in spaces if space.name != space_name]) + logger.debug("Total other spaces found for tenant " + tenant_name + " CPU: " + str(total_cpu) + " Memory : " + str(total_memory)) + + if (incoming_cpu+total_cpu)<=tenant_cpu and (incoming_memory+total_memory)<=tenant_memory: + return True, "" + else: + return False, "Resources exceeded" + +def get_total_resources(objects) -> Tuple[float, float]: + total_cpu = total_mem = 0 + for obj in objects: + total_cpu += get_cpu(obj.obj) + total_mem += get_memory(obj.obj) + return total_cpu, total_mem + +@kubernetes_api +def get_organization_data(api, organization: str) -> Tuple[object, List[object]]: + return k8spin_common.Organization.objects(api).get(name=organization), k8spin_common.Tenant.objects(api, namespace="org-"+organization) + +@kubernetes_api +def get_tenant_data(api, organization:str, tenant: str) -> Tuple[object, List[object]]: + return k8spin_common.Tenant.objects(api, namespace="org-"+organization).get(name=tenant), k8spin_common.Space.objects(api, namespace="org-" + organization + "-tenant-"+tenant) \ No newline at end of file diff --git a/tests/e2e/__init__.py b/tests/e2e/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py new file mode 100644 index 0000000..d46440d --- /dev/null +++ b/tests/e2e/conftest.py @@ -0,0 +1,73 @@ +import logging +import os +from functools import partial +from pathlib import Path +from tempfile import NamedTemporaryFile +from typing import Generator +import yaml +from pytest import fixture +from requests_html import HTMLSession + +kind_cluster_name = "k8spin-operator-e2e" + +@fixture(scope="session") +def cluster(kind_cluster) -> Generator[dict, None, None]: + kubectl = kind_cluster.kubectl + + operator_image = "k8spin/k8spin-operator:dev" + kind_cluster.load_docker_image(operator_image) + + webhook_image = "k8spin/k8spin-webhook:dev" + kind_cluster.load_docker_image(webhook_image) + + logging.info("Deploying CertManager") + kubectl("apply", "-f", str(Path(__file__).parent.parent.parent / "deploy/cert-manager")) + kubectl("rollout", "status", "-n", "cert-manager", "deployment/cert-manager") + kubectl("rollout", "status", "-n", "cert-manager", "deployment/cert-manager-cainjector") + kubectl("wait", "--for=condition=Available", "deployment", "--timeout=2m", "-n", "cert-manager" ,"--all") + + logging.info("Deploying K8Spin CRDS") + kubectl("apply", "-f", str(Path(__file__).parent.parent.parent / "deploy/crds")) + + logging.info("Deploying Operator and Validator") + kubectl("apply", "-f", str(Path(__file__).parent.parent.parent / "deploy")) + + logging.info("Waiting for rollout ...") + kubectl("rollout", "status", "deployment/k8spin-webhook") + kubectl("rollout", "status", "deployment/k8spin-operator") + + # with kind_cluster.port_forward("service/kube-web-view", 80) as port: + # url = f"http://localhost:{port}/" + # yield {"url": url} + + yield kind_cluster + + os.makedirs("./e2elogs", exist_ok=True) + + webhook_logs = open("e2elogs/webhook-logs.txt", "w") + webhook_logs_out = kubectl("logs", "deploy/k8spin-webhook") + webhook_logs.write(webhook_logs_out) + webhook_logs.close() + + operator_logs = open("e2elogs/operator-logs.txt", "w") + operator_logs_out = kubectl("logs", "deploy/k8spin-operator") + operator_logs.write(operator_logs_out) + operator_logs.close() + + custer_status_logs = open("e2elogs/cluster-status-logs.txt", "w") + custer_status_out = kubectl("get", "all,org,tenant,space,ns", "-A") + custer_status_logs.write(custer_status_out) + custer_status_logs.close() + +@fixture(scope="session") +def session(populated_cluster): + + url = populated_cluster["url"].rstrip("/") + + s = HTMLSession() + + def new_request(prefix, f, method, url, *args, **kwargs): + return f(method, prefix + url, *args, **kwargs) + + s.request = partial(new_request, url, s.request) + return s \ No newline at end of file diff --git a/tests/e2e/test_basic.py b/tests/e2e/test_basic.py new file mode 100644 index 0000000..fea243b --- /dev/null +++ b/tests/e2e/test_basic.py @@ -0,0 +1,179 @@ + +from pathlib import Path +import time +import inspect + +from slugify import slugify +from pykube import Namespace +import pykube +import pytest + +from .utils import create_org_object, create_space_object, create_tenant_object +from k8spin_common.resources.organization import organization_namespacename_generator, get_organization +from k8spin_common.resources.tenant import tenant_namespacename_generator, get_tenant +from k8spin_common.resources.space import space_namespacename_generator, get_space + +TIMEOUT = 2 +ORG_NAME = "acme" +TENANT_NAME = "looney" +SPACE_NAME = "tunes" + + +def test_create_org(cluster): + test_id = "t1" + org = create_org_object(api=cluster.api, organization_name=test_id+ORG_NAME) + org.create() + time.sleep(TIMEOUT) + + namespace = Namespace.objects(cluster.api).get(name=organization_namespacename_generator(organization_name=test_id+ORG_NAME)) + assert namespace.labels["k8spin.cloud/type"] == "organization" + assert namespace.labels["k8spin.cloud/org"] == test_id+ORG_NAME + +def test_create_tenant(cluster): + test_id = "t2" + org = create_org_object(api=cluster.api, organization_name=test_id+ORG_NAME) + org.create() + time.sleep(TIMEOUT) + + tenant = create_tenant_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME) + tenant.create() + time.sleep(TIMEOUT) + + namespace = Namespace.objects(cluster.api).get(name=tenant_namespacename_generator(organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME)) + assert namespace.labels["k8spin.cloud/type"] == "tenant" + assert namespace.labels["k8spin.cloud/org"] == test_id+ORG_NAME + assert namespace.labels["k8spin.cloud/tenant"] == test_id+TENANT_NAME + +def test_create_spaces(cluster): + test_id = "t3" + org = create_org_object(api=cluster.api, organization_name=test_id+ORG_NAME) + org.create() + time.sleep(TIMEOUT) + + tenant = create_tenant_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME) + tenant.create() + time.sleep(TIMEOUT) + + space = create_space_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME, space_name=test_id+SPACE_NAME) + space.create() + time.sleep(TIMEOUT) + + namespace = Namespace.objects(cluster.api).get(name=space_namespacename_generator(organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME, space_name=test_id+SPACE_NAME)) + assert namespace.labels["k8spin.cloud/type"] == "space" + assert namespace.labels["k8spin.cloud/org"] == test_id+ORG_NAME + assert namespace.labels["k8spin.cloud/tenant"] == test_id+TENANT_NAME + assert namespace.labels["k8spin.cloud/space"] == test_id+SPACE_NAME + + resourceQuotas = pykube.ResourceQuota.objects(cluster.api, namespace.name).filter(selector={ + "k8spin.cloud/type": "quotas" + }) + assert len(resourceQuotas) == 1 + + limitRanges = pykube.LimitRange.objects(cluster.api, namespace.name).filter(selector={ + "k8spin.cloud/type": "defaults" + }) + assert len(limitRanges) == 1 + +def test_manage_limits_tenant(cluster): + test_id = "t4" + org = create_org_object(api=cluster.api, organization_name=test_id+ORG_NAME) + org.obj["spec"]["resources"]["cpu"] = "2" + org.obj["spec"]["resources"]["memory"] = "2G" + org.create() + time.sleep(TIMEOUT) + + tenant = create_tenant_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME) + tenant.obj["spec"]["resources"]["cpu"] = "3" + tenant.obj["spec"]["resources"]["memory"] = "2G" + with pytest.raises(pykube.exceptions.HTTPError, match=".*validating.tenants.k8spin.cloud.*Resources exceeded.*"): + tenant.create() + + tenant.obj["spec"]["resources"]["cpu"] = "2" + tenant.obj["spec"]["resources"]["memory"] = "3T" + with pytest.raises(pykube.exceptions.HTTPError, match=".*validating.tenants.k8spin.cloud.*Resources exceeded.*"): + tenant.create() + + tenant.obj["spec"]["resources"]["cpu"] = "2000" + tenant.obj["spec"]["resources"]["memory"] = "3000M" + with pytest.raises(pykube.exceptions.HTTPError, match=".*validating.tenants.k8spin.cloud.*Resources exceeded.*"): + tenant.create() + + tenant.obj["spec"]["resources"]["cpu"] = "2000m" + tenant.obj["spec"]["resources"]["memory"] = "2000M" + tenant.create() + +def test_manage_limits_tenant_modorg(cluster): + test_id = "t5" + org = create_org_object(api=cluster.api, organization_name=test_id+ORG_NAME) + org.obj["spec"]["resources"]["cpu"] = "2" + org.obj["spec"]["resources"]["memory"] = "2G" + org.create() + time.sleep(TIMEOUT) + + tenant = create_tenant_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME) + tenant.obj["spec"]["resources"]["cpu"] = "3" + tenant.obj["spec"]["resources"]["memory"] = "3G" + with pytest.raises(pykube.exceptions.HTTPError, match=".*validating.tenants.k8spin.cloud.*Resources exceeded.*"): + tenant.create() + + org = get_organization(organization_name=test_id+ORG_NAME) + org.obj["spec"]["resources"]["cpu"] = "10" + org.obj["spec"]["resources"]["memory"] = "10G" + org.update() + time.sleep(TIMEOUT) + + tenant.create() + +def test_manage_limits_space(cluster): + test_id = "t6" + org = create_org_object(api=cluster.api, organization_name=test_id+ORG_NAME) + org.obj["spec"]["resources"]["cpu"] = "10" + org.obj["spec"]["resources"]["memory"] = "10G" + org.create() + time.sleep(TIMEOUT) + + tenant = create_tenant_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME) + tenant.obj["spec"]["resources"]["cpu"] = "5" + tenant.obj["spec"]["resources"]["memory"] = "5G" + tenant.create() + time.sleep(TIMEOUT) + + space = create_space_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME, space_name=test_id+SPACE_NAME) + space.obj["spec"]["resources"]["cpu"] = "10" + space.obj["spec"]["resources"]["memory"] = "10G" + with pytest.raises(pykube.exceptions.HTTPError, match=".*validating.spaces.k8spin.cloud.*Resources exceeded.*"): + space.create() + + space.obj["spec"]["resources"]["cpu"] = "4" + space.obj["spec"]["resources"]["memory"] = "4G" + space.create() + +def test_manage_limits_space_modtenant(cluster): + test_id = "t7" + org = create_org_object(api=cluster.api, organization_name=test_id+ORG_NAME) + org.obj["spec"]["resources"]["cpu"] = "10" + org.obj["spec"]["resources"]["memory"] = "10G" + org.create() + time.sleep(TIMEOUT) + + tenant = create_tenant_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME) + tenant.obj["spec"]["resources"]["cpu"] = "5" + tenant.obj["spec"]["resources"]["memory"] = "5G" + tenant.create() + time.sleep(TIMEOUT) + + space = create_space_object(api=cluster.api, organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME, space_name=test_id+SPACE_NAME) + space.obj["spec"]["resources"]["cpu"] = "10" + space.obj["spec"]["resources"]["memory"] = "10G" + with pytest.raises(pykube.exceptions.HTTPError, match=".*validating.spaces.k8spin.cloud.*Resources exceeded.*"): + space.create() + + tenant = get_tenant(organization_name=test_id+ORG_NAME, tenant_name=test_id+TENANT_NAME) + tenant.obj["spec"]["resources"]["cpu"] = "10" + tenant.obj["spec"]["resources"]["memory"] = "10G" + tenant.update() + time.sleep(TIMEOUT) + + space.obj["spec"]["resources"]["cpu"] = "10" + space.obj["spec"]["resources"]["memory"] = "10G" + space.create() diff --git a/tests/e2e/utils.py b/tests/e2e/utils.py new file mode 100644 index 0000000..09a9566 --- /dev/null +++ b/tests/e2e/utils.py @@ -0,0 +1,55 @@ +import yaml +from k8spin_common import Organization, Tenant, Space + + +def create_org_object(api, organization_name): + obj = yaml.safe_load( + f""" + apiVersion: k8spin.cloud/v1 + kind: Organization + metadata: + name: {organization_name} + spec: + resources: + cpu: "5" + memory: "5G" + """ + ) + return Organization(api, obj) + +def create_tenant_object(api, organization_name, tenant_name): + obj = yaml.safe_load( + f""" + apiVersion: k8spin.cloud/v1 + kind: Tenant + metadata: + namespace: org-{organization_name} + name: {tenant_name} + spec: + resources: + cpu: "2" + memory: "2G" + """ + ) + return Tenant(api, obj) + +def create_space_object(api, organization_name, tenant_name, space_name): + obj = yaml.safe_load( + f""" + apiVersion: k8spin.cloud/v1 + kind: Space + metadata: + namespace: org-{organization_name}-tenant-{tenant_name} + name: {space_name} + spec: + resources: + cpu: "1" + memory: "1G" + containers: + defaults: + resources: + cpu: 10m + memory: 64Mi + """ + ) + return Space(api, obj) \ No newline at end of file diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..44c60e3 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,6 @@ +pytest +pytest-kind +requests +requests-html +python-slugify +#pytest-parallel