diff --git a/go.mod b/go.mod index bff72edb..206a9d1b 100644 --- a/go.mod +++ b/go.mod @@ -5,19 +5,14 @@ go 1.25.5 require ( github.com/argoproj/argo-rollouts v1.8.3 github.com/go-logr/logr v1.4.2 - github.com/openshift/api v0.0.0-20250411135543-10a8fa583797 - github.com/openshift/client-go v0.0.0-20250402181141-b3bad3b645f2 - github.com/parnurzeal/gorequest v0.3.0 + github.com/go-logr/zerologr v1.2.3 github.com/prometheus/client_golang v1.22.0 - github.com/sirupsen/logrus v1.9.3 + github.com/rs/zerolog v1.34.0 github.com/spf13/cobra v1.10.1 github.com/spf13/pflag v1.0.9 - github.com/stretchr/testify v1.10.0 k8s.io/api v0.32.3 k8s.io/apimachinery v0.32.3 k8s.io/client-go v0.32.3 - k8s.io/kubectl v0.32.3 - k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 sigs.k8s.io/controller-runtime v0.19.4 ) @@ -25,7 +20,6 @@ require ( github.com/beorn7/perks v1.0.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/fsnotify/fsnotify v1.7.0 // indirect @@ -42,18 +36,16 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 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/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 github.com/prometheus/common v0.63.0 // indirect github.com/prometheus/procfs v0.16.0 // indirect - github.com/smartystreets/goconvey v1.7.2 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect golang.org/x/net v0.39.0 // indirect @@ -70,6 +62,7 @@ require ( k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // 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 945a2757..02ee8daa 100644 --- a/go.sum +++ b/go.sum @@ -4,13 +4,12 @@ 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 h1:1NyRx2f4W4WBRyg0Kys0ZbaNmDDzZ2R/C7DTi+bbsJ0= -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= @@ -25,6 +24,8 @@ 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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs= +github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho= github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -33,6 +34,7 @@ github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZ github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -49,16 +51,12 @@ github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgY github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 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/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= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -71,25 +69,23 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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/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.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= @@ -105,20 +101,16 @@ github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2b github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= 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/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= -github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= -github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -138,7 +130,6 @@ golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6R golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= 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/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= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -153,7 +144,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ 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= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= @@ -165,7 +158,6 @@ golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -186,7 +178,6 @@ 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.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= k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= @@ -201,8 +192,6 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -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-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= diff --git a/internal/pkg/alerts/alert.go b/internal/pkg/alerts/alert.go deleted file mode 100644 index 6b9568ff..00000000 --- a/internal/pkg/alerts/alert.go +++ /dev/null @@ -1,154 +0,0 @@ -package alert - -import ( - "fmt" - "os" - "strings" - - "github.com/parnurzeal/gorequest" - "github.com/sirupsen/logrus" -) - -type AlertSink string - -const ( - AlertSinkSlack AlertSink = "slack" - AlertSinkTeams AlertSink = "teams" - AlertSinkGoogleChat AlertSink = "gchat" - AlertSinkRaw AlertSink = "raw" -) - -// function to send alert msg to webhook service -func SendWebhookAlert(msg string) { - webhook_url, ok := os.LookupEnv("ALERT_WEBHOOK_URL") - if !ok { - logrus.Error("ALERT_WEBHOOK_URL env variable not provided") - return - } - webhook_url = strings.TrimSpace(webhook_url) - alert_sink := os.Getenv("ALERT_SINK") - alert_sink = strings.ToLower(strings.TrimSpace(alert_sink)) - - // Provision to add Proxy to reach webhook server if required - webhook_proxy := os.Getenv("ALERT_WEBHOOK_PROXY") - webhook_proxy = strings.TrimSpace(webhook_proxy) - - // Provision to add Additional information in the alert. e.g ClusterName - alert_additional_info, ok := os.LookupEnv("ALERT_ADDITIONAL_INFO") - if ok { - alert_additional_info = strings.TrimSpace(alert_additional_info) - msg = fmt.Sprintf("%s : %s", alert_additional_info, msg) - } - - switch AlertSink(alert_sink) { - case AlertSinkSlack: - sendSlackAlert(webhook_url, webhook_proxy, msg) - case AlertSinkTeams: - sendTeamsAlert(webhook_url, webhook_proxy, msg) - case AlertSinkGoogleChat: - sendGoogleChatAlert(webhook_url, webhook_proxy, msg) - default: - msg = strings.ReplaceAll(msg, "*", "") - sendRawWebhookAlert(webhook_url, webhook_proxy, msg) - } -} - -// function to handle server redirection -func redirectPolicy(req gorequest.Request, via []gorequest.Request) error { - return fmt.Errorf("incorrect token (redirection)") -} - -// function to send alert to slack -func sendSlackAlert(webhookUrl string, proxy string, msg string) []error { - attachment := Attachment{ - Text: msg, - Color: "good", - AuthorName: "Reloader", - } - - payload := WebhookMessage{ - Attachments: []Attachment{attachment}, - } - - request := gorequest.New().Proxy(proxy) - resp, _, err := request. - Post(webhookUrl). - RedirectPolicy(redirectPolicy). - Send(payload). - End() - - if err != nil { - return err - } - if resp.StatusCode >= 400 { - return []error{fmt.Errorf("error sending msg. status: %v", resp.Status)} - } - - return nil -} - -// function to send alert to Microsoft Teams webhook -func sendTeamsAlert(webhookUrl string, proxy string, msg string) []error { - attachment := Attachment{ - Text: msg, - } - - request := gorequest.New().Proxy(proxy) - resp, _, err := request. - Post(webhookUrl). - RedirectPolicy(redirectPolicy). - Send(attachment). - End() - - if err != nil { - return err - } - if resp.StatusCode != 200 { - return []error{fmt.Errorf("error sending msg. status: %v", resp.Status)} - } - - return nil -} - -// function to send alert to Google Chat webhook -func sendGoogleChatAlert(webhookUrl string, proxy string, msg string) []error { - payload := map[string]interface{}{ - "text": msg, - } - - request := gorequest.New().Proxy(proxy) - resp, _, err := request. - Post(webhookUrl). - RedirectPolicy(redirectPolicy). - Send(payload). - End() - - if err != nil { - return err - } - if resp.StatusCode != 200 { - return []error{fmt.Errorf("error sending msg. status: %v", resp.Status)} - } - - return nil -} - -// function to send alert to webhook service as text -func sendRawWebhookAlert(webhookUrl string, proxy string, msg string) []error { - request := gorequest.New().Proxy(proxy) - resp, _, err := request. - Post(webhookUrl). - Type("text"). - RedirectPolicy(redirectPolicy). - Send(msg). - End() - - if err != nil { - return err - } - if resp.StatusCode >= 400 { - return []error{fmt.Errorf("error sending msg. status: %v", resp.Status)} - } - - return nil -} diff --git a/internal/pkg/alerts/slack_alert.go b/internal/pkg/alerts/slack_alert.go deleted file mode 100644 index a21727a2..00000000 --- a/internal/pkg/alerts/slack_alert.go +++ /dev/null @@ -1,61 +0,0 @@ -package alert - -type WebhookMessage struct { - Username string `json:"username,omitempty"` - IconEmoji string `json:"icon_emoji,omitempty"` - IconURL string `json:"icon_url,omitempty"` - Channel string `json:"channel,omitempty"` - ThreadTimestamp string `json:"thread_ts,omitempty"` - Text string `json:"text,omitempty"` - Attachments []Attachment `json:"attachments,omitempty"` - Parse string `json:"parse,omitempty"` - ResponseType string `json:"response_type,omitempty"` - ReplaceOriginal bool `json:"replace_original,omitempty"` - DeleteOriginal bool `json:"delete_original,omitempty"` - ReplyBroadcast bool `json:"reply_broadcast,omitempty"` -} - -type Attachment struct { - Color string `json:"color,omitempty"` - Fallback string `json:"fallback,omitempty"` - - CallbackID string `json:"callback_id,omitempty"` - ID int `json:"id,omitempty"` - - AuthorID string `json:"author_id,omitempty"` - AuthorName string `json:"author_name,omitempty"` - AuthorSubname string `json:"author_subname,omitempty"` - AuthorLink string `json:"author_link,omitempty"` - AuthorIcon string `json:"author_icon,omitempty"` - - Title string `json:"title,omitempty"` - TitleLink string `json:"title_link,omitempty"` - Pretext string `json:"pretext,omitempty"` - Text string `json:"text,omitempty"` - - ImageURL string `json:"image_url,omitempty"` - ThumbURL string `json:"thumb_url,omitempty"` - - ServiceName string `json:"service_name,omitempty"` - ServiceIcon string `json:"service_icon,omitempty"` - FromURL string `json:"from_url,omitempty"` - OriginalURL string `json:"original_url,omitempty"` - - MarkdownIn []string `json:"mrkdwn_in,omitempty"` - - Footer string `json:"footer,omitempty"` - FooterIcon string `json:"footer_icon,omitempty"` -} - -type Field struct { - Title string `json:"title"` - Value string `json:"value"` - Short bool `json:"short"` -} - -type Action struct { - Type string `json:"type"` - Text string `json:"text"` - Url string `json:"url"` - Style string `json:"style"` -} diff --git a/internal/pkg/app/app.go b/internal/pkg/app/app.go deleted file mode 100644 index 8d09188f..00000000 --- a/internal/pkg/app/app.go +++ /dev/null @@ -1,9 +0,0 @@ -package app - -import "github.com/stakater/Reloader/internal/pkg/cmd" - -// Run runs the command -func Run() error { - cmd := cmd.NewReloaderCommand() - return cmd.Execute() -} diff --git a/internal/pkg/callbacks/rolling_upgrade.go b/internal/pkg/callbacks/rolling_upgrade.go deleted file mode 100644 index 13e5a63c..00000000 --- a/internal/pkg/callbacks/rolling_upgrade.go +++ /dev/null @@ -1,579 +0,0 @@ -package callbacks - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/pkg/kube" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" - v1 "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - patchtypes "k8s.io/apimachinery/pkg/types" - - "maps" - - argorolloutv1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" -) - -// ItemFunc is a generic function to return a specific resource in given namespace -type ItemFunc func(kube.Clients, string, string) (runtime.Object, error) - -// ItemsFunc is a generic function to return a specific resource array in given namespace -type ItemsFunc func(kube.Clients, string) []runtime.Object - -// ContainersFunc is a generic func to return containers -type ContainersFunc func(runtime.Object) []v1.Container - -// InitContainersFunc is a generic func to return containers -type InitContainersFunc func(runtime.Object) []v1.Container - -// VolumesFunc is a generic func to return volumes -type VolumesFunc func(runtime.Object) []v1.Volume - -// UpdateFunc performs the resource update -type UpdateFunc func(kube.Clients, string, runtime.Object) error - -// PatchFunc performs the resource patch -type PatchFunc func(kube.Clients, string, runtime.Object, patchtypes.PatchType, []byte) error - -// PatchTemplateFunc is a generic func to return strategic merge JSON patch template -type PatchTemplatesFunc func() PatchTemplates - -// AnnotationsFunc is a generic func to return annotations -type AnnotationsFunc func(runtime.Object) map[string]string - -// PodAnnotationsFunc is a generic func to return annotations -type PodAnnotationsFunc func(runtime.Object) map[string]string - -// RollingUpgradeFuncs contains generic functions to perform rolling upgrade -type RollingUpgradeFuncs struct { - ItemFunc ItemFunc - ItemsFunc ItemsFunc - AnnotationsFunc AnnotationsFunc - PodAnnotationsFunc PodAnnotationsFunc - ContainersFunc ContainersFunc - ContainerPatchPathFunc ContainersFunc - InitContainersFunc InitContainersFunc - UpdateFunc UpdateFunc - PatchFunc PatchFunc - PatchTemplatesFunc PatchTemplatesFunc - VolumesFunc VolumesFunc - ResourceType string - SupportsPatch bool -} - -// PatchTemplates contains merge JSON patch templates -type PatchTemplates struct { - AnnotationTemplate string - EnvVarTemplate string - DeleteEnvVarTemplate string -} - -// GetDeploymentItem returns the deployment in given namespace -func GetDeploymentItem(clients kube.Clients, name string, namespace string) (runtime.Object, error) { - deployment, err := clients.KubernetesClient.AppsV1().Deployments(namespace).Get(context.TODO(), name, meta_v1.GetOptions{}) - if err != nil { - logrus.Errorf("Failed to get deployment %v", err) - return nil, err - } - - if deployment.Spec.Template.Annotations == nil { - annotations := make(map[string]string) - deployment.Spec.Template.Annotations = annotations - } - - return deployment, nil -} - -// GetDeploymentItems returns the deployments in given namespace -func GetDeploymentItems(clients kube.Clients, namespace string) []runtime.Object { - deployments, err := clients.KubernetesClient.AppsV1().Deployments(namespace).List(context.TODO(), meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list deployments %v", err) - } - - items := make([]runtime.Object, len(deployments.Items)) - // Ensure we always have pod annotations to add to - for i, v := range deployments.Items { - if v.Spec.Template.Annotations == nil { - annotations := make(map[string]string) - deployments.Items[i].Spec.Template.Annotations = annotations - } - items[i] = &deployments.Items[i] - } - - return items -} - -// GetCronJobItem returns the job in given namespace -func GetCronJobItem(clients kube.Clients, name string, namespace string) (runtime.Object, error) { - cronjob, err := clients.KubernetesClient.BatchV1().CronJobs(namespace).Get(context.TODO(), name, meta_v1.GetOptions{}) - if err != nil { - logrus.Errorf("Failed to get cronjob %v", err) - return nil, err - } - - return cronjob, nil -} - -// GetCronJobItems returns the jobs in given namespace -func GetCronJobItems(clients kube.Clients, namespace string) []runtime.Object { - cronjobs, err := clients.KubernetesClient.BatchV1().CronJobs(namespace).List(context.TODO(), meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list cronjobs %v", err) - } - - items := make([]runtime.Object, len(cronjobs.Items)) - // Ensure we always have pod annotations to add to - for i, v := range cronjobs.Items { - if v.Spec.JobTemplate.Spec.Template.Annotations == nil { - annotations := make(map[string]string) - cronjobs.Items[i].Spec.JobTemplate.Spec.Template.Annotations = annotations - } - items[i] = &cronjobs.Items[i] - } - - return items -} - -// GetJobItem returns the job in given namespace -func GetJobItem(clients kube.Clients, name string, namespace string) (runtime.Object, error) { - job, err := clients.KubernetesClient.BatchV1().Jobs(namespace).Get(context.TODO(), name, meta_v1.GetOptions{}) - if err != nil { - logrus.Errorf("Failed to get job %v", err) - return nil, err - } - - return job, nil -} - -// GetJobItems returns the jobs in given namespace -func GetJobItems(clients kube.Clients, namespace string) []runtime.Object { - jobs, err := clients.KubernetesClient.BatchV1().Jobs(namespace).List(context.TODO(), meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list jobs %v", err) - } - - items := make([]runtime.Object, len(jobs.Items)) - // Ensure we always have pod annotations to add to - for i, v := range jobs.Items { - if v.Spec.Template.Annotations == nil { - annotations := make(map[string]string) - jobs.Items[i].Spec.Template.Annotations = annotations - } - items[i] = &jobs.Items[i] - } - - return items -} - -// GetDaemonSetItem returns the daemonSet in given namespace -func GetDaemonSetItem(clients kube.Clients, name string, namespace string) (runtime.Object, error) { - daemonSet, err := clients.KubernetesClient.AppsV1().DaemonSets(namespace).Get(context.TODO(), name, meta_v1.GetOptions{}) - if err != nil { - logrus.Errorf("Failed to get daemonSet %v", err) - return nil, err - } - - return daemonSet, nil -} - -// GetDaemonSetItems returns the daemonSets in given namespace -func GetDaemonSetItems(clients kube.Clients, namespace string) []runtime.Object { - daemonSets, err := clients.KubernetesClient.AppsV1().DaemonSets(namespace).List(context.TODO(), meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list daemonSets %v", err) - } - - items := make([]runtime.Object, len(daemonSets.Items)) - // Ensure we always have pod annotations to add to - for i, v := range daemonSets.Items { - if v.Spec.Template.Annotations == nil { - daemonSets.Items[i].Spec.Template.Annotations = make(map[string]string) - } - items[i] = &daemonSets.Items[i] - } - - return items -} - -// GetStatefulSetItem returns the statefulSet in given namespace -func GetStatefulSetItem(clients kube.Clients, name string, namespace string) (runtime.Object, error) { - statefulSet, err := clients.KubernetesClient.AppsV1().StatefulSets(namespace).Get(context.TODO(), name, meta_v1.GetOptions{}) - if err != nil { - logrus.Errorf("Failed to get statefulSet %v", err) - return nil, err - } - - return statefulSet, nil -} - -// GetStatefulSetItems returns the statefulSets in given namespace -func GetStatefulSetItems(clients kube.Clients, namespace string) []runtime.Object { - statefulSets, err := clients.KubernetesClient.AppsV1().StatefulSets(namespace).List(context.TODO(), meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list statefulSets %v", err) - } - - items := make([]runtime.Object, len(statefulSets.Items)) - // Ensure we always have pod annotations to add to - for i, v := range statefulSets.Items { - if v.Spec.Template.Annotations == nil { - statefulSets.Items[i].Spec.Template.Annotations = make(map[string]string) - } - items[i] = &statefulSets.Items[i] - } - - return items -} - -// GetRolloutItem returns the rollout in given namespace -func GetRolloutItem(clients kube.Clients, name string, namespace string) (runtime.Object, error) { - rollout, err := clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).Get(context.TODO(), name, meta_v1.GetOptions{}) - if err != nil { - logrus.Errorf("Failed to get Rollout %v", err) - return nil, err - } - - return rollout, nil -} - -// GetRolloutItems returns the rollouts in given namespace -func GetRolloutItems(clients kube.Clients, namespace string) []runtime.Object { - rollouts, err := clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).List(context.TODO(), meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list Rollouts %v", err) - } - - items := make([]runtime.Object, len(rollouts.Items)) - // Ensure we always have pod annotations to add to - for i, v := range rollouts.Items { - if v.Spec.Template.Annotations == nil { - rollouts.Items[i].Spec.Template.Annotations = make(map[string]string) - } - items[i] = &rollouts.Items[i] - } - - return items -} - -// GetDeploymentAnnotations returns the annotations of given deployment -func GetDeploymentAnnotations(item runtime.Object) map[string]string { - if item.(*appsv1.Deployment).Annotations == nil { - item.(*appsv1.Deployment).Annotations = make(map[string]string) - } - return item.(*appsv1.Deployment).Annotations -} - -// GetCronJobAnnotations returns the annotations of given cronjob -func GetCronJobAnnotations(item runtime.Object) map[string]string { - if item.(*batchv1.CronJob).Annotations == nil { - item.(*batchv1.CronJob).Annotations = make(map[string]string) - } - return item.(*batchv1.CronJob).Annotations -} - -// GetJobAnnotations returns the annotations of given job -func GetJobAnnotations(item runtime.Object) map[string]string { - if item.(*batchv1.Job).Annotations == nil { - item.(*batchv1.Job).Annotations = make(map[string]string) - } - return item.(*batchv1.Job).Annotations -} - -// GetDaemonSetAnnotations returns the annotations of given daemonSet -func GetDaemonSetAnnotations(item runtime.Object) map[string]string { - if item.(*appsv1.DaemonSet).Annotations == nil { - item.(*appsv1.DaemonSet).Annotations = make(map[string]string) - } - return item.(*appsv1.DaemonSet).Annotations -} - -// GetStatefulSetAnnotations returns the annotations of given statefulSet -func GetStatefulSetAnnotations(item runtime.Object) map[string]string { - if item.(*appsv1.StatefulSet).Annotations == nil { - item.(*appsv1.StatefulSet).Annotations = make(map[string]string) - } - return item.(*appsv1.StatefulSet).Annotations -} - -// GetRolloutAnnotations returns the annotations of given rollout -func GetRolloutAnnotations(item runtime.Object) map[string]string { - if item.(*argorolloutv1alpha1.Rollout).Annotations == nil { - item.(*argorolloutv1alpha1.Rollout).Annotations = make(map[string]string) - } - return item.(*argorolloutv1alpha1.Rollout).Annotations -} - -// GetDeploymentPodAnnotations returns the pod's annotations of given deployment -func GetDeploymentPodAnnotations(item runtime.Object) map[string]string { - if item.(*appsv1.Deployment).Spec.Template.Annotations == nil { - item.(*appsv1.Deployment).Spec.Template.Annotations = make(map[string]string) - } - return item.(*appsv1.Deployment).Spec.Template.Annotations -} - -// GetCronJobPodAnnotations returns the pod's annotations of given cronjob -func GetCronJobPodAnnotations(item runtime.Object) map[string]string { - if item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Annotations == nil { - item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Annotations = make(map[string]string) - } - return item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Annotations -} - -// GetJobPodAnnotations returns the pod's annotations of given job -func GetJobPodAnnotations(item runtime.Object) map[string]string { - if item.(*batchv1.Job).Spec.Template.Annotations == nil { - item.(*batchv1.Job).Spec.Template.Annotations = make(map[string]string) - } - return item.(*batchv1.Job).Spec.Template.Annotations -} - -// GetDaemonSetPodAnnotations returns the pod's annotations of given daemonSet -func GetDaemonSetPodAnnotations(item runtime.Object) map[string]string { - if item.(*appsv1.DaemonSet).Spec.Template.Annotations == nil { - item.(*appsv1.DaemonSet).Spec.Template.Annotations = make(map[string]string) - } - return item.(*appsv1.DaemonSet).Spec.Template.Annotations -} - -// GetStatefulSetPodAnnotations returns the pod's annotations of given statefulSet -func GetStatefulSetPodAnnotations(item runtime.Object) map[string]string { - if item.(*appsv1.StatefulSet).Spec.Template.Annotations == nil { - item.(*appsv1.StatefulSet).Spec.Template.Annotations = make(map[string]string) - } - return item.(*appsv1.StatefulSet).Spec.Template.Annotations -} - -// GetRolloutPodAnnotations returns the pod's annotations of given rollout -func GetRolloutPodAnnotations(item runtime.Object) map[string]string { - if item.(*argorolloutv1alpha1.Rollout).Spec.Template.Annotations == nil { - item.(*argorolloutv1alpha1.Rollout).Spec.Template.Annotations = make(map[string]string) - } - return item.(*argorolloutv1alpha1.Rollout).Spec.Template.Annotations -} - -// GetDeploymentContainers returns the containers of given deployment -func GetDeploymentContainers(item runtime.Object) []v1.Container { - return item.(*appsv1.Deployment).Spec.Template.Spec.Containers -} - -// GetCronJobContainers returns the containers of given cronjob -func GetCronJobContainers(item runtime.Object) []v1.Container { - return item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Spec.Containers -} - -// GetJobContainers returns the containers of given job -func GetJobContainers(item runtime.Object) []v1.Container { - return item.(*batchv1.Job).Spec.Template.Spec.Containers -} - -// GetDaemonSetContainers returns the containers of given daemonSet -func GetDaemonSetContainers(item runtime.Object) []v1.Container { - return item.(*appsv1.DaemonSet).Spec.Template.Spec.Containers -} - -// GetStatefulSetContainers returns the containers of given statefulSet -func GetStatefulSetContainers(item runtime.Object) []v1.Container { - return item.(*appsv1.StatefulSet).Spec.Template.Spec.Containers -} - -// GetRolloutContainers returns the containers of given rollout -func GetRolloutContainers(item runtime.Object) []v1.Container { - return item.(*argorolloutv1alpha1.Rollout).Spec.Template.Spec.Containers -} - -// GetDeploymentInitContainers returns the containers of given deployment -func GetDeploymentInitContainers(item runtime.Object) []v1.Container { - return item.(*appsv1.Deployment).Spec.Template.Spec.InitContainers -} - -// GetCronJobInitContainers returns the containers of given cronjob -func GetCronJobInitContainers(item runtime.Object) []v1.Container { - return item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Spec.InitContainers -} - -// GetJobInitContainers returns the containers of given job -func GetJobInitContainers(item runtime.Object) []v1.Container { - return item.(*batchv1.Job).Spec.Template.Spec.InitContainers -} - -// GetDaemonSetInitContainers returns the containers of given daemonSet -func GetDaemonSetInitContainers(item runtime.Object) []v1.Container { - return item.(*appsv1.DaemonSet).Spec.Template.Spec.InitContainers -} - -// GetStatefulSetInitContainers returns the containers of given statefulSet -func GetStatefulSetInitContainers(item runtime.Object) []v1.Container { - return item.(*appsv1.StatefulSet).Spec.Template.Spec.InitContainers -} - -// GetRolloutInitContainers returns the containers of given rollout -func GetRolloutInitContainers(item runtime.Object) []v1.Container { - return item.(*argorolloutv1alpha1.Rollout).Spec.Template.Spec.InitContainers -} - -// GetPatchTemplates returns patch templates -func GetPatchTemplates() PatchTemplates { - return PatchTemplates{ - AnnotationTemplate: `{"spec":{"template":{"metadata":{"annotations":{"%s":"%s"}}}}}`, // strategic merge patch - EnvVarTemplate: `{"spec":{"template":{"spec":{"containers":[{"name":"%s","env":[{"name":"%s","value":"%s"}]}]}}}}`, // strategic 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) - _, err := clients.KubernetesClient.AppsV1().Deployments(namespace).Update(context.TODO(), deployment, meta_v1.UpdateOptions{FieldManager: "Reloader"}) - return err -} - -// PatchDeployment performs rolling upgrade on deployment -func PatchDeployment(clients kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - deployment := resource.(*appsv1.Deployment) - _, err := clients.KubernetesClient.AppsV1().Deployments(namespace).Patch(context.TODO(), deployment.Name, patchType, bytes, meta_v1.PatchOptions{FieldManager: "Reloader"}) - return err -} - -// CreateJobFromCronjob performs rolling upgrade on cronjob -func CreateJobFromCronjob(clients kube.Clients, namespace string, resource runtime.Object) error { - cronJob := resource.(*batchv1.CronJob) - - annotations := make(map[string]string) - annotations["cronjob.kubernetes.io/instantiate"] = "manual" - maps.Copy(annotations, cronJob.Spec.JobTemplate.Annotations) - - job := &batchv1.Job{ - ObjectMeta: meta_v1.ObjectMeta{ - GenerateName: cronJob.Name + "-", - Namespace: cronJob.Namespace, - Annotations: annotations, - Labels: cronJob.Spec.JobTemplate.Labels, - OwnerReferences: []meta_v1.OwnerReference{*meta_v1.NewControllerRef(cronJob, batchv1.SchemeGroupVersion.WithKind("CronJob"))}, - }, - Spec: cronJob.Spec.JobTemplate.Spec, - } - _, err := clients.KubernetesClient.BatchV1().Jobs(namespace).Create(context.TODO(), job, meta_v1.CreateOptions{FieldManager: "Reloader"}) - return err -} - -func PatchCronJob(clients kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - return errors.New("not supported patching: CronJob") -} - -// ReCreateJobFromjob performs rolling upgrade on job -func ReCreateJobFromjob(clients kube.Clients, namespace string, resource runtime.Object) error { - oldJob := resource.(*batchv1.Job) - job := oldJob.DeepCopy() - - // Delete the old job - policy := meta_v1.DeletePropagationBackground - err := clients.KubernetesClient.BatchV1().Jobs(namespace).Delete(context.TODO(), job.Name, meta_v1.DeleteOptions{PropagationPolicy: &policy}) - if err != nil { - return err - } - - // Remove fields that should not be specified when creating a new Job - job.ResourceVersion = "" - job.UID = "" - job.CreationTimestamp = meta_v1.Time{} - job.Status = batchv1.JobStatus{} - - // Remove problematic labels - delete(job.Spec.Template.Labels, "controller-uid") - delete(job.Spec.Template.Labels, batchv1.ControllerUidLabel) - delete(job.Spec.Template.Labels, batchv1.JobNameLabel) - delete(job.Spec.Template.Labels, "job-name") - - // Remove the selector to allow it to be auto-generated - job.Spec.Selector = nil - - // Create the new job with same spec - _, err = clients.KubernetesClient.BatchV1().Jobs(namespace).Create(context.TODO(), job, meta_v1.CreateOptions{FieldManager: "Reloader"}) - return err -} - -func PatchJob(clients kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - return errors.New("not supported patching: Job") -} - -// UpdateDaemonSet performs rolling upgrade on daemonSet -func UpdateDaemonSet(clients kube.Clients, namespace string, resource runtime.Object) error { - daemonSet := resource.(*appsv1.DaemonSet) - _, err := clients.KubernetesClient.AppsV1().DaemonSets(namespace).Update(context.TODO(), daemonSet, meta_v1.UpdateOptions{FieldManager: "Reloader"}) - return err -} - -func PatchDaemonSet(clients kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - daemonSet := resource.(*appsv1.DaemonSet) - _, err := clients.KubernetesClient.AppsV1().DaemonSets(namespace).Patch(context.TODO(), daemonSet.Name, patchType, bytes, meta_v1.PatchOptions{FieldManager: "Reloader"}) - return err -} - -// UpdateStatefulSet performs rolling upgrade on statefulSet -func UpdateStatefulSet(clients kube.Clients, namespace string, resource runtime.Object) error { - statefulSet := resource.(*appsv1.StatefulSet) - _, err := clients.KubernetesClient.AppsV1().StatefulSets(namespace).Update(context.TODO(), statefulSet, meta_v1.UpdateOptions{FieldManager: "Reloader"}) - return err -} - -func PatchStatefulSet(clients kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - statefulSet := resource.(*appsv1.StatefulSet) - _, err := clients.KubernetesClient.AppsV1().StatefulSets(namespace).Patch(context.TODO(), statefulSet.Name, patchType, bytes, meta_v1.PatchOptions{FieldManager: "Reloader"}) - return err -} - -// UpdateRollout performs rolling upgrade on rollout -func UpdateRollout(clients kube.Clients, namespace string, resource runtime.Object) error { - rollout := resource.(*argorolloutv1alpha1.Rollout) - strategy := rollout.GetAnnotations()[options.RolloutStrategyAnnotation] - var err error - switch options.ToArgoRolloutStrategy(strategy) { - case options.RestartStrategy: - _, err = clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).Patch(context.TODO(), rollout.Name, patchtypes.MergePatchType, []byte(fmt.Sprintf(`{"spec": {"restartAt": "%s"}}`, time.Now().Format(time.RFC3339))), meta_v1.PatchOptions{FieldManager: "Reloader"}) - case options.RolloutStrategy: - _, err = clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).Update(context.TODO(), rollout, meta_v1.UpdateOptions{FieldManager: "Reloader"}) - } - return err -} - -func PatchRollout(clients kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - return errors.New("not supported patching: Rollout") -} - -// GetDeploymentVolumes returns the Volumes of given deployment -func GetDeploymentVolumes(item runtime.Object) []v1.Volume { - return item.(*appsv1.Deployment).Spec.Template.Spec.Volumes -} - -// GetCronJobVolumes returns the Volumes of given cronjob -func GetCronJobVolumes(item runtime.Object) []v1.Volume { - return item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Spec.Volumes -} - -// GetJobVolumes returns the Volumes of given job -func GetJobVolumes(item runtime.Object) []v1.Volume { - return item.(*batchv1.Job).Spec.Template.Spec.Volumes -} - -// GetDaemonSetVolumes returns the Volumes of given daemonSet -func GetDaemonSetVolumes(item runtime.Object) []v1.Volume { - return item.(*appsv1.DaemonSet).Spec.Template.Spec.Volumes -} - -// GetStatefulSetVolumes returns the Volumes of given statefulSet -func GetStatefulSetVolumes(item runtime.Object) []v1.Volume { - return item.(*appsv1.StatefulSet).Spec.Template.Spec.Volumes -} - -// GetRolloutVolumes returns the Volumes of given rollout -func GetRolloutVolumes(item runtime.Object) []v1.Volume { - return item.(*argorolloutv1alpha1.Rollout).Spec.Template.Spec.Volumes -} diff --git a/internal/pkg/callbacks/rolling_upgrade_test.go b/internal/pkg/callbacks/rolling_upgrade_test.go deleted file mode 100644 index 452867f4..00000000 --- a/internal/pkg/callbacks/rolling_upgrade_test.go +++ /dev/null @@ -1,773 +0,0 @@ -package callbacks_test - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - 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/runtime" - watch "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes/fake" - - argorolloutv1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" - fakeargoclientset "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/fake" - patchtypes "k8s.io/apimachinery/pkg/types" - - "github.com/stakater/Reloader/internal/pkg/callbacks" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/testutil" - "github.com/stakater/Reloader/pkg/kube" -) - -var ( - clients = setupTestClients() -) - -type testFixtures struct { - defaultContainers []v1.Container - defaultInitContainers []v1.Container - defaultVolumes []v1.Volume - namespace string -} - -func newTestFixtures() testFixtures { - return testFixtures{ - defaultContainers: []v1.Container{{Name: "container1"}, {Name: "container2"}}, - defaultInitContainers: []v1.Container{{Name: "init-container1"}, {Name: "init-container2"}}, - defaultVolumes: []v1.Volume{{Name: "volume1"}, {Name: "volume2"}}, - namespace: "default", - } -} - -func setupTestClients() kube.Clients { - return kube.Clients{ - KubernetesClient: fake.NewSimpleClientset(), - ArgoRolloutClient: fakeargoclientset.NewSimpleClientset(), - } -} - -// TestUpdateRollout test update rollout strategy annotation -func TestUpdateRollout(t *testing.T) { - namespace := "test-ns" - - cases := map[string]struct { - name string - strategy string - isRestart bool - }{ - "test-without-strategy": { - name: "defaults to rollout strategy", - strategy: "", - isRestart: false, - }, - "test-with-restart-strategy": { - name: "triggers a restart strategy", - strategy: "restart", - isRestart: true, - }, - "test-with-rollout-strategy": { - name: "triggers a rollout strategy", - strategy: "rollout", - isRestart: false, - }, - } - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - rollout, err := testutil.CreateRollout( - clients.ArgoRolloutClient, name, namespace, - map[string]string{options.RolloutStrategyAnnotation: tc.strategy}, - ) - if err != nil { - t.Errorf("creating rollout: %v", err) - } - modifiedChan := watchRollout(rollout.Name, namespace) - - err = callbacks.UpdateRollout(clients, namespace, rollout) - if err != nil { - t.Errorf("updating rollout: %v", err) - } - rollout, err = clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts( - namespace).Get(context.TODO(), rollout.Name, metav1.GetOptions{}) - - if err != nil { - t.Errorf("getting rollout: %v", err) - } - if isRestartStrategy(rollout) == tc.isRestart { - t.Errorf("Should not be a restart strategy") - } - select { - case <-modifiedChan: - // object has been modified - case <-time.After(1 * time.Second): - t.Errorf("Rollout has not been updated") - } - }) - } -} - -func TestPatchRollout(t *testing.T) { - namespace := "test-ns" - rollout := testutil.GetRollout(namespace, "test", map[string]string{options.RolloutStrategyAnnotation: ""}) - err := callbacks.PatchRollout(clients, namespace, rollout, patchtypes.StrategicMergePatchType, []byte(`{"spec": {}}`)) - assert.EqualError(t, err, "not supported patching: Rollout") -} - -func TestResourceItem(t *testing.T) { - fixtures := newTestFixtures() - - tests := []struct { - name string - createFunc func(kube.Clients, string, string) (runtime.Object, error) - getItemFunc func(kube.Clients, string, string) (runtime.Object, error) - deleteFunc func(kube.Clients, string, string) error - }{ - { - name: "Deployment", - createFunc: createTestDeploymentWithAnnotations, - getItemFunc: callbacks.GetDeploymentItem, - deleteFunc: deleteTestDeployment, - }, - { - name: "CronJob", - createFunc: createTestCronJobWithAnnotations, - getItemFunc: callbacks.GetCronJobItem, - deleteFunc: deleteTestCronJob, - }, - { - name: "Job", - createFunc: createTestJobWithAnnotations, - getItemFunc: callbacks.GetJobItem, - deleteFunc: deleteTestJob, - }, - { - name: "DaemonSet", - createFunc: createTestDaemonSetWithAnnotations, - getItemFunc: callbacks.GetDaemonSetItem, - deleteFunc: deleteTestDaemonSet, - }, - { - name: "StatefulSet", - createFunc: createTestStatefulSetWithAnnotations, - getItemFunc: callbacks.GetStatefulSetItem, - deleteFunc: deleteTestStatefulSet, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resource, err := tt.createFunc(clients, fixtures.namespace, "1") - assert.NoError(t, err) - - accessor, err := meta.Accessor(resource) - assert.NoError(t, err) - - _, err = tt.getItemFunc(clients, accessor.GetName(), fixtures.namespace) - assert.NoError(t, err) - - err = tt.deleteFunc(clients, fixtures.namespace, accessor.GetName()) - assert.NoError(t, err) - }) - } -} - -func TestResourceItems(t *testing.T) { - fixtures := newTestFixtures() - - tests := []struct { - name string - createFunc func(kube.Clients, string) error - getItemsFunc func(kube.Clients, string) []runtime.Object - deleteFunc func(kube.Clients, string) error - expectedCount int - }{ - { - name: "Deployments", - createFunc: createTestDeployments, - getItemsFunc: callbacks.GetDeploymentItems, - deleteFunc: deleteTestDeployments, - expectedCount: 2, - }, - { - name: "CronJobs", - createFunc: createTestCronJobs, - getItemsFunc: callbacks.GetCronJobItems, - deleteFunc: deleteTestCronJobs, - expectedCount: 2, - }, - { - name: "Jobs", - createFunc: createTestJobs, - getItemsFunc: callbacks.GetJobItems, - deleteFunc: deleteTestJobs, - expectedCount: 2, - }, - { - name: "DaemonSets", - createFunc: createTestDaemonSets, - getItemsFunc: callbacks.GetDaemonSetItems, - deleteFunc: deleteTestDaemonSets, - expectedCount: 2, - }, - { - name: "StatefulSets", - createFunc: createTestStatefulSets, - getItemsFunc: callbacks.GetStatefulSetItems, - deleteFunc: deleteTestStatefulSets, - expectedCount: 2, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.createFunc(clients, fixtures.namespace) - assert.NoError(t, err) - - items := tt.getItemsFunc(clients, fixtures.namespace) - assert.Equal(t, tt.expectedCount, len(items)) - }) - } -} - -func TestGetAnnotations(t *testing.T) { - testAnnotations := map[string]string{"version": "1"} - - tests := []struct { - name string - resource runtime.Object - getFunc func(runtime.Object) map[string]string - }{ - {"Deployment", &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Annotations: testAnnotations}}, callbacks.GetDeploymentAnnotations}, - {"CronJob", &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{Annotations: testAnnotations}}, callbacks.GetCronJobAnnotations}, - {"Job", &batchv1.Job{ObjectMeta: metav1.ObjectMeta{Annotations: testAnnotations}}, callbacks.GetJobAnnotations}, - {"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}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, testAnnotations, tt.getFunc(tt.resource)) - }) - } -} - -func TestGetPodAnnotations(t *testing.T) { - testAnnotations := map[string]string{"version": "1"} - - tests := []struct { - name string - resource runtime.Object - getFunc func(runtime.Object) map[string]string - }{ - {"Deployment", createResourceWithPodAnnotations(&appsv1.Deployment{}, testAnnotations), callbacks.GetDeploymentPodAnnotations}, - {"CronJob", createResourceWithPodAnnotations(&batchv1.CronJob{}, testAnnotations), callbacks.GetCronJobPodAnnotations}, - {"Job", createResourceWithPodAnnotations(&batchv1.Job{}, testAnnotations), callbacks.GetJobPodAnnotations}, - {"DaemonSet", createResourceWithPodAnnotations(&appsv1.DaemonSet{}, testAnnotations), callbacks.GetDaemonSetPodAnnotations}, - {"StatefulSet", createResourceWithPodAnnotations(&appsv1.StatefulSet{}, testAnnotations), callbacks.GetStatefulSetPodAnnotations}, - {"Rollout", createResourceWithPodAnnotations(&argorolloutv1alpha1.Rollout{}, testAnnotations), callbacks.GetRolloutPodAnnotations}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, testAnnotations, tt.getFunc(tt.resource)) - }) - } -} - -func TestGetContainers(t *testing.T) { - fixtures := newTestFixtures() - - tests := []struct { - name string - resource runtime.Object - getFunc func(runtime.Object) []v1.Container - }{ - {"Deployment", createResourceWithContainers(&appsv1.Deployment{}, fixtures.defaultContainers), callbacks.GetDeploymentContainers}, - {"DaemonSet", createResourceWithContainers(&appsv1.DaemonSet{}, fixtures.defaultContainers), callbacks.GetDaemonSetContainers}, - {"StatefulSet", createResourceWithContainers(&appsv1.StatefulSet{}, fixtures.defaultContainers), callbacks.GetStatefulSetContainers}, - {"CronJob", createResourceWithContainers(&batchv1.CronJob{}, fixtures.defaultContainers), callbacks.GetCronJobContainers}, - {"Job", createResourceWithContainers(&batchv1.Job{}, fixtures.defaultContainers), callbacks.GetJobContainers}, - {"Rollout", createResourceWithContainers(&argorolloutv1alpha1.Rollout{}, fixtures.defaultContainers), callbacks.GetRolloutContainers}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, fixtures.defaultContainers, tt.getFunc(tt.resource)) - }) - } -} - -func TestGetInitContainers(t *testing.T) { - fixtures := newTestFixtures() - - tests := []struct { - name string - resource runtime.Object - getFunc func(runtime.Object) []v1.Container - }{ - {"Deployment", createResourceWithInitContainers(&appsv1.Deployment{}, fixtures.defaultInitContainers), callbacks.GetDeploymentInitContainers}, - {"DaemonSet", createResourceWithInitContainers(&appsv1.DaemonSet{}, fixtures.defaultInitContainers), callbacks.GetDaemonSetInitContainers}, - {"StatefulSet", createResourceWithInitContainers(&appsv1.StatefulSet{}, fixtures.defaultInitContainers), callbacks.GetStatefulSetInitContainers}, - {"CronJob", createResourceWithInitContainers(&batchv1.CronJob{}, fixtures.defaultInitContainers), callbacks.GetCronJobInitContainers}, - {"Job", createResourceWithInitContainers(&batchv1.Job{}, fixtures.defaultInitContainers), callbacks.GetJobInitContainers}, - {"Rollout", createResourceWithInitContainers(&argorolloutv1alpha1.Rollout{}, fixtures.defaultInitContainers), callbacks.GetRolloutInitContainers}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, fixtures.defaultInitContainers, tt.getFunc(tt.resource)) - }) - } -} - -func TestUpdateResources(t *testing.T) { - fixtures := newTestFixtures() - - tests := []struct { - name string - createFunc func(kube.Clients, string, string) (runtime.Object, error) - updateFunc func(kube.Clients, string, runtime.Object) error - deleteFunc func(kube.Clients, string, string) error - }{ - {"Deployment", createTestDeploymentWithAnnotations, callbacks.UpdateDeployment, deleteTestDeployment}, - {"DaemonSet", createTestDaemonSetWithAnnotations, callbacks.UpdateDaemonSet, deleteTestDaemonSet}, - {"StatefulSet", createTestStatefulSetWithAnnotations, callbacks.UpdateStatefulSet, deleteTestStatefulSet}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resource, err := tt.createFunc(clients, fixtures.namespace, "1") - assert.NoError(t, err) - - err = tt.updateFunc(clients, fixtures.namespace, resource) - assert.NoError(t, err) - - accessor, err := meta.Accessor(resource) - assert.NoError(t, err) - - err = tt.deleteFunc(clients, fixtures.namespace, accessor.GetName()) - assert.NoError(t, err) - }) - } -} - -func TestPatchResources(t *testing.T) { - fixtures := newTestFixtures() - - tests := []struct { - name string - createFunc func(kube.Clients, string, string) (runtime.Object, error) - patchFunc func(kube.Clients, string, runtime.Object, patchtypes.PatchType, []byte) error - deleteFunc func(kube.Clients, string, string) error - assertFunc func(err error) - }{ - {"Deployment", createTestDeploymentWithAnnotations, callbacks.PatchDeployment, deleteTestDeployment, func(err error) { - assert.NoError(t, err) - patchedResource, err := callbacks.GetDeploymentItem(clients, "test-deployment", fixtures.namespace) - assert.NoError(t, err) - assert.Equal(t, "test", patchedResource.(*appsv1.Deployment).Annotations["test"]) - }}, - {"DaemonSet", createTestDaemonSetWithAnnotations, callbacks.PatchDaemonSet, deleteTestDaemonSet, func(err error) { - assert.NoError(t, err) - patchedResource, err := callbacks.GetDaemonSetItem(clients, "test-daemonset", fixtures.namespace) - assert.NoError(t, err) - assert.Equal(t, "test", patchedResource.(*appsv1.DaemonSet).Annotations["test"]) - }}, - {"StatefulSet", createTestStatefulSetWithAnnotations, callbacks.PatchStatefulSet, deleteTestStatefulSet, func(err error) { - assert.NoError(t, err) - patchedResource, err := callbacks.GetStatefulSetItem(clients, "test-statefulset", fixtures.namespace) - assert.NoError(t, err) - assert.Equal(t, "test", patchedResource.(*appsv1.StatefulSet).Annotations["test"]) - }}, - {"CronJob", createTestCronJobWithAnnotations, callbacks.PatchCronJob, deleteTestCronJob, func(err error) { - assert.EqualError(t, err, "not supported patching: CronJob") - }}, - {"Job", createTestJobWithAnnotations, callbacks.PatchJob, deleteTestJob, func(err error) { - assert.EqualError(t, err, "not supported patching: Job") - }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resource, err := tt.createFunc(clients, fixtures.namespace, "1") - assert.NoError(t, err) - - err = tt.patchFunc(clients, fixtures.namespace, resource, patchtypes.StrategicMergePatchType, []byte(`{"metadata":{"annotations":{"test":"test"}}}`)) - tt.assertFunc(err) - - accessor, err := meta.Accessor(resource) - assert.NoError(t, err) - - err = tt.deleteFunc(clients, fixtures.namespace, accessor.GetName()) - assert.NoError(t, err) - }) - } -} - -func TestCreateJobFromCronjob(t *testing.T) { - fixtures := newTestFixtures() - - runtimeObj, err := createTestCronJobWithAnnotations(clients, fixtures.namespace, "1") - assert.NoError(t, err) - - cronJob := runtimeObj.(*batchv1.CronJob) - err = callbacks.CreateJobFromCronjob(clients, fixtures.namespace, cronJob) - assert.NoError(t, err) - - jobList, err := clients.KubernetesClient.BatchV1().Jobs(fixtures.namespace).List(context.TODO(), metav1.ListOptions{}) - assert.NoError(t, err) - - ownerFound := false - for _, job := range jobList.Items { - if isControllerOwner("CronJob", cronJob.Name, job.OwnerReferences) { - ownerFound = true - break - } - } - assert.Truef(t, ownerFound, "Missing CronJob owner reference") - - err = deleteTestCronJob(clients, fixtures.namespace, cronJob.Name) - assert.NoError(t, err) -} - -func TestReCreateJobFromJob(t *testing.T) { - fixtures := newTestFixtures() - - job, err := createTestJobWithAnnotations(clients, fixtures.namespace, "1") - assert.NoError(t, err) - - err = callbacks.ReCreateJobFromjob(clients, fixtures.namespace, job.(*batchv1.Job)) - assert.NoError(t, err) - - err = deleteTestJob(clients, fixtures.namespace, "test-job") - assert.NoError(t, err) -} - -func TestGetVolumes(t *testing.T) { - fixtures := newTestFixtures() - - tests := []struct { - name string - resource runtime.Object - getFunc func(runtime.Object) []v1.Volume - }{ - {"Deployment", createResourceWithVolumes(&appsv1.Deployment{}, fixtures.defaultVolumes), callbacks.GetDeploymentVolumes}, - {"CronJob", createResourceWithVolumes(&batchv1.CronJob{}, fixtures.defaultVolumes), callbacks.GetCronJobVolumes}, - {"Job", createResourceWithVolumes(&batchv1.Job{}, fixtures.defaultVolumes), callbacks.GetJobVolumes}, - {"DaemonSet", createResourceWithVolumes(&appsv1.DaemonSet{}, fixtures.defaultVolumes), callbacks.GetDaemonSetVolumes}, - {"StatefulSet", createResourceWithVolumes(&appsv1.StatefulSet{}, fixtures.defaultVolumes), callbacks.GetStatefulSetVolumes}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, fixtures.defaultVolumes, tt.getFunc(tt.resource)) - }) - } -} - -func TesGetPatchTemplateAnnotation(t *testing.T) { - templates := callbacks.GetPatchTemplates() - assert.NotEmpty(t, templates.AnnotationTemplate) - assert.Equal(t, 2, strings.Count(templates.AnnotationTemplate, "%s")) -} - -func TestGetPatchTemplateEnvVar(t *testing.T) { - templates := callbacks.GetPatchTemplates() - assert.NotEmpty(t, templates.EnvVarTemplate) - assert.Equal(t, 3, strings.Count(templates.EnvVarTemplate, "%s")) -} - -func TestGetPatchDeleteTemplateEnvVar(t *testing.T) { - templates := callbacks.GetPatchTemplates() - assert.NotEmpty(t, templates.DeleteEnvVarTemplate) - assert.Equal(t, 2, strings.Count(templates.DeleteEnvVarTemplate, "%d")) -} - -// Helper functions - -func isRestartStrategy(rollout *argorolloutv1alpha1.Rollout) bool { - return rollout.Spec.RestartAt == nil -} - -func watchRollout(name, namespace string) chan interface{} { - timeOut := int64(1) - modifiedChan := make(chan interface{}) - watcher, _ := clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).Watch(context.Background(), metav1.ListOptions{TimeoutSeconds: &timeOut}) - go watchModified(watcher, name, modifiedChan) - return modifiedChan -} - -func watchModified(watcher watch.Interface, name string, modifiedChan chan interface{}) { - for event := range watcher.ResultChan() { - item := event.Object.(*argorolloutv1alpha1.Rollout) - if item.Name == name { - switch event.Type { - case watch.Modified: - modifiedChan <- nil - } - return - } - } -} - -func createTestDeployments(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - _, err := testutil.CreateDeployment(clients.KubernetesClient, fmt.Sprintf("test-deployment-%d", i), namespace, false) - if err != nil { - return err - } - } - return nil -} - -func deleteTestDeployments(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - err := testutil.DeleteDeployment(clients.KubernetesClient, namespace, fmt.Sprintf("test-deployment-%d", i)) - if err != nil { - return err - } - } - return nil -} - -func createTestCronJobs(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - _, err := testutil.CreateCronJob(clients.KubernetesClient, fmt.Sprintf("test-cron-%d", i), namespace, false) - if err != nil { - return err - } - } - return nil -} - -func deleteTestCronJobs(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - err := testutil.DeleteCronJob(clients.KubernetesClient, namespace, fmt.Sprintf("test-cron-%d", i)) - if err != nil { - return err - } - } - return nil -} - -func createTestJobs(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - _, err := testutil.CreateJob(clients.KubernetesClient, fmt.Sprintf("test-job-%d", i), namespace, false) - if err != nil { - return err - } - } - return nil -} - -func deleteTestJobs(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - err := testutil.DeleteJob(clients.KubernetesClient, namespace, fmt.Sprintf("test-job-%d", i)) - if err != nil { - return err - } - } - return nil -} - -func createTestDaemonSets(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - _, err := testutil.CreateDaemonSet(clients.KubernetesClient, fmt.Sprintf("test-daemonset-%d", i), namespace, false) - if err != nil { - return err - } - } - return nil -} - -func deleteTestDaemonSets(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - err := testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, fmt.Sprintf("test-daemonset-%d", i)) - if err != nil { - return err - } - } - return nil -} - -func createTestStatefulSets(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - _, err := testutil.CreateStatefulSet(clients.KubernetesClient, fmt.Sprintf("test-statefulset-%d", i), namespace, false) - if err != nil { - return err - } - } - return nil -} - -func deleteTestStatefulSets(clients kube.Clients, namespace string) error { - for i := 1; i <= 2; i++ { - err := testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, fmt.Sprintf("test-statefulset-%d", i)) - if err != nil { - return err - } - } - return nil -} - -func createResourceWithPodAnnotations(obj runtime.Object, annotations map[string]string) runtime.Object { - switch v := obj.(type) { - case *appsv1.Deployment: - v.Spec.Template.Annotations = annotations - case *appsv1.DaemonSet: - v.Spec.Template.Annotations = annotations - case *appsv1.StatefulSet: - v.Spec.Template.Annotations = annotations - case *batchv1.CronJob: - v.Spec.JobTemplate.Spec.Template.Annotations = annotations - case *batchv1.Job: - v.Spec.Template.Annotations = annotations - case *argorolloutv1alpha1.Rollout: - v.Spec.Template.Annotations = annotations - } - return obj -} - -func createResourceWithContainers(obj runtime.Object, containers []v1.Container) runtime.Object { - switch v := obj.(type) { - case *appsv1.Deployment: - v.Spec.Template.Spec.Containers = containers - case *appsv1.DaemonSet: - v.Spec.Template.Spec.Containers = containers - case *appsv1.StatefulSet: - v.Spec.Template.Spec.Containers = containers - case *batchv1.CronJob: - v.Spec.JobTemplate.Spec.Template.Spec.Containers = containers - case *batchv1.Job: - v.Spec.Template.Spec.Containers = containers - case *argorolloutv1alpha1.Rollout: - v.Spec.Template.Spec.Containers = containers - } - return obj -} - -func createResourceWithInitContainers(obj runtime.Object, initContainers []v1.Container) runtime.Object { - switch v := obj.(type) { - case *appsv1.Deployment: - v.Spec.Template.Spec.InitContainers = initContainers - case *appsv1.DaemonSet: - v.Spec.Template.Spec.InitContainers = initContainers - case *appsv1.StatefulSet: - v.Spec.Template.Spec.InitContainers = initContainers - case *batchv1.CronJob: - v.Spec.JobTemplate.Spec.Template.Spec.InitContainers = initContainers - case *batchv1.Job: - v.Spec.Template.Spec.InitContainers = initContainers - case *argorolloutv1alpha1.Rollout: - v.Spec.Template.Spec.InitContainers = initContainers - } - return obj -} - -func createResourceWithVolumes(obj runtime.Object, volumes []v1.Volume) runtime.Object { - switch v := obj.(type) { - case *appsv1.Deployment: - v.Spec.Template.Spec.Volumes = volumes - case *batchv1.CronJob: - v.Spec.JobTemplate.Spec.Template.Spec.Volumes = volumes - case *batchv1.Job: - v.Spec.Template.Spec.Volumes = volumes - case *appsv1.DaemonSet: - v.Spec.Template.Spec.Volumes = volumes - case *appsv1.StatefulSet: - v.Spec.Template.Spec.Volumes = volumes - } - return obj -} - -func createTestDeploymentWithAnnotations(clients kube.Clients, namespace, version string) (runtime.Object, error) { - deployment := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment", - Namespace: namespace, - Annotations: map[string]string{"version": version}, - }, - } - return clients.KubernetesClient.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{}) -} - -func deleteTestDeployment(clients kube.Clients, namespace, name string) error { - return clients.KubernetesClient.AppsV1().Deployments(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) -} - -func createTestDaemonSetWithAnnotations(clients kube.Clients, namespace, version string) (runtime.Object, error) { - daemonSet := &appsv1.DaemonSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-daemonset", - Namespace: namespace, - Annotations: map[string]string{"version": version}, - }, - } - return clients.KubernetesClient.AppsV1().DaemonSets(namespace).Create(context.TODO(), daemonSet, metav1.CreateOptions{}) -} - -func deleteTestDaemonSet(clients kube.Clients, namespace, name string) error { - return clients.KubernetesClient.AppsV1().DaemonSets(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) -} - -func createTestStatefulSetWithAnnotations(clients kube.Clients, namespace, version string) (runtime.Object, error) { - statefulSet := &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-statefulset", - Namespace: namespace, - Annotations: map[string]string{"version": version}, - }, - } - return clients.KubernetesClient.AppsV1().StatefulSets(namespace).Create(context.TODO(), statefulSet, metav1.CreateOptions{}) -} - -func deleteTestStatefulSet(clients kube.Clients, namespace, name string) error { - return clients.KubernetesClient.AppsV1().StatefulSets(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) -} - -func createTestCronJobWithAnnotations(clients kube.Clients, namespace, version string) (runtime.Object, error) { - cronJob := &batchv1.CronJob{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-cronjob", - Namespace: namespace, - Annotations: map[string]string{"version": version}, - }, - } - return clients.KubernetesClient.BatchV1().CronJobs(namespace).Create(context.TODO(), cronJob, metav1.CreateOptions{}) -} - -func deleteTestCronJob(clients kube.Clients, namespace, name string) error { - return clients.KubernetesClient.BatchV1().CronJobs(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) -} - -func createTestJobWithAnnotations(clients kube.Clients, namespace, version string) (runtime.Object, error) { - job := &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-job", - Namespace: namespace, - Annotations: map[string]string{"version": version}, - }, - } - return clients.KubernetesClient.BatchV1().Jobs(namespace).Create(context.TODO(), job, metav1.CreateOptions{}) -} - -func deleteTestJob(clients kube.Clients, namespace, name string) error { - return clients.KubernetesClient.BatchV1().Jobs(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) -} - -func isControllerOwner(kind, name string, ownerRefs []metav1.OwnerReference) bool { - for _, ownerRef := range ownerRefs { - if *ownerRef.Controller && ownerRef.Kind == kind && ownerRef.Name == name { - return true - } - } - return false -} diff --git a/internal/pkg/cmd/reloader.go b/internal/pkg/cmd/reloader.go deleted file mode 100644 index 3b86bd20..00000000 --- a/internal/pkg/cmd/reloader.go +++ /dev/null @@ -1,265 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "net/http" - _ "net/http/pprof" - "os" - - "github.com/stakater/Reloader/internal/pkg/config" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/leadership" - - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/stakater/Reloader/internal/pkg/controller" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/util" - "github.com/stakater/Reloader/pkg/common" - "github.com/stakater/Reloader/pkg/kube" -) - -// cfg holds the configuration for this reloader instance. -// It is populated by flag parsing and used throughout the application. -var cfg *config.Config - -// NewReloaderCommand starts the reloader controller -func NewReloaderCommand() *cobra.Command { - // Create config with defaults - cfg = config.NewDefault() - - cmd := &cobra.Command{ - Use: "reloader", - Short: "A watcher for your Kubernetes cluster", - PreRunE: validateFlags, - Run: startReloader, - } - - // Bind flags to the new config package - config.BindFlags(cmd.PersistentFlags(), cfg) - - return cmd -} - -func validateFlags(*cobra.Command, []string) error { - // Apply post-parse flag processing (converts string flags to proper types) - if err := config.ApplyFlags(cfg); err != nil { - return fmt.Errorf("applying flags: %w", err) - } - - // Validate the configuration - if err := cfg.Validate(); err != nil { - return fmt.Errorf("validating config: %w", err) - } - - // Sync new config to old options package for backward compatibility - // This bridge allows existing code to keep working during migration - syncConfigToOptions(cfg) - - // Validate that HA options are correct - if cfg.EnableHA { - if err := validateHAEnvs(); err != nil { - return err - } - } - - return nil -} - -// syncConfigToOptions bridges the new Config struct to the old options package. -// This allows existing code to continue working during the migration period. -// TODO: Remove this once all code is migrated to use Config directly. -func syncConfigToOptions(cfg *config.Config) { - options.AutoReloadAll = cfg.AutoReloadAll - options.ConfigmapUpdateOnChangeAnnotation = cfg.Annotations.ConfigmapReload - options.SecretUpdateOnChangeAnnotation = cfg.Annotations.SecretReload - options.ReloaderAutoAnnotation = cfg.Annotations.Auto - options.ConfigmapReloaderAutoAnnotation = cfg.Annotations.ConfigmapAuto - options.SecretReloaderAutoAnnotation = cfg.Annotations.SecretAuto - options.IgnoreResourceAnnotation = cfg.Annotations.Ignore - options.ConfigmapExcludeReloaderAnnotation = cfg.Annotations.ConfigmapExclude - options.SecretExcludeReloaderAnnotation = cfg.Annotations.SecretExclude - options.AutoSearchAnnotation = cfg.Annotations.Search - options.SearchMatchAnnotation = cfg.Annotations.Match - options.RolloutStrategyAnnotation = cfg.Annotations.RolloutStrategy - options.PauseDeploymentAnnotation = cfg.Annotations.PausePeriod - options.PauseDeploymentTimeAnnotation = cfg.Annotations.PausedAt - options.LogFormat = cfg.LogFormat - options.LogLevel = cfg.LogLevel - options.WebhookUrl = cfg.WebhookURL - options.ResourcesToIgnore = cfg.IgnoredResources - options.WorkloadTypesToIgnore = cfg.IgnoredWorkloads - options.NamespacesToIgnore = cfg.IgnoredNamespaces - options.NamespaceSelectors = cfg.NamespaceSelectorStrings - options.ResourceSelectors = cfg.ResourceSelectorStrings - options.EnableHA = cfg.EnableHA - options.SyncAfterRestart = cfg.SyncAfterRestart - options.EnablePProf = cfg.EnablePProf - options.PProfAddr = cfg.PProfAddr - - // Convert ReloadStrategy to string for old options - options.ReloadStrategy = string(cfg.ReloadStrategy) - - // Convert bool flags to string for old options (IsArgoRollouts, ReloadOnCreate, ReloadOnDelete) - if cfg.ArgoRolloutsEnabled { - options.IsArgoRollouts = "true" - } else { - options.IsArgoRollouts = "false" - } - if cfg.ReloadOnCreate { - options.ReloadOnCreate = "true" - } else { - options.ReloadOnCreate = "false" - } - if cfg.ReloadOnDelete { - options.ReloadOnDelete = "true" - } else { - options.ReloadOnDelete = "false" - } -} - -func configureLogging(logFormat, logLevel string) error { - switch logFormat { - case "json": - logrus.SetFormatter(&logrus.JSONFormatter{}) - default: - // just let the library use default on empty string. - if logFormat != "" { - return fmt.Errorf("unsupported logging formatter: %q", logFormat) - } - } - // set log level - level, err := logrus.ParseLevel(logLevel) - if err != nil { - return err - } - logrus.SetLevel(level) - return nil -} - -func validateHAEnvs() error { - podName, podNamespace := getHAEnvs() - - if podName == "" { - return fmt.Errorf("%s not set, cannot run in HA mode without %s set", constants.PodNameEnv, constants.PodNameEnv) - } - if podNamespace == "" { - return fmt.Errorf("%s not set, cannot run in HA mode without %s set", constants.PodNamespaceEnv, constants.PodNamespaceEnv) - } - return nil -} - -func getHAEnvs() (string, string) { - podName := os.Getenv(constants.PodNameEnv) - podNamespace := os.Getenv(constants.PodNamespaceEnv) - - return podName, podNamespace -} - -func startReloader(cmd *cobra.Command, args []string) { - common.GetCommandLineOptions() - err := configureLogging(cfg.LogFormat, cfg.LogLevel) - if err != nil { - logrus.Warn(err) - } - - logrus.Info("Starting Reloader") - isGlobal := false - currentNamespace := os.Getenv("KUBERNETES_NAMESPACE") - if len(currentNamespace) == 0 { - currentNamespace = v1.NamespaceAll - isGlobal = true - logrus.Warnf("KUBERNETES_NAMESPACE is unset, will detect changes in all namespaces.") - } - - // create the clientset - clientset, err := kube.GetKubernetesClient() - if err != nil { - logrus.Fatal(err) - } - - // Use config's IgnoredResources (already validated and normalized to lowercase) - ignoredResourcesList := util.List(cfg.IgnoredResources) - - ignoredNamespacesList := cfg.IgnoredNamespaces - namespaceLabelSelector := "" - - if isGlobal { - namespaceLabelSelector, err = common.GetNamespaceLabelSelector(options.NamespaceSelectors) - if err != nil { - logrus.Fatal(err) - } - } - - resourceLabelSelector, err := common.GetResourceLabelSelector(options.ResourceSelectors) - if err != nil { - logrus.Fatal(err) - } - - if len(namespaceLabelSelector) > 0 { - logrus.Warnf("namespace-selector is set, will only detect changes in namespaces with these labels: %s.", namespaceLabelSelector) - } - - if len(resourceLabelSelector) > 0 { - logrus.Warnf("resource-label-selector is set, will only detect changes on resources with these labels: %s.", resourceLabelSelector) - } - - if cfg.WebhookURL != "" { - logrus.Warnf("webhook-url is set, will only send webhook, no resources will be reloaded") - } - - collectors := metrics.SetupPrometheusEndpoint() - - var controllers []*controller.Controller - for k := range kube.ResourceMap { - if ignoredResourcesList.Contains(k) || (len(namespaceLabelSelector) == 0 && k == "namespaces") { - continue - } - - c, err := controller.NewController(clientset, k, currentNamespace, ignoredNamespacesList, namespaceLabelSelector, resourceLabelSelector, collectors) - if err != nil { - logrus.Fatalf("%s", err) - } - - controllers = append(controllers, c) - - // If HA is enabled we only run the controller when we're the leader - if cfg.EnableHA { - continue - } - // Now let's start the controller - stop := make(chan struct{}) - defer close(stop) - logrus.Infof("Starting Controller to watch resource type: %s", k) - go c.Run(1, stop) - } - - // Run leadership election - if cfg.EnableHA { - podName, podNamespace := getHAEnvs() - lock := leadership.GetNewLock(clientset.CoordinationV1(), constants.LockName, podName, podNamespace) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go leadership.RunLeaderElection(lock, ctx, cancel, podName, controllers) - } - - common.PublishMetaInfoConfigmap(clientset) - - if cfg.EnablePProf { - go startPProfServer() - } - - leadership.SetupLivenessEndpoint() - logrus.Fatal(http.ListenAndServe(cfg.MetricsAddr, nil)) -} - -func startPProfServer() { - logrus.Infof("Starting pprof server on %s", cfg.PProfAddr) - if err := http.ListenAndServe(cfg.PProfAddr, nil); err != nil { - logrus.Errorf("Failed to start pprof server: %v", err) - } -} diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 9ce3e0ce..e7ec77f7 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -1,5 +1,4 @@ // Package config provides configuration management for Reloader. -// It replaces the old global variables pattern with an immutable Config struct. package config import ( @@ -12,11 +11,7 @@ import ( type ReloadStrategy string const ( - // ReloadStrategyEnvVars adds/updates environment variables to trigger restart. - // This is the default and recommended strategy for GitOps compatibility. - ReloadStrategyEnvVars ReloadStrategy = "env-vars" - - // ReloadStrategyAnnotations adds/updates pod template annotations to trigger restart. + ReloadStrategyEnvVars ReloadStrategy = "env-vars" ReloadStrategyAnnotations ReloadStrategy = "annotations" ) @@ -24,143 +19,81 @@ const ( type ArgoRolloutStrategy string const ( - // ArgoRolloutStrategyRestart uses the restart mechanism for Argo Rollouts. ArgoRolloutStrategyRestart ArgoRolloutStrategy = "restart" - - // ArgoRolloutStrategyRollout uses the rollout mechanism for Argo Rollouts. ArgoRolloutStrategyRollout ArgoRolloutStrategy = "rollout" ) // Config holds all configuration for Reloader. -// This struct is immutable after creation - all fields should be set during initialization. type Config struct { - // Annotations holds customizable annotation keys. - Annotations AnnotationConfig - - // AutoReloadAll enables automatic reload for all resources without requiring annotations. - AutoReloadAll bool - - // ReloadStrategy determines how workload restarts are triggered. - ReloadStrategy ReloadStrategy - - // ArgoRolloutsEnabled enables support for Argo Rollouts workload type. + Annotations AnnotationConfig + AutoReloadAll bool + ReloadStrategy ReloadStrategy ArgoRolloutsEnabled bool - - // ArgoRolloutStrategy determines how Argo Rollouts are updated. ArgoRolloutStrategy ArgoRolloutStrategy + ReloadOnCreate bool + ReloadOnDelete bool + SyncAfterRestart bool + EnableHA bool + WebhookURL string - // ReloadOnCreate enables watching for resource creation events. - ReloadOnCreate bool - - // ReloadOnDelete enables watching for resource deletion events. - ReloadOnDelete bool - - // SyncAfterRestart triggers a sync operation after a restart is performed. - SyncAfterRestart bool - - // EnableHA enables high-availability mode with leader election. - EnableHA bool - - // WebhookURL is an optional URL to send notifications to instead of triggering reload. - WebhookURL string - - // Filtering configuration - IgnoredResources []string // ConfigMaps/Secrets to ignore (case-insensitive) - IgnoredWorkloads []string // Workload types to ignore - IgnoredNamespaces []string // Namespaces to ignore - NamespaceSelectors []labels.Selector - ResourceSelectors []labels.Selector - - // Raw selector strings (for backward compatibility with old code) + IgnoredResources []string + IgnoredWorkloads []string + IgnoredNamespaces []string + NamespaceSelectors []labels.Selector + ResourceSelectors []labels.Selector NamespaceSelectorStrings []string ResourceSelectorStrings []string - // Logging configuration - LogFormat string // "json" or "" for default - LogLevel string // trace, debug, info, warning, error, fatal, panic - - // Metrics configuration - MetricsAddr string // Address to serve metrics on (default :9090) - - // Health probe configuration - HealthAddr string // Address to serve health probes on (default :8081) - - // Profiling configuration + LogFormat string + LogLevel string + MetricsAddr string + HealthAddr string EnablePProf bool PProfAddr string - // Alerting configuration - Alerting AlertingConfig - - // Leader election configuration - LeaderElection LeaderElectionConfig - - // WatchedNamespace limits watching to a specific namespace (empty = all namespaces) + Alerting AlertingConfig + LeaderElection LeaderElectionConfig WatchedNamespace string - - // SyncPeriod is the period for re-syncing watched resources - SyncPeriod time.Duration + SyncPeriod time.Duration } -// AnnotationConfig holds all customizable annotation keys. +// AnnotationConfig holds customizable annotation keys. type AnnotationConfig struct { - // Prefix is the base prefix for all annotations (default: reloader.stakater.com) - Prefix string - - // Auto annotations - Auto string // reloader.stakater.com/auto - ConfigmapAuto string // configmap.reloader.stakater.com/auto - SecretAuto string // secret.reloader.stakater.com/auto - - // Reload annotations (explicit resource names) - ConfigmapReload string // configmap.reloader.stakater.com/reload - SecretReload string // secret.reloader.stakater.com/reload - - // Exclude annotations - ConfigmapExclude string // configmaps.exclude.reloader.stakater.com/reload - SecretExclude string // secrets.exclude.reloader.stakater.com/reload - - // Ignore annotation - Ignore string // reloader.stakater.com/ignore - - // Search/Match annotations - Search string // reloader.stakater.com/search - Match string // reloader.stakater.com/match - - // Rollout strategy annotation - RolloutStrategy string // reloader.stakater.com/rollout-strategy - - // Pause annotations - PausePeriod string // deployment.reloader.stakater.com/pause-period - PausedAt string // deployment.reloader.stakater.com/paused-at - - // Last reloaded from annotation (set by Reloader) - LastReloadedFrom string // reloader.stakater.com/last-reloaded-from + Prefix string + Auto string + ConfigmapAuto string + SecretAuto string + ConfigmapReload string + SecretReload string + ConfigmapExclude string + SecretExclude string + Ignore string + Search string + Match string + RolloutStrategy string + PausePeriod string + PausedAt string + LastReloadedFrom string } // AlertingConfig holds configuration for alerting integrations. type AlertingConfig struct { - // Enabled enables alerting notifications on reload events. - Enabled bool - - // WebhookURL is the webhook URL to send alerts to. + Enabled bool WebhookURL string - - // Sink determines the alert format: "slack", "teams", "gchat", or "raw" (default). - Sink string - - // Proxy is an optional HTTP proxy for webhook requests. - Proxy string - - // Additional is optional context prepended to alert messages. + Sink string + Proxy string Additional string } // LeaderElectionConfig holds configuration for leader election. type LeaderElectionConfig struct { - LockName string - Namespace string - Identity string + LockName string + Namespace string + Identity string + LeaseDuration time.Duration + RenewDeadline time.Duration + RetryPeriod time.Duration + ReleaseOnCancel bool } // NewDefault creates a Config with default values. @@ -189,7 +122,11 @@ func NewDefault() *Config { PProfAddr: ":6060", Alerting: AlertingConfig{}, LeaderElection: LeaderElectionConfig{ - LockName: "stakater-reloader-lock", + LockName: "reloader-leader-election", + LeaseDuration: 15 * time.Second, + RenewDeadline: 10 * time.Second, + RetryPeriod: 2 * time.Second, + ReleaseOnCancel: true, }, WatchedNamespace: "", SyncPeriod: 0, @@ -247,7 +184,6 @@ func (c *Config) IsNamespaceIgnored(namespace string) bool { return false } -// equalFold is a simple case-insensitive string comparison. func equalFold(s, t string) bool { if len(s) != len(t) { return false @@ -255,7 +191,6 @@ func equalFold(s, t string) bool { for i := 0; i < len(s); i++ { c1, c2 := s[i], t[i] if c1 != c2 { - // Convert to lowercase for comparison if 'A' <= c1 && c1 <= 'Z' { c1 += 'a' - 'A' } diff --git a/internal/pkg/config/flags.go b/internal/pkg/config/flags.go index ef423082..f3fe4955 100644 --- a/internal/pkg/config/flags.go +++ b/internal/pkg/config/flags.go @@ -46,9 +46,21 @@ func BindFlags(fs *pflag.FlagSet, cfg *Config) { fs.BoolVar(&cfg.SyncAfterRestart, "sync-after-restart", cfg.SyncAfterRestart, "Trigger sync operation after restart") - // High availability + // High availability / Leader election fs.BoolVar(&cfg.EnableHA, "enable-ha", cfg.EnableHA, "Enable high-availability mode with leader election") + fs.StringVar(&cfg.LeaderElection.LockName, "leader-election-id", cfg.LeaderElection.LockName, + "Name of the lease resource for leader election") + fs.StringVar(&cfg.LeaderElection.Namespace, "leader-election-namespace", cfg.LeaderElection.Namespace, + "Namespace for the leader election lease (defaults to pod namespace)") + fs.DurationVar(&cfg.LeaderElection.LeaseDuration, "leader-election-lease-duration", cfg.LeaderElection.LeaseDuration, + "Duration that non-leader candidates will wait before attempting to acquire leadership") + fs.DurationVar(&cfg.LeaderElection.RenewDeadline, "leader-election-renew-deadline", cfg.LeaderElection.RenewDeadline, + "Duration that the acting leader will retry refreshing leadership before giving up") + fs.DurationVar(&cfg.LeaderElection.RetryPeriod, "leader-election-retry-period", cfg.LeaderElection.RetryPeriod, + "Duration between leader election retries") + fs.BoolVar(&cfg.LeaderElection.ReleaseOnCancel, "leader-election-release-on-cancel", cfg.LeaderElection.ReleaseOnCancel, + "Release the leader lock when the manager is stopped") // Webhook fs.StringVar(&cfg.WebhookURL, "webhook-url", cfg.WebhookURL, diff --git a/internal/pkg/constants/constants.go b/internal/pkg/constants/constants.go index 18d1cc75..c1dc81a0 100644 --- a/internal/pkg/constants/constants.go +++ b/internal/pkg/constants/constants.go @@ -1,32 +1,7 @@ package constants +// Environment variable names for pod identity in HA mode. const ( - // DefaultHttpListenAddr is the default listening address for global http server - DefaultHttpListenAddr = ":9090" - - // ConfigmapEnvVarPostfix is a postfix for configmap envVar - ConfigmapEnvVarPostfix = "CONFIGMAP" - // SecretEnvVarPostfix is a postfix for secret envVar - SecretEnvVarPostfix = "SECRET" - // EnvVarPrefix is a Prefix for environment variable - EnvVarPrefix = "STAKATER_" - - // ReloaderAnnotationPrefix is a Prefix for all reloader annotations - ReloaderAnnotationPrefix = "reloader.stakater.com" - // LastReloadedFromAnnotation is an annotation used to describe the last resource that triggered a reload - LastReloadedFromAnnotation = "last-reloaded-from" - - // ReloadStrategyFlag The reload strategy flag name - ReloadStrategyFlag = "reload-strategy" - // EnvVarsReloadStrategy instructs Reloader to add container environment variables to facilitate a restart - EnvVarsReloadStrategy = "env-vars" - // AnnotationsReloadStrategy instructs Reloader to add pod template annotations to facilitate a restart - AnnotationsReloadStrategy = "annotations" -) - -// Leadership election related consts -const ( - LockName string = "stakater-reloader-lock" PodNameEnv string = "POD_NAME" PodNamespaceEnv string = "POD_NAMESPACE" ) diff --git a/internal/pkg/constants/enums.go b/internal/pkg/constants/enums.go deleted file mode 100644 index 43fc6035..00000000 --- a/internal/pkg/constants/enums.go +++ /dev/null @@ -1,15 +0,0 @@ -package constants - -// Result is a status for deployment update -type Result int - -const ( - // Updated is returned when environment variable is created/updated - Updated Result = 1 + iota - // NotUpdated is returned when environment variable is found but had value equals to the new value - NotUpdated - // NoEnvVarFound is returned when no environment variable is found - NoEnvVarFound - // NoContainerFound is returned when no environment variable is found - NoContainerFound -) diff --git a/internal/pkg/controller/configmap_reconciler.go b/internal/pkg/controller/configmap_reconciler.go index cb78a7ba..447348c0 100644 --- a/internal/pkg/controller/configmap_reconciler.go +++ b/internal/pkg/controller/configmap_reconciler.go @@ -6,6 +6,7 @@ import ( "time" "github.com/go-logr/logr" + "github.com/stakater/Reloader/internal/pkg/alerting" "github.com/stakater/Reloader/internal/pkg/config" "github.com/stakater/Reloader/internal/pkg/events" "github.com/stakater/Reloader/internal/pkg/metrics" @@ -33,6 +34,7 @@ type ConfigMapReconciler struct { Collectors *metrics.Collectors EventRecorder *events.Recorder WebhookClient *webhook.Client + Alerter alerting.Alerter initialized bool initOnce sync.Once @@ -131,6 +133,19 @@ func (r *ConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( "workload", decision.Workload.GetName(), "kind", decision.Workload.Kind(), ) + + // Send alert notification + if err := r.Alerter.Send(ctx, alerting.AlertMessage{ + WorkloadKind: string(decision.Workload.Kind()), + WorkloadName: decision.Workload.GetName(), + WorkloadNamespace: decision.Workload.GetNamespace(), + ResourceKind: "ConfigMap", + ResourceName: cm.Name, + ResourceNamespace: cm.Namespace, + Timestamp: time.Now(), + }); err != nil { + log.Error(err, "failed to send alert") + } } } @@ -202,6 +217,19 @@ func (r *ConfigMapReconciler) handleDelete(ctx context.Context, req ctrl.Request if updated { r.EventRecorder.ReloadSuccess(decision.Workload.GetObject(), "ConfigMap", req.Name) r.recordMetrics(true, req.Namespace) + + // Send alert notification + if err := r.Alerter.Send(ctx, alerting.AlertMessage{ + WorkloadKind: string(decision.Workload.Kind()), + WorkloadName: decision.Workload.GetName(), + WorkloadNamespace: decision.Workload.GetNamespace(), + ResourceKind: "ConfigMap", + ResourceName: req.Name, + ResourceNamespace: req.Namespace, + Timestamp: time.Now(), + }); err != nil { + log.Error(err, "failed to send alert") + } } } diff --git a/internal/pkg/controller/controller.go b/internal/pkg/controller/controller.go deleted file mode 100644 index 15b2e0f1..00000000 --- a/internal/pkg/controller/controller.go +++ /dev/null @@ -1,282 +0,0 @@ -package controller - -import ( - "fmt" - "time" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/handler" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/util" - "github.com/stakater/Reloader/pkg/kube" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" - "k8s.io/kubectl/pkg/scheme" - "k8s.io/utils/strings/slices" -) - -// Controller for checking events -type Controller struct { - client kubernetes.Interface - indexer cache.Indexer - queue workqueue.TypedRateLimitingInterface[any] - informer cache.Controller - namespace string - resource string - ignoredNamespaces util.List - collectors metrics.Collectors - recorder record.EventRecorder - namespaceSelector string - resourceSelector string -} - -// controllerInitialized flag determines whether controlled is being initialized -var secretControllerInitialized bool = false -var configmapControllerInitialized bool = false -var selectedNamespacesCache []string - -// NewController for initializing a Controller -func NewController( - client kubernetes.Interface, resource string, namespace string, ignoredNamespaces []string, namespaceLabelSelector string, resourceLabelSelector string, collectors metrics.Collectors) (*Controller, error) { - - if options.SyncAfterRestart { - secretControllerInitialized = true - configmapControllerInitialized = true - } - - c := Controller{ - client: client, - namespace: namespace, - ignoredNamespaces: ignoredNamespaces, - namespaceSelector: namespaceLabelSelector, - resourceSelector: resourceLabelSelector, - resource: resource, - } - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{ - Interface: client.CoreV1().Events(""), - }) - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: fmt.Sprintf("reloader-%s", resource)}) - - queue := workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[any]()) - - optionsModifier := func(options *metav1.ListOptions) { - if resource == "namespaces" { - options.LabelSelector = c.namespaceSelector - } else if len(c.resourceSelector) > 0 { - options.LabelSelector = c.resourceSelector - } else { - options.FieldSelector = fields.Everything().String() - } - } - - listWatcher := cache.NewFilteredListWatchFromClient(client.CoreV1().RESTClient(), resource, namespace, optionsModifier) - - _, informer := cache.NewInformerWithOptions(cache.InformerOptions{ - ListerWatcher: listWatcher, - ObjectType: kube.ResourceMap[resource], - ResyncPeriod: 0, - Handler: cache.ResourceEventHandlerFuncs{ - AddFunc: c.Add, - UpdateFunc: c.Update, - DeleteFunc: c.Delete, - }, - Indexers: cache.Indexers{}, - }) - c.informer = informer - c.queue = queue - c.collectors = collectors - c.recorder = recorder - - logrus.Infof("created controller for: %s", resource) - return &c, nil -} - -// Add function to add a new object to the queue in case of creating a resource -func (c *Controller) Add(obj interface{}) { - - switch object := obj.(type) { - case *v1.Namespace: - c.addSelectedNamespaceToCache(*object) - return - } - - if options.ReloadOnCreate == "true" { - if !c.resourceInIgnoredNamespace(obj) && c.resourceInSelectedNamespaces(obj) && secretControllerInitialized && configmapControllerInitialized { - c.queue.Add(handler.ResourceCreatedHandler{ - Resource: obj, - Collectors: c.collectors, - Recorder: c.recorder, - }) - } - } -} - -func (c *Controller) resourceInIgnoredNamespace(raw interface{}) bool { - switch object := raw.(type) { - case *v1.ConfigMap: - return c.ignoredNamespaces.Contains(object.Namespace) - case *v1.Secret: - return c.ignoredNamespaces.Contains(object.Namespace) - } - return false -} - -func (c *Controller) resourceInSelectedNamespaces(raw interface{}) bool { - if len(c.namespaceSelector) == 0 { - return true - } - - switch object := raw.(type) { - case *v1.ConfigMap: - if slices.Contains(selectedNamespacesCache, object.GetNamespace()) { - return true - } - case *v1.Secret: - if slices.Contains(selectedNamespacesCache, object.GetNamespace()) { - return true - } - } - return false -} - -func (c *Controller) addSelectedNamespaceToCache(namespace v1.Namespace) { - selectedNamespacesCache = append(selectedNamespacesCache, namespace.GetName()) - logrus.Infof("added namespace to be watched: %s", namespace.GetName()) -} - -func (c *Controller) removeSelectedNamespaceFromCache(namespace v1.Namespace) { - for i, v := range selectedNamespacesCache { - if v == namespace.GetName() { - selectedNamespacesCache = append(selectedNamespacesCache[:i], selectedNamespacesCache[i+1:]...) - logrus.Infof("removed namespace from watch: %s", namespace.GetName()) - return - } - } -} - -// Update function to add an old object and a new object to the queue in case of updating a resource -func (c *Controller) Update(old interface{}, new interface{}) { - switch new.(type) { - case *v1.Namespace: - return - } - - if !c.resourceInIgnoredNamespace(new) && c.resourceInSelectedNamespaces(new) { - c.queue.Add(handler.ResourceUpdatedHandler{ - Resource: new, - OldResource: old, - Collectors: c.collectors, - Recorder: c.recorder, - }) - } -} - -// Delete function to add an object to the queue in case of deleting a resource -func (c *Controller) Delete(old interface{}) { - - if options.ReloadOnDelete == "true" { - if !c.resourceInIgnoredNamespace(old) && c.resourceInSelectedNamespaces(old) && secretControllerInitialized && configmapControllerInitialized { - c.queue.Add(handler.ResourceDeleteHandler{ - Resource: old, - Collectors: c.collectors, - Recorder: c.recorder, - }) - } - } - - switch object := old.(type) { - case *v1.Namespace: - c.removeSelectedNamespaceFromCache(*object) - return - } -} - -// Run function for controller which handles the queue -func (c *Controller) Run(threadiness int, stopCh chan struct{}) { - defer runtime.HandleCrash() - - // Let the workers stop when we are done - defer c.queue.ShutDown() - - go c.informer.Run(stopCh) - - // Wait for all involved caches to be synced, before processing items from the queue is started - if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) { - runtime.HandleError(fmt.Errorf("timed out waiting for caches to sync")) - return - } - - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - - <-stopCh - logrus.Infof("Stopping Controller") -} - -func (c *Controller) runWorker() { - // At this point the controller is fully initialized and we can start processing the resources - if c.resource == string(v1.ResourceSecrets) { - secretControllerInitialized = true - } else if c.resource == string(v1.ResourceConfigMaps) { - configmapControllerInitialized = true - } - - for c.processNextItem() { - } -} - -func (c *Controller) processNextItem() bool { - // Wait until there is a new item in the working queue - resourceHandler, quit := c.queue.Get() - if quit { - return false - } - // Tell the queue that we are done with processing this key. This unblocks the key for other workers - // This allows safe parallel processing because two events with the same key are never processed in - // parallel. - defer c.queue.Done(resourceHandler) - - // Invoke the method containing the business logic - err := resourceHandler.(handler.ResourceHandler).Handle() - // Handle the error if something went wrong during the execution of the business logic - c.handleErr(err, resourceHandler) - return true -} - -// handleErr checks if an error happened and makes sure we will retry later. -func (c *Controller) handleErr(err error, key interface{}) { - if err == nil { - // Forget about the #AddRateLimited history of the key on every successful synchronization. - // This ensures that future processing of updates for this key is not delayed because of - // an outdated error history. - c.queue.Forget(key) - return - } - - // This controller retries 5 times if something goes wrong. After that, it stops trying. - if c.queue.NumRequeues(key) < 5 { - logrus.Errorf("Error syncing events: %v", err) - - // Re-enqueue the key rate limited. Based on the rate limiter on the - // queue and the re-enqueue history, the key will be processed later again. - c.queue.AddRateLimited(key) - return - } - - c.queue.Forget(key) - // Report to an external entity that, even after several retries, we could not successfully process this key - runtime.HandleError(err) - logrus.Errorf("Dropping key out of the queue: %v", err) - logrus.Debugf("Dropping the key %q out of the queue: %v", key, err) -} diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go deleted file mode 100644 index 58580c79..00000000 --- a/internal/pkg/controller/controller_test.go +++ /dev/null @@ -1,2368 +0,0 @@ -//go:build integration -// +build integration - -package controller - -import ( - "context" - "os" - "testing" - "time" - - "github.com/stakater/Reloader/internal/pkg/constants" - - "github.com/stakater/Reloader/internal/pkg/metrics" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/handler" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/testutil" - "github.com/stakater/Reloader/internal/pkg/util" - "github.com/stakater/Reloader/pkg/common" - "github.com/stakater/Reloader/pkg/kube" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -var ( - clients = kube.GetClients() - namespace = "test-reloader-" + testutil.RandSeq(5) - configmapNamePrefix = "testconfigmap-reloader" - secretNamePrefix = "testsecret-reloader" - data = "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" - newData = "dGVzdE5ld1NlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" - updatedData = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" - collectors = metrics.NewCollectors() -) - -const ( - sleepDuration = 3 * time.Second -) - -func TestMain(m *testing.M) { - - testutil.CreateNamespace(namespace, clients.KubernetesClient) - - logrus.Infof("Creating controller") - for k := range kube.ResourceMap { - if k == "namespaces" { - continue - } - c, err := NewController(clients.KubernetesClient, k, namespace, []string{}, "", "", collectors) - if err != nil { - logrus.Fatalf("%s", err) - } - - // Now let's start the controller - stop := make(chan struct{}) - defer close(stop) - go c.Run(1, stop) - } - time.Sleep(sleepDuration) - - logrus.Infof("Running Testcases") - retCode := m.Run() - - testutil.DeleteNamespace(namespace, clients.KubernetesClient) - - os.Exit(retCode) -} - -// Perform rolling upgrade on deployment and create pod annotation var upon updating the configmap -func TestControllerUpdatingConfigmapShouldCreatePodAnnotationInDeployment(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying deployment update - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and create pod annotation var upon updating the configmap -func TestControllerUpdatingConfigmapShouldAutoCreatePodAnnotationInDeployment(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, false) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying deployment update - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and create pod annotation var upon creating the configmap -func TestControllerCreatingConfigmapShouldCreatePodAnnotationInDeployment(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // TODO: Fix this test case - t.Skip("Skipping TestControllerCreatingConfigmapShouldCreatePodAnnotationInDeployment test case") - - // Creating configmap - configmapName := configmapNamePrefix + "-create-" + testutil.RandSeq(5) - _, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Deleting configmap for first time - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - - time.Sleep(sleepDuration) - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.stakater.com") - if err != nil { - t.Errorf("Error while creating the configmap second time %v", err) - } - - time.Sleep(sleepDuration) - - // Verifying deployment update - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and update pod annotation var upon updating the configmap -func TestControllerForUpdatingConfigmapShouldUpdateDeploymentUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Updating configmap for second time - updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying deployment update - logrus.Infof("Verifying pod annotation has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Do not Perform rolling upgrade on deployment and create pod annotation var upon updating the labels configmap -func TestControllerUpdatingConfigmapLabelsShouldNotCreateOrCreatePodAnnotationInDeployment(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "test", "www.google.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying deployment update - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.google.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if updated { - t.Errorf("Deployment should not be updated by changing label") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on pod and create pod annotation var upon creating the secret -func TestControllerCreatingSecretShouldCreatePodAnnotationInDeployment(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // TODO: Fix this test case - t.Skip("Skipping TestControllerCreatingConfigmapShouldCreatePodAnnotationInDeployment test case") - - // Creating secret - secretName := secretNamePrefix + "-create-" + testutil.RandSeq(5) - _, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) - - _, err = testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, newData) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - time.Sleep(sleepDuration) - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - time.Sleep(sleepDuration) - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - // Deleting Deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on pod and create pod annotation var upon updating the secret -func TestControllerUpdatingSecretShouldCreatePodAnnotationInDeployment(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - // Deleting Deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and update pod annotation var upon updating the secret -func TestControllerUpdatingSecretShouldUpdatePodAnnotationInDeployment(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - // Deleting Deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Do not Perform rolling upgrade on pod and create or update a pod annotation upon updating the label in secret -func TestControllerUpdatingSecretLabelsShouldNotCreateOrUpdatePodAnnotationInDeployment(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - err = testutil.UpdateSecret(secretClient, namespace, secretName, "test", data) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if updated { - t.Errorf("Deployment should not be updated by changing label in secret") - } - - // Deleting Deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on DaemonSet and create pod annotation var upon updating the configmap -func TestControllerUpdatingConfigmapShouldCreatePodAnnotationInDaemonSet(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying DaemonSet update - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - time.Sleep(sleepDuration) - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on DaemonSet and update pod annotation var upon updating the configmap -func TestControllerForUpdatingConfigmapShouldUpdateDaemonSetUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - time.Sleep(sleepDuration) - - // Updating configmap for second time - updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - time.Sleep(sleepDuration) - - // Verifying DaemonSet update - logrus.Infof("Verifying pod annotation has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - time.Sleep(sleepDuration) - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on pod and create pod annotation var upon updating the secret -func TestControllerUpdatingSecretShouldCreatePodAnnotationInDaemonSet(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on DaemonSet and update pod annotation var upon updating the secret -func TestControllerUpdatingSecretShouldUpdatePodAnnotationInDaemonSet(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - time.Sleep(sleepDuration) - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Do not Perform rolling upgrade on pod and create or update a pod annotation upon updating the label in secret -func TestControllerUpdatingSecretLabelsShouldNotCreateOrUpdatePodAnnotationInDaemonSet(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - err = testutil.UpdateSecret(secretClient, namespace, secretName, "test", data) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if updated { - t.Errorf("DaemonSet should not be updated by changing label in secret") - } - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on StatefulSet and create pod annotation var upon updating the configmap -func TestControllerUpdatingConfigmapShouldCreatePodAnnotationInStatefulSet(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying StatefulSet update - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - statefulSetFuncs := handler.GetStatefulSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - time.Sleep(sleepDuration) - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on StatefulSet and update pod annotation var upon updating the configmap -func TestControllerForUpdatingConfigmapShouldUpdateStatefulSetUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Updating configmap for second time - updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying StatefulSet update - logrus.Infof("Verifying pod annotation has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - statefulSetFuncs := handler.GetStatefulSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - time.Sleep(sleepDuration) - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on pod and create pod annotation var upon updating the secret -func TestControllerUpdatingSecretShouldCreatePodAnnotationInStatefulSet(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - statefulSetFuncs := handler.GetStatefulSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and create env var upon updating the configmap -func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying deployment update - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and create env var upon updating the configmap -func TestControllerUpdatingConfigmapShouldAutoCreateEnvInDeployment(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, false) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying deployment update - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and create env var upon creating the configmap -func TestControllerCreatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // TODO: Fix this test case - t.Skip("Skipping TestControllerCreatingConfigmapShouldCreateEnvInDeployment test case") - - // Creating configmap - configmapName := configmapNamePrefix + "-create-" + testutil.RandSeq(5) - _, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Deleting configmap for first time - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - - time.Sleep(sleepDuration) - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.stakater.com") - if err != nil { - t.Errorf("Error while creating the configmap second time %v", err) - } - - time.Sleep(sleepDuration) - - // Verifying deployment update - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and update env var upon updating the configmap -func TestControllerForUpdatingConfigmapShouldUpdateDeploymentUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Updating configmap for second time - updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying deployment update - logrus.Infof("Verifying env var has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Do not Perform rolling upgrade on deployment and create env var upon updating the labels configmap -func TestControllerUpdatingConfigmapLabelsShouldNotCreateOrUpdateEnvInDeployment(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "test", "www.google.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying deployment update - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.google.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) - if updated { - t.Errorf("Deployment should not be updated by changing label") - } - time.Sleep(sleepDuration) - - // Deleting deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on pod and create a env var upon creating the secret -func TestControllerCreatingSecretShouldCreateEnvInDeployment(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // TODO: Fix this test case - t.Skip("Skipping TestControllerCreatingConfigmapShouldCreateEnvInDeployment test case") - - // Creating secret - secretName := secretNamePrefix + "-create-" + testutil.RandSeq(5) - _, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) - - _, err = testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, newData) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - time.Sleep(sleepDuration) - - // Verifying Upgrade - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - time.Sleep(sleepDuration) - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - // Deleting Deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on pod and create a env var upon updating the secret -func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - // Deleting Deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on deployment and update env var upon updating the secret -func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying env var has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - // Deleting Deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Do not Perform rolling upgrade on pod and create or update a env var upon updating the label in secret -func TestControllerUpdatingSecretLabelsShouldNotCreateOrUpdateEnvInDeployment(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in deployment creation: %v", err) - } - - err = testutil.UpdateSecret(secretClient, namespace, secretName, "test", data) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, deploymentFuncs) - if updated { - t.Errorf("Deployment should not be updated by changing label in secret") - } - - // Deleting Deployment - err = testutil.DeleteDeployment(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on DaemonSet and create env var upon updating the configmap -func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying DaemonSet update - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - time.Sleep(sleepDuration) - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on DaemonSet and update env var upon updating the configmap -func TestControllerForUpdatingConfigmapShouldUpdateDaemonSetUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - time.Sleep(sleepDuration) - - // Updating configmap for second time - updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - time.Sleep(sleepDuration) - - // Verifying DaemonSet update - logrus.Infof("Verifying env var has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - time.Sleep(sleepDuration) - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on pod and create a env var upon updating the secret -func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on DaemonSet and update env var upon updating the secret -func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - time.Sleep(sleepDuration) - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying env var has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Do not Perform rolling upgrade on pod and create or update a env var upon updating the label in secret -func TestControllerUpdatingSecretLabelsShouldNotCreateOrUpdateEnvInDaemonSet(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating DaemonSet - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in DaemonSet creation: %v", err) - } - - err = testutil.UpdateSecret(secretClient, namespace, secretName, "test", data) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - daemonSetFuncs := handler.GetDaemonSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, daemonSetFuncs) - if updated { - t.Errorf("DaemonSet should not be updated by changing label in secret") - } - - // Deleting DaemonSet - err = testutil.DeleteDaemonSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the DaemonSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on StatefulSet and create env var upon updating the configmap -func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying StatefulSet update - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - statefulSetFuncs := handler.GetStatefulSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - time.Sleep(sleepDuration) - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on StatefulSet and update env var upon updating the configmap -func TestControllerForUpdatingConfigmapShouldUpdateStatefulSetUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(clients.KubernetesClient, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, configmapName, namespace, true) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Updating configmap for second time - updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying StatefulSet update - logrus.Infof("Verifying env var has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - config := common.Config{ - Namespace: namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - statefulSetFuncs := handler.GetStatefulSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - time.Sleep(sleepDuration) - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(clients.KubernetesClient, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on pod and create a env var upon updating the secret -func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - statefulSetFuncs := handler.GetStatefulSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on StatefulSet and update env var upon updating the secret -func TestControllerUpdatingSecretShouldUpdateEnvInStatefulSet(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying env var has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - statefulSetFuncs := handler.GetStatefulSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, constants.SecretEnvVarPostfix, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -// Perform rolling upgrade on StatefulSet and update pod annotation var upon updating the secret -func TestControllerUpdatingSecretShouldUpdatePodAnnotationInStatefulSet(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - // Creating secret - secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) - secretClient, err := testutil.CreateSecret(clients.KubernetesClient, namespace, secretName, data) - if err != nil { - t.Errorf("Error in secret creation: %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, secretName, namespace, true) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Updating Secret - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) - if err != nil { - t.Errorf("Error while updating secret %v", err) - } - - // Verifying Upgrade - logrus.Infof("Verifying pod annotation has been updated") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - config := common.Config{ - Namespace: namespace, - ResourceName: secretName, - SHAValue: shaData, - Annotation: options.SecretUpdateOnChangeAnnotation, - } - statefulSetFuncs := handler.GetStatefulSetRollingUpgradeFuncs() - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - //Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, namespace, secretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - time.Sleep(sleepDuration) -} - -func TestController_resourceInIgnoredNamespace(t *testing.T) { - type fields struct { - client kubernetes.Interface - indexer cache.Indexer - queue workqueue.TypedRateLimitingInterface[any] - informer cache.Controller - namespace string - ignoredNamespaces util.List - } - type args struct { - raw interface{} - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - name: "TestConfigMapResourceInIgnoredNamespaceShouldReturnTrue", - fields: fields{ - ignoredNamespaces: util.List{ - "system", - }, - }, - args: args{ - raw: testutil.GetConfigmap("system", "testcm", "test"), - }, - want: true, - }, - { - name: "TestSecretResourceInIgnoredNamespaceShouldReturnTrue", - fields: fields{ - ignoredNamespaces: util.List{ - "system", - }, - }, - args: args{ - raw: testutil.GetSecret("system", "testsecret", "test"), - }, - want: true, - }, - { - name: "TestConfigMapResourceInIgnoredNamespaceShouldReturnFalse", - fields: fields{ - ignoredNamespaces: util.List{ - "system", - }, - }, - args: args{ - raw: testutil.GetConfigmap("some-other-namespace", "testcm", "test"), - }, - want: false, - }, - { - name: "TestConfigMapResourceInIgnoredNamespaceShouldReturnFalse", - fields: fields{ - ignoredNamespaces: util.List{ - "system", - }, - }, - args: args{ - raw: testutil.GetSecret("some-other-namespace", "testsecret", "test"), - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Controller{ - client: tt.fields.client, - indexer: tt.fields.indexer, - queue: tt.fields.queue, - informer: tt.fields.informer, - namespace: tt.fields.namespace, - ignoredNamespaces: tt.fields.ignoredNamespaces, - } - if got := c.resourceInIgnoredNamespace(tt.args.raw); got != tt.want { - t.Errorf("Controller.resourceInIgnoredNamespace() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestController_resourceInNamespaceSelector(t *testing.T) { - type fields struct { - indexer cache.Indexer - queue workqueue.TypedRateLimitingInterface[any] - informer cache.Controller - namespace v1.Namespace - namespaceSelector string - } - type args struct { - raw interface{} - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - name: "TestConfigMapResourceInNamespaceSelector", - fields: fields{ - namespaceSelector: "select=this,select2=this2", - namespace: v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "selected-namespace", - Labels: map[string]string{ - "select": "this", - "select2": "this2", - }, - }, - }, - }, - args: args{ - raw: testutil.GetConfigmap("selected-namespace", "testcm", "test"), - }, - want: true, - }, { - name: "TestConfigMapResourceNotInNamespaceSelector", - fields: fields{ - namespaceSelector: "select=this,select2=this2", - namespace: v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "not-selected-namespace", - Labels: map[string]string{}, - }, - }, - }, - args: args{ - raw: testutil.GetConfigmap("not-selected-namespace", "testcm", "test"), - }, - want: false, - }, - { - name: "TestSecretResourceInNamespaceSelector", - fields: fields{ - namespaceSelector: "select=this,select2=this2", - namespace: v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "selected-namespace", - Labels: map[string]string{ - "select": "this", - "select2": "this2", - }, - }, - }, - }, - args: args{ - raw: testutil.GetSecret("selected-namespace", "testsecret", "test"), - }, - want: true, - }, { - name: "TestSecretResourceNotInNamespaceSelector", - fields: fields{ - namespaceSelector: "select=this,select2=this2", - namespace: v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "not-selected-namespace", - Labels: map[string]string{}, - }, - }, - }, - args: args{ - raw: testutil.GetSecret("not-selected-namespace", "secret", "test"), - }, - want: false, - }, { - name: "TestSecretResourceInNamespaceSelectorKeyExists", - fields: fields{ - namespaceSelector: "select", - namespace: v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "selected-namespace", - Labels: map[string]string{ - "select": "this", - }, - }, - }, - }, - args: args{ - raw: testutil.GetSecret("selected-namespace", "secret", "test"), - }, - want: true, - }, { - name: "TestSecretResourceInNamespaceSelectorValueIn", - fields: fields{ - namespaceSelector: "select in (select1, select2, select3)", - namespace: v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "selected-namespace", - Labels: map[string]string{ - "select": "select2", - }, - }, - }, - }, - args: args{ - raw: testutil.GetSecret("selected-namespace", "secret", "test"), - }, - want: true, - }, { - name: "TestSecretResourceInNamespaceSelectorKeyDoesNotExist", - fields: fields{ - namespaceSelector: "!select2", - namespace: v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "selected-namespace", - Labels: map[string]string{ - "select": "this", - }, - }, - }, - }, - args: args{ - raw: testutil.GetSecret("selected-namespace", "secret", "test"), - }, - want: true, - }, { - name: "TestSecretResourceInNamespaceSelectorMultipleConditions", - fields: fields{ - namespaceSelector: "select,select2=this2,select3!=this4", - namespace: v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "selected-namespace", - Labels: map[string]string{ - "select": "this", - "select2": "this2", - "select3": "this3", - }, - }, - }, - }, - args: args{ - raw: testutil.GetSecret("selected-namespace", "secret", "test"), - }, - want: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - fakeClient := fake.NewSimpleClientset() - namespace, _ := fakeClient.CoreV1().Namespaces().Create(context.Background(), &tt.fields.namespace, metav1.CreateOptions{}) - logrus.Infof("created fakeClient namespace for testing = %s", namespace.Name) - - c := &Controller{ - client: fakeClient, - indexer: tt.fields.indexer, - queue: tt.fields.queue, - informer: tt.fields.informer, - namespace: tt.fields.namespace.Name, - namespaceSelector: tt.fields.namespaceSelector, - } - - listOptions := metav1.ListOptions{} - listOptions.LabelSelector = tt.fields.namespaceSelector - namespaces, _ := fakeClient.CoreV1().Namespaces().List(context.Background(), listOptions) - - for _, ns := range namespaces.Items { - c.addSelectedNamespaceToCache(ns) - } - - if got := c.resourceInSelectedNamespaces(tt.args.raw); got != tt.want { - t.Errorf("Controller.resourceInNamespaceSelector() = %v, want %v", got, tt.want) - } - - for _, ns := range namespaces.Items { - c.removeSelectedNamespaceFromCache(ns) - } - }) - } -} diff --git a/internal/pkg/controller/manager.go b/internal/pkg/controller/manager.go index d4914cdd..2c887340 100644 --- a/internal/pkg/controller/manager.go +++ b/internal/pkg/controller/manager.go @@ -3,10 +3,10 @@ package controller import ( "context" "fmt" - "time" argorolloutsv1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/go-logr/logr" + "github.com/stakater/Reloader/internal/pkg/alerting" "github.com/stakater/Reloader/internal/pkg/config" "github.com/stakater/Reloader/internal/pkg/events" "github.com/stakater/Reloader/internal/pkg/metrics" @@ -36,12 +36,10 @@ type ManagerOptions struct { } // NewManager creates a new controller-runtime manager with the given options. +// This follows controller-runtime and operator-sdk conventions for leader election. func NewManager(opts ManagerOptions) (ctrl.Manager, error) { cfg := opts.Config - - leaseDuration := 15 * time.Second - renewDeadline := 10 * time.Second - retryPeriod := 2 * time.Second + le := cfg.LeaderElection mgrOpts := ctrl.Options{ Scheme: runtimeScheme, @@ -49,11 +47,19 @@ func NewManager(opts ManagerOptions) (ctrl.Manager, error) { BindAddress: cfg.MetricsAddr, }, HealthProbeBindAddress: cfg.HealthAddr, - LeaderElection: cfg.EnableHA, - LeaderElectionID: "reloader-leader-election", - LeaseDuration: &leaseDuration, - RenewDeadline: &renewDeadline, - RetryPeriod: &retryPeriod, + + // Leader election configuration following operator-sdk best practices: + // - LeaderElection enables/disables leader election + // - LeaderElectionID is the name of the lease resource + // - LeaderElectionNamespace where the lease is created (defaults to pod namespace) + // - LeaderElectionReleaseOnCancel allows faster failover by releasing the lock on shutdown + LeaderElection: cfg.EnableHA, + LeaderElectionID: le.LockName, + LeaderElectionNamespace: le.Namespace, + LeaderElectionReleaseOnCancel: le.ReleaseOnCancel, + LeaseDuration: &le.LeaseDuration, + RenewDeadline: &le.RenewDeadline, + RetryPeriod: &le.RetryPeriod, } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), mgrOpts) @@ -61,6 +67,10 @@ func NewManager(opts ManagerOptions) (ctrl.Manager, error) { return nil, fmt.Errorf("creating manager: %w", err) } + // Add health and readiness probes. + // The healthz probe reports whether the manager is running. + // The readyz probe reports whether the manager is ready to serve requests. + // When leader election is enabled, readyz will fail until this instance becomes leader. if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { return nil, fmt.Errorf("setting up health check: %w", err) } @@ -76,6 +86,13 @@ func SetupReconcilers(mgr ctrl.Manager, cfg *config.Config, log logr.Logger, col registry := workload.NewRegistry(cfg.ArgoRolloutsEnabled) reloadService := reload.NewService(cfg) eventRecorder := events.NewRecorder(mgr.GetEventRecorderFor("reloader")) + pauseHandler := reload.NewPauseHandler(cfg) + + // Create alerter based on configuration + alerter := alerting.NewAlerter(cfg) + if cfg.Alerting.Enabled { + log.Info("alerting enabled", "sink", cfg.Alerting.Sink) + } // Create webhook client if URL is configured var webhookClient *webhook.Client @@ -84,6 +101,7 @@ func SetupReconcilers(mgr ctrl.Manager, cfg *config.Config, log logr.Logger, col log.Info("webhook mode enabled", "url", cfg.WebhookURL) } + // Setup ConfigMap reconciler if !cfg.IsResourceIgnored("configmaps") { if err := (&ConfigMapReconciler{ Client: mgr.GetClient(), @@ -94,11 +112,13 @@ func SetupReconcilers(mgr ctrl.Manager, cfg *config.Config, log logr.Logger, col Collectors: collectors, EventRecorder: eventRecorder, WebhookClient: webhookClient, + Alerter: alerter, }).SetupWithManager(mgr); err != nil { return fmt.Errorf("setting up configmap reconciler: %w", err) } } + // Setup Secret reconciler if !cfg.IsResourceIgnored("secrets") { if err := (&SecretReconciler{ Client: mgr.GetClient(), @@ -109,11 +129,36 @@ func SetupReconcilers(mgr ctrl.Manager, cfg *config.Config, log logr.Logger, col Collectors: collectors, EventRecorder: eventRecorder, WebhookClient: webhookClient, + Alerter: alerter, }).SetupWithManager(mgr); err != nil { return fmt.Errorf("setting up secret reconciler: %w", err) } } + // Setup Namespace reconciler if namespace selectors are configured + if len(cfg.NamespaceSelectors) > 0 { + nsCache := NewNamespaceCache(true) + if err := (&NamespaceReconciler{ + Client: mgr.GetClient(), + Log: log.WithName("namespace-reconciler"), + Config: cfg, + Cache: nsCache, + }).SetupWithManager(mgr); err != nil { + return fmt.Errorf("setting up namespace reconciler: %w", err) + } + log.Info("namespace reconciler enabled for label selector filtering") + } + + // Setup Deployment reconciler for pause handling + if err := (&DeploymentReconciler{ + Client: mgr.GetClient(), + Log: log.WithName("deployment-reconciler"), + Config: cfg, + PauseHandler: pauseHandler, + }).SetupWithManager(mgr); err != nil { + return fmt.Errorf("setting up deployment reconciler: %w", err) + } + return nil } diff --git a/internal/pkg/controller/secret_reconciler.go b/internal/pkg/controller/secret_reconciler.go index 0d68ae13..8c5dbd2b 100644 --- a/internal/pkg/controller/secret_reconciler.go +++ b/internal/pkg/controller/secret_reconciler.go @@ -6,6 +6,7 @@ import ( "time" "github.com/go-logr/logr" + "github.com/stakater/Reloader/internal/pkg/alerting" "github.com/stakater/Reloader/internal/pkg/config" "github.com/stakater/Reloader/internal/pkg/events" "github.com/stakater/Reloader/internal/pkg/metrics" @@ -33,6 +34,7 @@ type SecretReconciler struct { Collectors *metrics.Collectors EventRecorder *events.Recorder WebhookClient *webhook.Client + Alerter alerting.Alerter initialized bool initOnce sync.Once @@ -131,6 +133,19 @@ func (r *SecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr "workload", decision.Workload.GetName(), "kind", decision.Workload.Kind(), ) + + // Send alert notification + if err := r.Alerter.Send(ctx, alerting.AlertMessage{ + WorkloadKind: string(decision.Workload.Kind()), + WorkloadName: decision.Workload.GetName(), + WorkloadNamespace: decision.Workload.GetNamespace(), + ResourceKind: "Secret", + ResourceName: secret.Name, + ResourceNamespace: secret.Namespace, + Timestamp: time.Now(), + }); err != nil { + log.Error(err, "failed to send alert") + } } } @@ -199,6 +214,19 @@ func (r *SecretReconciler) handleDelete(ctx context.Context, req ctrl.Request, l if updated { r.EventRecorder.ReloadSuccess(decision.Workload.GetObject(), "Secret", req.Name) r.recordMetrics(true, req.Namespace) + + // Send alert notification + if err := r.Alerter.Send(ctx, alerting.AlertMessage{ + WorkloadKind: string(decision.Workload.Kind()), + WorkloadName: decision.Workload.GetName(), + WorkloadNamespace: decision.Workload.GetNamespace(), + ResourceKind: "Secret", + ResourceName: req.Name, + ResourceNamespace: req.Namespace, + Timestamp: time.Now(), + }); err != nil { + log.Error(err, "failed to send alert") + } } } diff --git a/internal/pkg/crypto/sha.go b/internal/pkg/crypto/sha.go deleted file mode 100644 index 043fc227..00000000 --- a/internal/pkg/crypto/sha.go +++ /dev/null @@ -1,20 +0,0 @@ -package crypto - -import ( - "crypto/sha1" - "fmt" - "io" - - "github.com/sirupsen/logrus" -) - -// GenerateSHA generates SHA from string -func GenerateSHA(data string) string { - hasher := sha1.New() - _, err := io.WriteString(hasher, data) - if err != nil { - logrus.Errorf("Unable to write data in hash writer %v", err) - } - sha := hasher.Sum(nil) - return fmt.Sprintf("%x", sha) -} diff --git a/internal/pkg/crypto/sha_test.go b/internal/pkg/crypto/sha_test.go deleted file mode 100644 index 60d5af63..00000000 --- a/internal/pkg/crypto/sha_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package crypto - -import ( - "testing" -) - -// TestGenerateSHA generates the sha from given data and verifies whether it is correct or not -func TestGenerateSHA(t *testing.T) { - data := "www.stakater.com" - sha := "abd4ed82fb04548388a6cf3c339fd9dc84d275df" - result := GenerateSHA(data) - if result != sha { - t.Errorf("Failed to generate SHA") - } -} diff --git a/internal/pkg/handler/create.go b/internal/pkg/handler/create.go deleted file mode 100644 index fab73788..00000000 --- a/internal/pkg/handler/create.go +++ /dev/null @@ -1,47 +0,0 @@ -package handler - -import ( - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/pkg/common" - v1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/record" -) - -// ResourceCreatedHandler contains new objects -type ResourceCreatedHandler struct { - Resource interface{} - Collectors metrics.Collectors - Recorder record.EventRecorder -} - -// Handle processes the newly created resource -func (r ResourceCreatedHandler) Handle() error { - if r.Resource == nil { - logrus.Errorf("Resource creation handler received nil resource") - } else { - config, _ := r.GetConfig() - // Send webhook - if options.WebhookUrl != "" { - return sendUpgradeWebhook(config, options.WebhookUrl) - } - // process resource based on its type - return doRollingUpgrade(config, r.Collectors, r.Recorder, invokeReloadStrategy) - } - return nil -} - -// GetConfig gets configurations containing SHA, annotations, namespace and resource name -func (r ResourceCreatedHandler) GetConfig() (common.Config, string) { - var oldSHAData string - var config common.Config - if _, ok := r.Resource.(*v1.ConfigMap); ok { - config = common.GetConfigmapConfig(r.Resource.(*v1.ConfigMap)) - } else if _, ok := r.Resource.(*v1.Secret); ok { - config = common.GetSecretConfig(r.Resource.(*v1.Secret)) - } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) - } - return config, oldSHAData -} diff --git a/internal/pkg/handler/delete.go b/internal/pkg/handler/delete.go deleted file mode 100644 index 65c671e8..00000000 --- a/internal/pkg/handler/delete.go +++ /dev/null @@ -1,100 +0,0 @@ -package handler - -import ( - "fmt" - "slices" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/callbacks" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/testutil" - "github.com/stakater/Reloader/pkg/common" - - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - patchtypes "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" -) - -// ResourceDeleteHandler contains new objects -type ResourceDeleteHandler struct { - Resource interface{} - Collectors metrics.Collectors - Recorder record.EventRecorder -} - -// Handle processes resources being deleted -func (r ResourceDeleteHandler) Handle() error { - if r.Resource == nil { - logrus.Errorf("Resource delete handler received nil resource") - } else { - config, _ := r.GetConfig() - // Send webhook - if options.WebhookUrl != "" { - return sendUpgradeWebhook(config, options.WebhookUrl) - } - // process resource based on its type - return doRollingUpgrade(config, r.Collectors, r.Recorder, invokeDeleteStrategy) - } - return nil -} - -// GetConfig gets configurations containing SHA, annotations, namespace and resource name -func (r ResourceDeleteHandler) GetConfig() (common.Config, string) { - var oldSHAData string - var config common.Config - if _, ok := r.Resource.(*v1.ConfigMap); ok { - config = common.GetConfigmapConfig(r.Resource.(*v1.ConfigMap)) - } else if _, ok := r.Resource.(*v1.Secret); ok { - config = common.GetSecretConfig(r.Resource.(*v1.Secret)) - } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) - } - return config, oldSHAData -} - -func invokeDeleteStrategy(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config common.Config, autoReload bool) InvokeStrategyResult { - if options.ReloadStrategy == constants.AnnotationsReloadStrategy { - return removePodAnnotations(upgradeFuncs, item, config, autoReload) - } - - return removeContainerEnvVars(upgradeFuncs, item, config, autoReload) -} - -func removePodAnnotations(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config common.Config, autoReload bool) InvokeStrategyResult { - config.SHAValue = testutil.GetSHAfromEmptyData() - return updatePodAnnotations(upgradeFuncs, item, config, autoReload) -} - -func removeContainerEnvVars(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config common.Config, autoReload bool) InvokeStrategyResult { - envVar := getEnvVarName(config.ResourceName, config.Type) - container := getContainerUsingResource(upgradeFuncs, item, config, autoReload) - - if container == nil { - return InvokeStrategyResult{constants.NoContainerFound, nil} - } - - //remove if env var exists - if len(container.Env) > 0 { - index := slices.IndexFunc(container.Env, func(envVariable v1.EnvVar) bool { - return envVariable.Name == envVar - }) - if index != -1 { - var patch []byte - if upgradeFuncs.SupportsPatch { - containers := upgradeFuncs.ContainersFunc(item) - containerIndex := slices.IndexFunc(containers, func(c v1.Container) bool { - return c.Name == container.Name - }) - patch = fmt.Appendf(nil, upgradeFuncs.PatchTemplatesFunc().DeleteEnvVarTemplate, containerIndex, index) - } - - container.Env = append(container.Env[:index], container.Env[index+1:]...) - return InvokeStrategyResult{constants.Updated, &Patch{Type: patchtypes.JSONPatchType, Bytes: patch}} - } - } - - return InvokeStrategyResult{constants.NotUpdated, nil} -} diff --git a/internal/pkg/handler/handler.go b/internal/pkg/handler/handler.go deleted file mode 100644 index 1f5858e5..00000000 --- a/internal/pkg/handler/handler.go +++ /dev/null @@ -1,9 +0,0 @@ -package handler - -import "github.com/stakater/Reloader/pkg/common" - -// ResourceHandler handles the creation and update of resources -type ResourceHandler interface { - Handle() error - GetConfig() (common.Config, string) -} diff --git a/internal/pkg/handler/pause_deployment.go b/internal/pkg/handler/pause_deployment.go deleted file mode 100644 index 28d1b9ef..00000000 --- a/internal/pkg/handler/pause_deployment.go +++ /dev/null @@ -1,242 +0,0 @@ -package handler - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/pkg/kube" - app "k8s.io/api/apps/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - patchtypes "k8s.io/apimachinery/pkg/types" -) - -// Keeps track of currently active timers -var activeTimers = make(map[string]*time.Timer) - -// Returns unique key for the activeTimers map -func getTimerKey(namespace, deploymentName string) string { - return fmt.Sprintf("%s/%s", namespace, deploymentName) -} - -// Checks if a deployment is currently paused -func IsPaused(deployment *app.Deployment) bool { - return deployment.Spec.Paused -} - -// Deployment paused by reloader ? -func IsPausedByReloader(deployment *app.Deployment) bool { - if IsPaused(deployment) { - pausedAtAnnotationValue := deployment.Annotations[options.PauseDeploymentTimeAnnotation] - return pausedAtAnnotationValue != "" - } - return false -} - -// Returns the time, the deployment was paused by reloader, nil otherwise -func GetPauseStartTime(deployment *app.Deployment) (*time.Time, error) { - if !IsPausedByReloader(deployment) { - return nil, nil - } - - pausedAtStr := deployment.Annotations[options.PauseDeploymentTimeAnnotation] - parsedTime, err := time.Parse(time.RFC3339, pausedAtStr) - if err != nil { - return nil, err - } - - return &parsedTime, nil -} - -// ParsePauseDuration parses the pause interval value and returns a time.Duration -func ParsePauseDuration(pauseIntervalValue string) (time.Duration, error) { - pauseDuration, err := time.ParseDuration(pauseIntervalValue) - if err != nil { - logrus.Warnf("Failed to parse pause interval value '%s': %v", pauseIntervalValue, err) - return 0, err - } - return pauseDuration, nil -} - -// Pauses a deployment for a specified duration and creates a timer to resume it -// after the specified duration -func PauseDeployment(deployment *app.Deployment, clients kube.Clients, namespace, pauseIntervalValue string) (*app.Deployment, error) { - deploymentName := deployment.Name - pauseDuration, err := ParsePauseDuration(pauseIntervalValue) - - if err != nil { - return nil, err - } - - if !IsPaused(deployment) { - logrus.Infof("Pausing Deployment '%s' in namespace '%s' for %s", deploymentName, namespace, pauseDuration) - - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - - pausePatch, err := CreatePausePatch() - if err != nil { - logrus.Errorf("Failed to create pause patch for deployment '%s': %v", deploymentName, err) - return deployment, err - } - - err = deploymentFuncs.PatchFunc(clients, namespace, deployment, patchtypes.StrategicMergePatchType, pausePatch) - - if err != nil { - logrus.Errorf("Failed to patch deployment '%s' in namespace '%s': %v", deploymentName, namespace, err) - return deployment, err - } - - updatedDeployment, err := clients.KubernetesClient.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) - - CreateResumeTimer(deployment, clients, namespace, pauseDuration) - return updatedDeployment, err - } - - if !IsPausedByReloader(deployment) { - logrus.Infof("Deployment '%s' in namespace '%s' already paused", deploymentName, namespace) - return deployment, nil - } - - // Deployment has already been paused by reloader, check for timer - logrus.Debugf("Deployment '%s' in namespace '%s' is already paused by reloader", deploymentName, namespace) - - timerKey := getTimerKey(namespace, deploymentName) - _, timerExists := activeTimers[timerKey] - - if !timerExists { - logrus.Warnf("Timer does not exist for already paused deployment '%s' in namespace '%s', creating new one", - deploymentName, namespace) - HandleMissingTimer(deployment, pauseDuration, clients, namespace) - } - return deployment, nil -} - -// Handles the case where missing timers for deployments that have been paused by reloader. -// Could occur after new leader election or reloader restart -func HandleMissingTimer(deployment *app.Deployment, pauseDuration time.Duration, clients kube.Clients, namespace string) { - deploymentName := deployment.Name - pauseStartTime, err := GetPauseStartTime(deployment) - if err != nil { - logrus.Errorf("Error parsing pause start time for deployment '%s' in namespace '%s': %v. Resuming deployment immediately", - deploymentName, namespace, err) - ResumeDeployment(deployment, namespace, clients) - return - } - - if pauseStartTime == nil { - return - } - - elapsedPauseTime := time.Since(*pauseStartTime) - remainingPauseTime := pauseDuration - elapsedPauseTime - - if remainingPauseTime <= 0 { - logrus.Infof("Pause period for deployment '%s' in namespace '%s' has expired. Resuming immediately", - deploymentName, namespace) - ResumeDeployment(deployment, namespace, clients) - return - } - - logrus.Infof("Creating missing timer for already paused deployment '%s' in namespace '%s' with remaining time %s", - deploymentName, namespace, remainingPauseTime) - CreateResumeTimer(deployment, clients, namespace, remainingPauseTime) -} - -// CreateResumeTimer creates a timer to resume the deployment after the specified duration -func CreateResumeTimer(deployment *app.Deployment, clients kube.Clients, namespace string, pauseDuration time.Duration) { - deploymentName := deployment.Name - timerKey := getTimerKey(namespace, deployment.Name) - - // Check if there's an existing timer for this deployment - if _, exists := activeTimers[timerKey]; exists { - logrus.Debugf("Timer already exists for deployment '%s' in namespace '%s', Skipping creation", - deploymentName, namespace) - return - } - - // Create and store the new timer - timer := time.AfterFunc(pauseDuration, func() { - ResumeDeployment(deployment, namespace, clients) - }) - - // Add the new timer to the map - activeTimers[timerKey] = timer - - logrus.Debugf("Created pause timer for deployment '%s' in namespace '%s' with duration %s", - deploymentName, namespace, pauseDuration) -} - -// ResumeDeployment resumes a deployment that has been paused by reloader -func ResumeDeployment(deployment *app.Deployment, namespace string, clients kube.Clients) { - deploymentName := deployment.Name - - currentDeployment, err := clients.KubernetesClient.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) - - if err != nil { - logrus.Errorf("Failed to get deployment '%s' in namespace '%s': %v", deploymentName, namespace, err) - return - } - - if !IsPausedByReloader(currentDeployment) { - logrus.Infof("Deployment '%s' in namespace '%s' not paused by Reloader. Skipping resume", deploymentName, namespace) - return - } - - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - - resumePatch, err := CreateResumePatch() - if err != nil { - logrus.Errorf("Failed to create resume patch for deployment '%s': %v", deploymentName, err) - return - } - - // Remove the timer - timerKey := getTimerKey(namespace, deploymentName) - if timer, exists := activeTimers[timerKey]; exists { - timer.Stop() - delete(activeTimers, timerKey) - logrus.Debugf("Removed pause timer for deployment '%s' in namespace '%s'", deploymentName, namespace) - } - - err = deploymentFuncs.PatchFunc(clients, namespace, currentDeployment, patchtypes.StrategicMergePatchType, resumePatch) - - if err != nil { - logrus.Errorf("Failed to resume deployment '%s' in namespace '%s': %v", deploymentName, namespace, err) - return - } - - logrus.Infof("Successfully resumed deployment '%s' in namespace '%s'", deploymentName, namespace) -} - -func CreatePausePatch() ([]byte, error) { - patchData := map[string]interface{}{ - "spec": map[string]interface{}{ - "paused": true, - }, - "metadata": map[string]interface{}{ - "annotations": map[string]string{ - options.PauseDeploymentTimeAnnotation: time.Now().Format(time.RFC3339), - }, - }, - } - - return json.Marshal(patchData) -} - -func CreateResumePatch() ([]byte, error) { - patchData := map[string]interface{}{ - "spec": map[string]interface{}{ - "paused": false, - }, - "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ - options.PauseDeploymentTimeAnnotation: nil, - }, - }, - } - - return json.Marshal(patchData) -} diff --git a/internal/pkg/handler/pause_deployment_test.go b/internal/pkg/handler/pause_deployment_test.go deleted file mode 100644 index c14cbfcb..00000000 --- a/internal/pkg/handler/pause_deployment_test.go +++ /dev/null @@ -1,391 +0,0 @@ -package handler - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/pkg/kube" - "github.com/stretchr/testify/assert" - appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - testclient "k8s.io/client-go/kubernetes/fake" -) - -func TestIsPaused(t *testing.T) { - tests := []struct { - name string - deployment *appsv1.Deployment - paused bool - }{ - { - name: "paused deployment", - deployment: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Paused: true, - }, - }, - paused: true, - }, - { - name: "unpaused deployment", - deployment: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Paused: false, - }, - }, - paused: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - result := IsPaused(test.deployment) - assert.Equal(t, test.paused, result) - }) - } -} - -func TestIsPausedByReloader(t *testing.T) { - tests := []struct { - name string - deployment *appsv1.Deployment - pausedByReloader bool - }{ - { - name: "paused by reloader", - deployment: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Paused: true, - }, - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - options.PauseDeploymentTimeAnnotation: time.Now().Format(time.RFC3339), - }, - }, - }, - pausedByReloader: true, - }, - { - name: "not paused by reloader", - deployment: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Paused: true, - }, - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{}, - }, - }, - pausedByReloader: false, - }, - { - name: "not paused", - deployment: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Paused: false, - }, - }, - pausedByReloader: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - pausedByReloader := IsPausedByReloader(test.deployment) - assert.Equal(t, test.pausedByReloader, pausedByReloader) - }) - } -} - -func TestGetPauseStartTime(t *testing.T) { - now := time.Now() - nowStr := now.Format(time.RFC3339) - - tests := []struct { - name string - deployment *appsv1.Deployment - pausedByReloader bool - expectedStartTime time.Time - }{ - { - name: "valid pause time", - deployment: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Paused: true, - }, - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - options.PauseDeploymentTimeAnnotation: nowStr, - }, - }, - }, - pausedByReloader: true, - expectedStartTime: now, - }, - { - name: "not paused by reloader", - deployment: &appsv1.Deployment{ - Spec: appsv1.DeploymentSpec{ - Paused: false, - }, - }, - pausedByReloader: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - actualStartTime, err := GetPauseStartTime(test.deployment) - - assert.NoError(t, err) - - if !test.pausedByReloader { - assert.Nil(t, actualStartTime) - } else { - assert.NotNil(t, actualStartTime) - assert.WithinDuration(t, test.expectedStartTime, *actualStartTime, time.Second) - } - }) - } -} - -func TestParsePauseDuration(t *testing.T) { - tests := []struct { - name string - pauseIntervalValue string - expectedDuration time.Duration - invalidDuration bool - }{ - { - name: "valid duration", - pauseIntervalValue: "10s", - expectedDuration: 10 * time.Second, - invalidDuration: false, - }, - { - name: "valid minute duration", - pauseIntervalValue: "2m", - expectedDuration: 2 * time.Minute, - invalidDuration: false, - }, - { - name: "invalid duration", - pauseIntervalValue: "invalid", - expectedDuration: 0, - invalidDuration: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - actualDuration, err := ParsePauseDuration(test.pauseIntervalValue) - - if test.invalidDuration { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, test.expectedDuration, actualDuration) - } - }) - } -} - -func TestHandleMissingTimerSimple(t *testing.T) { - tests := []struct { - name string - deployment *appsv1.Deployment - shouldBePaused bool // Should be unpaused after HandleMissingTimer ? - }{ - { - name: "deployment paused by reloader, pause period has expired and no timer", - deployment: &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment-1", - Annotations: map[string]string{ - options.PauseDeploymentTimeAnnotation: time.Now().Add(-6 * time.Minute).Format(time.RFC3339), - options.PauseDeploymentAnnotation: "5m", - }, - }, - Spec: appsv1.DeploymentSpec{ - Paused: true, - }, - }, - shouldBePaused: false, - }, - { - name: "deployment paused by reloader, pause period expires in the future and no timer", - deployment: &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment-2", - Annotations: map[string]string{ - options.PauseDeploymentTimeAnnotation: time.Now().Add(1 * time.Minute).Format(time.RFC3339), - options.PauseDeploymentAnnotation: "5m", - }, - }, - Spec: appsv1.DeploymentSpec{ - Paused: true, - }, - }, - shouldBePaused: true, - }, - } - - for _, test := range tests { - // Clean up any timers at the end of the test - defer func() { - for key, timer := range activeTimers { - timer.Stop() - delete(activeTimers, key) - } - }() - - t.Run(test.name, func(t *testing.T) { - fakeClient := testclient.NewSimpleClientset() - clients := kube.Clients{ - KubernetesClient: fakeClient, - } - - _, err := fakeClient.AppsV1().Deployments("default").Create( - context.TODO(), - test.deployment, - metav1.CreateOptions{}) - assert.NoError(t, err, "Expected no error when creating deployment") - - pauseDuration, _ := ParsePauseDuration(test.deployment.Annotations[options.PauseDeploymentAnnotation]) - HandleMissingTimer(test.deployment, pauseDuration, clients, "default") - - updatedDeployment, _ := fakeClient.AppsV1().Deployments("default").Get(context.TODO(), test.deployment.Name, metav1.GetOptions{}) - - assert.Equal(t, test.shouldBePaused, updatedDeployment.Spec.Paused, - "Deployment should have correct paused state after timer expiration") - - if test.shouldBePaused { - pausedAtAnnotationValue := updatedDeployment.Annotations[options.PauseDeploymentTimeAnnotation] - assert.NotEmpty(t, pausedAtAnnotationValue, - "Pause annotation should be present and contain a value when deployment is paused") - } - }) - } -} - -func TestPauseDeployment(t *testing.T) { - tests := []struct { - name string - deployment *appsv1.Deployment - expectedError bool - expectedPaused bool - expectedAnnotation bool // Should have pause time annotation - pauseInterval string - }{ - { - name: "deployment without pause annotation", - deployment: &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment", - Annotations: map[string]string{}, - }, - Spec: appsv1.DeploymentSpec{ - Paused: false, - }, - }, - expectedError: true, - expectedPaused: false, - expectedAnnotation: false, - pauseInterval: "", - }, - { - name: "deployment already paused but not by reloader", - deployment: &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment", - Annotations: map[string]string{ - options.PauseDeploymentAnnotation: "5m", - }, - }, - Spec: appsv1.DeploymentSpec{ - Paused: true, - }, - }, - expectedError: false, - expectedPaused: true, - expectedAnnotation: false, - pauseInterval: "5m", - }, - { - name: "deployment unpaused that needs to be paused by reloader", - deployment: &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment-3", - Annotations: map[string]string{ - options.PauseDeploymentAnnotation: "5m", - }, - }, - Spec: appsv1.DeploymentSpec{ - Paused: false, - }, - }, - expectedError: false, - expectedPaused: true, - expectedAnnotation: true, - pauseInterval: "5m", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - fakeClient := testclient.NewSimpleClientset() - clients := kube.Clients{ - KubernetesClient: fakeClient, - } - - _, err := fakeClient.AppsV1().Deployments("default").Create( - context.TODO(), - test.deployment, - metav1.CreateOptions{}) - assert.NoError(t, err, "Expected no error when creating deployment") - - updatedDeployment, err := PauseDeployment(test.deployment, clients, "default", test.pauseInterval) - if test.expectedError { - assert.Error(t, err, "Expected an error pausing the deployment") - return - } else { - assert.NoError(t, err, "Expected no error pausing the deployment") - } - - assert.Equal(t, test.expectedPaused, updatedDeployment.Spec.Paused, - "Deployment should have correct paused state after pause") - - if test.expectedAnnotation { - pausedAtAnnotationValue := updatedDeployment.Annotations[options.PauseDeploymentTimeAnnotation] - assert.NotEmpty(t, pausedAtAnnotationValue, - "Pause annotation should be present and contain a value when deployment is paused") - } else { - pausedAtAnnotationValue := updatedDeployment.Annotations[options.PauseDeploymentTimeAnnotation] - assert.Empty(t, pausedAtAnnotationValue, - "Pause annotation should not be present when deployment has not been paused by reloader") - } - }) - } -} - -// Simple helper function for test cases -func FindDeploymentByName(deployments []runtime.Object, deploymentName string) (*appsv1.Deployment, error) { - for _, deployment := range deployments { - accessor, err := meta.Accessor(deployment) - if err != nil { - return nil, fmt.Errorf("error getting accessor for item: %v", err) - } - if accessor.GetName() == deploymentName { - deploymentObj, ok := deployment.(*appsv1.Deployment) - if !ok { - return nil, fmt.Errorf("failed to cast to Deployment") - } - return deploymentObj, nil - } - } - return nil, fmt.Errorf("deployment '%s' not found", deploymentName) -} diff --git a/internal/pkg/handler/update.go b/internal/pkg/handler/update.go deleted file mode 100644 index ae0bb1e2..00000000 --- a/internal/pkg/handler/update.go +++ /dev/null @@ -1,53 +0,0 @@ -package handler - -import ( - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/util" - "github.com/stakater/Reloader/pkg/common" - v1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/record" -) - -// ResourceUpdatedHandler contains updated objects -type ResourceUpdatedHandler struct { - Resource interface{} - OldResource interface{} - Collectors metrics.Collectors - Recorder record.EventRecorder -} - -// Handle processes the updated resource -func (r ResourceUpdatedHandler) Handle() error { - if r.Resource == nil || r.OldResource == nil { - logrus.Errorf("Resource update handler received nil resource") - } else { - config, oldSHAData := r.GetConfig() - if config.SHAValue != oldSHAData { - // Send a webhook if update - if options.WebhookUrl != "" { - return sendUpgradeWebhook(config, options.WebhookUrl) - } - // process resource based on its type - return doRollingUpgrade(config, r.Collectors, r.Recorder, invokeReloadStrategy) - } - } - return nil -} - -// GetConfig gets configurations containing SHA, annotations, namespace and resource name -func (r ResourceUpdatedHandler) GetConfig() (common.Config, string) { - var oldSHAData string - var config common.Config - if _, ok := r.Resource.(*v1.ConfigMap); ok { - oldSHAData = util.GetSHAfromConfigmap(r.OldResource.(*v1.ConfigMap)) - config = common.GetConfigmapConfig(r.Resource.(*v1.ConfigMap)) - } else if _, ok := r.Resource.(*v1.Secret); ok { - oldSHAData = util.GetSHAfromSecret(r.OldResource.(*v1.Secret).Data) - config = common.GetSecretConfig(r.Resource.(*v1.Secret)) - } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) - } - return config, oldSHAData -} diff --git a/internal/pkg/handler/upgrade.go b/internal/pkg/handler/upgrade.go deleted file mode 100644 index 6f185f1e..00000000 --- a/internal/pkg/handler/upgrade.go +++ /dev/null @@ -1,619 +0,0 @@ -package handler - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "os" - - "github.com/parnurzeal/gorequest" - "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" - alert "github.com/stakater/Reloader/internal/pkg/alerts" - "github.com/stakater/Reloader/internal/pkg/callbacks" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/util" - "github.com/stakater/Reloader/pkg/common" - "github.com/stakater/Reloader/pkg/kube" - app "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime" - patchtypes "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/retry" -) - -// GetDeploymentRollingUpgradeFuncs returns all callback funcs for a deployment -func GetDeploymentRollingUpgradeFuncs() callbacks.RollingUpgradeFuncs { - return callbacks.RollingUpgradeFuncs{ - ItemFunc: callbacks.GetDeploymentItem, - ItemsFunc: callbacks.GetDeploymentItems, - AnnotationsFunc: callbacks.GetDeploymentAnnotations, - PodAnnotationsFunc: callbacks.GetDeploymentPodAnnotations, - ContainersFunc: callbacks.GetDeploymentContainers, - InitContainersFunc: callbacks.GetDeploymentInitContainers, - UpdateFunc: callbacks.UpdateDeployment, - PatchFunc: callbacks.PatchDeployment, - PatchTemplatesFunc: callbacks.GetPatchTemplates, - VolumesFunc: callbacks.GetDeploymentVolumes, - ResourceType: "Deployment", - SupportsPatch: true, - } -} - -// GetDeploymentRollingUpgradeFuncs returns all callback funcs for a cronjob -func GetCronJobCreateJobFuncs() callbacks.RollingUpgradeFuncs { - return callbacks.RollingUpgradeFuncs{ - ItemFunc: callbacks.GetCronJobItem, - ItemsFunc: callbacks.GetCronJobItems, - AnnotationsFunc: callbacks.GetCronJobAnnotations, - PodAnnotationsFunc: callbacks.GetCronJobPodAnnotations, - ContainersFunc: callbacks.GetCronJobContainers, - InitContainersFunc: callbacks.GetCronJobInitContainers, - UpdateFunc: callbacks.CreateJobFromCronjob, - PatchFunc: callbacks.PatchCronJob, - PatchTemplatesFunc: func() callbacks.PatchTemplates { return callbacks.PatchTemplates{} }, - VolumesFunc: callbacks.GetCronJobVolumes, - ResourceType: "CronJob", - SupportsPatch: false, - } -} - -// GetDeploymentRollingUpgradeFuncs returns all callback funcs for a cronjob -func GetJobCreateJobFuncs() callbacks.RollingUpgradeFuncs { - return callbacks.RollingUpgradeFuncs{ - ItemFunc: callbacks.GetJobItem, - ItemsFunc: callbacks.GetJobItems, - AnnotationsFunc: callbacks.GetJobAnnotations, - PodAnnotationsFunc: callbacks.GetJobPodAnnotations, - ContainersFunc: callbacks.GetJobContainers, - InitContainersFunc: callbacks.GetJobInitContainers, - UpdateFunc: callbacks.ReCreateJobFromjob, - PatchFunc: callbacks.PatchJob, - PatchTemplatesFunc: func() callbacks.PatchTemplates { return callbacks.PatchTemplates{} }, - VolumesFunc: callbacks.GetJobVolumes, - ResourceType: "Job", - SupportsPatch: false, - } -} - -// GetDaemonSetRollingUpgradeFuncs returns all callback funcs for a daemonset -func GetDaemonSetRollingUpgradeFuncs() callbacks.RollingUpgradeFuncs { - return callbacks.RollingUpgradeFuncs{ - ItemFunc: callbacks.GetDaemonSetItem, - ItemsFunc: callbacks.GetDaemonSetItems, - AnnotationsFunc: callbacks.GetDaemonSetAnnotations, - PodAnnotationsFunc: callbacks.GetDaemonSetPodAnnotations, - ContainersFunc: callbacks.GetDaemonSetContainers, - InitContainersFunc: callbacks.GetDaemonSetInitContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - PatchFunc: callbacks.PatchDaemonSet, - PatchTemplatesFunc: callbacks.GetPatchTemplates, - VolumesFunc: callbacks.GetDaemonSetVolumes, - ResourceType: "DaemonSet", - SupportsPatch: true, - } -} - -// GetStatefulSetRollingUpgradeFuncs returns all callback funcs for a statefulSet -func GetStatefulSetRollingUpgradeFuncs() callbacks.RollingUpgradeFuncs { - return callbacks.RollingUpgradeFuncs{ - ItemFunc: callbacks.GetStatefulSetItem, - ItemsFunc: callbacks.GetStatefulSetItems, - AnnotationsFunc: callbacks.GetStatefulSetAnnotations, - PodAnnotationsFunc: callbacks.GetStatefulSetPodAnnotations, - ContainersFunc: callbacks.GetStatefulSetContainers, - InitContainersFunc: callbacks.GetStatefulSetInitContainers, - UpdateFunc: callbacks.UpdateStatefulSet, - PatchFunc: callbacks.PatchStatefulSet, - PatchTemplatesFunc: callbacks.GetPatchTemplates, - VolumesFunc: callbacks.GetStatefulSetVolumes, - ResourceType: "StatefulSet", - SupportsPatch: true, - } -} - -// GetArgoRolloutRollingUpgradeFuncs returns all callback funcs for a rollout -func GetArgoRolloutRollingUpgradeFuncs() callbacks.RollingUpgradeFuncs { - return callbacks.RollingUpgradeFuncs{ - ItemFunc: callbacks.GetRolloutItem, - ItemsFunc: callbacks.GetRolloutItems, - AnnotationsFunc: callbacks.GetRolloutAnnotations, - PodAnnotationsFunc: callbacks.GetRolloutPodAnnotations, - ContainersFunc: callbacks.GetRolloutContainers, - InitContainersFunc: callbacks.GetRolloutInitContainers, - UpdateFunc: callbacks.UpdateRollout, - PatchFunc: callbacks.PatchRollout, - PatchTemplatesFunc: func() callbacks.PatchTemplates { return callbacks.PatchTemplates{} }, - VolumesFunc: callbacks.GetRolloutVolumes, - ResourceType: "Rollout", - SupportsPatch: false, - } -} - -func sendUpgradeWebhook(config common.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) - - body, errs := sendWebhook(webhookUrl) - if errs != nil { - // return the first error - return errs[0] - } else { - logrus.Info(body) - } - - return nil -} - -func sendWebhook(url string) (string, []error) { - request := gorequest.New() - resp, _, err := request.Post(url).Send(`{"webhook":"update successful"}`).End() - if err != nil { - // the reloader seems to retry automatically so no retry logic added - return "", err - } - defer func() { - closeErr := resp.Body.Close() - if closeErr != nil { - logrus.Error(closeErr) - } - }() - var buffer bytes.Buffer - _, bufferErr := io.Copy(&buffer, resp.Body) - if bufferErr != nil { - logrus.Error(bufferErr) - } - return buffer.String(), nil -} - -func doRollingUpgrade(config common.Config, collectors metrics.Collectors, recorder record.EventRecorder, invoke invokeStrategy) error { - clients := kube.GetClients() - - // Get ignored workload types to avoid listing resources without RBAC permissions - ignoredWorkloadTypes, err := util.GetIgnoredWorkloadTypesList() - if err != nil { - logrus.Errorf("Failed to parse ignored workload types: %v", err) - ignoredWorkloadTypes = util.List{} // Continue with empty list if parsing fails - } - - err = rollingUpgrade(clients, config, GetDeploymentRollingUpgradeFuncs(), collectors, recorder, invoke) - if err != nil { - return err - } - - // Only process CronJobs if they are not ignored - if !ignoredWorkloadTypes.Contains("cronjobs") { - err = rollingUpgrade(clients, config, GetCronJobCreateJobFuncs(), collectors, recorder, invoke) - if err != nil { - return err - } - } - - // Only process Jobs if they are not ignored - if !ignoredWorkloadTypes.Contains("jobs") { - err = rollingUpgrade(clients, config, GetJobCreateJobFuncs(), collectors, recorder, invoke) - if err != nil { - return err - } - } - - err = rollingUpgrade(clients, config, GetDaemonSetRollingUpgradeFuncs(), collectors, recorder, invoke) - if err != nil { - return err - } - err = rollingUpgrade(clients, config, GetStatefulSetRollingUpgradeFuncs(), collectors, recorder, invoke) - if err != nil { - return err - } - - if options.IsArgoRollouts == "true" { - err = rollingUpgrade(clients, config, GetArgoRolloutRollingUpgradeFuncs(), collectors, recorder, invoke) - if err != nil { - return err - } - } - - return nil -} - -func rollingUpgrade(clients kube.Clients, config common.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, recorder record.EventRecorder, strategy invokeStrategy) error { - err := PerformAction(clients, config, upgradeFuncs, collectors, recorder, strategy) - if err != nil { - logrus.Errorf("Rolling upgrade for '%s' failed with error = %v", config.ResourceName, err) - } - return err -} - -// PerformAction invokes the deployment if there is any change in configmap or secret data -func PerformAction(clients kube.Clients, config common.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, recorder record.EventRecorder, strategy invokeStrategy) error { - items := upgradeFuncs.ItemsFunc(clients, config.Namespace) - - for _, item := range items { - err := retryOnConflict(retry.DefaultRetry, func(fetchResource bool) error { - return upgradeResource(clients, config, upgradeFuncs, collectors, recorder, strategy, item, fetchResource) - }) - if err != nil { - return err - } - } - - return nil -} - -func retryOnConflict(backoff wait.Backoff, fn func(_ bool) error) error { - var lastError error - fetchResource := false // do not fetch resource on first attempt, already done by ItemsFunc - err := wait.ExponentialBackoff(backoff, func() (bool, error) { - err := fn(fetchResource) - fetchResource = true - switch { - case err == nil: - return true, nil - case apierrors.IsConflict(err): - lastError = err - return false, nil - default: - return false, err - } - }) - if wait.Interrupted(err) { - err = lastError - } - return err -} - -func upgradeResource(clients kube.Clients, config common.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, recorder record.EventRecorder, strategy invokeStrategy, resource runtime.Object, fetchResource bool) error { - accessor, err := meta.Accessor(resource) - if err != nil { - return err - } - - resourceName := accessor.GetName() - if fetchResource { - resource, err = upgradeFuncs.ItemFunc(clients, resourceName, config.Namespace) - if err != nil { - return err - } - } - annotations := upgradeFuncs.AnnotationsFunc(resource) - podAnnotations := upgradeFuncs.PodAnnotationsFunc(resource) - result := common.ShouldReload(config, upgradeFuncs.ResourceType, annotations, podAnnotations, common.GetCommandLineOptions()) - - if !result.ShouldReload { - logrus.Debugf("No changes detected in '%s' of type '%s' in namespace '%s'", config.ResourceName, config.Type, config.Namespace) - return nil - } - - strategyResult := strategy(upgradeFuncs, resource, config, result.AutoReload) - - if strategyResult.Result != constants.Updated { - return nil - } - - // find correct annotation and update the resource - pauseInterval, foundPauseInterval := annotations[options.PauseDeploymentAnnotation] - - if foundPauseInterval { - deployment, ok := resource.(*app.Deployment) - if !ok { - logrus.Warnf("Annotation '%s' only applicable for deployments", options.PauseDeploymentAnnotation) - } else { - _, err = PauseDeployment(deployment, clients, config.Namespace, pauseInterval) - if err != nil { - logrus.Errorf("Failed to pause deployment '%s' in namespace '%s': %v", resourceName, config.Namespace, err) - return err - } - } - } - - if upgradeFuncs.SupportsPatch && strategyResult.Patch != nil { - err = upgradeFuncs.PatchFunc(clients, config.Namespace, resource, strategyResult.Patch.Type, strategyResult.Patch.Bytes) - } else { - err = upgradeFuncs.UpdateFunc(clients, config.Namespace, resource) - } - - if err != nil { - message := fmt.Sprintf("Update for '%s' of type '%s' in namespace '%s' failed with error %v", resourceName, upgradeFuncs.ResourceType, config.Namespace, err) - logrus.Errorf("Update for '%s' of type '%s' in namespace '%s' failed with error %v", resourceName, upgradeFuncs.ResourceType, config.Namespace, err) - - collectors.Reloaded.With(prometheus.Labels{"success": "false"}).Inc() - collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "false", "namespace": config.Namespace}).Inc() - if recorder != nil { - recorder.Event(resource, v1.EventTypeWarning, "ReloadFail", message) - } - return err - } else { - message := fmt.Sprintf("Changes detected in '%s' of type '%s' in namespace '%s'", config.ResourceName, config.Type, config.Namespace) - message += fmt.Sprintf(", Updated '%s' of type '%s' in namespace '%s'", resourceName, upgradeFuncs.ResourceType, config.Namespace) - - logrus.Infof("Changes detected in '%s' of type '%s' in namespace '%s'; updated '%s' of type '%s' in namespace '%s'", config.ResourceName, config.Type, config.Namespace, resourceName, upgradeFuncs.ResourceType, config.Namespace) - - collectors.Reloaded.With(prometheus.Labels{"success": "true"}).Inc() - collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": config.Namespace}).Inc() - alert_on_reload, ok := os.LookupEnv("ALERT_ON_RELOAD") - if recorder != nil { - recorder.Event(resource, v1.EventTypeNormal, "Reloaded", message) - } - if ok && alert_on_reload == "true" { - msg := fmt.Sprintf( - "Reloader detected changes in *%s* of type *%s* in namespace *%s*. Hence reloaded *%s* of type *%s* in namespace *%s*", - config.ResourceName, config.Type, config.Namespace, resourceName, upgradeFuncs.ResourceType, config.Namespace) - alert.SendWebhookAlert(msg) - } - } - - return nil -} - -func getVolumeMountName(volumes []v1.Volume, mountType string, volumeName string) string { - for i := range volumes { - switch mountType { - case constants.ConfigmapEnvVarPostfix: - if volumes[i].ConfigMap != nil && volumes[i].ConfigMap.Name == volumeName { - return volumes[i].Name - } - - if volumes[i].Projected != nil { - for j := range volumes[i].Projected.Sources { - if volumes[i].Projected.Sources[j].ConfigMap != nil && volumes[i].Projected.Sources[j].ConfigMap.Name == volumeName { - return volumes[i].Name - } - } - } - case constants.SecretEnvVarPostfix: - if volumes[i].Secret != nil && volumes[i].Secret.SecretName == volumeName { - return volumes[i].Name - } - - if volumes[i].Projected != nil { - for j := range volumes[i].Projected.Sources { - if volumes[i].Projected.Sources[j].Secret != nil && volumes[i].Projected.Sources[j].Secret.Name == volumeName { - return volumes[i].Name - } - } - } - } - } - - return "" -} - -func getContainerWithVolumeMount(containers []v1.Container, volumeMountName string) *v1.Container { - for i := range containers { - volumeMounts := containers[i].VolumeMounts - for j := range volumeMounts { - if volumeMounts[j].Name == volumeMountName { - return &containers[i] - } - } - } - - return nil -} - -func getContainerWithEnvReference(containers []v1.Container, resourceName string, resourceType string) *v1.Container { - for i := range containers { - envs := containers[i].Env - for j := range envs { - envVarSource := envs[j].ValueFrom - if envVarSource != nil { - if resourceType == constants.SecretEnvVarPostfix && envVarSource.SecretKeyRef != nil && envVarSource.SecretKeyRef.Name == resourceName { - return &containers[i] - } else if resourceType == constants.ConfigmapEnvVarPostfix && envVarSource.ConfigMapKeyRef != nil && envVarSource.ConfigMapKeyRef.Name == resourceName { - return &containers[i] - } - } - } - - envsFrom := containers[i].EnvFrom - for j := range envsFrom { - if resourceType == constants.SecretEnvVarPostfix && envsFrom[j].SecretRef != nil && envsFrom[j].SecretRef.Name == resourceName { - return &containers[i] - } else if resourceType == constants.ConfigmapEnvVarPostfix && envsFrom[j].ConfigMapRef != nil && envsFrom[j].ConfigMapRef.Name == resourceName { - return &containers[i] - } - } - } - return nil -} - -func getContainerUsingResource(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config common.Config, autoReload bool) *v1.Container { - volumes := upgradeFuncs.VolumesFunc(item) - containers := upgradeFuncs.ContainersFunc(item) - initContainers := upgradeFuncs.InitContainersFunc(item) - var container *v1.Container - // Get the volumeMountName to find volumeMount in container - volumeMountName := getVolumeMountName(volumes, config.Type, config.ResourceName) - // Get the container with mounted configmap/secret - if volumeMountName != "" { - container = getContainerWithVolumeMount(containers, volumeMountName) - if container == nil && len(initContainers) > 0 { - container = getContainerWithVolumeMount(initContainers, volumeMountName) - if container != nil { - // if configmap/secret is being used in init container then return the first Pod container to save reloader env - if len(containers) > 0 { - return &containers[0] - } - // No containers available, return nil to avoid crash - return nil - } - } else if container != nil { - return container - } - } - - // Get the container with referenced secret or configmap as env var - container = getContainerWithEnvReference(containers, config.ResourceName, config.Type) - if container == nil && len(initContainers) > 0 { - container = getContainerWithEnvReference(initContainers, config.ResourceName, config.Type) - if container != nil { - // if configmap/secret is being used in init container then return the first Pod container to save reloader env - if len(containers) > 0 { - return &containers[0] - } - // No containers available, return nil to avoid crash - return nil - } - } - - // Get the first container if the annotation is related to specified configmap or secret i.e. configmap.reloader.stakater.com/reload - if container == nil && !autoReload { - if len(containers) > 0 { - return &containers[0] - } - // No containers available, return nil to avoid crash - return nil - } - - return container -} - -type Patch struct { - Type patchtypes.PatchType - Bytes []byte -} - -type InvokeStrategyResult struct { - Result constants.Result - Patch *Patch -} - -type invokeStrategy func(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config common.Config, autoReload bool) InvokeStrategyResult - -func invokeReloadStrategy(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config common.Config, autoReload bool) InvokeStrategyResult { - if options.ReloadStrategy == constants.AnnotationsReloadStrategy { - return updatePodAnnotations(upgradeFuncs, item, config, autoReload) - } - return updateContainerEnvVars(upgradeFuncs, item, config, autoReload) -} - -func updatePodAnnotations(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config common.Config, autoReload bool) InvokeStrategyResult { - container := getContainerUsingResource(upgradeFuncs, item, config, autoReload) - if container == nil { - return InvokeStrategyResult{constants.NoContainerFound, nil} - } - - // Generate reloaded annotations. Attaching this to the item's annotation will trigger a rollout - // Note: the data on this struct is purely informational and is not used for future updates - reloadSource := common.NewReloadSourceFromConfig(config, []string{container.Name}) - annotations, patch, err := createReloadedAnnotations(&reloadSource, upgradeFuncs) - if err != nil { - logrus.Errorf("Failed to create reloaded annotations for %s! error = %v", config.ResourceName, err) - return InvokeStrategyResult{constants.NotUpdated, nil} - } - - // Copy the all annotations to the item's annotations - pa := upgradeFuncs.PodAnnotationsFunc(item) - if pa == nil { - return InvokeStrategyResult{constants.NotUpdated, nil} - } - - for k, v := range annotations { - pa[k] = v - } - - return InvokeStrategyResult{constants.Updated, &Patch{Type: patchtypes.StrategicMergePatchType, Bytes: patch}} -} - -func getReloaderAnnotationKey() string { - return fmt.Sprintf("%s/%s", - constants.ReloaderAnnotationPrefix, - constants.LastReloadedFromAnnotation, - ) -} - -func createReloadedAnnotations(target *common.ReloadSource, upgradeFuncs callbacks.RollingUpgradeFuncs) (map[string]string, []byte, error) { - if target == nil { - return nil, nil, errors.New("target is required") - } - - // Create a single "last-invokeReloadStrategy-from" annotation that stores metadata about the - // resource that caused the last invokeReloadStrategy. - // Intentionally only storing the last item in order to keep - // the generated annotations as small as possible. - annotations := make(map[string]string) - lastReloadedResourceName := getReloaderAnnotationKey() - - lastReloadedResource, err := json.Marshal(target) - if err != nil { - return nil, nil, err - } - - annotations[lastReloadedResourceName] = string(lastReloadedResource) - - var patch []byte - if upgradeFuncs.SupportsPatch { - escapedValue, err := jsonEscape(annotations[lastReloadedResourceName]) - if err != nil { - return nil, nil, err - } - patch = fmt.Appendf(nil, upgradeFuncs.PatchTemplatesFunc().AnnotationTemplate, lastReloadedResourceName, escapedValue) - } - - return annotations, patch, nil -} - -func getEnvVarName(resourceName string, typeName string) string { - return constants.EnvVarPrefix + util.ConvertToEnvVarName(resourceName) + "_" + typeName -} - -func updateContainerEnvVars(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config common.Config, autoReload bool) InvokeStrategyResult { - envVar := getEnvVarName(config.ResourceName, config.Type) - container := getContainerUsingResource(upgradeFuncs, item, config, autoReload) - - if container == nil { - return InvokeStrategyResult{constants.NoContainerFound, nil} - } - - //update if env var exists - updateResult := updateEnvVar(container, envVar, config.SHAValue) - - // if no existing env var exists lets create one - if updateResult == constants.NoEnvVarFound { - e := v1.EnvVar{ - Name: envVar, - Value: config.SHAValue, - } - container.Env = append(container.Env, e) - updateResult = constants.Updated - } - - var patch []byte - if upgradeFuncs.SupportsPatch { - patch = fmt.Appendf(nil, upgradeFuncs.PatchTemplatesFunc().EnvVarTemplate, container.Name, envVar, config.SHAValue) - } - - return InvokeStrategyResult{updateResult, &Patch{Type: patchtypes.StrategicMergePatchType, Bytes: patch}} -} - -func updateEnvVar(container *v1.Container, envVar string, shaData string) constants.Result { - envs := container.Env - for j := range envs { - if envs[j].Name == envVar { - if envs[j].Value != shaData { - envs[j].Value = shaData - return constants.Updated - } - return constants.NotUpdated - } - } - - return constants.NoEnvVarFound -} - -func jsonEscape(toEscape string) (string, error) { - bytes, err := json.Marshal(toEscape) - if err != nil { - return "", err - } - escaped := string(bytes) - return escaped[1 : len(escaped)-1], nil -} diff --git a/internal/pkg/handler/upgrade_test.go b/internal/pkg/handler/upgrade_test.go deleted file mode 100644 index 9a0e9458..00000000 --- a/internal/pkg/handler/upgrade_test.go +++ /dev/null @@ -1,4288 +0,0 @@ -package handler - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - argorolloutv1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" - "github.com/prometheus/client_golang/prometheus" - promtestutil "github.com/prometheus/client_golang/prometheus/testutil" - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/callbacks" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/testutil" - "github.com/stakater/Reloader/internal/pkg/util" - "github.com/stakater/Reloader/pkg/common" - "github.com/stakater/Reloader/pkg/kube" - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - patchtypes "k8s.io/apimachinery/pkg/types" - testclient "k8s.io/client-go/kubernetes/fake" -) - -var ( - clients = kube.Clients{KubernetesClient: testclient.NewSimpleClientset()} - - arsNamespace = "test-handler-" + testutil.RandSeq(5) - arsConfigmapName = "testconfigmap-handler-" + testutil.RandSeq(5) - arsSecretName = "testsecret-handler-" + testutil.RandSeq(5) - arsProjectedConfigMapName = "testprojectedconfigmap-handler-" + testutil.RandSeq(5) - arsProjectedSecretName = "testprojectedsecret-handler-" + testutil.RandSeq(5) - arsConfigmapWithInitContainer = "testconfigmapInitContainerhandler-" + testutil.RandSeq(5) - arsSecretWithInitContainer = "testsecretWithInitContainer-handler-" + testutil.RandSeq(5) - arsProjectedConfigMapWithInitContainer = "testProjectedConfigMapWithInitContainer-handler" + testutil.RandSeq(5) - arsProjectedSecretWithInitContainer = "testProjectedSecretWithInitContainer-handler" + testutil.RandSeq(5) - arsConfigmapWithInitEnv = "configmapWithInitEnv-" + testutil.RandSeq(5) - arsSecretWithInitEnv = "secretWithInitEnv-handler-" + testutil.RandSeq(5) - arsConfigmapWithEnvName = "testconfigmapWithEnv-handler-" + testutil.RandSeq(5) - arsConfigmapWithEnvFromName = "testconfigmapWithEnvFrom-handler-" + testutil.RandSeq(5) - arsSecretWithEnvName = "testsecretWithEnv-handler-" + testutil.RandSeq(5) - arsSecretWithEnvFromName = "testsecretWithEnvFrom-handler-" + testutil.RandSeq(5) - arsConfigmapWithPodAnnotations = "testconfigmapPodAnnotations-handler-" + testutil.RandSeq(5) - arsConfigmapWithBothAnnotations = "testconfigmapBothAnnotations-handler-" + testutil.RandSeq(5) - arsConfigmapAnnotated = "testconfigmapAnnotated-handler-" + testutil.RandSeq(5) - arsConfigMapWithNonAnnotatedDeployment = "testconfigmapNonAnnotatedDeployment-handler-" + testutil.RandSeq(5) - arsSecretWithSecretAutoAnnotation = "testsecretwithsecretautoannotationdeployment-handler-" + testutil.RandSeq(5) - arsConfigmapWithConfigMapAutoAnnotation = "testconfigmapwithconfigmapautoannotationdeployment-handler-" + testutil.RandSeq(5) - arsSecretWithExcludeSecretAnnotation = "testsecretwithsecretexcludeannotationdeployment-handler-" + testutil.RandSeq(5) - arsConfigmapWithExcludeConfigMapAnnotation = "testconfigmapwithconfigmapexcludeannotationdeployment-handler-" + testutil.RandSeq(5) - arsConfigmapWithIgnoreAnnotation = "testconfigmapWithIgnoreAnnotation-handler-" + testutil.RandSeq(5) - arsSecretWithIgnoreAnnotation = "testsecretWithIgnoreAnnotation-handler-" + testutil.RandSeq(5) - arsConfigmapWithPausedDeployment = "testconfigmapWithPausedDeployment-handler-" + testutil.RandSeq(5) - - ersNamespace = "test-handler-" + testutil.RandSeq(5) - ersConfigmapName = "testconfigmap-handler-" + testutil.RandSeq(5) - ersSecretName = "testsecret-handler-" + testutil.RandSeq(5) - ersProjectedConfigMapName = "testprojectedconfigmap-handler-" + testutil.RandSeq(5) - ersProjectedSecretName = "testprojectedsecret-handler-" + testutil.RandSeq(5) - ersConfigmapWithInitContainer = "testconfigmapInitContainerhandler-" + testutil.RandSeq(5) - ersSecretWithInitContainer = "testsecretWithInitContainer-handler-" + testutil.RandSeq(5) - ersProjectedConfigMapWithInitContainer = "testProjectedConfigMapWithInitContainer-handler" + testutil.RandSeq(5) - ersProjectedSecretWithInitContainer = "testProjectedSecretWithInitContainer-handler" + testutil.RandSeq(5) - ersConfigmapWithInitEnv = "configmapWithInitEnv-" + testutil.RandSeq(5) - ersSecretWithInitEnv = "secretWithInitEnv-handler-" + testutil.RandSeq(5) - ersConfigmapWithEnvName = "testconfigmapWithEnv-handler-" + testutil.RandSeq(5) - ersConfigmapWithEnvFromName = "testconfigmapWithEnvFrom-handler-" + testutil.RandSeq(5) - ersSecretWithEnvName = "testsecretWithEnv-handler-" + testutil.RandSeq(5) - ersSecretWithEnvFromName = "testsecretWithEnvFrom-handler-" + testutil.RandSeq(5) - ersConfigmapWithPodAnnotations = "testconfigmapPodAnnotations-handler-" + testutil.RandSeq(5) - ersConfigmapWithBothAnnotations = "testconfigmapBothAnnotations-handler-" + testutil.RandSeq(5) - ersConfigmapAnnotated = "testconfigmapAnnotated-handler-" + testutil.RandSeq(5) - ersSecretWithSecretAutoAnnotation = "testsecretwithsecretautoannotationdeployment-handler-" + testutil.RandSeq(5) - ersConfigmapWithConfigMapAutoAnnotation = "testconfigmapwithconfigmapautoannotationdeployment-handler-" + testutil.RandSeq(5) - ersSecretWithSecretExcludeAnnotation = "testsecretwithsecretexcludeannotationdeployment-handler-" + testutil.RandSeq(5) - ersConfigmapWithConfigMapExcludeAnnotation = "testconfigmapwithconfigmapexcludeannotationdeployment-handler-" + testutil.RandSeq(5) - ersConfigmapWithIgnoreAnnotation = "testconfigmapWithIgnoreAnnotation-handler-" + testutil.RandSeq(5) - ersSecretWithIgnoreAnnotation = "testsecretWithIgnoreAnnotation-handler-" + testutil.RandSeq(5) - ersConfigmapWithPausedDeployment = "testconfigmapWithPausedDeployment-handler-" + testutil.RandSeq(5) -) - -func TestMain(m *testing.M) { - - // Creating namespaces - testutil.CreateNamespace(arsNamespace, clients.KubernetesClient) - testutil.CreateNamespace(ersNamespace, clients.KubernetesClient) - - logrus.Infof("Setting up the annotation reload strategy test resources") - setupArs() - logrus.Infof("Setting up the env-var reload strategy test resources") - setupErs() - - logrus.Infof("Running Testcases") - retCode := m.Run() - - logrus.Infof("tearing down the annotation reload strategy test resources") - teardownArs() - logrus.Infof("tearing down the env-var reload strategy test resources") - teardownErs() - - os.Exit(retCode) -} - -func setupArs() { - // Creating configmap - _, err := testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapName, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretName, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - // Creating configmap will be used in projected volume - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsProjectedConfigMapName, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret will be used in projected volume - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsProjectedSecretName, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - // Creating configmap will be used in projected volume in init containers - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsProjectedConfigMapWithInitContainer, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret will be used in projected volume in init containers - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsProjectedSecretWithInitContainer, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithEnvName, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithEnvName, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithEnvFromName, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithInitEnv, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithInitContainer, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithEnvFromName, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithInitEnv, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithInitContainer, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithPodAnnotations, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigMapWithNonAnnotatedDeployment, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret used with secret auto annotation - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithSecretAutoAnnotation, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - // Creating configmap used with configmap auto annotation - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithConfigMapAutoAnnotation, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating configmap for testing pausing deployments - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithPausedDeployment, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret used with secret auto annotation - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithExcludeSecretAnnotation, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - // Creating configmap used with configmap auto annotation - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithExcludeConfigMapAnnotation, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating configmap with ignore annotation - _, err = testutil.CreateConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithIgnoreAnnotation, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - // Patch with ignore annotation - cmClient := clients.KubernetesClient.CoreV1().ConfigMaps(arsNamespace) - patch := []byte(`{"metadata":{"annotations":{"reloader.stakater.com/ignore":"true"}}}`) - _, _ = cmClient.Patch(context.TODO(), arsConfigmapWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{}) - - // Creating secret with ignore annotation - _, err = testutil.CreateSecret(clients.KubernetesClient, arsNamespace, arsSecretWithIgnoreAnnotation, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - secretClient := clients.KubernetesClient.CoreV1().Secrets(arsNamespace) - _, _ = secretClient.Patch(context.TODO(), arsSecretWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{}) - - // Creating Deployment referencing configmap with ignore annotation - _, err = testutil.CreateDeployment(clients.KubernetesClient, arsConfigmapWithIgnoreAnnotation, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap ignore annotation creation: %v", err) - } - // Creating Deployment referencing secret with ignore annotation - _, err = testutil.CreateDeployment(clients.KubernetesClient, arsSecretWithIgnoreAnnotation, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret ignore annotation creation: %v", err) - } - - // Creating Deployment with configmap - _, err = testutil.CreateDeployment(clients.KubernetesClient, arsConfigmapName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with configmap mounted in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, arsConfigmapWithInitContainer, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with configmap in projected volume - _, err = testutil.CreateDeployment(clients.KubernetesClient, arsProjectedConfigMapName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with configmap in projected volume mounted in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, arsProjectedConfigMapWithInitContainer, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with secret in projected volume - _, err = testutil.CreateDeployment(clients.KubernetesClient, arsProjectedSecretName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with secret in projected volume mounted in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, arsProjectedSecretWithInitContainer, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with secret mounted in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, arsSecretWithInitContainer, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with configmap mounted as Env in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, arsConfigmapWithInitEnv, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with secret mounted as Env in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, arsSecretWithInitEnv, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with secret - _, err = testutil.CreateDeployment(clients.KubernetesClient, arsSecretName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with env var source as configmap - _, err = testutil.CreateDeployment(clients.KubernetesClient, arsConfigmapWithEnvName, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with configmap configmap as env var source creation: %v", err) - } - - // Creating Deployment with env var source as secret - _, err = testutil.CreateDeployment(clients.KubernetesClient, arsSecretWithEnvName, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with secret configmap as env var source creation: %v", err) - } - - // Creating Deployment with envFrom source as secret - _, err = testutil.CreateDeploymentWithEnvVarSource(clients.KubernetesClient, arsConfigmapWithEnvFromName, arsNamespace) - if err != nil { - logrus.Errorf("Error in Deployment with secret configmap as envFrom source creation: %v", err) - } - - // Creating Deployment with envFrom source as secret - _, err = testutil.CreateDeploymentWithEnvVarSource(clients.KubernetesClient, arsSecretWithEnvFromName, arsNamespace) - if err != nil { - logrus.Errorf("Error in Deployment with secret configmap as envFrom source creation: %v", err) - } - - // Creating Deployment with envFrom source as secret - _, err = testutil.CreateDeploymentWithEnvVarSourceAndAnnotations( - clients.KubernetesClient, - arsConfigmapAnnotated, - arsNamespace, - map[string]string{"reloader.stakater.com/search": "true"}, - ) - if err != nil { - logrus.Errorf("Error in Deployment with secret configmap as envFrom source creation: %v", err) - } - - // Creating Deployment with configmap and without annotations - _, err = testutil.CreateDeploymentWithEnvVarSourceAndAnnotations(clients.KubernetesClient, arsConfigMapWithNonAnnotatedDeployment, arsNamespace, map[string]string{}) - if err != nil { - logrus.Errorf("Error in Deployment with configmap and without annotation creation: %v", err) - } - - // Creating Deployment with secret and with secret auto annotation - _, err = testutil.CreateDeploymentWithTypedAutoAnnotation(clients.KubernetesClient, arsSecretWithSecretAutoAnnotation, arsNamespace, testutil.SecretResourceType) - if err != nil { - logrus.Errorf("Error in Deployment with secret and with secret auto annotation: %v", err) - } - - // Creating Deployment with secret and with secret auto annotation - _, err = testutil.CreateDeploymentWithTypedAutoAnnotation(clients.KubernetesClient, arsConfigmapWithConfigMapAutoAnnotation, arsNamespace, testutil.ConfigmapResourceType) - if err != nil { - logrus.Errorf("Error in Deployment with configmap and with configmap auto annotation: %v", err) - } - - // Creating Deployment with secret and exclude secret annotation - _, err = testutil.CreateDeploymentWithExcludeAnnotation(clients.KubernetesClient, arsSecretWithExcludeSecretAnnotation, arsNamespace, testutil.SecretResourceType) - if err != nil { - logrus.Errorf("Error in Deployment with secret and with secret exclude annotation: %v", err) - } - - // Creating Deployment with secret and exclude configmap annotation - _, err = testutil.CreateDeploymentWithExcludeAnnotation(clients.KubernetesClient, arsConfigmapWithExcludeConfigMapAnnotation, arsNamespace, testutil.ConfigmapResourceType) - if err != nil { - logrus.Errorf("Error in Deployment with configmap and with configmap exclude annotation: %v", err) - } - - // Creating DaemonSet with configmap - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, arsConfigmapName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in DaemonSet with configmap creation: %v", err) - } - - // Creating DaemonSet with secret - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, arsSecretName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in DaemonSet with secret creation: %v", err) - } - - // Creating DaemonSet with configmap in projected volume - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, arsProjectedConfigMapName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in DaemonSet with configmap creation: %v", err) - } - - // Creating DaemonSet with secret in projected volume - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, arsProjectedSecretName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in DaemonSet with secret creation: %v", err) - } - - // Creating DaemonSet with env var source as configmap - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, arsConfigmapWithEnvName, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in DaemonSet with configmap as env var source creation: %v", err) - } - - // Creating DaemonSet with env var source as secret - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, arsSecretWithEnvName, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in DaemonSet with secret configmap as env var source creation: %v", err) - } - - // Creating StatefulSet with configmap - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, arsConfigmapName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in StatefulSet with configmap creation: %v", err) - } - - // Creating StatefulSet with secret - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, arsSecretName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in StatefulSet with secret creation: %v", err) - } - - // Creating StatefulSet with configmap in projected volume - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, arsProjectedConfigMapName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in StatefulSet with configmap creation: %v", err) - } - - // Creating StatefulSet with secret in projected volume - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, arsProjectedSecretName, arsNamespace, true) - if err != nil { - logrus.Errorf("Error in StatefulSet with configmap creation: %v", err) - } - - // Creating StatefulSet with env var source as configmap - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, arsConfigmapWithEnvName, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in StatefulSet with configmap configmap as env var source creation: %v", err) - } - - // Creating StatefulSet with env var source as secret - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, arsSecretWithEnvName, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in StatefulSet with secret configmap as env var source creation: %v", err) - } - - // Creating Deployment with pod annotations - _, err = testutil.CreateDeploymentWithPodAnnotations(clients.KubernetesClient, arsConfigmapWithPodAnnotations, arsNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with pod annotations: %v", err) - } - - // Creating Deployment with both annotations - _, err = testutil.CreateDeploymentWithPodAnnotations(clients.KubernetesClient, arsConfigmapWithBothAnnotations, arsNamespace, true) - - if err != nil { - logrus.Errorf("Error in Deployment with both annotations: %v", err) - } - - // Creating Deployment with pause annotation - _, err = testutil.CreateDeploymentWithAnnotations(clients.KubernetesClient, arsConfigmapWithPausedDeployment, arsNamespace, map[string]string{options.PauseDeploymentAnnotation: "10s"}, false) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } -} - -func teardownArs() { - // Deleting Deployment with configmap - deploymentError := testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with secret - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsSecretName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret %v", deploymentError) - } - - // Deleting Deployment with configmap in projected volume - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsProjectedConfigMapName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with configmap in projected volume mounted in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsProjectedConfigMapWithInitContainer) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with secret in projected volume - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsProjectedSecretName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with secret in projected volume mounted in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsProjectedSecretWithInitContainer) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with configmap as env var source - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithEnvName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap as env var source %v", deploymentError) - } - - // Deleting Deployment with secret - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsSecretWithEnvName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret as env var source %v", deploymentError) - } - - // Deleting Deployment with configmap mounted in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithInitContainer) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap mounted in init container %v", deploymentError) - } - - // Deleting Deployment with secret mounted in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsSecretWithInitContainer) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret mounted in init container %v", deploymentError) - } - - // Deleting Deployment with configmap mounted as env in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithInitEnv) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap mounted as env in init container %v", deploymentError) - } - - // Deleting Deployment with secret mounted as env in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsSecretWithInitEnv) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret mounted as env in init container %v", deploymentError) - } - - // Deleting Deployment with configmap as envFrom source - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithEnvFromName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap as envFrom source %v", deploymentError) - } - - // Deleting Deployment with secret as envFrom source - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsSecretWithEnvFromName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret as envFrom source %v", deploymentError) - } - - // Deleting Deployment with pod annotations - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithPodAnnotations) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with pod annotations %v", deploymentError) - } - - // Deleting Deployment with both annotations - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithBothAnnotations) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with both annotations %v", deploymentError) - } - - // Deleting Deployment with search annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapAnnotated) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with search annotation %v", deploymentError) - } - - // Deleting Deployment with secret and secret auto annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsSecretWithSecretAutoAnnotation) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret auto annotation %v", deploymentError) - } - - // Deleting Deployment with configmap and configmap auto annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithConfigMapAutoAnnotation) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap auto annotation %v", deploymentError) - } - - // Deleting Deployment with secret and exclude secret annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsSecretWithExcludeSecretAnnotation) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret auto annotation %v", deploymentError) - } - - // Deleting Deployment with configmap and exclude configmap annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithExcludeConfigMapAnnotation) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap auto annotation %v", deploymentError) - } - - // Deleting DaemonSet with configmap - daemonSetError := testutil.DeleteDaemonSet(clients.KubernetesClient, arsNamespace, arsConfigmapName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with configmap %v", daemonSetError) - } - - // Deleting Deployment with secret - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, arsNamespace, arsSecretName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with secret %v", daemonSetError) - } - - // Deleting DaemonSet with configmap in projected volume - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, arsNamespace, arsProjectedConfigMapName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with configmap %v", daemonSetError) - } - - // Deleting Deployment with secret in projected volume - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, arsNamespace, arsProjectedSecretName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with secret %v", daemonSetError) - } - - // Deleting Deployment with configmap as env var source - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, arsNamespace, arsConfigmapWithEnvName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with configmap as env var source %v", daemonSetError) - } - - // Deleting Deployment with secret as env var source - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, arsNamespace, arsSecretWithEnvName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with secret as env var source %v", daemonSetError) - } - - // Deleting StatefulSet with configmap - statefulSetError := testutil.DeleteStatefulSet(clients.KubernetesClient, arsNamespace, arsConfigmapName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with configmap %v", statefulSetError) - } - - // Deleting Deployment with secret - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, arsNamespace, arsSecretName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with secret %v", statefulSetError) - } - - // Deleting StatefulSet with configmap in projected volume - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, arsNamespace, arsProjectedConfigMapName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with configmap %v", statefulSetError) - } - - // Deleting Deployment with secret in projected volume - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, arsNamespace, arsProjectedSecretName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with secret %v", statefulSetError) - } - - // Deleting StatefulSet with configmap as env var source - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, arsNamespace, arsConfigmapWithEnvName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with configmap as env var source %v", statefulSetError) - } - - // Deleting Deployment with secret as env var source - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, arsNamespace, arsSecretWithEnvName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with secret as env var source %v", statefulSetError) - } - - // Deleting Deployment with pause annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, arsNamespace, arsConfigmapWithPausedDeployment) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Configmap - err := testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - - // Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsSecretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - - // Deleting configmap used in projected volume - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsProjectedConfigMapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - - // Deleting Secret used in projected volume - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsProjectedSecretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - - // Deleting configmap used in projected volume in init containers - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsProjectedConfigMapWithInitContainer) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - - // Deleting secret used in projected volume in init containers - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsProjectedSecretWithInitContainer) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - - // Deleting Configmap used as env var source - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithEnvName) - if err != nil { - logrus.Errorf("Error while deleting the configmap used as env var source %v", err) - } - - // Deleting Secret used as env var source - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsSecretWithEnvName) - if err != nil { - logrus.Errorf("Error while deleting the secret used as env var source %v", err) - } - - // Deleting Configmap used in init container - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithInitContainer) - if err != nil { - logrus.Errorf("Error while deleting the configmap used in init container %v", err) - } - - // Deleting Secret used in init container - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsSecretWithInitContainer) - if err != nil { - logrus.Errorf("Error while deleting the secret used in init container %v", err) - } - - // Deleting Configmap used as env var source - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithEnvFromName) - if err != nil { - logrus.Errorf("Error while deleting the configmap used as env var source %v", err) - } - - // Deleting Secret used as env var source - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsSecretWithEnvFromName) - if err != nil { - logrus.Errorf("Error while deleting the secret used as env var source %v", err) - } - - // Deleting Configmap used as env var source - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithInitEnv) - if err != nil { - logrus.Errorf("Error while deleting the configmap used as env var source in init container %v", err) - } - - // Deleting Secret used as env var source - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsSecretWithInitEnv) - if err != nil { - logrus.Errorf("Error while deleting the secret used as env var source in init container %v", err) - } - - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithPodAnnotations) - if err != nil { - logrus.Errorf("Error while deleting the configmap used with pod annotations: %v", err) - } - - // Deleting Secret used with secret auto annotation - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsSecretWithSecretAutoAnnotation) - if err != nil { - logrus.Errorf("Error while deleting the secret used with secret auto annotations: %v", err) - } - - // Deleting ConfigMap used with configmap auto annotation - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithConfigMapAutoAnnotation) - if err != nil { - logrus.Errorf("Error while deleting the configmap used with configmap auto annotations: %v", err) - } - - // Deleting Secret used with exclude secret annotation - err = testutil.DeleteSecret(clients.KubernetesClient, arsNamespace, arsSecretWithExcludeSecretAnnotation) - if err != nil { - logrus.Errorf("Error while deleting the secret used with secret auto annotations: %v", err) - } - - // Deleting ConfigMap used with exclude configmap annotation - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithExcludeConfigMapAnnotation) - if err != nil { - logrus.Errorf("Error while deleting the configmap used with configmap auto annotations: %v", err) - } - - // Deleting configmap for testing pausing deployments - err = testutil.DeleteConfigMap(clients.KubernetesClient, arsNamespace, arsConfigmapWithPausedDeployment) - if err != nil { - logrus.Errorf("Error while deleting the configmap: %v", err) - } - - // Deleting namespace - testutil.DeleteNamespace(arsNamespace, clients.KubernetesClient) - -} - -func setupErs() { - // Creating configmap - _, err := testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapName, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretName, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - // Creating configmap will be used in projected volume - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersProjectedConfigMapName, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret will be used in projected volume - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersProjectedSecretName, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - // Creating configmap will be used in projected volume in init containers - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersProjectedConfigMapWithInitContainer, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret will be used in projected volume in init containers - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersProjectedSecretWithInitContainer, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithEnvName, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithEnvName, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithEnvFromName, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating configmap for testing pausing deployments - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithPausedDeployment, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithInitEnv, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithInitContainer, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithEnvFromName, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithInitEnv, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithInitContainer, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithPodAnnotations, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret used with secret auto annotation - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithSecretAutoAnnotation, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - // Creating configmap used with configmap auto annotation - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithConfigMapAutoAnnotation, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating secret used with secret exclude annotation - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithSecretExcludeAnnotation, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - - // Creating configmap used with configmap exclude annotation - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithConfigMapExcludeAnnotation, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - - // Creating configmap with ignore annotation - _, err = testutil.CreateConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithIgnoreAnnotation, "www.google.com") - if err != nil { - logrus.Errorf("Error in configmap creation: %v", err) - } - cmClient := clients.KubernetesClient.CoreV1().ConfigMaps(ersNamespace) - patch := []byte(`{"metadata":{"annotations":{"reloader.stakater.com/ignore":"true"}}}`) - _, _ = cmClient.Patch(context.TODO(), ersConfigmapWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{}) - - // Creating secret with ignore annotation - _, err = testutil.CreateSecret(clients.KubernetesClient, ersNamespace, ersSecretWithIgnoreAnnotation, data) - if err != nil { - logrus.Errorf("Error in secret creation: %v", err) - } - secretClient := clients.KubernetesClient.CoreV1().Secrets(ersNamespace) - _, _ = secretClient.Patch(context.TODO(), ersSecretWithIgnoreAnnotation, patchtypes.MergePatchType, patch, metav1.PatchOptions{}) - - // Creating Deployment referencing configmap with ignore annotation - _, err = testutil.CreateDeployment(clients.KubernetesClient, ersConfigmapWithIgnoreAnnotation, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap ignore annotation creation: %v", err) - } - // Creating Deployment referencing secret with ignore annotation - _, err = testutil.CreateDeployment(clients.KubernetesClient, ersSecretWithIgnoreAnnotation, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret ignore annotation creation: %v", err) - } - - // Creating Deployment with configmap - _, err = testutil.CreateDeployment(clients.KubernetesClient, ersConfigmapName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with configmap mounted in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, ersConfigmapWithInitContainer, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with configmap in projected volume - _, err = testutil.CreateDeployment(clients.KubernetesClient, ersProjectedConfigMapName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with configmap in projected volume mounted in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, ersProjectedConfigMapWithInitContainer, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with secret in projected volume - _, err = testutil.CreateDeployment(clients.KubernetesClient, ersProjectedSecretName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with secret in projected volume mounted in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, ersProjectedSecretWithInitContainer, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with secret mounted in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, ersSecretWithInitContainer, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with configmap mounted as Env in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, ersConfigmapWithInitEnv, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating Deployment with secret mounted as Env in init container - _, err = testutil.CreateDeploymentWithInitContainer(clients.KubernetesClient, ersSecretWithInitEnv, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with secret - _, err = testutil.CreateDeployment(clients.KubernetesClient, ersSecretName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with secret creation: %v", err) - } - - // Creating Deployment with env var source as configmap - _, err = testutil.CreateDeployment(clients.KubernetesClient, ersConfigmapWithEnvName, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with configmap configmap as env var source creation: %v", err) - } - - // Creating Deployment with env var source as secret - _, err = testutil.CreateDeployment(clients.KubernetesClient, ersSecretWithEnvName, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with secret configmap as env var source creation: %v", err) - } - - // Creating Deployment with envFrom source as secret - _, err = testutil.CreateDeploymentWithEnvVarSource(clients.KubernetesClient, ersConfigmapWithEnvFromName, ersNamespace) - if err != nil { - logrus.Errorf("Error in Deployment with secret configmap as envFrom source creation: %v", err) - } - - // Creating Deployment with envFrom source as secret - _, err = testutil.CreateDeploymentWithEnvVarSource(clients.KubernetesClient, ersSecretWithEnvFromName, ersNamespace) - if err != nil { - logrus.Errorf("Error in Deployment with secret configmap as envFrom source creation: %v", err) - } - - // Creating Deployment with envFrom source as secret - _, err = testutil.CreateDeploymentWithEnvVarSourceAndAnnotations( - clients.KubernetesClient, - ersConfigmapAnnotated, - ersNamespace, - map[string]string{"reloader.stakater.com/search": "true"}, - ) - if err != nil { - logrus.Errorf("Error in Deployment with secret configmap as envFrom source creation: %v", err) - } - - // Creating Deployment with secret and with secret auto annotation - _, err = testutil.CreateDeploymentWithTypedAutoAnnotation(clients.KubernetesClient, ersSecretWithSecretAutoAnnotation, ersNamespace, testutil.SecretResourceType) - if err != nil { - logrus.Errorf("Error in Deployment with secret and with secret auto annotation: %v", err) - } - - // Creating Deployment with secret and with secret auto annotation - _, err = testutil.CreateDeploymentWithTypedAutoAnnotation(clients.KubernetesClient, ersConfigmapWithConfigMapAutoAnnotation, ersNamespace, testutil.ConfigmapResourceType) - if err != nil { - logrus.Errorf("Error in Deployment with configmap and with configmap auto annotation: %v", err) - } - - // Creating Deployment with secret and with secret exclude annotation - _, err = testutil.CreateDeploymentWithExcludeAnnotation(clients.KubernetesClient, ersSecretWithSecretExcludeAnnotation, ersNamespace, testutil.SecretResourceType) - if err != nil { - logrus.Errorf("Error in Deployment with secret and with secret exclude annotation: %v", err) - } - - // Creating Deployment with secret and with secret exclude annotation - _, err = testutil.CreateDeploymentWithExcludeAnnotation(clients.KubernetesClient, ersConfigmapWithConfigMapExcludeAnnotation, ersNamespace, testutil.ConfigmapResourceType) - if err != nil { - logrus.Errorf("Error in Deployment with configmap and with configmap exclude annotation: %v", err) - } - - // Creating Deployment with pause annotation - _, err = testutil.CreateDeploymentWithAnnotations(clients.KubernetesClient, ersConfigmapWithPausedDeployment, ersNamespace, map[string]string{options.PauseDeploymentAnnotation: "10s"}, false) - if err != nil { - logrus.Errorf("Error in Deployment with configmap creation: %v", err) - } - - // Creating DaemonSet with configmap - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, ersConfigmapName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in DaemonSet with configmap creation: %v", err) - } - - // Creating DaemonSet with secret - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, ersSecretName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in DaemonSet with secret creation: %v", err) - } - - // Creating DaemonSet with configmap in projected volume - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, ersProjectedConfigMapName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in DaemonSet with configmap creation: %v", err) - } - - // Creating DaemonSet with secret in projected volume - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, ersProjectedSecretName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in DaemonSet with secret creation: %v", err) - } - - // Creating DaemonSet with env var source as configmap - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, ersConfigmapWithEnvName, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in DaemonSet with configmap as env var source creation: %v", err) - } - - // Creating DaemonSet with env var source as secret - _, err = testutil.CreateDaemonSet(clients.KubernetesClient, ersSecretWithEnvName, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in DaemonSet with secret configmap as env var source creation: %v", err) - } - - // Creating StatefulSet with configmap - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, ersConfigmapName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in StatefulSet with configmap creation: %v", err) - } - - // Creating StatefulSet with secret - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, ersSecretName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in StatefulSet with secret creation: %v", err) - } - - // Creating StatefulSet with configmap in projected volume - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, ersProjectedConfigMapName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in StatefulSet with configmap creation: %v", err) - } - - // Creating StatefulSet with secret in projected volume - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, ersProjectedSecretName, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in StatefulSet with configmap creation: %v", err) - } - - // Creating StatefulSet with env var source as configmap - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, ersConfigmapWithEnvName, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in StatefulSet with configmap configmap as env var source creation: %v", err) - } - - // Creating StatefulSet with env var source as secret - _, err = testutil.CreateStatefulSet(clients.KubernetesClient, ersSecretWithEnvName, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in StatefulSet with secret configmap as env var source creation: %v", err) - } - - // Creating Deployment with pod annotations - _, err = testutil.CreateDeploymentWithPodAnnotations(clients.KubernetesClient, ersConfigmapWithPodAnnotations, ersNamespace, false) - if err != nil { - logrus.Errorf("Error in Deployment with pod annotations: %v", err) - } - - // Creating Deployment with both annotations - _, err = testutil.CreateDeploymentWithPodAnnotations(clients.KubernetesClient, ersConfigmapWithBothAnnotations, ersNamespace, true) - if err != nil { - logrus.Errorf("Error in Deployment with both annotations: %v", err) - } -} - -func teardownErs() { - // Deleting Deployment with configmap - deploymentError := testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with secret - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersSecretName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret %v", deploymentError) - } - - // Deleting Deployment with configmap in projected volume - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersProjectedConfigMapName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with configmap in projected volume mounted in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersProjectedConfigMapWithInitContainer) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with secret in projected volume - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersProjectedSecretName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with secret in projected volume mounted in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersProjectedSecretWithInitContainer) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Deployment with configmap as env var source - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithEnvName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap as env var source %v", deploymentError) - } - - // Deleting Deployment with secret - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersSecretWithEnvName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret as env var source %v", deploymentError) - } - - // Deleting Deployment with configmap mounted in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithInitContainer) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap mounted in init container %v", deploymentError) - } - - // Deleting Deployment with secret mounted in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersSecretWithInitContainer) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret mounted in init container %v", deploymentError) - } - - // Deleting Deployment with configmap mounted as env in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithInitEnv) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap mounted as env in init container %v", deploymentError) - } - - // Deleting Deployment with secret mounted as env in init container - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersSecretWithInitEnv) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret mounted as env in init container %v", deploymentError) - } - - // Deleting Deployment with configmap as envFrom source - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithEnvFromName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap as envFrom source %v", deploymentError) - } - - // Deleting Deployment with secret as envFrom source - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersSecretWithEnvFromName) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret as envFrom source %v", deploymentError) - } - - // Deleting Deployment with pod annotations - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithPodAnnotations) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with pod annotations %v", deploymentError) - } - - // Deleting Deployment with both annotations - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithBothAnnotations) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with both annotations %v", deploymentError) - } - - // Deleting Deployment with search annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapAnnotated) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with search annotation %v", deploymentError) - } - - // Deleting Deployment with secret and secret auto annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersSecretWithSecretAutoAnnotation) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret auto annotation %v", deploymentError) - } - - // Deleting Deployment with configmap and configmap auto annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithConfigMapAutoAnnotation) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap auto annotation %v", deploymentError) - } - - // Deleting Deployment with secret and secret exclude annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersSecretWithSecretExcludeAnnotation) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with secret exclude annotation %v", deploymentError) - } - - // Deleting Deployment with configmap and configmap exclude annotation - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithConfigMapExcludeAnnotation) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap exclude annotation %v", deploymentError) - } - - // Deleting DaemonSet with configmap - daemonSetError := testutil.DeleteDaemonSet(clients.KubernetesClient, ersNamespace, ersConfigmapName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with configmap %v", daemonSetError) - } - - // Deleting Deployment with secret - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, ersNamespace, ersSecretName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with secret %v", daemonSetError) - } - - // Deleting DaemonSet with configmap in projected volume - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, ersNamespace, ersProjectedConfigMapName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with configmap %v", daemonSetError) - } - - // Deleting Deployment with secret in projected volume - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, ersNamespace, ersProjectedSecretName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with secret %v", daemonSetError) - } - - // Deleting Deployment with configmap as env var source - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, ersNamespace, ersConfigmapWithEnvName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with configmap as env var source %v", daemonSetError) - } - - // Deleting Deployment with secret as env var source - daemonSetError = testutil.DeleteDaemonSet(clients.KubernetesClient, ersNamespace, ersSecretWithEnvName) - if daemonSetError != nil { - logrus.Errorf("Error while deleting daemonSet with secret as env var source %v", daemonSetError) - } - - // Deleting StatefulSet with configmap - statefulSetError := testutil.DeleteStatefulSet(clients.KubernetesClient, ersNamespace, ersConfigmapName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with configmap %v", statefulSetError) - } - - // Deleting Deployment with secret - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, ersNamespace, ersSecretName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with secret %v", statefulSetError) - } - - // Deleting StatefulSet with configmap in projected volume - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, ersNamespace, ersProjectedConfigMapName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with configmap %v", statefulSetError) - } - - // Deleting Deployment with secret in projected volume - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, ersNamespace, ersProjectedSecretName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with secret %v", statefulSetError) - } - - // Deleting StatefulSet with configmap as env var source - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, ersNamespace, ersConfigmapWithEnvName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with configmap as env var source %v", statefulSetError) - } - - // Deleting Deployment with secret as env var source - statefulSetError = testutil.DeleteStatefulSet(clients.KubernetesClient, ersNamespace, ersSecretWithEnvName) - if statefulSetError != nil { - logrus.Errorf("Error while deleting statefulSet with secret as env var source %v", statefulSetError) - } - - // Deleting Deployment for testing pausing deployments - deploymentError = testutil.DeleteDeployment(clients.KubernetesClient, ersNamespace, ersConfigmapWithPausedDeployment) - if deploymentError != nil { - logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) - } - - // Deleting Configmap - err := testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - - // Deleting Secret - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersSecretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - - // Deleting configmap used in projected volume - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersProjectedConfigMapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - - // Deleting Secret used in projected volume - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersProjectedSecretName) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - - // Deleting configmap used in projected volume in init containers - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersProjectedConfigMapWithInitContainer) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - - // Deleting secret used in projected volume in init containers - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersProjectedSecretWithInitContainer) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - - // Deleting Configmap used as env var source - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithEnvName) - if err != nil { - logrus.Errorf("Error while deleting the configmap used as env var source %v", err) - } - - // Deleting Secret used as env var source - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersSecretWithEnvName) - if err != nil { - logrus.Errorf("Error while deleting the secret used as env var source %v", err) - } - - // Deleting Configmap used in init container - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithInitContainer) - if err != nil { - logrus.Errorf("Error while deleting the configmap used in init container %v", err) - } - - // Deleting Secret used in init container - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersSecretWithInitContainer) - if err != nil { - logrus.Errorf("Error while deleting the secret used in init container %v", err) - } - - // Deleting Configmap used as env var source - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithEnvFromName) - if err != nil { - logrus.Errorf("Error while deleting the configmap used as env var source %v", err) - } - - // Deleting Secret used as env var source - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersSecretWithEnvFromName) - if err != nil { - logrus.Errorf("Error while deleting the secret used as env var source %v", err) - } - - // Deleting Configmap used as env var source - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithInitEnv) - if err != nil { - logrus.Errorf("Error while deleting the configmap used as env var source in init container %v", err) - } - - // Deleting Secret used as env var source - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersSecretWithInitEnv) - if err != nil { - logrus.Errorf("Error while deleting the secret used as env var source in init container %v", err) - } - - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithPodAnnotations) - if err != nil { - logrus.Errorf("Error while deleting the configmap used with pod annotations: %v", err) - } - - // Deleting Secret used with secret auto annotation - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersSecretWithSecretAutoAnnotation) - if err != nil { - logrus.Errorf("Error while deleting the secret used with secret auto annotation: %v", err) - } - - // Deleting ConfigMap used with configmap auto annotation - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithConfigMapAutoAnnotation) - if err != nil { - logrus.Errorf("Error while deleting the configmap used with configmap auto annotation: %v", err) - } - - // Deleting Secret used with secret exclude annotation - err = testutil.DeleteSecret(clients.KubernetesClient, ersNamespace, ersSecretWithSecretExcludeAnnotation) - if err != nil { - logrus.Errorf("Error while deleting the secret used with secret exclude annotation: %v", err) - } - - // Deleting ConfigMap used with configmap exclude annotation - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithConfigMapExcludeAnnotation) - if err != nil { - logrus.Errorf("Error while deleting the configmap used with configmap exclude annotation: %v", err) - } - - // Deleting ConfigMap for testing pausing deployments - err = testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapWithPausedDeployment) - if err != nil { - logrus.Errorf("Error while deleting the configmap: %v", err) - } - - // Deleting namespace - testutil.DeleteNamespace(ersNamespace, clients.KubernetesClient) - -} - -func getConfigWithAnnotations(resourceType string, name string, shaData string, annotation string, typedAutoAnnotation string) common.Config { - ns := ersNamespace - if options.ReloadStrategy == constants.AnnotationsReloadStrategy { - ns = arsNamespace - } - - return common.Config{ - Namespace: ns, - ResourceName: name, - SHAValue: shaData, - Annotation: annotation, - TypedAutoAnnotation: typedAutoAnnotation, - Type: resourceType, - } -} - -func getCollectors() metrics.Collectors { - return metrics.NewCollectors() -} - -var labelSucceeded = prometheus.Labels{"success": "true"} -var labelFailed = prometheus.Labels{"success": "false"} - -func testRollingUpgradeInvokeDeleteStrategyArs(t *testing.T, clients kube.Clients, config common.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, envVarPostfix string) { - err := PerformAction(clients, config, upgradeFuncs, collectors, nil, invokeDeleteStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", upgradeFuncs.ResourceType, envVarPostfix) - } - - config.SHAValue = testutil.GetSHAfromEmptyData() - removed := testutil.VerifyResourceAnnotationUpdate(clients, config, upgradeFuncs) - if !removed { - t.Errorf("%s was not updated", upgradeFuncs.ResourceType) - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 2 { - t.Errorf("Counter was not increased") - } -} - -func testRollingUpgradeWithPatchAndInvokeDeleteStrategyArs(t *testing.T, clients kube.Clients, config common.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, envVarPostfix string) { - err := PerformAction(clients, config, upgradeFuncs, collectors, nil, invokeDeleteStrategy) - upgradeFuncs.PatchFunc = func(client kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - assert.Equal(t, patchtypes.StrategicMergePatchType, patchType) - assert.NotEmpty(t, bytes) - return nil - } - upgradeFuncs.UpdateFunc = func(kube.Clients, string, runtime.Object) error { - t.Errorf("Update should not be called") - return nil - } - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", upgradeFuncs.ResourceType, envVarPostfix) - } -} - -func TestRollingUpgradeForDeploymentWithConfigmapUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - itemCalled := 0 - itemsCalled := 0 - - deploymentFuncs.ItemFunc = func(client kube.Clients, namespace string, name string) (runtime.Object, error) { - itemCalled++ - return callbacks.GetDeploymentItem(client, namespace, name) - } - deploymentFuncs.ItemsFunc = func(client kube.Clients, namespace string) []runtime.Object { - itemsCalled++ - return callbacks.GetDeploymentItems(client, namespace) - } - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - assert.Equal(t, 0, itemCalled, "ItemFunc should not be called") - assert.Equal(t, 2, itemsCalled, "ItemsFunc should be called twice") - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithPatchAndRetryUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - - assert.True(t, deploymentFuncs.SupportsPatch) - assert.NotEmpty(t, deploymentFuncs.PatchTemplatesFunc().AnnotationTemplate) - - itemCalled := 0 - itemsCalled := 0 - - deploymentFuncs.ItemFunc = func(client kube.Clients, namespace string, name string) (runtime.Object, error) { - itemCalled++ - return callbacks.GetDeploymentItem(client, namespace, name) - } - deploymentFuncs.ItemsFunc = func(client kube.Clients, namespace string) []runtime.Object { - itemsCalled++ - return callbacks.GetDeploymentItems(client, namespace) - } - - patchCalled := 0 - deploymentFuncs.PatchFunc = func(client kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - patchCalled++ - if patchCalled < 2 { - return &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonConflict}} // simulate conflict - } - assert.Equal(t, patchtypes.StrategicMergePatchType, patchType) - assert.NotEmpty(t, bytes) - assert.Contains(t, string(bytes), `{"spec":{"template":{"metadata":{"annotations":{"reloader.stakater.com/last-reloaded-from":`) - assert.Contains(t, string(bytes), `\"hash\":\"3c9a892aeaedc759abc3df9884a37b8be5680382\"`) - return nil - } - - deploymentFuncs.UpdateFunc = func(kube.Clients, string, runtime.Object) error { - t.Errorf("Update should not be called") - return nil - } - - collectors := getCollectors() - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap") - } - - assert.Equal(t, 1, itemCalled, "ItemFunc should be called once") - assert.Equal(t, 1, itemsCalled, "ItemsFunc should be called once") - assert.Equal(t, 2, patchCalled, "PatchFunc should be called twice") - - deploymentFuncs = GetDeploymentRollingUpgradeFuncs() - testRollingUpgradeWithPatchAndInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapWithoutReloadAnnotationAndWithoutAutoReloadAllNoTriggersUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigMapWithNonAnnotatedDeployment, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigMapWithNonAnnotatedDeployment, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if updated { - t.Errorf("Deployment was updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) > 0 { - t.Errorf("Counter was increased unexpectedly") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) > 0 { - t.Errorf("Counter by namespace was increased unexpectedly") - } -} - -func TestRollingUpgradeForDeploymentWithConfigmapWithoutReloadAnnotationButWithAutoReloadAllUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - options.AutoReloadAll = true - defer func() { options.AutoReloadAll = false }() - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigMapWithNonAnnotatedDeployment, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigMapWithNonAnnotatedDeployment, shaData, "", options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapInProjectedVolumeUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsProjectedConfigMapName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsProjectedConfigMapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap in projected volume") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapViaSearchAnnotationUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapAnnotated, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapAnnotated, shaData, "", options.ConfigmapReloaderAutoAnnotation) - config.ResourceAnnotations = map[string]string{"reloader.stakater.com/match": "true"} - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapViaSearchAnnotationNoTriggersUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapAnnotated, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapAnnotated, shaData, "", options.ConfigmapReloaderAutoAnnotation) - config.ResourceAnnotations = map[string]string{"reloader.stakater.com/match": "false"} - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - time.Sleep(5 * time.Second) - if updated { - t.Errorf("Deployment was updated unexpectedly") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) > 0 { - t.Errorf("Counter was increased unexpectedly") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) > 0 { - t.Errorf("Counter by namespace was increased unexpectedly") - } -} - -func TestRollingUpgradeForDeploymentWithConfigmapViaSearchAnnotationNotMappedUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - deployment, err := testutil.CreateDeploymentWithEnvVarSourceAndAnnotations( - clients.KubernetesClient, - arsConfigmapAnnotated+"-different", - arsNamespace, - map[string]string{"reloader.stakater.com/search": "true"}, - ) - if err != nil { - t.Errorf("Failed to create deployment with search annotation.") - } - defer func() { - _ = clients.KubernetesClient.AppsV1().Deployments(arsNamespace).Delete(context.TODO(), deployment.Name, metav1.DeleteOptions{}) - }() - // defer clients.KubernetesClient.AppsV1().Deployments(namespace).Delete(deployment.Name, &v1.DeleteOptions{}) - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapAnnotated, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapAnnotated, shaData, "", options.ConfigmapReloaderAutoAnnotation) - config.ResourceAnnotations = map[string]string{"reloader.stakater.com/match": "false"} - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err = PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if updated { - t.Errorf("Deployment was updated unexpectedly") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) > 0 { - t.Errorf("Counter was increased unexpectedly") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) > 0 { - t.Errorf("Counter by namespace was increased unexpectedly") - } -} - -func TestRollingUpgradeForDeploymentWithConfigmapInInitContainerUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithInitContainer, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithInitContainer, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapInProjectVolumeInInitContainerUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsProjectedConfigMapWithInitContainer, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsProjectedConfigMapWithInitContainer, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap in projected volume") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapAsEnvVarUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithEnvName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithEnvName, shaData, options.ReloaderAutoAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap used as env var") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapAsEnvVarInInitContainerUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithInitEnv, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithInitEnv, shaData, options.ReloaderAutoAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap used as env var") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapAsEnvVarFromUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithEnvFromName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithEnvFromName, shaData, options.ReloaderAutoAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap used as env var") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretInProjectedVolumeUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsProjectedSecretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsProjectedSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret in projected volume") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretinInitContainerUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretWithInitContainer, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsSecretWithInitContainer, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretInProjectedVolumeinInitContainerUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsProjectedSecretWithInitContainer, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsProjectedSecretWithInitContainer, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret in projected volume") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretAsEnvVarUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretWithEnvName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsSecretWithEnvName, shaData, options.ReloaderAutoAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretAsEnvVarFromUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretWithEnvFromName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsSecretWithEnvFromName, shaData, options.ReloaderAutoAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretAsEnvVarInInitContainerUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretWithInitEnv, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsSecretWithInitEnv, shaData, options.ReloaderAutoAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretExcludeAnnotationUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretWithExcludeSecretAnnotation, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsSecretWithExcludeSecretAnnotation, shaData, "", options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment did not update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if updated { - t.Errorf("Deployment which had to be excluded was updated") - } -} - -func TestRollingUpgradeForDeploymentWithSecretAutoAnnotationUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretWithSecretAutoAnnotation, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, arsSecretWithSecretAutoAnnotation, shaData, "", options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithExcludeConfigMapAnnotationUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithExcludeConfigMapAnnotation, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithExcludeConfigMapAnnotation, shaData, "", options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with exclude ConfigMap") - } - - logrus.Infof("Verifying deployment did update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if updated { - t.Errorf("Deployment which had to be excluded was updated") - } -} - -func TestRollingUpgradeForDeploymentWithConfigMapAutoAnnotationUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithConfigMapAutoAnnotation, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithConfigMapAutoAnnotation, shaData, "", options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with ConfigMap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithConfigmapUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapName, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - itemCalled := 0 - itemsCalled := 0 - - daemonSetFuncs.ItemFunc = func(client kube.Clients, namespace string, name string) (runtime.Object, error) { - itemCalled++ - return callbacks.GetDaemonSetItem(client, namespace, name) - } - daemonSetFuncs.ItemsFunc = func(client kube.Clients, namespace string) []runtime.Object { - itemsCalled++ - return callbacks.GetDaemonSetItems(client, namespace) - } - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with configmap") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - assert.Equal(t, 0, itemCalled, "ItemFunc should not be called") - assert.Equal(t, 2, itemsCalled, "ItemsFunc should be called twice") - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithPatchAndRetryUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapName, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - - itemCalled := 0 - itemsCalled := 0 - - daemonSetFuncs.ItemFunc = func(client kube.Clients, namespace string, name string) (runtime.Object, error) { - itemCalled++ - return callbacks.GetDaemonSetItem(client, namespace, name) - } - daemonSetFuncs.ItemsFunc = func(client kube.Clients, namespace string) []runtime.Object { - itemsCalled++ - return callbacks.GetDaemonSetItems(client, namespace) - } - - assert.True(t, daemonSetFuncs.SupportsPatch) - assert.NotEmpty(t, daemonSetFuncs.PatchTemplatesFunc().AnnotationTemplate) - - patchCalled := 0 - daemonSetFuncs.PatchFunc = func(client kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - patchCalled++ - if patchCalled < 2 { - return &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonConflict}} // simulate conflict - } - assert.Equal(t, patchtypes.StrategicMergePatchType, patchType) - assert.NotEmpty(t, bytes) - assert.Contains(t, string(bytes), `{"spec":{"template":{"metadata":{"annotations":{"reloader.stakater.com/last-reloaded-from":`) - assert.Contains(t, string(bytes), `\"hash\":\"314a2269170750a974d79f02b5b9ee517de7f280\"`) - return nil - } - - daemonSetFuncs.UpdateFunc = func(kube.Clients, string, runtime.Object) error { - t.Errorf("Update should not be called") - return nil - } - - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with configmap") - } - - assert.Equal(t, 1, itemCalled, "ItemFunc should be called once") - assert.Equal(t, 1, itemsCalled, "ItemsFunc should be called once") - assert.Equal(t, 2, patchCalled, "PatchFunc should be called twice") - - daemonSetFuncs = GetDeploymentRollingUpgradeFuncs() - testRollingUpgradeWithPatchAndInvokeDeleteStrategyArs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithConfigmapInProjectedVolumeUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsProjectedConfigMapName, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, arsProjectedConfigMapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with configmap in projected volume") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithConfigmapAsEnvVarUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithEnvName, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithEnvName, shaData, options.ReloaderAutoAnnotation, options.ConfigmapReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with configmap used as env var") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithSecretUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretName, "d3d3LmZhY2Vib29rLmNvbQ==") - config := getConfigWithAnnotations(envVarPostfix, arsSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with secret") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithSecretInProjectedVolumeUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsProjectedSecretName, "d3d3LmZhY2Vib29rLmNvbQ==") - config := getConfigWithAnnotations(envVarPostfix, arsProjectedSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with secret in projected volume") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithConfigmapUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapName, "www.twitter.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - collectors := getCollectors() - - itemCalled := 0 - itemsCalled := 0 - - statefulSetFuncs.ItemFunc = func(client kube.Clients, namespace string, name string) (runtime.Object, error) { - itemCalled++ - return callbacks.GetStatefulSetItem(client, namespace, name) - } - statefulSetFuncs.ItemsFunc = func(client kube.Clients, namespace string) []runtime.Object { - itemsCalled++ - return callbacks.GetStatefulSetItems(client, namespace) - } - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with configmap") - } - - logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - assert.Equal(t, 0, itemCalled, "ItemFunc should not be called") - assert.Equal(t, 2, itemsCalled, "ItemsFunc should be called twice") - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithPatchAndRetryUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapName, "www.twitter.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - - itemCalled := 0 - itemsCalled := 0 - - statefulSetFuncs.ItemFunc = func(client kube.Clients, namespace string, name string) (runtime.Object, error) { - itemCalled++ - return callbacks.GetStatefulSetItem(client, namespace, name) - } - statefulSetFuncs.ItemsFunc = func(client kube.Clients, namespace string) []runtime.Object { - itemsCalled++ - return callbacks.GetStatefulSetItems(client, namespace) - } - - assert.True(t, statefulSetFuncs.SupportsPatch) - assert.NotEmpty(t, statefulSetFuncs.PatchTemplatesFunc().AnnotationTemplate) - - patchCalled := 0 - statefulSetFuncs.PatchFunc = func(client kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - patchCalled++ - if patchCalled < 2 { - return &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonConflict}} // simulate conflict - } - assert.Equal(t, patchtypes.StrategicMergePatchType, patchType) - assert.NotEmpty(t, bytes) - assert.Contains(t, string(bytes), `{"spec":{"template":{"metadata":{"annotations":{"reloader.stakater.com/last-reloaded-from":`) - assert.Contains(t, string(bytes), `\"hash\":\"f821414d40d8815fb330763f74a4ff7ab651d4fa\"`) - return nil - } - - statefulSetFuncs.UpdateFunc = func(kube.Clients, string, runtime.Object) error { - t.Errorf("Update should not be called") - return nil - } - - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with configmap") - } - - assert.Equal(t, 1, itemCalled, "ItemFunc should be called once") - assert.Equal(t, 1, itemsCalled, "ItemsFunc should be called once") - assert.Equal(t, 2, patchCalled, "PatchFunc should be called twice") - - statefulSetFuncs = GetDeploymentRollingUpgradeFuncs() - testRollingUpgradeWithPatchAndInvokeDeleteStrategyArs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithConfigmapInProjectedVolumeUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsProjectedConfigMapName, "www.twitter.com") - config := getConfigWithAnnotations(envVarPostfix, arsProjectedConfigMapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with configmap in projected volume") - } - - logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithSecretUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsSecretName, "d3d3LnR3aXR0ZXIuY29t") - config := getConfigWithAnnotations(envVarPostfix, arsSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with secret") - } - - logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithSecretInProjectedVolumeUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, arsNamespace, arsProjectedSecretName, "d3d3LnR3aXR0ZXIuY29t") - config := getConfigWithAnnotations(envVarPostfix, arsProjectedSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with secret in projected volume") - } - - logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyArs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithPodAnnotationsUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithPodAnnotations, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithPodAnnotations, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with pod annotations") - } - - logrus.Infof("Verifying deployment update") - items := deploymentFuncs.ItemsFunc(clients, config.Namespace) - var foundPod, foundBoth bool - for _, i := range items { - accessor, err := meta.Accessor(i) - if err != nil { - t.Errorf("Error getting accessor for item: %v", err) - } - name := accessor.GetName() - if name == arsConfigmapWithPodAnnotations { - annotations := deploymentFuncs.PodAnnotationsFunc(i) - updated := testutil.GetResourceSHAFromAnnotation(annotations) - if updated != config.SHAValue { - t.Errorf("Deployment was not updated") - } - foundPod = true - } - if name == arsConfigmapWithBothAnnotations { - annotations := deploymentFuncs.PodAnnotationsFunc(i) - updated := testutil.GetResourceSHAFromAnnotation(annotations) - if updated == config.SHAValue { - t.Errorf("Deployment was updated") - } - foundBoth = true - } - } - if !foundPod { - t.Errorf("Deployment with pod annotations was not found") - } - if !foundBoth { - t.Errorf("Deployment with both annotations was not found") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } -} - -func TestFailedRollingUpgradeUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapName, "fail.stakater.com") - config := getConfigWithAnnotations(constants.ConfigmapEnvVarPostfix, arsConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - deploymentFuncs.UpdateFunc = func(_ kube.Clients, _ string, _ runtime.Object) error { - return fmt.Errorf("error") - } - deploymentFuncs.PatchFunc = func(kube.Clients, string, runtime.Object, patchtypes.PatchType, []byte) error { - return fmt.Errorf("error") - } - collectors := getCollectors() - - _ = PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelFailed)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "false", "namespace": arsNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } -} - -func TestIgnoreAnnotationNoReloadUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, arsNamespace, arsConfigmapWithIgnoreAnnotation, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, arsConfigmapWithIgnoreAnnotation, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - config.ResourceAnnotations = map[string]string{"reloader.stakater.com/ignore": "true"} - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap and ignore annotation using ARS") - } - - // Ensure deployment is NOT updated - updated := testutil.VerifyResourceAnnotationUpdate(clients, config, deploymentFuncs) - if updated { - t.Errorf("Deployment was updated but should not have been") - } - - // Ensure counters remain zero - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 0 { - t.Errorf("Reload counter should not have increased") - } - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": arsNamespace})) != 0 { - t.Errorf("Reload counter by namespace should not have increased") - } -} -func TestIgnoreAnnotationNoReloadUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithIgnoreAnnotation, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithIgnoreAnnotation, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - config.ResourceAnnotations = map[string]string{"reloader.stakater.com/ignore": "true"} - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap and ignore annotation using ERS") - } - - // Ensure deployment is NOT updated - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if updated { - t.Errorf("Deployment was updated but should not have been (ERS)") - } - - // Ensure counters remain zero - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 0 { - t.Errorf("Reload counter should not have increased (ERS)") - } - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 0 { - t.Errorf("Reload counter by namespace should not have increased (ERS)") - } -} - -func testRollingUpgradeInvokeDeleteStrategyErs(t *testing.T, clients kube.Clients, config common.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, envVarPostfix string) { - err := PerformAction(clients, config, upgradeFuncs, collectors, nil, invokeDeleteStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", upgradeFuncs.ResourceType, envVarPostfix) - } - - removed := testutil.VerifyResourceEnvVarRemoved(clients, config, envVarPostfix, upgradeFuncs) - if !removed { - t.Errorf("%s was not updated", upgradeFuncs.ResourceType) - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 2 { - t.Errorf("Counter was not increased") - } -} - -func testRollingUpgradeWithPatchAndInvokeDeleteStrategyErs(t *testing.T, clients kube.Clients, config common.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, envVarPostfix string) { - assert.NotEmpty(t, upgradeFuncs.PatchTemplatesFunc().DeleteEnvVarTemplate) - - err := PerformAction(clients, config, upgradeFuncs, collectors, nil, invokeDeleteStrategy) - upgradeFuncs.PatchFunc = func(client kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - assert.Equal(t, patchtypes.JSONPatchType, patchType) - assert.NotEmpty(t, bytes) - return nil - } - upgradeFuncs.UpdateFunc = func(kube.Clients, string, runtime.Object) error { - t.Errorf("Update should not be called") - return nil - } - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", upgradeFuncs.ResourceType, envVarPostfix) - } -} - -func TestRollingUpgradeForDeploymentWithConfigmapUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", deploymentFuncs.ResourceType, envVarPostfix) - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithPatchAndRetryUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - - assert.True(t, deploymentFuncs.SupportsPatch) - assert.NotEmpty(t, deploymentFuncs.PatchTemplatesFunc().EnvVarTemplate) - - patchCalled := 0 - deploymentFuncs.PatchFunc = func(client kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - patchCalled++ - if patchCalled < 2 { - return &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonConflict}} // simulate conflict - } - assert.Equal(t, patchtypes.StrategicMergePatchType, patchType) - assert.NotEmpty(t, bytes) - assert.Contains(t, string(bytes), `{"spec":{"template":{"spec":{"containers":[{"name":`) - assert.Contains(t, string(bytes), `"value":"3c9a892aeaedc759abc3df9884a37b8be5680382"`) - return nil - } - - deploymentFuncs.UpdateFunc = func(kube.Clients, string, runtime.Object) error { - t.Errorf("Update should not be called") - return nil - } - - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", deploymentFuncs.ResourceType, envVarPostfix) - } - - assert.Equal(t, 2, patchCalled) - - deploymentFuncs = GetDeploymentRollingUpgradeFuncs() - testRollingUpgradeWithPatchAndInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapInProjectedVolumeUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersProjectedConfigMapName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersProjectedConfigMapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap in projected volume") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapViaSearchAnnotationUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapAnnotated, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapAnnotated, shaData, "", options.ConfigmapReloaderAutoAnnotation) - config.ResourceAnnotations = map[string]string{"reloader.stakater.com/match": "true"} - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", deploymentFuncs.ResourceType, envVarPostfix) - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapViaSearchAnnotationNoTriggersUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapAnnotated, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapAnnotated, shaData, "", options.ConfigmapReloaderAutoAnnotation) - config.ResourceAnnotations = map[string]string{"reloader.stakater.com/match": "false"} - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", deploymentFuncs.ResourceType, envVarPostfix) - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - time.Sleep(5 * time.Second) - if updated { - t.Errorf("Deployment was updated unexpectedly") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) > 0 { - t.Errorf("Counter was increased unexpectedly") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) > 0 { - t.Errorf("Counter by namespace was increased unexpectedly") - } -} - -func TestRollingUpgradeForDeploymentWithConfigmapViaSearchAnnotationNotMappedUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - deployment, err := testutil.CreateDeploymentWithEnvVarSourceAndAnnotations( - clients.KubernetesClient, - ersConfigmapAnnotated+"-different", - ersNamespace, - map[string]string{"reloader.stakater.com/search": "true"}, - ) - if err != nil { - t.Errorf("Failed to create deployment with search annotation.") - } - defer func() { - _ = clients.KubernetesClient.AppsV1().Deployments(ersNamespace).Delete(context.TODO(), deployment.Name, metav1.DeleteOptions{}) - }() - // defer clients.KubernetesClient.AppsV1().Deployments(namespace).Delete(deployment.Name, &v1.DeleteOptions{}) - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapAnnotated, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapAnnotated, shaData, "", options.ConfigmapReloaderAutoAnnotation) - config.ResourceAnnotations = map[string]string{"reloader.stakater.com/match": "false"} - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err = PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", deploymentFuncs.ResourceType, envVarPostfix) - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if updated { - t.Errorf("Deployment was updated unexpectedly") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) > 0 { - t.Errorf("Counter was increased unexpectedly") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) > 0 { - t.Errorf("Counter by namespace was increased unexpectedly") - } -} - -func TestRollingUpgradeForDeploymentWithConfigmapInInitContainerUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithInitContainer, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithInitContainer, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for %s with %s", deploymentFuncs.ResourceType, envVarPostfix) - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapInProjectVolumeInInitContainerUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersProjectedConfigMapWithInitContainer, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersProjectedConfigMapWithInitContainer, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap in projected volume") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapAsEnvVarUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithEnvName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithEnvName, shaData, options.ReloaderAutoAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap used as env var") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapAsEnvVarInInitContainerUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithInitEnv, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithInitEnv, shaData, options.ReloaderAutoAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap used as env var") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigmapAsEnvVarFromUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithEnvFromName, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithEnvFromName, shaData, options.ReloaderAutoAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Configmap used as env var") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretInProjectedVolumeUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersProjectedSecretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersProjectedSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret in projected volume") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretinInitContainerUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretWithInitContainer, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersSecretWithInitContainer, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretInProjectedVolumeinInitContainerUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersProjectedSecretWithInitContainer, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersProjectedSecretWithInitContainer, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret in projected volume") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretAsEnvVarUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretWithEnvName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersSecretWithEnvName, shaData, options.ReloaderAutoAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretAsEnvVarFromUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretWithEnvFromName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersSecretWithEnvFromName, shaData, options.ReloaderAutoAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretAsEnvVarInInitContainerUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretWithInitEnv, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersSecretWithInitEnv, shaData, options.ReloaderAutoAnnotation, options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithSecretExcludeAnnotationUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretWithSecretExcludeAnnotation, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersSecretWithSecretExcludeAnnotation, shaData, "", options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with exclude Secret") - } - - logrus.Infof("Verifying deployment did not update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if updated { - t.Errorf("Deployment that had to be excluded was updated") - } -} - -func TestRollingUpgradeForDeploymentWithSecretAutoAnnotationUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretWithSecretAutoAnnotation, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := getConfigWithAnnotations(envVarPostfix, ersSecretWithSecretAutoAnnotation, shaData, "", options.SecretReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with Secret") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithConfigMapExcludeAnnotationUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithConfigMapExcludeAnnotation, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithConfigMapExcludeAnnotation, shaData, "", options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with exclude ConfigMap") - } - - logrus.Infof("Verifying deployment did not update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if updated { - t.Errorf("Deployment which had to be excluded was updated") - } -} - -func TestRollingUpgradeForDeploymentWithConfigMapAutoAnnotationUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithConfigMapAutoAnnotation, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithConfigMapAutoAnnotation, shaData, "", options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with ConfigMap") - } - - logrus.Infof("Verifying deployment update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, deploymentFuncs) - if !updated { - t.Errorf("Deployment was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, deploymentFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithConfigmapUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapName, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with configmap") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithPatchAndRetryUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapName, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - - assert.True(t, daemonSetFuncs.SupportsPatch) - assert.NotEmpty(t, daemonSetFuncs.PatchTemplatesFunc().EnvVarTemplate) - - patchCalled := 0 - daemonSetFuncs.PatchFunc = func(client kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - patchCalled++ - if patchCalled < 2 { - return &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonConflict}} // simulate conflict - } - assert.Equal(t, patchtypes.StrategicMergePatchType, patchType) - assert.NotEmpty(t, bytes) - assert.Contains(t, string(bytes), `{"spec":{"template":{"spec":{"containers":[{"name":`) - assert.Contains(t, string(bytes), `"value":"314a2269170750a974d79f02b5b9ee517de7f280"`) - return nil - } - - daemonSetFuncs.UpdateFunc = func(kube.Clients, string, runtime.Object) error { - t.Errorf("Update should not be called") - return nil - } - - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with configmap") - } - - assert.Equal(t, 2, patchCalled) - - daemonSetFuncs = GetDeploymentRollingUpgradeFuncs() - testRollingUpgradeWithPatchAndInvokeDeleteStrategyErs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithConfigmapInProjectedVolumeUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersProjectedConfigMapName, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, ersProjectedConfigMapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with configmap in projected volume") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithConfigmapAsEnvVarUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithEnvName, "www.facebook.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithEnvName, shaData, options.ReloaderAutoAnnotation, options.ConfigmapReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with configmap used as env var") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithSecretUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretName, "d3d3LmZhY2Vib29rLmNvbQ==") - config := getConfigWithAnnotations(envVarPostfix, ersSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with secret") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDaemonSetWithSecretInProjectedVolumeUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersProjectedSecretName, "d3d3LmZhY2Vib29rLmNvbQ==") - config := getConfigWithAnnotations(envVarPostfix, ersProjectedSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - daemonSetFuncs := GetDaemonSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, daemonSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for DaemonSet with secret in projected volume") - } - - logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, daemonSetFuncs) - if !updated { - t.Errorf("DaemonSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, daemonSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithConfigmapUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapName, "www.twitter.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with configmap") - } - - logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithPatchAndRetryUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapName, "www.twitter.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - - assert.True(t, statefulSetFuncs.SupportsPatch) - assert.NotEmpty(t, statefulSetFuncs.PatchTemplatesFunc().EnvVarTemplate) - - patchCalled := 0 - statefulSetFuncs.PatchFunc = func(client kube.Clients, namespace string, resource runtime.Object, patchType patchtypes.PatchType, bytes []byte) error { - patchCalled++ - if patchCalled < 2 { - return &errors.StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonConflict}} // simulate conflict - } - assert.Equal(t, patchtypes.StrategicMergePatchType, patchType) - assert.NotEmpty(t, bytes) - assert.Contains(t, string(bytes), `{"spec":{"template":{"spec":{"containers":[{"name":`) - assert.Contains(t, string(bytes), `"value":"f821414d40d8815fb330763f74a4ff7ab651d4fa"`) - return nil - } - - statefulSetFuncs.UpdateFunc = func(kube.Clients, string, runtime.Object) error { - t.Errorf("Update should not be called") - return nil - } - - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with configmap") - } - - assert.Equal(t, 2, patchCalled) - - statefulSetFuncs = GetDeploymentRollingUpgradeFuncs() - testRollingUpgradeWithPatchAndInvokeDeleteStrategyErs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithConfigmapInProjectedVolumeUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersProjectedConfigMapName, "www.twitter.com") - config := getConfigWithAnnotations(envVarPostfix, ersProjectedConfigMapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with configmap in projected volume") - } - - logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithSecretUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersSecretName, "d3d3LnR3aXR0ZXIuY29t") - config := getConfigWithAnnotations(envVarPostfix, ersSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with secret") - } - - logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForStatefulSetWithSecretInProjectedVolumeUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.SecretEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, ersNamespace, ersProjectedSecretName, "d3d3LnR3aXR0ZXIuY29t") - config := getConfigWithAnnotations(envVarPostfix, ersProjectedSecretName, shaData, options.SecretUpdateOnChangeAnnotation, options.SecretReloaderAutoAnnotation) - statefulSetFuncs := GetStatefulSetRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, statefulSetFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for StatefulSet with secret in projected volume") - } - - logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, statefulSetFuncs) - if !updated { - t.Errorf("StatefulSet was not updated") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - testRollingUpgradeInvokeDeleteStrategyErs(t, clients, config, statefulSetFuncs, collectors, envVarPostfix) -} - -func TestRollingUpgradeForDeploymentWithPodAnnotationsUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapWithPodAnnotations, "www.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapWithPodAnnotations, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - err := PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - time.Sleep(5 * time.Second) - if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with pod annotations") - } - - logrus.Infof("Verifying deployment update") - envName := constants.EnvVarPrefix + util.ConvertToEnvVarName(config.ResourceName) + "_" + envVarPostfix - items := deploymentFuncs.ItemsFunc(clients, config.Namespace) - var foundPod, foundBoth bool - for _, i := range items { - accessor, err := meta.Accessor(i) - if err != nil { - t.Errorf("Error getting accessor for item: %v", err) - } - name := accessor.GetName() - if name == ersConfigmapWithPodAnnotations { - containers := deploymentFuncs.ContainersFunc(i) - updated := testutil.GetResourceSHAFromEnvVar(containers, envName) - if updated != config.SHAValue { - t.Errorf("Deployment was not updated") - } - foundPod = true - } - if name == ersConfigmapWithBothAnnotations { - containers := deploymentFuncs.ContainersFunc(i) - updated := testutil.GetResourceSHAFromEnvVar(containers, envName) - if updated == config.SHAValue { - t.Errorf("Deployment was updated") - } - foundBoth = true - } - } - if !foundPod { - t.Errorf("Deployment with pod annotations was not found") - } - if !foundBoth { - t.Errorf("Deployment with both annotations was not found") - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } -} - -func TestFailedRollingUpgradeUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapName, "fail.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, ersConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - deploymentFuncs.UpdateFunc = func(_ kube.Clients, _ string, _ runtime.Object) error { - return fmt.Errorf("error") - } - deploymentFuncs.PatchFunc = func(kube.Clients, string, runtime.Object, patchtypes.PatchType, []byte) error { - return fmt.Errorf("error") - } - collectors := getCollectors() - - _ = PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelFailed)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "false", "namespace": ersNamespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } -} - -func TestPausingDeploymentUsingErs(t *testing.T) { - options.ReloadStrategy = constants.EnvVarsReloadStrategy - testPausingDeployment(t, options.ReloadStrategy, ersConfigmapWithPausedDeployment, ersNamespace) -} - -func TestPausingDeploymentUsingArs(t *testing.T) { - options.ReloadStrategy = constants.AnnotationsReloadStrategy - testPausingDeployment(t, options.ReloadStrategy, arsConfigmapWithPausedDeployment, arsNamespace) -} - -func testPausingDeployment(t *testing.T, reloadStrategy string, testName string, namespace string) { - options.ReloadStrategy = reloadStrategy - envVarPostfix := constants.ConfigmapEnvVarPostfix - - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, testName, "pause.stakater.com") - config := getConfigWithAnnotations(envVarPostfix, testName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - deploymentFuncs := GetDeploymentRollingUpgradeFuncs() - collectors := getCollectors() - - _ = PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - - // Wait for deployment to have paused-at annotation - logrus.Infof("Waiting for deployment %s to have paused-at annotation", testName) - err := waitForDeploymentPausedAtAnnotation(clients, deploymentFuncs, config.Namespace, testName, 30*time.Second) - if err != nil { - t.Errorf("Failed to wait for deployment paused-at annotation: %v", err) - } - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": namespace})) != 1 { - t.Errorf("Counter by namespace was not increased") - } - - logrus.Infof("Verifying deployment has been paused") - items := deploymentFuncs.ItemsFunc(clients, config.Namespace) - deploymentPaused, err := isDeploymentPaused(items, testName) - if err != nil { - t.Errorf("%s", err.Error()) - } - if !deploymentPaused { - t.Errorf("Deployment has not been paused") - } - - shaData = testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, testName, "pause-changed.stakater.com") - config = getConfigWithAnnotations(envVarPostfix, testName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation) - - _ = PerformAction(clients, config, deploymentFuncs, collectors, nil, invokeReloadStrategy) - - if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 2 { - t.Errorf("Counter was not increased") - } - - if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": namespace})) != 2 { - t.Errorf("Counter by namespace was not increased") - } - - logrus.Infof("Verifying deployment is still paused") - items = deploymentFuncs.ItemsFunc(clients, config.Namespace) - deploymentPaused, err = isDeploymentPaused(items, testName) - if err != nil { - t.Errorf("%s", err.Error()) - } - if !deploymentPaused { - t.Errorf("Deployment should still be paused") - } - - logrus.Infof("Verifying deployment has been resumed after pause interval") - time.Sleep(11 * time.Second) - items = deploymentFuncs.ItemsFunc(clients, config.Namespace) - deploymentPaused, err = isDeploymentPaused(items, testName) - if err != nil { - t.Errorf("%s", err.Error()) - } - if deploymentPaused { - t.Errorf("Deployment should have been resumed after pause interval") - } -} - -func isDeploymentPaused(deployments []runtime.Object, deploymentName string) (bool, error) { - deployment, err := FindDeploymentByName(deployments, deploymentName) - if err != nil { - return false, err - } - return IsPaused(deployment), nil -} - -// waitForDeploymentPausedAtAnnotation waits for a deployment to have the pause-period annotation -func waitForDeploymentPausedAtAnnotation(clients kube.Clients, deploymentFuncs callbacks.RollingUpgradeFuncs, namespace, deploymentName string, timeout time.Duration) error { - start := time.Now() - - for time.Since(start) < timeout { - items := deploymentFuncs.ItemsFunc(clients, namespace) - deployment, err := FindDeploymentByName(items, deploymentName) - if err == nil { - annotations := deployment.GetAnnotations() - if annotations != nil { - if _, exists := annotations[options.PauseDeploymentTimeAnnotation]; exists { - return nil - } - } - } - - time.Sleep(100 * time.Millisecond) - } - - return fmt.Errorf("timeout waiting for deployment %s to have pause-period annotation", deploymentName) -} - -// MockArgoRolloutWithEmptyContainers creates a mock Argo Rollout with no containers -// This simulates the scenario where Argo Rollouts with workloadRef return empty containers -func MockArgoRolloutWithEmptyContainers(namespace, name string) *runtime.Object { - rollout := &argorolloutv1alpha1.Rollout{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: argorolloutv1alpha1.RolloutSpec{ - Template: v1.PodTemplateSpec{ - Spec: v1.PodSpec{ - Containers: []v1.Container{}, // Empty containers slice - InitContainers: []v1.Container{}, // Empty init containers slice - Volumes: []v1.Volume{}, // Empty volumes slice - }, - }, - }, - } - var obj runtime.Object = rollout - return &obj -} - -// TestGetContainerUsingResourceWithArgoRolloutEmptyContainers tests with real Argo Rollout functions -func TestGetContainerUsingResourceWithArgoRolloutEmptyContainers(t *testing.T) { - namespace := "test-namespace" - resourceName := "test-configmap" - - // Use real Argo Rollout functions but mock the containers function - rolloutFuncs := GetArgoRolloutRollingUpgradeFuncs() - originalContainersFunc := rolloutFuncs.ContainersFunc - originalInitContainersFunc := rolloutFuncs.InitContainersFunc - - // Override to return empty containers (simulating workloadRef scenario) - rolloutFuncs.ContainersFunc = func(item runtime.Object) []v1.Container { - return []v1.Container{} // Empty like workloadRef rollouts - } - rolloutFuncs.InitContainersFunc = func(item runtime.Object) []v1.Container { - return []v1.Container{} // Empty like workloadRef rollouts - } - - // Restore original functions after test - defer func() { - rolloutFuncs.ContainersFunc = originalContainersFunc - rolloutFuncs.InitContainersFunc = originalInitContainersFunc - }() - - // Use proper Argo Rollout object instead of Pod - mockRollout := MockArgoRolloutWithEmptyContainers(namespace, "test-rollout") - - config := common.Config{ - Namespace: namespace, - ResourceName: resourceName, - Type: constants.ConfigmapEnvVarPostfix, - SHAValue: "test-sha", - } - - // Test both autoReload scenarios using subtests as suggested by Felix - for _, autoReload := range []bool{true, false} { - t.Run(fmt.Sprintf("autoReload_%t", autoReload), func(t *testing.T) { - // This tests the actual fix in the context of Argo Rollouts - result := getContainerUsingResource(rolloutFuncs, *mockRollout, config, autoReload) - - if result != nil { - t.Errorf("Expected nil when using real Argo Rollout functions with empty containers (workloadRef scenario), got %v", result) - } - }) - } -} diff --git a/internal/pkg/leadership/leadership.go b/internal/pkg/leadership/leadership.go deleted file mode 100644 index f8c85bc1..00000000 --- a/internal/pkg/leadership/leadership.go +++ /dev/null @@ -1,107 +0,0 @@ -package leadership - -import ( - "context" - "net/http" - "sync" - "time" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/controller" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/leaderelection" - "k8s.io/client-go/tools/leaderelection/resourcelock" - - coordinationv1 "k8s.io/client-go/kubernetes/typed/coordination/v1" -) - -var ( - // Used for liveness probe - m sync.Mutex - healthy bool = true -) - -func GetNewLock(client coordinationv1.CoordinationV1Interface, lockName, podname, namespace string) *resourcelock.LeaseLock { - return &resourcelock.LeaseLock{ - LeaseMeta: v1.ObjectMeta{ - Name: lockName, - Namespace: namespace, - }, - Client: client, - LockConfig: resourcelock.ResourceLockConfig{ - Identity: podname, - }, - } -} - -// runLeaderElection runs leadership election. If an instance of the controller is the leader and stops leading it will shutdown. -func RunLeaderElection(lock *resourcelock.LeaseLock, ctx context.Context, cancel context.CancelFunc, id string, controllers []*controller.Controller) { - // Construct channels for the controllers to use - var stopChannels []chan struct{} - for i := 0; i < len(controllers); i++ { - stop := make(chan struct{}) - stopChannels = append(stopChannels, stop) - } - - leaderelection.RunOrDie(ctx, leaderelection.LeaderElectionConfig{ - Lock: lock, - ReleaseOnCancel: true, - LeaseDuration: 15 * time.Second, - RenewDeadline: 10 * time.Second, - RetryPeriod: 2 * time.Second, - Callbacks: leaderelection.LeaderCallbacks{ - OnStartedLeading: func(c context.Context) { - logrus.Info("became leader, starting controllers") - runControllers(controllers, stopChannels) - }, - OnStoppedLeading: func() { - logrus.Info("no longer leader, shutting down") - stopControllers(stopChannels) - cancel() - m.Lock() - defer m.Unlock() - healthy = false - }, - OnNewLeader: func(current_id string) { - if current_id == id { - logrus.Info("still the leader!") - return - } - logrus.Infof("new leader is %s", current_id) - }, - }, - }) -} - -func runControllers(controllers []*controller.Controller, stopChannels []chan struct{}) { - for i, c := range controllers { - c := c - go c.Run(1, stopChannels[i]) - } -} - -func stopControllers(stopChannels []chan struct{}) { - for _, c := range stopChannels { - close(c) - } -} - -// Healthz sets up the liveness probe endpoint. If leadership election is -// enabled and a replica stops leading the liveness probe will fail and the -// kubelet will restart the container. -func SetupLivenessEndpoint() { - http.HandleFunc("/live", healthz) -} - -func healthz(w http.ResponseWriter, req *http.Request) { - m.Lock() - defer m.Unlock() - if healthy { - if i, err := w.Write([]byte("alive")); err != nil { - logrus.Infof("failed to write liveness response, wrote: %d bytes, got err: %s", i, err) - } - return - } - - w.WriteHeader(http.StatusInternalServerError) -} diff --git a/internal/pkg/leadership/leadership_test.go b/internal/pkg/leadership/leadership_test.go deleted file mode 100644 index d850e7a9..00000000 --- a/internal/pkg/leadership/leadership_test.go +++ /dev/null @@ -1,216 +0,0 @@ -//go:build integration -// +build integration - -package leadership - -import ( - "context" - "fmt" - "net/http" - "net/http/httptest" - "os" - "testing" - "time" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/controller" - "github.com/stakater/Reloader/internal/pkg/handler" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/testutil" - "github.com/stakater/Reloader/pkg/common" - "github.com/stakater/Reloader/pkg/kube" -) - -func TestMain(m *testing.M) { - - testutil.CreateNamespace(testutil.Namespace, testutil.Clients.KubernetesClient) - - logrus.Infof("Running Testcases") - retCode := m.Run() - - testutil.DeleteNamespace(testutil.Namespace, testutil.Clients.KubernetesClient) - - os.Exit(retCode) -} - -func TestHealthz(t *testing.T) { - request, err := http.NewRequest(http.MethodGet, "/live", nil) - if err != nil { - t.Fatalf(("failed to create request")) - } - - response := httptest.NewRecorder() - - healthz(response, request) - got := response.Code - want := 200 - - if got != want { - t.Fatalf("got: %q, want: %q", got, want) - } - - // Have the liveness probe serve a 500 - healthy = false - - request, err = http.NewRequest(http.MethodGet, "/live", nil) - if err != nil { - t.Fatalf(("failed to create request")) - } - - response = httptest.NewRecorder() - - healthz(response, request) - got = response.Code - want = 500 - - if got != want { - t.Fatalf("got: %q, want: %q", got, want) - } -} - -// TestRunLeaderElection validates that the liveness endpoint serves 500 when -// leadership election fails -func TestRunLeaderElection(t *testing.T) { - ctx, cancel := context.WithCancel(context.TODO()) - - lock := GetNewLock(testutil.Clients.KubernetesClient.CoordinationV1(), constants.LockName, testutil.Pod, testutil.Namespace) - - go RunLeaderElection(lock, ctx, cancel, testutil.Pod, []*controller.Controller{}) - - // Liveness probe should be serving OK - request, err := http.NewRequest(http.MethodGet, "/live", nil) - if err != nil { - t.Fatalf(("failed to create request")) - } - - response := httptest.NewRecorder() - - healthz(response, request) - got := response.Code - want := 500 - - if got != want { - t.Fatalf("got: %q, want: %q", got, want) - } - - // Cancel the leader election context, so leadership is released and - // live endpoint serves 500 - cancel() - - request, err = http.NewRequest(http.MethodGet, "/live", nil) - if err != nil { - t.Fatalf(("failed to create request")) - } - - response = httptest.NewRecorder() - - healthz(response, request) - got = response.Code - want = 500 - - if got != want { - t.Fatalf("got: %q, want: %q", got, want) - } -} - -// TestRunLeaderElectionWithControllers tests that leadership election works -// with real controllers and that on context cancellation the controllers stop -// running. -func TestRunLeaderElectionWithControllers(t *testing.T) { - t.Logf("Creating controller") - var controllers []*controller.Controller - for k := range kube.ResourceMap { - c, err := controller.NewController(testutil.Clients.KubernetesClient, k, testutil.Namespace, []string{}, "", "", metrics.NewCollectors()) - if err != nil { - logrus.Fatalf("%s", err) - } - - controllers = append(controllers, c) - } - time.Sleep(3 * time.Second) - - lock := GetNewLock(testutil.Clients.KubernetesClient.CoordinationV1(), fmt.Sprintf("%s-%d", constants.LockName, 1), testutil.Pod, testutil.Namespace) - - ctx, cancel := context.WithCancel(context.TODO()) - - // Start running leadership election, this also starts the controllers - go RunLeaderElection(lock, ctx, cancel, testutil.Pod, controllers) - time.Sleep(3 * time.Second) - - // Create some stuff and do a thing - configmapName := testutil.ConfigmapNamePrefix + "-update-" + testutil.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(testutil.Clients.KubernetesClient, testutil.Namespace, configmapName, "www.google.com") - if err != nil { - t.Fatalf("Error while creating the configmap %v", err) - } - - // Creating deployment - _, err = testutil.CreateDeployment(testutil.Clients.KubernetesClient, configmapName, testutil.Namespace, true) - if err != nil { - t.Fatalf("Error in deployment creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, testutil.Namespace, configmapName, "", "www.stakater.com") - if updateErr != nil { - t.Fatalf("Configmap was not updated") - } - time.Sleep(3 * time.Second) - - // Verifying deployment update - logrus.Infof("Verifying pod envvars has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, testutil.Namespace, configmapName, "www.stakater.com") - config := common.Config{ - Namespace: testutil.Namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs := handler.GetDeploymentRollingUpgradeFuncs() - updated := testutil.VerifyResourceEnvVarUpdate(testutil.Clients, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) - if !updated { - t.Fatalf("Deployment was not updated") - } - time.Sleep(testutil.SleepDuration) - - // Cancel the leader election context, so leadership is released - logrus.Info("shutting down controller from test") - cancel() - time.Sleep(5 * time.Second) - - // Updating configmap again - updateErr = testutil.UpdateConfigMap(configmapClient, testutil.Namespace, configmapName, "", "www.stakater.com/new") - if updateErr != nil { - t.Fatalf("Configmap was not updated") - } - - // Verifying that the deployment was not updated as leadership has been lost - logrus.Infof("Verifying pod envvars has not been updated") - shaData = testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, testutil.Namespace, configmapName, "www.stakater.com/new") - config = common.Config{ - Namespace: testutil.Namespace, - ResourceName: configmapName, - SHAValue: shaData, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - } - deploymentFuncs = handler.GetDeploymentRollingUpgradeFuncs() - updated = testutil.VerifyResourceEnvVarUpdate(testutil.Clients, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) - if updated { - t.Fatalf("Deployment was updated") - } - - // Deleting deployment - err = testutil.DeleteDeployment(testutil.Clients.KubernetesClient, testutil.Namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the deployment %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(testutil.Clients.KubernetesClient, testutil.Namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(testutil.SleepDuration) -} diff --git a/internal/pkg/metadata/metadata.go b/internal/pkg/metadata/metadata.go index 09db4e8e..f0e22f12 100644 --- a/internal/pkg/metadata/metadata.go +++ b/internal/pkg/metadata/metadata.go @@ -10,7 +10,7 @@ import ( "runtime" "time" - "github.com/sirupsen/logrus" + "github.com/go-logr/logr" "github.com/stakater/Reloader/internal/pkg/config" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -199,30 +199,29 @@ func (m *MetaInfo) ToConfigMap() *corev1.ConfigMap { type Publisher struct { client client.Client cfg *config.Config + log logr.Logger } // NewPublisher creates a new Publisher. -func NewPublisher(c client.Client, cfg *config.Config) *Publisher { +func NewPublisher(c client.Client, cfg *config.Config, log logr.Logger) *Publisher { return &Publisher{ client: c, cfg: cfg, + log: log, } } // Publish creates or updates the metadata ConfigMap. -// Returns an error if the operation fails, or nil on success. -// If RELOADER_NAMESPACE is not set, this is a no-op. func (p *Publisher) Publish(ctx context.Context) error { namespace := os.Getenv(EnvReloaderNamespace) if namespace == "" { - logrus.Warn("RELOADER_NAMESPACE is not set, skipping meta info configmap creation") + p.log.Info("RELOADER_NAMESPACE is not set, skipping meta info configmap creation") return nil } metaInfo := NewMetaInfo(p.cfg) configMap := metaInfo.ToConfigMap() - // Try to get existing ConfigMap existing := &corev1.ConfigMap{} err := p.client.Get(ctx, client.ObjectKey{ Name: ConfigMapName, @@ -233,34 +232,36 @@ func (p *Publisher) Publish(ctx context.Context) error { if !errors.IsNotFound(err) { return fmt.Errorf("failed to get existing meta info configmap: %w", err) } - // ConfigMap doesn't exist, create it - logrus.Info("Creating meta info configmap") + p.log.Info("Creating meta info configmap") if err := p.client.Create(ctx, configMap, client.FieldOwner(FieldManager)); err != nil { return fmt.Errorf("failed to create meta info configmap: %w", err) } - logrus.Info("Meta info configmap created successfully") + p.log.Info("Meta info configmap created successfully") return nil } - // ConfigMap exists, update it - logrus.Info("Meta info configmap already exists, updating it") + p.log.Info("Meta info configmap already exists, updating it") existing.Data = configMap.Data existing.Labels = configMap.Labels if err := p.client.Update(ctx, existing, client.FieldOwner(FieldManager)); err != nil { return fmt.Errorf("failed to update meta info configmap: %w", err) } - logrus.Info("Meta info configmap updated successfully") + p.log.Info("Meta info configmap updated successfully") return nil } // PublishMetaInfoConfigMap is a convenience function that creates a Publisher and calls Publish. -// This provides a simple API similar to the v1 PublishMetaInfoConfigmap function. -func PublishMetaInfoConfigMap(ctx context.Context, c client.Client, cfg *config.Config) error { - publisher := NewPublisher(c, cfg) +func PublishMetaInfoConfigMap(ctx context.Context, c client.Client, cfg *config.Config, log logr.Logger) error { + publisher := NewPublisher(c, cfg, log) return publisher.Publish(ctx) } -// toJSON marshals data to JSON string. Returns empty string on error. +// CreateOrUpdate creates or updates the metadata ConfigMap using the provided client. +func CreateOrUpdate(c client.Client, cfg *config.Config, log logr.Logger) error { + ctx := context.Background() + return PublishMetaInfoConfigMap(ctx, c, cfg, log) +} + func toJSON(data interface{}) string { jsonData, err := json.Marshal(data) if err != nil { @@ -269,8 +270,6 @@ func toJSON(data interface{}) string { return string(jsonData) } -// parseUTCTime parses a time string in RFC3339 format. -// Returns zero time if value is empty or parsing fails. func parseUTCTime(value string) time.Time { if value == "" { return time.Time{} diff --git a/internal/pkg/metadata/metadata_test.go b/internal/pkg/metadata/metadata_test.go index 94fce183..b001da6a 100644 --- a/internal/pkg/metadata/metadata_test.go +++ b/internal/pkg/metadata/metadata_test.go @@ -6,6 +6,7 @@ import ( "os" "testing" + "github.com/go-logr/logr" "github.com/stakater/Reloader/internal/pkg/config" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" @@ -13,6 +14,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" ) +// testLogger returns a no-op logger for testing. +func testLogger() logr.Logger { + return logr.Discard() +} + func TestNewBuildInfo(t *testing.T) { // Set build variables for testing oldVersion := Version @@ -166,7 +172,7 @@ func TestPublisher_Publish_NoNamespace(t *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build() cfg := config.NewDefault() - publisher := NewPublisher(fakeClient, cfg) + publisher := NewPublisher(fakeClient, cfg, testLogger()) err := publisher.Publish(context.Background()) if err != nil { @@ -188,7 +194,7 @@ func TestPublisher_Publish_CreateNew(t *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build() cfg := config.NewDefault() - publisher := NewPublisher(fakeClient, cfg) + publisher := NewPublisher(fakeClient, cfg, testLogger()) ctx := context.Background() err := publisher.Publish(ctx) @@ -233,7 +239,7 @@ func TestPublisher_Publish_UpdateExisting(t *testing.T) { Build() cfg := config.NewDefault() - publisher := NewPublisher(fakeClient, cfg) + publisher := NewPublisher(fakeClient, cfg, testLogger()) ctx := context.Background() err := publisher.Publish(ctx) @@ -277,7 +283,7 @@ func TestPublishMetaInfoConfigMap(t *testing.T) { cfg := config.NewDefault() ctx := context.Background() - err := PublishMetaInfoConfigMap(ctx, fakeClient, cfg) + err := PublishMetaInfoConfigMap(ctx, fakeClient, cfg, testLogger()) if err != nil { t.Errorf("PublishMetaInfoConfigMap() error = %v", err) } diff --git a/internal/pkg/options/flags.go b/internal/pkg/options/flags.go deleted file mode 100644 index 0f99be8a..00000000 --- a/internal/pkg/options/flags.go +++ /dev/null @@ -1,92 +0,0 @@ -package options - -import "github.com/stakater/Reloader/internal/pkg/constants" - -type ArgoRolloutStrategy int - -const ( - // RestartStrategy is the annotation value for restart strategy for rollouts - RestartStrategy ArgoRolloutStrategy = iota - // RolloutStrategy is the annotation value for rollout strategy for rollouts - RolloutStrategy -) - -var ( - // Auto reload all resources when their corresponding configmaps/secrets are updated - AutoReloadAll = false - // ConfigmapUpdateOnChangeAnnotation is an annotation to detect changes in - // configmaps specified by name - ConfigmapUpdateOnChangeAnnotation = "configmap.reloader.stakater.com/reload" - // SecretUpdateOnChangeAnnotation is an annotation to detect changes in - // secrets specified by name - SecretUpdateOnChangeAnnotation = "secret.reloader.stakater.com/reload" - // ReloaderAutoAnnotation is an annotation to detect changes in secrets/configmaps - ReloaderAutoAnnotation = "reloader.stakater.com/auto" - // IgnoreResourceAnnotation is an annotation to ignore changes in secrets/configmaps - IgnoreResourceAnnotation = "reloader.stakater.com/ignore" - // ConfigmapReloaderAutoAnnotation is an annotation to detect changes in configmaps - ConfigmapReloaderAutoAnnotation = "configmap.reloader.stakater.com/auto" - // SecretReloaderAutoAnnotation is an annotation to detect changes in secrets - SecretReloaderAutoAnnotation = "secret.reloader.stakater.com/auto" - // ConfigmapReloaderAutoAnnotation is a comma separated list of configmaps that excludes detecting changes on cms - ConfigmapExcludeReloaderAnnotation = "configmaps.exclude.reloader.stakater.com/reload" - // SecretExcludeReloaderAnnotation is a comma separated list of secrets that excludes detecting changes on secrets - SecretExcludeReloaderAnnotation = "secrets.exclude.reloader.stakater.com/reload" - // AutoSearchAnnotation is an annotation to detect changes in - // configmaps or triggers with the SearchMatchAnnotation - AutoSearchAnnotation = "reloader.stakater.com/search" - // SearchMatchAnnotation is an annotation to tag secrets to be found with - // AutoSearchAnnotation - SearchMatchAnnotation = "reloader.stakater.com/match" - // RolloutStrategyAnnotation is an annotation to define rollout update strategy - RolloutStrategyAnnotation = "reloader.stakater.com/rollout-strategy" - // PauseDeploymentAnnotation is an annotation to define the time period to pause a deployment after - // a configmap/secret change has been detected. Valid values are described here: https://pkg.go.dev/time#ParseDuration - // only positive values are allowed - PauseDeploymentAnnotation = "deployment.reloader.stakater.com/pause-period" - // Annotation set by reloader to indicate that the deployment has been paused - PauseDeploymentTimeAnnotation = "deployment.reloader.stakater.com/paused-at" - // LogFormat is the log format to use (json, or empty string for default) - LogFormat = "" - // LogLevel is the log level to use (trace, debug, info, warning, error, fatal and panic) - LogLevel = "" - // IsArgoRollouts Adds support for argo rollouts - IsArgoRollouts = "false" - // ReloadStrategy Specify the update strategy - ReloadStrategy = constants.EnvVarsReloadStrategy - // ReloadOnCreate Adds support to watch create events - ReloadOnCreate = "false" - // ReloadOnDelete Adds support to watch delete events - ReloadOnDelete = "false" - SyncAfterRestart = false - // EnableHA adds support for running multiple replicas via leadership election - EnableHA = false - // Url to send a request to instead of triggering a reload - WebhookUrl = "" - // ResourcesToIgnore is a list of resources to ignore when watching for changes - ResourcesToIgnore = []string{} - // WorkloadTypesToIgnore is a list of workload types to ignore when watching for changes - WorkloadTypesToIgnore = []string{} - // NamespacesToIgnore is a list of namespace names to ignore when watching for changes - NamespacesToIgnore = []string{} - // NamespaceSelectors is a list of namespace selectors to watch for changes - NamespaceSelectors = []string{} - // ResourceSelectors is a list of resource selectors to watch for changes - ResourceSelectors = []string{} - // EnablePProf enables pprof for profiling - EnablePProf = false - // PProfAddr is the address to start pprof server on - // Default is :6060 - PProfAddr = ":6060" -) - -func ToArgoRolloutStrategy(s string) ArgoRolloutStrategy { - switch s { - case "restart": - return RestartStrategy - case "rollout": - fallthrough - default: - return RolloutStrategy - } -} diff --git a/internal/pkg/reload/hasher.go b/internal/pkg/reload/hasher.go index 3839fd1a..0d259ac3 100644 --- a/internal/pkg/reload/hasher.go +++ b/internal/pkg/reload/hasher.go @@ -13,7 +13,6 @@ import ( ) // Hasher computes content hashes for ConfigMaps and Secrets. -// The hash is used to detect changes and trigger workload reloads. type Hasher struct{} // NewHasher creates a new Hasher instance. @@ -22,7 +21,6 @@ func NewHasher() *Hasher { } // HashConfigMap computes a SHA1 hash of the ConfigMap's data and binaryData. -// The hash is deterministic - same content always produces the same hash. func (h *Hasher) HashConfigMap(cm *corev1.ConfigMap) string { if cm == nil { return h.computeSHA("") @@ -31,7 +29,6 @@ func (h *Hasher) HashConfigMap(cm *corev1.ConfigMap) string { } // HashSecret computes a SHA1 hash of the Secret's data. -// The hash is deterministic - same content always produces the same hash. func (h *Hasher) HashSecret(secret *corev1.Secret) string { if secret == nil { return h.computeSHA("") @@ -39,8 +36,6 @@ func (h *Hasher) HashSecret(secret *corev1.Secret) string { return h.hashSecretData(secret.Data) } -// hashConfigMapData computes a hash from ConfigMap data and binary data. -// Keys are sorted to ensure deterministic output. func (h *Hasher) hashConfigMapData(data map[string]string, binaryData map[string][]byte) string { values := make([]string, 0, len(data)+len(binaryData)) @@ -49,7 +44,6 @@ func (h *Hasher) hashConfigMapData(data map[string]string, binaryData map[string } for k, v := range binaryData { - // Binary data is base64 encoded for consistent hashing values = append(values, k+"="+base64.StdEncoding.EncodeToString(v)) } @@ -57,13 +51,10 @@ func (h *Hasher) hashConfigMapData(data map[string]string, binaryData map[string return h.computeSHA(strings.Join(values, ";")) } -// hashSecretData computes a hash from Secret data. -// Keys are sorted to ensure deterministic output. func (h *Hasher) hashSecretData(data map[string][]byte) string { values := make([]string, 0, len(data)) for k, v := range data { - // Secret data is stored as raw bytes, not base64 encoded values = append(values, k+"="+string(v)) } @@ -71,7 +62,6 @@ func (h *Hasher) hashSecretData(data map[string][]byte) string { return h.computeSHA(strings.Join(values, ";")) } -// computeSHA generates a SHA1 hash from a string. func (h *Hasher) computeSHA(data string) string { hasher := sha1.New() _, _ = io.WriteString(hasher, data) @@ -79,7 +69,6 @@ func (h *Hasher) computeSHA(data string) string { } // EmptyHash returns an empty string to signal resource deletion. -// This triggers env var removal when using the env-vars strategy. func (h *Hasher) EmptyHash() string { return "" } diff --git a/internal/pkg/reload/matcher.go b/internal/pkg/reload/matcher.go index 51bd7d6c..6f56d8ef 100644 --- a/internal/pkg/reload/matcher.go +++ b/internal/pkg/reload/matcher.go @@ -19,13 +19,9 @@ const ( // MatchResult contains the result of checking if a workload should be reloaded. type MatchResult struct { - // ShouldReload indicates whether the workload should be reloaded. ShouldReload bool - // AutoReload indicates if this is an auto-reload (vs explicit annotation). - // This affects which container to target for env var injection. - AutoReload bool - // Reason provides a human-readable explanation of the decision. - Reason string + AutoReload bool + Reason string } // Matcher determines whether a workload should be reloaded based on annotations. @@ -40,32 +36,16 @@ func NewMatcher(cfg *config.Config) *Matcher { // MatchInput contains all the information needed to determine if a reload should occur. type MatchInput struct { - // ResourceName is the name of the ConfigMap or Secret that changed. - ResourceName string - // ResourceNamespace is the namespace of the ConfigMap or Secret. - ResourceNamespace string - // ResourceType is whether this is a ConfigMap or Secret. - ResourceType ResourceType - // ResourceAnnotations are the annotations on the ConfigMap or Secret. + ResourceName string + ResourceNamespace string + ResourceType ResourceType ResourceAnnotations map[string]string - // WorkloadAnnotations are the annotations on the workload (Deployment, etc.). WorkloadAnnotations map[string]string - // PodAnnotations are the annotations on the pod template. - PodAnnotations map[string]string + PodAnnotations map[string]string } // ShouldReload determines if a workload should be reloaded based on its annotations. -// -// The matching logic follows this precedence (BUG FIX: explicit annotations checked first): -// 1. If the resource has the ignore annotation, skip it -// 2. If the resource is in the exclude list for this workload, skip it -// 3. If explicit reload annotation matches the resource name, reload (not auto) -// 4. If search annotation is enabled and resource has match annotation, reload (auto) -// 5. If auto annotation is "true", reload (auto) -// 6. If typed auto annotation is "true", reload (auto) -// 7. If AutoReloadAll is enabled and no explicit "false" annotations, reload (auto) func (m *Matcher) ShouldReload(input MatchInput) MatchResult { - // Check resource-level ignore annotation if m.isResourceIgnored(input.ResourceAnnotations) { return MatchResult{ ShouldReload: false, @@ -73,10 +53,8 @@ func (m *Matcher) ShouldReload(input MatchInput) MatchResult { } } - // Determine which annotations to use (workload or pod template) annotations := m.selectAnnotations(input) - // Check if resource is excluded if m.isResourceExcluded(input.ResourceName, input.ResourceType, annotations) { return MatchResult{ ShouldReload: false, @@ -84,8 +62,6 @@ func (m *Matcher) ShouldReload(input MatchInput) MatchResult { } } - // Check explicit reload annotation (e.g., configmap.reloader.stakater.com/reload: "my-config") - // BUG FIX: Check this BEFORE auto annotations to ensure explicit references take precedence if m.matchesExplicitAnnotation(input.ResourceName, input.ResourceType, annotations) { return MatchResult{ ShouldReload: true, @@ -94,7 +70,6 @@ func (m *Matcher) ShouldReload(input MatchInput) MatchResult { } } - // Check search/match pattern if m.matchesSearchPattern(input.ResourceAnnotations, annotations) { return MatchResult{ ShouldReload: true, @@ -103,7 +78,6 @@ func (m *Matcher) ShouldReload(input MatchInput) MatchResult { } } - // Check auto annotations if m.matchesAutoAnnotation(input.ResourceType, annotations) { return MatchResult{ ShouldReload: true, @@ -112,7 +86,6 @@ func (m *Matcher) ShouldReload(input MatchInput) MatchResult { } } - // Check global auto-reload-all setting if m.matchesAutoReloadAll(input.ResourceType, annotations) { return MatchResult{ ShouldReload: true, @@ -127,7 +100,6 @@ func (m *Matcher) ShouldReload(input MatchInput) MatchResult { } } -// isResourceIgnored checks if the resource has the ignore annotation set to true. func (m *Matcher) isResourceIgnored(resourceAnnotations map[string]string) bool { if resourceAnnotations == nil { return false @@ -135,44 +107,34 @@ func (m *Matcher) isResourceIgnored(resourceAnnotations map[string]string) bool return resourceAnnotations[m.cfg.Annotations.Ignore] == "true" } -// selectAnnotations determines which set of annotations to use for matching. -// If workload annotations don't have relevant annotations, fall back to pod annotations. func (m *Matcher) selectAnnotations(input MatchInput) map[string]string { - // Check if any relevant annotation exists on workload annotations if m.hasRelevantAnnotations(input.WorkloadAnnotations, input.ResourceType) { return input.WorkloadAnnotations } - // Fall back to pod annotations if m.hasRelevantAnnotations(input.PodAnnotations, input.ResourceType) { return input.PodAnnotations } - // Default to workload annotations even if empty return input.WorkloadAnnotations } -// hasRelevantAnnotations checks if the annotations contain any reload-related annotation. func (m *Matcher) hasRelevantAnnotations(annotations map[string]string, resourceType ResourceType) bool { if annotations == nil { return false } - // Check for explicit annotation explicitAnn := m.getExplicitAnnotation(resourceType) if _, ok := annotations[explicitAnn]; ok { return true } - // Check for search annotation if _, ok := annotations[m.cfg.Annotations.Search]; ok { return true } - // Check for auto annotation if _, ok := annotations[m.cfg.Annotations.Auto]; ok { return true } - // Check for typed auto annotation typedAutoAnn := m.getTypedAutoAnnotation(resourceType) if _, ok := annotations[typedAutoAnn]; ok { return true @@ -181,7 +143,6 @@ func (m *Matcher) hasRelevantAnnotations(annotations map[string]string, resource return false } -// isResourceExcluded checks if the resource is in the exclude list. func (m *Matcher) isResourceExcluded(resourceName string, resourceType ResourceType, annotations map[string]string) bool { if annotations == nil { return false @@ -209,7 +170,6 @@ func (m *Matcher) isResourceExcluded(resourceName string, resourceType ResourceT return false } -// matchesExplicitAnnotation checks if the resource name matches the explicit reload annotation. func (m *Matcher) matchesExplicitAnnotation(resourceName string, resourceType ResourceType, annotations map[string]string) bool { if annotations == nil { return false @@ -221,16 +181,13 @@ func (m *Matcher) matchesExplicitAnnotation(resourceName string, resourceType Re return false } - // Support comma-separated list of resource names with regex matching for _, value := range strings.Split(annotationValue, ",") { value = strings.TrimSpace(value) if value == "" { continue } - // Support regex patterns re, err := regexp.Compile("^" + value + "$") if err != nil { - // If regex is invalid, fall back to exact match if value == resourceName { return true } @@ -244,7 +201,6 @@ func (m *Matcher) matchesExplicitAnnotation(resourceName string, resourceType Re return false } -// matchesSearchPattern checks if the search/match pattern is enabled. func (m *Matcher) matchesSearchPattern(resourceAnnotations, workloadAnnotations map[string]string) bool { if workloadAnnotations == nil || resourceAnnotations == nil { return false @@ -259,29 +215,24 @@ func (m *Matcher) matchesSearchPattern(resourceAnnotations, workloadAnnotations return ok && matchValue == "true" } -// matchesAutoAnnotation checks if auto reload is enabled via annotations. func (m *Matcher) matchesAutoAnnotation(resourceType ResourceType, annotations map[string]string) bool { if annotations == nil { return false } - // Check generic auto annotation if annotations[m.cfg.Annotations.Auto] == "true" { return true } - // Check typed auto annotation typedAutoAnn := m.getTypedAutoAnnotation(resourceType) return annotations[typedAutoAnn] == "true" } -// matchesAutoReloadAll checks if global auto-reload-all is enabled. func (m *Matcher) matchesAutoReloadAll(resourceType ResourceType, annotations map[string]string) bool { if !m.cfg.AutoReloadAll { return false } - // If auto annotation is explicitly set to false, don't auto-reload if annotations != nil { if annotations[m.cfg.Annotations.Auto] == "false" { return false @@ -295,7 +246,6 @@ func (m *Matcher) matchesAutoReloadAll(resourceType ResourceType, annotations ma return true } -// getExplicitAnnotation returns the explicit reload annotation for the resource type. func (m *Matcher) getExplicitAnnotation(resourceType ResourceType) string { switch resourceType { case ResourceTypeConfigMap: @@ -307,7 +257,6 @@ func (m *Matcher) getExplicitAnnotation(resourceType ResourceType) string { } } -// getTypedAutoAnnotation returns the typed auto annotation for the resource type. func (m *Matcher) getTypedAutoAnnotation(resourceType ResourceType) string { switch resourceType { case ResourceTypeConfigMap: diff --git a/internal/pkg/reload/matcher_test.go b/internal/pkg/reload/matcher_test.go index 5e4f8b8f..8595d0ab 100644 --- a/internal/pkg/reload/matcher_test.go +++ b/internal/pkg/reload/matcher_test.go @@ -92,7 +92,7 @@ func TestMatcher_ShouldReload(t *testing.T) { ResourceType: ResourceTypeConfigMap, ResourceAnnotations: nil, WorkloadAnnotations: map[string]string{ - "reloader.stakater.com/auto": "true", + "reloader.stakater.com/auto": "true", "configmap.reloader.stakater.com/reload": "external-config", }, PodAnnotations: nil, @@ -277,7 +277,7 @@ func TestMatcher_ShouldReload(t *testing.T) { ResourceType: ResourceTypeSecret, ResourceAnnotations: nil, WorkloadAnnotations: map[string]string{ - "reloader.stakater.com/auto": "true", + "reloader.stakater.com/auto": "true", "secrets.exclude.reloader.stakater.com/reload": "my-secret", }, PodAnnotations: nil, @@ -403,7 +403,7 @@ func TestMatcher_BugFix_AutoDoesNotIgnoreExplicit(t *testing.T) { ResourceType: ResourceTypeConfigMap, ResourceAnnotations: nil, WorkloadAnnotations: map[string]string{ - "reloader.stakater.com/auto": "true", // Enables auto-reload + "reloader.stakater.com/auto": "true", // Enables auto-reload "configmap.reloader.stakater.com/reload": "external-config", // Explicit list }, PodAnnotations: nil, diff --git a/internal/pkg/reload/pause_test.go b/internal/pkg/reload/pause_test.go index 9c7992d0..74e8162b 100644 --- a/internal/pkg/reload/pause_test.go +++ b/internal/pkg/reload/pause_test.go @@ -65,10 +65,10 @@ func TestPauseHandler_GetPausePeriod(t *testing.T) { handler := NewPauseHandler(cfg) tests := []struct { - name string - workload workload.WorkloadAccessor - wantPeriod time.Duration - wantErr bool + name string + workload workload.WorkloadAccessor + wantPeriod time.Duration + wantErr bool }{ { name: "valid pause period", diff --git a/internal/pkg/reload/predicate_test.go b/internal/pkg/reload/predicate_test.go index 45059002..b2cce701 100644 --- a/internal/pkg/reload/predicate_test.go +++ b/internal/pkg/reload/predicate_test.go @@ -128,10 +128,10 @@ func TestNamespaceFilterPredicate_Generic(t *testing.T) { func TestLabelSelectorPredicate_Create(t *testing.T) { tests := []struct { - name string - selector string - objectLabels map[string]string - wantAllow bool + name string + selector string + objectLabels map[string]string + wantAllow bool }{ { name: "match single label", @@ -355,38 +355,38 @@ func TestCombinedFiltering(t *testing.T) { labelPredicate := LabelSelectorPredicate(cfg) tests := []struct { - name string - namespace string - labels map[string]string - wantNSAllow bool + name string + namespace string + labels map[string]string + wantNSAllow bool wantLabelAllow bool }{ { - name: "allowed namespace and matching labels", - namespace: "default", - labels: map[string]string{"managed": "true"}, - wantNSAllow: true, + name: "allowed namespace and matching labels", + namespace: "default", + labels: map[string]string{"managed": "true"}, + wantNSAllow: true, wantLabelAllow: true, }, { - name: "allowed namespace but non-matching labels", - namespace: "default", - labels: map[string]string{"managed": "false"}, - wantNSAllow: true, + name: "allowed namespace but non-matching labels", + namespace: "default", + labels: map[string]string{"managed": "false"}, + wantNSAllow: true, wantLabelAllow: false, }, { - name: "ignored namespace with matching labels", - namespace: "kube-system", - labels: map[string]string{"managed": "true"}, - wantNSAllow: false, + name: "ignored namespace with matching labels", + namespace: "kube-system", + labels: map[string]string{"managed": "true"}, + wantNSAllow: false, wantLabelAllow: true, }, { - name: "ignored namespace and non-matching labels", - namespace: "kube-system", - labels: map[string]string{"managed": "false"}, - wantNSAllow: false, + name: "ignored namespace and non-matching labels", + namespace: "kube-system", + labels: map[string]string{"managed": "false"}, + wantNSAllow: false, wantLabelAllow: false, }, } diff --git a/internal/pkg/reload/service.go b/internal/pkg/reload/service.go index d0f681d0..6d7825fe 100644 --- a/internal/pkg/reload/service.go +++ b/internal/pkg/reload/service.go @@ -3,13 +3,11 @@ package reload import ( "context" "encoding/json" - "fmt" "time" "github.com/stakater/Reloader/internal/pkg/config" "github.com/stakater/Reloader/internal/pkg/workload" corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) // Service orchestrates the reload logic for ConfigMaps and Secrets. @@ -69,18 +67,15 @@ type ReloadDecision struct { } // ProcessConfigMap evaluates all workloads to determine which should be reloaded. -// This method does not modify any workloads - it only returns decisions. func (s *Service) ProcessConfigMap(change ConfigMapChange, workloads []workload.WorkloadAccessor) []ReloadDecision { if change.ConfigMap == nil { return nil } - // Check if we should process this event type if !s.shouldProcessEvent(change.EventType) { return nil } - // Compute hash hash := s.hasher.HashConfigMap(change.ConfigMap) if change.EventType == EventTypeDelete { hash = s.hasher.EmptyHash() @@ -97,18 +92,15 @@ func (s *Service) ProcessConfigMap(change ConfigMapChange, workloads []workload. } // ProcessSecret evaluates all workloads to determine which should be reloaded. -// This method does not modify any workloads - it only returns decisions. func (s *Service) ProcessSecret(change SecretChange, workloads []workload.WorkloadAccessor) []ReloadDecision { if change.Secret == nil { return nil } - // Check if we should process this event type if !s.shouldProcessEvent(change.EventType) { return nil } - // Compute hash hash := s.hasher.HashSecret(change.Secret) if change.EventType == EventTypeDelete { hash = s.hasher.EmptyHash() @@ -124,7 +116,6 @@ func (s *Service) ProcessSecret(change SecretChange, workloads []workload.Worklo ) } -// processResource processes a resource change against all workloads. func (s *Service) processResource( resourceName string, resourceNamespace string, @@ -136,17 +127,14 @@ func (s *Service) processResource( var decisions []ReloadDecision for _, wl := range workloads { - // Skip workloads in different namespaces if wl.GetNamespace() != resourceNamespace { continue } - // Check if workload should be ignored based on type if s.cfg.IsWorkloadIgnored(string(wl.Kind())) { continue } - // Check if workload uses this resource (via volumes or env) var usesResource bool switch resourceType { case ResourceTypeConfigMap: @@ -155,7 +143,6 @@ func (s *Service) processResource( usesResource = wl.UsesSecret(resourceName) } - // Build match input input := MatchInput{ ResourceName: resourceName, ResourceNamespace: resourceNamespace, @@ -165,11 +152,8 @@ func (s *Service) processResource( PodAnnotations: wl.GetPodTemplateAnnotations(), } - // Check if we should reload matchResult := s.matcher.ShouldReload(input) - // For auto-reload, the workload must actually use the resource - // For explicit annotation, the user explicitly requested it shouldReload := matchResult.ShouldReload if matchResult.AutoReload && !usesResource { shouldReload = false @@ -187,7 +171,6 @@ func (s *Service) processResource( return decisions } -// shouldProcessEvent checks if the event type should be processed. func (s *Service) shouldProcessEvent(eventType EventType) bool { switch eventType { case EventTypeCreate: @@ -202,8 +185,6 @@ func (s *Service) shouldProcessEvent(eventType EventType) bool { } // ApplyReload applies the reload strategy to a workload. -// This modifies the workload in-place but does not persist the changes. -// Returns true if changes were made, false otherwise. func (s *Service) ApplyReload( ctx context.Context, wl workload.WorkloadAccessor, @@ -213,7 +194,6 @@ func (s *Service) ApplyReload( hash string, autoReload bool, ) (bool, error) { - // Find the target container container := s.findTargetContainer(wl, resourceName, resourceType, autoReload) input := StrategyInput{ @@ -226,13 +206,11 @@ func (s *Service) ApplyReload( AutoReload: autoReload, } - // Apply the strategy-specific changes updated, err := s.strategy.Apply(input) if err != nil { return false, err } - // Always set the attribution annotation regardless of strategy if updated { s.setAttributionAnnotation(wl, resourceName, resourceType, namespace, hash, container) } @@ -240,8 +218,6 @@ func (s *Service) ApplyReload( return updated, nil } -// setAttributionAnnotation sets the last-reloaded-from annotation on the pod template. -// This is always set regardless of the reload strategy for audit purposes. func (s *Service) setAttributionAnnotation( wl workload.WorkloadAccessor, resourceName string, @@ -266,16 +242,12 @@ func (s *Service) setAttributionAnnotation( sourceJSON, err := json.Marshal(source) if err != nil { - // Non-fatal: skip annotation if marshaling fails return } wl.SetPodTemplateAnnotation(s.cfg.Annotations.LastReloadedFrom, string(sourceJSON)) } -// findTargetContainer finds the container to target for the reload. -// For auto-reload, it finds the container that uses the resource. -// For explicit annotation, it returns the first container. func (s *Service) findTargetContainer( wl workload.WorkloadAccessor, resourceName string, @@ -287,7 +259,6 @@ func (s *Service) findTargetContainer( return nil } - // For explicit annotation, return the first container if !autoReload { return &containers[0] } @@ -295,40 +266,31 @@ func (s *Service) findTargetContainer( volumes := wl.GetVolumes() initContainers := wl.GetInitContainers() - // For auto-reload, find the container that uses the resource - // Check volumes first volumeName := s.findVolumeUsingResource(volumes, resourceName, resourceType) if volumeName != "" { container := s.findContainerWithVolumeMount(containers, volumeName) if container != nil { return container } - // Check init containers container = s.findContainerWithVolumeMount(initContainers, volumeName) if container != nil { - // Return the first regular container for init container refs return &containers[0] } } - // Check env references container := s.findContainerWithEnvRef(containers, resourceName, resourceType) if container != nil { return container } - // Check init container env references container = s.findContainerWithEnvRef(initContainers, resourceName, resourceType) if container != nil { - // Return the first regular container for init container refs return &containers[0] } - // Default to first container return &containers[0] } -// findVolumeUsingResource finds a volume that uses the given resource. func (s *Service) findVolumeUsingResource(volumes []corev1.Volume, resourceName string, resourceType ResourceType) string { for _, vol := range volumes { switch resourceType { @@ -359,7 +321,6 @@ func (s *Service) findVolumeUsingResource(volumes []corev1.Volume, resourceName return "" } -// findContainerWithVolumeMount finds a container that mounts the given volume. func (s *Service) findContainerWithVolumeMount(containers []corev1.Container, volumeName string) *corev1.Container { for i := range containers { for _, mount := range containers[i].VolumeMounts { @@ -371,10 +332,8 @@ func (s *Service) findContainerWithVolumeMount(containers []corev1.Container, vo return nil } -// findContainerWithEnvRef finds a container that references the resource via env. func (s *Service) findContainerWithEnvRef(containers []corev1.Container, resourceName string, resourceType ResourceType) *corev1.Container { for i := range containers { - // Check env vars for _, env := range containers[i].Env { if env.ValueFrom == nil { continue @@ -391,7 +350,6 @@ func (s *Service) findContainerWithEnvRef(containers []corev1.Container, resourc } } - // Check envFrom for _, envFrom := range containers[i].EnvFrom { switch resourceType { case ResourceTypeConfigMap: @@ -412,36 +370,3 @@ func (s *Service) findContainerWithEnvRef(containers []corev1.Container, resourc func (s *Service) Hasher() *Hasher { return s.hasher } - -// Matcher returns the matcher used by this service. -func (s *Service) Matcher() *Matcher { - return s.matcher -} - -// Strategy returns the strategy used by this service. -func (s *Service) Strategy() Strategy { - return s.strategy -} - -// ListWorkloads lists all workloads in the given namespace. -// If namespace is empty, lists workloads in all namespaces. -func ListWorkloads(ctx context.Context, c client.Client, namespace string, registry *workload.Registry) ([]workload.WorkloadAccessor, error) { - var workloads []workload.WorkloadAccessor - - for _, kind := range registry.SupportedKinds() { - list, err := listWorkloadsByKind(ctx, c, namespace, kind) - if err != nil { - return nil, fmt.Errorf("listing %s: %w", kind, err) - } - workloads = append(workloads, list...) - } - - return workloads, nil -} - -// listWorkloadsByKind lists workloads of a specific kind. -func listWorkloadsByKind(ctx context.Context, c client.Client, namespace string, kind workload.Kind) ([]workload.WorkloadAccessor, error) { - // This will be implemented by the controller using the appropriate list functions - // For now, return empty slice as the controller will handle this - return nil, nil -} diff --git a/internal/pkg/reload/strategy.go b/internal/pkg/reload/strategy.go index 9a147fe2..8912e1cb 100644 --- a/internal/pkg/reload/strategy.go +++ b/internal/pkg/reload/strategy.go @@ -22,35 +22,22 @@ const ( // Strategy defines how workload restarts are triggered. type Strategy interface { - // Apply applies the reload strategy to the pod spec. - // Returns true if changes were made, false otherwise. Apply(input StrategyInput) (bool, error) - - // Name returns the strategy name for logging purposes. Name() string } // StrategyInput contains the information needed to apply a reload strategy. type StrategyInput struct { - // ResourceName is the name of the ConfigMap or Secret that changed. - ResourceName string - // ResourceType is the type of resource (configmap or secret). - ResourceType ResourceType - // Namespace is the namespace of the resource. - Namespace string - // Hash is the SHA hash of the resource content. - Hash string - // Container is the container to target for env var injection. - // If nil, the first container is used. - Container *corev1.Container - // PodAnnotations is the pod template annotations map (for annotation strategy). + ResourceName string + ResourceType ResourceType + Namespace string + Hash string + Container *corev1.Container PodAnnotations map[string]string - // AutoReload indicates if this is an auto-reload (affects container selection). - AutoReload bool + AutoReload bool } // ReloadSource contains metadata about what triggered a reload. -// This is stored in the annotation when using annotation strategy. type ReloadSource struct { Kind string `json:"kind"` Name string `json:"name"` @@ -61,7 +48,6 @@ type ReloadSource struct { } // EnvVarStrategy triggers reloads by adding/updating environment variables. -// This is the default strategy and is GitOps-friendly. type EnvVarStrategy struct{} // NewEnvVarStrategy creates a new EnvVarStrategy. @@ -69,13 +55,11 @@ func NewEnvVarStrategy() *EnvVarStrategy { return &EnvVarStrategy{} } -// Name returns the strategy name. func (s *EnvVarStrategy) Name() string { return string(config.ReloadStrategyEnvVars) } // Apply adds, updates, or removes an environment variable to trigger a restart. -// When hash is empty (resource deleted), the env var is removed. func (s *EnvVarStrategy) Apply(input StrategyInput) (bool, error) { if input.Container == nil { return false, fmt.Errorf("container is required for env-var strategy") @@ -83,25 +67,20 @@ func (s *EnvVarStrategy) Apply(input StrategyInput) (bool, error) { envVarName := s.envVarName(input.ResourceName, input.ResourceType) - // Handle deletion: remove the env var when hash is empty if input.Hash == "" { return s.removeEnvVar(input.Container, envVarName), nil } - // Check if env var already exists for i := range input.Container.Env { if input.Container.Env[i].Name == envVarName { if input.Container.Env[i].Value == input.Hash { - // Already up to date return false, nil } - // Update existing input.Container.Env[i].Value = input.Hash return true, nil } } - // Add new env var input.Container.Env = append(input.Container.Env, corev1.EnvVar{ Name: envVarName, Value: input.Hash, @@ -110,12 +89,9 @@ func (s *EnvVarStrategy) Apply(input StrategyInput) (bool, error) { return true, nil } -// removeEnvVar removes an environment variable from a container. -// Returns true if a variable was removed. func (s *EnvVarStrategy) removeEnvVar(container *corev1.Container, name string) bool { for i := range container.Env { if container.Env[i].Name == name { - // Remove by replacing with last element and truncating container.Env[i] = container.Env[len(container.Env)-1] container.Env = container.Env[:len(container.Env)-1] return true @@ -124,7 +100,6 @@ func (s *EnvVarStrategy) removeEnvVar(container *corev1.Container, name string) return false } -// envVarName generates the environment variable name for a resource. func (s *EnvVarStrategy) envVarName(resourceName string, resourceType ResourceType) string { var postfix string switch resourceType { @@ -136,8 +111,6 @@ func (s *EnvVarStrategy) envVarName(resourceName string, resourceType ResourceTy return EnvVarPrefix + convertToEnvVarName(resourceName) + "_" + postfix } -// convertToEnvVarName converts a string to a valid environment variable name. -// Invalid characters are replaced with underscores, and the result is uppercased. func convertToEnvVarName(text string) string { var buffer bytes.Buffer upper := strings.ToUpper(text) @@ -169,7 +142,6 @@ func NewAnnotationStrategy(cfg *config.Config) *AnnotationStrategy { return &AnnotationStrategy{cfg: cfg} } -// Name returns the strategy name. func (s *AnnotationStrategy) Name() string { return string(config.ReloadStrategyAnnotations) } @@ -185,7 +157,6 @@ func (s *AnnotationStrategy) Apply(input StrategyInput) (bool, error) { containerName = input.Container.Name } - // Create reload source metadata source := ReloadSource{ Kind: string(input.ResourceType), Name: input.ResourceName, @@ -204,7 +175,6 @@ func (s *AnnotationStrategy) Apply(input StrategyInput) (bool, error) { existingValue := input.PodAnnotations[annotationKey] if existingValue == string(sourceJSON) { - // Already up to date return false, nil } diff --git a/internal/pkg/testutil/kube.go b/internal/pkg/testutil/kube.go deleted file mode 100644 index 1ad43e18..00000000 --- a/internal/pkg/testutil/kube.go +++ /dev/null @@ -1,1231 +0,0 @@ -package testutil - -import ( - "context" - "encoding/json" - "fmt" - "math/rand" - "sort" - "strconv" - "strings" - "time" - - argorolloutv1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" - argorollout "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned" - openshiftv1 "github.com/openshift/api/apps/v1" - appsclient "github.com/openshift/client-go/apps/clientset/versioned" - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/callbacks" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/crypto" - "github.com/stakater/Reloader/internal/pkg/metrics" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/util" - "github.com/stakater/Reloader/pkg/common" - "github.com/stakater/Reloader/pkg/kube" - 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/client-go/kubernetes" - core_v1 "k8s.io/client-go/kubernetes/typed/core/v1" -) - -var ( - letters = []rune("abcdefghijklmnopqrstuvwxyz") - // ConfigmapResourceType is a resource type which controller watches for changes - ConfigmapResourceType = "configMaps" - // SecretResourceType is a resource type which controller watches for changes - SecretResourceType = "secrets" -) - -var ( - Clients = kube.GetClients() - Pod = "test-reloader-" + RandSeq(5) - Namespace = "test-reloader-" + RandSeq(5) - ConfigmapNamePrefix = "testconfigmap-reloader" - SecretNamePrefix = "testsecret-reloader" - Data = "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" - NewData = "dGVzdE5ld1NlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" - UpdatedData = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" - Collectors = metrics.NewCollectors() - SleepDuration = 3 * time.Second -) - -// CreateNamespace creates namespace for testing -func CreateNamespace(namespace string, client kubernetes.Interface) { - _, err := client.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}, metav1.CreateOptions{}) - if err != nil { - logrus.Fatalf("Failed to create namespace for testing %v", err) - } else { - logrus.Infof("Creating namespace for testing = %s", namespace) - } -} - -// DeleteNamespace deletes namespace for testing -func DeleteNamespace(namespace string, client kubernetes.Interface) { - err := client.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}) - if err != nil { - logrus.Fatalf("Failed to delete namespace that was created for testing %v", err) - } else { - logrus.Infof("Deleting namespace for testing = %s", namespace) - } -} - -func getObjectMeta(namespace string, name string, autoReload bool, secretAutoReload bool, configmapAutoReload bool, extraAnnotations map[string]string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - Annotations: getAnnotations(name, autoReload, secretAutoReload, configmapAutoReload, extraAnnotations), - } -} - -func getAnnotations(name string, autoReload bool, secretAutoReload bool, configmapAutoReload bool, extraAnnotations map[string]string) map[string]string { - annotations := make(map[string]string) - if autoReload { - annotations[options.ReloaderAutoAnnotation] = "true" - } - if secretAutoReload { - annotations[options.SecretReloaderAutoAnnotation] = "true" - } - if configmapAutoReload { - annotations[options.ConfigmapReloaderAutoAnnotation] = "true" - } - - if len(annotations) == 0 { - annotations = map[string]string{ - options.ConfigmapUpdateOnChangeAnnotation: name, - options.SecretUpdateOnChangeAnnotation: name} - } - for k, v := range extraAnnotations { - annotations[k] = v - } - return annotations -} - -func getEnvVarSources(name string) []v1.EnvFromSource { - return []v1.EnvFromSource{ - { - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - { - SecretRef: &v1.SecretEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - } -} - -func getVolumes(name string) []v1.Volume { - return []v1.Volume{ - { - Name: "projectedconfigmap", - VolumeSource: v1.VolumeSource{ - Projected: &v1.ProjectedVolumeSource{ - Sources: []v1.VolumeProjection{ - { - ConfigMap: &v1.ConfigMapProjection{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - }, - }, - }, - }, - { - Name: "projectedsecret", - VolumeSource: v1.VolumeSource{ - Projected: &v1.ProjectedVolumeSource{ - Sources: []v1.VolumeProjection{ - { - Secret: &v1.SecretProjection{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - }, - }, - }, - }, - { - Name: "configmap", - VolumeSource: v1.VolumeSource{ - ConfigMap: &v1.ConfigMapVolumeSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - }, - }, - }, - { - Name: "secret", - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: name, - }, - }, - }, - } -} - -func getVolumeMounts() []v1.VolumeMount { - return []v1.VolumeMount{ - { - MountPath: "etc/config", - Name: "configmap", - }, - { - MountPath: "etc/sec", - Name: "secret", - }, - { - MountPath: "etc/projectedconfig", - Name: "projectedconfigmap", - }, - { - MountPath: "etc/projectedsec", - Name: "projectedsecret", - }, - } -} - -func getPodTemplateSpecWithEnvVars(name string) v1.PodTemplateSpec { - return v1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"secondLabel": "temp"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Image: "tutum/hello-world", - Name: name, - Env: []v1.EnvVar{ - { - Name: "BUCKET_NAME", - Value: "test", - }, - { - Name: "CONFIGMAP_" + util.ConvertToEnvVarName(name), - ValueFrom: &v1.EnvVarSource{ - ConfigMapKeyRef: &v1.ConfigMapKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - Key: "test.url", - }, - }, - }, - { - Name: "SECRET_" + util.ConvertToEnvVarName(name), - ValueFrom: &v1.EnvVarSource{ - SecretKeyRef: &v1.SecretKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ - Name: name, - }, - Key: "test.url", - }, - }, - }, - }, - }, - }, - }, - } -} - -func getPodTemplateSpecWithEnvVarSources(name string) v1.PodTemplateSpec { - return v1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"secondLabel": "temp"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Image: "tutum/hello-world", - Name: name, - EnvFrom: getEnvVarSources(name), - }, - }, - }, - } -} - -func getPodTemplateSpecWithVolumes(name string) v1.PodTemplateSpec { - return v1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"secondLabel": "temp"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Image: "tutum/hello-world", - Name: name, - Env: []v1.EnvVar{ - { - Name: "BUCKET_NAME", - Value: "test", - }, - }, - VolumeMounts: getVolumeMounts(), - }, - }, - Volumes: getVolumes(name), - }, - } -} - -func getPodTemplateSpecWithInitContainer(name string) v1.PodTemplateSpec { - return v1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"secondLabel": "temp"}, - }, - Spec: v1.PodSpec{ - InitContainers: []v1.Container{ - { - Image: "busybox", - Name: "busyBox", - VolumeMounts: getVolumeMounts(), - }, - }, - Containers: []v1.Container{ - { - Image: "tutum/hello-world", - Name: name, - Env: []v1.EnvVar{ - { - Name: "BUCKET_NAME", - Value: "test", - }, - }, - }, - }, - Volumes: getVolumes(name), - }, - } -} - -func getPodTemplateSpecWithInitContainerAndEnv(name string) v1.PodTemplateSpec { - return v1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"secondLabel": "temp"}, - }, - Spec: v1.PodSpec{ - InitContainers: []v1.Container{ - { - Image: "busybox", - Name: "busyBox", - EnvFrom: getEnvVarSources(name), - }, - }, - Containers: []v1.Container{ - { - Image: "tutum/hello-world", - Name: name, - Env: []v1.EnvVar{ - { - Name: "BUCKET_NAME", - Value: "test", - }, - }, - }, - }, - }, - } -} - -// GetDeployment provides deployment for testing -func GetDeployment(namespace string, deploymentName string) *appsv1.Deployment { - replicaset := int32(1) - return &appsv1.Deployment{ - ObjectMeta: getObjectMeta(namespace, deploymentName, false, false, false, map[string]string{}), - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Strategy: appsv1.DeploymentStrategy{ - Type: appsv1.RollingUpdateDeploymentStrategyType, - }, - Template: getPodTemplateSpecWithVolumes(deploymentName), - }, - } -} - -// GetDeploymentConfig provides deployment for testing -func GetDeploymentConfig(namespace string, deploymentConfigName string) *openshiftv1.DeploymentConfig { - replicaset := int32(1) - podTemplateSpecWithVolume := getPodTemplateSpecWithVolumes(deploymentConfigName) - return &openshiftv1.DeploymentConfig{ - ObjectMeta: getObjectMeta(namespace, deploymentConfigName, false, false, false, map[string]string{}), - Spec: openshiftv1.DeploymentConfigSpec{ - Replicas: replicaset, - Strategy: openshiftv1.DeploymentStrategy{ - Type: openshiftv1.DeploymentStrategyTypeRolling, - }, - Template: &podTemplateSpecWithVolume, - }, - } -} - -// GetDeploymentWithInitContainer provides deployment with init container and volumeMounts -func GetDeploymentWithInitContainer(namespace string, deploymentName string) *appsv1.Deployment { - replicaset := int32(1) - return &appsv1.Deployment{ - ObjectMeta: getObjectMeta(namespace, deploymentName, false, false, false, map[string]string{}), - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Strategy: appsv1.DeploymentStrategy{ - Type: appsv1.RollingUpdateDeploymentStrategyType, - }, - Template: getPodTemplateSpecWithInitContainer(deploymentName), - }, - } -} - -// GetDeploymentWithInitContainerAndEnv provides deployment with init container and EnvSource -func GetDeploymentWithInitContainerAndEnv(namespace string, deploymentName string) *appsv1.Deployment { - replicaset := int32(1) - return &appsv1.Deployment{ - ObjectMeta: getObjectMeta(namespace, deploymentName, true, false, false, map[string]string{}), - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Strategy: appsv1.DeploymentStrategy{ - Type: appsv1.RollingUpdateDeploymentStrategyType, - }, - Template: getPodTemplateSpecWithInitContainerAndEnv(deploymentName), - }, - } -} - -func GetDeploymentWithEnvVars(namespace string, deploymentName string) *appsv1.Deployment { - replicaset := int32(1) - return &appsv1.Deployment{ - ObjectMeta: getObjectMeta(namespace, deploymentName, true, false, false, map[string]string{}), - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Strategy: appsv1.DeploymentStrategy{ - Type: appsv1.RollingUpdateDeploymentStrategyType, - }, - Template: getPodTemplateSpecWithEnvVars(deploymentName), - }, - } -} - -func GetDeploymentConfigWithEnvVars(namespace string, deploymentConfigName string) *openshiftv1.DeploymentConfig { - replicaset := int32(1) - podTemplateSpecWithEnvVars := getPodTemplateSpecWithEnvVars(deploymentConfigName) - return &openshiftv1.DeploymentConfig{ - ObjectMeta: getObjectMeta(namespace, deploymentConfigName, false, false, false, map[string]string{}), - Spec: openshiftv1.DeploymentConfigSpec{ - Replicas: replicaset, - Strategy: openshiftv1.DeploymentStrategy{ - Type: openshiftv1.DeploymentStrategyTypeRolling, - }, - Template: &podTemplateSpecWithEnvVars, - }, - } -} - -func GetDeploymentWithEnvVarSources(namespace string, deploymentName string) *appsv1.Deployment { - replicaset := int32(1) - return &appsv1.Deployment{ - ObjectMeta: getObjectMeta(namespace, deploymentName, true, false, false, map[string]string{}), - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Strategy: appsv1.DeploymentStrategy{ - Type: appsv1.RollingUpdateDeploymentStrategyType, - }, - Template: getPodTemplateSpecWithEnvVarSources(deploymentName), - }, - } -} - -func GetDeploymentWithPodAnnotations(namespace string, deploymentName string, both bool) *appsv1.Deployment { - replicaset := int32(1) - deployment := &appsv1.Deployment{ - ObjectMeta: getObjectMeta(namespace, deploymentName, false, false, false, map[string]string{}), - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Strategy: appsv1.DeploymentStrategy{ - Type: appsv1.RollingUpdateDeploymentStrategyType, - }, - Template: getPodTemplateSpecWithEnvVarSources(deploymentName), - }, - } - if !both { - deployment.Annotations = nil - } - deployment.Spec.Template.Annotations = getAnnotations(deploymentName, true, false, false, map[string]string{}) - return deployment -} - -func GetDeploymentWithTypedAutoAnnotation(namespace string, deploymentName string, resourceType string) *appsv1.Deployment { - replicaset := int32(1) - var objectMeta metav1.ObjectMeta - switch resourceType { - case SecretResourceType: - objectMeta = getObjectMeta(namespace, deploymentName, false, true, false, map[string]string{}) - case ConfigmapResourceType: - objectMeta = getObjectMeta(namespace, deploymentName, false, false, true, map[string]string{}) - } - - return &appsv1.Deployment{ - ObjectMeta: objectMeta, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Strategy: appsv1.DeploymentStrategy{ - Type: appsv1.RollingUpdateDeploymentStrategyType, - }, - Template: getPodTemplateSpecWithVolumes(deploymentName), - }, - } -} - -func GetDeploymentWithExcludeAnnotation(namespace string, deploymentName string, resourceType string) *appsv1.Deployment { - replicaset := int32(1) - - annotation := map[string]string{} - - switch resourceType { - case SecretResourceType: - annotation[options.SecretExcludeReloaderAnnotation] = deploymentName - case ConfigmapResourceType: - annotation[options.ConfigmapExcludeReloaderAnnotation] = deploymentName - } - - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: deploymentName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - Annotations: annotation, - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Strategy: appsv1.DeploymentStrategy{ - Type: appsv1.RollingUpdateDeploymentStrategyType, - }, - Template: getPodTemplateSpecWithVolumes(deploymentName), - }, - } -} - -// GetDaemonSet provides daemonset for testing -func GetDaemonSet(namespace string, daemonsetName string) *appsv1.DaemonSet { - return &appsv1.DaemonSet{ - ObjectMeta: getObjectMeta(namespace, daemonsetName, false, false, false, map[string]string{}), - Spec: appsv1.DaemonSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ - Type: appsv1.RollingUpdateDaemonSetStrategyType, - }, - Template: getPodTemplateSpecWithVolumes(daemonsetName), - }, - } -} - -func GetDaemonSetWithEnvVars(namespace string, daemonSetName string) *appsv1.DaemonSet { - return &appsv1.DaemonSet{ - ObjectMeta: getObjectMeta(namespace, daemonSetName, true, false, false, map[string]string{}), - Spec: appsv1.DaemonSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ - Type: appsv1.RollingUpdateDaemonSetStrategyType, - }, - Template: getPodTemplateSpecWithEnvVars(daemonSetName), - }, - } -} - -// GetStatefulSet provides statefulset for testing -func GetStatefulSet(namespace string, statefulsetName string) *appsv1.StatefulSet { - return &appsv1.StatefulSet{ - ObjectMeta: getObjectMeta(namespace, statefulsetName, false, false, false, map[string]string{}), - Spec: appsv1.StatefulSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ - Type: appsv1.RollingUpdateStatefulSetStrategyType, - }, - Template: getPodTemplateSpecWithVolumes(statefulsetName), - }, - } -} - -// GetStatefulSet provides statefulset for testing -func GetStatefulSetWithEnvVar(namespace string, statefulsetName string) *appsv1.StatefulSet { - return &appsv1.StatefulSet{ - ObjectMeta: getObjectMeta(namespace, statefulsetName, true, false, false, map[string]string{}), - Spec: appsv1.StatefulSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ - Type: appsv1.RollingUpdateStatefulSetStrategyType, - }, - Template: getPodTemplateSpecWithEnvVars(statefulsetName), - }, - } -} - -// GetConfigmap provides configmap for testing -func GetConfigmap(namespace string, configmapName string, testData string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: configmapName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - }, - Data: map[string]string{"test.url": testData}, - } -} - -// GetConfigmapWithUpdatedLabel provides configmap for testing -func GetConfigmapWithUpdatedLabel(namespace string, configmapName string, testLabel string, testData string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: configmapName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": testLabel}, - }, - Data: map[string]string{"test.url": testData}, - } -} - -// GetSecret provides secret for testing -func GetSecret(namespace string, secretName string, data string) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - }, - Data: map[string][]byte{"test.url": []byte(data)}, - } -} - -func GetCronJob(namespace string, cronJobName string) *batchv1.CronJob { - return &batchv1.CronJob{ - ObjectMeta: getObjectMeta(namespace, cronJobName, false, false, false, map[string]string{}), - Spec: batchv1.CronJobSpec{ - Schedule: "*/5 * * * *", // Run every 5 minutes - JobTemplate: batchv1.JobTemplateSpec{ - Spec: batchv1.JobSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Template: getPodTemplateSpecWithVolumes(cronJobName), - }, - }, - }, - } -} - -func GetJob(namespace string, jobName string) *batchv1.Job { - return &batchv1.Job{ - ObjectMeta: getObjectMeta(namespace, jobName, false, false, false, map[string]string{}), - Spec: batchv1.JobSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Template: getPodTemplateSpecWithVolumes(jobName), - }, - } -} - -func GetCronJobWithEnvVar(namespace string, cronJobName string) *batchv1.CronJob { - return &batchv1.CronJob{ - ObjectMeta: getObjectMeta(namespace, cronJobName, true, false, false, map[string]string{}), - Spec: batchv1.CronJobSpec{ - Schedule: "*/5 * * * *", // Run every 5 minutes - JobTemplate: batchv1.JobTemplateSpec{ - Spec: batchv1.JobSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Template: getPodTemplateSpecWithEnvVars(cronJobName), - }, - }, - }, - } -} - -func GetJobWithEnvVar(namespace string, jobName string) *batchv1.Job { - return &batchv1.Job{ - ObjectMeta: getObjectMeta(namespace, jobName, true, false, false, map[string]string{}), - Spec: batchv1.JobSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Template: getPodTemplateSpecWithEnvVars(jobName), - }, - } -} - -// GetSecretWithUpdatedLabel provides secret for testing -func GetSecretWithUpdatedLabel(namespace string, secretName string, label string, data string) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": label}, - }, - Data: map[string][]byte{"test.url": []byte(data)}, - } -} - -// GetResourceSHAFromEnvVar returns the SHA value of given environment variable -func GetResourceSHAFromEnvVar(containers []v1.Container, envVar string) string { - for i := range containers { - envs := containers[i].Env - for j := range envs { - if envs[j].Name == envVar { - return envs[j].Value - } - } - } - return "" -} - -// GetResourceSHAFromAnnotation returns the SHA value of given environment variable -func GetResourceSHAFromAnnotation(podAnnotations map[string]string) string { - lastReloadedResourceName := fmt.Sprintf("%s/%s", - constants.ReloaderAnnotationPrefix, - constants.LastReloadedFromAnnotation, - ) - - annotationJson, ok := podAnnotations[lastReloadedResourceName] - if !ok { - return "" - } - - var last common.ReloadSource - bytes := []byte(annotationJson) - err := json.Unmarshal(bytes, &last) - if err != nil { - return "" - } - - return last.Hash -} - -// ConvertResourceToSHA generates SHA from secret or configmap data -func ConvertResourceToSHA(resourceType string, namespace string, resourceName string, data string) string { - values := []string{} - switch resourceType { - case SecretResourceType: - secret := GetSecret(namespace, resourceName, data) - for k, v := range secret.Data { - values = append(values, k+"="+string(v[:])) - } - case ConfigmapResourceType: - configmap := GetConfigmap(namespace, resourceName, data) - for k, v := range configmap.Data { - values = append(values, k+"="+v) - } - } - sort.Strings(values) - return crypto.GenerateSHA(strings.Join(values, ";")) -} - -// CreateConfigMap creates a configmap in given namespace and returns the ConfigMapInterface -func CreateConfigMap(client kubernetes.Interface, namespace string, configmapName string, data string) (core_v1.ConfigMapInterface, error) { - logrus.Infof("Creating configmap") - configmapClient := client.CoreV1().ConfigMaps(namespace) - _, err := configmapClient.Create(context.TODO(), GetConfigmap(namespace, configmapName, data), metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return configmapClient, err -} - -// CreateSecret creates a secret in given namespace and returns the SecretInterface -func CreateSecret(client kubernetes.Interface, namespace string, secretName string, data string) (core_v1.SecretInterface, error) { - logrus.Infof("Creating secret") - secretClient := client.CoreV1().Secrets(namespace) - _, err := secretClient.Create(context.TODO(), GetSecret(namespace, secretName, data), metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return secretClient, err -} - -// CreateDeployment creates a deployment in given namespace and returns the Deployment -func CreateDeployment(client kubernetes.Interface, deploymentName string, namespace string, volumeMount bool) (*appsv1.Deployment, error) { - logrus.Infof("Creating Deployment") - deploymentClient := client.AppsV1().Deployments(namespace) - var deploymentObj *appsv1.Deployment - if volumeMount { - deploymentObj = GetDeployment(namespace, deploymentName) - } else { - deploymentObj = GetDeploymentWithEnvVars(namespace, deploymentName) - } - deployment, err := deploymentClient.Create(context.TODO(), deploymentObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return deployment, err -} - -// CreateDeployment creates a deployment in given namespace and returns the Deployment -func CreateDeploymentWithAnnotations(client kubernetes.Interface, deploymentName string, namespace string, additionalAnnotations map[string]string, volumeMount bool) (*appsv1.Deployment, error) { - logrus.Infof("Creating Deployment") - deploymentClient := client.AppsV1().Deployments(namespace) - var deploymentObj *appsv1.Deployment - if volumeMount { - deploymentObj = GetDeployment(namespace, deploymentName) - } else { - deploymentObj = GetDeploymentWithEnvVars(namespace, deploymentName) - } - - for annotationKey, annotationValue := range additionalAnnotations { - deploymentObj.Annotations[annotationKey] = annotationValue - } - - deployment, err := deploymentClient.Create(context.TODO(), deploymentObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return deployment, err -} - -// CreateDeploymentConfig creates a deploymentConfig in given namespace and returns the DeploymentConfig -func CreateDeploymentConfig(client appsclient.Interface, deploymentName string, namespace string, volumeMount bool) (*openshiftv1.DeploymentConfig, error) { - logrus.Infof("Creating DeploymentConfig") - deploymentConfigsClient := client.AppsV1().DeploymentConfigs(namespace) - var deploymentConfigObj *openshiftv1.DeploymentConfig - if volumeMount { - deploymentConfigObj = GetDeploymentConfig(namespace, deploymentName) - } else { - deploymentConfigObj = GetDeploymentConfigWithEnvVars(namespace, deploymentName) - } - deploymentConfig, err := deploymentConfigsClient.Create(context.TODO(), deploymentConfigObj, metav1.CreateOptions{}) - time.Sleep(5 * time.Second) - return deploymentConfig, err -} - -// CreateDeploymentWithInitContainer creates a deployment in given namespace with init container and returns the Deployment -func CreateDeploymentWithInitContainer(client kubernetes.Interface, deploymentName string, namespace string, volumeMount bool) (*appsv1.Deployment, error) { - logrus.Infof("Creating Deployment") - deploymentClient := client.AppsV1().Deployments(namespace) - var deploymentObj *appsv1.Deployment - if volumeMount { - deploymentObj = GetDeploymentWithInitContainer(namespace, deploymentName) - } else { - deploymentObj = GetDeploymentWithInitContainerAndEnv(namespace, deploymentName) - } - deployment, err := deploymentClient.Create(context.TODO(), deploymentObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return deployment, err -} - -// CreateDeploymentWithEnvVarSource creates a deployment in given namespace and returns the Deployment -func CreateDeploymentWithEnvVarSource(client kubernetes.Interface, deploymentName string, namespace string) (*appsv1.Deployment, error) { - logrus.Infof("Creating Deployment") - deploymentClient := client.AppsV1().Deployments(namespace) - deploymentObj := GetDeploymentWithEnvVarSources(namespace, deploymentName) - deployment, err := deploymentClient.Create(context.TODO(), deploymentObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return deployment, err - -} - -// CreateDeploymentWithPodAnnotations creates a deployment in given namespace and returns the Deployment -func CreateDeploymentWithPodAnnotations(client kubernetes.Interface, deploymentName string, namespace string, both bool) (*appsv1.Deployment, error) { - logrus.Infof("Creating Deployment") - deploymentClient := client.AppsV1().Deployments(namespace) - deploymentObj := GetDeploymentWithPodAnnotations(namespace, deploymentName, both) - deployment, err := deploymentClient.Create(context.TODO(), deploymentObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return deployment, err -} - -// CreateDeploymentWithEnvVarSourceAndAnnotations returns a deployment in given -// namespace with given annotations. -func CreateDeploymentWithEnvVarSourceAndAnnotations(client kubernetes.Interface, deploymentName string, namespace string, annotations map[string]string) (*appsv1.Deployment, error) { - logrus.Infof("Creating Deployment") - deploymentClient := client.AppsV1().Deployments(namespace) - deploymentObj := GetDeploymentWithEnvVarSources(namespace, deploymentName) - deploymentObj.Annotations = annotations - deployment, err := deploymentClient.Create(context.TODO(), deploymentObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return deployment, err -} - -// CreateDeploymentWithTypedAutoAnnotation creates a deployment in given namespace and returns the Deployment with typed auto annotation -func CreateDeploymentWithTypedAutoAnnotation(client kubernetes.Interface, deploymentName string, namespace string, resourceType string) (*appsv1.Deployment, error) { - logrus.Infof("Creating Deployment") - deploymentClient := client.AppsV1().Deployments(namespace) - deploymentObj := GetDeploymentWithTypedAutoAnnotation(namespace, deploymentName, resourceType) - deployment, err := deploymentClient.Create(context.TODO(), deploymentObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return deployment, err -} - -// CreateDeploymentWithExcludeAnnotation creates a deployment in given namespace and returns the Deployment with typed auto annotation -func CreateDeploymentWithExcludeAnnotation(client kubernetes.Interface, deploymentName string, namespace string, resourceType string) (*appsv1.Deployment, error) { - logrus.Infof("Creating Deployment") - deploymentClient := client.AppsV1().Deployments(namespace) - deploymentObj := GetDeploymentWithExcludeAnnotation(namespace, deploymentName, resourceType) - deployment, err := deploymentClient.Create(context.TODO(), deploymentObj, metav1.CreateOptions{}) - return deployment, err -} - -// CreateDaemonSet creates a deployment in given namespace and returns the DaemonSet -func CreateDaemonSet(client kubernetes.Interface, daemonsetName string, namespace string, volumeMount bool) (*appsv1.DaemonSet, error) { - logrus.Infof("Creating DaemonSet") - daemonsetClient := client.AppsV1().DaemonSets(namespace) - var daemonsetObj *appsv1.DaemonSet - if volumeMount { - daemonsetObj = GetDaemonSet(namespace, daemonsetName) - } else { - daemonsetObj = GetDaemonSetWithEnvVars(namespace, daemonsetName) - } - daemonset, err := daemonsetClient.Create(context.TODO(), daemonsetObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return daemonset, err -} - -// CreateStatefulSet creates a deployment in given namespace and returns the StatefulSet -func CreateStatefulSet(client kubernetes.Interface, statefulsetName string, namespace string, volumeMount bool) (*appsv1.StatefulSet, error) { - logrus.Infof("Creating StatefulSet") - statefulsetClient := client.AppsV1().StatefulSets(namespace) - var statefulsetObj *appsv1.StatefulSet - if volumeMount { - statefulsetObj = GetStatefulSet(namespace, statefulsetName) - } else { - statefulsetObj = GetStatefulSetWithEnvVar(namespace, statefulsetName) - } - statefulset, err := statefulsetClient.Create(context.TODO(), statefulsetObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return statefulset, err -} - -// CreateCronJob creates a cronjob in given namespace and returns the CronJob -func CreateCronJob(client kubernetes.Interface, cronJobName string, namespace string, volumeMount bool) (*batchv1.CronJob, error) { - logrus.Infof("Creating CronJob") - cronJobClient := client.BatchV1().CronJobs(namespace) - var cronJobObj *batchv1.CronJob - if volumeMount { - cronJobObj = GetCronJob(namespace, cronJobName) - } else { - cronJobObj = GetCronJobWithEnvVar(namespace, cronJobName) - } - cronJob, err := cronJobClient.Create(context.TODO(), cronJobObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return cronJob, err -} - -// CreateJob creates a job in given namespace and returns the Job -func CreateJob(client kubernetes.Interface, jobName string, namespace string, volumeMount bool) (*batchv1.Job, error) { - logrus.Infof("Creating Job") - jobClient := client.BatchV1().Jobs(namespace) - var jobObj *batchv1.Job - if volumeMount { - jobObj = GetJob(namespace, jobName) - } else { - jobObj = GetJobWithEnvVar(namespace, jobName) - } - job, err := jobClient.Create(context.TODO(), jobObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return job, err -} - -// DeleteDeployment creates a deployment in given namespace and returns the error if any -func DeleteDeployment(client kubernetes.Interface, namespace string, deploymentName string) error { - logrus.Infof("Deleting Deployment") - deploymentError := client.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{}) - time.Sleep(3 * time.Second) - return deploymentError -} - -// DeleteDeploymentConfig deletes a deploymentConfig in given namespace and returns the error if any -func DeleteDeploymentConfig(client appsclient.Interface, namespace string, deploymentConfigName string) error { - logrus.Infof("Deleting DeploymentConfig") - deploymentConfigError := client.AppsV1().DeploymentConfigs(namespace).Delete(context.TODO(), deploymentConfigName, metav1.DeleteOptions{}) - time.Sleep(3 * time.Second) - return deploymentConfigError -} - -// DeleteDaemonSet creates a daemonset in given namespace and returns the error if any -func DeleteDaemonSet(client kubernetes.Interface, namespace string, daemonsetName string) error { - logrus.Infof("Deleting DaemonSet %s", daemonsetName) - daemonsetError := client.AppsV1().DaemonSets(namespace).Delete(context.TODO(), daemonsetName, metav1.DeleteOptions{}) - time.Sleep(3 * time.Second) - return daemonsetError -} - -// DeleteStatefulSet creates a statefulset in given namespace and returns the error if any -func DeleteStatefulSet(client kubernetes.Interface, namespace string, statefulsetName string) error { - logrus.Infof("Deleting StatefulSet %s", statefulsetName) - statefulsetError := client.AppsV1().StatefulSets(namespace).Delete(context.TODO(), statefulsetName, metav1.DeleteOptions{}) - time.Sleep(3 * time.Second) - return statefulsetError -} - -// DeleteCronJob deletes a cronJob in given namespace and returns the error if any -func DeleteCronJob(client kubernetes.Interface, namespace string, cronJobName string) error { - logrus.Infof("Deleting CronJob %s", cronJobName) - cronJobError := client.BatchV1().CronJobs(namespace).Delete(context.TODO(), cronJobName, metav1.DeleteOptions{}) - time.Sleep(3 * time.Second) - return cronJobError -} - -// Deleteob deletes a job in given namespace and returns the error if any -func DeleteJob(client kubernetes.Interface, namespace string, jobName string) error { - logrus.Infof("Deleting Job %s", jobName) - jobError := client.BatchV1().Jobs(namespace).Delete(context.TODO(), jobName, metav1.DeleteOptions{}) - time.Sleep(3 * time.Second) - return jobError -} - -// UpdateConfigMap updates a configmap in given namespace and returns the error if any -func UpdateConfigMap(configmapClient core_v1.ConfigMapInterface, namespace string, configmapName string, label string, data string) error { - logrus.Infof("Updating configmap %q.\n", configmapName) - var configmap *v1.ConfigMap - if label != "" { - configmap = GetConfigmapWithUpdatedLabel(namespace, configmapName, label, data) - } else { - configmap = GetConfigmap(namespace, configmapName, data) - } - _, updateErr := configmapClient.Update(context.TODO(), configmap, metav1.UpdateOptions{}) - time.Sleep(3 * time.Second) - return updateErr -} - -// UpdateSecret updates a secret in given namespace and returns the error if any -func UpdateSecret(secretClient core_v1.SecretInterface, namespace string, secretName string, label string, data string) error { - logrus.Infof("Updating secret %q.\n", secretName) - var secret *v1.Secret - if label != "" { - secret = GetSecretWithUpdatedLabel(namespace, secretName, label, data) - } else { - secret = GetSecret(namespace, secretName, data) - } - _, updateErr := secretClient.Update(context.TODO(), secret, metav1.UpdateOptions{}) - time.Sleep(3 * time.Second) - return updateErr -} - -// DeleteConfigMap deletes a configmap in given namespace and returns the error if any -func DeleteConfigMap(client kubernetes.Interface, namespace string, configmapName string) error { - logrus.Infof("Deleting configmap %q.\n", configmapName) - err := client.CoreV1().ConfigMaps(namespace).Delete(context.TODO(), configmapName, metav1.DeleteOptions{}) - time.Sleep(3 * time.Second) - return err -} - -// DeleteSecret deletes a secret in given namespace and returns the error if any -func DeleteSecret(client kubernetes.Interface, namespace string, secretName string) error { - logrus.Infof("Deleting secret %q.\n", secretName) - err := client.CoreV1().Secrets(namespace).Delete(context.TODO(), secretName, metav1.DeleteOptions{}) - time.Sleep(3 * time.Second) - return err -} - -// RandSeq generates a random sequence -func RandSeq(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - return string(b) -} - -// VerifyResourceEnvVarUpdate verifies whether the rolling upgrade happened or not -func VerifyResourceEnvVarUpdate(clients kube.Clients, config common.Config, envVarPostfix string, upgradeFuncs callbacks.RollingUpgradeFuncs) bool { - items := upgradeFuncs.ItemsFunc(clients, config.Namespace) - for _, i := range items { - containers := upgradeFuncs.ContainersFunc(i) - accessor, err := meta.Accessor(i) - if err != nil { - return false - } - annotations := accessor.GetAnnotations() - // match statefulsets with the correct annotation - annotationValue := annotations[config.Annotation] - searchAnnotationValue := annotations[options.AutoSearchAnnotation] - reloaderEnabledValue := annotations[options.ReloaderAutoAnnotation] - typedAutoAnnotationEnabledValue := annotations[config.TypedAutoAnnotation] - reloaderEnabled, err := strconv.ParseBool(reloaderEnabledValue) - typedAutoAnnotationEnabled, errTyped := strconv.ParseBool(typedAutoAnnotationEnabledValue) - matches := false - if err == nil && reloaderEnabled || errTyped == nil && typedAutoAnnotationEnabled { - matches = true - } else if annotationValue != "" { - values := strings.Split(annotationValue, ",") - for _, value := range values { - value = strings.Trim(value, " ") - if value == config.ResourceName { - matches = true - break - } - } - } else if searchAnnotationValue == "true" { - if config.ResourceAnnotations[options.SearchMatchAnnotation] == "true" { - matches = true - } - } - - if matches { - envName := constants.EnvVarPrefix + util.ConvertToEnvVarName(config.ResourceName) + "_" + envVarPostfix - updated := GetResourceSHAFromEnvVar(containers, envName) - if updated == config.SHAValue { - return true - } - } - } - return false -} - -// VerifyResourceEnvVarRemoved verifies whether the rolling upgrade happened or not and all Envvars SKAKATER_name_CONFIGMAP/SECRET are removed -func VerifyResourceEnvVarRemoved(clients kube.Clients, config common.Config, envVarPostfix string, upgradeFuncs callbacks.RollingUpgradeFuncs) bool { - items := upgradeFuncs.ItemsFunc(clients, config.Namespace) - for _, i := range items { - containers := upgradeFuncs.ContainersFunc(i) - accessor, err := meta.Accessor(i) - if err != nil { - return false - } - - annotations := accessor.GetAnnotations() - // match statefulsets with the correct annotation - - annotationValue := annotations[config.Annotation] - searchAnnotationValue := annotations[options.AutoSearchAnnotation] - reloaderEnabledValue := annotations[options.ReloaderAutoAnnotation] - typedAutoAnnotationEnabledValue := annotations[config.TypedAutoAnnotation] - reloaderEnabled, err := strconv.ParseBool(reloaderEnabledValue) - typedAutoAnnotationEnabled, errTyped := strconv.ParseBool(typedAutoAnnotationEnabledValue) - - matches := false - if err == nil && reloaderEnabled || errTyped == nil && typedAutoAnnotationEnabled { - matches = true - } else if annotationValue != "" { - values := strings.Split(annotationValue, ",") - for _, value := range values { - value = strings.Trim(value, " ") - if value == config.ResourceName { - matches = true - break - } - } - } else if searchAnnotationValue == "true" { - if config.ResourceAnnotations[options.SearchMatchAnnotation] == "true" { - matches = true - } - } - - if matches { - envName := constants.EnvVarPrefix + util.ConvertToEnvVarName(config.ResourceName) + "_" + envVarPostfix - value := GetResourceSHAFromEnvVar(containers, envName) - if value == "" { - return true - } - } - } - return false -} - -// VerifyResourceAnnotationUpdate verifies whether the rolling upgrade happened or not -func VerifyResourceAnnotationUpdate(clients kube.Clients, config common.Config, upgradeFuncs callbacks.RollingUpgradeFuncs) bool { - items := upgradeFuncs.ItemsFunc(clients, config.Namespace) - for _, i := range items { - podAnnotations := upgradeFuncs.PodAnnotationsFunc(i) - accessor, err := meta.Accessor(i) - if err != nil { - return false - } - annotations := accessor.GetAnnotations() - // match statefulsets with the correct annotation - annotationValue := annotations[config.Annotation] - searchAnnotationValue := annotations[options.AutoSearchAnnotation] - reloaderEnabledValue := annotations[options.ReloaderAutoAnnotation] - typedAutoAnnotationEnabledValue := annotations[config.TypedAutoAnnotation] - reloaderEnabled, _ := strconv.ParseBool(reloaderEnabledValue) - typedAutoAnnotationEnabled, _ := strconv.ParseBool(typedAutoAnnotationEnabledValue) - matches := false - if reloaderEnabled || typedAutoAnnotationEnabled || reloaderEnabledValue == "" && typedAutoAnnotationEnabledValue == "" && options.AutoReloadAll { - matches = true - } else if annotationValue != "" { - values := strings.Split(annotationValue, ",") - for _, value := range values { - value = strings.Trim(value, " ") - if value == config.ResourceName { - matches = true - break - } - } - } else if searchAnnotationValue == "true" { - if config.ResourceAnnotations[options.SearchMatchAnnotation] == "true" { - matches = true - } - } - - if matches { - updated := GetResourceSHAFromAnnotation(podAnnotations) - if updated == config.SHAValue { - return true - } - } - } - return false -} - -func GetSHAfromEmptyData() string { - return crypto.GenerateSHA("") -} - -// GetRollout provides rollout for testing -func GetRollout(namespace string, rolloutName string, annotations map[string]string) *argorolloutv1alpha1.Rollout { - replicaset := int32(1) - return &argorolloutv1alpha1.Rollout{ - ObjectMeta: getObjectMeta(namespace, rolloutName, false, false, false, annotations), - Spec: argorolloutv1alpha1.RolloutSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"secondLabel": "temp"}, - }, - Replicas: &replicaset, - Template: getPodTemplateSpecWithVolumes(rolloutName), - }, - } -} - -// CreateRollout creates a rolout in given namespace and returns the Rollout -func CreateRollout(client argorollout.Interface, rolloutName string, namespace string, annotations map[string]string) (*argorolloutv1alpha1.Rollout, error) { - logrus.Infof("Creating Rollout") - rolloutClient := client.ArgoprojV1alpha1().Rollouts(namespace) - rolloutObj := GetRollout(namespace, rolloutName, annotations) - rollout, err := rolloutClient.Create(context.TODO(), rolloutObj, metav1.CreateOptions{}) - time.Sleep(3 * time.Second) - return rollout, err -} diff --git a/internal/pkg/util/interface.go b/internal/pkg/util/interface.go deleted file mode 100644 index ff261ab0..00000000 --- a/internal/pkg/util/interface.go +++ /dev/null @@ -1,50 +0,0 @@ -package util - -import ( - "reflect" - "strconv" - - "github.com/sirupsen/logrus" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// InterfaceSlice converts an interface to an interface array -func InterfaceSlice(slice interface{}) []interface{} { - s := reflect.ValueOf(slice) - if s.Kind() != reflect.Slice { - logrus.Errorf("InterfaceSlice() given a non-slice type") - } - - ret := make([]interface{}, s.Len()) - - for i := 0; i < s.Len(); i++ { - ret[i] = s.Index(i).Interface() - } - - return ret -} - -type ObjectMeta struct { - metav1.ObjectMeta -} - -func ToObjectMeta(kubernetesObject interface{}) ObjectMeta { - objectValue := reflect.ValueOf(kubernetesObject) - fieldName := reflect.TypeOf((*metav1.ObjectMeta)(nil)).Elem().Name() - field := objectValue.FieldByName(fieldName).Interface().(metav1.ObjectMeta) - - return ObjectMeta{ - ObjectMeta: field, - } -} - -// ParseBool returns result in bool format after parsing -func ParseBool(value interface{}) bool { - if reflect.Bool == reflect.TypeOf(value).Kind() { - return value.(bool) - } else if reflect.String == reflect.TypeOf(value).Kind() { - result, _ := strconv.ParseBool(value.(string)) - return result - } - return false -} diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go deleted file mode 100644 index ec86d1c9..00000000 --- a/internal/pkg/util/util.go +++ /dev/null @@ -1,128 +0,0 @@ -package util - -import ( - "bytes" - "encoding/base64" - "errors" - "fmt" - "sort" - "strings" - - "github.com/spf13/cobra" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/crypto" - "github.com/stakater/Reloader/internal/pkg/options" - v1 "k8s.io/api/core/v1" -) - -// ConvertToEnvVarName converts the given text into a usable env var -// removing any special chars with '_' and transforming text to upper case -func ConvertToEnvVarName(text string) string { - var buffer bytes.Buffer - upper := strings.ToUpper(text) - lastCharValid := false - for i := 0; i < len(upper); i++ { - ch := upper[i] - if (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') { - buffer.WriteString(string(ch)) - lastCharValid = true - } else { - if lastCharValid { - buffer.WriteString("_") - } - lastCharValid = false - } - } - return buffer.String() -} - -func GetSHAfromConfigmap(configmap *v1.ConfigMap) string { - values := []string{} - for k, v := range configmap.Data { - values = append(values, k+"="+v) - } - for k, v := range configmap.BinaryData { - values = append(values, k+"="+base64.StdEncoding.EncodeToString(v)) - } - sort.Strings(values) - return crypto.GenerateSHA(strings.Join(values, ";")) -} - -func GetSHAfromSecret(data map[string][]byte) string { - values := []string{} - for k, v := range data { - values = append(values, k+"="+string(v[:])) - } - sort.Strings(values) - return crypto.GenerateSHA(strings.Join(values, ";")) -} - -type List []string - -func (l *List) Contains(s string) bool { - for _, v := range *l { - if v == s { - return true - } - } - return false -} - -func ConfigureReloaderFlags(cmd *cobra.Command) { - cmd.PersistentFlags().BoolVar(&options.AutoReloadAll, "auto-reload-all", false, "Auto reload all resources") - cmd.PersistentFlags().StringVar(&options.ConfigmapUpdateOnChangeAnnotation, "configmap-annotation", "configmap.reloader.stakater.com/reload", "annotation to detect changes in configmaps, specified by name") - cmd.PersistentFlags().StringVar(&options.SecretUpdateOnChangeAnnotation, "secret-annotation", "secret.reloader.stakater.com/reload", "annotation to detect changes in secrets, specified by name") - cmd.PersistentFlags().StringVar(&options.ReloaderAutoAnnotation, "auto-annotation", "reloader.stakater.com/auto", "annotation to detect changes in secrets/configmaps") - cmd.PersistentFlags().StringVar(&options.ConfigmapReloaderAutoAnnotation, "configmap-auto-annotation", "configmap.reloader.stakater.com/auto", "annotation to detect changes in configmaps") - cmd.PersistentFlags().StringVar(&options.SecretReloaderAutoAnnotation, "secret-auto-annotation", "secret.reloader.stakater.com/auto", "annotation to detect changes in secrets") - cmd.PersistentFlags().StringVar(&options.AutoSearchAnnotation, "auto-search-annotation", "reloader.stakater.com/search", "annotation to detect changes in configmaps or secrets tagged with special match annotation") - cmd.PersistentFlags().StringVar(&options.SearchMatchAnnotation, "search-match-annotation", "reloader.stakater.com/match", "annotation to mark secrets or configmaps to match the search") - cmd.PersistentFlags().StringVar(&options.PauseDeploymentAnnotation, "pause-deployment-annotation", "deployment.reloader.stakater.com/pause-period", "annotation to define the time period to pause a deployment after a configmap/secret change has been detected") - cmd.PersistentFlags().StringVar(&options.PauseDeploymentTimeAnnotation, "pause-deployment-time-annotation", "deployment.reloader.stakater.com/paused-at", "annotation to indicate when a deployment was paused by Reloader") - cmd.PersistentFlags().StringVar(&options.LogFormat, "log-format", "", "Log format to use (empty string for text, or JSON)") - cmd.PersistentFlags().StringVar(&options.LogLevel, "log-level", "info", "Log level to use (trace, debug, info, warning, error, fatal and panic)") - cmd.PersistentFlags().StringVar(&options.WebhookUrl, "webhook-url", "", "webhook to trigger instead of performing a reload") - cmd.PersistentFlags().StringSliceVar(&options.ResourcesToIgnore, "resources-to-ignore", options.ResourcesToIgnore, "list of resources to ignore (valid options 'configMaps' or 'secrets')") - cmd.PersistentFlags().StringSliceVar(&options.WorkloadTypesToIgnore, "ignored-workload-types", options.WorkloadTypesToIgnore, "list of workload types to ignore (valid options: 'jobs', 'cronjobs', or both)") - cmd.PersistentFlags().StringSliceVar(&options.NamespacesToIgnore, "namespaces-to-ignore", options.NamespacesToIgnore, "list of namespaces to ignore") - cmd.PersistentFlags().StringSliceVar(&options.NamespaceSelectors, "namespace-selector", options.NamespaceSelectors, "list of key:value labels to filter on for namespaces") - cmd.PersistentFlags().StringSliceVar(&options.ResourceSelectors, "resource-label-selector", options.ResourceSelectors, "list of key:value labels to filter on for configmaps and secrets") - cmd.PersistentFlags().StringVar(&options.IsArgoRollouts, "is-Argo-Rollouts", "false", "Add support for argo rollouts") - cmd.PersistentFlags().StringVar(&options.ReloadStrategy, constants.ReloadStrategyFlag, constants.EnvVarsReloadStrategy, "Specifies the desired reload strategy") - cmd.PersistentFlags().StringVar(&options.ReloadOnCreate, "reload-on-create", "false", "Add support to watch create events") - cmd.PersistentFlags().StringVar(&options.ReloadOnDelete, "reload-on-delete", "false", "Add support to watch delete events") - cmd.PersistentFlags().BoolVar(&options.EnableHA, "enable-ha", false, "Adds support for running multiple replicas via leadership election") - cmd.PersistentFlags().BoolVar(&options.SyncAfterRestart, "sync-after-restart", false, "Sync add events after reloader restarts") - cmd.PersistentFlags().BoolVar(&options.EnablePProf, "enable-pprof", false, "Enable pprof for profiling") - cmd.PersistentFlags().StringVar(&options.PProfAddr, "pprof-addr", ":6060", "Address to start pprof server on. Default is :6060") -} - -func GetIgnoredResourcesList() (List, error) { - - ignoredResourcesList := options.ResourcesToIgnore // getStringSliceFromFlags(cmd, "resources-to-ignore") - - for _, v := range ignoredResourcesList { - if v != "configMaps" && v != "secrets" { - return nil, fmt.Errorf("'resources-to-ignore' only accepts 'configMaps' or 'secrets', not '%s'", v) - } - } - - if len(ignoredResourcesList) > 1 { - return nil, errors.New("'resources-to-ignore' only accepts 'configMaps' or 'secrets', not both") - } - - return ignoredResourcesList, nil -} - -func GetIgnoredWorkloadTypesList() (List, error) { - - ignoredWorkloadTypesList := options.WorkloadTypesToIgnore - - for _, v := range ignoredWorkloadTypesList { - if v != "jobs" && v != "cronjobs" { - return nil, fmt.Errorf("'ignored-workload-types' accepts 'jobs', 'cronjobs', or both, not '%s'", v) - } - } - - return ignoredWorkloadTypesList, nil -} diff --git a/internal/pkg/util/util_test.go b/internal/pkg/util/util_test.go deleted file mode 100644 index 338f329f..00000000 --- a/internal/pkg/util/util_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package util - -import ( - "testing" - - "github.com/stakater/Reloader/internal/pkg/options" - v1 "k8s.io/api/core/v1" -) - -func TestConvertToEnvVarName(t *testing.T) { - data := "www.stakater.com" - envVar := ConvertToEnvVarName(data) - if envVar != "WWW_STAKATER_COM" { - t.Errorf("Failed to convert data into environment variable") - } -} - -func TestGetHashFromConfigMap(t *testing.T) { - data := map[*v1.ConfigMap]string{ - { - Data: map[string]string{"test": "test"}, - }: "Only Data", - { - Data: map[string]string{"test": "test"}, - BinaryData: map[string][]byte{"bintest": []byte("test")}, - }: "Both Data and BinaryData", - { - BinaryData: map[string][]byte{"bintest": []byte("test")}, - }: "Only BinaryData", - } - converted := map[string]string{} - for cm, cmName := range data { - converted[cmName] = GetSHAfromConfigmap(cm) - } - - // Test that the has for each configmap is really unique - for cmName, cmHash := range converted { - count := 0 - for _, cmHash2 := range converted { - if cmHash == cmHash2 { - count++ - } - } - if count > 1 { - t.Errorf("Found duplicate hashes for %v", cmName) - } - } -} - -func TestGetIgnoredWorkloadTypesList(t *testing.T) { - // Save original state - originalWorkloadTypes := options.WorkloadTypesToIgnore - defer func() { - options.WorkloadTypesToIgnore = originalWorkloadTypes - }() - - tests := []struct { - name string - workloadTypes []string - expectError bool - expected []string - }{ - { - name: "Both jobs and cronjobs", - workloadTypes: []string{"jobs", "cronjobs"}, - expectError: false, - expected: []string{"jobs", "cronjobs"}, - }, - { - name: "Only jobs", - workloadTypes: []string{"jobs"}, - expectError: false, - expected: []string{"jobs"}, - }, - { - name: "Only cronjobs", - workloadTypes: []string{"cronjobs"}, - expectError: false, - expected: []string{"cronjobs"}, - }, - { - name: "Empty list", - workloadTypes: []string{}, - expectError: false, - expected: []string{}, - }, - { - name: "Invalid workload type", - workloadTypes: []string{"invalid"}, - expectError: true, - expected: nil, - }, - { - name: "Mixed valid and invalid", - workloadTypes: []string{"jobs", "invalid"}, - expectError: true, - expected: nil, - }, - { - name: "Duplicate values", - workloadTypes: []string{"jobs", "jobs"}, - expectError: false, - expected: []string{"jobs", "jobs"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Set the global option - options.WorkloadTypesToIgnore = tt.workloadTypes - - result, err := GetIgnoredWorkloadTypesList() - - if tt.expectError && err == nil { - t.Errorf("Expected error but got none") - } - - if !tt.expectError && err != nil { - t.Errorf("Expected no error but got: %v", err) - } - - if !tt.expectError { - if len(result) != len(tt.expected) { - t.Errorf("Expected %v, got %v", tt.expected, result) - return - } - - for i, expected := range tt.expected { - if i >= len(result) || result[i] != expected { - t.Errorf("Expected %v, got %v", tt.expected, result) - break - } - } - } - }) - } -} - -func TestListContains(t *testing.T) { - tests := []struct { - name string - list List - item string - expected bool - }{ - { - name: "List contains item", - list: List{"jobs", "cronjobs"}, - item: "jobs", - expected: true, - }, - { - name: "List does not contain item", - list: List{"jobs"}, - item: "cronjobs", - expected: false, - }, - { - name: "Empty list", - list: List{}, - item: "jobs", - expected: false, - }, - { - name: "Case sensitive matching", - list: List{"jobs", "cronjobs"}, - item: "Jobs", - expected: false, - }, - { - name: "Multiple occurrences", - list: List{"jobs", "jobs", "cronjobs"}, - item: "jobs", - expected: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := tt.list.Contains(tt.item) - if result != tt.expected { - t.Errorf("Expected %v, got %v", tt.expected, result) - } - }) - } -} diff --git a/main.go b/main.go deleted file mode 100644 index 1c429710..00000000 --- a/main.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "os" - - "github.com/stakater/Reloader/internal/pkg/app" -) - -func main() { - if err := app.Run(); err != nil { - os.Exit(1) - } - os.Exit(0) -} diff --git a/pkg/common/common.go b/pkg/common/common.go deleted file mode 100644 index 84d98274..00000000 --- a/pkg/common/common.go +++ /dev/null @@ -1,358 +0,0 @@ -package common - -import ( - "context" - "os" - "regexp" - "strconv" - "strings" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/util" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/kubernetes" -) - -type Map map[string]string - -type ReloadCheckResult struct { - ShouldReload bool - AutoReload bool -} - -// ReloaderOptions contains all configurable options for the Reloader controller. -// These options control how Reloader behaves when watching for changes in ConfigMaps and Secrets. -type ReloaderOptions struct { - // AutoReloadAll enables automatic reloading of all resources when their corresponding ConfigMaps/Secrets are updated - AutoReloadAll bool `json:"autoReloadAll"` - // ConfigmapUpdateOnChangeAnnotation is the annotation key used to detect changes in ConfigMaps specified by name - ConfigmapUpdateOnChangeAnnotation string `json:"configmapUpdateOnChangeAnnotation"` - // SecretUpdateOnChangeAnnotation is the annotation key used to detect changes in Secrets specified by name - SecretUpdateOnChangeAnnotation string `json:"secretUpdateOnChangeAnnotation"` - // ReloaderAutoAnnotation is the annotation key used to detect changes in any referenced ConfigMaps or Secrets - ReloaderAutoAnnotation string `json:"reloaderAutoAnnotation"` - // IgnoreResourceAnnotation is the annotation key used to ignore resources from being watched - IgnoreResourceAnnotation string `json:"ignoreResourceAnnotation"` - // ConfigmapReloaderAutoAnnotation is the annotation key used to detect changes in ConfigMaps only - ConfigmapReloaderAutoAnnotation string `json:"configmapReloaderAutoAnnotation"` - // SecretReloaderAutoAnnotation is the annotation key used to detect changes in Secrets only - SecretReloaderAutoAnnotation string `json:"secretReloaderAutoAnnotation"` - // ConfigmapExcludeReloaderAnnotation is the annotation key containing comma-separated list of ConfigMaps to exclude from watching - ConfigmapExcludeReloaderAnnotation string `json:"configmapExcludeReloaderAnnotation"` - // SecretExcludeReloaderAnnotation is the annotation key containing comma-separated list of Secrets to exclude from watching - SecretExcludeReloaderAnnotation string `json:"secretExcludeReloaderAnnotation"` - // AutoSearchAnnotation is the annotation key used to detect changes in ConfigMaps/Secrets tagged with SearchMatchAnnotation - AutoSearchAnnotation string `json:"autoSearchAnnotation"` - // SearchMatchAnnotation is the annotation key used to tag ConfigMaps/Secrets to be found by AutoSearchAnnotation - SearchMatchAnnotation string `json:"searchMatchAnnotation"` - // RolloutStrategyAnnotation is the annotation key used to define the rollout update strategy for workloads - RolloutStrategyAnnotation string `json:"rolloutStrategyAnnotation"` - // PauseDeploymentAnnotation is the annotation key used to define the time period to pause a deployment after - PauseDeploymentAnnotation string `json:"pauseDeploymentAnnotation"` - // PauseDeploymentTimeAnnotation is the annotation key used to indicate when a deployment was paused by Reloader - PauseDeploymentTimeAnnotation string `json:"pauseDeploymentTimeAnnotation"` - - // LogFormat specifies the log format to use (json, or empty string for default text format) - LogFormat string `json:"logFormat"` - // LogLevel specifies the log level to use (trace, debug, info, warning, error, fatal, panic) - LogLevel string `json:"logLevel"` - // IsArgoRollouts indicates whether support for Argo Rollouts is enabled - IsArgoRollouts bool `json:"isArgoRollouts"` - // ReloadStrategy specifies the strategy used to trigger resource reloads (env-vars or annotations) - ReloadStrategy string `json:"reloadStrategy"` - // ReloadOnCreate indicates whether to trigger reloads when ConfigMaps/Secrets are created - ReloadOnCreate bool `json:"reloadOnCreate"` - // ReloadOnDelete indicates whether to trigger reloads when ConfigMaps/Secrets are deleted - ReloadOnDelete bool `json:"reloadOnDelete"` - // SyncAfterRestart indicates whether to sync add events after Reloader restarts (only works when ReloadOnCreate is true) - SyncAfterRestart bool `json:"syncAfterRestart"` - // EnableHA indicates whether High Availability mode is enabled with leader election - EnableHA bool `json:"enableHA"` - // WebhookUrl is the URL to send webhook notifications to instead of performing reloads - WebhookUrl string `json:"webhookUrl"` - // ResourcesToIgnore is a list of resource types to ignore (e.g., "configmaps" or "secrets") - ResourcesToIgnore []string `json:"resourcesToIgnore"` - // WorkloadTypesToIgnore is a list of workload types to ignore (e.g., "jobs" or "cronjobs") - WorkloadTypesToIgnore []string `json:"workloadTypesToIgnore"` - // NamespaceSelectors is a list of label selectors to filter namespaces to watch - NamespaceSelectors []string `json:"namespaceSelectors"` - // ResourceSelectors is a list of label selectors to filter ConfigMaps and Secrets to watch - ResourceSelectors []string `json:"resourceSelectors"` - // NamespacesToIgnore is a list of namespace names to ignore when watching for changes - NamespacesToIgnore []string `json:"namespacesToIgnore"` - // EnablePProf enables pprof for profiling - EnablePProf bool `json:"enablePProf"` - // PProfAddr is the address to start pprof server on - PProfAddr string `json:"pprofAddr"` -} - -var CommandLineOptions *ReloaderOptions - -func PublishMetaInfoConfigmap(clientset kubernetes.Interface) { - namespace := os.Getenv("RELOADER_NAMESPACE") - if namespace == "" { - logrus.Warn("RELOADER_NAMESPACE is not set, skipping meta info configmap creation") - return - } - - metaInfo := &MetaInfo{ - BuildInfo: *NewBuildInfo(), - ReloaderOptions: *GetCommandLineOptions(), - DeploymentInfo: metav1.ObjectMeta{ - Name: os.Getenv("RELOADER_DEPLOYMENT_NAME"), - Namespace: namespace, - }, - } - - configMap := metaInfo.ToConfigMap() - - if _, err := clientset.CoreV1().ConfigMaps(namespace).Get(context.Background(), configMap.Name, metav1.GetOptions{}); err == nil { - logrus.Info("Meta info configmap already exists, updating it") - _, err = clientset.CoreV1().ConfigMaps(namespace).Update(context.Background(), configMap, metav1.UpdateOptions{}) - if err != nil { - logrus.Warn("Failed to update existing meta info configmap: ", err) - } - return - } - - _, err := clientset.CoreV1().ConfigMaps(namespace).Create(context.Background(), configMap, metav1.CreateOptions{}) - if err != nil { - logrus.Warn("Failed to create meta info configmap: ", err) - } -} - -func GetNamespaceLabelSelector(slice []string) (string, error) { - for i, kv := range slice { - // Legacy support for ":" as a delimiter and "*" for wildcard. - if strings.Contains(kv, ":") { - split := strings.Split(kv, ":") - if split[1] == "*" { - slice[i] = split[0] - } else { - slice[i] = split[0] + "=" + split[1] - } - } - // Convert wildcard to valid apimachinery operator - if strings.Contains(kv, "=") { - split := strings.Split(kv, "=") - if split[1] == "*" { - slice[i] = split[0] - } - } - } - - namespaceLabelSelector := strings.Join(slice[:], ",") - _, err := labels.Parse(namespaceLabelSelector) - if err != nil { - logrus.Fatal(err) - } - - return namespaceLabelSelector, nil -} - -func GetResourceLabelSelector(slice []string) (string, error) { - for i, kv := range slice { - // Legacy support for ":" as a delimiter and "*" for wildcard. - if strings.Contains(kv, ":") { - split := strings.Split(kv, ":") - if split[1] == "*" { - slice[i] = split[0] - } else { - slice[i] = split[0] + "=" + split[1] - } - } - // Convert wildcard to valid apimachinery operator - if strings.Contains(kv, "=") { - split := strings.Split(kv, "=") - if split[1] == "*" { - slice[i] = split[0] - } - } - } - - resourceLabelSelector := strings.Join(slice[:], ",") - _, err := labels.Parse(resourceLabelSelector) - if err != nil { - logrus.Fatal(err) - } - - return resourceLabelSelector, nil -} - -// ShouldReload checks if a resource should be reloaded based on its annotations and the provided options. -func ShouldReload(config Config, resourceType string, annotations Map, podAnnotations Map, options *ReloaderOptions) ReloadCheckResult { - - // Check if this workload type should be ignored - if len(options.WorkloadTypesToIgnore) > 0 { - ignoredWorkloadTypes, err := util.GetIgnoredWorkloadTypesList() - if err != nil { - logrus.Errorf("Failed to parse ignored workload types: %v", err) - } else { - // Map Kubernetes resource types to CLI-friendly names for comparison - var resourceToCheck string - switch resourceType { - case "Job": - resourceToCheck = "jobs" - case "CronJob": - resourceToCheck = "cronjobs" - default: - resourceToCheck = resourceType // For other types, use as-is - } - - // Check if current resource type should be ignored - if ignoredWorkloadTypes.Contains(resourceToCheck) { - return ReloadCheckResult{ - ShouldReload: false, - } - } - } - } - - ignoreResourceAnnotatonValue := config.ResourceAnnotations[options.IgnoreResourceAnnotation] - if ignoreResourceAnnotatonValue == "true" { - return ReloadCheckResult{ - ShouldReload: false, - } - } - - annotationValue, found := annotations[config.Annotation] - searchAnnotationValue, foundSearchAnn := annotations[options.AutoSearchAnnotation] - reloaderEnabledValue, foundAuto := annotations[options.ReloaderAutoAnnotation] - typedAutoAnnotationEnabledValue, foundTypedAuto := annotations[config.TypedAutoAnnotation] - excludeConfigmapAnnotationValue, foundExcludeConfigmap := annotations[options.ConfigmapExcludeReloaderAnnotation] - excludeSecretAnnotationValue, foundExcludeSecret := annotations[options.SecretExcludeReloaderAnnotation] - - if !found && !foundAuto && !foundTypedAuto && !foundSearchAnn { - annotations = podAnnotations - annotationValue = annotations[config.Annotation] - searchAnnotationValue = annotations[options.AutoSearchAnnotation] - reloaderEnabledValue = annotations[options.ReloaderAutoAnnotation] - typedAutoAnnotationEnabledValue = annotations[config.TypedAutoAnnotation] - } - - isResourceExcluded := false - - switch config.Type { - case constants.ConfigmapEnvVarPostfix: - if foundExcludeConfigmap { - isResourceExcluded = checkIfResourceIsExcluded(config.ResourceName, excludeConfigmapAnnotationValue) - } - case constants.SecretEnvVarPostfix: - if foundExcludeSecret { - isResourceExcluded = checkIfResourceIsExcluded(config.ResourceName, excludeSecretAnnotationValue) - } - } - - if isResourceExcluded { - return ReloadCheckResult{ - ShouldReload: false, - } - } - - reloaderEnabled, _ := strconv.ParseBool(reloaderEnabledValue) - typedAutoAnnotationEnabled, _ := strconv.ParseBool(typedAutoAnnotationEnabledValue) - if reloaderEnabled || typedAutoAnnotationEnabled || reloaderEnabledValue == "" && typedAutoAnnotationEnabledValue == "" && options.AutoReloadAll { - return ReloadCheckResult{ - ShouldReload: true, - AutoReload: true, - } - } - - values := strings.Split(annotationValue, ",") - for _, value := range values { - value = strings.TrimSpace(value) - re := regexp.MustCompile("^" + value + "$") - if re.Match([]byte(config.ResourceName)) { - return ReloadCheckResult{ - ShouldReload: true, - AutoReload: false, - } - } - } - - if searchAnnotationValue == "true" { - matchAnnotationValue := config.ResourceAnnotations[options.SearchMatchAnnotation] - if matchAnnotationValue == "true" { - return ReloadCheckResult{ - ShouldReload: true, - AutoReload: true, - } - } - } - - return ReloadCheckResult{ - ShouldReload: false, - } -} - -func checkIfResourceIsExcluded(resourceName, excludedResources string) bool { - if excludedResources == "" { - return false - } - - excludedResourcesList := strings.Split(excludedResources, ",") - for _, excludedResource := range excludedResourcesList { - if strings.TrimSpace(excludedResource) == resourceName { - return true - } - } - - return false -} - -func init() { - GetCommandLineOptions() -} - -func GetCommandLineOptions() *ReloaderOptions { - if CommandLineOptions == nil { - CommandLineOptions = &ReloaderOptions{} - } - - CommandLineOptions.AutoReloadAll = options.AutoReloadAll - CommandLineOptions.ConfigmapUpdateOnChangeAnnotation = options.ConfigmapUpdateOnChangeAnnotation - CommandLineOptions.SecretUpdateOnChangeAnnotation = options.SecretUpdateOnChangeAnnotation - CommandLineOptions.ReloaderAutoAnnotation = options.ReloaderAutoAnnotation - CommandLineOptions.IgnoreResourceAnnotation = options.IgnoreResourceAnnotation - CommandLineOptions.ConfigmapReloaderAutoAnnotation = options.ConfigmapReloaderAutoAnnotation - CommandLineOptions.SecretReloaderAutoAnnotation = options.SecretReloaderAutoAnnotation - CommandLineOptions.ConfigmapExcludeReloaderAnnotation = options.ConfigmapExcludeReloaderAnnotation - CommandLineOptions.SecretExcludeReloaderAnnotation = options.SecretExcludeReloaderAnnotation - CommandLineOptions.AutoSearchAnnotation = options.AutoSearchAnnotation - CommandLineOptions.SearchMatchAnnotation = options.SearchMatchAnnotation - CommandLineOptions.RolloutStrategyAnnotation = options.RolloutStrategyAnnotation - CommandLineOptions.PauseDeploymentAnnotation = options.PauseDeploymentAnnotation - CommandLineOptions.PauseDeploymentTimeAnnotation = options.PauseDeploymentTimeAnnotation - CommandLineOptions.LogFormat = options.LogFormat - CommandLineOptions.LogLevel = options.LogLevel - CommandLineOptions.ReloadStrategy = options.ReloadStrategy - CommandLineOptions.SyncAfterRestart = options.SyncAfterRestart - CommandLineOptions.EnableHA = options.EnableHA - CommandLineOptions.WebhookUrl = options.WebhookUrl - CommandLineOptions.ResourcesToIgnore = options.ResourcesToIgnore - CommandLineOptions.WorkloadTypesToIgnore = options.WorkloadTypesToIgnore - CommandLineOptions.NamespaceSelectors = options.NamespaceSelectors - CommandLineOptions.ResourceSelectors = options.ResourceSelectors - CommandLineOptions.NamespacesToIgnore = options.NamespacesToIgnore - CommandLineOptions.IsArgoRollouts = parseBool(options.IsArgoRollouts) - CommandLineOptions.ReloadOnCreate = parseBool(options.ReloadOnCreate) - CommandLineOptions.ReloadOnDelete = parseBool(options.ReloadOnDelete) - CommandLineOptions.EnablePProf = options.EnablePProf - CommandLineOptions.PProfAddr = options.PProfAddr - - return CommandLineOptions -} - -func parseBool(value string) bool { - if value == "" { - return false - } - result, err := strconv.ParseBool(value) - if err != nil { - return false // Default to false if parsing fails - } - return result -} diff --git a/pkg/common/common_test.go b/pkg/common/common_test.go deleted file mode 100644 index 532d3adf..00000000 --- a/pkg/common/common_test.go +++ /dev/null @@ -1,224 +0,0 @@ -package common - -import ( - "testing" - - "github.com/stakater/Reloader/internal/pkg/options" -) - -func TestShouldReload_IgnoredWorkloadTypes(t *testing.T) { - // Save original state - originalWorkloadTypes := options.WorkloadTypesToIgnore - defer func() { - options.WorkloadTypesToIgnore = originalWorkloadTypes - }() - - tests := []struct { - name string - ignoredWorkloadTypes []string - resourceType string - shouldReload bool - description string - }{ - { - name: "Jobs ignored - Job should not reload", - ignoredWorkloadTypes: []string{"jobs"}, - resourceType: "Job", - shouldReload: false, - description: "When jobs are ignored, Job resources should not be reloaded", - }, - { - name: "Jobs ignored - CronJob should reload", - ignoredWorkloadTypes: []string{"jobs"}, - resourceType: "CronJob", - shouldReload: true, - description: "When jobs are ignored, CronJob resources should still be processed", - }, - { - name: "CronJobs ignored - CronJob should not reload", - ignoredWorkloadTypes: []string{"cronjobs"}, - resourceType: "CronJob", - shouldReload: false, - description: "When cronjobs are ignored, CronJob resources should not be reloaded", - }, - { - name: "CronJobs ignored - Job should reload", - ignoredWorkloadTypes: []string{"cronjobs"}, - resourceType: "Job", - shouldReload: true, - description: "When cronjobs are ignored, Job resources should still be processed", - }, - { - name: "Both ignored - Job should not reload", - ignoredWorkloadTypes: []string{"jobs", "cronjobs"}, - resourceType: "Job", - shouldReload: false, - description: "When both are ignored, Job resources should not be reloaded", - }, - { - name: "Both ignored - CronJob should not reload", - ignoredWorkloadTypes: []string{"jobs", "cronjobs"}, - resourceType: "CronJob", - shouldReload: false, - description: "When both are ignored, CronJob resources should not be reloaded", - }, - { - name: "Both ignored - Deployment should reload", - ignoredWorkloadTypes: []string{"jobs", "cronjobs"}, - resourceType: "Deployment", - shouldReload: true, - description: "When both are ignored, other workload types should still be processed", - }, - { - name: "None ignored - Job should reload", - ignoredWorkloadTypes: []string{}, - resourceType: "Job", - shouldReload: true, - description: "When nothing is ignored, all workload types should be processed", - }, - { - name: "None ignored - CronJob should reload", - ignoredWorkloadTypes: []string{}, - resourceType: "CronJob", - shouldReload: true, - description: "When nothing is ignored, all workload types should be processed", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Set the ignored workload types - options.WorkloadTypesToIgnore = tt.ignoredWorkloadTypes - - // Create minimal test config and options - config := Config{ - ResourceName: "test-resource", - Annotation: "configmap.reloader.stakater.com/reload", - } - - annotations := Map{ - "configmap.reloader.stakater.com/reload": "test-config", - } - - // Create ReloaderOptions with the ignored workload types - opts := &ReloaderOptions{ - WorkloadTypesToIgnore: tt.ignoredWorkloadTypes, - AutoReloadAll: true, // Enable auto-reload to simplify test - ReloaderAutoAnnotation: "reloader.stakater.com/auto", - } - - // Call ShouldReload - result := ShouldReload(config, tt.resourceType, annotations, Map{}, opts) - - // Check the result - if result.ShouldReload != tt.shouldReload { - t.Errorf("For resource type %s with ignored types %v, expected ShouldReload=%v, got=%v", - tt.resourceType, tt.ignoredWorkloadTypes, tt.shouldReload, result.ShouldReload) - } - - t.Logf("✓ %s", tt.description) - }) - } -} - -func TestShouldReload_IgnoredWorkloadTypes_ValidationError(t *testing.T) { - // Save original state - originalWorkloadTypes := options.WorkloadTypesToIgnore - defer func() { - options.WorkloadTypesToIgnore = originalWorkloadTypes - }() - - // Test with invalid workload type - should still continue processing - options.WorkloadTypesToIgnore = []string{"invalid"} - - config := Config{ - ResourceName: "test-resource", - Annotation: "configmap.reloader.stakater.com/reload", - } - - annotations := Map{ - "configmap.reloader.stakater.com/reload": "test-config", - } - - opts := &ReloaderOptions{ - WorkloadTypesToIgnore: []string{"invalid"}, - AutoReloadAll: true, // Enable auto-reload to simplify test - ReloaderAutoAnnotation: "reloader.stakater.com/auto", - } - - // Should not panic and should continue with normal processing - result := ShouldReload(config, "Job", annotations, Map{}, opts) - - // Since validation failed, it should continue with normal processing (should reload) - if !result.ShouldReload { - t.Errorf("Expected ShouldReload=true when validation fails, got=%v", result.ShouldReload) - } -} - -// Test that validates the fix for issue #996 -func TestShouldReload_IssueRBACPermissionFixed(t *testing.T) { - // Save original state - originalWorkloadTypes := options.WorkloadTypesToIgnore - defer func() { - options.WorkloadTypesToIgnore = originalWorkloadTypes - }() - - tests := []struct { - name string - ignoredWorkloadTypes []string - resourceType string - description string - }{ - { - name: "Issue #996 - ignoreJobs prevents Job processing", - ignoredWorkloadTypes: []string{"jobs"}, - resourceType: "Job", - description: "Job resources are skipped entirely, preventing RBAC permission errors", - }, - { - name: "Issue #996 - ignoreCronJobs prevents CronJob processing", - ignoredWorkloadTypes: []string{"cronjobs"}, - resourceType: "CronJob", - description: "CronJob resources are skipped entirely, preventing RBAC permission errors", - }, - { - name: "Issue #996 - both ignored prevent both types", - ignoredWorkloadTypes: []string{"jobs", "cronjobs"}, - resourceType: "Job", - description: "Job resources are skipped entirely when both types are ignored", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Set the ignored workload types - options.WorkloadTypesToIgnore = tt.ignoredWorkloadTypes - - config := Config{ - ResourceName: "test-resource", - Annotation: "configmap.reloader.stakater.com/reload", - } - - annotations := Map{ - "configmap.reloader.stakater.com/reload": "test-config", - } - - opts := &ReloaderOptions{ - WorkloadTypesToIgnore: tt.ignoredWorkloadTypes, - AutoReloadAll: true, // Enable auto-reload to simplify test - ReloaderAutoAnnotation: "reloader.stakater.com/auto", - } - - // Call ShouldReload - result := ShouldReload(config, tt.resourceType, annotations, Map{}, opts) - - // Should not reload when workload type is ignored - if result.ShouldReload { - t.Errorf("Expected ShouldReload=false for ignored workload type %s, got=%v", - tt.resourceType, result.ShouldReload) - } - - t.Logf("✓ %s", tt.description) - }) - } -} diff --git a/pkg/common/config.go b/pkg/common/config.go deleted file mode 100644 index 4227c2bc..00000000 --- a/pkg/common/config.go +++ /dev/null @@ -1,48 +0,0 @@ -package common - -import ( - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/internal/pkg/util" - v1 "k8s.io/api/core/v1" -) - -// Config contains rolling upgrade configuration parameters -type Config struct { - Namespace string - ResourceName string - ResourceAnnotations map[string]string - Annotation string - TypedAutoAnnotation string - SHAValue string - Type string - Labels map[string]string -} - -// GetConfigmapConfig provides utility config for configmap -func GetConfigmapConfig(configmap *v1.ConfigMap) Config { - return Config{ - Namespace: configmap.Namespace, - ResourceName: configmap.Name, - ResourceAnnotations: configmap.Annotations, - Annotation: options.ConfigmapUpdateOnChangeAnnotation, - TypedAutoAnnotation: options.ConfigmapReloaderAutoAnnotation, - SHAValue: util.GetSHAfromConfigmap(configmap), - Type: constants.ConfigmapEnvVarPostfix, - Labels: configmap.Labels, - } -} - -// GetSecretConfig provides utility config for secret -func GetSecretConfig(secret *v1.Secret) Config { - return Config{ - Namespace: secret.Namespace, - ResourceName: secret.Name, - ResourceAnnotations: secret.Annotations, - Annotation: options.SecretUpdateOnChangeAnnotation, - TypedAutoAnnotation: options.SecretReloaderAutoAnnotation, - SHAValue: util.GetSHAfromSecret(secret.Data), - Type: constants.SecretEnvVarPostfix, - Labels: secret.Labels, - } -} diff --git a/pkg/common/metainfo.go b/pkg/common/metainfo.go deleted file mode 100644 index b792c529..00000000 --- a/pkg/common/metainfo.go +++ /dev/null @@ -1,129 +0,0 @@ -package common - -import ( - "encoding/json" - "fmt" - "runtime" - "time" - - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// Version, Commit, and BuildDate are set during the build process -// using the -X linker flag to inject these values into the binary. -// They provide metadata about the build version, commit hash, build date, and whether there are -// uncommitted changes in the source code at the time of build. -// This information is useful for debugging and tracking the specific build of the Reloader binary. -var Version = "dev" -var Commit = "unknown" -var BuildDate = "unknown" - -const ( - MetaInfoConfigmapName = "reloader-meta-info" - MetaInfoConfigmapLabelKey = "reloader.stakater.com/meta-info" - MetaInfoConfigmapLabelValue = "reloader-oss" -) - -// MetaInfo contains comprehensive metadata about the Reloader instance. -// This includes build information, configuration options, and deployment details. -type MetaInfo struct { - // BuildInfo contains information about the build version, commit, and compilation details - BuildInfo BuildInfo `json:"buildInfo"` - // ReloaderOptions contains all the configuration options and flags used by this Reloader instance - ReloaderOptions ReloaderOptions `json:"reloaderOptions"` - // DeploymentInfo contains metadata about the Kubernetes deployment of this Reloader instance - DeploymentInfo metav1.ObjectMeta `json:"deploymentInfo"` -} - -// BuildInfo contains information about the build and version of the Reloader binary. -// This includes Go version, release version, commit details, and build timestamp. -type BuildInfo struct { - // GoVersion is the version of Go used to compile the binary - GoVersion string `json:"goVersion"` - // ReleaseVersion is the version tag or branch of the Reloader release - ReleaseVersion string `json:"releaseVersion"` - // CommitHash is the Git commit hash of the source code used to build this binary - CommitHash string `json:"commitHash"` - // CommitTime is the timestamp of the Git commit used to build this binary - CommitTime time.Time `json:"commitTime"` -} - -func NewBuildInfo() *BuildInfo { - metaInfo := &BuildInfo{ - GoVersion: runtime.Version(), - ReleaseVersion: Version, - CommitHash: Commit, - CommitTime: ParseUTCTime(BuildDate), - } - - return metaInfo -} - -func (m *MetaInfo) ToConfigMap() *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: MetaInfoConfigmapName, - Namespace: m.DeploymentInfo.Namespace, - Labels: map[string]string{ - MetaInfoConfigmapLabelKey: MetaInfoConfigmapLabelValue, - }, - }, - Data: map[string]string{ - "buildInfo": toJson(m.BuildInfo), - "reloaderOptions": toJson(m.ReloaderOptions), - "deploymentInfo": toJson(m.DeploymentInfo), - }, - } -} - -func NewMetaInfo(configmap *v1.ConfigMap) (*MetaInfo, error) { - var buildInfo BuildInfo - if val, ok := configmap.Data["buildInfo"]; ok { - err := json.Unmarshal([]byte(val), &buildInfo) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal buildInfo: %w", err) - } - } - - var reloaderOptions ReloaderOptions - if val, ok := configmap.Data["reloaderOptions"]; ok { - err := json.Unmarshal([]byte(val), &reloaderOptions) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal reloaderOptions: %w", err) - } - } - - var deploymentInfo metav1.ObjectMeta - if val, ok := configmap.Data["deploymentInfo"]; ok { - err := json.Unmarshal([]byte(val), &deploymentInfo) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal deploymentInfo: %w", err) - } - } - - return &MetaInfo{ - BuildInfo: buildInfo, - ReloaderOptions: reloaderOptions, - DeploymentInfo: deploymentInfo, - }, nil -} - -func toJson(data interface{}) string { - jsonData, err := json.Marshal(data) - if err != nil { - return "" - } - return string(jsonData) -} - -func ParseUTCTime(value string) time.Time { - if value == "" { - return time.Time{} // Return zero time if value is empty - } - t, err := time.Parse(time.RFC3339, value) - if err != nil { - return time.Time{} // Return zero time if parsing fails - } - return t -} diff --git a/pkg/common/reload_source.go b/pkg/common/reload_source.go deleted file mode 100644 index 09382613..00000000 --- a/pkg/common/reload_source.go +++ /dev/null @@ -1,39 +0,0 @@ -package common - -import "time" - -type ReloadSource struct { - Type string `json:"type"` - Name string `json:"name"` - Namespace string `json:"namespace"` - Hash string `json:"hash"` - ContainerRefs []string `json:"containerRefs"` - ObservedAt int64 `json:"observedAt"` -} - -func NewReloadSource( - resourceName string, - resourceNamespace string, - resourceType string, - resourceHash string, - containerRefs []string, -) ReloadSource { - return ReloadSource{ - ObservedAt: time.Now().Unix(), - Name: resourceName, - Namespace: resourceNamespace, - Type: resourceType, - Hash: resourceHash, - ContainerRefs: containerRefs, - } -} - -func NewReloadSourceFromConfig(config Config, containerRefs []string) ReloadSource { - return NewReloadSource( - config.ResourceName, - config.Namespace, - config.Type, - config.SHAValue, - containerRefs, - ) -} diff --git a/pkg/kube/client.go b/pkg/kube/client.go deleted file mode 100644 index 42300639..00000000 --- a/pkg/kube/client.go +++ /dev/null @@ -1,118 +0,0 @@ -package kube - -import ( - "context" - "os" - - "k8s.io/client-go/tools/clientcmd" - - argorollout "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned" - appsclient "github.com/openshift/client-go/apps/clientset/versioned" - "github.com/sirupsen/logrus" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" -) - -// Clients struct exposes interfaces for kubernetes as well as openshift if available -type Clients struct { - KubernetesClient kubernetes.Interface - OpenshiftAppsClient appsclient.Interface - ArgoRolloutClient argorollout.Interface -} - -var ( - // IsOpenshift is true if environment is Openshift, it is false if environment is Kubernetes - IsOpenshift = isOpenshift() -) - -// GetClients returns a `Clients` object containing both openshift and kubernetes clients with an openshift identifier -func GetClients() Clients { - client, err := GetKubernetesClient() - if err != nil { - logrus.Fatalf("Unable to create Kubernetes client error = %v", err) - } - - var appsClient *appsclient.Clientset - - if IsOpenshift { - appsClient, err = GetOpenshiftAppsClient() - if err != nil { - logrus.Warnf("Unable to create Openshift Apps client error = %v", err) - } - } - - var rolloutClient *argorollout.Clientset - - rolloutClient, err = GetArgoRolloutClient() - if err != nil { - logrus.Warnf("Unable to create ArgoRollout client error = %v", err) - } - - return Clients{ - KubernetesClient: client, - OpenshiftAppsClient: appsClient, - ArgoRolloutClient: rolloutClient, - } -} - -func GetArgoRolloutClient() (*argorollout.Clientset, error) { - config, err := getConfig() - if err != nil { - return nil, err - } - return argorollout.NewForConfig(config) -} - -func isOpenshift() bool { - client, err := GetKubernetesClient() - if err != nil { - logrus.Fatalf("Unable to create Kubernetes client error = %v", err) - } - _, err = client.RESTClient().Get().AbsPath("/apis/project.openshift.io").Do(context.TODO()).Raw() - if err == nil { - logrus.Info("Environment: Openshift") - return true - } - logrus.Info("Environment: Kubernetes") - return false -} - -// GetOpenshiftAppsClient returns an Openshift Client that can query on Apps -func GetOpenshiftAppsClient() (*appsclient.Clientset, error) { - config, err := getConfig() - if err != nil { - return nil, err - } - return appsclient.NewForConfig(config) -} - -// GetKubernetesClient gets the client for k8s, if ~/.kube/config exists so get that config else incluster config -func GetKubernetesClient() (*kubernetes.Clientset, error) { - config, err := getConfig() - if err != nil { - return nil, err - } - return kubernetes.NewForConfig(config) -} - -func getConfig() (*rest.Config, error) { - var config *rest.Config - kubeconfigPath := os.Getenv("KUBECONFIG") - if kubeconfigPath == "" { - kubeconfigPath = os.Getenv("HOME") + "/.kube/config" - } - //If file exists so use that config settings - if _, err := os.Stat(kubeconfigPath); err == nil { - config, err = clientcmd.BuildConfigFromFlags("", kubeconfigPath) - if err != nil { - return nil, err - } - } else { //Use Incluster Configuration - config, err = rest.InClusterConfig() - if err != nil { - return nil, err - } - } - - return config, nil -} diff --git a/pkg/kube/resourcemapper.go b/pkg/kube/resourcemapper.go deleted file mode 100644 index 89ac2afc..00000000 --- a/pkg/kube/resourcemapper.go +++ /dev/null @@ -1,13 +0,0 @@ -package kube - -import ( - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// ResourceMap are resources from where changes are going to be detected -var ResourceMap = map[string]runtime.Object{ - "configmaps": &v1.ConfigMap{}, - "secrets": &v1.Secret{}, - "namespaces": &v1.Namespace{}, -}