diff --git a/go.mod b/go.mod index e170dd223..d10830f9a 100644 --- a/go.mod +++ b/go.mod @@ -7,22 +7,22 @@ require ( 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 + github.com/google/uuid v1.6.0 github.com/pkg/errors v0.9.1 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.70.0 github.com/prometheus-operator/prometheus-operator/pkg/client v0.70.0 - github.com/prometheus/client_golang v1.18.0 + github.com/prometheus/client_golang v1.19.0 github.com/rs/zerolog v1.32.0 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 - github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.21.0 - golang.org/x/sync v0.5.0 + github.com/stretchr/testify v1.9.0 + golang.org/x/crypto v0.22.0 + golang.org/x/sync v0.7.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.29.0 - k8s.io/apiextensions-apiserver v0.29.0 - k8s.io/apimachinery v0.29.0 - k8s.io/client-go v0.29.0 + k8s.io/api v0.29.2 + k8s.io/apiextensions-apiserver v0.29.2 + k8s.io/apimachinery v0.29.2 + k8s.io/client-go v1.5.2 sigs.k8s.io/secrets-store-csi-driver v1.4.0 sigs.k8s.io/yaml v1.4.0 ) @@ -31,8 +31,8 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v5.7.0+incompatible // indirect + github.com/emicklei/go-restful/v3 v3.11.2 // indirect + github.com/evanphx/json-patch v5.8.1+incompatible // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect @@ -40,7 +40,7 @@ require ( 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/golang/protobuf v1.5.4 // 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 @@ -49,10 +49,11 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kedacore/keda/v2 v2.14.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -60,8 +61,8 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -69,14 +70,14 @@ 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.1 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/oauth2 v0.19.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect @@ -84,10 +85,10 @@ require ( 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/klog/v2 v2.120.1 // 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 + k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect + sigs.k8s.io/controller-runtime v0.17.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 86044ea02..f7d5896d5 100644 --- a/go.sum +++ b/go.sum @@ -14,10 +14,14 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= +github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/equinor/radix-common v1.7.1 h1:kl7Tuo2VEo2WHGm/vkvktrZ9t9S3Nht7Mob3CSIzcJI= github.com/equinor/radix-common v1.7.1/go.mod h1:M6mhgHtFQ3rnjJnyOuECXiZOh7XQ5xVeHMyCAU+YPzQ= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.8.1+incompatible h1:2toJaoe7/rNa1zpeQx0UnVEjqk6z2ecyA20V/zg8vTU= +github.com/evanphx/json-patch v5.8.1+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -44,6 +48,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -55,8 +60,10 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= 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/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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= @@ -67,6 +74,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kedacore/keda/v2 v2.14.0 h1:0vxF1cEbEcAVWwbSHzgmanA43Hnnz5oGZZPN9yC7/rg= +github.com/kedacore/keda/v2 v2.14.0/go.mod h1:Gk8Bm9uiiQcUwhS31Aib72y+9K4LvBaMPZuA1n3kKR8= 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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -82,6 +91,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -96,8 +106,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -111,10 +123,16 @@ github.com/prometheus-operator/prometheus-operator/pkg/client v0.70.0 h1:PpdpJDS github.com/prometheus-operator/prometheus-operator/pkg/client v0.70.0/go.mod h1:4I5Rt6iIu95JBYYaDYA+Er+YBfUwIq9Pwh5TEoBmawg= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= 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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -144,12 +162,14 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS 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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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.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/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -164,8 +184,12 @@ 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.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= 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/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= 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= @@ -179,8 +203,11 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -188,6 +215,7 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -202,10 +230,13 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -247,20 +278,33 @@ 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= k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= +k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= +k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= +k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg= +k8s.io/apiextensions-apiserver v0.29.2/go.mod h1:aLfYjpA5p3OwtqNXQFkhJ56TB+spV8Gc4wfMhUA3/b8= k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= +k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= +k8s.io/client-go v1.5.2 h1:JOxmv4FxrCIOS54kAABbN8/hA9jqGpns+Zc6soNgd8U= +k8s.io/client-go v1.5.2/go.mod h1:OmM68YRko3DQ0sjlnWxzjQF9lcSLHJXuGMTo23rc7wI= 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/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 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= +k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY= +k8s.io/utils v0.0.0-20240310230437-4693a0247e57/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/controller-runtime v0.17.3 h1:65QmN7r3FWgTxDMz9fvGnO1kbf2nu+acg9p2R9oYYYk= +sigs.k8s.io/controller-runtime v0.17.3/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY= 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= diff --git a/pkg/apis/deployment/deployment.go b/pkg/apis/deployment/deployment.go index 04a96d58a..cb61a43a5 100644 --- a/pkg/apis/deployment/deployment.go +++ b/pkg/apis/deployment/deployment.go @@ -18,6 +18,7 @@ import ( "github.com/equinor/radix-operator/pkg/apis/metrics" v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned" + kedav2 "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned" monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" "github.com/rs/zerolog/log" appsv1 "k8s.io/api/apps/v1" @@ -47,6 +48,7 @@ type Deployment struct { kubeutil *kube.Kube prometheusperatorclient monitoring.Interface certClient certclient.Interface + kedaClient kedav2.Interface registration *v1.RadixRegistration radixDeployment *v1.RadixDeployment auxResourceManagers []AuxiliaryResourceManager @@ -58,11 +60,12 @@ type Deployment struct { var _ DeploymentSyncerFactory = DeploymentSyncerFactoryFunc(NewDeploymentSyncer) // NewDeploymentSyncer Constructor -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 { +func NewDeploymentSyncer(kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient radixclient.Interface, kedaClient kedav2.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, + kedaClient: kedaClient, prometheusperatorclient: prometheusperatorclient, certClient: certClient, registration: registration, @@ -391,7 +394,12 @@ func (deploy *Deployment) garbageCollectComponentsNoLongerInSpec(ctx context.Con return err } - err = deploy.garbageCollectHPAsNoLongerInSpec(ctx) + err = deploy.garbageCollectDeprecatedHPAs(ctx) + if err != nil { + return err + } + + err = deploy.garbageCollectScalersNoLongerInSpec(ctx) if err != nil { return err } @@ -537,7 +545,7 @@ func (deploy *Deployment) syncDeploymentForRadixComponent(ctx context.Context, c return fmt.Errorf("failed to create deployment: %w", err) } - err = deploy.createOrUpdateHPA(ctx, component) + err = deploy.createOrUpdateScaleObject(ctx, component) if err != nil { return fmt.Errorf("failed to create hpa: %w", err) } diff --git a/pkg/apis/deployment/deploymentfactory.go b/pkg/apis/deployment/deploymentfactory.go index 7862d58af..0c9d7e506 100644 --- a/pkg/apis/deployment/deploymentfactory.go +++ b/pkg/apis/deployment/deploymentfactory.go @@ -7,6 +7,7 @@ import ( "github.com/equinor/radix-operator/pkg/apis/kube" v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned" + kedav2 "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned" monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" "k8s.io/client-go/kubernetes" ) @@ -17,6 +18,7 @@ type DeploymentSyncerFactoryFunc func( kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient radixclient.Interface, + kedaClient kedav2.Interface, prometheusperatorclient monitoring.Interface, certClient certclient.Interface, registration *v1.RadixRegistration, @@ -30,6 +32,7 @@ func (f DeploymentSyncerFactoryFunc) CreateDeploymentSyncer( kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient radixclient.Interface, + kedaClient kedav2.Interface, prometheusperatorclient monitoring.Interface, certClient certclient.Interface, registration *v1.RadixRegistration, @@ -38,7 +41,7 @@ func (f DeploymentSyncerFactoryFunc) CreateDeploymentSyncer( auxResourceManagers []AuxiliaryResourceManager, config *config.Config, ) DeploymentSyncer { - return f(kubeclient, kubeutil, radixclient, prometheusperatorclient, certClient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config) + return f(kubeclient, kubeutil, radixclient, kedaClient, prometheusperatorclient, certClient, registration, radixDeployment, ingressAnnotationProviders, auxResourceManagers, config) } // DeploymentSyncerFactory defines a factory to create a DeploymentSyncer @@ -47,6 +50,7 @@ type DeploymentSyncerFactory interface { kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient radixclient.Interface, + kedaClient kedav2.Interface, prometheusperatorclient monitoring.Interface, certClient certclient.Interface, registration *v1.RadixRegistration, diff --git a/pkg/apis/deployment/hpa.go b/pkg/apis/deployment/hpa.go deleted file mode 100644 index 056fe48ea..000000000 --- a/pkg/apis/deployment/hpa.go +++ /dev/null @@ -1,173 +0,0 @@ -package deployment - -import ( - "context" - "fmt" - - "github.com/equinor/radix-common/utils/numbers" - "github.com/equinor/radix-operator/pkg/apis/kube" - v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" - "github.com/rs/zerolog/log" - autoscalingv2 "k8s.io/api/autoscaling/v2" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const targetCPUUtilizationPercentage int32 = 80 - -func (deploy *Deployment) createOrUpdateHPA(ctx context.Context, deployComponent v1.RadixCommonDeployComponent) error { - namespace := deploy.radixDeployment.Namespace - componentName := deployComponent.GetName() - horizontalScaling := deployComponent.GetHorizontalScaling() - - // Check if hpa config exists - if horizontalScaling == nil { - log.Ctx(ctx).Debug().Msgf("Skip creating HorizontalPodAutoscaler %s in namespace %s: no HorizontalScaling config exists", componentName, namespace) - return nil - } - - if isComponentStopped(deployComponent) { - return nil - } - - var memoryTarget, cpuTarget *int32 - if horizontalScaling.RadixHorizontalScalingResources != nil { - if horizontalScaling.RadixHorizontalScalingResources.Memory != nil { - memoryTarget = horizontalScaling.RadixHorizontalScalingResources.Memory.AverageUtilization - } - - if horizontalScaling.RadixHorizontalScalingResources.Cpu != nil { - cpuTarget = horizontalScaling.RadixHorizontalScalingResources.Cpu.AverageUtilization - } - } - - if memoryTarget == nil && cpuTarget == nil { - cpuTarget = numbers.Int32Ptr(targetCPUUtilizationPercentage) - } - - hpa := deploy.getHPAConfig(componentName, horizontalScaling.MinReplicas, horizontalScaling.MaxReplicas, cpuTarget, memoryTarget) - - log.Ctx(ctx).Debug().Msgf("Creating HorizontalPodAutoscaler object %s in namespace %s", componentName, namespace) - createdHPA, err := deploy.kubeclient.AutoscalingV2().HorizontalPodAutoscalers(namespace).Create(ctx, hpa, metav1.CreateOptions{}) - if errors.IsAlreadyExists(err) { - log.Ctx(ctx).Debug().Msgf("HorizontalPodAutoscaler object %s already exists in namespace %s, updating the object now", componentName, namespace) - updatedHPA, err := deploy.kubeclient.AutoscalingV2().HorizontalPodAutoscalers(namespace).Update(ctx, hpa, metav1.UpdateOptions{}) - if err != nil { - return fmt.Errorf("failed to update HorizontalPodAutoscaler object: %v", err) - } - log.Ctx(ctx).Debug().Msgf("Updated HorizontalPodAutoscaler: %s in namespace %s", updatedHPA.Name, namespace) - return nil - } - if err != nil { - return fmt.Errorf("failed to create HorizontalPodAutoscaler object: %v", err) - } - log.Ctx(ctx).Debug().Msgf("Created HorizontalPodAutoscaler: %s in namespace %s", createdHPA.Name, namespace) - return nil -} - -func (deploy *Deployment) garbageCollectHPAsNoLongerInSpec(ctx context.Context) error { - namespace := deploy.radixDeployment.GetNamespace() - hpas, err := deploy.kubeclient.AutoscalingV2().HorizontalPodAutoscalers(namespace).List(ctx, metav1.ListOptions{}) - - if err != nil { - return err - } - - for _, hpa := range hpas.Items { - componentName, ok := RadixComponentNameFromComponentLabel(&hpa) - if !ok { - continue - } - - if !componentName.ExistInDeploymentSpecComponentList(deploy.radixDeployment) { - err = deploy.kubeclient.AutoscalingV2().HorizontalPodAutoscalers(namespace).Delete(ctx, hpa.Name, metav1.DeleteOptions{}) - if err != nil { - return err - } - } - } - - return nil -} - -func (deploy *Deployment) getHPAConfig(componentName string, minReplicas *int32, maxReplicas int32, cpuTarget *int32, memoryTarget *int32) *autoscalingv2.HorizontalPodAutoscaler { - appName := deploy.radixDeployment.Spec.AppName - ownerReference := []metav1.OwnerReference{ - getOwnerReferenceOfDeployment(deploy.radixDeployment), - } - - metrics := getHpaMetrics(cpuTarget, memoryTarget) - - hpa := &autoscalingv2.HorizontalPodAutoscaler{ - ObjectMeta: metav1.ObjectMeta{ - Name: componentName, - Labels: map[string]string{ - kube.RadixAppLabel: appName, - kube.RadixComponentLabel: componentName, - }, - OwnerReferences: ownerReference, - }, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ - MinReplicas: minReplicas, - MaxReplicas: maxReplicas, - Metrics: metrics, - ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ - Kind: "Deployment", - Name: componentName, - APIVersion: "apps/v1", - }, - }, - } - - return hpa -} - -func getHpaMetrics(cpuTarget *int32, memoryTarget *int32) []autoscalingv2.MetricSpec { - var metrics []autoscalingv2.MetricSpec - if cpuTarget != nil { - metrics = []autoscalingv2.MetricSpec{ - { - Type: autoscalingv2.ResourceMetricSourceType, - Resource: &autoscalingv2.ResourceMetricSource{ - Name: corev1.ResourceCPU, - Target: autoscalingv2.MetricTarget{ - Type: autoscalingv2.UtilizationMetricType, - AverageUtilization: cpuTarget, - }, - }, - }, - } - } - - if memoryTarget != nil { - metrics = append(metrics, autoscalingv2.MetricSpec{ - Type: autoscalingv2.ResourceMetricSourceType, - Resource: &autoscalingv2.ResourceMetricSource{ - Name: corev1.ResourceMemory, - Target: autoscalingv2.MetricTarget{ - Type: autoscalingv2.UtilizationMetricType, - AverageUtilization: memoryTarget, - }, - }, - }) - } - return metrics -} - -func (deploy *Deployment) deleteHPAIfExists(ctx context.Context, componentName string) error { - namespace := deploy.radixDeployment.GetNamespace() - _, err := deploy.kubeclient.AutoscalingV2().HorizontalPodAutoscalers(namespace).Get(ctx, componentName, metav1.GetOptions{}) - if err != nil && errors.IsNotFound(err) { - return nil - } - if err != nil { - return fmt.Errorf("failed to get hpa: %v", err) - } - err = deploy.kubeclient.AutoscalingV2().HorizontalPodAutoscalers(namespace).Delete(ctx, componentName, metav1.DeleteOptions{}) - if err != nil { - return fmt.Errorf("failed to delete existing hpa: %v", err) - } - - return nil -} diff --git a/pkg/apis/deployment/kubedeployment.go b/pkg/apis/deployment/kubedeployment.go index 6289d26de..df2a710cc 100644 --- a/pkg/apis/deployment/kubedeployment.go +++ b/pkg/apis/deployment/kubedeployment.go @@ -29,7 +29,7 @@ func (deploy *Deployment) createOrUpdateDeployment(ctx context.Context, deployCo // If component is stopped or HorizontalScaling is nil then delete hpa if exists before updating deployment if isComponentStopped(deployComponent) || deployComponent.GetHorizontalScaling() == nil { - err = deploy.deleteHPAIfExists(ctx, deployComponent.GetName()) + err = deploy.deleteScaledObjectIfExists(ctx, deployComponent.GetName()) if err != nil { return err } diff --git a/pkg/apis/deployment/scaleobject.go b/pkg/apis/deployment/scaleobject.go new file mode 100644 index 000000000..f3c32fdc1 --- /dev/null +++ b/pkg/apis/deployment/scaleobject.go @@ -0,0 +1,189 @@ +package deployment + +import ( + "context" + "fmt" + "strconv" + + "github.com/equinor/radix-common/utils/numbers" + "github.com/equinor/radix-common/utils/pointers" + "github.com/equinor/radix-operator/pkg/apis/kube" + v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + "github.com/kedacore/keda/v2/apis/keda/v1alpha1" + "github.com/rs/zerolog/log" + autoscalingv2 "k8s.io/api/autoscaling/v2" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const targetCPUUtilizationPercentage int32 = 80 + +func (deploy *Deployment) createOrUpdateScaleObject(ctx context.Context, deployComponent v1.RadixCommonDeployComponent) error { + namespace := deploy.radixDeployment.Namespace + componentName := deployComponent.GetName() + horizontalScaling := deployComponent.GetHorizontalScaling() + + // Check if scaler config exists + if horizontalScaling == nil { + log.Ctx(ctx).Debug().Msgf("Skip creating ScaledObject %s in namespace %s: no HorizontalScaling config exists", componentName, namespace) + return nil + } + + if isComponentStopped(deployComponent) { + return nil + } + + var memoryTarget, cpuTarget *int32 + if horizontalScaling.RadixHorizontalScalingResources != nil { + if horizontalScaling.RadixHorizontalScalingResources.Memory != nil { + memoryTarget = horizontalScaling.RadixHorizontalScalingResources.Memory.AverageUtilization + } + + if horizontalScaling.RadixHorizontalScalingResources.Cpu != nil { + cpuTarget = horizontalScaling.RadixHorizontalScalingResources.Cpu.AverageUtilization + } + } + + if memoryTarget == nil && cpuTarget == nil { + cpuTarget = numbers.Int32Ptr(targetCPUUtilizationPercentage) + } + + scaler := deploy.getScalerConfig(componentName, horizontalScaling.MinReplicas, horizontalScaling.MaxReplicas, cpuTarget, memoryTarget) + + log.Ctx(ctx).Debug().Msgf("Creating ScaledObject object %s in namespace %s", componentName, namespace) + createdScaler, err := deploy.kedaClient.KedaV1alpha1().ScaledObjects(namespace).Create(ctx, scaler, metav1.CreateOptions{}) + if errors.IsAlreadyExists(err) { + log.Ctx(ctx).Debug().Msgf("ScaledObject %s already exists in namespace %s, updating the object now", componentName, namespace) + updatedScaler, err := deploy.kedaClient.KedaV1alpha1().ScaledObjects(namespace).Update(ctx, scaler, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("failed to update ScaledObject: %v", err) + } + log.Ctx(ctx).Debug().Msgf("Updated ScaledObject: %s in namespace %s", updatedScaler.Name, namespace) + return nil + } + if err != nil { + return fmt.Errorf("failed to create ScaledObject: %v", err) + } + log.Ctx(ctx).Debug().Msgf("Created ScaledObject: %s in namespace %s", createdScaler.Name, namespace) + return nil +} + +func (deploy *Deployment) garbageCollectDeprecatedHPAs(ctx context.Context) error { + namespace := deploy.radixDeployment.GetNamespace() + hpas, err := deploy.kubeclient.AutoscalingV2().HorizontalPodAutoscalers(namespace).List(ctx, metav1.ListOptions{}) + + if err != nil { + return err + } + + for _, hpa := range hpas.Items { + err = deploy.kubeclient.AutoscalingV2().HorizontalPodAutoscalers(namespace).Delete(ctx, hpa.Name, metav1.DeleteOptions{}) + if err != nil && errors.IsNotFound(err) { + return err + } + } + + return nil +} + +func (deploy *Deployment) garbageCollectScalersNoLongerInSpec(ctx context.Context) error { + namespace := deploy.radixDeployment.GetNamespace() + scalers, err := deploy.kedaClient.KedaV1alpha1().ScaledObjects(namespace).List(ctx, metav1.ListOptions{}) + + if err != nil { + return err + } + + for _, scaler := range scalers.Items { + componentName, ok := RadixComponentNameFromComponentLabel(&scaler) + if !ok { + continue + } + + if !componentName.ExistInDeploymentSpecComponentList(deploy.radixDeployment) { + err = deploy.kedaClient.KedaV1alpha1().ScaledObjects(namespace).Delete(ctx, scaler.Name, metav1.DeleteOptions{}) + if err != nil { + return err + } + } + } + + return nil +} + +func (deploy *Deployment) getScalerConfig(componentName string, minReplicas *int32, maxReplicas int32, cpuTarget *int32, memoryTarget *int32) *v1alpha1.ScaledObject { + appName := deploy.radixDeployment.Spec.AppName + ownerReference := []metav1.OwnerReference{ + getOwnerReferenceOfDeployment(deploy.radixDeployment), + } + + triggers := getScalingTriggers(cpuTarget, memoryTarget) + + scaler := &v1alpha1.ScaledObject{ + ObjectMeta: metav1.ObjectMeta{ + Name: componentName, + Labels: map[string]string{ + kube.RadixAppLabel: appName, + kube.RadixComponentLabel: componentName, + }, + OwnerReferences: ownerReference, + }, + Spec: v1alpha1.ScaledObjectSpec{ + MinReplicaCount: minReplicas, + MaxReplicaCount: pointers.Ptr(maxReplicas), + PollingInterval: pointers.Ptr[int32](30), // Default + Advanced: &v1alpha1.AdvancedConfig{RestoreToOriginalReplicaCount: true}, + ScaleTargetRef: &v1alpha1.ScaleTarget{ + Kind: "Deployment", + Name: componentName, + APIVersion: "apps/v1", + }, + Triggers: triggers, + }, + } + + return scaler +} + +func getScalingTriggers(cpuTarget *int32, memoryTarget *int32) []v1alpha1.ScaleTriggers { + var triggers []v1alpha1.ScaleTriggers + if cpuTarget != nil { + triggers = append(triggers, v1alpha1.ScaleTriggers{ + Name: "cpu", + Type: "cpu", + MetricType: autoscalingv2.UtilizationMetricType, + Metadata: map[string]string{ + "averageUtilization": strconv.Itoa(int(*cpuTarget)), + }, + }) + } + + if memoryTarget != nil { + triggers = append(triggers, v1alpha1.ScaleTriggers{ + Name: "memory", + Type: "memory", + MetricType: autoscalingv2.UtilizationMetricType, + Metadata: map[string]string{ + "averageUtilization": strconv.Itoa(int(*memoryTarget)), + }, + }) + } + return triggers +} + +func (deploy *Deployment) deleteScaledObjectIfExists(ctx context.Context, componentName string) error { + namespace := deploy.radixDeployment.GetNamespace() + _, err := deploy.kedaClient.KedaV1alpha1().ScaledObjects(namespace).Get(ctx, componentName, metav1.GetOptions{}) + if err != nil && errors.IsNotFound(err) { + return nil + } + if err != nil { + return fmt.Errorf("failed to get hpa: %v", err) + } + err = deploy.kedaClient.KedaV1alpha1().ScaledObjects(namespace).Delete(ctx, componentName, metav1.DeleteOptions{}) + if err != nil { + return fmt.Errorf("failed to delete existing hpa: %v", err) + } + + return nil +} diff --git a/pkg/apis/deployment/hpa_test.go b/pkg/apis/deployment/scaleobject_test.go similarity index 100% rename from pkg/apis/deployment/hpa_test.go rename to pkg/apis/deployment/scaleobject_test.go diff --git a/pkg/apis/utils/init.go b/pkg/apis/utils/init.go index 099b49719..567ecd566 100644 --- a/pkg/apis/utils/init.go +++ b/pkg/apis/utils/init.go @@ -8,6 +8,7 @@ import ( certclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned" radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned" + kedav2 "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned" monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -49,7 +50,7 @@ func (zl ZerologWarningHandlerAdapter) HandleWarningHeader(_ int, _ string, text } // GetKubernetesClient Gets clients to talk to the API -func GetKubernetesClient(ctx context.Context, configOptions ...KubernetesClientConfigOption) (kubernetes.Interface, radixclient.Interface, monitoring.Interface, secretProviderClient.Interface, certclient.Interface) { +func GetKubernetesClient(ctx context.Context, configOptions ...KubernetesClientConfigOption) (kubernetes.Interface, radixclient.Interface, kedav2.Interface, monitoring.Interface, secretProviderClient.Interface, certclient.Interface) { pollTimeout, pollInterval := time.Minute, 15*time.Second kubeConfigPath := os.Getenv("HOME") + "/.kube/config" config, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath) @@ -84,6 +85,13 @@ func GetKubernetesClient(ctx context.Context, configOptions ...KubernetesClientC log.Fatal().Err(err).Msg("Failed to initialize Radix client") } + kedaClient, err := PollUntilRESTClientSuccessfulConnection(ctx, pollTimeout, pollInterval, func() (*kedav2.Clientset, error) { + return kedav2.NewForConfig(config) + }) + if err != nil { + log.Fatal().Err(err).Msg("Failed to initialize Prometheus client") + } + prometheusOperatorClient, err := PollUntilRESTClientSuccessfulConnection(ctx, pollTimeout, pollInterval, func() (*monitoring.Clientset, error) { return monitoring.NewForConfig(config) }) @@ -105,5 +113,5 @@ func GetKubernetesClient(ctx context.Context, configOptions ...KubernetesClientC } log.Info().Msgf("Successfully constructed k8s client to API server %v", config.Host) - return client, radixClient, prometheusOperatorClient, secretProviderClient, certClient + return client, radixClient, kedaClient, prometheusOperatorClient, secretProviderClient, certClient } diff --git a/radix-operator/deployment/handler.go b/radix-operator/deployment/handler.go index 541dfaffc..38e0d4d9f 100644 --- a/radix-operator/deployment/handler.go +++ b/radix-operator/deployment/handler.go @@ -10,6 +10,7 @@ import ( "github.com/equinor/radix-operator/pkg/apis/kube" radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned" "github.com/equinor/radix-operator/radix-operator/common" + kedav2 "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned" monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" "github.com/rs/zerolog/log" @@ -83,12 +84,14 @@ type Handler struct { ingressConfiguration ingress.IngressConfiguration deploymentSyncerFactory deployment.DeploymentSyncerFactory config *config.Config + kedaClient kedav2.Interface } // NewHandler Constructor func NewHandler(kubeclient kubernetes.Interface, kubeutil *kube.Kube, radixclient radixclient.Interface, + kedaClient kedav2.Interface, prometheusperatorclient monitoring.Interface, certClient certclient.Interface, config *config.Config, @@ -97,6 +100,7 @@ func NewHandler(kubeclient kubernetes.Interface, handler := &Handler{ kubeclient: kubeclient, radixclient: radixclient, + kedaClient: kedaClient, prometheusperatorclient: prometheusperatorclient, certClient: certClient, kubeutil: kubeutil, @@ -152,7 +156,7 @@ func (t *Handler) Sync(ctx context.Context, namespace, name string, 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, t.certClient, radixRegistration, syncRD, ingressAnnotations, auxResourceManagers, t.config) + deployment := t.deploymentSyncerFactory.CreateDeploymentSyncer(t.kubeclient, t.kubeutil, t.radixclient, t.kedaClient, t.prometheusperatorclient, t.certClient, radixRegistration, syncRD, ingressAnnotations, auxResourceManagers, t.config) err = deployment.OnSync(ctx) if err != nil { // TODO: should we record a Warning event when there is an error, similar to batch handler? Possibly do it in common.Controller? diff --git a/radix-operator/main.go b/radix-operator/main.go index e23b3af26..cc3d9aed3 100644 --- a/radix-operator/main.go +++ b/radix-operator/main.go @@ -30,6 +30,7 @@ import ( "github.com/equinor/radix-operator/radix-operator/environment" "github.com/equinor/radix-operator/radix-operator/job" "github.com/equinor/radix-operator/radix-operator/registration" + kedav2 "github.com/kedacore/keda/v2/pkg/generated/clientset/versioned" "github.com/pkg/errors" monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -79,6 +80,7 @@ type App struct { ingressConfiguration ingress.IngressConfiguration kubeUtil *kube.Kube config *apiconfig.Config + kedaClient kedav2.Interface } func main() { @@ -115,7 +117,7 @@ func initializeApp(ctx context.Context) (*App, error) { } app.rateLimitConfig = utils.WithKubernetesClientRateLimiter(flowcontrol.NewTokenBucketRateLimiter(app.opts.kubeClientRateLimitQPS, app.opts.kubeClientRateLimitBurst)) app.warningHandler = utils.WithKubernetesWarningHandler(utils.ZerologWarningHandlerAdapter(log.Warn)) - app.client, app.radixClient, app.prometheusOperatorClient, app.secretProviderClient, app.certClient = utils.GetKubernetesClient(ctx, app.rateLimitConfig, app.warningHandler) + app.client, app.radixClient, app.kedaClient, app.prometheusOperatorClient, app.secretProviderClient, app.certClient = utils.GetKubernetesClient(ctx, app.rateLimitConfig, app.warningHandler) app.eventRecorder = common.NewEventRecorder("Radix controller", app.client.CoreV1().Events(""), log.Logger) @@ -311,6 +313,7 @@ func (a *App) createDeploymentController(ctx context.Context) *common.Controller a.kubeUtil.KubeClient(), a.kubeUtil, a.kubeUtil.RadixClient(), + a.kedaClient, a.prometheusOperatorClient, a.certClient, a.config,