diff --git a/test/e2e/BUILD.bazel b/test/e2e/BUILD.bazel index 7d2d1b6b0..99cab3938 100644 --- a/test/e2e/BUILD.bazel +++ b/test/e2e/BUILD.bazel @@ -9,6 +9,7 @@ container_bundle( "quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.23.0": "@io_kubernetes_ingress-nginx//image", "k8s.gcr.io/defaultbackend:bazel": "@io_gcr_k8s_defaultbackend//image", "vault:bazel": "@com_hashicorp_vault//image", + "sample-webhook:bazel": "//test/e2e/framework/addon/samplewebhook/sample:image", "gcr.io/kubernetes-helm/tiller:bazel": "@io_gcr_helm_tiller//image", "{STABLE_DOCKER_REPO}/cert-manager-controller:{STABLE_DOCKER_TAG}": "//cmd/controller:image", "{STABLE_DOCKER_REPO}/cert-manager-acmesolver:{STABLE_DOCKER_TAG}": "//cmd/acmesolver:image", diff --git a/test/e2e/framework/addon/BUILD.bazel b/test/e2e/framework/addon/BUILD.bazel index 36ace231c..a60083aee 100644 --- a/test/e2e/framework/addon/BUILD.bazel +++ b/test/e2e/framework/addon/BUILD.bazel @@ -33,6 +33,7 @@ filegroup( "//test/e2e/framework/addon/chart:all-srcs", "//test/e2e/framework/addon/nginxingress:all-srcs", "//test/e2e/framework/addon/pebble:all-srcs", + "//test/e2e/framework/addon/samplewebhook:all-srcs", "//test/e2e/framework/addon/tiller:all-srcs", "//test/e2e/framework/addon/vault:all-srcs", ], diff --git a/test/e2e/framework/addon/samplewebhook/BUILD.bazel b/test/e2e/framework/addon/samplewebhook/BUILD.bazel new file mode 100644 index 000000000..a1137f539 --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["addon.go"], + importpath = "github.com/jetstack/cert-manager/test/e2e/framework/addon/samplewebhook", + visibility = ["//visibility:public"], + deps = [ + "//test/e2e/framework/addon/certmanager:go_default_library", + "//test/e2e/framework/addon/chart:go_default_library", + "//test/e2e/framework/addon/tiller:go_default_library", + "//test/e2e/framework/config:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//test/e2e/framework/addon/samplewebhook/sample:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/test/e2e/framework/addon/samplewebhook/addon.go b/test/e2e/framework/addon/samplewebhook/addon.go new file mode 100644 index 000000000..1ca5bd482 --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/addon.go @@ -0,0 +1,126 @@ +/* +Copyright 2019 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. +*/ + +package samplewebhook + +import ( + "fmt" + "github.com/jetstack/cert-manager/test/e2e/framework/addon/certmanager" + "github.com/jetstack/cert-manager/test/e2e/framework/addon/chart" + "github.com/jetstack/cert-manager/test/e2e/framework/addon/tiller" + "github.com/jetstack/cert-manager/test/e2e/framework/config" +) + +// CertmanagerWebhook defines an addon that installs a cert-manager DNS01 +// webhook +type CertmanagerWebhook struct { + config *config.Config + chart *chart.Chart + tillerDetails *tiller.Details + + // Tiller is the tiller instance used to deploy the chart + Tiller *tiller.Tiller + + Certmanager *certmanager.Certmanager + + Name string + + // Required namespace to deploy the webhook into. + Namespace string + + // Optional override for the group name to use for the webhook + GroupName string +} + +// Details return the details about the webhook instance deployed +type Details struct { + GroupName string + SolverName string +} + +func (p *CertmanagerWebhook) Setup(cfg *config.Config) error { + p.config = cfg + if p.Name == "" { + return fmt.Errorf("name field must be set") + } + if p.Namespace == "" { + return fmt.Errorf("namespace name must be specified") + } + if p.Tiller == nil { + return fmt.Errorf("tiller field must be set") + } + if p.Certmanager == nil { + return fmt.Errorf("certmanager field must be set") + } + if p.config.Kubectl == "" { + return fmt.Errorf("path to kubectl must be provided") + } + if p.GroupName == "" { + p.GroupName = p.Name + ".acme.example.com" + } + var err error + p.tillerDetails, err = p.Tiller.Details() + if err != nil { + return err + } + p.chart = &chart.Chart{ + Tiller: p.Tiller, + ReleaseName: "wh-" + p.Name, + Namespace: p.Namespace, + ChartName: cfg.RepoRoot + "/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook", + Vars: []chart.StringTuple{ + {Key: "certManager.namespace", Value: p.Certmanager.Namespace}, + {Key: "image.repository", Value: "sample-webhook"}, + {Key: "image.tag", Value: "bazel"}, + {Key: "groupName", Value: "acme.example.com"}, + }, + Values: []string{cfg.RepoRoot + "/test/fixtures/example-webhook-values.yaml"}, + // doesn't matter when installing from disk + ChartVersion: "0", + UpdateDeps: true, + } + err = p.chart.Setup(cfg) + if err != nil { + return err + } + return nil +} + +// Provision will actually deploy this instance of Pebble-ingress to the cluster. +func (p *CertmanagerWebhook) Provision() error { + return p.chart.Provision() +} + +// Details returns details that can be used to utilise the instance of Pebble. +func (p *CertmanagerWebhook) Details() *Details { + return &Details{ + GroupName: p.GroupName, + SolverName: "my-custom-solver", + } +} + +// Deprovision will destroy this instance of Pebble +func (p *CertmanagerWebhook) Deprovision() error { + return p.chart.Deprovision() +} + +func (p *CertmanagerWebhook) SupportsGlobal() bool { + return false +} + +func (p *CertmanagerWebhook) Logs() (map[string]string, error) { + return p.chart.Logs() +} diff --git a/test/e2e/framework/addon/samplewebhook/sample/BUILD.bazel b/test/e2e/framework/addon/samplewebhook/sample/BUILD.bazel new file mode 100644 index 000000000..b25e4df5e --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +load("@io_bazel_rules_docker//go:image.bzl", "go_image") + +go_image( + name = "image", + base = "@alpine_linux-amd64//image", + embed = [":go_default_library"], + goarch = "amd64", + goos = "linux", + pure = "on", + visibility = ["//visibility:public"], +) + +go_library( + name = "go_default_library", + srcs = ["main.go"], + importpath = "github.com/jetstack/cert-manager/test/e2e/framework/addon/samplewebhook/sample", + visibility = ["//visibility:private"], + deps = [ + "//pkg/acme/webhook/apis/acme/v1alpha1:go_default_library", + "//pkg/acme/webhook/cmd:go_default_library", + "//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + ], +) + +go_binary( + name = "sample", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/.helmignore b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/Chart.yaml b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/Chart.yaml new file mode 100644 index 000000000..77c6eadac --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: example-webhook +version: 0.1.0 diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/NOTES.txt b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/NOTES.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/_helpers.tpl b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/_helpers.tpl new file mode 100644 index 000000000..d3c474b64 --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/_helpers.tpl @@ -0,0 +1,48 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "example-webhook.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "example-webhook.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "example-webhook.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "example-webhook.selfSignedIssuer" -}} +{{ printf "%s-selfsign" (include "example-webhook.fullname" .) }} +{{- end -}} + +{{- define "example-webhook.rootCAIssuer" -}} +{{ printf "%s-ca" (include "example-webhook.fullname" .) }} +{{- end -}} + +{{- define "example-webhook.rootCACertificate" -}} +{{ printf "%s-ca" (include "example-webhook.fullname" .) }} +{{- end -}} + +{{- define "example-webhook.servingCertificate" -}} +{{ printf "%s-webhook-tls" (include "example-webhook.fullname" .) }} +{{- end -}} diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/apiservice.yaml b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/apiservice.yaml new file mode 100644 index 000000000..504ab914b --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/apiservice.yaml @@ -0,0 +1,19 @@ +apiVersion: apiregistration.k8s.io/v1beta1 +kind: APIService +metadata: + name: v1alpha1.{{ .Values.groupName }} + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + annotations: + certmanager.k8s.io/inject-ca-from: "{{ .Release.Namespace }}/{{ include "example-webhook.servingCertificate" . }}" +spec: + group: {{ .Values.groupName }} + groupPriorityMinimum: 1000 + versionPriority: 15 + service: + name: {{ include "example-webhook.fullname" . }} + namespace: {{ .Release.Namespace }} + version: v1alpha1 diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/deployment.yaml b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/deployment.yaml new file mode 100644 index 000000000..23e906449 --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ include "example-webhook.fullname" . }} + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ include "example-webhook.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ include "example-webhook.name" . }} + release: {{ .Release.Name }} + spec: + serviceAccountName: {{ include "example-webhook.fullname" . }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - --tls-cert-file=/tls/tls.crt + - --tls-private-key-file=/tls/tls.key + env: + - name: GROUP_NAME + value: {{ .Values.groupName | quote }} + ports: + - name: https + containerPort: 443 + protocol: TCP + livenessProbe: + httpGet: + scheme: HTTPS + path: /healthz + port: https + readinessProbe: + httpGet: + scheme: HTTPS + path: /healthz + port: https + volumeMounts: + - name: certs + mountPath: /tls + readOnly: true + resources: +{{ toYaml .Values.resources | indent 12 }} + volumes: + - name: certs + secret: + secretName: {{ include "example-webhook.servingCertificate" . }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/pki.yaml b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/pki.yaml new file mode 100644 index 000000000..89b6a23cc --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/pki.yaml @@ -0,0 +1,76 @@ +--- +# Create a selfsigned Issuer, in order to create a root CA certificate for +# signing webhook serving certificates +apiVersion: certmanager.k8s.io/v1alpha1 +kind: Issuer +metadata: + name: {{ include "example-webhook.selfSignedIssuer" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + selfSigned: {} + +--- + +# Generate a CA Certificate used to sign certificates for the webhook +apiVersion: certmanager.k8s.io/v1alpha1 +kind: Certificate +metadata: + name: {{ include "example-webhook.rootCACertificate" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + secretName: {{ include "example-webhook.rootCACertificate" . }} + duration: 43800h # 5y + issuerRef: + name: {{ include "example-webhook.selfSignedIssuer" . }} + commonName: "ca.example-webhook.cert-manager" + isCA: true + +--- + +# Create an Issuer that uses the above generated CA certificate to issue certs +apiVersion: certmanager.k8s.io/v1alpha1 +kind: Issuer +metadata: + name: {{ include "example-webhook.rootCAIssuer" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + ca: + secretName: {{ include "example-webhook.rootCACertificate" . }} + +--- + +# Finally, generate a serving certificate for the webhook to use +apiVersion: certmanager.k8s.io/v1alpha1 +kind: Certificate +metadata: + name: {{ include "example-webhook.servingCertificate" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + secretName: {{ include "example-webhook.servingCertificate" . }} + duration: 8760h # 1y + issuerRef: + name: {{ include "example-webhook.rootCAIssuer" . }} + dnsNames: + - {{ include "example-webhook.fullname" . }} + - {{ include "example-webhook.fullname" . }}.{{ .Release.Namespace }} + - {{ include "example-webhook.fullname" . }}.{{ .Release.Namespace }}.svc diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/rbac.yaml b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/rbac.yaml new file mode 100644 index 000000000..3e51ef0fd --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/rbac.yaml @@ -0,0 +1,90 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "example-webhook.fullname" . }} + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +--- +# Grant the webhook permission to read the ConfigMap containing the Kubernetes +# apiserver's requestheader-ca-certificate. +# This ConfigMap is automatically created by the Kubernetes apiserver. +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: {{ include "example-webhook.fullname" . }}:webhook-authentication-reader + namespace: kube-system + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: extension-apiserver-authentication-reader +subjects: + - apiGroup: "" + kind: ServiceAccount + name: {{ include "example-webhook.fullname" . }} + namespace: {{ .Release.Namespace }} +--- +# apiserver gets the auth-delegator role to delegate auth decisions to +# the core apiserver +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: {{ include "example-webhook.fullname" . }}:auth-delegator + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: + - apiGroup: "" + kind: ServiceAccount + name: {{ include "example-webhook.fullname" . }} + namespace: {{ .Release.Namespace }} +--- +# Grant cert-manager permission to validate using our apiserver +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: {{ include "example-webhook.fullname" . }}:domain-solver + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +rules: + - apiGroups: + - {{ .Values.groupName }} + resources: + - '*' + verbs: + - 'create' +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: {{ include "example-webhook.fullname" . }}:domain-solver + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cert-manager:domain-solver +subjects: + - apiGroup: "" + kind: ServiceAccount + name: {{ .Values.certManager.serviceAccountName }} + namespace: {{ .Values.certManager.namespace }} diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/service.yaml b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/service.yaml new file mode 100644 index 000000000..572089efd --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "example-webhook.fullname" . }} + labels: + app: {{ include "example-webhook.name" . }} + chart: {{ include "example-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: https + protocol: TCP + name: https + selector: + app: {{ include "example-webhook.name" . }} + release: {{ .Release.Name }} diff --git a/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/values.yaml b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/values.yaml new file mode 100644 index 000000000..31eb15140 --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/chart/example-webhook/values.yaml @@ -0,0 +1,43 @@ +# The GroupName here is used to identify your company or business unit that +# created this webhook. +# For example, this may be "acme.mycompany.com". +# This name will need to be referenced in each Issuer's `webhook` stanza to +# inform cert-manager of where to send ChallengePayload resources in order to +# solve the DNS01 challenge. +# This group name should be **unique**, hence using your own company's domain +# here is recommended. +groupName: acme.mycompany.com + +certManager: + namespace: cert-manager + serviceAccountName: cert-manager + +image: + repository: mycompany/webhook-image + tag: latest + pullPolicy: IfNotPresent + +nameOverride: "" +fullnameOverride: "" + +service: + type: ClusterIP + port: 443 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/test/e2e/framework/addon/samplewebhook/sample/main.go b/test/e2e/framework/addon/samplewebhook/sample/main.go new file mode 100644 index 000000000..85aeac934 --- /dev/null +++ b/test/e2e/framework/addon/samplewebhook/sample/main.go @@ -0,0 +1,147 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + + extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + //"k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + + "github.com/jetstack/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1" + "github.com/jetstack/cert-manager/pkg/acme/webhook/cmd" +) + +var GroupName = os.Getenv("GROUP_NAME") + +func main() { + if GroupName == "" { + panic("GROUP_NAME must be specified") + } + + // This will register our custom DNS provider with the webhook serving + // library, making it available as an API under the provided GroupName. + // You can register multiple DNS provider implementations with a single + // webhook, where the Name() method will be used to disambiguate between + // the different implementations. + cmd.RunWebhookServer(GroupName, + &customDNSProviderSolver{}, + ) +} + +// customDNSProviderSolver implements the provider-specific logic needed to +// 'present' an ACME challenge TXT record for your own DNS provider. +// To do so, it must implement the `github.com/jetstack/cert-manager/pkg/acme/webhook.Solver` +// interface. +type customDNSProviderSolver struct { + // If a Kubernetes 'clientset' is needed, you must: + // 1. uncomment the additional `client` field in this structure below + // 2. uncomment the "k8s.io/client-go/kubernetes" import at the top of the file + // 3. uncomment the relevant code in the Initialize method below + // 4. ensure your webhook's service account has the required RBAC role + // assigned to it for interacting with the Kubernetes APIs you need. + //client kubernetes.Clientset +} + +// customDNSProviderConfig is a structure that is used to decode into when +// solving a DNS01 challenge. +// This information is provided by cert-manager, and may be a reference to +// additional configuration that's needed to solve the challenge for this +// particular certificate or issuer. +// This typically includes references to Secret resources containing DNS +// provider credentials, in cases where a 'multi-tenant' DNS solver is being +// created. +// If you do *not* require per-issuer or per-certificate configuration to be +// provided to your webhook, you can skip decoding altogether in favour of +// using CLI flags or similar to provide configuration. +// You should not include sensitive information here. If credentials need to +// be used by your provider here, you should reference a Kubernetes Secret +// resource and fetch these credentials using a Kubernetes clientset. +type customDNSProviderConfig struct { + // Change the two fields below according to the format of the configuration + // to be decoded. + // These fields will be set by users in the + // `issuer.spec.acme.dns01.providers.webhook.config` field. + + //Email string `json:"email"` + //APIKeySecretRef v1alpha1.SecretKeySelector `json:"apiKeySecretRef"` +} + +// Name is used as the name for this DNS solver when referencing it on the ACME +// Issuer resource. +// This should be unique **within the group name**, i.e. you can have two +// solvers configured with the same Name() **so long as they do not co-exist +// within a single webhook deployment**. +// For example, `cloudflare` may be used as the name of a solver. +func (c *customDNSProviderSolver) Name() string { + return "my-custom-solver" +} + +// Present is responsible for actually presenting the DNS record with the +// DNS provider. +// This method should tolerate being called multiple times with the same value. +// cert-manager itself will later perform a self check to ensure that the +// solver has correctly configured the DNS provider. +func (c *customDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error { + cfg, err := loadConfig(ch.Config) + if err != nil { + return err + } + + // TODO: do something more useful with the decoded configuration + fmt.Printf("Decoded configuration %v", cfg) + + // TODO: add code that sets a record in the DNS provider's console + return nil +} + +// CleanUp should delete the relevant TXT record from the DNS provider console. +// If multiple TXT records exist with the same record name (e.g. +// _acme-challenge.example.com) then **only** the record with the same `key` +// value provided on the ChallengeRequest should be cleaned up. +// This is in order to facilitate multiple DNS validations for the same domain +// concurrently. +func (c *customDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error { + // TODO: add code that deletes a record from the DNS provider's console + return nil +} + +// Initialize will be called when the webhook first starts. +// This method can be used to instantiate the webhook, i.e. initialising +// connections or warming up caches. +// Typically, the kubeClientConfig parameter is used to build a Kubernetes +// client that can be used to fetch resources from the Kubernetes API, e.g. +// Secret resources containing credentials used to authenticate with DNS +// provider accounts. +// The stopCh can be used to handle early termination of the webhook, in cases +// where a SIGTERM or similar signal is sent to the webhook process. +func (c *customDNSProviderSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error { + ///// UNCOMMENT THE BELOW CODE TO MAKE A KUBERNETES CLIENTSET AVAILABLE TO + ///// YOUR CUSTOM DNS PROVIDER + + //cl, err := kubernetes.NewForConfig(kubeClientConfig) + //if err != nil { + // return err + //} + // + //c.client = cl + + ///// END OF CODE TO MAKE KUBERNETES CLIENTSET AVAILABLE + return nil +} + +// loadConfig is a small helper function that decodes JSON configuration into +// the typed config struct. +func loadConfig(cfgJSON *extapi.JSON) (customDNSProviderConfig, error) { + cfg := customDNSProviderConfig{} + // handle the 'base case' where no configuration has been provided + if cfgJSON == nil { + return cfg, nil + } + if err := json.Unmarshal(cfgJSON.Raw, &cfg); err != nil { + return cfg, fmt.Errorf("error decoding solver config: %v", err) + } + + return cfg, nil +} diff --git a/test/fixtures/example-webhook-values.yaml b/test/fixtures/example-webhook-values.yaml new file mode 100644 index 000000000..e27f0ce04 --- /dev/null +++ b/test/fixtures/example-webhook-values.yaml @@ -0,0 +1,7 @@ +resources: + requests: + cpu: 50m + memory: 50Mi + limits: + cpu: 100m + memory: 200Mi