nolar / kopf

A Python framework to write Kubernetes operators in just a few lines of code
https://kopf.readthedocs.io/
MIT License
2.01k stars 154 forks source link

Admission controller validate function not triggering #863

Open jeff-minard-ck opened 2 years ago

jeff-minard-ck commented 2 years ago

Keywords

admission

Problem

I've been attempting to setup an admission (validation) webhook with kopf but, while the code runs, it seems to just admit everything no matter what I code up.

Versions: python 3.9, kopf 1.35.2, k8s 1.21

main code ```python import kopf import logging import os from kubernetes import config # configures the kubernetes client based on finding k8s info in the pod config.load_incluster_config() tel_root = os.environ.get('TELEPRESENCE_ROOT', '') if tel_root: logging.info("Running in telepresence, discovering KSA creds") @kopf.on.login() def login_fn(**_): secrets = tel_root + "/var/run/secrets/kubernetes.io/serviceaccount" with open(secrets+'/token', 'r') as tokenfile: token = tokenfile.read() return kopf.ConnectionInfo( server=os.environ.get('KUBERNETES_PORT_443_TCP').replace('tcp://', 'https://'), insecure=False, ca_path=secrets+'/ca.crt', token=token, ) @kopf.on.startup() def configure(settings: kopf.OperatorSettings, **_): settings.admission.server = kopf.WebhookServer( addr='0.0.0.0', port=8080, cafile=tel_root + '/etc/tls/ca.pem', certfile=tel_root + '/etc/tls/server-tls.crt', pkeyfile=tel_root + '/etc/tls/server-tls.key', ) @kopf.on.validate('apps', 'v1', 'deployments') def c1(spec): print(spec) raise kopf.AdmissionError("I don't like you.") @kopf.on.validate('deployments') def c2(spec): print(spec) raise kopf.AdmissionError("I don't like you.") @kopf.on.validate('deployment') def c3(spec): print(spec) raise kopf.AdmissionError("I don't like you.") @kopf.on.validate('*') def c4(spec): print(spec) raise kopf.AdmissionError("I don't like you.") @kopf.on.validate(kopf.EVERYTHING) def c5(spec): print(spec) raise kopf.AdmissionError("I don't like you.") ```
Helm Chart (to create certs, etc) ```yaml #--- #apiVersion: v1 #kind: Namespace #metadata: # name: {{ $.Release.Namespace }} --- {{- $altNames := list "paas-tod-watch.paas-tod-watch" "paas-tod-watch.paas-tod-watch.svc" }} {{- $ca := genCA "tod-watch-ca" 3650 }} {{- $cert := genSignedCert "tod-watch-webhook" nil $altNames 3650 $ca }} {{- $secretName := printf "%s-%s" "tod-watch-tls" (sha256sum $cert.Key | trunc 50) }} kind: ValidatingWebhookConfiguration apiVersion: admissionregistration.k8s.io/v1 metadata: name: paas-tod-watch webhooks: - name: paas-tod-watch.paas-tod-watch.svc admissionReviewVersions: ["v1", "v1beta1"] failurePolicy: Fail timeoutSeconds: 5 sideEffects: None rules: - operations: ["UPDATE"] apiGroups: ["*"] apiVersions: ["*"] resources: ["deployments"] scope: "*" clientConfig: caBundle: {{ $ca.Cert | b64enc | quote }} service: namespace: paas-tod-watch name: paas-tod-watch path: / --- kind: Secret apiVersion: v1 metadata: name: {{ $secretName }} namespace: {{ $.Release.Namespace }} type: Opaque stringData: ca.pem: | {{- $ca.Cert | trim | nindent 4 }} server-tls.crt: | {{- $cert.Cert | trim | nindent 4 }} server-tls.key: | {{- $cert.Key | trim | nindent 4 }} --- kind: Service apiVersion: v1 metadata: name: paas-tod-watch namespace: paas-tod-watch spec: selector: app: paas-tod-watch ports: - name: https protocol: TCP port: 443 targetPort: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: paas-tod-watch namespace: {{ $.Release.Namespace }} labels: app: paas-tod-watch spec: replicas: 1 selector: matchLabels: app: paas-tod-watch template: metadata: name: paas-tod-watch labels: app: paas-tod-watch spec: terminationGracePeriodSeconds: 1 containers: - name: operator image: alpine command: ["tail", "-f", "/dev/null"] resources: limits: cpu: 1000m memory: 150Mi requests: cpu: 1000m memory: 150Mi volumeMounts: - name: tls mountPath: /etc/tls readOnly: true volumes: - name: tls secret: secretName: {{ $secretName }} ```

