diff --git a/go.mod b/go.mod index 8e506579c..ba5d9cd63 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( dario.cat/mergo v1.0.0 + github.com/cert-manager/cert-manager v1.14.2 github.com/equinor/radix-common v1.7.1 github.com/golang/mock v1.6.0 github.com/google/uuid v1.5.0 @@ -33,18 +34,18 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.20.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/swag v0.22.7 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/hashicorp/hcl v1.0.1-vault-5 // indirect + github.com/imdario/mergo v0.3.16 // indirect 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 @@ -66,10 +67,10 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sys v0.15.0 // indirect @@ -77,14 +78,15 @@ require ( golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/kube-openapi v0.0.0-20231129212854-f0671cc7e66a // indirect - k8s.io/utils v0.0.0-20231127182322-b307cd553661 // indirect + k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022 // indirect + k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect sigs.k8s.io/controller-runtime v0.16.3 // indirect + sigs.k8s.io/gateway-api v1.0.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index 33f9646c2..dff17ae85 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,11 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 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/cert-manager/cert-manager v1.14.2 h1:C/uci6yxiCRO04PWomBbSX+T4JT58FIIpDj5SZ6Ks6I= +github.com/cert-manager/cert-manager v1.14.2/go.mod h1:pik7K6jXfgh++lfVJ/i1HzEnDluSUtTVLXSHikj8Lho= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -20,16 +21,15 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= -github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= +github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8= +github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -55,10 +55,10 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= +github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 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= @@ -67,11 +67,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -113,8 +110,8 @@ github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lne github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= @@ -136,13 +133,14 @@ github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= 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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -159,8 +157,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= +golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -213,8 +211,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -223,8 +221,8 @@ google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -236,7 +234,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/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.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= @@ -249,12 +246,14 @@ k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= -k8s.io/kube-openapi v0.0.0-20231129212854-f0671cc7e66a h1:ZeIPbyHHqahGIbeyLJJjAUhnxCKqXaDY+n89Ms8szyA= -k8s.io/kube-openapi v0.0.0-20231129212854-f0671cc7e66a/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= -k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022 h1:avRdiaB03v88Mfvum2S3BBwkNuTlmuar4LlfO9Hajko= +k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022/go.mod h1:sIV51WBTkZrlGOJMCDZDA1IaPBUDTulPpD4y7oe038k= +k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= +k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs= +sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/secrets-store-csi-driver v1.4.0 h1:R9JVcKOs11fEuiOLlH1BWMeyb6WYzvElRVkq1BWJkr4= diff --git a/pipeline-runner/main.go b/pipeline-runner/main.go index ee43083d4..47faeb9d1 100755 --- a/pipeline-runner/main.go +++ b/pipeline-runner/main.go @@ -65,7 +65,7 @@ func main() { // runs os.Exit(1) if error func prepareRunner(pipelineArgs *model.PipelineArguments) (*pipe.PipelineRunner, error) { - client, radixClient, prometheusOperatorClient, secretProviderClient := utils.GetKubernetesClient() + client, radixClient, prometheusOperatorClient, secretProviderClient, _ := utils.GetKubernetesClient() pipelineDefinition, err := pipeline.GetPipelineFromName(pipelineArgs.PipelineType) if err != nil { diff --git a/pkg/apis/batch/syncer_test.go b/pkg/apis/batch/syncer_test.go index a62a4b97d..657bd02a1 100644 --- a/pkg/apis/batch/syncer_test.go +++ b/pkg/apis/batch/syncer_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + certfake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake" "github.com/equinor/radix-common/utils/numbers" "github.com/equinor/radix-common/utils/pointers" "github.com/equinor/radix-common/utils/slice" @@ -37,6 +38,7 @@ type syncerTestSuite struct { radixClient *fakeradix.Clientset kubeUtil *kube.Kube promClient *prometheusfake.Clientset + certClient *certfake.Clientset } func TestSyncerTestSuite(t *testing.T) { @@ -78,6 +80,7 @@ func (s *syncerTestSuite) SetupTest() { s.kubeClient = fake.NewSimpleClientset() s.radixClient = fakeradix.NewSimpleClientset() s.promClient = prometheusfake.NewSimpleClientset() + s.certClient = certfake.NewSimpleClientset() s.kubeUtil, _ = kube.New(s.kubeClient, s.radixClient, secretproviderfake.NewSimpleClientset()) s.T().Setenv(defaults.OperatorEnvLimitDefaultMemoryEnvironmentVariable, "1500Mi") s.T().Setenv(defaults.OperatorRollingUpdateMaxUnavailable, "25%") @@ -1052,7 +1055,7 @@ func (s *syncerTestSuite) Test_JobWithAzureSecretRefs() { s.Require().NoError(err) rd, err = s.radixClient.RadixV1().RadixDeployments(namespace).Create(context.Background(), rd, metav1.CreateOptions{}) s.Require().NoError(err) - deploySyncer := deployment.NewDeploymentSyncer(s.kubeClient, s.kubeUtil, s.radixClient, s.promClient, utils.NewRegistrationBuilder().WithName(appName).BuildRR(), rd, nil, nil, &config.Config{}) + deploySyncer := deployment.NewDeploymentSyncer(s.kubeClient, s.kubeUtil, s.radixClient, s.promClient, s.certClient, utils.NewRegistrationBuilder().WithName(appName).BuildRR(), rd, nil, nil, &config.Config{}) s.Require().NoError(deploySyncer.OnSync()) sut := s.createSyncer(batch) diff --git a/pkg/apis/deployment/deployment.go b/pkg/apis/deployment/deployment.go index 900a87b08..0e7772009 100644 --- a/pkg/apis/deployment/deployment.go +++ b/pkg/apis/deployment/deployment.go @@ -9,6 +9,7 @@ import ( "strings" "time" + certclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" "github.com/equinor/radix-common/utils/slice" "github.com/equinor/radix-operator/pkg/apis/config" "github.com/equinor/radix-operator/pkg/apis/ingress" @@ -42,6 +43,7 @@ type Deployment struct { radixclient radixclient.Interface kubeutil *kube.Kube prometheusperatorclient monitoring.Interface + certClient certclient.Interface registration *v1.RadixRegistration radixDeployment *v1.RadixDeployment auxResourceManagers []AuxiliaryResourceManager @@ -53,12 +55,13 @@ type Deployment struct { var _ DeploymentSyncerFactory = DeploymentSyncerFactoryFunc(NewDeploymentSyncer) // NewDeploymentSyncer Constructor -func NewDeploymentSyncer(kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient radixclient.Interface, prometheusperatorclient monitoring.Interface, registration *v1.RadixRegistration, radixDeployment *v1.RadixDeployment, ingressAnnotationProviders []ingress.AnnotationProvider, auxResourceManagers []AuxiliaryResourceManager, config *config.Config) DeploymentSyncer { +func NewDeploymentSyncer(kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient radixclient.Interface, prometheusperatorclient monitoring.Interface, certClient certclient.Interface, registration *v1.RadixRegistration, radixDeployment *v1.RadixDeployment, ingressAnnotationProviders []ingress.AnnotationProvider, auxResourceManagers []AuxiliaryResourceManager, config *config.Config) DeploymentSyncer { return &Deployment{ kubeclient: kubeclient, radixclient: radixclient, kubeutil: kubeutil, prometheusperatorclient: prometheusperatorclient, + certClient: certClient, registration: registration, radixDeployment: radixDeployment, auxResourceManagers: auxResourceManagers, @@ -241,6 +244,10 @@ func (deploy *Deployment) syncDeployment() error { return stderrors.Join(errs...) } + if err := deploy.syncExternalDnsResources(); err != nil { + return fmt.Errorf("failed to sync external DNS resources: %w", err) + } + if err := deploy.syncAuxiliaryResources(); err != nil { return fmt.Errorf("failed to sync auxiliary resource : %v", err) } @@ -459,10 +466,6 @@ func getLabelSelectorForComponent(component v1.RadixCommonDeployComponent) strin return fmt.Sprintf("%s=%s", kube.RadixComponentLabel, component.GetName()) } -func getLabelSelectorForExternalAlias(component v1.RadixCommonDeployComponent) string { - return fmt.Sprintf("%s=%s, %s=%s", kube.RadixComponentLabel, component.GetName(), kube.RadixExternalAliasLabel, "true") -} - func getLabelSelectorForBlobVolumeMountSecret(component v1.RadixCommonDeployComponent) string { return fmt.Sprintf("%s=%s, %s=%s", kube.RadixComponentLabel, component.GetName(), kube.RadixMountTypeLabel, string(v1.MountTypeBlob)) } diff --git a/pkg/apis/deployment/deployment_test.go b/pkg/apis/deployment/deployment_test.go index 56589cf30..6c881362e 100644 --- a/pkg/apis/deployment/deployment_test.go +++ b/pkg/apis/deployment/deployment_test.go @@ -10,6 +10,9 @@ import ( "testing" "time" + cmv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" + v1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + certfake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake" radixutils "github.com/equinor/radix-common/utils" radixmaps "github.com/equinor/radix-common/utils/maps" "github.com/equinor/radix-common/utils/pointers" @@ -32,7 +35,6 @@ import ( prometheusclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" prometheusfake "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/fake" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" autoscalingv2 "k8s.io/api/autoscaling/v2" @@ -44,13 +46,11 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/client-go/kubernetes" kubefake "k8s.io/client-go/kubernetes/fake" - kubetesting "k8s.io/client-go/testing" secretproviderfake "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned/fake" ) @@ -72,12 +72,13 @@ var testConfig = config.Config{ }, } -func setupTest(t *testing.T) (*test.Utils, *kubefake.Clientset, *kube.Kube, *radixfake.Clientset, *prometheusfake.Clientset, *secretproviderfake.Clientset) { +func setupTest(t *testing.T) (*test.Utils, *kubefake.Clientset, *kube.Kube, *radixfake.Clientset, *prometheusfake.Clientset, *secretproviderfake.Clientset, *certfake.Clientset) { // Setup kubeclient := kubefake.NewSimpleClientset() radixClient := radixfake.NewSimpleClientset() prometheusClient := prometheusfake.NewSimpleClientset() secretProviderClient := secretproviderfake.NewSimpleClientset() + certClient := certfake.NewSimpleClientset() kubeUtil, _ := kube.New( kubeclient, radixClient, @@ -86,7 +87,7 @@ func setupTest(t *testing.T) (*test.Utils, *kubefake.Clientset, *kube.Kube, *rad handlerTestUtils := test.NewTestUtils(kubeclient, radixClient, secretProviderClient) err := handlerTestUtils.CreateClusterPrerequisites(testClusterName, testEgressIps, "anysubid") require.NoError(t, err) - return &handlerTestUtils, kubeclient, kubeUtil, radixClient, prometheusClient, secretProviderClient + return &handlerTestUtils, kubeclient, kubeUtil, radixClient, prometheusClient, secretProviderClient, certClient } func teardownTest() { @@ -110,7 +111,7 @@ func TestObjectSynced_MultiComponent_ContainsAllElements(t *testing.T) { for _, componentsExist := range []bool{true, false} { testScenario := utils.TernaryString(componentsExist, "Updating deployment", "Creating deployment") - tu, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() _ = os.Setenv(defaults.ActiveClusternameEnvironmentVariable, "AnotherClusterName") @@ -145,7 +146,6 @@ func TestObjectSynced_MultiComponent_ContainsAllElements(t *testing.T) { WithPublicPort("http"). WithDNSAppAlias(true). WithEnvironmentVariable(defaults.RadixCommitHashEnvironmentVariable, commitId). - WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: "updated_some.alias.com"}, radixv1.RadixDeployExternalDNS{FQDN: "updated_another.alias.com"}, radixv1.RadixDeployExternalDNS{FQDN: "updated_external.alias.com", UseCertificateAutomation: true}). WithResource(map[string]string{ "memory": "65Mi", "cpu": "251m", @@ -167,7 +167,7 @@ func TestObjectSynced_MultiComponent_ContainsAllElements(t *testing.T) { WithPort("http", 3001). WithPublicPort("http"). WithSecrets([]string{remainingSecret, addingSecret})) - _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, existingRadixDeploymentBuilder) + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, existingRadixDeploymentBuilder) assert.NoError(t, err) } else { @@ -186,7 +186,6 @@ func TestObjectSynced_MultiComponent_ContainsAllElements(t *testing.T) { WithPublicPort("http"). WithDNSAppAlias(true). WithEnvironmentVariable(defaults.RadixCommitHashEnvironmentVariable, commitId). - WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: "some.alias.com"}, radixv1.RadixDeployExternalDNS{FQDN: "another.alias.com"}, radixv1.RadixDeployExternalDNS{FQDN: "external.alias.com", UseCertificateAutomation: true}). WithResource(map[string]string{ "memory": "64Mi", "cpu": "250m", @@ -225,7 +224,7 @@ func TestObjectSynced_MultiComponent_ContainsAllElements(t *testing.T) { WithSecrets([]string{outdatedSecret, remainingSecret})) // Test - _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, aRadixDeploymentBuilder) + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, aRadixDeploymentBuilder) assert.NoError(t, err) } @@ -378,23 +377,13 @@ func TestObjectSynced_MultiComponent_ContainsAllElements(t *testing.T) { secrets, _ := kubeclient.CoreV1().Secrets(envNamespace).List(context.TODO(), metav1.ListOptions{}) if !componentsExist { - assert.Equal(t, 5, len(secrets.Items), "Number of secrets was not according to spec") - } else { assert.Equal(t, 3, len(secrets.Items), "Number of secrets was not according to spec") + } else { + assert.Equal(t, 1, len(secrets.Items), "Number of secrets was not according to spec") } componentSecretName := utils.GetComponentSecretName(componentNameRadixQuote) assert.True(t, secretByNameExists(componentSecretName, secrets), "Component secret is not as expected") - - // Exists due to external DNS, even though this is not active cluster - if !componentsExist { - assert.True(t, secretByNameExists("some.alias.com", secrets), "TLS certificate for external alias is not properly defined") - assert.True(t, secretByNameExists("another.alias.com", secrets), "TLS certificate for second external alias is not properly defined") - } else { - assert.True(t, secretByNameExists("updated_some.alias.com", secrets), "TLS certificate for external alias is not properly defined") - assert.True(t, secretByNameExists("updated_another.alias.com", secrets), "TLS certificate for second external alias is not properly defined") - } - blobFuseSecretExists := secretByNameExists(defaults.GetBlobFuseCredsSecretName(componentNameRadixQuote, blobVolumeName), secrets) blobCsiAzureFuseSecretExists := secretByNameExists(defaults.GetCsiAzureVolumeMountCredsSecretName(componentNameRadixQuote, blobCsiAzureVolumeName), secrets) if !componentsExist { @@ -414,18 +403,16 @@ func TestObjectSynced_MultiComponent_ContainsAllElements(t *testing.T) { t.Run(fmt.Sprintf("%s: validate roles", testScenario), func(t *testing.T) { t.Parallel() roles, _ := kubeclient.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.ElementsMatch(t, []string{"radix-app-adm-radixquote", "radix-app-adm-app", "radix-app-reader-radixquote", "radix-app-reader-app"}, getRoleNames(roles)) + assert.Subset(t, getRoleNames(roles), []string{"radix-app-adm-radixquote", "radix-app-reader-radixquote"}) }) t.Run(fmt.Sprintf("%s validate rolebindings", testScenario), func(t *testing.T) { t.Parallel() rolebindings, _ := kubeclient.RbacV1().RoleBindings(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.ElementsMatch(t, []string{"radix-app-adm-radixquote", "radix-app-adm-app", "radix-app-reader-radixquote", "radix-app-reader-app"}, getRoleBindingNames(rolebindings)) + require.Subset(t, getRoleBindingNames(rolebindings), []string{"radix-app-adm-radixquote", "radix-app-reader-radixquote"}) assert.ElementsMatch(t, adminGroups, slice.Map(getRoleBindingByName("radix-app-adm-radixquote", rolebindings).Subjects, func(s rbacv1.Subject) string { return s.Name })) - assert.ElementsMatch(t, adminGroups, slice.Map(getRoleBindingByName("radix-app-adm-app", rolebindings).Subjects, func(s rbacv1.Subject) string { return s.Name })) assert.ElementsMatch(t, readerGroups, slice.Map(getRoleBindingByName("radix-app-reader-radixquote", rolebindings).Subjects, func(s rbacv1.Subject) string { return s.Name })) - assert.ElementsMatch(t, readerGroups, slice.Map(getRoleBindingByName("radix-app-reader-app", rolebindings).Subjects, func(s rbacv1.Subject) string { return s.Name })) }) t.Run(fmt.Sprintf("%s: validate networkpolicy", testScenario), func(t *testing.T) { @@ -446,7 +433,7 @@ func TestObjectSynced_MultiJob_ContainsAllElements(t *testing.T) { for _, jobsExist := range []bool{false, true} { testScenario := utils.TernaryString(jobsExist, "Updating deployment", "Creating deployment") - tu, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) os.Setenv(defaults.ActiveClusternameEnvironmentVariable, "AnotherClusterName") os.Setenv(defaults.OperatorRadixJobSchedulerEnvironmentVariable, jobSchedulerImage) @@ -495,7 +482,7 @@ func TestObjectSynced_MultiJob_ContainsAllElements(t *testing.T) { WithAlwaysPullImageOnDeploy(false), ). WithComponents() - _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, existingRadixDeploymentBuilder) + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, existingRadixDeploymentBuilder) assert.NoError(t, err) } else { @@ -543,7 +530,7 @@ func TestObjectSynced_MultiJob_ContainsAllElements(t *testing.T) { WithComponents() // Test - _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, aRadixDeploymentBuilder) + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, aRadixDeploymentBuilder) assert.NoError(t, err) } @@ -659,12 +646,12 @@ func TestObjectSynced_MultiJob_ContainsAllElements(t *testing.T) { t.Run(fmt.Sprintf("%s: validate roles", testScenario), func(t *testing.T) { t.Parallel() roles, _ := kubeclient.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.ElementsMatch(t, []string{"radix-app-adm-job", "radix-app-reader-job"}, getRoleNames(roles)) + assert.Subset(t, getRoleNames(roles), []string{"radix-app-adm-job", "radix-app-reader-job"}) }) t.Run(fmt.Sprintf("%s validate rolebindings", testScenario), func(t *testing.T) { rolebindings, _ := kubeclient.RbacV1().RoleBindings(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.ElementsMatch(t, []string{"radix-app-adm-job", "radix-app-reader-job", defaults.RadixJobSchedulerRoleName}, getRoleBindingNames(rolebindings)) + assert.Subset(t, getRoleBindingNames(rolebindings), []string{"radix-app-adm-job", "radix-app-reader-job", defaults.RadixJobSchedulerRoleName}) assert.ElementsMatch(t, adminGroups, slice.Map(getRoleBindingByName("radix-app-adm-job", rolebindings).Subjects, func(s rbacv1.Subject) string { return s.Name })) assert.ElementsMatch(t, readerGroups, slice.Map(getRoleBindingByName("radix-app-reader-job", rolebindings).Subjects, func(s rbacv1.Subject) string { return s.Name })) }) @@ -713,12 +700,12 @@ func getDeploymentsForRadixJobAux(deployments []appsv1.Deployment) []appsv1.Depl } func TestObjectSynced_MultiComponent_NonActiveCluster_ContainsOnlyClusterSpecificIngresses(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() os.Setenv(defaults.ActiveClusternameEnvironmentVariable, "AnotherClusterName") // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("edcradix"). WithEnvironment("test"). WithComponents( @@ -776,10 +763,10 @@ func TestObjectSynced_ReadOnlyFileSystem(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() envNamespace := utils.GetEnvironmentNamespace("anyapp", "test") - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("any-app"). WithEnvironment("any-env"). WithComponents( @@ -797,13 +784,13 @@ func TestObjectSynced_ReadOnlyFileSystem(t *testing.T) { } -func TestObjectSynced_MultiComponent_ActiveCluster_ContainsAllAliasesAndSupportingObjects(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) +func TestObjectSynced_MultiComponent_ActiveCluster_ContainsAllIngresses(t *testing.T) { + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("edcradix"). WithEnvironment("test"). WithJobComponents(). @@ -852,7 +839,6 @@ func TestObjectSynced_MultiComponent_ActiveCluster_ContainsAllAliasesAndSupporti assert.Equal(t, "true", externalDNS1.Labels[kube.RadixExternalAliasLabel], "Ingress should not be an external app alias") assert.Empty(t, externalDNS1.Labels[kube.RadixActiveClusterAliasLabel], "Ingress should not be an active cluster alias") assert.Equal(t, "app", externalDNS1.Labels[kube.RadixComponentLabel], "Ingress should have the corresponding component") - assert.Equal(t, map[string]string{kube.RadixExternalDNSUseCertificateAutomationAnnotation: "false"}, externalDNS1.Annotations) assert.Equal(t, "external1.alias.com", externalDNS1.Spec.Rules[0].Host, "App should have an external alias") externalDNS2 := getIngressByName("external2.alias.com", ingresses) @@ -861,7 +847,6 @@ func TestObjectSynced_MultiComponent_ActiveCluster_ContainsAllAliasesAndSupporti assert.Equal(t, "true", externalDNS2.Labels[kube.RadixExternalAliasLabel], "Ingress should not be an external app alias") assert.Empty(t, externalDNS2.Labels[kube.RadixActiveClusterAliasLabel], "Ingress should not be an active cluster alias") assert.Equal(t, "app", externalDNS2.Labels[kube.RadixComponentLabel], "Ingress should have the corresponding component") - assert.Equal(t, map[string]string{kube.RadixExternalDNSUseCertificateAutomationAnnotation: "false"}, externalDNS2.Annotations) assert.Equal(t, "external2.alias.com", externalDNS2.Spec.Rules[0].Host, "App should have an external alias") externalDNS3 := getIngressByName("external3.alias.com", ingresses) @@ -870,13 +855,6 @@ func TestObjectSynced_MultiComponent_ActiveCluster_ContainsAllAliasesAndSupporti assert.Equal(t, "true", externalDNS3.Labels[kube.RadixExternalAliasLabel], "Ingress should not be an external app alias") assert.Empty(t, externalDNS3.Labels[kube.RadixActiveClusterAliasLabel], "Ingress should not be an active cluster alias") assert.Equal(t, "app", externalDNS3.Labels[kube.RadixComponentLabel], "Ingress should have the corresponding component") - expectedAnnotations := map[string]string{ - kube.RadixExternalDNSUseCertificateAutomationAnnotation: "true", - "cert-manager.io/cluster-issuer": testConfig.CertificateAutomation.ClusterIssuer, - "cert-manager.io/duration": testConfig.CertificateAutomation.Duration.String(), - "cert-manager.io/renew-before": testConfig.CertificateAutomation.RenewBefore.String(), - } - assert.Equal(t, expectedAnnotations, externalDNS3.Annotations) assert.Equal(t, "external3.alias.com", externalDNS3.Spec.Rules[0].Host, "App should have an external alias") appActiveClusterIngress := getIngressByName("app-active-cluster-url-alias", ingresses) @@ -893,30 +871,14 @@ func TestObjectSynced_MultiComponent_ActiveCluster_ContainsAllAliasesAndSupporti assert.Equal(t, "true", quoteActiveClusterIngress.Labels[kube.RadixActiveClusterAliasLabel], "Ingress should not be an active cluster alias") assert.Equal(t, "radixquote", quoteActiveClusterIngress.Labels[kube.RadixComponentLabel], "Ingress should have the corresponding component") - roles, _ := client.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.ElementsMatch(t, []string{"radix-app-adm-app", "radix-app-reader-app"}, getRoleNames(roles)) - - appAdmAppRole := getRoleByName("radix-app-adm-app", roles) - assert.Equal(t, "secrets", appAdmAppRole.Rules[0].Resources[0], "Expected role radix-app-adm-app should be able to access secrets") - assert.ElementsMatch(t, []string{"external1.alias.com", "external2.alias.com"}, appAdmAppRole.Rules[0].ResourceNames) - - secrets, _ := client.CoreV1().Secrets(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.True(t, secretByNameExists("external1.alias.com", secrets), "TLS certificate for external alias is not properly defined") - assert.True(t, secretByNameExists("external2.alias.com", secrets), "TLS certificate for second external alias is not properly defined") - - assert.Equal(t, corev1.SecretType(corev1.SecretTypeTLS), getSecretByName("external1.alias.com", secrets).Type, "TLS certificate for external alias is not properly defined type") - assert.Equal(t, corev1.SecretType(corev1.SecretTypeTLS), getSecretByName("external2.alias.com", secrets).Type, "TLS certificate for external alias is not properly defined type") - - rolebindings, _ := client.RbacV1().RoleBindings(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.ElementsMatch(t, []string{"radix-app-adm-app", "radix-app-reader-app"}, getRoleBindingNames(rolebindings)) } func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { defer teardownTest() // Test t.Run("app with component use default SA", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithJobComponents(). WithAppName("any-other-app"). WithEnvironment("test")) @@ -930,11 +892,11 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { }) t.Run("app with component using identity use custom SA", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) appName, envName, componentName, clientId, newClientId := "any-app", "any-env", "any-component", "any-client-id", "new-client-id" // Deploy component with Azure identity must create custom SA - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents( utils.NewDeployComponentBuilder(). WithName(componentName). @@ -958,7 +920,7 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { assert.Equal(t, "true", expectedDeployments[0].Spec.Template.Labels["azure.workload.identity/use"]) // Deploy component with new Azure identity must update SA - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents( utils.NewDeployComponentBuilder(). WithName(componentName). @@ -982,7 +944,7 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { assert.Equal(t, "true", expectedDeployments[0].Spec.Template.Labels["azure.workload.identity/use"]) // Redploy component without Azure identity should delete custom SA - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents(utils.NewDeployComponentBuilder().WithName(componentName)). WithJobComponents(). WithAppName(appName). @@ -1000,14 +962,14 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { }) t.Run("app with component using identity fails if SA exist with missing is-service-account-for-component label", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) appName, envName, componentName, clientId := "any-app", "any-env", "any-component", "any-client-id" _, err := client.CoreV1().ServiceAccounts("any-app-any-env").Create( context.Background(), &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: utils.GetComponentServiceAccountName(componentName), Labels: map[string]string{kube.RadixComponentLabel: componentName}}}, metav1.CreateOptions{}) require.NoError(t, err) - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents( utils.NewDeployComponentBuilder(). WithName(componentName). @@ -1020,14 +982,14 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { }) t.Run("app with component using identity success if SA exist with correct labels", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) appName, envName, componentName, clientId := "any-app", "any-env", "any-component", "any-client-id" _, err := client.CoreV1().ServiceAccounts("any-app-any-env").Create( context.Background(), &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: utils.GetComponentServiceAccountName(componentName), Labels: map[string]string{kube.RadixComponentLabel: componentName, kube.IsServiceAccountForComponent: "true", "any-other-label": "any-value"}}}, metav1.CreateOptions{}) require.NoError(t, err) - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents( utils.NewDeployComponentBuilder(). WithName(componentName). @@ -1046,7 +1008,7 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { }) t.Run("component removed, custom SA is garbage collected", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) appName, envName, componentName, clientId, anyOtherServiceAccountName := "any-app", "any-env", "any-component", "any-client-id", "any-other-serviceaccount" // A service account that must not be deleted @@ -1056,7 +1018,7 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { metav1.CreateOptions{}) require.NoError(t, err) // Deploy component with Azure identity must create custom SA - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents( utils.NewDeployComponentBuilder(). WithName(componentName). @@ -1073,7 +1035,7 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { assert.Equal(t, 3, len(serviceAccounts.Items), "Number of service accounts was not expected") // Redploy component without Azure identity should delete custom SA - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents( utils.NewDeployComponentBuilder(). WithName(componentName). @@ -1090,8 +1052,8 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { }) t.Run("app with job use radix-job-scheduler SA", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents(). WithAppName("any-other-app"). WithEnvironment("test")) @@ -1107,10 +1069,10 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { // Test t.Run("app from component to job and back to component", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) // Initial deployment, app is a component - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents( utils.NewDeployComponentBuilder().WithName("comp1")). WithJobComponents(). @@ -1125,7 +1087,7 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { assert.Equal(t, defaultServiceAccountName, expectedDeployments[0].Spec.Template.Spec.ServiceAccountName) // Change app to be a job - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents(). WithJobComponents( utils.NewDeployJobComponentBuilder().WithName("job1")). @@ -1146,7 +1108,7 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { assert.Equal(t, defaultServiceAccountName, expectedJobAuxDeployments[0].Spec.Template.Spec.ServiceAccountName) // And change app back to a component - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents( utils.NewDeployComponentBuilder().WithName("comp1")). WithJobComponents(). @@ -1163,8 +1125,8 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { }) t.Run("webhook runs as radix-github-webhook SA", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithJobComponents(). WithAppName("radix-github-webhook"). WithEnvironment("test")) @@ -1179,8 +1141,8 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { }) t.Run("radix-api runs as radix-api SA", func(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithJobComponents(). WithAppName("radix-api"). WithEnvironment("test")) @@ -1196,10 +1158,10 @@ func TestObjectSynced_ServiceAccountSettingsAndRbac(t *testing.T) { func TestObjectSynced_MultiComponentWithSameName_ContainsOneComponent(t *testing.T) { // Setup - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("app"). WithEnvironment("test"). WithJobComponents(). @@ -1230,13 +1192,13 @@ func TestObjectSynced_MultiComponentWithSameName_ContainsOneComponent(t *testing func TestConfigMap_IsGarbageCollected(t *testing.T) { // Setup - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyEnvironment := "test" namespace := utils.GetEnvironmentNamespace(appName, anyEnvironment) // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(appName). WithEnvironment(anyEnvironment). WithJobComponents(). @@ -1267,7 +1229,7 @@ func TestConfigMap_IsGarbageCollected(t *testing.T) { assert.Len(t, envVarMetadataCms, 2) // delete 2nd component - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(appName). WithEnvironment(anyEnvironment). WithJobComponents(). @@ -1296,13 +1258,13 @@ func TestConfigMap_IsGarbageCollected(t *testing.T) { func TestObjectSynced_NoEnvAndNoSecrets_ContainsDefaultEnvVariables(t *testing.T) { // Setup - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyEnvironment := "test" commitId := string(uuid.NewUUID()) // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("app"). WithEnvironment(anyEnvironment). WithJobComponents(). @@ -1349,11 +1311,11 @@ func TestObjectSynced_NoEnvAndNoSecrets_ContainsDefaultEnvVariables(t *testing.T func TestObjectSynced_WithLabels_LabelsAppliedToDeployment(t *testing.T) { // Setup - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeploymentWithComponentModifier(func(builder utils.DeployComponentBuilder) utils.DeployComponentBuilder { return builder.WithEnvironmentVariable(defaults.RadixCommitHashEnvironmentVariable, "4faca8595c5283a9d0f17a623b9255a0d9866a2e") }). @@ -1374,7 +1336,7 @@ func TestObjectSynced_WithLabels_LabelsAppliedToDeployment(t *testing.T) { func TestObjectSynced_NotLatest_DeploymentIsIgnored(t *testing.T) { // Setup - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Test @@ -1384,7 +1346,7 @@ func TestObjectSynced_NotLatest_DeploymentIsIgnored(t *testing.T) { firstUID = "fda3d224-3115-11e9-b189-06c15a8f2fbb" secondUID = "5a8f2fbb-3115-11e9-b189-06c1fda3d224" - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("app1"). WithEnvironment("prod"). @@ -1409,7 +1371,7 @@ func TestObjectSynced_NotLatest_DeploymentIsIgnored(t *testing.T) { time.Sleep(1 * time.Millisecond) // This is one second newer deployment - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("app1"). WithEnvironment("prod"). WithImageTag("seconddeployment"). @@ -1444,7 +1406,7 @@ func TestObjectSynced_NotLatest_DeploymentIsIgnored(t *testing.T) { WithPort("http", 8080). WithPublicPort("http")) - err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, rdBuilder) + err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, rdBuilder) require.NoError(t, err) deployments, _ = client.AppsV1().Deployments(envNamespace).List(context.TODO(), metav1.ListOptions{}) @@ -1458,10 +1420,10 @@ func TestObjectSynced_NotLatest_DeploymentIsIgnored(t *testing.T) { } func Test_UpdateAndAddDeployment_DeploymentAnnotationIsCorrectlyUpdated(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Test first deployment - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("first_deployment"). WithAppName("anyapp1"). WithEnvironment("test"). @@ -1482,7 +1444,7 @@ func Test_UpdateAndAddDeployment_DeploymentAnnotationIsCorrectlyUpdated(t *testi assert.Empty(t, secondDeployment.Spec.Template.Annotations[kube.RadixDeploymentNameAnnotation]) // Test second deployment - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("second_deployment"). WithAppName("anyapp1"). WithEnvironment("test"). @@ -1502,10 +1464,10 @@ func Test_UpdateAndAddDeployment_DeploymentAnnotationIsCorrectlyUpdated(t *testi } func TestObjectUpdated_UpdatePort_IngressIsCorrectlyReconciled(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("anyapp1"). WithEnvironment("test"). @@ -1531,7 +1493,7 @@ func TestObjectUpdated_UpdatePort_IngressIsCorrectlyReconciled(t *testing.T) { time.Sleep(1 * time.Second) - err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("anyapp1"). WithEnvironment("test"). @@ -1547,12 +1509,12 @@ func TestObjectUpdated_UpdatePort_IngressIsCorrectlyReconciled(t *testing.T) { } func TestObjectUpdated_ZeroReplicasExistsAndNotSpecifiedReplicas_SetsDefaultReplicaCount(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() envNamespace := utils.GetEnvironmentNamespace("anyapp", "test") // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1565,7 +1527,7 @@ func TestObjectUpdated_ZeroReplicasExistsAndNotSpecifiedReplicas_SetsDefaultRepl deployments, _ := client.AppsV1().Deployments(envNamespace).List(context.TODO(), metav1.ListOptions{}) assert.Equal(t, int32(0), *deployments.Items[0].Spec.Replicas) - err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1578,12 +1540,12 @@ func TestObjectUpdated_ZeroReplicasExistsAndNotSpecifiedReplicas_SetsDefaultRepl } func TestObjectSynced_DeploymentReplicasSetAccordingToSpec(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() envNamespace := utils.GetEnvironmentNamespace("anyapp", "test") // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1614,12 +1576,12 @@ func TestObjectSynced_DeploymentReplicasSetAccordingToSpec(t *testing.T) { } func TestObjectSynced_DeploymentReplicasFromCurrentDeploymentWhenHPAEnabled(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() envNamespace := utils.GetEnvironmentNamespace("anyapp", "test") // Initial sync creating deployments should use replicas from spec - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("deployment1"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1636,7 +1598,7 @@ func TestObjectSynced_DeploymentReplicasFromCurrentDeploymentWhenHPAEnabled(t *t _, err = client.AppsV1().Deployments(envNamespace).Update(context.Background(), comp1, metav1.UpdateOptions{}) require.NoError(t, err) // Resync existing RD should use replicas from current deployment for HPA enabled component - err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("deployment1"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1649,7 +1611,7 @@ func TestObjectSynced_DeploymentReplicasFromCurrentDeploymentWhenHPAEnabled(t *t assert.Equal(t, int32(3), *comp1.Spec.Replicas) // Resync new RD should use replicas from current deployment for HPA enabled component - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("deployment2"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1662,7 +1624,7 @@ func TestObjectSynced_DeploymentReplicasFromCurrentDeploymentWhenHPAEnabled(t *t assert.Equal(t, int32(3), *comp1.Spec.Replicas) // Resync new RD with HPA removed should use replicas from RD spec - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("deployment3"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1676,12 +1638,12 @@ func TestObjectSynced_DeploymentReplicasFromCurrentDeploymentWhenHPAEnabled(t *t } func TestObjectSynced_StopAndStartDeploymentWhenHPAEnabled(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() envNamespace := utils.GetEnvironmentNamespace("anyapp", "test") // Initial sync creating deployments should use replicas from spec - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("deployment1"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1694,7 +1656,7 @@ func TestObjectSynced_StopAndStartDeploymentWhenHPAEnabled(t *testing.T) { assert.Equal(t, int32(2), *comp1.Spec.Replicas) // Resync existing RD with replicas 0 (stop) should set deployment replicas to 0 - err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("deployment1"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1707,7 +1669,7 @@ func TestObjectSynced_StopAndStartDeploymentWhenHPAEnabled(t *testing.T) { assert.Equal(t, int32(0), *comp1.Spec.Replicas) // Resync existing RD with replicas set back to original value (start) should use replicas from spec - err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("deployment1"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1722,12 +1684,12 @@ func TestObjectSynced_StopAndStartDeploymentWhenHPAEnabled(t *testing.T) { } func TestObjectSynced_DeploymentRevisionHistoryLimit(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() envNamespace := utils.GetEnvironmentNamespace("anyapp", "test") // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1768,7 +1730,7 @@ func TestObjectSynced_DeploymentsUsedByScheduledJobsMaintainHistoryLimit(t *test for _, ts := range scenarios { t.Run(ts.name, func(tt *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() envNamespace := utils.GetEnvironmentNamespace("anyapp", "test") @@ -1776,7 +1738,7 @@ func TestObjectSynced_DeploymentsUsedByScheduledJobsMaintainHistoryLimit(t *test now := time.Now() timeShift := 1 for _, deploymentName := range ts.deploymentNames { - _, err := applyDeploymentWithModifiedSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.NewDeploymentBuilder(). + _, err := applyDeploymentWithModifiedSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.NewDeploymentBuilder(). WithRadixApplication(radixApplication). WithDeploymentName(deploymentName). WithAppName("anyapp"). @@ -1830,12 +1792,12 @@ func addRadixBatches(radixclient radixclient.Interface, envNamespace string, dep } func TestObjectUpdated_MultipleReplicasExistsAndNotSpecifiedReplicas_SetsDefaultReplicaCount(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() envNamespace := utils.GetEnvironmentNamespace("anyapp", "test") // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1848,7 +1810,7 @@ func TestObjectUpdated_MultipleReplicasExistsAndNotSpecifiedReplicas_SetsDefault deployments, _ := client.AppsV1().Deployments(envNamespace).List(context.TODO(), metav1.ListOptions{}) assert.Equal(t, int32(3), *deployments.Items[0].Spec.Replicas) - err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + err = applyDeploymentUpdateWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("a_deployment_name"). WithAppName("anyapp"). WithEnvironment("test"). @@ -1861,11 +1823,11 @@ func TestObjectUpdated_MultipleReplicasExistsAndNotSpecifiedReplicas_SetsDefault } func TestObjectUpdated_WithAppAliasRemoved_AliasIngressIsCorrectlyReconciled(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Setup os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("any-app"). WithEnvironment("dev"). WithComponents( @@ -1883,7 +1845,7 @@ func TestObjectUpdated_WithAppAliasRemoved_AliasIngressIsCorrectlyReconciled(t * assert.Truef(t, ingressByNameExists("frontend-active-cluster-url-alias", ingresses), "App should have another external alias") // Remove app alias from dev - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("any-app"). WithEnvironment("dev"). WithComponents( @@ -1900,7 +1862,7 @@ func TestObjectUpdated_WithAppAliasRemoved_AliasIngressIsCorrectlyReconciled(t * } func TestObjectSynced_MultiComponentToOneComponent_HandlesChange(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -1909,7 +1871,7 @@ func TestObjectSynced_MultiComponentToOneComponent_HandlesChange(t *testing.T) { componentThreeName := "componentThreeName" // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -1939,7 +1901,7 @@ func TestObjectSynced_MultiComponentToOneComponent_HandlesChange(t *testing.T) { assert.Equal(t, componentOneName, deployments.Items[0].Name, "app deployment not there") // Remove components - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -1989,18 +1951,18 @@ func TestObjectSynced_MultiComponentToOneComponent_HandlesChange(t *testing.T) { t.Run("validate roles", func(t *testing.T) { t.Parallel() roles, _ := client.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.ElementsMatch(t, []string{"radix-app-adm-componentTwoName", "radix-app-reader-componentTwoName"}, getRoleNames(roles)) + assert.ElementsMatch(t, []string{"radix-app-adm-componentTwoName", "radix-app-reader-componentTwoName", "radix-app-externaldns-adm", "radix-app-externaldns-reader"}, getRoleNames(roles)) }) t.Run("validate rolebindings", func(t *testing.T) { t.Parallel() rolebindings, _ := client.RbacV1().RoleBindings(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.ElementsMatch(t, []string{"radix-app-adm-componentTwoName", "radix-app-reader-componentTwoName"}, getRoleBindingNames(rolebindings)) + assert.ElementsMatch(t, []string{"radix-app-adm-componentTwoName", "radix-app-reader-componentTwoName", "radix-app-externaldns-adm", "radix-app-externaldns-reader"}, getRoleBindingNames(rolebindings)) }) } func TestObjectSynced_PublicToNonPublic_HandlesChange(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -2008,7 +1970,7 @@ func TestObjectSynced_PublicToNonPublic_HandlesChange(t *testing.T) { componentTwoName := "componentTwoName" // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2026,7 +1988,7 @@ func TestObjectSynced_PublicToNonPublic_HandlesChange(t *testing.T) { assert.Equal(t, 2, len(ingresses.Items), "Both components should be public") // Remove public on component 2 - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2043,7 +2005,7 @@ func TestObjectSynced_PublicToNonPublic_HandlesChange(t *testing.T) { assert.Equal(t, 1, len(ingresses.Items), "Only component 1 should be public") // Remove public on component 1 - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2062,14 +2024,14 @@ func TestObjectSynced_PublicToNonPublic_HandlesChange(t *testing.T) { //nolint:staticcheck func TestObjectSynced_PublicPort_OldPublic(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" componentOneName := "componentOneName" // New publicPort exists, old public does not exist - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2088,7 +2050,7 @@ func TestObjectSynced_PublicPort_OldPublic(t *testing.T) { assert.Equal(t, int32(80), ingresses.Items[0].Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number) // New publicPort exists, old public exists (ignored) - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2106,7 +2068,7 @@ func TestObjectSynced_PublicPort_OldPublic(t *testing.T) { assert.Equal(t, int32(80), ingresses.Items[0].Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number) // New publicPort does not exist, old public does not exist - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2123,7 +2085,7 @@ func TestObjectSynced_PublicPort_OldPublic(t *testing.T) { assert.Equal(t, 0, len(ingresses.Items), "Component should not be public") // New publicPort does not exist, old public exists (used) - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2159,11 +2121,11 @@ func TestObjectUpdated_WithAllExternalAliasRemoved_ExternalAliasIngressIsCorrect anyComponentName := "frontend" envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironment) - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Setup os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironment). WithJobComponents(). @@ -2176,23 +2138,14 @@ func TestObjectUpdated_WithAllExternalAliasRemoved_ExternalAliasIngressIsCorrect require.NoError(t, err) // Test ingresses, _ := client.NetworkingV1().Ingresses(envNamespace).List(context.TODO(), metav1.ListOptions{}) - secrets, _ := client.CoreV1().Secrets(envNamespace).List(context.TODO(), metav1.ListOptions{}) - roles, _ := client.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - rolebindings, _ := client.RbacV1().RoleBindings(envNamespace).List(context.TODO(), metav1.ListOptions{}) assert.Equal(t, 3, len(ingresses.Items), "Environment should have three ingresses") assert.Truef(t, ingressByNameExists("some.alias.com", ingresses), "App should have had an external alias ingress") assert.Truef(t, ingressByNameExists("frontend-active-cluster-url-alias", ingresses), "App should have active cluster alias") assert.Truef(t, ingressByNameExists("frontend", ingresses), "App should have cluster specific alias") - assert.ElementsMatch(t, []string{"radix-app-adm-frontend", "radix-app-reader-frontend"}, getRoleNames(roles)) - assert.ElementsMatch(t, []string{"radix-app-adm-frontend", "radix-app-reader-frontend"}, getRoleBindingNames(rolebindings)) - - assert.Equal(t, 1, len(secrets.Items), "Environment should have one secret for TLS cert") - assert.True(t, secretByNameExists("some.alias.com", secrets), "TLS certificate for external alias is not properly defined") - // Remove app alias from dev - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironment). WithJobComponents(). @@ -2203,17 +2156,10 @@ func TestObjectUpdated_WithAllExternalAliasRemoved_ExternalAliasIngressIsCorrect WithPublicPort("http"))) require.NoError(t, err) ingresses, _ = client.NetworkingV1().Ingresses(envNamespace).List(context.TODO(), metav1.ListOptions{}) - secrets, _ = client.CoreV1().Secrets(envNamespace).List(context.TODO(), metav1.ListOptions{}) - rolebindings, _ = client.RbacV1().RoleBindings(envNamespace).List(context.TODO(), metav1.ListOptions{}) assert.Equal(t, 2, len(ingresses.Items), "External alias ingress should have been removed") assert.Truef(t, ingressByNameExists("frontend-active-cluster-url-alias", ingresses), "App should have active cluster alias") assert.Truef(t, ingressByNameExists("frontend", ingresses), "App should have cluster specific alias") - - assert.Equal(t, 0, len(rolebindings.Items), "Role should have been removed") - assert.Equal(t, 0, len(rolebindings.Items), "Rolebinding should have been removed") - assert.Equal(t, 0, len(secrets.Items), "Secret should have been removed") - } func TestObjectUpdated_WithOneExternalAliasRemovedOrModified_AllChangesProperlyReconciled(t *testing.T) { @@ -2222,12 +2168,12 @@ func TestObjectUpdated_WithOneExternalAliasRemovedOrModified_AllChangesProperlyR anyComponentName := "frontend" envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironment) - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Setup os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironment). WithComponents( @@ -2255,12 +2201,7 @@ func TestObjectUpdated_WithOneExternalAliasRemovedOrModified_AllChangesProperlyR assert.Equal(t, "another.alias.com", anotherExternalAliasIngress.Spec.Rules[0].Host, "App should have an external alias") assert.Equal(t, int32(8080), anotherExternalAliasIngress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number, "Correct service port") - roles, _ := client.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.Equal(t, 3, len(roles.Items[0].Rules[0].ResourceNames)) - assert.Equal(t, "some.alias.com", roles.Items[0].Rules[0].ResourceNames[1], "Expected role should be able to access TLS certificate for external alias") - assert.Equal(t, "another.alias.com", roles.Items[0].Rules[0].ResourceNames[2], "Expected role should be able to access TLS certificate for second external alias") - - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironment). WithComponents( @@ -2286,12 +2227,7 @@ func TestObjectUpdated_WithOneExternalAliasRemovedOrModified_AllChangesProperlyR assert.Equal(t, "yet.another.alias.com", yetAnotherExternalAliasIngress.Spec.Rules[0].Host, "App should have an external alias") assert.Equal(t, int32(8081), yetAnotherExternalAliasIngress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number, "Correct service port") - roles, _ = client.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.Equal(t, 3, len(roles.Items[0].Rules[0].ResourceNames)) - assert.Equal(t, "some.alias.com", roles.Items[0].Rules[0].ResourceNames[1], "Expected role should be able to access TLS certificate for external alias") - assert.Equal(t, "yet.another.alias.com", roles.Items[0].Rules[0].ResourceNames[2], "Expected role should be able to access TLS certificate for second external alias") - - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironment). WithComponents( @@ -2312,12 +2248,8 @@ func TestObjectUpdated_WithOneExternalAliasRemovedOrModified_AllChangesProperlyR assert.Equal(t, "yet.another.alias.com", yetAnotherExternalAliasIngress.Spec.Rules[0].Host, "App should have an external alias") assert.Equal(t, int32(8081), yetAnotherExternalAliasIngress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number, "Correct service port") - roles, _ = client.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.Equal(t, 2, len(roles.Items[0].Rules[0].ResourceNames)) - assert.Equal(t, "yet.another.alias.com", roles.Items[0].Rules[0].ResourceNames[1], "Expected role should be able to access TLS certificate for second external alias") - // Remove app alias from dev - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironment). WithComponents( @@ -2330,10 +2262,6 @@ func TestObjectUpdated_WithOneExternalAliasRemovedOrModified_AllChangesProperlyR assert.Equal(t, 2, len(ingresses.Items), "External alias ingress should have been removed") assert.Truef(t, ingressByNameExists("frontend-active-cluster-url-alias", ingresses), "App should have active cluster alias") assert.Truef(t, ingressByNameExists("frontend", ingresses), "App should have cluster specific alias") - - roles, _ = client.RbacV1().Roles(envNamespace).List(context.TODO(), metav1.ListOptions{}) - assert.Equal(t, 0, len(roles.Items), "Role should have been removed") - } func TestFixedAliasIngress_ActiveCluster(t *testing.T) { @@ -2342,7 +2270,7 @@ func TestFixedAliasIngress_ActiveCluster(t *testing.T) { anyComponentName := "frontend" envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironment) - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() radixDeployBuilder := utils.ARadixDeployment(). WithAppName(anyAppName). @@ -2355,7 +2283,7 @@ func TestFixedAliasIngress_ActiveCluster(t *testing.T) { // Current cluster is active cluster os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, radixDeployBuilder) + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, radixDeployBuilder) require.NoError(t, err) ingresses, _ := client.NetworkingV1().Ingresses(envNamespace).List(context.TODO(), metav1.ListOptions{}) assert.Equal(t, 2, len(ingresses.Items), "Environment should have two ingresses") @@ -2366,7 +2294,7 @@ func TestFixedAliasIngress_ActiveCluster(t *testing.T) { // Current cluster is not active cluster os.Setenv(defaults.ActiveClusternameEnvironmentVariable, "newClusterName") - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, radixDeployBuilder) + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, radixDeployBuilder) require.NoError(t, err) ingresses, _ = client.NetworkingV1().Ingresses(envNamespace).List(context.TODO(), metav1.ListOptions{}) assert.Equal(t, 1, len(ingresses.Items), "Environment should have one ingresses") @@ -2378,7 +2306,7 @@ func TestNewDeploymentStatus(t *testing.T) { anyEnv := "dev" anyComponentName := "frontend" - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() radixDeployBuilder := utils.ARadixDeployment(). WithAppName(anyApp). @@ -2390,7 +2318,7 @@ func TestNewDeploymentStatus(t *testing.T) { WithPort("http", 8080). WithPublicPort("http")) - rd, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, radixDeployBuilder) + rd, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, radixDeployBuilder) require.NoError(t, err) assert.Equal(t, radixv1.DeploymentActive, rd.Status.Condition) assert.True(t, !rd.Status.ActiveFrom.IsZero()) @@ -2408,7 +2336,7 @@ func TestNewDeploymentStatus(t *testing.T) { WithPort("http", 8080). WithPublicPort("http")) - rd2, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, radixDeployBuilder) + rd2, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, radixDeployBuilder) require.NoError(t, err) rd, _ = getUpdatedRD(radixclient, rd) @@ -2423,12 +2351,12 @@ func Test_AddMultipleNewDeployments_CorrectStatuses(t *testing.T) { anyApp := "any-app" anyEnv := "dev" anyComponentName := "frontend" - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() - rd1 := addRadixDeployment(anyApp, anyEnv, anyComponentName, tu, client, kubeUtil, radixclient, prometheusclient) + rd1 := addRadixDeployment(anyApp, anyEnv, anyComponentName, tu, client, kubeUtil, radixclient, prometheusclient, certClient) time.Sleep(2 * time.Millisecond) - rd2 := addRadixDeployment(anyApp, anyEnv, anyComponentName, tu, client, kubeUtil, radixclient, prometheusclient) + rd2 := addRadixDeployment(anyApp, anyEnv, anyComponentName, tu, client, kubeUtil, radixclient, prometheusclient, certClient) rd1, _ = getUpdatedRD(radixclient, rd1) assert.Equal(t, radixv1.DeploymentInactive, rd1.Status.Condition) @@ -2437,7 +2365,7 @@ func Test_AddMultipleNewDeployments_CorrectStatuses(t *testing.T) { assert.True(t, !rd2.Status.ActiveFrom.IsZero()) time.Sleep(3 * time.Millisecond) - rd3 := addRadixDeployment(anyApp, anyEnv, anyComponentName, tu, client, kubeUtil, radixclient, prometheusclient) + rd3 := addRadixDeployment(anyApp, anyEnv, anyComponentName, tu, client, kubeUtil, radixclient, prometheusclient, certClient) rd1, _ = getUpdatedRD(radixclient, rd1) rd2, _ = getUpdatedRD(radixclient, rd2) @@ -2449,7 +2377,7 @@ func Test_AddMultipleNewDeployments_CorrectStatuses(t *testing.T) { assert.True(t, !rd3.Status.ActiveFrom.IsZero()) time.Sleep(4 * time.Millisecond) - rd4 := addRadixDeployment(anyApp, anyEnv, anyComponentName, tu, client, kubeUtil, radixclient, prometheusclient) + rd4 := addRadixDeployment(anyApp, anyEnv, anyComponentName, tu, client, kubeUtil, radixclient, prometheusclient, certClient) rd1, _ = getUpdatedRD(radixclient, rd1) rd2, _ = getUpdatedRD(radixclient, rd2) rd3, _ = getUpdatedRD(radixclient, rd3) @@ -2468,7 +2396,7 @@ func getUpdatedRD(radixclient radixclient.Interface, rd *radixv1.RadixDeployment return radixclient.RadixV1().RadixDeployments(rd.GetNamespace()).Get(context.TODO(), rd.GetName(), metav1.GetOptions{ResourceVersion: rd.ResourceVersion}) } -func addRadixDeployment(anyApp string, anyEnv string, anyComponentName string, tu *test.Utils, client kubernetes.Interface, kubeUtil *kube.Kube, radixclient radixclient.Interface, prometheusclient prometheusclient.Interface) *radixv1.RadixDeployment { +func addRadixDeployment(anyApp string, anyEnv string, anyComponentName string, tu *test.Utils, client kubernetes.Interface, kubeUtil *kube.Kube, radixclient radixclient.Interface, prometheusclient prometheusclient.Interface, certClient *certfake.Clientset) *radixv1.RadixDeployment { radixDeployBuilder := utils.ARadixDeployment(). WithAppName(anyApp). WithEnvironment(anyEnv). @@ -2478,7 +2406,7 @@ func addRadixDeployment(anyApp string, anyEnv string, anyComponentName string, t WithName(anyComponentName). WithPort("http", 8080). WithPublicPort("http")) - rd, _ := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, radixDeployBuilder) + rd, _ := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, radixDeployBuilder) return rd } @@ -2488,10 +2416,10 @@ func TestObjectUpdated_RemoveOneSecret_SecretIsRemoved(t *testing.T) { anyComponentName := "frontend" envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironment) - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Setup - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironment). WithComponents( @@ -2521,7 +2449,7 @@ func TestObjectUpdated_RemoveOneSecret_SecretIsRemoved(t *testing.T) { require.NoError(t, err) // Removing one secret from config and therefore from the deployment // should cause it to disappear - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironment). WithComponents( @@ -2537,207 +2465,13 @@ func TestObjectUpdated_RemoveOneSecret_SecretIsRemoved(t *testing.T) { assert.True(t, radixutils.ArrayEqualElements([]string{"a_secret", "a_third_secret"}, radixmaps.GetKeysFromByteMap(anyComponentSecret.Data)), "Component secret data is not as expected") } -func TestObjectUpdated_ExternalDNS_EnableAutomation_DeleteAndRecreateResources(t *testing.T) { - anyAppName := "any-app" - anyEnvironment := "dev" - fqdn := "some.alias.com" - envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironment) - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) - os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - defer teardownTest() - - // Initial sync with UseCertificateAutomation false - rd1 := utils.ARadixDeployment().WithDeploymentName("rd1"). - WithAppName(anyAppName). - WithEnvironment(anyEnvironment). - WithJobComponents(). - WithComponents( - utils.NewDeployComponentBuilder(). - WithName("anycomp"). - WithPort("http", 8080). - WithPublicPort("http"). - WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: false}), - ) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, rd1) - require.NoError(t, err) - _, err = client.NetworkingV1().Ingresses(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.NoError(t, err) - _, err = client.CoreV1().Secrets(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.NoError(t, err) - - // New sync with UseCertificateAutomation true - var m mock.Mock - client.Fake.PrependReactor("*", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { - if action.GetVerb() == "delete" && slice.Any([]string{"ingresses", "secrets"}, func(r string) bool { return r == action.GetResource().Resource }) { - deleteAction := action.(kubetesting.DeleteAction) - m.MethodCalled(deleteAction.GetVerb(), deleteAction.GetResource().Resource, deleteAction.GetNamespace(), deleteAction.GetName()) - } - return false, nil, nil - }) - m.On("delete", "ingresses", envNamespace, fqdn).Times(1) - m.On("delete", "secrets", envNamespace, fqdn).Times(1) - - rd2 := utils.ARadixDeployment().WithDeploymentName("rd2"). - WithAppName(anyAppName). - WithEnvironment(anyEnvironment). - WithJobComponents(). - WithComponents( - utils.NewDeployComponentBuilder(). - WithName("anycomp"). - WithPort("http", 8080). - WithPublicPort("http"). - WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: true}), - ) - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, rd2) - require.NoError(t, err) - m.AssertExpectations(t) - ingress, err := client.NetworkingV1().Ingresses(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.NoError(t, err) - expectedAnnotations := map[string]string{ - kube.RadixExternalDNSUseCertificateAutomationAnnotation: "true", - "cert-manager.io/cluster-issuer": testConfig.CertificateAutomation.ClusterIssuer, - "cert-manager.io/duration": testConfig.CertificateAutomation.Duration.String(), - "cert-manager.io/renew-before": testConfig.CertificateAutomation.RenewBefore.String(), - } - assert.Equal(t, expectedAnnotations, ingress.Annotations) - _, err = client.CoreV1().Secrets(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - assert.True(t, kubeerrors.IsNotFound(err)) -} - -func TestObjectUpdated_ExternalDNS_DisableAutomation_DeleteIngressResetSecret(t *testing.T) { - anyAppName, anyEnvironment, anyComponent := "any-app", "dev", "anyComp" - fqdn := "some.alias.com" - envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironment) - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) - os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - defer teardownTest() - - // Initial sync with UseCertificateAutomation true - rd1 := utils.ARadixDeployment().WithDeploymentName("rd1"). - WithAppName(anyAppName). - WithEnvironment(anyEnvironment). - WithJobComponents(). - WithComponents( - utils.NewDeployComponentBuilder(). - WithName(anyComponent). - WithPort("http", 8080). - WithPublicPort("http"). - WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: true}), - ) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, rd1) - require.NoError(t, err) - _, err = client.NetworkingV1().Ingresses(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - assert.NoError(t, err) - _, err = client.CoreV1().Secrets(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.True(t, kubeerrors.IsNotFound(err)) - // Create the TLS secret that cert-manager would normally created - tlsSecret := &corev1.Secret{ - Type: corev1.SecretTypeTLS, - ObjectMeta: metav1.ObjectMeta{Name: fqdn, Namespace: envNamespace}, - Data: map[string][]byte{ - corev1.TLSPrivateKeyKey: []byte("anykey"), - corev1.TLSCertKey: []byte("anycert"), - }, - } - _, err = client.CoreV1().Secrets(envNamespace).Create(context.Background(), tlsSecret, metav1.CreateOptions{}) - require.NoError(t, err) - - // New sync with UseCertificateAutomation false - var m mock.Mock - client.Fake.PrependReactor("*", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { - if action.GetVerb() == "delete" && slice.Any([]string{"ingresses", "secrets"}, func(r string) bool { return r == action.GetResource().Resource }) { - deleteAction := action.(kubetesting.DeleteAction) - m.MethodCalled(deleteAction.GetVerb(), deleteAction.GetResource().Resource, deleteAction.GetNamespace(), deleteAction.GetName()) - } - return false, nil, nil - }) - m.On("delete", "ingresses", envNamespace, fqdn).Times(1) - - rd2 := utils.ARadixDeployment().WithDeploymentName("rd2"). - WithAppName(anyAppName). - WithEnvironment(anyEnvironment). - WithComponents( - utils.NewDeployComponentBuilder(). - WithName(anyComponent). - WithPort("http", 8080). - WithPublicPort("http"). - WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: false}), - ) - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, rd2) - require.NoError(t, err) - m.AssertExpectations(t) - ingress, err := client.NetworkingV1().Ingresses(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.NoError(t, err) - assert.Equal(t, map[string]string{kube.RadixExternalDNSUseCertificateAutomationAnnotation: "false"}, ingress.Annotations) - secret, err := client.CoreV1().Secrets(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.NoError(t, err) - assert.Equal(t, map[string][]byte{corev1.TLSCertKey: nil, corev1.TLSPrivateKeyKey: nil}, secret.Data) - assert.Equal(t, map[string]string{kube.RadixAppLabel: anyAppName, kube.RadixComponentLabel: anyComponent, kube.RadixExternalAliasLabel: "true"}, secret.Labels) -} - -func TestObjectUpdated_ExternalDNS_TLSSecretDataRetainedBetweenSync(t *testing.T) { - anyAppName := "any-app" - anyEnvironment := "dev" - fqdn := "some.alias.com" - envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironment) - tlsKey, tlsCert := []byte("anytlskey"), []byte("anytlscert") - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) - os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - defer teardownTest() - - // Initial sync - rd1 := utils.ARadixDeployment().WithDeploymentName("rd1"). - WithAppName(anyAppName). - WithEnvironment(anyEnvironment). - WithJobComponents(). - WithComponents( - utils.NewDeployComponentBuilder(). - WithName("anycomp"). - WithPort("http", 8080). - WithPublicPort("http"). - WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: false}), - ) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, rd1) - require.NoError(t, err) - _, err = client.NetworkingV1().Ingresses(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.NoError(t, err) - secret, err := client.CoreV1().Secrets(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.NoError(t, err) - secret.Data[corev1.TLSPrivateKeyKey] = tlsKey - secret.Data[corev1.TLSCertKey] = tlsCert - secret, err = client.CoreV1().Secrets(envNamespace).Update(context.Background(), secret, metav1.UpdateOptions{}) - require.NoError(t, err) - require.Equal(t, tlsKey, secret.Data[corev1.TLSPrivateKeyKey]) - require.Equal(t, tlsCert, secret.Data[corev1.TLSCertKey]) - - // New RD with same external DNS - rd2 := utils.ARadixDeployment().WithDeploymentName("rd2"). - WithAppName(anyAppName). - WithEnvironment(anyEnvironment). - WithComponents( - utils.NewDeployComponentBuilder(). - WithName("anycomp"). - WithPort("http", 8080). - WithPublicPort("http"). - WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: false}), - ) - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, rd2) - require.NoError(t, err) - _, err = client.NetworkingV1().Ingresses(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - assert.NoError(t, err) - secret, err = client.CoreV1().Secrets(envNamespace).Get(context.Background(), fqdn, metav1.GetOptions{}) - require.NoError(t, err) - assert.Equal(t, tlsKey, secret.Data[corev1.TLSPrivateKeyKey]) - assert.Equal(t, tlsCert, secret.Data[corev1.TLSCertKey]) -} - func TestHistoryLimit_IsBroken_FixedAmountOfDeployments(t *testing.T) { anyAppName := "any-app" anyComponentName := "frontend" anyEnvironment := "dev" anyLimit := 3 - tu, client, kubeUtils, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtils, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Current cluster is active cluster deploymentHistoryLimitSetter := func(syncer DeploymentSyncer) { @@ -2748,7 +2482,7 @@ func TestHistoryLimit_IsBroken_FixedAmountOfDeployments(t *testing.T) { } } envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironment) - _, err := applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, + _, err := applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("firstdeployment"). WithAppName(anyAppName). @@ -2760,7 +2494,7 @@ func TestHistoryLimit_IsBroken_FixedAmountOfDeployments(t *testing.T) { WithPublicPort("http")), deploymentHistoryLimitSetter) require.NoError(t, err) - _, err = applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, + _, err = applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("seconddeployment"). WithAppName(anyAppName). @@ -2772,7 +2506,7 @@ func TestHistoryLimit_IsBroken_FixedAmountOfDeployments(t *testing.T) { WithPublicPort("http")), deploymentHistoryLimitSetter) require.NoError(t, err) - _, err = applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, + _, err = applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("thirddeployment"). WithAppName(anyAppName). @@ -2784,7 +2518,7 @@ func TestHistoryLimit_IsBroken_FixedAmountOfDeployments(t *testing.T) { WithPublicPort("http")), deploymentHistoryLimitSetter) require.NoError(t, err) - _, err = applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, + _, err = applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("fourthdeployment"). WithAppName(anyAppName). @@ -2804,7 +2538,7 @@ func TestHistoryLimit_IsBroken_FixedAmountOfDeployments(t *testing.T) { assert.True(t, radixDeploymentByNameExists("thirddeployment", deployments)) assert.True(t, radixDeploymentByNameExists("fourthdeployment", deployments)) - _, err = applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, + _, err = applyDeploymentWithModifiedSync(tu, client, kubeUtils, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithDeploymentName("fifthdeployment"). WithAppName(anyAppName). @@ -2829,7 +2563,7 @@ func TestHistoryLimit_IsBroken_FixedAmountOfDeployments(t *testing.T) { } func TestHPAConfig(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -2840,7 +2574,7 @@ func TestHPAConfig(t *testing.T) { maxReplicas := int32(4) // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2875,7 +2609,7 @@ func TestHPAConfig(t *testing.T) { }) // Test - remove HPA from component three - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -2910,7 +2644,7 @@ func TestHPAConfig(t *testing.T) { } func TestMonitoringConfig(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() myAppName := "anyappname" myEnvName := "test" @@ -2938,7 +2672,7 @@ func TestMonitoringConfig(t *testing.T) { assert.Equal(t, compName, serviceMonitor.Spec.Selector.MatchLabels[kube.RadixComponentLabel]) } - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(myAppName). WithEnvironment(myEnvName). WithComponents( @@ -2988,7 +2722,7 @@ func TestMonitoringConfig(t *testing.T) { } func TestObjectUpdated_UpdatePort_DeploymentPodPortSpecIsCorrect(t *testing.T) { - tu, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() var portTestFunc = func(portName string, portNumber int32, ports []corev1.ContainerPort) { port := getPortByName(portName, ports) @@ -2997,7 +2731,7 @@ func TestObjectUpdated_UpdatePort_DeploymentPodPortSpecIsCorrect(t *testing.T) { } // Initial build - _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("app"). WithEnvironment("env"). WithComponents( @@ -3021,7 +2755,7 @@ func TestObjectUpdated_UpdatePort_DeploymentPodPortSpecIsCorrect(t *testing.T) { portTestFunc("scheduler-port", 8080, job.Spec.Template.Spec.Containers[0].Ports) // Update ports - _, err = applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName("app"). WithEnvironment("env"). WithComponents( @@ -3044,7 +2778,7 @@ func TestObjectUpdated_UpdatePort_DeploymentPodPortSpecIsCorrect(t *testing.T) { } func TestUseGpuNode(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -3059,7 +2793,7 @@ func TestUseGpuNode(t *testing.T) { nodeGpu2 := "nvidia-v100, nvidia-p100" nodeGpu3 := "nvidia-v100, nvidia-p100, -nvidia-k80" nodeGpu4 := "nvidia-p100, -nvidia-k80" - rd, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + rd, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -3123,7 +2857,7 @@ func TestUseGpuNode(t *testing.T) { } func TestUseGpuNodeOnDeploy(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -3138,7 +2872,7 @@ func TestUseGpuNodeOnDeploy(t *testing.T) { gpuNvidiaV100 := "nvidia-v100" gpuNvidiaP100 := "nvidia-p100" gpuNvidiaK80 := "nvidia-k80" - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -3255,7 +2989,7 @@ func TestUseGpuNodeOnDeploy(t *testing.T) { } func TestUseGpuNodeCount(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -3273,7 +3007,7 @@ func TestUseGpuNodeCount(t *testing.T) { nodeGpuCount0 := "0" nodeGpuCountMinus1 := "-1" nodeGpuCountInvalidTextValue := "invalid-count" - rd, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + rd, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -3352,7 +3086,7 @@ func TestUseGpuNodeCount(t *testing.T) { } func TestUseGpuNodeCountOnDeployment(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -3372,7 +3106,7 @@ func TestUseGpuNodeCountOnDeployment(t *testing.T) { nodeGpuCount0 := "0" nodeGpuCountMinus1 := "-1" nodeGpuCountInvalidTextValue := "invalid-count" - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -3503,7 +3237,7 @@ func TestUseGpuNodeCountOnDeployment(t *testing.T) { } func TestUseGpuNodeWithGpuCountOnDeployment(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -3516,7 +3250,7 @@ func TestUseGpuNodeWithGpuCountOnDeployment(t *testing.T) { gpuNvidiaP100 := "nvidia-p100" gpuNvidiaK80 := "nvidia-k80" nodeGpuCount10 := "10" - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithComponents( @@ -3646,7 +3380,7 @@ func Test_JobScheduler_ObjectsGarbageCollected(t *testing.T) { t.Run(theory.name, func(t *testing.T) { t.Parallel() - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) // Add jobs used in test addJob(client, "dev-job-job1", "app-dev", "job", true) @@ -3670,7 +3404,7 @@ func Test_JobScheduler_ObjectsGarbageCollected(t *testing.T) { addService(client, "non-job-service1", "app-dev", "") addService(client, "prod-job-service1", "app-prod", "job") - if _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, theory.builder); err != nil { + if _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, theory.builder); err != nil { assert.FailNow(t, fmt.Sprintf("error apply deployment: %v", err)) } @@ -3801,7 +3535,7 @@ func Test_JobScheduler_ObjectsGarbageCollected(t *testing.T) { } func Test_IngressAnnotations_Called(t *testing.T) { - _, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + _, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) defer os.Unsetenv(defaults.ActiveClusternameEnvironmentVariable) @@ -3818,7 +3552,7 @@ func Test_IngressAnnotations_Called(t *testing.T) { annotations2 := ingress.NewMockAnnotationProvider(ctrl) annotations2.EXPECT().GetAnnotations(&rd.Spec.Components[0], rd.Namespace).Times(3).Return(map[string]string{"bar": "y", "baz": "z"}, nil) - syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, rr, rd, []ingress.AnnotationProvider{annotations1, annotations2}, nil, &config.Config{}) + syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, []ingress.AnnotationProvider{annotations1, annotations2}, nil, &config.Config{}) err = syncer.OnSync() require.NoError(t, err) ingresses, _ := kubeclient.NetworkingV1().Ingresses("").List(context.Background(), metav1.ListOptions{}) @@ -3831,7 +3565,7 @@ func Test_IngressAnnotations_Called(t *testing.T) { } func Test_IngressAnnotations_ReturnError(t *testing.T) { - _, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + _, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() rr := utils.NewRegistrationBuilder().WithName("app").BuildRR() rd := utils.NewDeploymentBuilder().WithAppName("app").WithEnvironment("dev").WithComponent(utils.NewDeployComponentBuilder().WithName("comp").WithPublicPort("http")).BuildRD() @@ -3844,13 +3578,13 @@ func Test_IngressAnnotations_ReturnError(t *testing.T) { annotations1 := ingress.NewMockAnnotationProvider(ctrl) annotations1.EXPECT().GetAnnotations(&rd.Spec.Components[0], "app-dev").Times(1).Return(nil, errors.New("any error")) - syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, rr, rd, []ingress.AnnotationProvider{annotations1}, nil, &config.Config{}) + syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, []ingress.AnnotationProvider{annotations1}, nil, &config.Config{}) err = syncer.OnSync() assert.Error(t, err) } func Test_AuxiliaryResourceManagers_Called(t *testing.T) { - _, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + _, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() rr := utils.NewRegistrationBuilder().WithName("app").BuildRR() rd := utils.NewDeploymentBuilder().WithAppName("app").WithEnvironment("dev").WithComponent(utils.NewDeployComponentBuilder().WithName("comp").WithPublicPort("http")).BuildRD() @@ -3864,13 +3598,13 @@ func Test_AuxiliaryResourceManagers_Called(t *testing.T) { auxResource.EXPECT().GarbageCollect().Times(1).Return(nil) auxResource.EXPECT().Sync().Times(1).Return(nil) - syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, rr, rd, nil, []AuxiliaryResourceManager{auxResource}, &config.Config{}) + syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, nil, []AuxiliaryResourceManager{auxResource}, &config.Config{}) err = syncer.OnSync() assert.NoError(t, err) } func Test_AuxiliaryResourceManagers_Sync_ReturnErr(t *testing.T) { - _, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + _, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() rr := utils.NewRegistrationBuilder().WithName("app").BuildRR() rd := utils.NewDeploymentBuilder().WithAppName("app").WithEnvironment("dev").WithComponent(utils.NewDeployComponentBuilder().WithName("comp").WithPublicPort("http")).BuildRD() @@ -3885,13 +3619,13 @@ func Test_AuxiliaryResourceManagers_Sync_ReturnErr(t *testing.T) { auxResource.EXPECT().GarbageCollect().Times(1).Return(nil) auxResource.EXPECT().Sync().Times(1).Return(auxErr) - syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, rr, rd, nil, []AuxiliaryResourceManager{auxResource}, &config.Config{}) + syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, nil, []AuxiliaryResourceManager{auxResource}, &config.Config{}) err = syncer.OnSync() assert.Contains(t, err.Error(), auxErr.Error()) } func Test_AuxiliaryResourceManagers_GarbageCollect_ReturnErr(t *testing.T) { - _, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + _, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() rr := utils.NewRegistrationBuilder().WithName("app").BuildRR() rd := utils.NewDeploymentBuilder().WithAppName("app").WithEnvironment("dev").WithComponent(utils.NewDeployComponentBuilder().WithName("comp").WithPublicPort("http")).BuildRD() @@ -3906,19 +3640,19 @@ func Test_AuxiliaryResourceManagers_GarbageCollect_ReturnErr(t *testing.T) { auxResource.EXPECT().GarbageCollect().Times(1).Return(auxErr) auxResource.EXPECT().Sync().Times(0) - syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, rr, rd, nil, []AuxiliaryResourceManager{auxResource}, &config.Config{}) + syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, nil, []AuxiliaryResourceManager{auxResource}, &config.Config{}) err = syncer.OnSync() assert.Contains(t, err.Error(), auxErr.Error()) } func Test_ComponentSynced_VolumeAndMounts(t *testing.T) { appName, environment, compName := "app", "dev", "comp" - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Setup os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.NewDeploymentBuilder(). WithRadixApplication( utils.NewRadixApplicationBuilder(). @@ -3948,12 +3682,12 @@ func Test_ComponentSynced_VolumeAndMounts(t *testing.T) { func Test_JobSynced_VolumeAndMounts(t *testing.T) { appName, environment, jobName := "app", "dev", "job" - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Setup os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.NewDeploymentBuilder(). WithRadixApplication( utils.ARadixApplication(). @@ -3984,12 +3718,12 @@ func Test_JobSynced_VolumeAndMounts(t *testing.T) { func Test_ComponentSynced_SecretRefs(t *testing.T) { appName, environment, compName := "app", "dev", "comp" - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Setup os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.NewDeploymentBuilder(). WithRadixApplication( utils.NewRadixApplicationBuilder(). @@ -4024,12 +3758,12 @@ func Test_ComponentSynced_SecretRefs(t *testing.T) { func Test_JobSynced_SecretRefs(t *testing.T) { appName, environment, jobName := "app", "dev", "job" - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() // Setup os.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.NewDeploymentBuilder(). WithRadixApplication( utils.NewRadixApplicationBuilder(). @@ -4067,7 +3801,7 @@ func Test_JobSynced_SecretRefs(t *testing.T) { func TestRadixBatch_IsGarbageCollected(t *testing.T) { // Setup - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyEnvironment := "test" namespace := utils.GetEnvironmentNamespace(appName, anyEnvironment) @@ -4104,7 +3838,7 @@ func TestRadixBatch_IsGarbageCollected(t *testing.T) { require.NoError(t, err) // Test - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(appName). WithEnvironment(anyEnvironment). WithJobComponents( @@ -4119,22 +3853,436 @@ func TestRadixBatch_IsGarbageCollected(t *testing.T) { assert.ElementsMatch(t, expectedBatchNames, actualBatchNames) } +func Test_ExternalDNS_Legacy_ResourcesMigrated(t *testing.T) { + appName, envName, compName := "anyapp", "anyenv", "anycomp" + fqdnManual, fqdnAutomation := "app1.example.com", "app2.example.com" + certDataManual, keyDataManual, certDataAutomation, keyDataAutomation := "manualcert", "manualkey", "automationcert", "automationkey" + ns := utils.GetEnvironmentNamespace(appName, envName) + + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + t.Setenv(defaults.ActiveClusternameEnvironmentVariable, testClusterName) + defer teardownTest() + + // Setup legacy secrets + legacyManualSecret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: fqdnManual, + Labels: map[string]string{kube.RadixAppLabel: appName, kube.RadixComponentLabel: compName, kube.RadixExternalAliasLabel: "true"}, + }, + Data: map[string][]byte{corev1.TLSPrivateKeyKey: []byte(keyDataManual), corev1.TLSCertKey: []byte(certDataManual)}, + } + _, err := kubeclient.CoreV1().Secrets(ns).Create(context.Background(), &legacyManualSecret, metav1.CreateOptions{}) + require.NoError(t, err) + legacyAutomationSecret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: fqdnAutomation, + Labels: map[string]string{"controller.cert-manager.io/fao": "true"}, + Annotations: map[string]string{"anyannotation": "anyvalue"}, + }, + Data: map[string][]byte{corev1.TLSPrivateKeyKey: []byte(keyDataAutomation), corev1.TLSCertKey: []byte(certDataAutomation)}, + } + _, err = kubeclient.CoreV1().Secrets(ns).Create(context.Background(), &legacyAutomationSecret, metav1.CreateOptions{}) + require.NoError(t, err) + + // Setup legacy certificate + legacyCert := cmv1.Certificate{ + ObjectMeta: metav1.ObjectMeta{ + Name: fqdnAutomation, + Labels: map[string]string{kube.RadixAppLabel: appName, kube.RadixComponentLabel: compName, kube.RadixExternalAliasLabel: "true"}, + OwnerReferences: []metav1.OwnerReference{{APIVersion: "networking.k8s.io/v1", Kind: "Ingress", Name: fqdnAutomation}}, + }, + } + _, err = certClient.CertmanagerV1().Certificates(ns).Create(context.Background(), &legacyCert, metav1.CreateOptions{}) + require.NoError(t, err) + + // Setup legacy ingresses + legacyAutomationIngress := networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: fqdnAutomation, + Labels: map[string]string{kube.RadixAppLabel: appName, kube.RadixComponentLabel: compName, kube.RadixExternalAliasLabel: "true"}, + Annotations: map[string]string{"radix.equinor.com/external-dns-use-certificate-automation": "true", "cert-manager.io/cluster-issuer": "any", "cert-manager.io/duration": "any", "cert-manager.io/renew-before": "any"}, + }, + } + _, err = kubeclient.NetworkingV1().Ingresses(ns).Create(context.Background(), &legacyAutomationIngress, metav1.CreateOptions{}) + require.NoError(t, err) + + legacyManualIngress := networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: fqdnManual, + Labels: map[string]string{kube.RadixAppLabel: appName, kube.RadixComponentLabel: compName, kube.RadixExternalAliasLabel: "true"}, + Annotations: map[string]string{"radix.equinor.com/external-dns-use-certificate-automation": "false"}, + }, + } + _, err = kubeclient.NetworkingV1().Ingresses(ns).Create(context.Background(), &legacyManualIngress, metav1.CreateOptions{}) + require.NoError(t, err) + + // Apply RD + _, err = applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, + utils.ARadixDeployment(). + WithAppName(appName). + WithEnvironment(envName). + WithComponents(utils.NewDeployComponentBuilder().WithName(compName).WithPublicPort("http").WithExternalDNS( + radixv1.RadixDeployExternalDNS{FQDN: fqdnManual, UseCertificateAutomation: false}, + radixv1.RadixDeployExternalDNS{FQDN: fqdnAutomation, UseCertificateAutomation: true}, + )). + WithJobComponents(), + ) + require.NoError(t, err) + + // Manual secret: changed labels and annotations + manualSecret, err := kubeclient.CoreV1().Secrets(ns).Get(context.Background(), fqdnManual, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, map[string]string{kube.RadixAppLabel: appName, kube.RadixExternalAliasFQDNLabel: fqdnManual}, manualSecret.Labels) + assert.Empty(t, manualSecret.Annotations) + assert.Equal(t, []byte(keyDataManual), manualSecret.Data[corev1.TLSPrivateKeyKey]) + assert.Equal(t, []byte(certDataManual), manualSecret.Data[corev1.TLSCertKey]) + + // Automation secret: retains original labels and annotations as these are controlled by the Certificate resource + automationSecret, err := kubeclient.CoreV1().Secrets(ns).Get(context.Background(), fqdnAutomation, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, legacyAutomationSecret.Labels, automationSecret.Labels) + assert.Equal(t, legacyAutomationSecret.Annotations, automationSecret.Annotations) + assert.Equal(t, []byte(keyDataAutomation), automationSecret.Data[corev1.TLSPrivateKeyKey]) + assert.Equal(t, []byte(certDataAutomation), automationSecret.Data[corev1.TLSCertKey]) + + // Automation ingress: annotations removed + automationIngress, err := kubeclient.NetworkingV1().Ingresses(ns).Get(context.Background(), fqdnAutomation, metav1.GetOptions{}) + require.NoError(t, err) + assert.Empty(t, automationIngress.Annotations) + + // Manual ingress: annotations removed + manualIngress, err := kubeclient.NetworkingV1().Ingresses(ns).Get(context.Background(), fqdnManual, metav1.GetOptions{}) + require.NoError(t, err) + assert.Empty(t, manualIngress.Annotations) + + // Automation automationIngress: annotations removed + cert, err := certClient.CertmanagerV1().Certificates(ns).Get(context.Background(), fqdnAutomation, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, map[string]string{kube.RadixAppLabel: appName, kube.RadixExternalAliasFQDNLabel: fqdnAutomation}, cert.Labels) + assert.Empty(t, cert.OwnerReferences) + expectedCertSpec := cmv1.CertificateSpec{ + DNSNames: []string{fqdnAutomation}, + Duration: &metav1.Duration{Duration: testConfig.CertificateAutomation.Duration}, + RenewBefore: &metav1.Duration{Duration: testConfig.CertificateAutomation.RenewBefore}, + IssuerRef: v1.ObjectReference{ + Name: testConfig.CertificateAutomation.ClusterIssuer, + Kind: "ClusterIssuer", + Group: "cert-manager.io", + }, + SecretName: fqdnAutomation, + SecretTemplate: &cmv1.CertificateSecretTemplate{ + Labels: map[string]string{kube.RadixAppLabel: appName, kube.RadixExternalAliasFQDNLabel: fqdnAutomation}, + }, + } + assert.Equal(t, expectedCertSpec, cert.Spec) +} + +func Test_ExternalDNS_ContainsAllResources(t *testing.T) { + appName, envName := "anyapp", "anyenv" + fqdnManual1, fqdnManual2 := "foo1.example.com", "foo2.example.com" + fqdnAutomation1, fqdnAutomation2 := "bar1.example.com", "bar2.example.com" + adminGroups, readerGroups := []string{"adm1", "adm2"}, []string{"rdr1", "rdr2"} + ns := utils.GetEnvironmentNamespace(appName, envName) + + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + defer teardownTest() + + rrBuilder := utils.NewRegistrationBuilder().WithName(appName).WithAdGroups(adminGroups).WithReaderAdGroups(readerGroups) + raBuilder := utils.NewRadixApplicationBuilder().WithAppName(appName).WithRadixRegistration(rrBuilder) + rdBuilder := utils.NewDeploymentBuilder(). + WithRadixApplication(raBuilder). + WithAppName(appName). + WithEnvironment(envName). + WithComponents(utils.NewDeployComponentBuilder().WithName("comp").WithExternalDNS( + radixv1.RadixDeployExternalDNS{FQDN: fqdnManual1, UseCertificateAutomation: false}, + radixv1.RadixDeployExternalDNS{FQDN: fqdnManual2, UseCertificateAutomation: false}, + radixv1.RadixDeployExternalDNS{FQDN: fqdnAutomation1, UseCertificateAutomation: true}, + radixv1.RadixDeployExternalDNS{FQDN: fqdnAutomation2, UseCertificateAutomation: true}, + )). + WithJobComponents() + + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rdBuilder) + require.NoError(t, err) + + // Non-automation secrets + secrets, _ := kubeclient.CoreV1().Secrets(ns).List(context.Background(), metav1.ListOptions{}) + require.ElementsMatch(t, []string{fqdnManual1, fqdnManual2}, getSecretNames(secrets)) + assertSecret := func(fqdn string) { + secret := getSecretByName(fqdn, secrets) + assert.Equal(t, map[string]string{kube.RadixAppLabel: appName, kube.RadixExternalAliasFQDNLabel: fqdn}, secret.Labels) + assert.Equal(t, corev1.SecretTypeTLS, secret.Type) + assert.Equal(t, map[string][]byte{corev1.TLSCertKey: nil, corev1.TLSPrivateKeyKey: nil}, secret.Data) + } + assertSecret(fqdnManual1) + assertSecret(fqdnManual2) + + // Certificate + certs, _ := certClient.CertmanagerV1().Certificates(ns).List(context.Background(), metav1.ListOptions{}) + require.ElementsMatch(t, []string{fqdnAutomation1, fqdnAutomation2}, slice.Map(certs.Items, func(c cmv1.Certificate) string { return c.Name })) + assertCert := func(fqdn string) { + cert, _ := slice.FindFirst(certs.Items, func(c cmv1.Certificate) bool { return c.Name == fqdn }) + assert.Equal(t, map[string]string{kube.RadixAppLabel: appName, kube.RadixExternalAliasFQDNLabel: fqdn}, cert.Labels) + assert.Empty(t, cert.OwnerReferences) + expectedCertSpec := cmv1.CertificateSpec{ + DNSNames: []string{fqdn}, + Duration: &metav1.Duration{Duration: testConfig.CertificateAutomation.Duration}, + RenewBefore: &metav1.Duration{Duration: testConfig.CertificateAutomation.RenewBefore}, + IssuerRef: v1.ObjectReference{ + Name: testConfig.CertificateAutomation.ClusterIssuer, + Kind: "ClusterIssuer", + Group: "cert-manager.io", + }, + SecretName: fqdn, + SecretTemplate: &cmv1.CertificateSecretTemplate{ + Labels: map[string]string{kube.RadixAppLabel: appName, kube.RadixExternalAliasFQDNLabel: fqdn}, + }, + } + assert.Equal(t, expectedCertSpec, cert.Spec) + } + assertCert(fqdnAutomation1) + assertCert(fqdnAutomation2) + + // Roles + roles, _ := kubeclient.RbacV1().Roles(ns).List(context.Background(), metav1.ListOptions{}) + require.Subset(t, getRoleNames(roles), []string{"radix-app-externaldns-adm", "radix-app-externaldns-reader"}) + expectedAdmRules := []rbacv1.PolicyRule{{Verbs: []string{"get", "list", "watch", "update", "patch", "delete"}, APIGroups: []string{""}, Resources: []string{"secrets"}, ResourceNames: []string{fqdnManual1, fqdnManual2}}} + assert.Equal(t, expectedAdmRules, getRoleByName("radix-app-externaldns-adm", roles).Rules) + expectedReaderRules := []rbacv1.PolicyRule{{Verbs: []string{"get", "list", "watch"}, Resources: []string{"secrets"}, APIGroups: []string{""}, ResourceNames: []string{fqdnManual1, fqdnManual2}}} + assert.Equal(t, expectedReaderRules, getRoleByName("radix-app-externaldns-reader", roles).Rules) + + // RoleBindings + roleBindings, _ := kubeclient.RbacV1().RoleBindings(ns).List(context.Background(), metav1.ListOptions{}) + require.Subset(t, getRoleBindingNames(roleBindings), []string{"radix-app-externaldns-adm", "radix-app-externaldns-reader"}) + assert.Equal(t, rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Name: "radix-app-externaldns-adm", Kind: "Role"}, getRoleBindingByName("radix-app-externaldns-adm", roleBindings).RoleRef) + assert.ElementsMatch(t, + slice.Map(adminGroups, func(group string) rbacv1.Subject { + return rbacv1.Subject{ + Kind: "Group", APIGroup: rbacv1.GroupName, Name: group, Namespace: "", + } + }), + getRoleBindingByName("radix-app-externaldns-adm", roleBindings).Subjects, + ) + assert.Equal(t, rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Name: "radix-app-externaldns-reader", Kind: "Role"}, getRoleBindingByName("radix-app-externaldns-reader", roleBindings).RoleRef) + assert.ElementsMatch(t, + slice.Map(readerGroups, func(group string) rbacv1.Subject { + return rbacv1.Subject{ + Kind: "Group", APIGroup: rbacv1.GroupName, Name: group, Namespace: "", + } + }), + getRoleBindingByName("radix-app-externaldns-reader", roleBindings).Subjects, + ) +} + +func Test_ExternalDNS_RetainSecretData(t *testing.T) { + appName, envName, fqdn := "anyapp", "anyenv", "app1.example.com" + keyData, certData := "keydata", "certdata" + ns := utils.GetEnvironmentNamespace(appName, envName) + + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + defer teardownTest() + + rdBuilder := utils.ARadixDeployment(). + WithDeploymentName("rd-init"). + WithAppName(appName). + WithEnvironment(envName). + WithComponents(utils.NewDeployComponentBuilder().WithName("comp"). + WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: false})). + WithJobComponents() + + // Init deployment and set TLS secret data + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rdBuilder) + require.NoError(t, err) + tlsSecret, err := kubeclient.CoreV1().Secrets(ns).Get(context.Background(), fqdn, metav1.GetOptions{}) + require.NoError(t, err) + tlsSecret.Data[corev1.TLSCertKey] = []byte(certData) + tlsSecret.Data[corev1.TLSPrivateKeyKey] = []byte(keyData) + _, err = kubeclient.CoreV1().Secrets(ns).Update(context.Background(), tlsSecret, metav1.UpdateOptions{}) + require.NoError(t, err) + + type testSpec struct { + componentName string + useAutomation bool + } + + tests := []testSpec{ + {componentName: "comp", useAutomation: true}, + {componentName: "comp", useAutomation: false}, + {componentName: "comp2", useAutomation: true}, + {componentName: "comp3", useAutomation: false}, + } + expectedTlsData := map[string][]byte{corev1.TLSCertKey: []byte(certData), corev1.TLSPrivateKeyKey: []byte(keyData)} + + for i, test := range tests { + t.Run(fmt.Sprintf("test %v", i), func(t *testing.T) { + rdBuilder. + WithDeploymentName(fmt.Sprintf("rd%v", i)). + WithComponents(utils.NewDeployComponentBuilder().WithName(test.componentName). + WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: test.useAutomation}), + ) + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rdBuilder) + require.NoError(t, err) + tlsSecret, err := kubeclient.CoreV1().Secrets(ns).Get(context.Background(), fqdn, metav1.GetOptions{}) + require.NoError(t, err) + assert.Equal(t, expectedTlsData, tlsSecret.Data) + _, err = certClient.CertmanagerV1().Certificates(ns).Get(context.Background(), fqdn, metav1.GetOptions{}) + if test.useAutomation { + assert.NoError(t, err) + } else { + assert.True(t, kubeerrors.IsNotFound(err)) + } + }) + } +} + +func Test_ExternalDNS_CertificateDurationAndRenewBefore_MinValue(t *testing.T) { + fqdn := "any.example.com" + _, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + defer teardownTest() + rr := utils.NewRegistrationBuilder().WithName("app").BuildRR() + rd := utils.NewDeploymentBuilder().WithAppName("app").WithEnvironment("dev").WithComponent( + utils.NewDeployComponentBuilder().WithName("comp").WithPublicPort("http").WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: true}), + ).BuildRD() + _, err := radixclient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + require.NoError(t, err) + _, err = radixclient.RadixV1().RadixDeployments("app-dev").Create(context.Background(), rd, metav1.CreateOptions{}) + require.NoError(t, err) + + // Duration and RenewBefore not below min values + cfg := &config.Config{ + CertificateAutomation: certificateconfig.AutomationConfig{ + ClusterIssuer: "anyissuer", + Duration: 10000 * time.Hour, + RenewBefore: 1000 * time.Hour, + }} + + syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, nil, nil, cfg) + require.NoError(t, syncer.OnSync()) + cert, _ := certClient.CertmanagerV1().Certificates("app-dev").Get(context.Background(), fqdn, metav1.GetOptions{}) + assert.Equal(t, cfg.CertificateAutomation.Duration, cert.Spec.Duration.Duration) + assert.Equal(t, cfg.CertificateAutomation.RenewBefore, cert.Spec.RenewBefore.Duration) + + // Duration below min value + cfg = &config.Config{ + CertificateAutomation: certificateconfig.AutomationConfig{ + ClusterIssuer: "anyissuer", + Duration: 2159 * time.Hour, + RenewBefore: 1000 * time.Hour, + }} + + syncer = NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, nil, nil, cfg) + require.NoError(t, syncer.OnSync()) + cert, _ = certClient.CertmanagerV1().Certificates("app-dev").Get(context.Background(), fqdn, metav1.GetOptions{}) + assert.Equal(t, 2160*time.Hour, cert.Spec.Duration.Duration) + assert.Equal(t, cfg.CertificateAutomation.RenewBefore, cert.Spec.RenewBefore.Duration) + + // RenewBefore below min value + cfg = &config.Config{ + CertificateAutomation: certificateconfig.AutomationConfig{ + ClusterIssuer: "anyissuer", + Duration: 10000 * time.Hour, + RenewBefore: 359 * time.Hour, + }} + + syncer = NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, nil, nil, cfg) + require.NoError(t, syncer.OnSync()) + cert, _ = certClient.CertmanagerV1().Certificates("app-dev").Get(context.Background(), fqdn, metav1.GetOptions{}) + assert.Equal(t, cfg.CertificateAutomation.Duration, cert.Spec.Duration.Duration) + assert.Equal(t, 360*time.Hour, cert.Spec.RenewBefore.Duration) +} + +func Test_ExternalDNS_ClusterIssuerNotSet(t *testing.T) { + fqdn := "any.example.com" + _, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + defer teardownTest() + rr := utils.NewRegistrationBuilder().WithName("app").BuildRR() + rd := utils.NewDeploymentBuilder().WithAppName("app").WithEnvironment("dev").WithComponent( + utils.NewDeployComponentBuilder().WithName("comp").WithPublicPort("http").WithExternalDNS(radixv1.RadixDeployExternalDNS{FQDN: fqdn, UseCertificateAutomation: true}), + ).BuildRD() + _, err := radixclient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + require.NoError(t, err) + _, err = radixclient.RadixV1().RadixDeployments("app-dev").Create(context.Background(), rd, metav1.CreateOptions{}) + require.NoError(t, err) + + // Duration and RenewBefore not below min values + cfg := &config.Config{ + CertificateAutomation: certificateconfig.AutomationConfig{ + Duration: 10000 * time.Hour, + RenewBefore: 1000 * time.Hour, + }} + + syncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rr, rd, nil, nil, cfg) + assert.ErrorContains(t, syncer.OnSync(), "cluster issuer not set in certificate automation config") +} + +func Test_ExternalDNS_GarbageCollectResourceNoLongerInSpec(t *testing.T) { + appName, envName := "anyapp", "anyenv" + ns := utils.GetEnvironmentNamespace(appName, envName) + + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + defer teardownTest() + + rdBuilder := utils.ARadixDeployment(). + WithDeploymentName("rd1"). + WithAppName(appName). + WithEnvironment(envName). + WithComponents( + utils.NewDeployComponentBuilder().WithName("comp").WithExternalDNS( + radixv1.RadixDeployExternalDNS{FQDN: "app1.example.com", UseCertificateAutomation: false}, + radixv1.RadixDeployExternalDNS{FQDN: "app2.example.com", UseCertificateAutomation: false}, + radixv1.RadixDeployExternalDNS{FQDN: "app3.example.com", UseCertificateAutomation: true}, + radixv1.RadixDeployExternalDNS{FQDN: "app4.example.com", UseCertificateAutomation: true}, + ), + ). + WithJobComponents() + _, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rdBuilder) + require.NoError(t, err) + + certs, _ := certClient.CertmanagerV1().Certificates(ns).List(context.Background(), metav1.ListOptions{}) + require.Len(t, certs.Items, 2) + // Simulate that cert-manager creates Certificate secrets + for _, cert := range certs.Items { + _, err := kubeclient.CoreV1().Secrets(ns).Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: cert.Spec.SecretName, Labels: cert.Spec.SecretTemplate.Labels}, + Type: corev1.SecretTypeTLS, + }, metav1.CreateOptions{}) + require.NoError(t, err) + } + + secrets, _ := kubeclient.CoreV1().Secrets(ns).List(context.Background(), metav1.ListOptions{}) + require.ElementsMatch(t, []string{"app1.example.com", "app2.example.com", "app3.example.com", "app4.example.com"}, getSecretNames(secrets)) + + // Remove app2 and app4 should remove respective TLS secrets and app4 certificate + rdBuilder.WithDeploymentName("rd2").WithComponents( + utils.NewDeployComponentBuilder().WithName("comp").WithExternalDNS( + radixv1.RadixDeployExternalDNS{FQDN: "app1.example.com", UseCertificateAutomation: false}, + radixv1.RadixDeployExternalDNS{FQDN: "app3.example.com", UseCertificateAutomation: true}, + ), + ) + _, err = applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rdBuilder) + require.NoError(t, err) + certs, _ = certClient.CertmanagerV1().Certificates(ns).List(context.Background(), metav1.ListOptions{}) + require.Len(t, certs.Items, 1) + assert.Equal(t, "app3.example.com", certs.Items[0].Name) + secrets, _ = kubeclient.CoreV1().Secrets(ns).List(context.Background(), metav1.ListOptions{}) + require.ElementsMatch(t, []string{"app1.example.com", "app3.example.com"}, getSecretNames(secrets)) +} + func parseQuantity(value string) resource.Quantity { q, _ := resource.ParseQuantity(value) return q } func applyDeploymentWithSyncForTestEnv(testEnv *testEnvProps, deploymentBuilder utils.DeploymentBuilder) (*radixv1.RadixDeployment, error) { - return applyDeploymentWithSync(testEnv.testUtil, testEnv.kubeclient, testEnv.kubeUtil, testEnv.radixclient, testEnv.prometheusclient, deploymentBuilder) + return applyDeploymentWithSync(testEnv.testUtil, testEnv.kubeclient, testEnv.kubeUtil, testEnv.radixclient, testEnv.prometheusclient, testEnv.certClient, deploymentBuilder) } func applyDeploymentWithSync(tu *test.Utils, kubeclient kubernetes.Interface, kubeUtil *kube.Kube, - radixclient radixclient.Interface, prometheusclient prometheusclient.Interface, deploymentBuilder utils.DeploymentBuilder) (*radixv1.RadixDeployment, error) { - return applyDeploymentWithModifiedSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, deploymentBuilder, func(syncer DeploymentSyncer) {}) + radixclient radixclient.Interface, prometheusclient prometheusclient.Interface, certClient *certfake.Clientset, deploymentBuilder utils.DeploymentBuilder) (*radixv1.RadixDeployment, error) { + return applyDeploymentWithModifiedSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, deploymentBuilder, func(syncer DeploymentSyncer) {}) } func applyDeploymentWithModifiedSync(tu *test.Utils, kubeclient kubernetes.Interface, kubeUtil *kube.Kube, - radixclient radixclient.Interface, prometheusclient prometheusclient.Interface, deploymentBuilder utils.DeploymentBuilder, modifySyncer func(syncer DeploymentSyncer)) (*radixv1.RadixDeployment, error) { + radixclient radixclient.Interface, prometheusclient prometheusclient.Interface, certClient *certfake.Clientset, deploymentBuilder utils.DeploymentBuilder, modifySyncer func(syncer DeploymentSyncer)) (*radixv1.RadixDeployment, error) { rd, err := tu.ApplyDeployment(deploymentBuilder) if err != nil { @@ -4146,7 +4294,7 @@ func applyDeploymentWithModifiedSync(tu *test.Utils, kubeclient kubernetes.Inter return nil, err } - deploymentSyncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, radixRegistration, rd /*testTenantId, testKubernetesApiPort, testRadixDeploymentHistoryLimit,*/, nil, nil, &testConfig) + deploymentSyncer := NewDeploymentSyncer(kubeclient, kubeUtil, radixclient, prometheusclient, certClient, radixRegistration, rd, nil, nil, &testConfig) modifySyncer(deploymentSyncer) err = deploymentSyncer.OnSync() if err != nil { @@ -4158,7 +4306,7 @@ func applyDeploymentWithModifiedSync(tu *test.Utils, kubeclient kubernetes.Inter } func applyDeploymentUpdateWithSync(tu *test.Utils, client kubernetes.Interface, kubeUtil *kube.Kube, - radixclient radixclient.Interface, prometheusclient prometheusclient.Interface, deploymentBuilder utils.DeploymentBuilder) error { + radixclient radixclient.Interface, prometheusclient prometheusclient.Interface, certClient *certfake.Clientset, deploymentBuilder utils.DeploymentBuilder) error { rd, err := tu.ApplyDeploymentUpdate(deploymentBuilder) if err != nil { return err @@ -4169,7 +4317,7 @@ func applyDeploymentUpdateWithSync(tu *test.Utils, client kubernetes.Interface, return err } - deployment := NewDeploymentSyncer(client, kubeUtil, radixclient, prometheusclient, radixRegistration, rd /*testTenantId, testKubernetesApiPort, testRadixDeploymentHistoryLimit,*/, nil, nil, &testConfig) + deployment := NewDeploymentSyncer(client, kubeUtil, radixclient, prometheusclient, certClient, radixRegistration, rd, nil, nil, &testConfig) err = deployment.OnSync() if err != nil { return err @@ -4346,6 +4494,13 @@ func secretByNameExists(name string, secrets *corev1.SecretList) bool { return getSecretByName(name, secrets) != nil } +func getSecretNames(secrets *corev1.SecretList) []string { + if secrets == nil { + return nil + } + return slice.Map(secrets.Items, func(s corev1.Secret) string { return s.Name }) +} + func getRoleBindingByName(name string, roleBindings *rbacv1.RoleBindingList) *rbacv1.RoleBinding { for _, roleBinding := range roleBindings.Items { if roleBinding.Name == name { diff --git a/pkg/apis/deployment/deploymentfactory.go b/pkg/apis/deployment/deploymentfactory.go index 6fb24b040..7862d58af 100644 --- a/pkg/apis/deployment/deploymentfactory.go +++ b/pkg/apis/deployment/deploymentfactory.go @@ -1,6 +1,7 @@ package deployment import ( + certclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" "github.com/equinor/radix-operator/pkg/apis/config" "github.com/equinor/radix-operator/pkg/apis/ingress" "github.com/equinor/radix-operator/pkg/apis/kube" @@ -17,6 +18,7 @@ type DeploymentSyncerFactoryFunc func( kubeutil *kube.Kube, radixclient radixclient.Interface, prometheusperatorclient monitoring.Interface, + certClient certclient.Interface, registration *v1.RadixRegistration, radixDeployment *v1.RadixDeployment, ingressAnnotationProviders []ingress.AnnotationProvider, @@ -29,13 +31,14 @@ func (f DeploymentSyncerFactoryFunc) CreateDeploymentSyncer( kubeutil *kube.Kube, radixclient radixclient.Interface, prometheusperatorclient monitoring.Interface, + certClient certclient.Interface, registration *v1.RadixRegistration, radixDeployment *v1.RadixDeployment, ingressAnnotationProviders []ingress.AnnotationProvider, auxResourceManagers []AuxiliaryResourceManager, config *config.Config, ) DeploymentSyncer { - return f(kubeclient, kubeutil, radixclient, prometheusperatorclient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config) + return f(kubeclient, kubeutil, radixclient, prometheusperatorclient, certClient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config) } // DeploymentSyncerFactory defines a factory to create a DeploymentSyncer @@ -45,6 +48,7 @@ type DeploymentSyncerFactory interface { kubeutil *kube.Kube, radixclient radixclient.Interface, prometheusperatorclient monitoring.Interface, + certClient certclient.Interface, registration *v1.RadixRegistration, radixDeployment *v1.RadixDeployment, ingressAnnotationProviders []ingress.AnnotationProvider, diff --git a/pkg/apis/deployment/deploymentfactory_mock.go b/pkg/apis/deployment/deploymentfactory_mock.go index fdf3f780c..0e29be034 100644 --- a/pkg/apis/deployment/deploymentfactory_mock.go +++ b/pkg/apis/deployment/deploymentfactory_mock.go @@ -7,13 +7,14 @@ package deployment import ( reflect "reflect" + versioned "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" config "github.com/equinor/radix-operator/pkg/apis/config" ingress "github.com/equinor/radix-operator/pkg/apis/ingress" kube "github.com/equinor/radix-operator/pkg/apis/kube" v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" - versioned "github.com/equinor/radix-operator/pkg/client/clientset/versioned" + versioned0 "github.com/equinor/radix-operator/pkg/client/clientset/versioned" gomock "github.com/golang/mock/gomock" - versioned0 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" + versioned1 "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" kubernetes "k8s.io/client-go/kubernetes" ) @@ -41,15 +42,15 @@ func (m *MockDeploymentSyncerFactory) EXPECT() *MockDeploymentSyncerFactoryMockR } // CreateDeploymentSyncer mocks base method. -func (m *MockDeploymentSyncerFactory) CreateDeploymentSyncer(kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient versioned.Interface, prometheusperatorclient versioned0.Interface, registration *v1.RadixRegistration, radixDeployment *v1.RadixDeployment, ingressAnnotationProviders []ingress.AnnotationProvider, auxResourceManagers []AuxiliaryResourceManager, config *config.Config) DeploymentSyncer { +func (m *MockDeploymentSyncerFactory) CreateDeploymentSyncer(kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient versioned0.Interface, prometheusperatorclient versioned1.Interface, certClient versioned.Interface, registration *v1.RadixRegistration, radixDeployment *v1.RadixDeployment, ingressAnnotationProviders []ingress.AnnotationProvider, auxResourceManagers []AuxiliaryResourceManager, config *config.Config) DeploymentSyncer { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateDeploymentSyncer", kubeclient, kubeutil, radixclient, prometheusperatorclient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config) + ret := m.ctrl.Call(m, "CreateDeploymentSyncer", kubeclient, kubeutil, radixclient, prometheusperatorclient, certClient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config) ret0, _ := ret[0].(DeploymentSyncer) return ret0 } // CreateDeploymentSyncer indicates an expected call of CreateDeploymentSyncer. -func (mr *MockDeploymentSyncerFactoryMockRecorder) CreateDeploymentSyncer(kubeclient, kubeutil, radixclient, prometheusperatorclient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config interface{}) *gomock.Call { +func (mr *MockDeploymentSyncerFactoryMockRecorder) CreateDeploymentSyncer(kubeclient, kubeutil, radixclient, prometheusperatorclient, certClient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDeploymentSyncer", reflect.TypeOf((*MockDeploymentSyncerFactory)(nil).CreateDeploymentSyncer), kubeclient, kubeutil, radixclient, prometheusperatorclient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDeploymentSyncer", reflect.TypeOf((*MockDeploymentSyncerFactory)(nil).CreateDeploymentSyncer), kubeclient, kubeutil, radixclient, prometheusperatorclient, certClient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config) } diff --git a/pkg/apis/deployment/environmentvariables_test.go b/pkg/apis/deployment/environmentvariables_test.go index a2f2b5e79..5a5c065eb 100644 --- a/pkg/apis/deployment/environmentvariables_test.go +++ b/pkg/apis/deployment/environmentvariables_test.go @@ -4,6 +4,7 @@ import ( "strings" "testing" + certfake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake" "github.com/equinor/radix-operator/pkg/apis/kube" v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" "github.com/equinor/radix-operator/pkg/apis/test" @@ -23,6 +24,7 @@ type testEnvProps struct { radixclient radixclient.Interface secretproviderclient secretProviderClient.Interface prometheusclient prometheusclient.Interface + certClient *certfake.Clientset kubeUtil *kube.Kube testUtil *test.Utils } @@ -421,7 +423,7 @@ func (testEnv *testEnvProps) applyRdComponent(t *testing.T, appName string, envN WithEmptyStatus(). WithComponents(componentBuilder) - rd, err := applyDeploymentWithSync(testEnv.testUtil, testEnv.kubeclient, testEnv.kubeUtil, testEnv.radixclient, testEnv.prometheusclient, radixDeployBuilder) + rd, err := applyDeploymentWithSync(testEnv.testUtil, testEnv.kubeclient, testEnv.kubeUtil, testEnv.radixclient, testEnv.prometheusclient, testEnv.certClient, radixDeployBuilder) assert.NoError(t, err) return rd } @@ -436,13 +438,13 @@ func (testEnv *testEnvProps) applyRdJobComponent(t *testing.T, appName string, e WithEmptyStatus(). WithJobComponents(jobBuilder) - rd, err := applyDeploymentWithSync(testEnv.testUtil, testEnv.kubeclient, testEnv.kubeUtil, testEnv.radixclient, testEnv.prometheusclient, radixDeployBuilder) + rd, err := applyDeploymentWithSync(testEnv.testUtil, testEnv.kubeclient, testEnv.kubeUtil, testEnv.radixclient, testEnv.prometheusclient, testEnv.certClient, radixDeployBuilder) assert.NoError(t, err) return rd } func setupTestEnv(t *testing.T) *testEnvProps { testEnv := testEnvProps{} - testEnv.testUtil, testEnv.kubeclient, testEnv.kubeUtil, testEnv.radixclient, testEnv.prometheusclient, testEnv.secretproviderclient = setupTest(t) + testEnv.testUtil, testEnv.kubeclient, testEnv.kubeUtil, testEnv.radixclient, testEnv.prometheusclient, testEnv.secretproviderclient, testEnv.certClient = setupTest(t) return &testEnv } diff --git a/pkg/apis/deployment/externaldns.go b/pkg/apis/deployment/externaldns.go new file mode 100644 index 000000000..3638c6137 --- /dev/null +++ b/pkg/apis/deployment/externaldns.go @@ -0,0 +1,246 @@ +package deployment + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + cm "github.com/cert-manager/cert-manager/pkg/apis/certmanager" + cmv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" + cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + "github.com/equinor/radix-common/utils/slice" + "github.com/equinor/radix-operator/pkg/apis/kube" + radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + "github.com/equinor/radix-operator/pkg/apis/utils" + radixlabels "github.com/equinor/radix-operator/pkg/apis/utils/labels" + v1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/strategicpatch" +) + +const ( + minCertDuration = 2160 * time.Hour + minCertRenewBefore = 360 * time.Hour +) + +func (deploy *Deployment) syncExternalDnsResources() error { + if err := deploy.garbageCollectExternalDnsResourcesNoLongerInSpec(); err != nil { + return err + } + + var secretNames []string + externalDnsList := deploy.getExternalDnsFromAllComponents() + + for _, externalDns := range externalDnsList { + if externalDns.UseCertificateAutomation { + if err := deploy.createOrUpdateExternalDnsCertificate(externalDns); err != nil { + return err + } + } else { + if err := deploy.garbageCollectExternalDnsCertificate(externalDns); err != nil { + return err + } + + secretName := utils.GetExternalDnsTlsSecretName(externalDns) + if err := deploy.createOrUpdateExternalDnsTlsSecret(externalDns, secretName); err != nil { + return err + } + secretNames = append(secretNames, secretName) + } + } + + return deploy.grantAccessToExternalDnsSecrets(secretNames) +} + +func (deploy *Deployment) garbageCollectExternalDnsResourcesNoLongerInSpec() error { + if err := deploy.garbageCollectExternalDnsCertificatesNoLongerInSpec(); err != nil { + return err + } + + return deploy.garbageCollectExternalDnsSecretsNoLongerInSpec() +} + +func (deploy *Deployment) garbageCollectExternalDnsSecretsNoLongerInSpec() error { + selector := radixlabels.ForApplicationName(deploy.registration.Name).AsSelector() + secrets, err := deploy.kubeclient.CoreV1().Secrets(deploy.radixDeployment.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return err + } + + externalDnsAliases := deploy.getExternalDnsFromAllComponents() + for _, secret := range secrets.Items { + fqdn, ok := secret.Labels[kube.RadixExternalAliasFQDNLabel] + if !ok { + continue + } + + if slice.Any(externalDnsAliases, func(rded radixv1.RadixDeployExternalDNS) bool { return rded.FQDN == fqdn }) { + continue + } + + if err := deploy.deleteSecret(&secret); err != nil { + return nil + } + } + return nil +} + +func (deploy *Deployment) garbageCollectExternalDnsCertificatesNoLongerInSpec() error { + selector := radixlabels.ForApplicationName(deploy.registration.Name).AsSelector() + certificates, err := deploy.certClient.CertmanagerV1().Certificates(deploy.radixDeployment.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return err + } + + externalDnsAliases := deploy.getExternalDnsFromAllComponents() + for _, cert := range certificates.Items { + fqdn, ok := cert.Labels[kube.RadixExternalAliasFQDNLabel] + if !ok { + continue + } + + if slice.Any(externalDnsAliases, func(rded radixv1.RadixDeployExternalDNS) bool { return rded.FQDN == fqdn }) { + continue + } + + if err := deploy.certClient.CertmanagerV1().Certificates(cert.Namespace).Delete(context.TODO(), cert.Name, metav1.DeleteOptions{}); err != nil { + return nil + } + } + return nil +} + +func (deploy *Deployment) garbageCollectExternalDnsCertificate(externalDns radixv1.RadixDeployExternalDNS) error { + selector := radixlabels.ForExternalDNSCertificate(deploy.registration.Name, externalDns).AsSelector() + certs, err := deploy.certClient.CertmanagerV1().Certificates(deploy.radixDeployment.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return err + } + + for _, cert := range certs.Items { + if err := deploy.certClient.CertmanagerV1().Certificates(cert.Namespace).Delete(context.TODO(), cert.Name, metav1.DeleteOptions{}); err != nil { + return err + } + } + + return nil +} + +func (deploy *Deployment) createOrUpdateExternalDnsCertificate(externalDns radixv1.RadixDeployExternalDNS) error { + if len(deploy.config.CertificateAutomation.ClusterIssuer) == 0 { + return errors.New("cluster issuer not set in certificate automation config") + } + + duration := deploy.config.CertificateAutomation.Duration + if duration < minCertDuration { + duration = minCertDuration + } + renewBefore := deploy.config.CertificateAutomation.RenewBefore + if renewBefore < minCertRenewBefore { + renewBefore = minCertRenewBefore + } + + certificate := &cmv1.Certificate{ + ObjectMeta: metav1.ObjectMeta{ + Name: externalDns.FQDN, + Namespace: deploy.radixDeployment.Namespace, + Labels: radixlabels.ForExternalDNSCertificate(deploy.registration.Name, externalDns), + }, + Spec: cmv1.CertificateSpec{ + DNSNames: []string{externalDns.FQDN}, + IssuerRef: cmmeta.ObjectReference{ + Group: cm.GroupName, + Kind: cmv1.ClusterIssuerKind, + Name: deploy.config.CertificateAutomation.ClusterIssuer, + }, + Duration: &metav1.Duration{Duration: duration}, + RenewBefore: &metav1.Duration{Duration: renewBefore}, + SecretName: utils.GetExternalDnsTlsSecretName(externalDns), + SecretTemplate: &cmv1.CertificateSecretTemplate{ + Labels: radixlabels.ForExternalDNSTLSSecret(deploy.registration.Name, externalDns), + }, + }, + } + + return deploy.applyCertificate(certificate) +} + +func (deploy *Deployment) applyCertificate(cert *cmv1.Certificate) error { + existingCert, err := deploy.certClient.CertmanagerV1().Certificates(cert.Namespace).Get(context.TODO(), cert.Name, metav1.GetOptions{}) + if err != nil { + if k8serrors.IsNotFound(err) { + _, err = deploy.certClient.CertmanagerV1().Certificates(cert.Namespace).Create(context.TODO(), cert, metav1.CreateOptions{}) + return err + } + return err + } + + newCert := existingCert.DeepCopy() + newCert.ObjectMeta.Labels = cert.ObjectMeta.Labels + newCert.ObjectMeta.Annotations = cert.ObjectMeta.Annotations + newCert.ObjectMeta.OwnerReferences = cert.ObjectMeta.OwnerReferences + newCert.Spec = cert.Spec + + exitingCertBytes, err := json.Marshal(existingCert) + if err != nil { + return fmt.Errorf("failed to marshal existing certificate %s/%s: %w", existingCert.Namespace, existingCert.Name, err) + } + + newCertBytes, err := json.Marshal(newCert) + if err != nil { + return fmt.Errorf("failed to marshal new certificate %s/%s: %w", newCert.Namespace, newCert.Name, err) + } + + patchBytes, err := strategicpatch.CreateTwoWayMergePatch(exitingCertBytes, newCertBytes, &cmv1.Certificate{}) + if err != nil { + return fmt.Errorf("failed to create two way merge patch for certificate %s/%s: %w", newCert.Namespace, newCert.Name, err) + } + + if !kube.IsEmptyPatch(patchBytes) { + _, err = deploy.certClient.CertmanagerV1().Certificates(newCert.Namespace).Update(context.TODO(), newCert, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("failed to update certificate %s/%s: %w", newCert.Namespace, newCert.Name, err) + } + } + + return nil +} + +func (deploy *Deployment) createOrUpdateExternalDnsTlsSecret(externalDns radixv1.RadixDeployExternalDNS, secretName string) error { + ns := deploy.radixDeployment.Namespace + secret := v1.Secret{ + Type: v1.SecretTypeTLS, + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Labels: radixlabels.ForExternalDNSTLSSecret(deploy.registration.Name, externalDns), + }, + Data: tlsSecretDefaultData(), + } + + existingSecret, err := deploy.kubeclient.CoreV1().Secrets(ns).Get(context.TODO(), secretName, metav1.GetOptions{}) + if err == nil { + secret.Data = existingSecret.Data + } else if !k8serrors.IsNotFound(err) { + return err + } + + _, err = deploy.kubeutil.ApplySecret(ns, &secret) + if err != nil { + return err + } + + return nil +} + +func (deploy *Deployment) getExternalDnsFromAllComponents() []radixv1.RadixDeployExternalDNS { + var externalDns []radixv1.RadixDeployExternalDNS + + for _, comp := range deploy.radixDeployment.Spec.Components { + externalDns = append(externalDns, comp.GetExternalDNS()...) + } + + return externalDns +} diff --git a/pkg/apis/deployment/hpa_test.go b/pkg/apis/deployment/hpa_test.go index 77a1bb9dd..b9be28101 100644 --- a/pkg/apis/deployment/hpa_test.go +++ b/pkg/apis/deployment/hpa_test.go @@ -12,7 +12,7 @@ import ( ) func TestHpa_DefaultConfigurationDoesNotHaveMemoryScaling(t *testing.T) { - tu, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) rrBuilder := utils.ARadixRegistration().WithName("someapp") raBuilder := utils.ARadixApplication(). WithRadixRegistration(rrBuilder) @@ -37,7 +37,7 @@ func TestHpa_DefaultConfigurationDoesNotHaveMemoryScaling(t *testing.T) { WithComponents(utils.NewDeployComponentBuilder(). WithHorizontalScaling(numbers.Int32Ptr(1), 3, testcase.cpuTarget, testcase.memoryTarget)) - rd, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, rdBuilder) + rd, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, rdBuilder) assert.NoError(t, err) hpa, err := kubeclient.AutoscalingV2().HorizontalPodAutoscalers(rd.GetNamespace()).Get(context.TODO(), rd.Spec.Components[0].GetName(), metav1.GetOptions{}) assert.NoError(t, err) diff --git a/pkg/apis/deployment/ingress.go b/pkg/apis/deployment/ingress.go index 4b331eb28..474d0c80f 100644 --- a/pkg/apis/deployment/ingress.go +++ b/pkg/apis/deployment/ingress.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "os" - "strconv" "strings" "github.com/equinor/radix-common/utils/slice" @@ -12,8 +11,8 @@ import ( "github.com/equinor/radix-operator/pkg/apis/ingress" "github.com/equinor/radix-operator/pkg/apis/kube" radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + "github.com/equinor/radix-operator/pkg/apis/utils" networkingv1 "k8s.io/api/networking/v1" - kubeerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -118,32 +117,6 @@ func (deploy *Deployment) createOrUpdateExternalDNSIngresses(deployComponent rad return err } - if existingIngress, err := deploy.kubeclient.NetworkingV1().Ingresses(namespace).Get(context.TODO(), ingress.Name, metav1.GetOptions{}); err == nil { - // Perform updated and deletions of ingress and TLS secret when we change automation flag. - // cert-manager does not cleanup its resources (certificates, orders etc) by simply clearing ingress annotation. The ingress must be deleted for this to happen. - existingIngressUseAutomation, expectedIngressUseAutomation := useCertificateAutomationForExternalDNS(existingIngress), useCertificateAutomationForExternalDNS(ingress) - if expectedIngressUseAutomation != existingIngressUseAutomation { - if expectedIngressUseAutomation { - // Delete existing TLS secret if the ingress should use automation since cert-manager will handle the secret lifecycle. - if err := deploy.deleteTLSSecretForIngress(existingIngress); err != nil { - return err - } - } else { - // Clear secret data (key + cert) generated by cert-manager automation when we disable automation. - // Labels for the existing secret is updated by method createOrUpdateSecrets - if err := deploy.clearTLSSecretDataForIngress(existingIngress); err != nil { - return err - } - } - - if err := deploy.kubeclient.NetworkingV1().Ingresses(existingIngress.Namespace).Delete(context.TODO(), existingIngress.Name, metav1.DeleteOptions{}); err != nil && !kubeerrors.IsNotFound(err) { - return err - } - } - } else if !kubeerrors.IsNotFound(err) { - return err - } - err = deploy.kubeutil.ApplyIngress(namespace, ingress) if err != nil { return err @@ -216,7 +189,7 @@ func (deploy *Deployment) garbageCollectIngressNoLongerInSpecForComponentAndExte } func (deploy *Deployment) garbageCollectIngressForComponentAndExternalAlias(component radixv1.RadixCommonDeployComponent, all bool) error { - labelSelector := getLabelSelectorForExternalAlias(component) + labelSelector := getLabelSelectorForExternalAliasIngress(component) ingresses, err := deploy.kubeutil.ListIngressesWithSelector(deploy.radixDeployment.GetNamespace(), labelSelector) if err != nil { return err @@ -235,12 +208,6 @@ func (deploy *Deployment) garbageCollectIngressForComponentAndExternalAlias(comp } if garbageCollectIngress { - // Delete TLS secrets created by cert-manager - if useCertificateAutomationForExternalDNS(ingress) { - if err := deploy.deleteTLSSecretForIngress(ingress); err != nil { - return err - } - } err = deploy.kubeclient.NetworkingV1().Ingresses(deploy.radixDeployment.GetNamespace()).Delete(context.TODO(), ingress.Name, metav1.DeleteOptions{}) if err != nil { return err @@ -251,6 +218,10 @@ func (deploy *Deployment) garbageCollectIngressForComponentAndExternalAlias(comp return nil } +func getLabelSelectorForExternalAliasIngress(component radixv1.RadixCommonDeployComponent) string { + return fmt.Sprintf("%s=%s, %s=%s", kube.RadixComponentLabel, component.GetName(), kube.RadixExternalAliasLabel, "true") +} + func (deploy *Deployment) getAppAliasIngressConfig(appName string, ownerReference []metav1.OwnerReference, component radixv1.RadixCommonDeployComponent, namespace string, publicPortNumber int32) (*networkingv1.Ingress, error) { appAlias := os.Getenv(defaults.OperatorAppAliasBaseURLEnvironmentVariable) // .app.dev.radix.equinor.com in launch.json if appAlias == "" { @@ -320,9 +291,8 @@ func (deploy *Deployment) getExternalAliasIngressConfig( namespace string, publicPortNumber int32, ) (*networkingv1.Ingress, error) { - ingressSpec := ingress.GetIngressSpec(externalAlias.FQDN, component.GetName(), externalAlias.FQDN, publicPortNumber) - annotationProviders := append(deploy.ingressAnnotationProviders, ingress.NewExternalDNSAnnotationProvider(externalAlias.UseCertificateAutomation, deploy.config.CertificateAutomation)) - ingressConfig, err := ingress.GetIngressConfig(namespace, appName, component, externalAlias.FQDN, ingressSpec, annotationProviders, ownerReference) + ingressSpec := ingress.GetIngressSpec(externalAlias.FQDN, component.GetName(), utils.GetExternalDnsTlsSecretName(externalAlias), publicPortNumber) + ingressConfig, err := ingress.GetIngressConfig(namespace, appName, component, externalAlias.FQDN, ingressSpec, deploy.ingressAnnotationProviders, ownerReference) if err != nil { return nil, err } @@ -330,34 +300,6 @@ func (deploy *Deployment) getExternalAliasIngressConfig( return ingressConfig, err } -func (deploy *Deployment) deleteTLSSecretForIngress(ing *networkingv1.Ingress) error { - for _, tls := range ing.Spec.TLS { - if len(tls.SecretName) > 0 { - if err := deploy.kubeclient.CoreV1().Secrets(ing.Namespace).Delete(context.TODO(), tls.SecretName, metav1.DeleteOptions{}); err != nil && !kubeerrors.IsNotFound(err) { - return err - } - } - } - - return nil -} - -func (deploy *Deployment) clearTLSSecretDataForIngress(ing *networkingv1.Ingress) error { - for _, tls := range ing.Spec.TLS { - if len(tls.SecretName) > 0 { - if secret, err := deploy.kubeclient.CoreV1().Secrets(ing.Namespace).Get(context.TODO(), tls.SecretName, metav1.GetOptions{}); err == nil { - secret.Data = tlsSecretDefaultData() - _, err = deploy.kubeclient.CoreV1().Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}) - return err - } else if !kubeerrors.IsNotFound(err) { - return err - } - } - } - - return nil -} - func getAppAliasIngressName(appName string) string { return fmt.Sprintf("%s-url-alias", appName) } @@ -383,14 +325,6 @@ func getHostName(componentName, namespace, clustername, dnsZone string) string { return fmt.Sprintf(hostnameTemplate, componentName, namespace, clustername, dnsZone) } -func useCertificateAutomationForExternalDNS(ing *networkingv1.Ingress) bool { - if boolStr, ok := ing.Annotations[kube.RadixExternalDNSUseCertificateAutomationAnnotation]; ok { - b, _ := strconv.ParseBool(boolStr) - return b - } - return false -} - func getPublicPortForComponent(deployComponent radixv1.RadixCommonDeployComponent) int32 { if deployComponent.GetPublicPort() == "" { // For backwards compatibility diff --git a/pkg/apis/deployment/kubedeployment_test.go b/pkg/apis/deployment/kubedeployment_test.go index 8e1ff70f3..10905bec7 100644 --- a/pkg/apis/deployment/kubedeployment_test.go +++ b/pkg/apis/deployment/kubedeployment_test.go @@ -291,8 +291,8 @@ func Test_UpdateResourcesInDeployment(t *testing.T) { } func applyDeploymentWithSyncWithComponentResources(t *testing.T, origRequests, origLimits map[string]string) Deployment { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) - rd, _ := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) + rd, _ := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithComponents(utils.NewDeployComponentBuilder(). WithName("comp1"). diff --git a/pkg/apis/deployment/pdb_test.go b/pkg/apis/deployment/pdb_test.go index 6a42f4f3a..61114654b 100644 --- a/pkg/apis/deployment/pdb_test.go +++ b/pkg/apis/deployment/pdb_test.go @@ -14,7 +14,7 @@ import ( ) func TestHorizontalScaleChangePDB(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -23,7 +23,7 @@ func TestHorizontalScaleChangePDB(t *testing.T) { componentThreeName := "componentThreeName" // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -61,7 +61,7 @@ func TestHorizontalScaleChangePDB(t *testing.T) { assert.Equal(t, int32(1), pdbs.Items[0].Spec.MinAvailable.IntVal) // Remove components - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -88,7 +88,7 @@ func TestHorizontalScaleChangePDB(t *testing.T) { } func TestObjectSynced_MultiComponentToOneComponent_HandlesPdbChange(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -97,7 +97,7 @@ func TestObjectSynced_MultiComponentToOneComponent_HandlesPdbChange(t *testing.T componentThreeName := "componentThreeName" // Test - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -134,7 +134,7 @@ func TestObjectSynced_MultiComponentToOneComponent_HandlesPdbChange(t *testing.T assert.Equal(t, int32(1), pdbs.Items[0].Spec.MinAvailable.IntVal) // Remove components - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -161,7 +161,7 @@ func TestObjectSynced_MultiComponentToOneComponent_HandlesPdbChange(t *testing.T } func TestObjectSynced_ScalingReplicas_HandlesChange(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -170,7 +170,7 @@ func TestObjectSynced_ScalingReplicas_HandlesChange(t *testing.T) { envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironmentName) // Define one component with >1 replicas and one component with <2 replicas - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -197,7 +197,7 @@ func TestObjectSynced_ScalingReplicas_HandlesChange(t *testing.T) { assert.Equal(t, int32(1), pdbs.Items[0].Spec.MinAvailable.IntVal) // Define two components with <2 replicas - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -222,7 +222,7 @@ func TestObjectSynced_ScalingReplicas_HandlesChange(t *testing.T) { assert.Equal(t, 0, len(pdbs.Items)) // Define one component with >1 replicas and one component with <2 replicas - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -250,7 +250,7 @@ func TestObjectSynced_ScalingReplicas_HandlesChange(t *testing.T) { assert.Equal(t, int32(1), pdbs.Items[0].Spec.MinAvailable.IntVal) // Delete component with >1 replicas. Expect PDBs to be removed - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -271,7 +271,7 @@ func TestObjectSynced_ScalingReplicas_HandlesChange(t *testing.T) { componentThreeName := "componentThreeName" // Set 3 components with >1 replicas. Expect 3 PDBs - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -306,7 +306,7 @@ func TestObjectSynced_ScalingReplicas_HandlesChange(t *testing.T) { } func TestObjectSynced_HorizontalScalingReplicas_HandlesChange(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -315,7 +315,7 @@ func TestObjectSynced_HorizontalScalingReplicas_HandlesChange(t *testing.T) { envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironmentName) // Define one component with >1 replicas and one component with <2 replicas - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -343,7 +343,7 @@ func TestObjectSynced_HorizontalScalingReplicas_HandlesChange(t *testing.T) { assert.Equal(t, int32(1), pdbs.Items[0].Spec.MinAvailable.IntVal) // Define two components with <2 replicas - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -370,7 +370,7 @@ func TestObjectSynced_HorizontalScalingReplicas_HandlesChange(t *testing.T) { } func TestObjectSynced_UpdatePdb_HandlesChange(t *testing.T) { - tu, client, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, client, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) defer teardownTest() anyAppName := "anyappname" anyEnvironmentName := "test" @@ -378,7 +378,7 @@ func TestObjectSynced_UpdatePdb_HandlesChange(t *testing.T) { envNamespace := utils.GetEnvironmentNamespace(anyAppName, anyEnvironmentName) // Define a component with >1 replicas - _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err := applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). @@ -408,7 +408,7 @@ func TestObjectSynced_UpdatePdb_HandlesChange(t *testing.T) { assert.Equal(t, "wrong", pdbWithWrongLabels.ObjectMeta.Labels[kube.RadixComponentLabel]) assert.Equal(t, "wrong", pdbWithWrongLabels.Spec.Selector.MatchLabels[kube.RadixComponentLabel]) - _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + _, err = applyDeploymentWithSync(tu, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(anyAppName). WithEnvironment(anyEnvironmentName). WithJobComponents(). diff --git a/pkg/apis/deployment/rolebinding.go b/pkg/apis/deployment/rolebinding.go index 5efdc631c..36e62a004 100644 --- a/pkg/apis/deployment/rolebinding.go +++ b/pkg/apis/deployment/rolebinding.go @@ -17,6 +17,17 @@ func getComponentSecretRbaclabels(appName, componentName string) kubelabels.Set return labels.Merge(labels.ForApplicationName(appName), labels.ForComponentName(componentName)) } +func (deploy *Deployment) grantAccessToExternalDnsSecrets(secretNames []string) error { + adminRoleName := "radix-app-externaldns-adm" + readerRoleName := "radix-app-externaldns-reader" + + if err := deploy.grantAdminAccessToSecrets(adminRoleName, secretNames, nil); err != nil { + return err + } + + return deploy.grantReaderAccessToSecrets(readerRoleName, secretNames, nil) +} + func (deploy *Deployment) grantAccessToComponentRuntimeSecrets(component radixv1.RadixCommonDeployComponent, secretNames []string) error { if len(secretNames) == 0 { err := deploy.garbageCollectRoleBindingsNoLongerInSpecForComponent(component) @@ -32,36 +43,44 @@ func (deploy *Deployment) grantAccessToComponentRuntimeSecrets(component radixv1 return nil } - namespace, registration := deploy.radixDeployment.Namespace, deploy.registration - extraLabels := getComponentSecretRbaclabels(registration.Name, component.GetName()) - - // App admin role and rolebinding + extraLabels := getComponentSecretRbaclabels(deploy.registration.Name, component.GetName()) adminRoleName := fmt.Sprintf("radix-app-adm-%s", component.GetName()) + readerRoleName := fmt.Sprintf("radix-app-reader-%s", component.GetName()) + + if err := deploy.grantAdminAccessToSecrets(adminRoleName, secretNames, extraLabels); err != nil { + return err + } + + return deploy.grantReaderAccessToSecrets(readerRoleName, secretNames, extraLabels) +} + +func (deploy *Deployment) grantAdminAccessToSecrets(roleName string, secretNames []string, extraLabels map[string]string) error { + namespace, registration := deploy.radixDeployment.Namespace, deploy.registration adminGroups, err := utils.GetAdGroups(registration) if err != nil { return err } - adminRole := kube.CreateManageSecretRole(registration.Name, adminRoleName, secretNames, extraLabels) - adminRoleBinding := roleBindingAppSecrets(registration.Name, adminRole, adminGroups) + role := kube.CreateManageSecretRole(registration.Name, roleName, secretNames, extraLabels) + roleBinding := roleBindingAppSecrets(registration.Name, role, adminGroups) - // App reader role and rolebinding - readerRoleName := fmt.Sprintf("radix-app-reader-%s", component.GetName()) - readerRole := kube.CreateReadSecretRole(registration.Name, readerRoleName, secretNames, extraLabels) - readerRoleBinding := roleBindingAppSecrets(registration.Name, readerRole, registration.Spec.ReaderAdGroups) - - // Apply roles and rolebindings - for _, role := range []*rbacv1.Role{adminRole, readerRole} { - if err := deploy.kubeutil.ApplyRole(namespace, role); err != nil { - return err - } + if err := deploy.kubeutil.ApplyRole(namespace, role); err != nil { + return err } - for _, roleBinding := range []*rbacv1.RoleBinding{adminRoleBinding, readerRoleBinding} { - if err := deploy.kubeutil.ApplyRoleBinding(namespace, roleBinding); err != nil { - return err - } + + return deploy.kubeutil.ApplyRoleBinding(namespace, roleBinding) +} + +func (deploy *Deployment) grantReaderAccessToSecrets(roleName string, secretNames []string, extraLabels map[string]string) error { + namespace, registration := deploy.radixDeployment.Namespace, deploy.registration + + role := kube.CreateReadSecretRole(registration.Name, roleName, secretNames, extraLabels) + roleBinding := roleBindingAppSecrets(registration.Name, role, registration.Spec.ReaderAdGroups) + + if err := deploy.kubeutil.ApplyRole(namespace, role); err != nil { + return err } - return nil + return deploy.kubeutil.ApplyRoleBinding(namespace, roleBinding) } func (deploy *Deployment) garbageCollectRoleBindingsNoLongerInSpecForComponent(component radixv1.RadixCommonDeployComponent) error { diff --git a/pkg/apis/deployment/secrets.go b/pkg/apis/deployment/secrets.go index 53b51bf08..7fe4c38c8 100644 --- a/pkg/apis/deployment/secrets.go +++ b/pkg/apis/deployment/secrets.go @@ -3,7 +3,6 @@ package deployment import ( "context" "fmt" - "strconv" "strings" "github.com/equinor/radix-common/utils/slice" @@ -54,7 +53,7 @@ func (deploy *Deployment) createOrUpdateSecretsForComponent(component radixv1.Ra if len(component.GetSecrets()) > 0 { secretName := utils.GetComponentSecretName(component.GetName()) if !deploy.kubeutil.SecretExists(namespace, secretName) { - err := deploy.createOrUpdateSecret(namespace, deploy.registration.Name, component.GetName(), secretName, false) + err := deploy.createOrUpdateComponentSecret(namespace, deploy.registration.Name, component.GetName(), secretName) if err != nil { return err } @@ -68,36 +67,6 @@ func (deploy *Deployment) createOrUpdateSecretsForComponent(component radixv1.Ra secretsToManage = append(secretsToManage, secretName) } - dnsExternalAlias := component.GetExternalDNS() - if len(dnsExternalAlias) > 0 { - err := deploy.garbageCollectSecretsNoLongerInSpecForComponentAndExternalAlias(component) - if err != nil { - return err - } - - // Create secrets to hold TLS certificates - for _, externalAlias := range dnsExternalAlias { - // Cert manager will create the TLS secret. - // When witching from manual to automation, the secret is deleted by createOrUpdateExternalDNSIngresses in ingress.go - // When switching from automation to manual, the existing secret data cleared and updated by createOrUpdateExternalDNSIngresses - if externalAlias.UseCertificateAutomation { - continue - } - - secretsToManage = append(secretsToManage, externalAlias.FQDN) - - err := deploy.createOrUpdateSecret(namespace, deploy.registration.Name, component.GetName(), externalAlias.FQDN, true) - if err != nil { - return err - } - } - } else { - err := deploy.garbageCollectAllSecretsForComponentAndExternalAlias(component) - if err != nil { - return err - } - } - volumeMountSecretsToManage, err := deploy.createOrUpdateVolumeMountSecrets(namespace, component.GetName(), component.GetVolumeMounts()) if err != nil { return err @@ -132,7 +101,7 @@ func (deploy *Deployment) createOrUpdateSecretsForComponent(component radixv1.Ra err = deploy.grantAccessToComponentRuntimeSecrets(component, secretsToManage) if err != nil { - return fmt.Errorf("failed to grant app admin access to own secrets. %v", err) + return fmt.Errorf("failed to grant access to secrets. %v", err) } if len(secretsToManage) == 0 { @@ -254,53 +223,10 @@ func (deploy *Deployment) garbageCollectSecretsNoLongerInSpecForComponent(compon return nil } -func (deploy *Deployment) garbageCollectAllSecretsForComponentAndExternalAlias(component radixv1.RadixCommonDeployComponent) error { - return deploy.garbageCollectSecretsForComponentAndExternalAlias(component, true) -} - -func (deploy *Deployment) garbageCollectSecretsNoLongerInSpecForComponentAndExternalAlias(component radixv1.RadixCommonDeployComponent) error { - return deploy.garbageCollectSecretsForComponentAndExternalAlias(component, false) -} - -func (deploy *Deployment) garbageCollectSecretsForComponentAndExternalAlias(component radixv1.RadixCommonDeployComponent, all bool) error { - secrets, err := deploy.listSecretsForComponentExternalAlias(component) - if err != nil { - return err - } - - for _, secret := range secrets { - garbageCollectSecret := true - - dnsExternalAlias := component.GetExternalDNS() - if !all && len(dnsExternalAlias) > 0 { - externalAliasForSecret := secret.Name - for _, externalAlias := range dnsExternalAlias { - if externalAlias.FQDN == externalAliasForSecret { - garbageCollectSecret = false - } - } - } - - if garbageCollectSecret { - log.Debugf("Delete secret %s for component %s and external alias %s", secret.Name, component.GetName(), slice.Map(dnsExternalAlias, func(dns radixv1.RadixDeployExternalDNS) string { return dns.FQDN })) - err = deploy.deleteSecret(secret) - if err != nil { - return err - } - } - } - - return nil -} - func (deploy *Deployment) listSecretsForComponent(component radixv1.RadixCommonDeployComponent) ([]*v1.Secret, error) { return deploy.listSecrets(getLabelSelectorForComponent(component)) } -func (deploy *Deployment) listSecretsForComponentExternalAlias(component radixv1.RadixCommonDeployComponent) ([]*v1.Secret, error) { - return deploy.listSecrets(getLabelSelectorForExternalAlias(component)) -} - func (deploy *Deployment) listSecretsForVolumeMounts(component radixv1.RadixCommonDeployComponent) ([]*v1.Secret, error) { blobVolumeMountSecret := getLabelSelectorForBlobVolumeMountSecret(component) secrets, err := deploy.listSecrets(blobVolumeMountSecret) @@ -326,28 +252,20 @@ func (deploy *Deployment) listSecrets(labelSelector string) ([]*v1.Secret, error return secrets, err } -func (deploy *Deployment) createOrUpdateSecret(ns, app, component, secretName string, isExternalAlias bool) error { - secretType := v1.SecretTypeOpaque - if isExternalAlias { - secretType = v1.SecretTypeTLS - } +func (deploy *Deployment) createOrUpdateComponentSecret(ns, app, component, secretName string) error { secret := v1.Secret{ - Type: secretType, + Type: v1.SecretTypeOpaque, ObjectMeta: metav1.ObjectMeta{ Name: secretName, Labels: map[string]string{ kube.RadixAppLabel: app, kube.RadixComponentLabel: component, - kube.RadixExternalAliasLabel: strconv.FormatBool(isExternalAlias), + kube.RadixExternalAliasLabel: "false", }, }, } - if isExternalAlias { - secret.Data = tlsSecretDefaultData() - } - existingSecret, err := deploy.kubeclient.CoreV1().Secrets(ns).Get(context.TODO(), secretName, metav1.GetOptions{}) if err == nil { secret.Data = existingSecret.Data diff --git a/pkg/apis/deployment/secrets_test.go b/pkg/apis/deployment/secrets_test.go index fa42bf18f..49c157bab 100644 --- a/pkg/apis/deployment/secrets_test.go +++ b/pkg/apis/deployment/secrets_test.go @@ -5,6 +5,7 @@ import ( "os" "testing" + certfake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake" "github.com/equinor/radix-operator/pkg/apis/defaults" "github.com/equinor/radix-operator/pkg/apis/kube" v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" @@ -22,17 +23,18 @@ import ( secretproviderfake "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned/fake" ) -func setupSecretsTest(t *testing.T) (*test.Utils, kubernetes.Interface, *kube.Kube, radixclient.Interface, prometheusclient.Interface) { +func setupSecretsTest(t *testing.T) (*test.Utils, kubernetes.Interface, *kube.Kube, radixclient.Interface, prometheusclient.Interface, *certfake.Clientset) { // Setup kubeclient := kubefake.NewSimpleClientset() radixclient := radix.NewSimpleClientset() prometheusclient := prometheusfake.NewSimpleClientset() secretproviderclient := secretproviderfake.NewSimpleClientset() + certClient := certfake.NewSimpleClientset() kubeUtil, _ := kube.New(kubeclient, radixclient, secretproviderclient) handlerTestUtils := test.NewTestUtils(kubeclient, radixclient, secretproviderclient) err := handlerTestUtils.CreateClusterPrerequisites(testClusterName, testEgressIps, "anysubid") require.NoError(t, err) - return &handlerTestUtils, kubeclient, kubeUtil, radixclient, prometheusclient + return &handlerTestUtils, kubeclient, kubeUtil, radixclient, prometheusclient, certClient } func teardownSecretsTest() { @@ -48,13 +50,13 @@ func teardownSecretsTest() { func TestSecretDeployed_ClientCertificateSecretGetsSet(t *testing.T) { // Setup - testUtils, client, kubeUtil, radixclient, prometheusclient := setupSecretsTest(t) + testUtils, client, kubeUtil, radixclient, prometheusclient, certClient := setupSecretsTest(t) appName, environment := "edcradix", "test" componentName1, componentName2, componentName3, componentName4 := "component1", "component2", "component3", "component4" verificationOn, verificationOff := v1.VerificationTypeOn, v1.VerificationTypeOff // Test - radixDeployment, err := applyDeploymentWithSync(testUtils, client, kubeUtil, radixclient, prometheusclient, utils.ARadixDeployment(). + radixDeployment, err := applyDeploymentWithSync(testUtils, client, kubeUtil, radixclient, prometheusclient, certClient, utils.ARadixDeployment(). WithAppName(appName). WithEnvironment(environment). WithComponents( diff --git a/pkg/apis/deployment/volumemount_test.go b/pkg/apis/deployment/volumemount_test.go index e0e31a07a..8d15ccee2 100644 --- a/pkg/apis/deployment/volumemount_test.go +++ b/pkg/apis/deployment/volumemount_test.go @@ -1376,7 +1376,7 @@ func (suite *VolumeMountTestSuite) Test_CreateOrUpdateCsiAzureKeyVaultResources( func Test_EmptyDir(t *testing.T) { appName, envName, compName := "anyapp", "anyenv", "anycomp" - tu, kubeclient, kubeUtil, radixclient, prometheusclient, _ := setupTest(t) + tu, kubeclient, kubeUtil, radixclient, prometheusclient, _, certClient := setupTest(t) builder := utils.NewDeploymentBuilder(). WithRadixApplication(utils.NewRadixApplicationBuilder().WithAppName(appName).WithRadixRegistration(utils.NewRegistrationBuilder().WithName(appName))). WithAppName(appName). @@ -1388,7 +1388,7 @@ func Test_EmptyDir(t *testing.T) { ), ) - rd, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, builder) + rd, err := applyDeploymentWithSync(tu, kubeclient, kubeUtil, radixclient, prometheusclient, certClient, builder) require.NoError(t, err) assert.NotNil(t, rd) diff --git a/pkg/apis/ingress/ingressannotationprovider.go b/pkg/apis/ingress/ingressannotationprovider.go index e09964674..3bb307325 100644 --- a/pkg/apis/ingress/ingressannotationprovider.go +++ b/pkg/apis/ingress/ingressannotationprovider.go @@ -1,25 +1,15 @@ package ingress import ( - "errors" "fmt" - "strconv" "strings" - "time" - certificateconfig "github.com/equinor/radix-operator/pkg/apis/config/certificate" "github.com/equinor/radix-operator/pkg/apis/defaults" - "github.com/equinor/radix-operator/pkg/apis/kube" radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" "github.com/equinor/radix-operator/pkg/apis/utils" oauthutil "github.com/equinor/radix-operator/pkg/apis/utils/oauth" ) -const ( - minCertDuration = 2160 * time.Hour - minCertRenewBefore = 360 * time.Hour -) - type AnnotationProvider interface { GetAnnotations(component radixv1.RadixCommonDeployComponent, namespace string) (map[string]string, error) } @@ -138,38 +128,3 @@ func (provider *oauth2AnnotationProvider) GetAnnotations(component radixv1.Radix return annotations, nil } - -func NewExternalDNSAnnotationProvider(useCertificateAutomation bool, automationConfig certificateconfig.AutomationConfig) AnnotationProvider { - return &externalDNSAnnotationProvider{ - useCertificateAutomation: useCertificateAutomation, - automationConfig: automationConfig, - } -} - -type externalDNSAnnotationProvider struct { - useCertificateAutomation bool - automationConfig certificateconfig.AutomationConfig -} - -func (provider *externalDNSAnnotationProvider) GetAnnotations(_ radixv1.RadixCommonDeployComponent, _ string) (map[string]string, error) { - annotations := map[string]string{kube.RadixExternalDNSUseCertificateAutomationAnnotation: strconv.FormatBool(provider.useCertificateAutomation)} - - if provider.useCertificateAutomation { - if len(provider.automationConfig.ClusterIssuer) == 0 { - return nil, errors.New("cluster issuer not set in certificate automation config") - } - duration := provider.automationConfig.Duration - if duration < minCertDuration { - duration = minCertDuration - } - renewBefore := provider.automationConfig.RenewBefore - if renewBefore < minCertRenewBefore { - renewBefore = minCertRenewBefore - } - annotations["cert-manager.io/cluster-issuer"] = provider.automationConfig.ClusterIssuer - annotations["cert-manager.io/duration"] = duration.String() - annotations["cert-manager.io/renew-before"] = renewBefore.String() - } - - return annotations, nil -} diff --git a/pkg/apis/ingress/ingressannotationprovider_test.go b/pkg/apis/ingress/ingressannotationprovider_test.go index 9d3d9117a..25b9443ec 100644 --- a/pkg/apis/ingress/ingressannotationprovider_test.go +++ b/pkg/apis/ingress/ingressannotationprovider_test.go @@ -3,17 +3,13 @@ package ingress import ( "errors" "testing" - "time" maputils "github.com/equinor/radix-common/utils/maps" - certificateconfig "github.com/equinor/radix-operator/pkg/apis/config/certificate" "github.com/equinor/radix-operator/pkg/apis/defaults" - "github.com/equinor/radix-operator/pkg/apis/kube" radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" "github.com/equinor/radix-operator/pkg/apis/utils" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -218,78 +214,3 @@ func (s *OAuth2AnnotationsTestSuite) Test_OAuthConfig_ApplyTo_ReturnError() { s.Error(err) s.Nil(actual) } - -func Test_ExternalDNSAnnotationProvider(t *testing.T) { - t.Run("expected annotations when useAnnotation is false", func(t *testing.T) { - sut := NewExternalDNSAnnotationProvider(false, certificateconfig.AutomationConfig{}) - actualAnnotations, err := sut.GetAnnotations(nil, "") - require.NoError(t, err) - expectedAnnotations := map[string]string{kube.RadixExternalDNSUseCertificateAutomationAnnotation: "false"} - assert.Equal(t, expectedAnnotations, actualAnnotations) - }) - - t.Run("expected annotations when useAnnotation is false", func(t *testing.T) { - automationConfig := certificateconfig.AutomationConfig{ - ClusterIssuer: "anyclusterissuer", - Duration: 4000*time.Hour - 1, - RenewBefore: 1000 * time.Hour, - } - sut := NewExternalDNSAnnotationProvider(true, automationConfig) - actualAnnotations, err := sut.GetAnnotations(nil, "") - require.NoError(t, err) - expectedAnnotations := map[string]string{ - kube.RadixExternalDNSUseCertificateAutomationAnnotation: "true", - "cert-manager.io/cluster-issuer": automationConfig.ClusterIssuer, - "cert-manager.io/duration": automationConfig.Duration.String(), - "cert-manager.io/renew-before": automationConfig.RenewBefore.String(), - } - assert.Equal(t, expectedAnnotations, actualAnnotations) - }) - - t.Run("expected annotations when RenewBefore less than min value", func(t *testing.T) { - automationConfig := certificateconfig.AutomationConfig{ - ClusterIssuer: "anyclusterissuer", - Duration: 4000 * time.Hour, - RenewBefore: 360*time.Hour - 1, - } - sut := NewExternalDNSAnnotationProvider(true, automationConfig) - actualAnnotations, err := sut.GetAnnotations(nil, "") - require.NoError(t, err) - expectedAnnotations := map[string]string{ - kube.RadixExternalDNSUseCertificateAutomationAnnotation: "true", - "cert-manager.io/cluster-issuer": automationConfig.ClusterIssuer, - "cert-manager.io/duration": automationConfig.Duration.String(), - "cert-manager.io/renew-before": (360 * time.Hour).String(), - } - assert.Equal(t, expectedAnnotations, actualAnnotations) - }) - - t.Run("expected annotations when Duration less than min value", func(t *testing.T) { - automationConfig := certificateconfig.AutomationConfig{ - ClusterIssuer: "anyclusterissuer", - Duration: 2160*time.Hour - 1, - RenewBefore: 1000 * time.Hour, - } - sut := NewExternalDNSAnnotationProvider(true, automationConfig) - actualAnnotations, err := sut.GetAnnotations(nil, "") - require.NoError(t, err) - expectedAnnotations := map[string]string{ - kube.RadixExternalDNSUseCertificateAutomationAnnotation: "true", - "cert-manager.io/cluster-issuer": automationConfig.ClusterIssuer, - "cert-manager.io/duration": (2160 * time.Hour).String(), - "cert-manager.io/renew-before": automationConfig.RenewBefore.String(), - } - assert.Equal(t, expectedAnnotations, actualAnnotations) - }) - - t.Run("return error if cluster issuer is empty", func(t *testing.T) { - automationConfig := certificateconfig.AutomationConfig{ - ClusterIssuer: "", - Duration: 4000 * time.Hour, - RenewBefore: 1000 * time.Hour, - } - sut := NewExternalDNSAnnotationProvider(true, automationConfig) - _, err := sut.GetAnnotations(nil, "") - assert.ErrorContains(t, err, "cluster issuer not set in certificate automation config") - }) -} diff --git a/pkg/apis/kube/kube.go b/pkg/apis/kube/kube.go index 4f32a7939..02bb55cfd 100644 --- a/pkg/apis/kube/kube.go +++ b/pkg/apis/kube/kube.go @@ -17,17 +17,16 @@ import ( // Radix Annotations const ( - RadixBranchAnnotation = "radix-branch" - RadixGitTagsAnnotation = "radix.equinor.com/radix-git-tags" - RadixCommitAnnotation = "radix.equinor.com/radix-commit" - RadixConfigHash = "radix.equinor.com/radix-config-hash" - RadixBuildSecretHash = "radix.equinor.com/build-secret-hash" - RadixComponentImagesAnnotation = "radix-component-images" + RadixBranchAnnotation = "radix-branch" + RadixGitTagsAnnotation = "radix.equinor.com/radix-git-tags" + RadixCommitAnnotation = "radix.equinor.com/radix-commit" + RadixConfigHash = "radix.equinor.com/radix-config-hash" + RadixBuildSecretHash = "radix.equinor.com/build-secret-hash" + RadixComponentImagesAnnotation = "radix-component-images" RadixBuildComponentsAnnotation = "radix-build-component" - RadixDeploymentNameAnnotation = "radix-deployment-name" - RadixDeploymentPromotedFromDeploymentAnnotation = "radix.equinor.com/radix-deployment-promoted-from-deployment" - RadixDeploymentPromotedFromEnvironmentAnnotation = "radix.equinor.com/radix-deployment-promoted-from-environment" - RadixExternalDNSUseCertificateAutomationAnnotation = "radix.equinor.com/external-dns-use-certificate-automation" + RadixDeploymentNameAnnotation = "radix-deployment-name" + RadixDeploymentPromotedFromDeploymentAnnotation = "radix.equinor.com/radix-deployment-promoted-from-deployment" + RadixDeploymentPromotedFromEnvironmentAnnotation = "radix.equinor.com/radix-deployment-promoted-from-environment" // See https://github.com/equinor/radix-velero-plugin/blob/master/velero-plugins/deployment/restore.go RestoredStatusAnnotation = "equinor.com/velero-restored-status" ) @@ -64,6 +63,7 @@ const ( RadixActiveClusterAliasLabel = "radix-app-active-cluster-alias" RadixAppAliasLabel = "radix-app-alias" RadixExternalAliasLabel = "radix-app-external-alias" + RadixExternalAliasFQDNLabel = "radix-app-external-alias-fqdn" RadixAliasLabel = "radix-alias" RadixMountTypeLabel = "mount-type" RadixVolumeMountNameLabel = "radix-volume-mount-name" diff --git a/pkg/apis/utils/init.go b/pkg/apis/utils/init.go index 7f6d920fa..14c22e943 100644 --- a/pkg/apis/utils/init.go +++ b/pkg/apis/utils/init.go @@ -6,6 +6,7 @@ import ( "os" "time" + certclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned" monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" "github.com/prometheus/client_golang/prometheus" @@ -35,7 +36,7 @@ func WithKubernetesClientRateLimiter(rateLimiter flowcontrol.RateLimiter) Kubern } // GetKubernetesClient Gets clients to talk to the API -func GetKubernetesClient(configOptions ...KubernetesClientConfigOption) (kubernetes.Interface, radixclient.Interface, monitoring.Interface, secretProviderClient.Interface) { +func GetKubernetesClient(configOptions ...KubernetesClientConfigOption) (kubernetes.Interface, radixclient.Interface, monitoring.Interface, secretProviderClient.Interface, certclient.Interface) { ctx := context.Background() pollTimeout, pollInterval := time.Minute, 15*time.Second kubeConfigPath := os.Getenv("HOME") + "/.kube/config" @@ -82,7 +83,13 @@ func GetKubernetesClient(configOptions ...KubernetesClientConfigOption) (kuberne if err != nil { log.Fatalf("secretProvider secret provider client client: %v", err) } + certClient, err := PollUntilRESTClientSuccessfulConnection(ctx, pollTimeout, pollInterval, func() (*certclient.Clientset, error) { + return certclient.NewForConfig(config) + }) + if err != nil { + log.Fatalf("secretProvider secret provider client client: %v", err) + } log.Printf("Successfully constructed k8s client to API server %v", config.Host) - return client, radixClient, prometheusOperatorClient, secretProviderClient + return client, radixClient, prometheusOperatorClient, secretProviderClient, certClient } diff --git a/pkg/apis/utils/labels/labels.go b/pkg/apis/utils/labels/labels.go index 7f451dd13..7101adc2a 100644 --- a/pkg/apis/utils/labels/labels.go +++ b/pkg/apis/utils/labels/labels.go @@ -316,3 +316,19 @@ func ForDNSAliasRbac(appName string) kubelabels.Set { kube.RadixAliasLabel: "true", } } + +// ForExternalDNSTLSSecret returns labels for External DNS TLS secret +func ForExternalDNSTLSSecret(appName string, externalDns v1.RadixDeployExternalDNS) kubelabels.Set { + return kubelabels.Set{ + kube.RadixAppLabel: appName, + kube.RadixExternalAliasFQDNLabel: externalDns.FQDN, + } +} + +// ForExternalDNSCertificate returns labels for External DNS certificate +func ForExternalDNSCertificate(appName string, externalDns v1.RadixDeployExternalDNS) kubelabels.Set { + return kubelabels.Set{ + kube.RadixAppLabel: appName, + kube.RadixExternalAliasFQDNLabel: externalDns.FQDN, + } +} diff --git a/pkg/apis/utils/labels/labels_test.go b/pkg/apis/utils/labels/labels_test.go index 9b84d0282..d41db6c76 100644 --- a/pkg/apis/utils/labels/labels_test.go +++ b/pkg/apis/utils/labels/labels_test.go @@ -166,6 +166,18 @@ func Test_ForDNSAliasRbac(t *testing.T) { assert.Equal(t, expected, actual) } +func Test_ForExternalDNSTLSSecret(t *testing.T) { + actual := ForExternalDNSTLSSecret("any-app", v1.RadixDeployExternalDNS{FQDN: "test.com"}) + expected := kubelabels.Set{kube.RadixAppLabel: "any-app", kube.RadixExternalAliasFQDNLabel: "test.com"} + assert.Equal(t, expected, actual) +} + +func Test_ForExternalDNSCertificate(t *testing.T) { + actual := ForExternalDNSCertificate("any-app", v1.RadixDeployExternalDNS{FQDN: "test.com"}) + expected := kubelabels.Set{kube.RadixAppLabel: "any-app", kube.RadixExternalAliasFQDNLabel: "test.com"} + assert.Equal(t, expected, actual) +} + func Test_RequirementRadixBatchNameLabelExists(t *testing.T) { actual := requirementRadixBatchNameLabelExists() expected := kubelabels.Set{kube.RadixBatchNameLabel: "anyname"} diff --git a/pkg/apis/utils/secrets.go b/pkg/apis/utils/secrets.go index b18fb3765..5381a8818 100644 --- a/pkg/apis/utils/secrets.go +++ b/pkg/apis/utils/secrets.go @@ -18,6 +18,11 @@ func GetComponentSecretName(componentName string) string { return strings.ToLower(fmt.Sprintf("%s-%s", componentName, hash)) } +// GetExternalDnsTlsSecretName Get name of TLS secret for external DNS +func GetExternalDnsTlsSecretName(externalDns radixv1.RadixDeployExternalDNS) string { + return externalDns.FQDN +} + // GetComponentClientCertificateSecretName Gets name of the component secret that holds the ca.crt public key for client certificate authentication func GetComponentClientCertificateSecretName(componentame string) string { return strings.ToLower(fmt.Sprintf("%s-clientcertca", componentame)) diff --git a/radix-operator/deployment/controller_test.go b/radix-operator/deployment/controller_test.go index aaf5f4916..c7aeb8243 100644 --- a/radix-operator/deployment/controller_test.go +++ b/radix-operator/deployment/controller_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + certfake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake" "github.com/equinor/radix-operator/pkg/apis/config" "github.com/equinor/radix-operator/pkg/apis/defaults" "github.com/equinor/radix-operator/pkg/apis/kube" @@ -14,7 +15,6 @@ import ( radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned" fakeradix "github.com/equinor/radix-operator/pkg/client/clientset/versioned/fake" informers "github.com/equinor/radix-operator/pkg/client/informers/externalversions" - prometheusclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" prometheusfake "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/fake" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -22,13 +22,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubeinformers "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" + kubefake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/record" secretproviderfake "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned/fake" ) -func setupTest(t *testing.T) (*test.Utils, kubernetes.Interface, *kube.Kube, radixclient.Interface, prometheusclient.Interface) { - client := fake.NewSimpleClientset() +func setupTest(t *testing.T) (*test.Utils, *kubefake.Clientset, *kube.Kube, *fakeradix.Clientset, *prometheusfake.Clientset, *certfake.Clientset) { + client := kubefake.NewSimpleClientset() radixClient := fakeradix.NewSimpleClientset() secretproviderclient := secretproviderfake.NewSimpleClientset() kubeUtil, _ := kube.New(client, radixClient, secretproviderclient) @@ -36,7 +36,8 @@ func setupTest(t *testing.T) (*test.Utils, kubernetes.Interface, *kube.Kube, rad err := handlerTestUtils.CreateClusterPrerequisites("AnyClusterName", "0.0.0.0", "anysubid") require.NoError(t, err) prometheusClient := prometheusfake.NewSimpleClientset() - return &handlerTestUtils, client, kubeUtil, radixClient, prometheusClient + certClient := certfake.NewSimpleClientset() + return &handlerTestUtils, client, kubeUtil, radixClient, prometheusClient, certClient } func teardownTest() { @@ -57,7 +58,7 @@ func Test_Controller_Calls_Handler(t *testing.T) { defer close(synced) // Setup - tu, client, kubeUtil, radixClient, prometheusclient := setupTest(t) + tu, client, kubeUtil, radixClient, prometheusclient, certClient := setupTest(t) _, err := client.CoreV1().Namespaces().Create( ctx, @@ -81,6 +82,7 @@ func Test_Controller_Calls_Handler(t *testing.T) { kubeUtil, radixClient, prometheusclient, + certClient, &config.Config{}, WithHasSyncedCallback(func(syncedOk bool) { synced <- syncedOk }), ) diff --git a/radix-operator/deployment/handler.go b/radix-operator/deployment/handler.go index 32e0a0c0a..64db26e9f 100644 --- a/radix-operator/deployment/handler.go +++ b/radix-operator/deployment/handler.go @@ -14,6 +14,7 @@ import ( monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" log "github.com/sirupsen/logrus" + certclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -76,6 +77,7 @@ type Handler struct { kubeclient kubernetes.Interface radixclient radixclient.Interface prometheusperatorclient monitoring.Interface + certClient certclient.Interface kubeutil *kube.Kube hasSynced common.HasSynced oauth2DefaultConfig defaults.OAuth2Config @@ -90,6 +92,7 @@ func NewHandler(kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient radixclient.Interface, prometheusperatorclient monitoring.Interface, + certClient certclient.Interface, config *config.Config, options ...HandlerConfigOption) *Handler { @@ -97,6 +100,7 @@ func NewHandler(kubeclient kubernetes.Interface, kubeclient: kubeclient, radixclient: radixclient, prometheusperatorclient: prometheusperatorclient, + certClient: certClient, kubeutil: kubeutil, config: config, } @@ -150,7 +154,7 @@ func (t *Handler) Sync(namespace, name string, eventRecorder record.EventRecorde deployment.NewOAuthProxyResourceManager(syncRD, radixRegistration, t.kubeutil, t.oauth2DefaultConfig, ingress.GetAuxOAuthProxyAnnotationProviders(), t.oauth2ProxyDockerImage), } - deployment := t.deploymentSyncerFactory.CreateDeploymentSyncer(t.kubeclient, t.kubeutil, t.radixclient, t.prometheusperatorclient, radixRegistration, syncRD, ingressAnnotations, auxResourceManagers, t.config) + deployment := t.deploymentSyncerFactory.CreateDeploymentSyncer(t.kubeclient, t.kubeutil, t.radixclient, t.prometheusperatorclient, t.certClient, radixRegistration, syncRD, ingressAnnotations, auxResourceManagers, t.config) err = deployment.OnSync() if err != nil { // Put back on queue diff --git a/radix-operator/deployment/handler_test.go b/radix-operator/deployment/handler_test.go index d4f6e17d0..f3353bfd9 100644 --- a/radix-operator/deployment/handler_test.go +++ b/radix-operator/deployment/handler_test.go @@ -7,6 +7,7 @@ import ( "github.com/equinor/radix-operator/pkg/apis/ingress" secretproviderfake "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned/fake" + certfake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake" "github.com/equinor/radix-operator/pkg/apis/defaults" deployment "github.com/equinor/radix-operator/pkg/apis/deployment" "github.com/equinor/radix-operator/pkg/apis/kube" @@ -27,6 +28,7 @@ type handlerSuite struct { radixClient *fakeradix.Clientset secretProviderClient *secretproviderfake.Clientset promClient *prometheusfake.Clientset + certClient *certfake.Clientset kubeUtil *kube.Kube eventRecorder *record.FakeRecorder } @@ -41,22 +43,24 @@ func (s *handlerSuite) SetupTest() { s.secretProviderClient = secretproviderfake.NewSimpleClientset() s.kubeUtil, _ = kube.New(s.kubeClient, s.radixClient, s.secretProviderClient) s.promClient = prometheusfake.NewSimpleClientset() + s.certClient = certfake.NewSimpleClientset() s.eventRecorder = &record.FakeRecorder{} } func (s *handlerSuite) Test_NewHandler_DefaultValues() { - h := NewHandler(s.kubeClient, s.kubeUtil, s.radixClient, s.promClient, &config.Config{}) + h := NewHandler(s.kubeClient, s.kubeUtil, s.radixClient, s.promClient, s.certClient, &config.Config{}) s.Equal(s.kubeClient, h.kubeclient) s.Equal(s.kubeUtil, h.kubeutil) s.Equal(s.radixClient, h.radixclient) s.Equal(s.promClient, h.prometheusperatorclient) + s.Equal(s.certClient, h.certClient) s.NotNil(h.hasSynced) } func (s *handlerSuite) Test_NewHandler_ConfigOptionsCalled() { var called bool configFunc := func(h *Handler) { called = true } - NewHandler(s.kubeClient, s.kubeUtil, s.radixClient, s.promClient, &config.Config{}, configFunc) + NewHandler(s.kubeClient, s.kubeUtil, s.radixClient, s.promClient, s.certClient, &config.Config{}, configFunc) s.True(called) } @@ -101,7 +105,7 @@ func (s *handlerSuite) Test_Sync() { factory := deployment.NewMockDeploymentSyncerFactory(ctrl) factory. EXPECT(). - CreateDeploymentSyncer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + CreateDeploymentSyncer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Times(0) h := Handler{radixclient: s.radixClient, kubeutil: s.kubeUtil} err := h.Sync(namespace, nonExistingRdName, s.eventRecorder) @@ -113,7 +117,7 @@ func (s *handlerSuite) Test_Sync() { factory := deployment.NewMockDeploymentSyncerFactory(ctrl) factory. EXPECT(). - CreateDeploymentSyncer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + CreateDeploymentSyncer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Times(0) h := Handler{radixclient: s.radixClient, kubeutil: s.kubeUtil} err := h.Sync(namespace, inactiveRdName, s.eventRecorder) @@ -125,7 +129,7 @@ func (s *handlerSuite) Test_Sync() { factory := deployment.NewMockDeploymentSyncerFactory(ctrl) factory. EXPECT(). - CreateDeploymentSyncer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + CreateDeploymentSyncer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Times(0) h := Handler{radixclient: s.radixClient, kubeutil: s.kubeUtil} err := h.Sync(namespace, activeRdMissingRrName, s.eventRecorder) @@ -152,10 +156,10 @@ func (s *handlerSuite) Test_Sync() { expectedConfig := &config.Config{} factory. EXPECT(). - CreateDeploymentSyncer(s.kubeClient, s.kubeUtil, s.radixClient, s.promClient, rr, activeRd, gomock.Eq(expectedIngressAnnotations), gomock.Eq(expectedAuxResources), expectedConfig). + CreateDeploymentSyncer(s.kubeClient, s.kubeUtil, s.radixClient, s.promClient, s.certClient, rr, activeRd, gomock.Eq(expectedIngressAnnotations), gomock.Eq(expectedAuxResources), expectedConfig). Return(syncer). Times(1) - h := Handler{kubeclient: s.kubeClient, radixclient: s.radixClient, kubeutil: s.kubeUtil, prometheusperatorclient: s.promClient, deploymentSyncerFactory: factory, oauth2ProxyDockerImage: "oauth:123", oauth2DefaultConfig: oauthConfig, ingressConfiguration: ingressConfig, hasSynced: func(b bool) { callbackExecuted = b }, config: expectedConfig} + h := Handler{kubeclient: s.kubeClient, radixclient: s.radixClient, kubeutil: s.kubeUtil, prometheusperatorclient: s.promClient, certClient: s.certClient, deploymentSyncerFactory: factory, oauth2ProxyDockerImage: "oauth:123", oauth2DefaultConfig: oauthConfig, ingressConfiguration: ingressConfig, hasSynced: func(b bool) { callbackExecuted = b }, config: expectedConfig} err := h.Sync(namespace, activeRdName, s.eventRecorder) s.NoError(err) s.True(callbackExecuted) @@ -168,7 +172,7 @@ func (s *handlerSuite) Test_Sync() { factory := deployment.NewMockDeploymentSyncerFactory(ctrl) factory. EXPECT(). - CreateDeploymentSyncer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + CreateDeploymentSyncer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(syncer). Times(1) h := Handler{radixclient: s.radixClient, kubeutil: s.kubeUtil, deploymentSyncerFactory: factory} diff --git a/radix-operator/main.go b/radix-operator/main.go index ce38774a4..0eb916eeb 100644 --- a/radix-operator/main.go +++ b/radix-operator/main.go @@ -11,6 +11,7 @@ import ( "syscall" "time" + certclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" apiconfig "github.com/equinor/radix-operator/pkg/apis/config" dnsaliasconfig "github.com/equinor/radix-operator/pkg/apis/config/dnsalias" "github.com/equinor/radix-operator/pkg/apis/defaults" @@ -56,7 +57,7 @@ func main() { panic(err) } rateLimitConfig := utils.WithKubernetesClientRateLimiter(flowcontrol.NewTokenBucketRateLimiter(kubeClientRateLimitQPS, kubeClientRateLimitBurst)) - client, radixClient, prometheusOperatorClient, secretProviderClient := utils.GetKubernetesClient(rateLimitConfig) + client, radixClient, prometheusOperatorClient, secretProviderClient, certClient := utils.GetKubernetesClient(rateLimitConfig) activeClusterNameEnvVar := os.Getenv(defaults.ActiveClusternameEnvironmentVariable) logger.Printf("Active cluster name: %v", activeClusterNameEnvVar) @@ -86,7 +87,7 @@ func main() { startController(createRegistrationController(kubeUtil, kubeInformerFactory, radixInformerFactory, eventRecorder), registrationControllerThreads, stop) startController(createApplicationController(kubeUtil, kubeInformerFactory, radixInformerFactory, eventRecorder, cfg.DNSConfig), applicationControllerThreads, stop) startController(createEnvironmentController(kubeUtil, kubeInformerFactory, radixInformerFactory, eventRecorder), environmentControllerThreads, stop) - startController(createDeploymentController(kubeUtil, prometheusOperatorClient, kubeInformerFactory, radixInformerFactory, eventRecorder, oauthDefaultConfig, ingressConfiguration, cfg), deploymentControllerThreads, stop) + startController(createDeploymentController(kubeUtil, prometheusOperatorClient, certClient, kubeInformerFactory, radixInformerFactory, eventRecorder, oauthDefaultConfig, ingressConfiguration, cfg), deploymentControllerThreads, stop) startController(createJobController(kubeUtil, kubeInformerFactory, radixInformerFactory, eventRecorder, cfg), jobControllerThreads, stop) startController(createAlertController(kubeUtil, prometheusOperatorClient, kubeInformerFactory, radixInformerFactory, eventRecorder), alertControllerThreads, stop) startController(createBatchController(kubeUtil, kubeInformerFactory, radixInformerFactory, eventRecorder), 1, stop) @@ -211,7 +212,7 @@ func createDNSAliasesController(kubeUtil *kube.Kube, recorder) } -func createDeploymentController(kubeUtil *kube.Kube, prometheusOperatorClient monitoring.Interface, +func createDeploymentController(kubeUtil *kube.Kube, prometheusOperatorClient monitoring.Interface, certClient certclient.Interface, kubeInformerFactory kubeinformers.SharedInformerFactory, radixInformerFactory radixinformers.SharedInformerFactory, recorder record.EventRecorder, oauthDefaultConfig defaults.OAuth2Config, ingressConfiguration ingress.IngressConfiguration, config *apiconfig.Config) *common.Controller { @@ -224,6 +225,7 @@ func createDeploymentController(kubeUtil *kube.Kube, prometheusOperatorClient mo kubeUtil, kubeUtil.RadixClient(), prometheusOperatorClient, + certClient, config, deployment.WithOAuth2DefaultConfig(oauthDefaultConfig), deployment.WithIngressConfiguration(ingressConfiguration),