diff --git a/.gitignore b/.gitignore index 73da63e55..a0a2e72a7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ styles/ site/ /mkdocs.yml yq -bin \ No newline at end of file +bin +test_config.py diff --git a/README.md b/README.md index 46b2c49dc..83abbe04c 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ flowchart LR Reloader -->|Triggers Rollout| Daemonset Reloader -->|Triggers Rollout| Statefulset Reloader -->|Triggers Rollout| ArgoRollout + Reloader -->|Triggers Rollout| KnativeService Reloader -->|Triggers Job| CronJob Reloader -->|Sends Notification| Slack,Teams,Webhook ``` @@ -104,7 +105,7 @@ Kubernetes does not trigger pod restarts when a referenced `Secret` or `ConfigMa - Restrict reloads to only **Secrets** or only **ConfigMaps** - Watch only **specific resources** - Use **opt-in via tagging** (`search` + `match`) -- Exclude workloads you don’t want to reload +- Exclude workloads you don't want to reload ### 1. 🔁 Automatic Reload (Default) @@ -250,7 +251,7 @@ kubectl apply -k https://github.com/stakater/Reloader/deployments/kubernetes ### 4. 🛠️ Custom Kustomize Setup -You can create your own `kustomization.yaml` and use Reloader’s as a base: +You can create your own `kustomization.yaml` and use Reloader's as a base: ```yaml apiVersion: kustomize.config.k8s.io/v1beta1 diff --git a/deployments/kubernetes/chart/reloader/templates/clusterrole.yaml b/deployments/kubernetes/chart/reloader/templates/clusterrole.yaml index 9f655aa91..bf51f6be1 100644 --- a/deployments/kubernetes/chart/reloader/templates/clusterrole.yaml +++ b/deployments/kubernetes/chart/reloader/templates/clusterrole.yaml @@ -106,6 +106,15 @@ rules: - get - update {{- end}} + - apiGroups: + - "serving.knative.dev" + resources: + - services + verbs: + - list + - get + - update + - patch - apiGroups: - "" resources: diff --git a/deployments/kubernetes/chart/reloader/templates/role.yaml b/deployments/kubernetes/chart/reloader/templates/role.yaml index a031e3e26..c13298300 100644 --- a/deployments/kubernetes/chart/reloader/templates/role.yaml +++ b/deployments/kubernetes/chart/reloader/templates/role.yaml @@ -83,16 +83,15 @@ rules: - delete - list - get -{{- if .Values.reloader.enableHA }} - apiGroups: - - "coordination.k8s.io" + - "serving.knative.dev" resources: - - leases + - services verbs: - - create + - list - get - update -{{- end}} + - patch - apiGroups: - "" resources: @@ -100,4 +99,14 @@ rules: verbs: - create - patch +{{- if .Values.reloader.enableHA }} + - apiGroups: + - "coordination.k8s.io" + resources: + - leases + verbs: + - create + - get + - update +{{- end}} {{- end }} diff --git a/deployments/kubernetes/manifests/clusterrole.yaml b/deployments/kubernetes/manifests/clusterrole.yaml index f2fc68104..62966a0f9 100644 --- a/deployments/kubernetes/manifests/clusterrole.yaml +++ b/deployments/kubernetes/manifests/clusterrole.yaml @@ -51,6 +51,15 @@ rules: - delete - list - get + - apiGroups: + - "serving.knative.dev" + resources: + - services + verbs: + - list + - get + - update + - patch - apiGroups: - "" resources: diff --git a/go.mod b/go.mod index c630aac95..43711262d 100644 --- a/go.mod +++ b/go.mod @@ -16,14 +16,17 @@ require ( k8s.io/client-go v0.32.3 k8s.io/kubectl v0.32.3 k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e + knative.dev/serving v0.42.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect + github.com/blendle/zapdriver v1.3.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect @@ -33,6 +36,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/go-containerregistry v0.13.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -44,6 +48,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/moul/http2curl v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -52,18 +57,23 @@ require ( github.com/smartystreets/goconvey v1.7.2 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/net v0.39.0 // indirect golang.org/x/oauth2 v0.29.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/term v0.31.0 // indirect golang.org/x/text v0.24.0 // indirect golang.org/x/time v0.11.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + knative.dev/networking v0.0.0-20240716111826-bab7f2a3e556 // indirect + knative.dev/pkg v0.0.0-20240716082220-4355f0c73608 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect diff --git a/go.sum b/go.sum index 126602fed..f1901ffa6 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,15 @@ +contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d h1:LblfooH1lKOpp1hIhukktmSAxFkqMPFk9KR6iZ0MJNI= +contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY= +contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= +contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= github.com/argoproj/argo-rollouts v1.8.2 h1:DBvkYvFTEH/zJ9MxJerqz/NMWEgZcHY5vxztyCBS5ak= github.com/argoproj/argo-rollouts v1.8.2/go.mod h1:xZIw+dg+B4IqMv5fNPenIBUiPb9xljL2st1xxkjhaC0= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= +github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -13,8 +21,16 @@ github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 h1:1NyRx2f4W4WBRyg github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380/go.mod h1:thX175TtLTzLj3p7N/Q9IiKZ7NF+p72cvL91emV0hzo= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= @@ -27,6 +43,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= @@ -34,6 +52,8 @@ github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcb github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k= +github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -43,6 +63,9 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -76,12 +99,15 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/openshift/api v0.0.0-20250411135543-10a8fa583797 h1:8x3G8QOZqo2bRAL8JFlPz/odqQECI/XmlZeRwnFxJ8I= github.com/openshift/api v0.0.0-20250411135543-10a8fa583797/go.mod h1:yk60tHAmHhtVpJQo3TwVYq2zpuP70iJIFDCmeKMIzPw= github.com/openshift/client-go v0.0.0-20250402181141-b3bad3b645f2 h1:bPXR0R8zp1o12nSUphN26hSM+OKYq5pMorbDCpApzDQ= github.com/openshift/client-go v0.0.0-20250402181141-b3bad3b645f2/go.mod h1:dT1cJyVTperQ53GvVRa+GZ27r02fDZy2k5j+9QoQsCo= github.com/parnurzeal/gorequest v0.3.0 h1:SoFyqCDC9COr1xuS6VA8fC8RU7XyrJZN2ona1kEX7FI= github.com/parnurzeal/gorequest v0.3.0/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -95,6 +121,8 @@ github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= +github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= +github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -117,13 +145,24 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -136,6 +175,8 @@ golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -161,6 +202,17 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/api v0.183.0 h1:PNMeRDwo1pJdgNcFQ9GstuLe/noWKIc89pRWRLMvLwE= +google.golang.org/api v0.183.0/go.mod h1:q43adC5/pHoSZTx5h2mSmdF7NcyfW9JuDyIOJAgS9ZQ= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -170,6 +222,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -187,6 +241,12 @@ k8s.io/kubectl v0.32.3 h1:VMi584rbboso+yjfv0d8uBHwwxbC438LKq+dXd5tOAI= k8s.io/kubectl v0.32.3/go.mod h1:6Euv2aso5GKzo/UVMacV6C7miuyevpfI91SvBvV9Zdg= k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro= k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +knative.dev/networking v0.0.0-20240716111826-bab7f2a3e556 h1:9OTyJkrjiFh/burZiti3WucGv8Qtt91VJTnXfO5dC2g= +knative.dev/networking v0.0.0-20240716111826-bab7f2a3e556/go.mod h1:1PosUDkXqoHNzYxtLIwa7LFqSsIXBShHOseAb6XBeEU= +knative.dev/pkg v0.0.0-20240716082220-4355f0c73608 h1:BOiRzcnRS9Z5ruxlCiS/K1/Hb5bUN0X4W3xCegdcYQE= +knative.dev/pkg v0.0.0-20240716082220-4355f0c73608/go.mod h1:M67lDZ4KbltYSon0Ox4/6qjlZNOIXW4Ldequ81yofbw= +knative.dev/serving v0.42.0 h1:utItXW+L6inUfJ7Y1LgnbAMc/RyxvvAQNliGU2XC34s= +knative.dev/serving v0.42.0/go.mod h1:3cgU8/864RcqA0ZPrc3jFcmS3uJL/mOlUZiYsXonwaE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= diff --git a/internal/pkg/callbacks/rolling_upgrade.go b/internal/pkg/callbacks/rolling_upgrade.go index c7b2e5ccb..cdeebd51a 100644 --- a/internal/pkg/callbacks/rolling_upgrade.go +++ b/internal/pkg/callbacks/rolling_upgrade.go @@ -19,6 +19,7 @@ import ( "maps" argorolloutv1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + knativeservingv1 "knative.dev/serving/pkg/apis/serving/v1" ) // ItemFunc is a generic function to return a specific resource in given namespace @@ -428,6 +429,15 @@ func GetPatchTemplates() PatchTemplates { } } +// GetKnativePatchTemplates returns patch templates for Knative services +func GetKnativePatchTemplates() PatchTemplates { + return PatchTemplates{ + AnnotationTemplate: `{"spec":{"template":{"metadata":{"annotations":{"%s":"%s"}}}}}`, // merge patch + EnvVarTemplate: `{"spec":{"template":{"spec":{"containers":[{"name":"%s","env":[{"name":"%s","value":"%s"}]}]}}}}`, // merge patch + DeleteEnvVarTemplate: `[{"op":"remove","path":"/spec/template/spec/containers/%d/env/%d"}]`, // JSON patch + } +} + // UpdateDeployment performs rolling upgrade on deployment func UpdateDeployment(clients kube.Clients, namespace string, resource runtime.Object) error { deployment := resource.(*appsv1.Deployment) @@ -577,3 +587,90 @@ func GetStatefulSetVolumes(item runtime.Object) []v1.Volume { func GetRolloutVolumes(item runtime.Object) []v1.Volume { return item.(*argorolloutv1alpha1.Rollout).Spec.Template.Spec.Volumes } + +// GetKnativeServiceItem returns the Knative service in given namespace +func GetKnativeServiceItem(clients kube.Clients, name string, namespace string) (runtime.Object, error) { + knativeService, err := clients.KnativeClient.ServingV1().Services(namespace).Get(context.TODO(), name, meta_v1.GetOptions{}) + if err != nil { + logrus.Errorf("Failed to get Knative service %v", err) + return nil, err + } + + if knativeService.Spec.Template.ObjectMeta.Annotations == nil { + annotations := make(map[string]string) + knativeService.Spec.Template.ObjectMeta.Annotations = annotations + } + + return knativeService, nil +} + +// GetKnativeServiceItems returns the Knative services in given namespace +func GetKnativeServiceItems(clients kube.Clients, namespace string) []runtime.Object { + knativeServices, err := clients.KnativeClient.ServingV1().Services(namespace).List(context.TODO(), meta_v1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list Knative services %v", err) + } + + items := make([]runtime.Object, len(knativeServices.Items)) + // Ensure we always have pod annotations to add to + for i, v := range knativeServices.Items { + if v.Spec.Template.ObjectMeta.Annotations == nil { + annotations := make(map[string]string) + knativeServices.Items[i].Spec.Template.ObjectMeta.Annotations = annotations + } + items[i] = &knativeServices.Items[i] + } + + return items +} + +// GetKnativeServiceAnnotations returns the annotations of given Knative service +func GetKnativeServiceAnnotations(item runtime.Object) map[string]string { + if item.(*knativeservingv1.Service).ObjectMeta.Annotations == nil { + item.(*knativeservingv1.Service).ObjectMeta.Annotations = make(map[string]string) + } + return item.(*knativeservingv1.Service).ObjectMeta.Annotations +} + +// GetKnativeServicePodAnnotations returns the pod's annotations of given Knative service +func GetKnativeServicePodAnnotations(item runtime.Object) map[string]string { + if item.(*knativeservingv1.Service).Spec.Template.ObjectMeta.Annotations == nil { + item.(*knativeservingv1.Service).Spec.Template.ObjectMeta.Annotations = make(map[string]string) + } + return item.(*knativeservingv1.Service).Spec.Template.ObjectMeta.Annotations +} + +// GetKnativeServiceContainers returns the containers of given Knative service +func GetKnativeServiceContainers(item runtime.Object) []v1.Container { + return item.(*knativeservingv1.Service).Spec.Template.Spec.Containers +} + +// GetKnativeServiceInitContainers returns the init containers of given Knative service +func GetKnativeServiceInitContainers(item runtime.Object) []v1.Container { + return item.(*knativeservingv1.Service).Spec.Template.Spec.InitContainers +} + +// GetKnativeServiceVolumes returns the Volumes of given Knative service +func GetKnativeServiceVolumes(item runtime.Object) []v1.Volume { + return item.(*knativeservingv1.Service).Spec.Template.Spec.Volumes +} + +// UpdateKnativeService performs rolling upgrade on Knative service +func UpdateKnativeService(clients kube.Clients, namespace string, resource runtime.Object) error { + knativeService := resource.(*knativeservingv1.Service) + _, err := clients.KnativeClient.ServingV1().Services(namespace).Update(context.TODO(), knativeService, meta_v1.UpdateOptions{FieldManager: "Reloader"}) + return err +} + +// PatchKnativeService performs rolling upgrade on Knative service +func PatchKnativeService(clients kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { + knativeService := resource.(*knativeservingv1.Service) + + // Knative services don't support StrategicMergePatchType, so convert to MergePatchType + if patchType == patchtypes.StrategicMergePatchType { + patchType = patchtypes.MergePatchType + } + + _, err := clients.KnativeClient.ServingV1().Services(namespace).Patch(context.TODO(), knativeService.Name, patchType, bytes, meta_v1.PatchOptions{FieldManager: "Reloader"}) + return err +} diff --git a/internal/pkg/callbacks/rolling_upgrade_test.go b/internal/pkg/callbacks/rolling_upgrade_test.go index b9f48f3fd..238f9d93d 100644 --- a/internal/pkg/callbacks/rolling_upgrade_test.go +++ b/internal/pkg/callbacks/rolling_upgrade_test.go @@ -25,6 +25,8 @@ import ( "github.com/stakater/Reloader/internal/pkg/options" "github.com/stakater/Reloader/internal/pkg/testutil" "github.com/stakater/Reloader/pkg/kube" + knativeservingv1 "knative.dev/serving/pkg/apis/serving/v1" + knativeclientfake "knative.dev/serving/pkg/client/clientset/versioned/fake" ) var ( @@ -51,6 +53,7 @@ func setupTestClients() kube.Clients { return kube.Clients{ KubernetesClient: fake.NewSimpleClientset(), ArgoRolloutClient: fakeargoclientset.NewSimpleClientset(), + KnativeClient: knativeclientfake.NewSimpleClientset(), } } @@ -159,6 +162,12 @@ func TestResourceItem(t *testing.T) { getItemFunc: callbacks.GetStatefulSetItem, deleteFunc: deleteTestStatefulSet, }, + { + name: "KnativeService", + createFunc: createTestKnativeServiceWithAnnotations, + getItemFunc: callbacks.GetKnativeServiceItem, + deleteFunc: deleteTestKnativeService, + }, } for _, tt := range tests { @@ -223,6 +232,13 @@ func TestResourceItems(t *testing.T) { deleteFunc: deleteTestStatefulSets, expectedCount: 2, }, + { + name: "KnativeServices", + createFunc: createTestKnativeServices, + getItemsFunc: callbacks.GetKnativeServiceItems, + deleteFunc: deleteTestKnativeServices, + expectedCount: 2, + }, } for _, tt := range tests { @@ -250,6 +266,7 @@ func TestGetAnnotations(t *testing.T) { {"DaemonSet", &appsv1.DaemonSet{ObjectMeta: metav1.ObjectMeta{Annotations: testAnnotations}}, callbacks.GetDaemonSetAnnotations}, {"StatefulSet", &appsv1.StatefulSet{ObjectMeta: metav1.ObjectMeta{Annotations: testAnnotations}}, callbacks.GetStatefulSetAnnotations}, {"Rollout", &argorolloutv1alpha1.Rollout{ObjectMeta: metav1.ObjectMeta{Annotations: testAnnotations}}, callbacks.GetRolloutAnnotations}, + {"KnativeService", &knativeservingv1.Service{ObjectMeta: metav1.ObjectMeta{Annotations: testAnnotations}}, callbacks.GetKnativeServiceAnnotations}, } for _, tt := range tests { @@ -273,6 +290,7 @@ func TestGetPodAnnotations(t *testing.T) { {"DaemonSet", createResourceWithPodAnnotations(&appsv1.DaemonSet{}, testAnnotations), callbacks.GetDaemonSetPodAnnotations}, {"StatefulSet", createResourceWithPodAnnotations(&appsv1.StatefulSet{}, testAnnotations), callbacks.GetStatefulSetPodAnnotations}, {"Rollout", createResourceWithPodAnnotations(&argorolloutv1alpha1.Rollout{}, testAnnotations), callbacks.GetRolloutPodAnnotations}, + {"KnativeService", createResourceWithPodAnnotations(&knativeservingv1.Service{}, testAnnotations), callbacks.GetKnativeServicePodAnnotations}, } for _, tt := range tests { @@ -296,6 +314,7 @@ func TestGetContainers(t *testing.T) { {"CronJob", createResourceWithContainers(&batchv1.CronJob{}, fixtures.defaultContainers), callbacks.GetCronJobContainers}, {"Job", createResourceWithContainers(&batchv1.Job{}, fixtures.defaultContainers), callbacks.GetJobContainers}, {"Rollout", createResourceWithContainers(&argorolloutv1alpha1.Rollout{}, fixtures.defaultContainers), callbacks.GetRolloutContainers}, + {"KnativeService", createResourceWithContainers(&knativeservingv1.Service{}, fixtures.defaultContainers), callbacks.GetKnativeServiceContainers}, } for _, tt := range tests { @@ -319,6 +338,7 @@ func TestGetInitContainers(t *testing.T) { {"CronJob", createResourceWithInitContainers(&batchv1.CronJob{}, fixtures.defaultInitContainers), callbacks.GetCronJobInitContainers}, {"Job", createResourceWithInitContainers(&batchv1.Job{}, fixtures.defaultInitContainers), callbacks.GetJobInitContainers}, {"Rollout", createResourceWithInitContainers(&argorolloutv1alpha1.Rollout{}, fixtures.defaultInitContainers), callbacks.GetRolloutInitContainers}, + {"KnativeService", createResourceWithInitContainers(&knativeservingv1.Service{}, fixtures.defaultInitContainers), callbacks.GetKnativeServiceInitContainers}, } for _, tt := range tests { @@ -340,6 +360,7 @@ func TestUpdateResources(t *testing.T) { {"Deployment", createTestDeploymentWithAnnotations, callbacks.UpdateDeployment, deleteTestDeployment}, {"DaemonSet", createTestDaemonSetWithAnnotations, callbacks.UpdateDaemonSet, deleteTestDaemonSet}, {"StatefulSet", createTestStatefulSetWithAnnotations, callbacks.UpdateStatefulSet, deleteTestStatefulSet}, + {"KnativeService", createTestKnativeServiceWithAnnotations, callbacks.UpdateKnativeService, deleteTestKnativeService}, } for _, tt := range tests { @@ -387,6 +408,12 @@ func TestPatchResources(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "test", patchedResource.(*appsv1.StatefulSet).ObjectMeta.Annotations["test"]) }}, + {"KnativeService", createTestKnativeServiceWithAnnotations, callbacks.PatchKnativeService, deleteTestKnativeService, func(err error) { + assert.NoError(t, err) + patchedResource, err := callbacks.GetKnativeServiceItem(clients, "test-knative-service", fixtures.namespace) + assert.NoError(t, err) + assert.Equal(t, "test", patchedResource.(*knativeservingv1.Service).ObjectMeta.Annotations["test"]) + }}, {"CronJob", createTestCronJobWithAnnotations, callbacks.PatchCronJob, deleteTestCronJob, func(err error) { assert.EqualError(t, err, "not supported patching: CronJob") }}, @@ -464,6 +491,7 @@ func TestGetVolumes(t *testing.T) { {"Job", createResourceWithVolumes(&batchv1.Job{}, fixtures.defaultVolumes), callbacks.GetJobVolumes}, {"DaemonSet", createResourceWithVolumes(&appsv1.DaemonSet{}, fixtures.defaultVolumes), callbacks.GetDaemonSetVolumes}, {"StatefulSet", createResourceWithVolumes(&appsv1.StatefulSet{}, fixtures.defaultVolumes), callbacks.GetStatefulSetVolumes}, + {"KnativeService", createResourceWithVolumes(&knativeservingv1.Service{}, fixtures.defaultVolumes), callbacks.GetKnativeServiceVolumes}, } for _, tt := range tests { @@ -632,6 +660,8 @@ func createResourceWithPodAnnotations(obj runtime.Object, annotations map[string v.Spec.Template.ObjectMeta.Annotations = annotations case *argorolloutv1alpha1.Rollout: v.Spec.Template.ObjectMeta.Annotations = annotations + case *knativeservingv1.Service: + v.Spec.Template.ObjectMeta.Annotations = annotations } return obj } @@ -650,6 +680,8 @@ func createResourceWithContainers(obj runtime.Object, containers []v1.Container) v.Spec.Template.Spec.Containers = containers case *argorolloutv1alpha1.Rollout: v.Spec.Template.Spec.Containers = containers + case *knativeservingv1.Service: + v.Spec.Template.Spec.Containers = containers } return obj } @@ -668,6 +700,8 @@ func createResourceWithInitContainers(obj runtime.Object, initContainers []v1.Co v.Spec.Template.Spec.InitContainers = initContainers case *argorolloutv1alpha1.Rollout: v.Spec.Template.Spec.InitContainers = initContainers + case *knativeservingv1.Service: + v.Spec.Template.Spec.InitContainers = initContainers } return obj } @@ -684,6 +718,8 @@ func createResourceWithVolumes(obj runtime.Object, volumes []v1.Volume) runtime. v.Spec.Template.Spec.Volumes = volumes case *appsv1.StatefulSet: v.Spec.Template.Spec.Volumes = volumes + case *knativeservingv1.Service: + v.Spec.Template.Spec.Volumes = volumes } return obj } @@ -771,3 +807,34 @@ func isControllerOwner(kind, name string, ownerRefs []metav1.OwnerReference) boo } return false } + +func createTestKnativeServiceWithAnnotations(clients kube.Clients, namespace string, name string) (runtime.Object, error) { + annotations := map[string]string{ + "test": "test", + } + return testutil.CreateKnativeService(clients.KnativeClient.(*knativeclientfake.Clientset), "test-knative-service", namespace, false, annotations) +} + +func createTestKnativeServices(clients kube.Clients, namespace string) error { + for i := 1; i <= 2; i++ { + _, err := testutil.CreateKnativeService(clients.KnativeClient.(*knativeclientfake.Clientset), fmt.Sprintf("test-knative-service-%d", i), namespace, false, nil) + if err != nil { + return err + } + } + return nil +} + +func deleteTestKnativeService(clients kube.Clients, namespace string, name string) error { + return testutil.DeleteKnativeService(clients.KnativeClient.(*knativeclientfake.Clientset), namespace, name) +} + +func deleteTestKnativeServices(clients kube.Clients, namespace string) error { + for i := 1; i <= 2; i++ { + err := testutil.DeleteKnativeService(clients.KnativeClient.(*knativeclientfake.Clientset), namespace, fmt.Sprintf("test-knative-service-%d", i)) + if err != nil { + return err + } + } + return nil +} diff --git a/internal/pkg/handler/upgrade.go b/internal/pkg/handler/upgrade.go index ecb4aae58..978220a52 100644 --- a/internal/pkg/handler/upgrade.go +++ b/internal/pkg/handler/upgrade.go @@ -139,6 +139,24 @@ func GetArgoRolloutRollingUpgradeFuncs() callbacks.RollingUpgradeFuncs { } } +// GetKnativeServiceRollingUpgradeFuncs returns all callback funcs for a Knative service +func GetKnativeServiceRollingUpgradeFuncs() callbacks.RollingUpgradeFuncs { + return callbacks.RollingUpgradeFuncs{ + ItemFunc: callbacks.GetKnativeServiceItem, + ItemsFunc: callbacks.GetKnativeServiceItems, + AnnotationsFunc: callbacks.GetKnativeServiceAnnotations, + PodAnnotationsFunc: callbacks.GetKnativeServicePodAnnotations, + ContainersFunc: callbacks.GetKnativeServiceContainers, + InitContainersFunc: callbacks.GetKnativeServiceInitContainers, + UpdateFunc: callbacks.UpdateKnativeService, + PatchFunc: callbacks.PatchKnativeService, + PatchTemplatesFunc: callbacks.GetKnativePatchTemplates, + VolumesFunc: callbacks.GetKnativeServiceVolumes, + ResourceType: "KnativeService", + SupportsPatch: true, + } +} + func sendUpgradeWebhook(config util.Config, webhookUrl string) error { logrus.Infof("Changes detected in '%s' of type '%s' in namespace '%s', Sending webhook to '%s'", config.ResourceName, config.Type, config.Namespace, webhookUrl) @@ -201,6 +219,12 @@ func doRollingUpgrade(config util.Config, collectors metrics.Collectors, recorde } } + // Add Knative service support + err = rollingUpgrade(clients, config, GetKnativeServiceRollingUpgradeFuncs(), collectors, recorder, invoke) + if err != nil { + return err + } + return nil } diff --git a/internal/pkg/testutil/kube.go b/internal/pkg/testutil/kube.go index f2d3bb4ad..f9c0a8e0d 100644 --- a/internal/pkg/testutil/kube.go +++ b/internal/pkg/testutil/kube.go @@ -25,10 +25,13 @@ import ( appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" core_v1 "k8s.io/client-go/kubernetes/typed/core/v1" + knativeservingv1 "knative.dev/serving/pkg/apis/serving/v1" + knativeclientfake "knative.dev/serving/pkg/client/clientset/versioned/fake" ) var ( @@ -1205,3 +1208,61 @@ func CreateRollout(client argorollout.Interface, rolloutName string, namespace s time.Sleep(3 * time.Second) return rollout, err } + +// CreateKnativeService creates a Knative service for testing +func CreateKnativeService(client *knativeclientfake.Clientset, name string, namespace string, envVarSourcePresent bool, annotations map[string]string) (runtime.Object, error) { + knativeService := GetKnativeService(name, namespace, envVarSourcePresent) + if annotations != nil { + knativeService.ObjectMeta.Annotations = annotations + } + return client.ServingV1().Services(namespace).Create(context.TODO(), knativeService, metav1.CreateOptions{}) +} + +// DeleteKnativeService deletes a Knative service for testing +func DeleteKnativeService(client *knativeclientfake.Clientset, namespace string, name string) error { + return client.ServingV1().Services(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) +} + +// GetKnativeService returns a Knative service for testing +func GetKnativeService(name string, namespace string, envVarSourcePresent bool) *knativeservingv1.Service { + knativeService := &knativeservingv1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: knativeservingv1.ServiceSpec{ + ConfigurationSpec: knativeservingv1.ConfigurationSpec{ + Template: knativeservingv1.RevisionTemplateSpec{ + Spec: knativeservingv1.RevisionSpec{ + PodSpec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "test-container", + Image: "test-image", + }, + }, + }, + }, + }, + }, + }, + } + + if envVarSourcePresent { + knativeService.Spec.Template.Spec.Containers[0].Env = []v1.EnvVar{ + { + Name: "TEST_ENV", + ValueFrom: &v1.EnvVarSource{ + ConfigMapKeyRef: &v1.ConfigMapKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "test-configmap", + }, + Key: "test-key", + }, + }, + }, + } + } + + return knativeService +} diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 423006392..ed0011125 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -11,6 +11,7 @@ import ( "github.com/sirupsen/logrus" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + knativeclient "knative.dev/serving/pkg/client/clientset/versioned" ) // Clients struct exposes interfaces for kubernetes as well as openshift if available @@ -18,6 +19,7 @@ type Clients struct { KubernetesClient kubernetes.Interface OpenshiftAppsClient appsclient.Interface ArgoRolloutClient argorollout.Interface + KnativeClient knativeclient.Interface } var ( @@ -48,10 +50,18 @@ func GetClients() Clients { logrus.Warnf("Unable to create ArgoRollout client error = %v", err) } + var knativeClient *knativeclient.Clientset + + knativeClient, err = GetKnativeClient() + if err != nil { + logrus.Warnf("Unable to create Knative client error = %v", err) + } + return Clients{ KubernetesClient: client, OpenshiftAppsClient: appsClient, ArgoRolloutClient: rolloutClient, + KnativeClient: knativeClient, } } @@ -95,6 +105,14 @@ func GetKubernetesClient() (*kubernetes.Clientset, error) { return kubernetes.NewForConfig(config) } +func GetKnativeClient() (*knativeclient.Clientset, error) { + config, err := getConfig() + if err != nil { + return nil, err + } + return knativeclient.NewForConfig(config) +} + func getConfig() (*rest.Config, error) { var config *rest.Config kubeconfigPath := os.Getenv("KUBECONFIG")