I get seemingly valid logs which correspond to me making edits to a deployment:

$ kopf run --standalone --log-format=json tod-watch.py -A -v
{"message": "Running in telepresence, discovering KSA creds", "timestamp": "2021-11-17T01:00:01.563259+00:00", "severity": "info"}
{"message": "Starting Kopf 1.35.2.", "timestamp": "2021-11-17T01:00:01.565091+00:00", "severity": "debug"}
{"message": "Activity 'configure' is invoked.", "timestamp": "2021-11-17T01:00:01.565297+00:00", "severity": "debug"}
{"message": "Activity 'configure' succeeded.", "timestamp": "2021-11-17T01:00:01.566683+00:00", "severity": "info"}
{"message": "Initial authentication has been initiated.", "timestamp": "2021-11-17T01:00:01.567298+00:00", "severity": "info"}
{"message": "Activity 'login_fn' is invoked.", "timestamp": "2021-11-17T01:00:01.567475+00:00", "severity": "debug"}
{"message": "Activity 'login_fn' succeeded.", "timestamp": "2021-11-17T01:00:01.572786+00:00", "severity": "info"}
{"message": "Initial authentication has finished.", "timestamp": "2021-11-17T01:00:01.573157+00:00", "severity": "info"}
{"message": "Starting the watch-stream for customresourcedefinitions.v1.apiextensions.k8s.io cluster-wide.", "timestamp": "2021-11-17T01:00:02.952114+00:00", "severity": "debug"}
{"message": "Using a provided certificate for HTTPS.", "timestamp": "2021-11-17T01:00:02.966136+00:00", "severity": "debug"}
{"message": "Stopping the watch-stream for customresourcedefinitions.v1.apiextensions.k8s.io cluster-wide.", "timestamp": "2021-11-17T01:00:02.982572+00:00", "severity": "debug"}
{"message": "Listening for webhooks at https://0.0.0.0:8080", "timestamp": "2021-11-17T01:00:02.983085+00:00", "severity": "debug"}
{"message": "Accessing the webhooks at https://127.0.0.1:8080", "timestamp": "2021-11-17T01:00:02.983382+00:00", "severity": "debug"}
{"message": "Not enough permissions to watch for resources: changes (creation/deletion/updates) will not be noticed; the resources are only refreshed on operator restarts.", "timestamp": "2021-11-17T01:00:02.983830+00:00", "severity": "warn"}
{"message": "127.0.0.1 [17/Nov/2021:01:00:12 +0000] \"POST /?timeout=5s HTTP/1.1\" 200 300 \"-\" \"kube-apiserver-admission\"", "remote_address": "127.0.0.1", "request_start_time": "[17/Nov/2021:01:00:12 +0000]", "first_request_line": "POST /?timeout=5s HTTP/1.1", "response_status": 200, "response_size": 300, "request_header": {"Referer": "-", "User-Agent": "kube-apiserver-admission"}, "timestamp": "2021-11-17T01:00:12.957825+00:00", "severity": "info"}
{"message": "127.0.0.1 [17/Nov/2021:01:00:14 +0000] \"POST /?timeout=5s HTTP/1.1\" 200 300 \"-\" \"kube-apiserver-admission\"", "remote_address": "127.0.0.1", "request_start_time": "[17/Nov/2021:01:00:14 +0000]", "first_request_line": "POST /?timeout=5s HTTP/1.1", "response_status": 200, "response_size": 300, "request_header": {"Referer": "-", "User-Agent": "kube-apiserver-admission"}, "timestamp": "2021-11-17T01:00:14.529590+00:00", "severity": "info"}

Yet, the function never triggers.

I've been pouring over the docs for a bit and I'm just a bit stumped on what I might be missing to get this moving in the right direction.

Thanks!

agnias-stratagem commented 2 years ago

I'm also having trouble getting a basic validation webhook running. I'm running an abridged version of example 17, and from what I've read what you have should be working. Hoping a developer can chime in. It would be helpful if example 17 had a corresponding ReadMe like other examples do. I'd be happy to write it once I figure out how to actually run it.

hmrks commented 1 month ago

I know this answer is 2 years too late, but for anyone else who is struggling with the same issue, this issue occurs because the webhooks.clientConfig.service.path must match the name of the validating/mutating handler function. In your case, although the request reaches the controller (hence the 200 response), it fails to trigger any action because there is no handler at /.