diff --git a/.github/scripts/platform-qa-provision-downstream-k3s.sh b/.github/scripts/platform-qa-provision-downstream-k3s.sh index 87b6ca118..6211cbe5d 100755 --- a/.github/scripts/platform-qa-provision-downstream-k3s.sh +++ b/.github/scripts/platform-qa-provision-downstream-k3s.sh @@ -96,13 +96,14 @@ EOF kubectl --kubeconfig "$RANCHER_KUBECONFIG" apply -f "$MACHINECONFIG_FILE" sleep 5 -REGISTRY_SECRET_NAME="dockerhub-registry-auth" +REGISTRY_SECRET_NAME="docker-auth-${CLUSTER_NAME}" cat < github.com/docker/distribution v2.8.2+incompatible // rancher-machine requires a replace is set github.com/docker/docker => github.com/docker/docker v20.10.27+incompatible // rancher-machine requires a replace is set - github.com/rancher/rancher/pkg/apis => github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83 - github.com/rancher/rancher/pkg/client => github.com/rancher/rancher/pkg/client v0.0.0-20251111120454-f829d8f1dc83 + github.com/rancher/rancher/pkg/apis => github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af + github.com/rancher/rancher/pkg/client => github.com/rancher/rancher/pkg/client v0.0.0-20260105201356-c4811cb9f2af go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc => go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 go.opentelemetry.io/otel => go.opentelemetry.io/otel v1.28.0 @@ -59,22 +59,22 @@ replace ( require ( github.com/rancher/rancher/pkg/apis v0.0.0 - github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67 + github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae github.com/rancher/tfp-automation v0.0.0-20251219210947-f4a1a9882c29 ) require ( github.com/aws/aws-sdk-go v1.55.8 github.com/pkg/errors v0.9.1 - github.com/rancher/norman v0.8.0 - github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738 + github.com/rancher/norman v0.8.1 + github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e github.com/rancher/wrangler v1.1.2 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.11.1 - golang.org/x/crypto v0.43.0 + golang.org/x/crypto v0.45.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.34.1 - k8s.io/apimachinery v0.34.1 + k8s.io/api v0.34.3 + k8s.io/apimachinery v0.34.3 k8s.io/client-go v12.0.0+incompatible k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 sigs.k8s.io/cluster-api v1.10.6 @@ -176,7 +176,7 @@ require ( github.com/rancher/aks-operator v1.13.0-rc.4 // indirect github.com/rancher/apiserver v0.8.0 // indirect github.com/rancher/eks-operator v1.13.0-rc.4 // indirect - github.com/rancher/fleet/pkg/apis v0.14.0-rc.1 // indirect + github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4 // indirect github.com/rancher/gke-operator v1.13.0-rc.3 // indirect github.com/rancher/lasso v0.2.5 // indirect github.com/rancher/rke v1.8.0 // indirect @@ -201,12 +201,12 @@ require ( go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.46.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/term v0.36.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.13.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect @@ -223,7 +223,7 @@ require ( k8s.io/kube-aggregator v0.34.1 // indirect k8s.io/kube-openapi v0.31.5 // indirect k8s.io/kubectl v0.34.1 // indirect - k8s.io/kubernetes v1.34.1 // indirect + k8s.io/kubernetes v1.34.2 // indirect k8s.io/pod-security-admission v0.34.1 // indirect sigs.k8s.io/cli-utils v0.37.2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect diff --git a/actions/go.sum b/actions/go.sum index ca9accaaa..2d2d0210b 100644 --- a/actions/go.sum +++ b/actions/go.sum @@ -248,22 +248,22 @@ github.com/rancher/apiserver v0.8.0 h1:yCXsCa67X/Y///NKJ/pq6pv6wmt3hq/OIzBaIna2g github.com/rancher/apiserver v0.8.0/go.mod h1:Wb+Z8ktNyIuqt9hw30geFBQFJQucWTqgu6trxxMtcyM= github.com/rancher/eks-operator v1.13.0-rc.4 h1:XowN8+m3QZTIBOBLzar4frtz0xtREb9kcX6KXhF4eas= github.com/rancher/eks-operator v1.13.0-rc.4/go.mod h1:SbaKX2ttFWCxGOYkrKYeWH/6E4oToq2rRTcrMa2Mmdk= -github.com/rancher/fleet/pkg/apis v0.14.0-rc.1 h1:ZsDc25j4/iuKJ8DhxaOSnHdqOskRRe7QxJAdD9HBn28= -github.com/rancher/fleet/pkg/apis v0.14.0-rc.1/go.mod h1:oc+QHbx4P9guY34dr6UbzCOgt17Q9eSZhlyOs7xSinY= +github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4 h1:l6pdMToVQSuhFaNmENuY1+v+5lltwHvw92zbt7iK6sU= +github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4/go.mod h1:srlFTlA6425rCPRELTdtFcZM8wDNPaqW4O4aj6sArs4= github.com/rancher/gke-operator v1.13.0-rc.3 h1:a6U+7+XIbJPH2CE7/vFUx6RpThNbFl7fqIqkEBb6zmA= github.com/rancher/gke-operator v1.13.0-rc.3/go.mod h1:TroxpmqMh63Hf4H5bC+2GYcgOCQp9kIUDfyKdNAMo6Q= github.com/rancher/lasso v0.2.5 h1:K++lWDDdfeN98Ixc1kCfUq0/q6tLjoHN++Np6QntXw0= github.com/rancher/lasso v0.2.5/go.mod h1:71rWfv+KkdSmSxZ9Ly5QYhxAu0nEUcaq9N2ByjcHqAM= -github.com/rancher/norman v0.8.0 h1://ZSe+B53cMgPNAbR7QBhzvIfWBxR4KaPWTKqG+g+O4= -github.com/rancher/norman v0.8.0/go.mod h1:vZ5qL+eKodJ7zOMQYdl6jwMrSFrqTKpA+KYSFEKew2M= -github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738 h1:qalvtaJ4WQzPu0lkJFSTh3L0TgIEU4h7Kni/lBdfAQw= -github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738/go.mod h1:NnexTOmNU92x0L5QfbeyUH6RPw87y7WHuLbAeDre9W8= -github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83 h1:5wbUhQaEesGsigLFNbXRJyACuGy2UNWMXVmBfDynk7M= -github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83/go.mod h1:xyYMxIycb9QpdxZjUxf95Cc4E27rfma8Z77U5ysRx3A= +github.com/rancher/norman v0.8.1 h1:114Rdt3xsWTUdqaxlIR2F6PJT0ls01vF0Rfglustgow= +github.com/rancher/norman v0.8.1/go.mod h1:vZ5qL+eKodJ7zOMQYdl6jwMrSFrqTKpA+KYSFEKew2M= +github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e h1:HExd4+6+bF4aYv0Wj/eORxc65Y4einqWaDqvJE+yjys= +github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e/go.mod h1:ORjiG9PXFw0JT3+CtC5Ih34joRgwCgedRhQQbos8Nag= +github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af h1:ZXIKdKynB1aelfSa4qyt9nCpa0kXTizkS7i58FoNaxY= +github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af/go.mod h1:NwWL+lOkxPRibQ6j+9uFSo6t1CJ18z1oY4OYJMOQ/R0= github.com/rancher/rke v1.8.0 h1:87jeoOccnnNCq27YgWgMh4o0GVrrVKbw+zfo+cHMZlo= github.com/rancher/rke v1.8.0/go.mod h1:x9N1abruzDFMwTpqq2cnaDYpKCptlNoW8VraNWB6Pc4= -github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67 h1:1BUWLjjtHbbPtQ4rgHAWruCWxM8qa0+QYwcvraneeME= -github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67/go.mod h1:SJtW8Jqv0rphZzsGnvB965YdyR2FqFtB+TbbzVLt8F4= +github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae h1:9Krx+fO7yiPGHEiAdknVRHwiwlPrg/RmtLfqOUFABxk= +github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae/go.mod h1:SJtW8Jqv0rphZzsGnvB965YdyR2FqFtB+TbbzVLt8F4= github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20250930163923-f2c9e60b1078 h1:1MJSgYkgXhr/Zc5idJkKa10SiBQd0HVtbxVOBoghlzY= github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20250930163923-f2c9e60b1078/go.mod h1:CV2Soy/Skw8/SA9dDJVgpeHxoEdtjYkNpNy6xvvC5kA= github.com/rancher/tfp-automation v0.0.0-20251219210947-f4a1a9882c29 h1:K7gKqQy8w4oPlm85x/t3WjMhHPi0nQTX49RMzJnkGK0= @@ -343,16 +343,16 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -363,8 +363,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= @@ -373,8 +373,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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= @@ -386,16 +386,16 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -406,8 +406,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= 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= diff --git a/actions/kubeapi/namespaces/create.go b/actions/kubeapi/namespaces/create.go index a4f484cf0..72c72c3a7 100644 --- a/actions/kubeapi/namespaces/create.go +++ b/actions/kubeapi/namespaces/create.go @@ -8,8 +8,10 @@ import ( "github.com/rancher/shepherd/extensions/defaults" "github.com/rancher/shepherd/extensions/unstructured" "github.com/rancher/shepherd/pkg/api/scheme" + namegen "github.com/rancher/shepherd/pkg/namegenerator" "github.com/rancher/shepherd/pkg/wait" - coreV1 "k8s.io/api/core/v1" + clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" + corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -19,7 +21,7 @@ import ( // CreateNamespace is a helper function that uses the dynamic client to create a namespace on a project. // It registers a delete function with a wait.WatchWait to ensure the namspace is deleted cleanly. -func CreateNamespace(client *rancher.Client, clusterID, projectName, namespaceName, containerDefaultResourceLimit string, labels, annotations map[string]string) (*coreV1.Namespace, error) { +func CreateNamespace(client *rancher.Client, clusterID, projectName, namespaceName, containerDefaultResourceLimit string, labels, annotations map[string]string) (*corev1.Namespace, error) { if annotations == nil { annotations = make(map[string]string) } @@ -33,7 +35,7 @@ func CreateNamespace(client *rancher.Client, clusterID, projectName, namespaceNa annotations["field.cattle.io/projectId"] = annotationValue } - namespace := &coreV1.Namespace{ + namespace := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: namespaceName, Annotations: annotations, @@ -123,10 +125,57 @@ func CreateNamespace(client *rancher.Client, clusterID, projectName, namespaceNa }) }) - newNamespace := &coreV1.Namespace{} + newNamespace := &corev1.Namespace{} err = scheme.Scheme.Convert(unstructuredResp, newNamespace, unstructuredResp.GroupVersionKind()) if err != nil { return nil, err } return newNamespace, nil } + +// CreateNamespaceUsingWrangler is a helper to create a namespace in the project using wrangler context +func CreateNamespaceUsingWrangler(client *rancher.Client, clusterID, projectName string, labels map[string]string) (*corev1.Namespace, error) { + namespaceName := namegen.AppendRandomString("testns") + annotations := map[string]string{ + ProjectIDAnnotation: clusterID + ":" + projectName, + } + + ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return nil, err + } + + createdNamespace, err := ctx.Core.Namespace().Create(&corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespaceName, + Annotations: annotations, + Labels: labels, + }, + }) + if err != nil { + return nil, err + } + + err = WaitForProjectIDUpdate(client, clusterID, projectName, createdNamespace.Name) + if err != nil { + return nil, err + } + + return createdNamespace, nil +} + +// CreateMultipleNamespacesInProject creates multiple namespaces in the specified project using wrangler context +func CreateMultipleNamespacesInProject(client *rancher.Client, clusterID, projectID string, count int) ([]*corev1.Namespace, error) { + var createdNamespaces []*corev1.Namespace + + for i := 0; i < count; i++ { + ns, err := CreateNamespaceUsingWrangler(client, clusterID, projectID, nil) + if err != nil { + return nil, fmt.Errorf("failed to create namespace %d/%d: %w", i+1, count, err) + } + + createdNamespaces = append(createdNamespaces, ns) + } + + return createdNamespaces, nil +} diff --git a/actions/kubeapi/namespaces/delete.go b/actions/kubeapi/namespaces/delete.go new file mode 100644 index 000000000..112e47444 --- /dev/null +++ b/actions/kubeapi/namespaces/delete.go @@ -0,0 +1,37 @@ +package namespaces + +import ( + "context" + + "github.com/rancher/shepherd/clients/rancher" + "github.com/rancher/shepherd/extensions/defaults" + clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kwait "k8s.io/apimachinery/pkg/util/wait" +) + +// DeleteNamespace deletes a namespace in a cluster using wrangler context +func DeleteNamespace(client *rancher.Client, clusterID, namespaceName string) error { + ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return err + } + + err = ctx.Core.Namespace().Delete(namespaceName, &metav1.DeleteOptions{}) + if err != nil { + return err + } + + return kwait.PollUntilContextTimeout(context.Background(), defaults.FiveSecondTimeout, defaults.TenSecondTimeout, false, func(context.Context) (bool, error) { + _, pollErr := ctx.Core.Namespace().Get(namespaceName, metav1.GetOptions{}) + if pollErr != nil { + if k8serrors.IsNotFound(pollErr) { + return true, nil + } + return false, pollErr + } + return false, nil + }, + ) +} diff --git a/actions/kubeapi/namespaces/list.go b/actions/kubeapi/namespaces/list.go index dc04d818d..611cef53e 100644 --- a/actions/kubeapi/namespaces/list.go +++ b/actions/kubeapi/namespaces/list.go @@ -2,13 +2,37 @@ package namespaces import ( "context" + "fmt" "github.com/rancher/shepherd/clients/rancher" "github.com/rancher/shepherd/pkg/api/scheme" + clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// GetNamespacesInProject retrieves all namespaces in a specific project within a cluster +func GetNamespacesInProject(client *rancher.Client, clusterID, projectName string) ([]*corev1.Namespace, error) { + clusterContext, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return nil, fmt.Errorf("failed to get wrangler context for cluster %s: %w", clusterID, err) + } + + nsList, err := clusterContext.Core.Namespace().List(metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", ProjectIDAnnotation, projectName), + }) + if err != nil { + return nil, fmt.Errorf("failed to list namespaces for project %s: %w", projectName, err) + } + + namespaces := make([]*corev1.Namespace, 0, len(nsList.Items)) + for i := range nsList.Items { + namespaces = append(namespaces, &nsList.Items[i]) + } + + return namespaces, nil +} + // NamespaceList is a struct that contains a list of namespaces. type NamespaceList struct { Items []corev1.Namespace diff --git a/actions/kubeapi/namespaces/namespaces.go b/actions/kubeapi/namespaces/namespaces.go index 3a8e75d55..1595c41ce 100644 --- a/actions/kubeapi/namespaces/namespaces.go +++ b/actions/kubeapi/namespaces/namespaces.go @@ -2,13 +2,25 @@ package namespaces import ( "context" + "encoding/json" "fmt" "github.com/rancher/shepherd/clients/rancher" + "github.com/rancher/shepherd/extensions/defaults" "github.com/rancher/shepherd/pkg/api/scheme" + clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + kwait "k8s.io/apimachinery/pkg/util/wait" +) + +const ( + ContainerDefaultResourceLimitAnnotation = "field.cattle.io/containerDefaultResourceLimit" + ProjectIDAnnotation = "field.cattle.io/projectId" + ResourceQuotaAnnotation = "field.cattle.io/resourceQuota" + ResourceQuotaStatusAnnotation = "cattle.io/status" + InitialUsedResourceQuotaValue = "0" ) // NamespaceGroupVersionResource is the required Group Version Resource for accessing namespaces in a cluster, @@ -49,3 +61,121 @@ func GetNamespaceByName(client *rancher.Client, clusterID, namespaceName string) return namespace, nil } + +// WaitForProjectIDUpdate is a helper that waits for the project-id annotation and label to be updated in a specified namespace +func WaitForProjectIDUpdate(client *rancher.Client, clusterID, projectName, namespaceName string) error { + expectedAnnotations := map[string]string{ + ProjectIDAnnotation: clusterID + ":" + projectName, + } + + expectedLabels := map[string]string{ + ProjectIDAnnotation: projectName, + } + + err := kwait.PollUntilContextTimeout(context.Background(), defaults.FiveSecondTimeout, defaults.OneMinuteTimeout, false, func(ctx context.Context) (done bool, pollErr error) { + + namespace, pollErr := GetNamespaceByName(client, clusterID, namespaceName) + if pollErr != nil { + return false, pollErr + } + + for key, expectedValue := range expectedAnnotations { + if actualValue, ok := namespace.Annotations[key]; !ok || actualValue != expectedValue { + return false, nil + } + } + + for key, expectedValue := range expectedLabels { + if actualValue, ok := namespace.Labels[key]; !ok || actualValue != expectedValue { + return false, nil + } + } + + return true, nil + }) + + if err != nil { + return err + } + + return nil +} + +// UpdateNamespaceResourceQuotaAnnotation updates the resource quota annotation on a namespace +func UpdateNamespaceResourceQuotaAnnotation(client *rancher.Client, clusterID string, namespaceName string, existingLimits map[string]string, extendedLimits map[string]string) error { + ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return err + } + + limit := make(map[string]interface{}, len(existingLimits)+1) + for k, v := range existingLimits { + limit[k] = v + } + + if len(extendedLimits) > 0 { + limit["extended"] = extendedLimits + } + + quota := map[string]interface{}{ + "limit": limit, + } + + quotaJSON, err := json.Marshal(quota) + if err != nil { + return fmt.Errorf("marshal resource quota annotation payload: %w", err) + } + quotaStr := string(quotaJSON) + + ns, err := ctx.Core.Namespace().Get(namespaceName, metav1.GetOptions{}) + if err != nil { + return err + } + + if ns.Annotations == nil { + ns.Annotations = make(map[string]string) + } + + ns.Annotations[ResourceQuotaAnnotation] = quotaStr + + _, err = ctx.Core.Namespace().Update(ns) + return err +} + +// MoveNamespaceToProject updates the project annotation/label to move the namespace into a different project +func MoveNamespaceToProject(client *rancher.Client, clusterID, namespaceName, newProjectName string) error { + ns, err := GetNamespaceByName(client, clusterID, namespaceName) + if err != nil { + return fmt.Errorf("failed to get namespace %s: %w", namespaceName, err) + } + + if ns.Annotations == nil { + ns.Annotations = make(map[string]string) + } + if ns.Labels == nil { + ns.Labels = make(map[string]string) + } + + ns.Annotations[ProjectIDAnnotation] = fmt.Sprintf("%s:%s", clusterID, newProjectName) + ns.Labels[ProjectIDAnnotation] = newProjectName + + clusterContext, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return fmt.Errorf("failed to get wrangler context for cluster %s: %w", clusterID, err) + } + latestNS, err := GetNamespaceByName(client, clusterID, namespaceName) + if err != nil { + return fmt.Errorf("failed to fetch namespace %s: %w", namespaceName, err) + } + ns.ResourceVersion = latestNS.ResourceVersion + + if _, err := clusterContext.Core.Namespace().Update(ns); err != nil { + return fmt.Errorf("failed to update namespace %s with new project annotation: %w", namespaceName, err) + } + + if err := WaitForProjectIDUpdate(client, clusterID, newProjectName, namespaceName); err != nil { + return fmt.Errorf("project ID annotation/label not updated for namespace %s: %w", namespaceName, err) + } + + return nil +} diff --git a/actions/kubeapi/namespaces/verify.go b/actions/kubeapi/namespaces/verify.go new file mode 100644 index 000000000..6123e5c78 --- /dev/null +++ b/actions/kubeapi/namespaces/verify.go @@ -0,0 +1,364 @@ +package namespaces + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" + + "github.com/rancher/shepherd/clients/rancher" + "github.com/rancher/shepherd/extensions/defaults" + clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" + quotas "github.com/rancher/tests/actions/kubeapi/resourcequotas" + "github.com/rancher/tests/actions/workloads/pods" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kwait "k8s.io/apimachinery/pkg/util/wait" +) + +// VerifyAnnotationInNamespace checks if a specific annotation exists or not in a namespace. +func VerifyAnnotationInNamespace(client *rancher.Client, clusterID, namespaceName, annotationKey string, expectedToExist bool) error { + namespace, err := GetNamespaceByName(client, clusterID, namespaceName) + if err != nil { + return err + } + + exists := false + if namespace.Annotations != nil { + _, exists = namespace.Annotations[annotationKey] + } + + if expectedToExist && !exists { + return fmt.Errorf("expected annotation %q to exist, but it does not", annotationKey) + } + + if !expectedToExist && exists { + return fmt.Errorf("expected annotation %q to not exist, but it does", annotationKey) + } + + return nil +} + +// VerifyNamespaceResourceQuota verifies that the namespace resource quota contains the expected hard limits. +func VerifyNamespaceResourceQuota(client *rancher.Client, clusterID, namespaceName string, expectedQuota map[string]string) error { + resourceQuotas, err := quotas.ListResourceQuotas(client, clusterID, namespaceName, metav1.ListOptions{}) + if err != nil { + return err + } + + if len(resourceQuotas.Items) != 1 { + return fmt.Errorf("expected 1 ResourceQuota, got %d", len(resourceQuotas.Items)) + } + + actualHard := resourceQuotas.Items[0].Spec.Hard + + for resourceName, expectedValue := range expectedQuota { + actualQuantity, exists := actualHard[corev1.ResourceName(resourceName)] + if !exists { + return fmt.Errorf("expected resource %q not found in ResourceQuota", resourceName) + } + + expectedQuantity := resource.MustParse(expectedValue) + + if actualQuantity.Cmp(expectedQuantity) != 0 { + return fmt.Errorf("resource %q mismatch: expected=%s actual=%s", resourceName, expectedQuantity.String(), actualQuantity.String()) + } + } + + return nil +} + +// VerifyLimitRange verifies that the LimitRange in the specified namespace matches the expected CPU and memory limits and requests. +func VerifyLimitRange(client *rancher.Client, clusterID, namespaceName string, expectedCPULimit, expectedCPURequest, expectedMemoryLimit, expectedMemoryRequest string) error { + clusterContext, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return err + } + + var limitRanges []corev1.LimitRange + err = kwait.PollUntilContextTimeout(context.TODO(), defaults.FiveSecondTimeout, defaults.TenSecondTimeout, true, func(ctx context.Context) (bool, error) { + limitRangeList, err := clusterContext.Core.LimitRange().List(namespaceName, metav1.ListOptions{}) + if err != nil { + return false, err + } + if len(limitRangeList.Items) == 0 { + return false, nil + } + limitRanges = limitRangeList.Items + return true, nil + }) + + if err != nil { + return fmt.Errorf("limit range not found in namespace %s after waiting: %v", namespaceName, err) + } + + if len(limitRanges) != 1 { + return fmt.Errorf("expected limit range count is 1, but got %d", len(limitRanges)) + } + + limitRange := limitRanges[0].Spec + if len(limitRange.Limits) == 0 { + return fmt.Errorf("no limits found in limit range spec") + } + + limits := limitRange.Limits[0] + + if actualCPULimit, ok := limits.Default[corev1.ResourceCPU]; !ok || actualCPULimit.String() != expectedCPULimit { + return fmt.Errorf("cpu limit mismatch: expected %s, got %s", expectedCPULimit, actualCPULimit.String()) + } + + if actualCPURequest, ok := limits.DefaultRequest[corev1.ResourceCPU]; !ok || actualCPURequest.String() != expectedCPURequest { + return fmt.Errorf("cpu request mismatch: expected %s, got %s", expectedCPURequest, actualCPURequest.String()) + } + + if actualMemoryLimit, ok := limits.Default[corev1.ResourceMemory]; !ok || actualMemoryLimit.String() != expectedMemoryLimit { + return fmt.Errorf("memory limit mismatch: expected %s, got %s", expectedMemoryLimit, actualMemoryLimit.String()) + } + + if actualMemoryRequest, ok := limits.DefaultRequest[corev1.ResourceMemory]; !ok || actualMemoryRequest.String() != expectedMemoryRequest { + return fmt.Errorf("memory request mismatch: expected %s, got %s", expectedMemoryRequest, actualMemoryRequest.String()) + } + + return nil +} + +// VerifyContainerResources checks if the container resources in a pod created by a deployment match the expected values. +func VerifyContainerResources(client *rancher.Client, clusterID, namespaceName, deploymentName, cpuLimit, cpuRequest, memoryLimit, memoryRequest string) error { + var errs []string + + podNames, err := pods.GetPodNamesFromDeployment(client, clusterID, namespaceName, deploymentName) + if err != nil { + return fmt.Errorf("error fetching pod by deployment name: %w", err) + } + + if len(podNames) == 0 { + return fmt.Errorf("expected at least one pod, got 0") + } + + pod, err := pods.GetPodByName(client, clusterID, namespaceName, podNames[0]) + if err != nil { + return err + } + + if len(pod.Spec.Containers) == 0 { + return fmt.Errorf("no containers found in pod %q", pod.Name) + } + + normalizeString := func(s string) string { + if s == "" { + return "0" + } + return s + } + + cpuLimit = normalizeString(cpuLimit) + cpuRequest = normalizeString(cpuRequest) + memoryLimit = normalizeString(memoryLimit) + memoryRequest = normalizeString(memoryRequest) + + containerResources := pod.Spec.Containers[0].Resources + containerCPULimit := containerResources.Limits[corev1.ResourceCPU] + containerCPURequest := containerResources.Requests[corev1.ResourceCPU] + containerMemoryLimit := containerResources.Limits[corev1.ResourceMemory] + containerMemoryRequest := containerResources.Requests[corev1.ResourceMemory] + + if cpuLimit != containerCPULimit.String() { + errs = append(errs, fmt.Sprintf("CPU limit mismatch: expected=%s actual=%s", cpuLimit, containerCPULimit.String())) + + } + if cpuRequest != containerCPURequest.String() { + errs = append(errs, fmt.Sprintf("CPU request mismatch: expected=%s actual=%s", cpuRequest, containerCPURequest.String())) + } + if memoryLimit != containerMemoryLimit.String() { + errs = append(errs, fmt.Sprintf("Memory limit mismatch: expected=%s actual=%s", memoryLimit, containerMemoryLimit.String())) + } + if memoryRequest != containerMemoryRequest.String() { + errs = append(errs, fmt.Sprintf("Memory request mismatch: expected=%s actual=%s", memoryRequest, containerMemoryRequest.String())) + } + + if len(errs) > 0 { + return errors.New(strings.Join(errs, "\n")) + } + + return nil +} + +// VerifyUsedNamespaceResourceQuota checks if the used resources in a namespace's ResourceQuota match the expected values. +func VerifyUsedNamespaceResourceQuota(client *rancher.Client, clusterID, namespace string, expectedUsed map[string]string) error { + ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return err + } + + rqList, err := ctx.Core.ResourceQuota().List( + namespace, + metav1.ListOptions{}, + ) + if err != nil { + return err + } + + if len(rqList.Items) != 1 { + return fmt.Errorf("expected exactly 1 ResourceQuota in namespace %s, found %d", namespace, len(rqList.Items)) + } + + rq := rqList.Items[0] + + for resource, expected := range expectedUsed { + actualQty, ok := rq.Status.Used[corev1.ResourceName(resource)] + if !ok { + return fmt.Errorf("resource %q not found in ResourceQuota.Status.Used", resource) + } + + if actualQty.String() != expected { + return fmt.Errorf("resource quota used mismatch for %q: expected=%s actual=%s", resource, expected, actualQty.String()) + } + } + + return nil +} + +// VerifyNamespaceResourceQuotaValidationStatus checks if the resource quota annotation in a namespace matches the expected limits and validation status. +func VerifyNamespaceResourceQuotaValidationStatus(client *rancher.Client, clusterID, namespaceName string, + expectedExistingLimits map[string]string, + expectedExtendedLimits map[string]string, + expectedStatus bool, + expectedErrorMessage string, +) error { + + namespace, err := GetNamespaceByName(client, clusterID, namespaceName) + if err != nil { + return err + } + + annotationData, err := getNamespaceAnnotationAsMap(client, clusterID, namespace.Name, ResourceQuotaAnnotation) + if err != nil { + return err + } + + limitMap, ok := annotationData["limit"].(map[string]interface{}) + if !ok { + return fmt.Errorf("invalid quota annotation format: missing 'limit'") + } + + for resource, expectedValue := range expectedExistingLimits { + actual, ok := limitMap[resource] + if !ok { + return fmt.Errorf("existing resource %q not found in namespace quota annotation", resource) + } + + actualStr := fmt.Sprintf("%v", actual) + if actualStr != expectedValue { + return fmt.Errorf("existing quota mismatch for %q: expected=%s actual=%s", resource, expectedValue, actualStr) + } + } + + if len(expectedExtendedLimits) > 0 { + extendedMap, ok := limitMap["extended"].(map[string]interface{}) + if !ok { + return fmt.Errorf("invalid quota annotation format: missing 'limit.extended'") + } + + for resource, expectedValue := range expectedExtendedLimits { + actual, ok := extendedMap[resource] + if !ok { + return fmt.Errorf( + "extended resource %q not found in namespace quota annotation", + resource, + ) + } + + actualStr := fmt.Sprintf("%v", actual) + if actualStr != expectedValue { + return fmt.Errorf("extended quota mismatch for %q: expected=%s actual=%s", resource, expectedValue, actualStr) + } + } + } + + statusAnnotation, ok := namespace.Annotations[ResourceQuotaStatusAnnotation] + if !ok { + return fmt.Errorf("missing %q annotation on namespace", ResourceQuotaStatusAnnotation) + } + + status, message, err := getConditionStatusAndMessageFromAnnotation(statusAnnotation, "ResourceQuotaValidated") + if err != nil { + return err + } + + if (status == "True") != expectedStatus { + return fmt.Errorf("resource quota validation status mismatch: expected=%t actual=%s", expectedStatus, status) + } + + if expectedErrorMessage != "" && !strings.Contains(message, expectedErrorMessage) { + return fmt.Errorf("expected error message to contain %q, got %q", expectedErrorMessage, message) + } + + return nil +} + +func getNamespaceAnnotationAsMap(client *rancher.Client, clusterID string, namespaceName, annotationKey string) (map[string]interface{}, error) { + namespace, err := GetNamespaceByName(client, clusterID, namespaceName) + if err != nil { + return nil, err + } + + if namespace.Annotations == nil { + return nil, fmt.Errorf("namespace %q has no annotations", namespaceName) + } + + limitAnnotation, exists := namespace.Annotations[annotationKey] + if !exists || limitAnnotation == "" { + return nil, fmt.Errorf("annotation %q not found on namespace %q", annotationKey, namespaceName) + } + + var data map[string]interface{} + if err := json.Unmarshal([]byte(limitAnnotation), &data); err != nil { + return nil, fmt.Errorf("failed to unmarshal annotation %q: %w", annotationKey, err) + } + + return data, nil +} + +func getConditionStatusAndMessageFromAnnotation(annotation string, conditionType string) (string, string, error) { + var annotationData map[string][]map[string]string + if err := json.Unmarshal([]byte(annotation), &annotationData); err != nil { + return "", "", fmt.Errorf("error parsing JSON: %v", err) + } + + conditions, ok := annotationData["Conditions"] + if !ok { + return "", "", fmt.Errorf("no 'Conditions' found in annotation") + } + + for _, condition := range conditions { + if condition["Type"] == conditionType { + status := condition["Status"] + message := condition["Message"] + + return status, message, nil + } + } + + return "", "", fmt.Errorf("no condition of type '%s' found", conditionType) +} + +// VerifyNamespaceHasNoResourceQuota checks that there are no ResourceQuota objects in the specified namespace. +func VerifyNamespaceHasNoResourceQuota(client *rancher.Client, clusterID, namespace string) error { + ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return err + } + + rqList, err := ctx.Core.ResourceQuota().List(namespace, metav1.ListOptions{}) + if err != nil { + return err + } + + if len(rqList.Items) != 0 { + return fmt.Errorf("expected no ResourceQuota in namespace %s, but found %d", namespace, len(rqList.Items)) + } + + return nil +} diff --git a/actions/kubeapi/projects/create.go b/actions/kubeapi/projects/create.go index 7f658df08..9814f1d3f 100644 --- a/actions/kubeapi/projects/create.go +++ b/actions/kubeapi/projects/create.go @@ -1,33 +1,71 @@ package projects import ( - "context" - v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3" "github.com/rancher/shepherd/clients/rancher" - "github.com/rancher/shepherd/extensions/unstructured" - "github.com/rancher/shepherd/pkg/api/scheme" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// CreateProject is a helper function that uses the dynamic client to create a project in a cluster. -func CreateProject(client *rancher.Client, project *v3.Project) (*v3.Project, error) { - dynamicClient, err := client.GetDownStreamClusterClient(LocalCluster) +// CreateProject is a helper to create a project using wrangler context +func CreateProject(client *rancher.Client, clusterID string) (*v3.Project, error) { + projectTemplate := NewProjectTemplate(clusterID) + + createdProject, err := client.WranglerContext.Mgmt.Project().Create(projectTemplate) if err != nil { return nil, err } - projectResource := dynamicClient.Resource(ProjectGroupVersionResource).Namespace(project.Namespace) - unstructuredResp, err := projectResource.Create(context.TODO(), unstructured.MustToUnstructured(project), metav1.CreateOptions{}) + err = WaitForProjectFinalizerToUpdate(client, createdProject.Name, createdProject.Namespace, 2) if err != nil { return nil, err } - newProject := &v3.Project{} - err = scheme.Scheme.Convert(unstructuredResp, newProject, unstructuredResp.GroupVersionKind()) + createdProject, err = client.WranglerContext.Mgmt.Project().Get(clusterID, createdProject.Name, metav1.GetOptions{}) if err != nil { return nil, err } - return newProject, nil + return createdProject, nil +} + +// CreateProjectAndNamespace is a helper to create a project and a namespace in the project using wrangler context +func CreateProjectAndNamespace(client *rancher.Client, clusterID string) (*v3.Project, *corev1.Namespace, error) { + createdProject, err := CreateProject(client, clusterID) + if err != nil { + return nil, nil, err + } + + createdNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(client, clusterID, createdProject.Name, nil) + if err != nil { + return nil, nil, err + } + + return createdProject, createdNamespace, nil +} + +// CreateProjectAndNamespaceWithTemplate is a helper to create a project and a namespace in the project using a provided project template +func CreateProjectAndNamespaceWithTemplate(client *rancher.Client, clusterID string, projectTemplate *v3.Project) (*v3.Project, *corev1.Namespace, error) { + createdProject, err := client.WranglerContext.Mgmt.Project().Create(projectTemplate) + if err != nil { + return nil, nil, err + } + + err = WaitForProjectFinalizerToUpdate(client, createdProject.Name, createdProject.Namespace, 2) + if err != nil { + return nil, nil, err + } + + createdProject, err = client.WranglerContext.Mgmt.Project().Get(clusterID, createdProject.Name, metav1.GetOptions{}) + if err != nil { + return nil, nil, err + } + + createdNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(client, clusterID, createdProject.Name, nil) + if err != nil { + return nil, nil, err + } + + return createdProject, createdNamespace, nil } diff --git a/actions/kubeapi/projects/delete.go b/actions/kubeapi/projects/delete.go index 13a704ebc..6ffe6076f 100644 --- a/actions/kubeapi/projects/delete.go +++ b/actions/kubeapi/projects/delete.go @@ -5,13 +5,14 @@ import ( "github.com/rancher/shepherd/clients/rancher" "github.com/rancher/shepherd/extensions/defaults" + rbacapi "github.com/rancher/tests/actions/kubeapi/rbac" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kwait "k8s.io/apimachinery/pkg/util/wait" ) // DeleteProject is a helper function that uses the dynamic client to delete a Project from a cluster. func DeleteProject(client *rancher.Client, projectNamespace string, projectName string) error { - dynamicClient, err := client.GetDownStreamClusterClient(LocalCluster) + dynamicClient, err := client.GetDownStreamClusterClient(rbacapi.LocalCluster) if err != nil { return err } diff --git a/actions/kubeapi/projects/list.go b/actions/kubeapi/projects/list.go index df20de397..1f7b4f10e 100644 --- a/actions/kubeapi/projects/list.go +++ b/actions/kubeapi/projects/list.go @@ -6,12 +6,13 @@ import ( v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3" "github.com/rancher/shepherd/clients/rancher" "github.com/rancher/shepherd/pkg/api/scheme" + rbacapi "github.com/rancher/tests/actions/kubeapi/rbac" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // ListProjects is a helper function that uses the dynamic client to list projects in a cluster. func ListProjects(client *rancher.Client, namespace string, listOpt metav1.ListOptions) (*v3.ProjectList, error) { - dynamicClient, err := client.GetDownStreamClusterClient(LocalCluster) + dynamicClient, err := client.GetDownStreamClusterClient(rbacapi.LocalCluster) if err != nil { return nil, err } diff --git a/actions/kubeapi/projects/projects.go b/actions/kubeapi/projects/projects.go index b51d60f0a..21c62550e 100644 --- a/actions/kubeapi/projects/projects.go +++ b/actions/kubeapi/projects/projects.go @@ -1,22 +1,34 @@ package projects import ( + "context" + v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3" + "github.com/rancher/shepherd/clients/rancher" + "github.com/rancher/shepherd/extensions/defaults" namegen "github.com/rancher/shepherd/pkg/namegenerator" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + kwait "k8s.io/apimachinery/pkg/util/wait" ) const ( - Admin = "admin" - StandardUser = "user" - DefaultNamespace = "fleet-default" - RancherNamespace = "cattle-system" - LocalCluster = "local" - Projects = "projects" - ProjectIDAnnotation = "field.cattle.io/projectId" - GroupName = "management.cattle.io" - Version = "v3" + DummyFinalizer = "dummy" + SystemProjectLabel = "authz.management.cattle.io/system-project" + ContainerDefaultLimitAnnotation = "field.cattle.io/containerDefaultResourceLimit" + ResourceQuotaAnnotation = "field.cattle.io/resourceQuota" + ResourceQuotaStatusAnnotation = "cattle.io/status" + Projects = "projects" + ExistingPodResourceQuotaKey = "pods" + ExtendedPodResourceQuotaKey = "count/pods" + ExtendedJobResourceQuotaKey = "count/jobs.batch" + ExtendedEphemeralStorageResourceQuotaKey = "ephemeral-storage" + ExtendedEphemeralStorageResourceQuotaRequest = "requests.ephemeral-storage" + ExtendedEphemeralStorageResourceQuotaLimit = "limits.ephemeral-storage" + ExceedededResourceQuotaErrorMessage = "exceeded quota" + NamespaceQuotaExceedsProjectQuotaErrorMessage = "namespace default quota limit exceeds project limit" + GroupName = "management.cattle.io" + Version = "v3" ) // ProjectGroupVersionResource is the required Group Version Resource for accessing projects in a cluster, using the dynamic client. @@ -26,7 +38,7 @@ var ProjectGroupVersionResource = schema.GroupVersionResource{ Resource: Projects, } -// NewProjectTemplate is a constructor that creates the project template +// NewProjectTemplate is a constructor that creates a public API project template func NewProjectTemplate(clusterID string) *v3.Project { project := &v3.Project{ ObjectMeta: metav1.ObjectMeta{ @@ -56,3 +68,59 @@ func NewProjectTemplate(clusterID string) *v3.Project { } return project } + +// WaitForProjectFinalizerToUpdate is a helper to wait for project finalizer to update to match the expected finalizer count +func WaitForProjectFinalizerToUpdate(client *rancher.Client, projectName string, projectNamespace string, finalizerCount int) error { + err := kwait.PollUntilContextTimeout(context.Background(), defaults.FiveSecondTimeout, defaults.TenSecondTimeout, false, func(ctx context.Context) (done bool, pollErr error) { + project, pollErr := client.WranglerContext.Mgmt.Project().Get(projectNamespace, projectName, metav1.GetOptions{}) + if pollErr != nil { + return false, pollErr + } + + if len(project.Finalizers) == finalizerCount { + return true, nil + } + return false, pollErr + }) + + if err != nil { + return err + } + + return nil +} + +// ApplyProjectAndNamespaceResourceQuotas applies quotas for project and namespace +func ApplyProjectAndNamespaceResourceQuotas(project *v3.Project, projectExisting *v3.ResourceQuotaLimit, projectExtended map[string]string, namespaceExisting *v3.ResourceQuotaLimit, namespaceExtended map[string]string) { + if projectExisting != nil { + project.Spec.ResourceQuota.Limit = *projectExisting + } + if len(projectExtended) > 0 { + if project.Spec.ResourceQuota.Limit.Extended == nil { + project.Spec.ResourceQuota.Limit.Extended = map[string]string{} + } + for k, v := range projectExtended { + project.Spec.ResourceQuota.Limit.Extended[k] = v + } + } + + if namespaceExisting != nil { + project.Spec.NamespaceDefaultResourceQuota.Limit = *namespaceExisting + } + if len(namespaceExtended) > 0 { + if project.Spec.NamespaceDefaultResourceQuota.Limit.Extended == nil { + project.Spec.NamespaceDefaultResourceQuota.Limit.Extended = map[string]string{} + } + for k, v := range namespaceExtended { + project.Spec.NamespaceDefaultResourceQuota.Limit.Extended[k] = v + } + } +} + +// ApplyProjectContainerDefaultLimits applies container default CPU/memory limits and requests. +func ApplyProjectContainerDefaultLimits(project *v3.Project, cpuLimit, cpuRequest, memoryLimit, memoryRequest string) { + project.Spec.ContainerDefaultResourceLimit.LimitsCPU = cpuLimit + project.Spec.ContainerDefaultResourceLimit.RequestsCPU = cpuRequest + project.Spec.ContainerDefaultResourceLimit.LimitsMemory = memoryLimit + project.Spec.ContainerDefaultResourceLimit.RequestsMemory = memoryRequest +} diff --git a/actions/kubeapi/projects/update.go b/actions/kubeapi/projects/update.go index 5da64e69e..feae72187 100644 --- a/actions/kubeapi/projects/update.go +++ b/actions/kubeapi/projects/update.go @@ -7,12 +7,13 @@ import ( "github.com/rancher/shepherd/clients/rancher" "github.com/rancher/shepherd/extensions/unstructured" "github.com/rancher/shepherd/pkg/api/scheme" + rbacapi "github.com/rancher/tests/actions/kubeapi/rbac" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // UpdateProject is a helper function that uses the dynamic client to update a project in a cluster. func UpdateProject(client *rancher.Client, existingProject *v3.Project, updatedProject *v3.Project) (*v3.Project, error) { - dynamicClient, err := client.GetDownStreamClusterClient(LocalCluster) + dynamicClient, err := client.GetDownStreamClusterClient(rbacapi.LocalCluster) if err != nil { return nil, err } diff --git a/actions/kubeapi/projects/verify.go b/actions/kubeapi/projects/verify.go new file mode 100644 index 000000000..65310cd55 --- /dev/null +++ b/actions/kubeapi/projects/verify.go @@ -0,0 +1,87 @@ +package projects + +import ( + "fmt" + + "github.com/rancher/shepherd/clients/rancher" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// VerifyUsedProjectExtendedResourceQuota checks used project-level extended resource quotas. +func VerifyUsedProjectExtendedResourceQuota(client *rancher.Client, clusterID, projectName string, expectedUsed map[string]string) error { + project, err := client.WranglerContext.Mgmt.Project().Get(clusterID, projectName, metav1.GetOptions{}) + if err != nil { + return err + } + + usedExtended := project.Spec.ResourceQuota.UsedLimit.Extended + if usedExtended == nil { + return fmt.Errorf("project usedLimit.extended is empty") + } + + for resource, expectedVal := range expectedUsed { + actual, ok := usedExtended[resource] + if !ok { + return fmt.Errorf("resource %q not found in project usedLimit.extended", resource) + } + + if actual != expectedVal { + return fmt.Errorf("project quota used mismatch for %q: expected=%s actual=%s", resource, expectedVal, actual) + } + } + + return nil +} + +// VerifyUsedProjectExistingResourceQuota checks used project-level resource quotas defined via existing fields, e.g. pods, limitsCpu, limitsMemory. +func VerifyUsedProjectExistingResourceQuota(client *rancher.Client, clusterID, projectName string, expectedUsed map[string]string) error { + project, err := client.WranglerContext.Mgmt.Project().Get(clusterID, projectName, metav1.GetOptions{}) + if err != nil { + return err + } + + used := project.Spec.ResourceQuota.UsedLimit + + for resource, expectedVal := range expectedUsed { + switch resource { + + case "pods": + if used.Pods != expectedVal { + return fmt.Errorf("project quota used mismatch for %q: expected=%s actual=%s", resource, expectedVal, used.Pods) + } + + case "limitsCpu": + if used.LimitsCPU != expectedVal { + return fmt.Errorf("project quota used mismatch for %q: expected=%s actual=%s", resource, expectedVal, used.LimitsCPU) + } + + case "limitsMemory": + if used.LimitsMemory != expectedVal { + return fmt.Errorf("project quota used mismatch for %q: expected=%s actual=%s", resource, expectedVal, used.LimitsMemory) + } + + default: + return fmt.Errorf("unsupported existing project quota resource %q", resource) + } + } + + return nil +} + +// VerifyProjectHasNoExtendedResourceQuota checks that the project has no extended resource quotas set. +func VerifyProjectHasNoExtendedResourceQuota(client *rancher.Client, clusterID, projectName string) error { + project, err := client.WranglerContext.Mgmt.Project().Get(clusterID, projectName, metav1.GetOptions{}) + if err != nil { + return err + } + + if len(project.Spec.ResourceQuota.Limit.Extended) != 0 { + return fmt.Errorf("expected no extended quota limits, found %+v", project.Spec.ResourceQuota.Limit.Extended) + } + + if len(project.Spec.ResourceQuota.UsedLimit.Extended) != 0 { + return fmt.Errorf("expected no extended quota usage, found %+v", project.Spec.ResourceQuota.UsedLimit.Extended) + } + + return nil +} diff --git a/actions/kubeapi/workloads/pods/pods.go b/actions/kubeapi/workloads/pods/pods.go index 168c8064f..76b24f91d 100644 --- a/actions/kubeapi/workloads/pods/pods.go +++ b/actions/kubeapi/workloads/pods/pods.go @@ -1,13 +1,129 @@ package pods import ( - "k8s.io/apimachinery/pkg/runtime/schema" + "context" + "fmt" + + "github.com/rancher/shepherd/clients/rancher" + "github.com/rancher/shepherd/extensions/defaults" + namegen "github.com/rancher/shepherd/pkg/namegenerator" + clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kwait "k8s.io/apimachinery/pkg/util/wait" +) + +const ( + PauseImage = "registry.k8s.io/pause:3.9" + DefaultImageName = "nginx" ) -// PodGroupVersionResource is the required Group Version Resource for accessing Pods in a cluster, -// using the dynamic client. -var PodGroupVersionResource = schema.GroupVersionResource{ - Group: "", - Version: "v1", - Resource: "pods", +// CreatePodWithResources creates a pod with arbitrary resource requests and limits +func CreatePodWithResources(client *rancher.Client, clusterID, namespace, imageName string, requests, limits map[corev1.ResourceName]string, waitForPod bool) (*corev1.Pod, error) { + if imageName == "" { + imageName = DefaultImageName + } + + ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return nil, err + } + + resources := corev1.ResourceRequirements{} + + if len(requests) > 0 { + resources.Requests = corev1.ResourceList{} + for name, value := range requests { + resources.Requests[name] = resource.MustParse(value) + } + } + + if len(limits) > 0 { + resources.Limits = corev1.ResourceList{} + for name, value := range limits { + resources.Limits[name] = resource.MustParse(value) + } + } + + container := corev1.Container{ + Name: namegen.AppendRandomString("container-"), + Image: imageName, + ImagePullPolicy: corev1.PullIfNotPresent, + } + + if len(resources.Requests) > 0 || len(resources.Limits) > 0 { + container.Resources = resources + } + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: namegen.AppendRandomString("pod-"), + Namespace: namespace, + }, + Spec: corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyNever, + Containers: []corev1.Container{ + container, + }, + }, + } + + createdPod, err := ctx.Core.Pod().Create(pod) + if err != nil { + return nil, err + } + + if waitForPod { + err = WaitForPodRunning(client, clusterID, namespace, createdPod.Name) + if err != nil { + return nil, err + } + } + + return createdPod, nil +} + +// WaitForPodRunning waits until the specified pod reaches the Running state +func WaitForPodRunning(client *rancher.Client, clusterID, namespace, podName string) error { + ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return err + } + + return kwait.PollUntilContextTimeout(context.Background(), defaults.FiveHundredMillisecondTimeout, defaults.OneMinuteTimeout, true, func(context.Context) (bool, error) { + pod, err := ctx.Core.Pod().Get(namespace, podName, metav1.GetOptions{}) + if err != nil { + if k8serrors.IsNotFound(err) { + return false, nil + } + return false, err + } + + switch pod.Status.Phase { + case corev1.PodRunning: + return true, nil + case corev1.PodFailed: + return false, fmt.Errorf("pod %s failed: %s", pod.Name, pod.Status.Message) + default: + return false, nil + } + }, + ) +} + +// DeletePod deletes the specified pod from the given namespace using wrangler context +func DeletePod(client *rancher.Client, clusterID, namespace, podName string) error { + ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) + if err != nil { + return err + } + + err = ctx.Core.Pod().Delete(namespace, podName, &metav1.DeleteOptions{}) + if err != nil { + return err + } + + return nil } diff --git a/actions/projects/projects.go b/actions/projects/projects.go index 47709d767..f2e68d53e 100644 --- a/actions/projects/projects.go +++ b/actions/projects/projects.go @@ -1,8 +1,6 @@ package projects import ( - "context" - "fmt" "sort" "strings" @@ -10,14 +8,9 @@ import ( v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3" "github.com/rancher/shepherd/clients/rancher" management "github.com/rancher/shepherd/clients/rancher/generated/management/v3" - "github.com/rancher/shepherd/extensions/defaults" - namegen "github.com/rancher/shepherd/pkg/namegenerator" - clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - "github.com/rancher/tests/actions/kubeapi/namespaces" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kwait "k8s.io/apimachinery/pkg/util/wait" ) // GetProjectByName is a helper function that returns the project by name in a specific cluster. @@ -87,10 +80,9 @@ func CreateProjectAndNamespace(client *rancher.Client, clusterID string) (*manag return nil, nil, err } - namespaceName := namegen.AppendRandomString("testns") projectName := strings.Split(createdProject.ID, ":")[1] - createdNamespace, err := namespaces.CreateNamespace(client, clusterID, projectName, namespaceName, "{}", map[string]string{}, map[string]string{}) + createdNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(client, clusterID, projectName, nil) if err != nil { return nil, nil, err } @@ -100,12 +92,12 @@ func CreateProjectAndNamespace(client *rancher.Client, clusterID string) (*manag // CreateProjectAndNamespaceUsingWrangler is a helper to create a project (wrangler context) and a namespace in the project func CreateProjectAndNamespaceUsingWrangler(client *rancher.Client, clusterID string) (*v3.Project, *corev1.Namespace, error) { - createdProject, err := CreateProjectUsingWrangler(client, clusterID) + createdProject, err := projectapi.CreateProject(client, clusterID) if err != nil { return nil, nil, err } - createdNamespace, err := CreateNamespaceUsingWrangler(client, clusterID, createdProject.Name, nil) + createdNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(client, clusterID, createdProject.Name, nil) if err != nil { return nil, nil, err } @@ -113,186 +105,15 @@ func CreateProjectAndNamespaceUsingWrangler(client *rancher.Client, clusterID st return createdProject, createdNamespace, nil } -// CreateProjectUsingWrangler is a helper to create a project using wrangler context -func CreateProjectUsingWrangler(client *rancher.Client, clusterID string) (*v3.Project, error) { - projectTemplate := projectsapi.NewProjectTemplate(clusterID) - createdProject, err := client.WranglerContext.Mgmt.Project().Create(projectTemplate) - if err != nil { - return nil, err - } - - err = WaitForProjectFinalizerToUpdate(client, createdProject.Name, createdProject.Namespace, 2) - if err != nil { - return nil, err - } - - createdProject, err = client.WranglerContext.Mgmt.Project().Get(clusterID, createdProject.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - return createdProject, nil -} - -// CreateNamespaceUsingWrangler is a helper to create a namespace in the project using wrangler context -func CreateNamespaceUsingWrangler(client *rancher.Client, clusterID, projectName string, labels map[string]string) (*corev1.Namespace, error) { - namespaceName := namegen.AppendRandomString("testns") - annotations := map[string]string{ - projectsapi.ProjectIDAnnotation: clusterID + ":" + projectName, - } - - ctx, err := clusterapi.GetClusterWranglerContext(client, clusterID) - if err != nil { - return nil, err - } - - createdNamespace, err := ctx.Core.Namespace().Create(&corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespaceName, - Annotations: annotations, - Labels: labels, - }, - }) - if err != nil { - return nil, err - } - - err = WaitForProjectIDUpdate(client, clusterID, projectName, createdNamespace.Name) - if err != nil { - return nil, err - } - - return createdNamespace, nil -} - -// WaitForProjectFinalizerToUpdate is a helper to wait for project finalizer to update to match the expected finalizer count -func WaitForProjectFinalizerToUpdate(client *rancher.Client, projectName string, projectNamespace string, finalizerCount int) error { - err := kwait.PollUntilContextTimeout(context.Background(), defaults.FiveSecondTimeout, defaults.TenSecondTimeout, false, func(ctx context.Context) (done bool, pollErr error) { - project, pollErr := client.WranglerContext.Mgmt.Project().Get(projectNamespace, projectName, metav1.GetOptions{}) - if pollErr != nil { - return false, pollErr - } - - if len(project.Finalizers) == finalizerCount { - return true, nil - } - return false, pollErr - }) - - if err != nil { - return err - } - - return nil -} - -// WaitForProjectIDUpdate is a helper that waits for the project-id annotation and label to be updated in a specified namespace -func WaitForProjectIDUpdate(client *rancher.Client, clusterID, projectName, namespaceName string) error { - expectedAnnotations := map[string]string{ - projectsapi.ProjectIDAnnotation: clusterID + ":" + projectName, - } - - expectedLabels := map[string]string{ - projectsapi.ProjectIDAnnotation: projectName, - } - - err := kwait.PollUntilContextTimeout(context.Background(), defaults.FiveSecondTimeout, defaults.OneMinuteTimeout, false, func(ctx context.Context) (done bool, pollErr error) { - namespace, pollErr := namespaces.GetNamespaceByName(client, clusterID, namespaceName) - if pollErr != nil { - return false, pollErr - } - - for key, expectedValue := range expectedAnnotations { - if actualValue, ok := namespace.Annotations[key]; !ok || actualValue != expectedValue { - return false, nil - } - } - - for key, expectedValue := range expectedLabels { - if actualValue, ok := namespace.Labels[key]; !ok || actualValue != expectedValue { - return false, nil - } - } - - return true, nil - }) - - if err != nil { - return err - } - - return nil -} - // UpdateProjectNamespaceFinalizer is a helper to update the finalizer in a project func UpdateProjectNamespaceFinalizer(client *rancher.Client, existingProject *v3.Project, finalizer []string) (*v3.Project, error) { updatedProject := existingProject.DeepCopy() updatedProject.ObjectMeta.Finalizers = finalizer - updatedProject, err := projectsapi.UpdateProject(client, existingProject, updatedProject) + updatedProject, err := projectapi.UpdateProject(client, existingProject, updatedProject) if err != nil { return nil, err } return updatedProject, nil } - -// MoveNamespaceToProject updates the project annotation/label to move the namespace into a different project -func MoveNamespaceToProject(client *rancher.Client, clusterID, namespaceName, newProjectName string) error { - ns, err := namespaces.GetNamespaceByName(client, clusterID, namespaceName) - if err != nil { - return fmt.Errorf("failed to get namespace %s: %w", namespaceName, err) - } - - if ns.Annotations == nil { - ns.Annotations = make(map[string]string) - } - if ns.Labels == nil { - ns.Labels = make(map[string]string) - } - - ns.Annotations[projectsapi.ProjectIDAnnotation] = fmt.Sprintf("%s:%s", clusterID, newProjectName) - ns.Labels[projectsapi.ProjectIDAnnotation] = newProjectName - - clusterContext, err := clusterapi.GetClusterWranglerContext(client, clusterID) - if err != nil { - return fmt.Errorf("failed to get wrangler context for cluster %s: %w", clusterID, err) - } - latestNS, err := namespaces.GetNamespaceByName(client, clusterID, namespaceName) - if err != nil { - return fmt.Errorf("failed to fetch namespace %s: %w", namespaceName, err) - } - ns.ResourceVersion = latestNS.ResourceVersion - - if _, err := clusterContext.Core.Namespace().Update(ns); err != nil { - return fmt.Errorf("failed to update namespace %s with new project annotation: %w", namespaceName, err) - } - - if err := WaitForProjectIDUpdate(client, clusterID, newProjectName, namespaceName); err != nil { - return fmt.Errorf("project ID annotation/label not updated for namespace %s: %w", namespaceName, err) - } - - return nil -} - -// GetNamespacesInProject retrieves all namespaces in a specific project within a cluster -func GetNamespacesInProject(client *rancher.Client, clusterID, projectName string) ([]*corev1.Namespace, error) { - clusterContext, err := clusterapi.GetClusterWranglerContext(client, clusterID) - if err != nil { - return nil, fmt.Errorf("failed to get wrangler context for cluster %s: %w", clusterID, err) - } - - nsList, err := clusterContext.Core.Namespace().List(metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", projectsapi.ProjectIDAnnotation, projectName), - }) - if err != nil { - return nil, fmt.Errorf("failed to list namespaces for project %s: %w", projectName, err) - } - - namespaces := make([]*corev1.Namespace, 0, len(nsList.Items)) - for i := range nsList.Items { - namespaces = append(namespaces, &nsList.Items[i]) - } - - return namespaces, nil -} diff --git a/actions/rbac/verify.go b/actions/rbac/verify.go index 06fd42df8..a15ef9166 100644 --- a/actions/rbac/verify.go +++ b/actions/rbac/verify.go @@ -13,9 +13,8 @@ import ( v1 "github.com/rancher/shepherd/clients/rancher/v1" "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/extensions/users" - namegen "github.com/rancher/shepherd/pkg/namegenerator" - namespacesapi "github.com/rancher/tests/actions/kubeapi/namespaces" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" rbacapi "github.com/rancher/tests/actions/kubeapi/rbac" "github.com/rancher/tests/actions/namespaces" log "github.com/sirupsen/logrus" @@ -99,8 +98,7 @@ func VerifyUserCanGetProject(t *testing.T, client, standardClient *rancher.Clien // VerifyUserCanCreateProjects validates a user with the required cluster permissions are able/not able to create projects in the downstream cluster func VerifyUserCanCreateProjects(t *testing.T, client, standardClient *rancher.Client, clusterID string, role Role) { - projectTemplate := projectsapi.NewProjectTemplate(clusterID) - memberProject, err := standardClient.WranglerContext.Mgmt.Project().Create(projectTemplate) + memberProject, err := projectapi.CreateProject(standardClient, clusterID) switch role { case ClusterOwner, ClusterMember: require.NoError(t, err) @@ -116,14 +114,12 @@ func VerifyUserCanCreateNamespace(t *testing.T, client, standardClient *rancher. standardClient, err := standardClient.ReLogin() require.NoError(t, err) - namespaceName := namegen.AppendRandomString("testns-") - createdNamespace, checkErr := namespacesapi.CreateNamespace(standardClient, clusterID, project.Name, namespaceName, "", map[string]string{}, map[string]string{}) + createdNamespace, checkErr := namespaceapi.CreateNamespaceUsingWrangler(standardClient, clusterID, project.Name, nil) switch role { case ClusterOwner, ProjectOwner, ProjectMember: require.NoError(t, checkErr) log.Info("Created a namespace as role ", role, createdNamespace.Name) - assert.Equal(t, namespaceName, createdNamespace.Name) namespaceStatus := &coreV1.NamespaceStatus{} err = v1.ConvertToK8sType(createdNamespace.Status, namespaceStatus) @@ -177,8 +173,7 @@ func VerifyUserCanDeleteNamespace(t *testing.T, client, standardClient *rancher. steveStandardClient, err := standardClient.Steve.ProxyDownstream(clusterID) require.NoError(t, err) - namespaceName := namegen.AppendRandomString("testns-") - adminNamespace, err := namespacesapi.CreateNamespace(client, clusterID, project.Name, namespaceName+"-admin", "", map[string]string{}, map[string]string{}) + adminNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(client, clusterID, project.Name, nil) require.NoError(t, err) namespaceID, err := steveAdminClient.SteveType(namespaces.NamespaceSteveType).ByID(adminNamespace.Name) diff --git a/go.mod b/go.mod index 6e877ab13..9004ad82f 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ replace ( github.com/openshift/api => github.com/openshift/api v0.0.0-20191219222812-2987a591a72c github.com/openshift/client-go => github.com/openshift/client-go v0.0.0-20191219222812-2987a591a72c - github.com/rancher/rancher/pkg/apis => github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83 - github.com/rancher/rancher/pkg/client => github.com/rancher/rancher/pkg/client v0.0.0-20251111120454-f829d8f1dc83 + github.com/rancher/rancher/pkg/apis => github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af + github.com/rancher/rancher/pkg/client => github.com/rancher/rancher/pkg/client v0.0.0-20260105201356-c4811cb9f2af github.com/rancher/tests/actions => ./actions github.com/rancher/tests/interoperability => ./interoperability @@ -71,9 +71,9 @@ require ( require ( github.com/gruntwork-io/terratest v0.49.0 github.com/mattn/go-sqlite3 v1.14.28 - github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738 - github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67 - github.com/rancher/tests/actions v0.0.0-20251111162746-32a475483166 + github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e + github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae + github.com/rancher/tests/actions v0.0.0-20260105193042-a805683addc3 github.com/rancher/tests/interoperability v0.0.0-00010101000000-000000000000 github.com/rancher/tfp-automation v0.0.0-20251219210947-f4a1a9882c29 go.qase.io/qase-api-client v0.0.0-00010101000000-000000000000 @@ -109,34 +109,34 @@ require ( github.com/rancher/apiserver v0.8.0 // indirect github.com/rancher/backup-restore-operator v1.2.1 github.com/rancher/eks-operator v1.13.0-rc.4 // indirect - github.com/rancher/fleet/pkg/apis v0.14.0-rc.1 + github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4 github.com/rancher/gke-operator v1.13.0-rc.3 // indirect github.com/rancher/lasso v0.2.5 // indirect - github.com/rancher/norman v0.8.0 + github.com/rancher/norman v0.8.1 github.com/rancher/rke v1.8.0 // indirect github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20250930163923-f2c9e60b1078 // indirect github.com/rancher/wrangler v1.1.2 // indirect github.com/rancher/wrangler/v3 v3.3.1 // indirect github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.11.1 - golang.org/x/crypto v0.43.0 - golang.org/x/net v0.46.0 + golang.org/x/crypto v0.45.0 + golang.org/x/net v0.47.0 golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/text v0.31.0 // indirect google.golang.org/grpc v1.75.1 // indirect gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.34.1 + k8s.io/api v0.34.3 k8s.io/apiextensions-apiserver v0.34.1 // indirect - k8s.io/apimachinery v0.34.1 + k8s.io/apimachinery v0.34.3 k8s.io/apiserver v0.34.1 // indirect k8s.io/cli-runtime v0.34.1 // indirect k8s.io/client-go v12.0.0+incompatible k8s.io/kube-aggregator v0.34.1 // indirect k8s.io/kube-openapi v0.31.5 // indirect k8s.io/kubectl v0.34.1 // indirect - k8s.io/kubernetes v1.34.1 // indirect + k8s.io/kubernetes v1.34.2 // indirect k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect sigs.k8s.io/cluster-api v1.10.6 // indirect sigs.k8s.io/yaml v1.6.0 // indirect @@ -190,8 +190,8 @@ require ( github.com/zclconf/go-cty v1.15.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/tools v0.37.0 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/tools v0.39.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect @@ -277,8 +277,8 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/term v0.36.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect golang.org/x/time v0.13.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index cdf64601b..6fb9a36f0 100644 --- a/go.sum +++ b/go.sum @@ -2170,25 +2170,25 @@ github.com/rancher/cis-operator v1.0.11 h1:3fOkwk45no0S9NaGFYgIINqxjBChqvvGhXWY/ github.com/rancher/cis-operator v1.0.11/go.mod h1:tSfpBeEcxYizMRT+rPtvQebJ5CcSJCKL9LW16Zdm7Zc= github.com/rancher/eks-operator v1.13.0-rc.4 h1:XowN8+m3QZTIBOBLzar4frtz0xtREb9kcX6KXhF4eas= github.com/rancher/eks-operator v1.13.0-rc.4/go.mod h1:SbaKX2ttFWCxGOYkrKYeWH/6E4oToq2rRTcrMa2Mmdk= -github.com/rancher/fleet/pkg/apis v0.14.0-rc.1 h1:ZsDc25j4/iuKJ8DhxaOSnHdqOskRRe7QxJAdD9HBn28= -github.com/rancher/fleet/pkg/apis v0.14.0-rc.1/go.mod h1:oc+QHbx4P9guY34dr6UbzCOgt17Q9eSZhlyOs7xSinY= +github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4 h1:l6pdMToVQSuhFaNmENuY1+v+5lltwHvw92zbt7iK6sU= +github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4/go.mod h1:srlFTlA6425rCPRELTdtFcZM8wDNPaqW4O4aj6sArs4= github.com/rancher/gke-operator v1.13.0-rc.3 h1:a6U+7+XIbJPH2CE7/vFUx6RpThNbFl7fqIqkEBb6zmA= github.com/rancher/gke-operator v1.13.0-rc.3/go.mod h1:TroxpmqMh63Hf4H5bC+2GYcgOCQp9kIUDfyKdNAMo6Q= github.com/rancher/lasso v0.0.0-20200515155337-a34e1e26ad91/go.mod h1:G6Vv2aj6xB2YjTVagmu4NkhBvbE8nBcGykHRENH6arI= github.com/rancher/lasso v0.2.5 h1:K++lWDDdfeN98Ixc1kCfUq0/q6tLjoHN++Np6QntXw0= github.com/rancher/lasso v0.2.5/go.mod h1:71rWfv+KkdSmSxZ9Ly5QYhxAu0nEUcaq9N2ByjcHqAM= -github.com/rancher/norman v0.8.0 h1://ZSe+B53cMgPNAbR7QBhzvIfWBxR4KaPWTKqG+g+O4= -github.com/rancher/norman v0.8.0/go.mod h1:vZ5qL+eKodJ7zOMQYdl6jwMrSFrqTKpA+KYSFEKew2M= +github.com/rancher/norman v0.8.1 h1:114Rdt3xsWTUdqaxlIR2F6PJT0ls01vF0Rfglustgow= +github.com/rancher/norman v0.8.1/go.mod h1:vZ5qL+eKodJ7zOMQYdl6jwMrSFrqTKpA+KYSFEKew2M= github.com/rancher/qase-go/client v0.0.0-20250627195016-142ff3dfec16 h1:FGbrfQuYsF94Ov+BDTGrIKOB73f93DDLlfI0nKjuPOk= github.com/rancher/qase-go/client v0.0.0-20250627195016-142ff3dfec16/go.mod h1:NP3xboG+t2p+XMnrcrJ/L384Ki0Cp3Pww/X+vm5Jcy0= -github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738 h1:qalvtaJ4WQzPu0lkJFSTh3L0TgIEU4h7Kni/lBdfAQw= -github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738/go.mod h1:NnexTOmNU92x0L5QfbeyUH6RPw87y7WHuLbAeDre9W8= -github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83 h1:5wbUhQaEesGsigLFNbXRJyACuGy2UNWMXVmBfDynk7M= -github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83/go.mod h1:xyYMxIycb9QpdxZjUxf95Cc4E27rfma8Z77U5ysRx3A= +github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e h1:HExd4+6+bF4aYv0Wj/eORxc65Y4einqWaDqvJE+yjys= +github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e/go.mod h1:ORjiG9PXFw0JT3+CtC5Ih34joRgwCgedRhQQbos8Nag= +github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af h1:ZXIKdKynB1aelfSa4qyt9nCpa0kXTizkS7i58FoNaxY= +github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af/go.mod h1:NwWL+lOkxPRibQ6j+9uFSo6t1CJ18z1oY4OYJMOQ/R0= github.com/rancher/rke v1.8.0 h1:87jeoOccnnNCq27YgWgMh4o0GVrrVKbw+zfo+cHMZlo= github.com/rancher/rke v1.8.0/go.mod h1:x9N1abruzDFMwTpqq2cnaDYpKCptlNoW8VraNWB6Pc4= -github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67 h1:1BUWLjjtHbbPtQ4rgHAWruCWxM8qa0+QYwcvraneeME= -github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67/go.mod h1:SJtW8Jqv0rphZzsGnvB965YdyR2FqFtB+TbbzVLt8F4= +github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae h1:9Krx+fO7yiPGHEiAdknVRHwiwlPrg/RmtLfqOUFABxk= +github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae/go.mod h1:SJtW8Jqv0rphZzsGnvB965YdyR2FqFtB+TbbzVLt8F4= github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20250930163923-f2c9e60b1078 h1:1MJSgYkgXhr/Zc5idJkKa10SiBQd0HVtbxVOBoghlzY= github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20250930163923-f2c9e60b1078/go.mod h1:CV2Soy/Skw8/SA9dDJVgpeHxoEdtjYkNpNy6xvvC5kA= github.com/rancher/tfp-automation v0.0.0-20251219210947-f4a1a9882c29 h1:K7gKqQy8w4oPlm85x/t3WjMhHPi0nQTX49RMzJnkGK0= @@ -2402,8 +2402,8 @@ golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ss golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2482,8 +2482,8 @@ golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2578,8 +2578,8 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2656,8 +2656,8 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2782,8 +2782,8 @@ golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2816,8 +2816,8 @@ golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2846,8 +2846,8 @@ golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2948,8 +2948,8 @@ golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= 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= diff --git a/interoperability/go.mod b/interoperability/go.mod index 3b4655d3f..1596c0acd 100644 --- a/interoperability/go.mod +++ b/interoperability/go.mod @@ -9,8 +9,8 @@ replace ( github.com/docker/docker => github.com/docker/docker v20.10.27+incompatible // rancher-machine requires a replace is set github.com/openshift/api => github.com/openshift/api v0.0.0-20191219222812-2987a591a72c github.com/openshift/client-go => github.com/openshift/client-go v0.0.0-20191219222812-2987a591a72c - github.com/rancher/rancher/pkg/apis => github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83 - github.com/rancher/rancher/pkg/client => github.com/rancher/rancher/pkg/client v0.0.0-20251111120454-f829d8f1dc83 + github.com/rancher/rancher/pkg/apis => github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af + github.com/rancher/rancher/pkg/client => github.com/rancher/rancher/pkg/client v0.0.0-20260105201356-c4811cb9f2af github.com/rancher/tests/actions => ./../actions go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc => go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 @@ -63,18 +63,18 @@ require ( github.com/harvester/harvester v1.5.0 github.com/mattn/go-sqlite3 v1.14.28 github.com/rancher/backup-restore-operator v1.2.1 - github.com/rancher/fleet/pkg/apis v0.14.0-rc.1 - github.com/rancher/norman v0.8.0 + github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4 + github.com/rancher/norman v0.8.1 github.com/rancher/rancher/pkg/apis v0.0.0 - github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67 - github.com/rancher/tests/actions v0.0.0-20251111162746-32a475483166 + github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae + github.com/rancher/tests/actions v0.0.0-20260105193042-a805683addc3 github.com/rancher/tfp-automation v0.0.0-20251219210947-f4a1a9882c29 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.34.1 + k8s.io/api v0.34.3 k8s.io/apiextensions-apiserver v0.34.1 - k8s.io/apimachinery v0.34.1 + k8s.io/apimachinery v0.34.3 ) require ( @@ -155,7 +155,7 @@ require ( github.com/rancher/eks-operator v1.13.0-rc.4 // indirect github.com/rancher/gke-operator v1.13.0-rc.3 // indirect github.com/rancher/lasso v0.2.5 // indirect - github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738 // indirect + github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e // indirect github.com/rancher/rke v1.8.0 // indirect github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20250930163923-f2c9e60b1078 // indirect github.com/rancher/wrangler v1.1.2 // indirect @@ -172,16 +172,16 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.43.0 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.46.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/term v0.36.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.13.0 // indirect - golang.org/x/tools v0.37.0 // indirect + golang.org/x/tools v0.39.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -195,7 +195,7 @@ require ( k8s.io/kube-aggregator v0.34.1 // indirect k8s.io/kube-openapi v0.31.5 // indirect k8s.io/kubectl v0.34.1 // indirect - k8s.io/kubernetes v1.34.1 // indirect + k8s.io/kubernetes v1.34.2 // indirect k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect kubevirt.io/api v1.4.0 // indirect kubevirt.io/containerized-data-importer-api v1.61.0 // indirect diff --git a/interoperability/go.sum b/interoperability/go.sum index 39ef75c60..352d6a8fd 100644 --- a/interoperability/go.sum +++ b/interoperability/go.sum @@ -2066,23 +2066,23 @@ github.com/rancher/backup-restore-operator v1.2.1 h1:jsCChVZlezQnEagVs9YG+kaG8w5 github.com/rancher/backup-restore-operator v1.2.1/go.mod h1:LSqy4Qzt+GVfLed63cSvwzAJ75tWH7Xdvczdkp4RiW4= github.com/rancher/eks-operator v1.13.0-rc.4 h1:XowN8+m3QZTIBOBLzar4frtz0xtREb9kcX6KXhF4eas= github.com/rancher/eks-operator v1.13.0-rc.4/go.mod h1:SbaKX2ttFWCxGOYkrKYeWH/6E4oToq2rRTcrMa2Mmdk= -github.com/rancher/fleet/pkg/apis v0.14.0-rc.1 h1:ZsDc25j4/iuKJ8DhxaOSnHdqOskRRe7QxJAdD9HBn28= -github.com/rancher/fleet/pkg/apis v0.14.0-rc.1/go.mod h1:oc+QHbx4P9guY34dr6UbzCOgt17Q9eSZhlyOs7xSinY= +github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4 h1:l6pdMToVQSuhFaNmENuY1+v+5lltwHvw92zbt7iK6sU= +github.com/rancher/fleet/pkg/apis v0.15.0-alpha.4/go.mod h1:srlFTlA6425rCPRELTdtFcZM8wDNPaqW4O4aj6sArs4= github.com/rancher/gke-operator v1.13.0-rc.3 h1:a6U+7+XIbJPH2CE7/vFUx6RpThNbFl7fqIqkEBb6zmA= github.com/rancher/gke-operator v1.13.0-rc.3/go.mod h1:TroxpmqMh63Hf4H5bC+2GYcgOCQp9kIUDfyKdNAMo6Q= github.com/rancher/lasso v0.0.0-20200515155337-a34e1e26ad91/go.mod h1:G6Vv2aj6xB2YjTVagmu4NkhBvbE8nBcGykHRENH6arI= github.com/rancher/lasso v0.2.5 h1:K++lWDDdfeN98Ixc1kCfUq0/q6tLjoHN++Np6QntXw0= github.com/rancher/lasso v0.2.5/go.mod h1:71rWfv+KkdSmSxZ9Ly5QYhxAu0nEUcaq9N2ByjcHqAM= -github.com/rancher/norman v0.8.0 h1://ZSe+B53cMgPNAbR7QBhzvIfWBxR4KaPWTKqG+g+O4= -github.com/rancher/norman v0.8.0/go.mod h1:vZ5qL+eKodJ7zOMQYdl6jwMrSFrqTKpA+KYSFEKew2M= -github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738 h1:qalvtaJ4WQzPu0lkJFSTh3L0TgIEU4h7Kni/lBdfAQw= -github.com/rancher/rancher v0.0.0-20251203234820-b95b2fb0d738/go.mod h1:NnexTOmNU92x0L5QfbeyUH6RPw87y7WHuLbAeDre9W8= -github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83 h1:5wbUhQaEesGsigLFNbXRJyACuGy2UNWMXVmBfDynk7M= -github.com/rancher/rancher/pkg/apis v0.0.0-20251111120454-f829d8f1dc83/go.mod h1:xyYMxIycb9QpdxZjUxf95Cc4E27rfma8Z77U5ysRx3A= +github.com/rancher/norman v0.8.1 h1:114Rdt3xsWTUdqaxlIR2F6PJT0ls01vF0Rfglustgow= +github.com/rancher/norman v0.8.1/go.mod h1:vZ5qL+eKodJ7zOMQYdl6jwMrSFrqTKpA+KYSFEKew2M= +github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e h1:HExd4+6+bF4aYv0Wj/eORxc65Y4einqWaDqvJE+yjys= +github.com/rancher/rancher v0.0.0-20251223145833-24cecce3325e/go.mod h1:ORjiG9PXFw0JT3+CtC5Ih34joRgwCgedRhQQbos8Nag= +github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af h1:ZXIKdKynB1aelfSa4qyt9nCpa0kXTizkS7i58FoNaxY= +github.com/rancher/rancher/pkg/apis v0.0.0-20260105201356-c4811cb9f2af/go.mod h1:NwWL+lOkxPRibQ6j+9uFSo6t1CJ18z1oY4OYJMOQ/R0= github.com/rancher/rke v1.8.0 h1:87jeoOccnnNCq27YgWgMh4o0GVrrVKbw+zfo+cHMZlo= github.com/rancher/rke v1.8.0/go.mod h1:x9N1abruzDFMwTpqq2cnaDYpKCptlNoW8VraNWB6Pc4= -github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67 h1:1BUWLjjtHbbPtQ4rgHAWruCWxM8qa0+QYwcvraneeME= -github.com/rancher/shepherd v0.0.0-20251203195144-c9f6483abe67/go.mod h1:SJtW8Jqv0rphZzsGnvB965YdyR2FqFtB+TbbzVLt8F4= +github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae h1:9Krx+fO7yiPGHEiAdknVRHwiwlPrg/RmtLfqOUFABxk= +github.com/rancher/shepherd v0.0.0-20251216155429-44067c0178ae/go.mod h1:SJtW8Jqv0rphZzsGnvB965YdyR2FqFtB+TbbzVLt8F4= github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20250930163923-f2c9e60b1078 h1:1MJSgYkgXhr/Zc5idJkKa10SiBQd0HVtbxVOBoghlzY= github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20250930163923-f2c9e60b1078/go.mod h1:CV2Soy/Skw8/SA9dDJVgpeHxoEdtjYkNpNy6xvvC5kA= github.com/rancher/tfp-automation v0.0.0-20251219210947-f4a1a9882c29 h1:K7gKqQy8w4oPlm85x/t3WjMhHPi0nQTX49RMzJnkGK0= @@ -2280,8 +2280,8 @@ golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ss golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2360,8 +2360,8 @@ golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2456,8 +2456,8 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2533,8 +2533,8 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2659,8 +2659,8 @@ golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2693,8 +2693,8 @@ golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2723,8 +2723,8 @@ golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2825,8 +2825,8 @@ golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= 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= diff --git a/validation/fleet/provisioning/networkisolation.go b/validation/fleet/provisioning/networkisolation.go index f1f2319c1..c5557fefb 100644 --- a/validation/fleet/provisioning/networkisolation.go +++ b/validation/fleet/provisioning/networkisolation.go @@ -229,5 +229,5 @@ func updateNamespaceWithNewProject(client *rancher.Client, clusterName string, r return err } - return projects.WaitForProjectIDUpdate(client, clusterName, hardenedProjectName, hardenedNS.Name) + return namespaces.WaitForProjectIDUpdate(client, clusterName, hardenedProjectName, hardenedNS.Name) } diff --git a/validation/projects/project_scoped_secrets_test.go b/validation/projects/project_scoped_secrets_test.go index 3c2c1ca12..5682d9060 100644 --- a/validation/projects/project_scoped_secrets_test.go +++ b/validation/projects/project_scoped_secrets_test.go @@ -10,17 +10,14 @@ import ( "github.com/rancher/shepherd/clients/rancher" management "github.com/rancher/shepherd/clients/rancher/generated/management/v3" "github.com/rancher/shepherd/extensions/clusters" - "github.com/rancher/shepherd/pkg/config" "github.com/rancher/shepherd/pkg/session" - clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" secretsapi "github.com/rancher/tests/actions/kubeapi/secrets" - "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/secrets" - "github.com/rancher/tests/actions/workloads/deployment" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -64,7 +61,7 @@ func (pss *ProjectScopedSecretTestSuite) SetupSuite() { func (pss *ProjectScopedSecretTestSuite) testProjectScopedSecret(clusterID string, secretType corev1.SecretType, secretData map[string][]byte) (*v3.Project, []*corev1.Namespace, *corev1.Secret) { log.Info("Create a project in the cluster.") - createdProject, err := projects.CreateProjectUsingWrangler(pss.client, clusterID) + createdProject, err := projectapi.CreateProject(pss.client, clusterID) require.NoError(pss.T(), err) log.Info("Create a project scoped secret in the project.") @@ -76,7 +73,7 @@ func (pss *ProjectScopedSecretTestSuite) testProjectScopedSecret(clusterID strin require.NoError(pss.T(), err) log.Info("Create five namespaces in the project.") - namespaceList, err := createNamespacesInProject(pss.client, clusterID, createdProject.Name, 5) + namespaceList, err := namespaceapi.CreateMultipleNamespacesInProject(pss.client, clusterID, createdProject.Name, 5) require.NoError(pss.T(), err) log.Info("Verify that the secret is propagated to all the namespaces in the project and the data matches the original project-scoped secret.") @@ -132,11 +129,11 @@ func (pss *ProjectScopedSecretTestSuite) TestCreateProjectScopedSecretAfterCreat defer subSession.Cleanup() log.Info("Create a project in the cluster.") - createdProject, err := projects.CreateProjectUsingWrangler(pss.client, pss.cluster.ID) + createdProject, err := projectapi.CreateProject(pss.client, pss.cluster.ID) require.NoError(pss.T(), err) log.Info("Create five namespaces in the project.") - namespaceList, err := createNamespacesInProject(pss.client, pss.cluster.ID, createdProject.Name, 5) + namespaceList, err := namespaceapi.CreateMultipleNamespacesInProject(pss.client, pss.cluster.ID, createdProject.Name, 5) require.NoError(pss.T(), err) log.Info("Create a project scoped secret in the project.") @@ -199,11 +196,11 @@ func (pss *ProjectScopedSecretTestSuite) TestProjectScopedSecretCleanupOnProject createdProject, namespaceList, createdProjectScopedSecret := pss.testProjectScopedSecret(pss.cluster.ID, corev1.SecretTypeOpaque, opaqueSecretData) log.Infof("Deleting the project: %s", createdProject.Name) - err := projectsapi.DeleteProject(pss.client, pss.cluster.ID, createdProject.Name) + err := projectapi.DeleteProject(pss.client, pss.cluster.ID, createdProject.Name) require.NoError(pss.T(), err, "Failed to delete the project") log.Infof("Verify the project %s is deleted", createdProject.Name) - projectList, err := projectsapi.ListProjects(pss.client, createdProject.Namespace, metav1.ListOptions{ + projectList, err := projectapi.ListProjects(pss.client, createdProject.Namespace, metav1.ListOptions{ FieldSelector: "metadata.name=" + createdProject.Name, }) require.NoError(pss.T(), err) @@ -226,23 +223,23 @@ func (pss *ProjectScopedSecretTestSuite) TestMoveNamespaceFromProjectWithoutToWi createdProject1, _, createdProjectScopedSecret := pss.testProjectScopedSecret(pss.cluster.ID, corev1.SecretTypeOpaque, opaqueSecretData) log.Info("Creating a second project in the cluster.") - createdProject2, err := projects.CreateProjectUsingWrangler(pss.client, pss.cluster.ID) + createdProject2, err := projectapi.CreateProject(pss.client, pss.cluster.ID) require.NoError(pss.T(), err) log.Info("Creating a namespace in the second project.") - namespaceList2, err := createNamespacesInProject(pss.client, pss.cluster.ID, createdProject2.Name, 1) + namespaceList2, err := namespaceapi.CreateMultipleNamespacesInProject(pss.client, pss.cluster.ID, createdProject2.Name, 1) require.NoError(pss.T(), err) targetNamespace := namespaceList2[0] log.Infof("Moving namespace '%s' from project '%s' to '%s'", targetNamespace.Name, createdProject2.Name, createdProject1.Name) - err = projects.MoveNamespaceToProject(pss.client, pss.cluster.ID, targetNamespace.Name, createdProject1.Name) + err = namespaceapi.MoveNamespaceToProject(pss.client, pss.cluster.ID, targetNamespace.Name, createdProject1.Name) require.NoError(pss.T(), err, "Failed to move namespace to project '%s'", createdProject1.Name) log.Infof("Verify that the project scoped secret '%s' is propagated to '%s'", createdProjectScopedSecret.Name, targetNamespace.Name) err = secrets.WaitForSecretInNamespaces(pss.client, pss.cluster.ID, createdProjectScopedSecret.Name, []*corev1.Namespace{targetNamespace}, true) require.NoError(pss.T(), err, "Project-scoped secret was not propagated to moved namespace") - namespaceList, err := projects.GetNamespacesInProject(pss.client, pss.cluster.ID, createdProject1.Name) + namespaceList, err := namespaceapi.GetNamespacesInProject(pss.client, pss.cluster.ID, createdProject1.Name) require.NoError(pss.T(), err) err = secrets.ValidatePropagatedNamespaceSecrets(pss.client, pss.cluster.ID, createdProject1.Name, createdProjectScopedSecret, namespaceList) require.NoError(pss.T(), err) @@ -255,19 +252,19 @@ func (pss *ProjectScopedSecretTestSuite) TestMoveNamespaceFromProjectWithToWitho createdProject1, namespaceList1, createdProjectScopedSecret := pss.testProjectScopedSecret(pss.cluster.ID, corev1.SecretTypeOpaque, opaqueSecretData) log.Info("Creating a second project in the cluster.") - createdProject2, err := projects.CreateProjectUsingWrangler(pss.client, pss.cluster.ID) + createdProject2, err := projectapi.CreateProject(pss.client, pss.cluster.ID) require.NoError(pss.T(), err) targetNamespace := namespaceList1[0] log.Infof("Moving namespace '%s' from project '%s' to '%s'", targetNamespace.Name, createdProject1.Name, createdProject2.Name) - err = projects.MoveNamespaceToProject(pss.client, pss.cluster.ID, targetNamespace.Name, createdProject2.Name) + err = namespaceapi.MoveNamespaceToProject(pss.client, pss.cluster.ID, targetNamespace.Name, createdProject2.Name) require.NoError(pss.T(), err, "Failed to move namespace to project '%s'", createdProject2.Name) log.Infof("Verifying project scoped secret '%s' is removed from namespace '%s'", createdProjectScopedSecret.Name, targetNamespace.Name) err = secrets.WaitForSecretInNamespaces(pss.client, pss.cluster.ID, createdProjectScopedSecret.Name, []*corev1.Namespace{targetNamespace}, false) require.NoError(pss.T(), err, "Project-scoped secret was not removed after namespace moved") - namespaceList, err := projects.GetNamespacesInProject(pss.client, pss.cluster.ID, createdProject1.Name) + namespaceList, err := namespaceapi.GetNamespacesInProject(pss.client, pss.cluster.ID, createdProject1.Name) require.NoError(pss.T(), err) err = secrets.ValidatePropagatedNamespaceSecrets(pss.client, pss.cluster.ID, createdProject1.Name, createdProjectScopedSecret, namespaceList) require.NoError(pss.T(), err) @@ -334,14 +331,14 @@ func (pss *ProjectScopedSecretTestSuite) TestProjectScopedSecretAsClusterMember( standardUser, standardUserClient, err := rbac.AddUserWithRoleToCluster(pss.client, rbac.StandardUser.String(), role, pss.cluster, nil) require.NoError(pss.T(), err) - projectTemplate := projectsapi.NewProjectTemplate(pss.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(pss.cluster.ID) projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": standardUser.ID, } createdProject, err := standardUserClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(pss.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) require.NoError(pss.T(), err) log.Info("Create a project scoped secret in the project.") @@ -353,7 +350,7 @@ func (pss *ProjectScopedSecretTestSuite) TestProjectScopedSecretAsClusterMember( require.NoError(pss.T(), err) log.Info("Create five namespaces in the project.") - namespaceList, err := createNamespacesInProject(standardUserClient, pss.cluster.ID, createdProject.Name, 5) + namespaceList, err := namespaceapi.CreateMultipleNamespacesInProject(standardUserClient, pss.cluster.ID, createdProject.Name, 5) require.NoError(pss.T(), err) log.Info("Verify that the secret is propagated to all the namespaces in the project and the data matches the original project-scoped secret.") @@ -382,7 +379,7 @@ func (pss *ProjectScopedSecretTestSuite) TestProjectMemberCannotAccessOtherProje createdProject1, _, createdProjectSecret := pss.testProjectScopedSecret(pss.cluster.ID, corev1.SecretTypeOpaque, opaqueSecretData) log.Info("Create another project in the cluster.") - createdProject2, err := projects.CreateProjectUsingWrangler(pss.client, pss.cluster.ID) + createdProject2, err := projectapi.CreateProject(pss.client, pss.cluster.ID) require.NoError(pss.T(), err) role := rbac.ProjectMember.String() @@ -474,7 +471,7 @@ func (pss *ProjectScopedSecretTestSuite) TestProjectScopedSecretWithSameNameInDi _, _, createdProjectScopedSecret := pss.testProjectScopedSecret(pss.cluster.ID, corev1.SecretTypeOpaque, opaqueSecretData) log.Info("Creating a second project in the cluster.") - createdProject2, err := projects.CreateProjectUsingWrangler(pss.client, pss.cluster.ID) + createdProject2, err := projectapi.CreateProject(pss.client, pss.cluster.ID) require.NoError(pss.T(), err) log.Infof("Create a project scoped secret with the same name '%s' in project '%s'", createdProjectScopedSecret.Name, createdProject2.Name) @@ -500,11 +497,11 @@ func (pss *ProjectScopedSecretTestSuite) TestProjectScopedSecrettPropagatedToNam defer subSession.Cleanup() log.Info("Create a project in the cluster.") - createdProject, err := projects.CreateProjectUsingWrangler(pss.client, pss.cluster.ID) + createdProject, err := projectapi.CreateProject(pss.client, pss.cluster.ID) require.NoError(pss.T(), err) log.Info("Create five namespaces in the project.") - namespaceList, err := createNamespacesInProject(pss.client, pss.cluster.ID, createdProject.Name, 5) + namespaceList, err := namespaceapi.CreateMultipleNamespacesInProject(pss.client, pss.cluster.ID, createdProject.Name, 5) require.NoError(pss.T(), err) targetNamespace := namespaceList[0] diff --git a/validation/projects/projects.go b/validation/projects/projects.go index a455def4d..4c8fe5c01 100644 --- a/validation/projects/projects.go +++ b/validation/projects/projects.go @@ -14,11 +14,10 @@ import ( "github.com/rancher/shepherd/extensions/defaults" namegen "github.com/rancher/shepherd/pkg/namegenerator" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - "github.com/rancher/tests/actions/kubeapi/namespaces" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" - quotas "github.com/rancher/tests/actions/kubeapi/resourcequotas" - "github.com/rancher/tests/actions/kubeapi/workloads/deployments" - "github.com/rancher/tests/actions/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" + quotaapi "github.com/rancher/tests/actions/kubeapi/resourcequotas" + deploymentapi "github.com/rancher/tests/actions/kubeapi/workloads/deployments" "github.com/rancher/tests/actions/workloads/pods" appv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -26,14 +25,6 @@ import ( kwait "k8s.io/apimachinery/pkg/util/wait" ) -const ( - dummyFinalizer = "dummy" - systemProjectLabel = "authz.management.cattle.io/system-project" - resourceQuotaAnnotation = "field.cattle.io/resourceQuota" - containerDefaultLimitAnnotation = "field.cattle.io/containerDefaultResourceLimit" - resourceQuotaStatusAnnotation = "cattle.io/status" -) - var prtb = v3.ProjectRoleTemplateBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "", @@ -55,7 +46,7 @@ func createProjectAndNamespace(client *rancher.Client, clusterID string, project } namespaceName := namegen.AppendRandomString("testns-") - createdNamespace, err := namespaces.CreateNamespace(client, clusterID, createdProject.Name, namespaceName, "", map[string]string{}, map[string]string{}) + createdNamespace, err := namespaceapi.CreateNamespace(client, clusterID, createdProject.Name, namespaceName, "", map[string]string{}, map[string]string{}) if err != nil { return nil, nil, err } @@ -64,7 +55,7 @@ func createProjectAndNamespace(client *rancher.Client, clusterID string, project } func createProjectAndNamespaceWithQuotas(client *rancher.Client, clusterID string, namespacePodLimit, projectPodLimit string) (*v3.Project, *corev1.Namespace, error) { - projectTemplate := projectsapi.NewProjectTemplate(clusterID) + projectTemplate := projectapi.NewProjectTemplate(clusterID) projectTemplate.Spec.NamespaceDefaultResourceQuota.Limit.Pods = namespacePodLimit projectTemplate.Spec.ResourceQuota.Limit.Pods = projectPodLimit createdProject, createdNamespace, err := createProjectAndNamespace(client, clusterID, projectTemplate) @@ -76,7 +67,7 @@ func createProjectAndNamespaceWithQuotas(client *rancher.Client, clusterID strin } func createProjectAndNamespaceWithLimits(client *rancher.Client, clusterID string, cpuLimit, cpuReservation, memoryLimit, memoryReservation string) (*v3.Project, *corev1.Namespace, error) { - projectTemplate := projectsapi.NewProjectTemplate(clusterID) + projectTemplate := projectapi.NewProjectTemplate(clusterID) projectTemplate.Spec.ContainerDefaultResourceLimit.LimitsCPU = cpuLimit projectTemplate.Spec.ContainerDefaultResourceLimit.RequestsCPU = cpuReservation projectTemplate.Spec.ContainerDefaultResourceLimit.LimitsMemory = memoryLimit @@ -91,7 +82,7 @@ func createProjectAndNamespaceWithLimits(client *rancher.Client, clusterID strin } func checkAnnotationExistsInNamespace(client *rancher.Client, clusterID string, namespaceName string, annotationKey string, expectedExistence bool) error { - namespace, err := namespaces.GetNamespaceByName(client, clusterID, namespaceName) + namespace, err := namespaceapi.GetNamespaceByName(client, clusterID, namespaceName) if err != nil { return err } @@ -124,7 +115,7 @@ func getLatestStatusMessageFromDeployment(deployment *appv1.Deployment, messageT } func checkDeploymentStatus(client *rancher.Client, clusterID, namespaceName, deploymentName, statusType, expectedStatusReason, expectedStatusMessage string, expectedReplicaCount int32) error { - updatedDeploymentList, err := deployments.ListDeployments(client, clusterID, namespaceName, metav1.ListOptions{ + updatedDeploymentList, err := deploymentapi.ListDeployments(client, clusterID, namespaceName, metav1.ListOptions{ FieldSelector: "metadata.name=" + deploymentName, }) if err != nil { @@ -181,7 +172,7 @@ func getStatusAndMessageFromAnnotation(annotation string, conditionType string) } func getNamespaceLimit(client *rancher.Client, clusterID string, namespaceName, annotation string) (map[string]interface{}, error) { - namespace, err := namespaces.GetNamespaceByName(client, clusterID, namespaceName) + namespace, err := namespaceapi.GetNamespaceByName(client, clusterID, namespaceName) if err != nil { return nil, err } @@ -201,7 +192,7 @@ func getNamespaceLimit(client *rancher.Client, clusterID string, namespaceName, } func checkNamespaceResourceQuota(client *rancher.Client, clusterID, namespaceName string, expectedPodLimit int) error { - quotas, err := quotas.ListResourceQuotas(client, clusterID, namespaceName, metav1.ListOptions{}) + quotas, err := quotaapi.ListResourceQuotas(client, clusterID, namespaceName, metav1.ListOptions{}) if err != nil { return err } @@ -223,12 +214,12 @@ func checkNamespaceResourceQuota(client *rancher.Client, clusterID, namespaceNam } func checkNamespaceResourceQuotaValidationStatus(client *rancher.Client, clusterID, namespaceName, namespacePodLimit string, expectedStatus bool, expectedErrorMessage string) error { - namespace, err := namespaces.GetNamespaceByName(client, clusterID, namespaceName) + namespace, err := namespaceapi.GetNamespaceByName(client, clusterID, namespaceName) if err != nil { return err } - limitData, err := getNamespaceLimit(client, clusterID, namespace.Name, resourceQuotaAnnotation) + limitData, err := getNamespaceLimit(client, clusterID, namespace.Name, projectapi.ResourceQuotaAnnotation) if err != nil { return err } @@ -238,7 +229,7 @@ func checkNamespaceResourceQuotaValidationStatus(client *rancher.Client, cluster return fmt.Errorf("namespace pod limit mismatch in the namespace spec. expected: %s, actual: %s", namespacePodLimit, actualNamespacePodLimit) } - status, message, err := getStatusAndMessageFromAnnotation(namespace.Annotations[resourceQuotaStatusAnnotation], "ResourceQuotaValidated") + status, message, err := getStatusAndMessageFromAnnotation(namespace.Annotations[projectapi.ResourceQuotaStatusAnnotation], "ResourceQuotaValidated") if err != nil { return err } @@ -261,7 +252,7 @@ func updateProjectContainerResourceLimit(client *rancher.Client, existingProject updatedProject.Spec.ContainerDefaultResourceLimit.LimitsMemory = memoryLimit updatedProject.Spec.ContainerDefaultResourceLimit.RequestsMemory = memoryReservation - updatedProject, err := projectsapi.UpdateProject(client, existingProject, updatedProject) + updatedProject, err := projectapi.UpdateProject(client, existingProject, updatedProject) if err != nil { return nil, err } @@ -378,27 +369,3 @@ func checkLimitRange(client *rancher.Client, clusterID, namespaceName string, ex return nil } - -func createNamespacesInProject(client *rancher.Client, clusterID, projectID string, count int) ([]*corev1.Namespace, error) { - var namespaces []*corev1.Namespace - - err := kwait.PollUntilContextTimeout(context.TODO(), defaults.FiveHundredMillisecondTimeout, defaults.TenSecondTimeout, true, func(ctx context.Context) (bool, error) { - namespaces = []*corev1.Namespace{} - - for i := 0; i < count; i++ { - ns, err := projects.CreateNamespaceUsingWrangler(client, clusterID, projectID, nil) - if err != nil { - return false, err - } - namespaces = append(namespaces, ns) - } - - return true, nil - }) - - if err != nil { - return nil, fmt.Errorf("failed to create %d namespaces: %w", count, err) - } - - return namespaces, nil -} diff --git a/validation/projects/projects_container_default_resource_limit_test.go b/validation/projects/projects_container_default_resource_limit_test.go index 304c9bce4..3793f8677 100644 --- a/validation/projects/projects_container_default_resource_limit_test.go +++ b/validation/projects/projects_container_default_resource_limit_test.go @@ -13,8 +13,8 @@ import ( "github.com/rancher/shepherd/pkg/session" "github.com/rancher/shepherd/pkg/wrangler" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - "github.com/rancher/tests/actions/kubeapi/namespaces" - "github.com/rancher/tests/actions/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/workloads/deployment" log "github.com/sirupsen/logrus" @@ -155,7 +155,7 @@ func (pcrl *ProjectsContainerResourceLimitTestSuite) TestCpuAndMemoryLimitEqualT require.Equal(pcrl.T(), memoryReservation, projectSpec.RequestsMemory, "Memory reservation mismatch") log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, pcrl.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, pcrl.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(pcrl.T(), err) log.Info("Verify that the limit range object is created for the namespace and the resource limit in the limit range is accurate.") @@ -194,7 +194,7 @@ func (pcrl *ProjectsContainerResourceLimitTestSuite) TestCpuAndMemoryLimitGreate require.Equal(pcrl.T(), memoryReservation, projectSpec.RequestsMemory, "Memory reservation mismatch") log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, pcrl.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, pcrl.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(pcrl.T(), err) log.Info("Verify that the limit range object is created for the namespace and the resource limit in the limit range is accurate.") @@ -342,7 +342,7 @@ func (pcrl *ProjectsContainerResourceLimitTestSuite) TestLimitDeletionPropagatio require.Equal(pcrl.T(), memoryReservation, projectSpec.RequestsMemory, "Memory reservation mismatch") log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, pcrl.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, pcrl.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(pcrl.T(), err) log.Info("Verify that the limit range object is created for the namespace and the resource limit in the limit range is accurate.") @@ -413,7 +413,7 @@ func (pcrl *ProjectsContainerResourceLimitTestSuite) TestOverrideDefaultLimitInN require.Equal(pcrl.T(), memoryReservation, projectSpec.RequestsMemory, "Memory reservation mismatch") log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, pcrl.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, pcrl.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(pcrl.T(), err) log.Info("Verify that the limit range object is created for the namespace and the resource limit in the limit range is accurate.") @@ -434,13 +434,13 @@ func (pcrl *ProjectsContainerResourceLimitTestSuite) TestOverrideDefaultLimitInN memoryLimit = "128Mi" memoryReservation = "64Mi" - updatedNamespace, err := namespaces.GetNamespaceByName(standardUserClient, pcrl.cluster.ID, createdNamespace.Name) + updatedNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, pcrl.cluster.ID, createdNamespace.Name) require.NoError(pcrl.T(), err) - if _, exists := updatedNamespace.Annotations[containerDefaultLimitAnnotation]; !exists { - updatedNamespace.Annotations[containerDefaultLimitAnnotation] = fmt.Sprintf(`{"limitsCpu":"%s","limitsMemory":"%s","requestsCpu":"%s","requestsMemory":"%s"}`, cpuLimit, memoryLimit, cpuReservation, memoryReservation) + if _, exists := updatedNamespace.Annotations[projectapi.ContainerDefaultLimitAnnotation]; !exists { + updatedNamespace.Annotations[projectapi.ContainerDefaultLimitAnnotation] = fmt.Sprintf(`{"limitsCpu":"%s","limitsMemory":"%s","requestsCpu":"%s","requestsMemory":"%s"}`, cpuLimit, memoryLimit, cpuReservation, memoryReservation) } - currentNamespace, err := namespaces.GetNamespaceByName(standardUserClient, pcrl.cluster.ID, updatedNamespace.Name) + currentNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, pcrl.cluster.ID, updatedNamespace.Name) require.NoError(pcrl.T(), err) updatedNamespace.ResourceVersion = currentNamespace.ResourceVersion namespace, err := standardUserContext.Core.Namespace().Update(updatedNamespace) diff --git a/validation/projects/projects_extended_resource_quota_test.go b/validation/projects/projects_extended_resource_quota_test.go new file mode 100644 index 000000000..2df990a5a --- /dev/null +++ b/validation/projects/projects_extended_resource_quota_test.go @@ -0,0 +1,1247 @@ +//go:build (validation || infra.any || cluster.any || extended) && !sanity && !stress && !2.8 && !2.9 && !2.10 && !2.11 && !2.12 && !2.13 + +package projects + +import ( + "testing" + + v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3" + "github.com/rancher/shepherd/clients/rancher" + management "github.com/rancher/shepherd/clients/rancher/generated/management/v3" + "github.com/rancher/shepherd/extensions/clusters" + namegen "github.com/rancher/shepherd/pkg/namegenerator" + "github.com/rancher/shepherd/pkg/session" + "github.com/rancher/shepherd/pkg/wrangler" + clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" + podapi "github.com/rancher/tests/actions/kubeapi/workloads/pods" + "github.com/rancher/tests/actions/rbac" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + corev1 "k8s.io/api/core/v1" +) + +type ProjectsExtendedResourceQuotaTestSuite struct { + suite.Suite + client *rancher.Client + session *session.Session + cluster *management.Cluster +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TearDownSuite() { + perq.session.Cleanup() +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) SetupSuite() { + perq.session = session.NewSession() + + client, err := rancher.NewClient("", perq.session) + require.NoError(perq.T(), err) + perq.client = client + + log.Info("Getting cluster name from the config file and append cluster details in the struct") + clusterName := client.RancherConfig.ClusterName + require.NotEmptyf(perq.T(), clusterName, "Cluster name to install should be set") + clusterID, err := clusters.GetClusterIDByName(perq.client, clusterName) + require.NoError(perq.T(), err, "Error getting cluster ID") + perq.cluster, err = perq.client.Management.Cluster.ByID(clusterID) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) setupUserForProject() (*rancher.Client, *wrangler.Context) { + log.Info("Create a standard user and add the user to the downstream cluster as cluster owner.") + _, standardUserClient, err := rbac.AddUserWithRoleToCluster(perq.client, rbac.StandardUser.String(), rbac.ClusterOwner.String(), perq.cluster, nil) + require.NoError(perq.T(), err, "Failed to add the user as a cluster owner to the downstream cluster") + + standardUserContext, err := clusterapi.GetClusterWranglerContext(standardUserClient, perq.cluster.ID) + require.NoError(perq.T(), err) + + return standardUserClient, standardUserContext +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestProjectLevelExtendedResourceQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with extended ephemeral storage quota applied at project level.") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Mi", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "60Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "120Mi", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + createdProject, firstNamespace, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, firstNamespace.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err, "%s annotation should exist", projectapi.ResourceQuotaAnnotation) + + log.Info("Verify that the resource quota object is created for the namespace and the quota limits and requests in the resource quota are accurate.") + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, firstNamespace.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Info("Create a pod in the first namespace within the resource quota limits of the namespace.") + podEphemeralStorageRequest := "60Mi" + podEphemeralStorageLimit := "120Mi" + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: podEphemeralStorageRequest, + } + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: podEphemeralStorageLimit, + } + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, firstNamespace.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err) + + log.Info("Verify that the resource quota usage in the namespace is accurate after creating the pod within quota limits.") + expectedUsage := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: podEphemeralStorageRequest, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: podEphemeralStorageLimit, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, firstNamespace.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Verify that the resource quota usage in the project is accurate after creating the first namespace.") + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Create a second namespace in the same project.") + namespaceName := namegen.AppendRandomString("testns-") + secondNamespace, err := namespaceapi.CreateNamespace(standardUserClient, perq.cluster.ID, createdProject.Name, namespaceName, "", nil, nil) + require.NoError(perq.T(), err) + + log.Info("Verify that the resource quota validation for the second namespace fails.") + err = namespaceapi.VerifyNamespaceResourceQuotaValidationStatus(standardUserClient, perq.cluster.ID, secondNamespace.Name, nil, namespaceExtendedQuota, false, "exceeds project limit") + require.NoError(perq.T(), err) + + log.Info("Attempt to create a pod in the second namespace exceeding the resource quota limits of the project and verify that the pod creation fails with exceeded quota error.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, secondNamespace.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify that resource quota usage in the second namespace remains unchanged after failed pod creation.") + expectedUsage = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, secondNamespace.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Verify project-level quota usage remains unchanged.") + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedUsage) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestProjectLevelExtendedPodCountQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create project with extended project-level pod count quota = 1") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "1", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "1", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + createdProject, ns1, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns1.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err, "%s annotation should exist", projectapi.ResourceQuotaAnnotation) + + log.Info("Verify that the resource quota object is created for the namespace and the quota limits and requests in the resource quota are accurate.") + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Info("Create a pod in the first namespace (should succeed).") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, nil, nil, true) + require.NoError(perq.T(), err) + + log.Info("Create second namespace in same project.") + ns2Name := namegen.AppendRandomString("testns-") + ns2, err := namespaceapi.CreateNamespace(standardUserClient, perq.cluster.ID, createdProject.Name, ns2Name, "", nil, nil) + require.NoError(perq.T(), err) + + log.Info("Attempt to create pod in second namespace - SHOULD FAIL if project-level extended quota is enforced.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns2.Name, podapi.PauseImage, nil, nil, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestProjectLevelExistingResourceQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with existing pod count resource quota.") + projectPodLimit := "1" + namespacePodLimit := "1" + + projectExistingQuota := &v3.ResourceQuotaLimit{ + Pods: projectPodLimit, + } + namespaceExistingQuota := &v3.ResourceQuotaLimit{ + Pods: namespacePodLimit, + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, projectExistingQuota, nil, namespaceExistingQuota, nil) + createdProject, firstNamespace, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + + log.Info("Create a second namespace in the same project.") + existingLimits := map[string]string{"pods": namespacePodLimit} + ns2Name := namegen.AppendRandomString("testns2-") + ns2, err := namespaceapi.CreateNamespace(standardUserClient, perq.cluster.ID, createdProject.Name, ns2Name, "", nil, nil) + require.NoError(perq.T(), err) + err = namespaceapi.VerifyNamespaceResourceQuotaValidationStatus(standardUserClient, perq.cluster.ID, ns2.Name, existingLimits, nil, false, "exceeds project limit") + require.NoError(perq.T(), err) + + log.Info("Create a pod in the first namespace.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, firstNamespace.Name, podapi.PauseImage, nil, nil, true) + require.NoError(perq.T(), err) + + log.Info("Attempt to create another pod in the second namespace and verify that it fails with exceeded quota error") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns2.Name, podapi.PauseImage, nil, nil, false) + require.Error(perq.T(), err, "expected project-level pod quota to block pods across namespaces") + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestNamespaceLevelExtendedResourceQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with extended ephemeral storage resource quota.") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "200Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "400Mi", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + createdProject, ns1, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + require.Equal(perq.T(), projectExtendedQuota, createdProject.Spec.ResourceQuota.Limit.Extended, "Project extended quota mismatch") + require.Equal(perq.T(), namespaceExtendedQuota, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Extended, "Namespace extended quota mismatch") + + log.Infof("Verify that the namespace has the annotation: %s.", projectapi.ResourceQuotaAnnotation) + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns1.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err, "%s annotation should exist", projectapi.ResourceQuotaAnnotation) + + log.Info("Verify the resource quota object created for the namespace has the correct hard and used limits.") + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + expectedUsage := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Infof("Create a pod in the namespace %s with the quota limits within the namespace quota limits, and verify that the pod is created.", ns1.Name) + podEphemeralStorageRequest := "50Mi" + podEphemeralStorageLimit := "100Mi" + + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: podEphemeralStorageRequest, + } + + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: podEphemeralStorageLimit, + } + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err, "Failed to create pod with ephemeral storage requests and limits") + + log.Info("Verify the resource quota object in the namespace has the correct used limits.") + expectedUsage = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: podEphemeralStorageRequest, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: podEphemeralStorageLimit, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Attempt to create another pod in the namespace exceeding the namespace quota limits, and verify that the pod creation fails with exceeded quota error.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err, "Pod creation with resource quota limits exceeding namespace quota should fail") + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify that resource quota usage in the namespace remains unchanged after failed pod creation.") + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Create another namespace in the same project.") + ns2Name := namegen.AppendRandomString("testns") + ns2, err := namespaceapi.CreateNamespace(standardUserClient, perq.cluster.ID, createdProject.Name, ns2Name, "", nil, nil) + require.NoError(perq.T(), err) + + log.Info("Verify that the resource quota usage in the project is accurate.") + expectedUsage = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Mi", + } + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Infof("Create a pod in the namespace %s with the quota limits within the namespace quota limits, and verify that the pods are created.", ns2.Name) + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns2.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err, "Failed to create pod with ephemeral storage requests and limits") + + log.Info("Verify that the resource quota usage in the namespace is accurate after creating a pod within the quota limits.") + expectedUsage = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: podEphemeralStorageRequest, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: podEphemeralStorageLimit, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns2.Name, expectedUsage) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestNamespaceLevelExtendedPodCountQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with extended pod count resource quota.") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "10", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "1", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + createdProject, ns1, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + require.Equal(perq.T(), projectExtendedQuota, createdProject.Spec.ResourceQuota.Limit.Extended, "Project extended quota mismatch") + require.Equal(perq.T(), namespaceExtendedQuota, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Extended, "Namespace extended quota mismatch") + + log.Infof("Verify that the namespace has the annotation: %s.", projectapi.ResourceQuotaAnnotation) + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns1.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err, "%s annotation should exist", projectapi.ResourceQuotaAnnotation) + + log.Info("Verify the resource quota object created for the namespace has the correct hard and used limits.") + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + expectedUsage := map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Infof("Create a pod in the namespace %s with the quota limits within the namespace quota limits, and verify that the pod is created.", ns1.Name) + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, nil, nil, true) + require.NoError(perq.T(), err) + + log.Info("Verify the resource quota object in the namespace has the correct used limits.") + expectedUsage = map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "1", + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Attempt to create another pod in the namespace exceeding the namespace quota limits, and verify that the pod creation fails with exceeded quota error.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, nil, nil, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify that resource quota usage in the namespace remains unchanged after failed pod creation.") + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Create another namespace in the same project.") + ns2Name := namegen.AppendRandomString("testns") + ns2, err := namespaceapi.CreateNamespace(standardUserClient, perq.cluster.ID, createdProject.Name, ns2Name, "", nil, nil) + require.NoError(perq.T(), err) + + log.Info("Verify that the resource quota usage in the project is accurate.") + expectedUsage = map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "2", + } + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Infof("Create a pod in the namespace %s with the quota limits within the namespace quota limits, and verify that the pods are created.", ns2.Name) + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns2.Name, podapi.PauseImage, nil, nil, true) + require.NoError(perq.T(), err) + + log.Info("Verify that the resource quota usage in the namespace is accurate after creating a pod within the quota limits.") + expectedUsage = map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "1", + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns2.Name, expectedUsage) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestNamespaceLevelShorthandExtendedResourceQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with shorthand extended ephemeral storage resource quota.") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaKey: "200Mi", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaKey: "50Mi", + } + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + createdProject, ns1, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + require.Equal(perq.T(), projectExtendedQuota, createdProject.Spec.ResourceQuota.Limit.Extended, "Project extended quota mismatch") + require.Equal(perq.T(), namespaceExtendedQuota, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Extended, "Namespace extended quota mismatch") + + log.Info("Verify the resource quota object created for the namespace has the correct hard and used limits.") + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + expectedUsage := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaKey: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Infof("Create a pod in the namespace %s with the quota limits within the namespace quota limits, and verify that the pod is created.", ns1.Name) + podEphemeralStorageRequest := "50Mi" + podEphemeralStorageLimit := "100Mi" + + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: podEphemeralStorageRequest, + } + + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: podEphemeralStorageLimit, + } + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err, "Failed to create pod with ephemeral storage requests and limits") + + log.Info("Verify the resource quota object in the namespace has the correct used limits.") + expectedUsage = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaKey: podEphemeralStorageRequest, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Attempt to create another pod in the namespace exceeding the namespace quota limits, and verify that the pod creation fails with exceeded quota error.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err, "Pod creation with resource quota limits exceeding namespace quota should fail") + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify that resource quota usage in the namespace remains unchanged after failed pod creation.") + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Create another namespace in the same project.") + ns2Name := namegen.AppendRandomString("testns") + ns2, err := namespaceapi.CreateNamespace(standardUserClient, perq.cluster.ID, createdProject.Name, ns2Name, "", nil, nil) + require.NoError(perq.T(), err) + + log.Info("Verify that the resource quota usage in the project is accurate.") + expectedUsage = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaKey: "100Mi", + } + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Infof("Create a pod in the namespace %s with the quota limits within the namespace quota limits, and verify that the pods are created.", ns2.Name) + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns2.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err, "Failed to create pod with ephemeral storage requests and limits") + + log.Info("Verify that the resource quota usage in the namespace is accurate after creating a pod within the quota limits.") + expectedUsage = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaKey: podEphemeralStorageRequest, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns2.Name, expectedUsage) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestNamespaceLevelExistingResourceQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with existing pod count resource quota.") + projectPodLimit := "10" + namespacePodLimit := "1" + + projectExistingQuota := &v3.ResourceQuotaLimit{ + Pods: projectPodLimit, + } + namespaceExistingQuota := &v3.ResourceQuotaLimit{ + Pods: namespacePodLimit, + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, projectExistingQuota, nil, namespaceExistingQuota, nil) + createdProject, ns1, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + require.Equal(perq.T(), projectPodLimit, createdProject.Spec.ResourceQuota.Limit.Pods, "Project existing quota mismatch") + require.Equal(perq.T(), namespacePodLimit, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Pods, "Namespace existing quota mismatch") + + log.Infof("Verify that the namespace has the annotation: %s.", projectapi.ResourceQuotaAnnotation) + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns1.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err, "%s annotation should exist", projectapi.ResourceQuotaAnnotation) + + log.Info("Verify the resource quota object created for the namespace has the correct hard and used limits.") + expectedNamespaceQuota := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespacePodLimit, + } + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedNamespaceQuota) + require.NoError(perq.T(), err) + expectedUsage := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Infof("Create a pod in the namespace %s with the quota limits within the namespace quota limits, and verify that the pod is created.", ns1.Name) + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, nil, nil, true) + require.NoError(perq.T(), err) + + log.Info("Verify the resource quota object in the namespace has the correct used limits.") + expectedUsage = map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "1", + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Attempt to create another pod in the namespace exceeding the namespace quota limits, and verify that the pod creation fails with exceeded quota error.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, nil, nil, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify that resource quota usage in the namespace remains unchanged after failed pod creation.") + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsage) + require.NoError(perq.T(), err) + + log.Info("Create another namespace in the same project.") + ns2Name := namegen.AppendRandomString("testns") + ns2, err := namespaceapi.CreateNamespace(standardUserClient, perq.cluster.ID, createdProject.Name, ns2Name, "", nil, nil) + require.NoError(perq.T(), err) + projectExistingUsed := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "2", + } + err = projectapi.VerifyUsedProjectExistingResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, projectExistingUsed) + require.NoError(perq.T(), err) + + log.Infof("Create a pod in the namespace %s with the quota limits within the namespace quota limits, and verify that the pods are created.", ns2.Name) + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns2.Name, podapi.PauseImage, nil, nil, true) + require.NoError(perq.T(), err) + + log.Info("Verify that the resource quota usage in the namespace is accurate after creating a pod within the quota limits.") + expectedUsage = map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "1", + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns2.Name, expectedUsage) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestNamespaceMixedQuotaExceedExtendedEphemeralStorage() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with existing pod count quota and extended ephemeral storage quota.") + projectPodCount := "10" + namespacePodCount := "2" + projectExistingQuota := &v3.ResourceQuotaLimit{ + Pods: projectPodCount, + } + namespaceExistingQuota := &v3.ResourceQuotaLimit{ + Pods: namespacePodCount, + } + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "200Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "400Mi", + } + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, projectExistingQuota, projectExtendedQuota, namespaceExistingQuota, namespaceExtendedQuota) + createdProject, ns1, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + require.Equal(perq.T(), projectPodCount, createdProject.Spec.ResourceQuota.Limit.Pods) + require.Equal(perq.T(), projectExtendedQuota, createdProject.Spec.ResourceQuota.Limit.Extended) + require.Equal(perq.T(), namespacePodCount, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Pods) + require.Equal(perq.T(), namespaceExtendedQuota, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Extended) + + log.Infof("Verify namespace %s has resource quota annotation.", ns1.Name) + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns1.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err) + + log.Info("Verify initial hard and used limits in namespace.") + expectedHard := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespacePodCount, + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedHard) + require.NoError(perq.T(), err) + + expectedUsed := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Create pod within both pod-count and ephemeral-storage limits.") + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "50Mi", + } + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "100Mi", + } + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err) + + expectedUsed = map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "1", + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Attempt to create another pod and verify that it fails due to exceeding ephemeral-storage limits.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify project level used quota is accurate.") + projectExtendedUsed := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + projectExistingUsed := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespacePodCount, + } + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, projectExtendedUsed) + require.NoError(perq.T(), err) + err = projectapi.VerifyUsedProjectExistingResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, projectExistingUsed) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestNamespaceMixedQuotaExceedExistingPodCount() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with existing pod count quota and extended ephemeral storage quota.") + projectPodCount := "10" + namespacePodCount := "1" + projectExistingQuota := &v3.ResourceQuotaLimit{ + Pods: projectPodCount, + } + namespaceExistingQuota := &v3.ResourceQuotaLimit{ + Pods: namespacePodCount, + } + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "300Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "500Mi", + } + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Mi", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, projectExistingQuota, projectExtendedQuota, namespaceExistingQuota, namespaceExtendedQuota) + createdProject, ns1, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + require.Equal(perq.T(), projectPodCount, createdProject.Spec.ResourceQuota.Limit.Pods) + require.Equal(perq.T(), projectExtendedQuota, createdProject.Spec.ResourceQuota.Limit.Extended) + require.Equal(perq.T(), namespacePodCount, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Pods) + require.Equal(perq.T(), namespaceExtendedQuota, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Extended) + + log.Infof("Verify namespace %s has resource quota annotation.", ns1.Name) + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns1.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err) + + log.Info("Verify initial hard and used limits in namespace.") + expectedHard := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespacePodCount, + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Mi", + } + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedHard) + require.NoError(perq.T(), err) + + expectedUsed := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Create a pod within both pod-count and ephemeral-storage limits.") + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "50Mi", + } + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "100Mi", + } + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err) + + expectedUsed = map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "1", + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Attempt to create another pod and verify that it fails due to exceeding pod limits.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify project level used quota is accurate.") + projectExtendedUsed := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Mi", + } + projectExistingUsed := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespacePodCount, + } + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, projectExtendedUsed) + require.NoError(perq.T(), err) + err = projectapi.VerifyUsedProjectExistingResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, projectExistingUsed) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestNamespaceLevelExistingOverridesExtended() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create project with existing and extended pod count resource quotas that conflict.") + projectPodCount := "10" + namespacePodCount := "1" + + projectExistingQuota := &v3.ResourceQuotaLimit{ + Pods: projectPodCount, + } + + namespaceExistingQuota := &v3.ResourceQuotaLimit{ + Pods: namespacePodCount, + } + + projectExtendedQuota := map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "5", + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "200Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "400Mi", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "3", + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, projectExistingQuota, projectExtendedQuota, namespaceExistingQuota, namespaceExtendedQuota) + createdProject, ns1, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + require.Equal(perq.T(), projectPodCount, createdProject.Spec.ResourceQuota.Limit.Pods) + require.Equal(perq.T(), namespacePodCount, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Pods) + require.Equal(perq.T(), projectExtendedQuota, createdProject.Spec.ResourceQuota.Limit.Extended) + require.Equal(perq.T(), namespaceExtendedQuota, createdProject.Spec.NamespaceDefaultResourceQuota.Limit.Extended) + + log.Infof("Verify namespace %s has resource quota annotation.", ns1.Name) + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns1.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err) + + log.Info("Verify initial hard and used limits in namespace.") + expectedHard := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespacePodCount, + projectapi.ExtendedPodResourceQuotaKey: "3", + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedHard) + require.NoError(perq.T(), err) + + expectedUsed := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedPodResourceQuotaKey: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Create a pod within both pod-count and ephemeral-storage limits.") + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "50Mi", + } + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "100Mi", + } + + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err) + + expectedUsed = map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "1", + projectapi.ExtendedPodResourceQuotaKey: "1", + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Attempt to create another pod and verify that it fails due to exceeding EXISTING pod limits.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns1.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify used quota remains unchanged after failure.") + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns1.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Verify project-level used quota is accurate.") + projectExtendedUsed := map[string]string{ + projectapi.ExtendedPodResourceQuotaKey: "3", + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + projectExistingUsed := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: namespacePodCount, + } + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, projectExtendedUsed) + require.NoError(perq.T(), err) + err = projectapi.VerifyUsedProjectExistingResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, projectExistingUsed) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestProjectResourceQuotaUsedLimitOnNamespaceDeleteAndCreate() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create project with existing and extended resource quotas.") + projectExistingQuota := &v3.ResourceQuotaLimit{ + Pods: "10", + } + + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "500Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "1Gi", + } + + namespaceExistingQuota := &v3.ResourceQuotaLimit{ + Pods: "2", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, projectExistingQuota, projectExtendedQuota, namespaceExistingQuota, namespaceExtendedQuota) + createdProject, _, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + + log.Info("Verify initial project UsedLimit after first namespace creation.") + expectedProjectExistingUsed := map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "2", + } + expectedProjectExtendedUsed := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + + err = projectapi.VerifyUsedProjectExistingResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedProjectExistingUsed) + require.NoError(perq.T(), err) + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedProjectExtendedUsed) + require.NoError(perq.T(), err) + + log.Info("Create a second namespace in the same project and verify project used quota increases.") + ns2Name := namegen.AppendRandomString("testns-") + ns2, err := namespaceapi.CreateNamespace(standardUserClient, perq.cluster.ID, createdProject.Name, ns2Name, "", nil, nil) + require.NoError(perq.T(), err) + + log.Info("Verify project UsedLimit is updated after namespace creation.") + expectedProjectExistingUsed = map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "4", + } + expectedProjectExtendedUsed = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Mi", + } + + err = projectapi.VerifyUsedProjectExistingResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedProjectExistingUsed) + require.NoError(perq.T(), err) + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedProjectExtendedUsed) + require.NoError(perq.T(), err) + + log.Infof("Delete namespace %s and verify project used quota decreases.", ns2.Name) + err = namespaceapi.DeleteNamespace(standardUserClient, perq.cluster.ID, ns2.Name) + require.NoError(perq.T(), err) + + log.Info("Verify project UsedLimit is updated after namespace deletion.") + expectedProjectExistingUsed = map[string]string{ + projectapi.ExistingPodResourceQuotaKey: "2", + } + expectedProjectExtendedUsed = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Mi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Mi", + } + + err = projectapi.VerifyUsedProjectExistingResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedProjectExistingUsed) + require.NoError(perq.T(), err) + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, createdProject.Name, expectedProjectExtendedUsed) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestMoveNamespaceWithoutQuotaToProjectWithExtendedQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a Project without resource quota.") + projectWithoutQuota, ns, err := projectapi.CreateProjectAndNamespace(standardUserClient, perq.cluster.ID) + require.NoError(perq.T(), err) + err = projectapi.VerifyProjectHasNoExtendedResourceQuota(standardUserClient, perq.cluster.ID, projectWithoutQuota.Name) + require.NoError(perq.T(), err) + + log.Info("Verify namespace initially has no resource quota annotation.") + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns.Name, projectapi.ResourceQuotaAnnotation, false) + require.NoError(perq.T(), err) + + log.Info("Create another project with extended ephemeral storage quota.") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Gi", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "10Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "20Gi", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + projectWithQuota, _, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + + log.Infof("Verify used limit for project %s before namespace move.", projectWithQuota.Name) + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, projectWithQuota.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Infof("Move namespace %s from %s to Project %s.", ns.Name, projectWithoutQuota.Name, projectWithQuota.Name) + err = namespaceapi.MoveNamespaceToProject(standardUserClient, perq.cluster.ID, ns.Name, projectWithQuota.Name) + require.NoError(perq.T(), err) + + log.Info("Verify resource quota annotation exists in the moved namespace.") + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err) + + log.Info("Verify ResourceQuota hard limits in the moved namespace.") + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Info("Verify initial used values for the resources in the moved namespace.") + expectedUsed := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: namespaceapi.InitialUsedResourceQuotaValue, + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Create a pod within extended quota limits.") + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "10Gi", + } + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "20Gi", + } + + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err) + + log.Info("Verify ResourceQuota used limit in the namespace is updated.") + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Info("Attempt to create pod exceeding extended quota limits.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Verify ResourceQuota used remains unchanged after failure.") + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Infof("Verify used limit for project %s is updated after namespace move.", projectWithQuota.Name) + expectedProjectUsed := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "20Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "40Gi", + } + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, projectWithQuota.Name, expectedProjectUsed) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestMoveNamespaceWithExtendedQuotaToProjectWithoutQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project with extended ephemeral storage quota.") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Gi", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "10Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "20Gi", + } + + projectWithQuotaTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectWithQuotaTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + projectWithQuota, ns, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectWithQuotaTemplate) + require.NoError(perq.T(), err) + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, projectWithQuota.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Info("Verify namespace has the resource quota annotation.") + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns.Name, projectapi.ResourceQuotaAnnotation, true) + require.NoError(perq.T(), err) + + log.Info("Create a pod within extended quota limits.") + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "10Gi", + } + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "20Gi", + } + + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err) + + log.Info("Verify resource quota used limit is updated in the namespace.") + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Info("Verify that creating a pod exceeding extended quota limits fails.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Create a second project without any resource quota.") + projectWithoutQuota, _, err := projectapi.CreateProjectAndNamespace(standardUserClient, perq.cluster.ID) + require.NoError(perq.T(), err) + err = projectapi.VerifyProjectHasNoExtendedResourceQuota(standardUserClient, perq.cluster.ID, projectWithoutQuota.Name) + require.NoError(perq.T(), err) + + log.Infof("Move namespace %s from project %s to project %s.", ns.Name, projectWithQuota.Name, projectWithoutQuota.Name) + err = namespaceapi.MoveNamespaceToProject(standardUserClient, perq.cluster.ID, ns.Name, projectWithoutQuota.Name) + require.NoError(perq.T(), err) + + log.Info("Verify resource quota is removed from the moved namespace.") + err = namespaceapi.VerifyAnnotationInNamespace(standardUserClient, perq.cluster.ID, ns.Name, projectapi.ResourceQuotaAnnotation, false) + require.NoError(perq.T(), err) + err = namespaceapi.VerifyNamespaceHasNoResourceQuota(standardUserClient, perq.cluster.ID, ns.Name) + require.NoError(perq.T(), err) + + log.Info("Verify pod creation is no longer quota restricted.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err) + + log.Infof("Verify used limit for project %s is updated after namespace move.", projectWithQuota.Name) + expectedProjectUsed := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: namespaceapi.InitialUsedResourceQuotaValue, + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: namespaceapi.InitialUsedResourceQuotaValue, + } + err = projectapi.VerifyUsedProjectExtendedResourceQuota(standardUserClient, perq.cluster.ID, projectWithQuota.Name, expectedProjectUsed) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestNamespaceOverrideExtendedQuotaWithinProjectLimits() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create project with extended ephemeral-storage quota.") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "100Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "200Gi", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "20Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "40Gi", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + _, ns, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.NoError(perq.T(), err) + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, namespaceExtendedQuota) + require.NoError(perq.T(), err) + + log.Info("Override namespace ResourceQuota within project limits.") + validNamespaceOverrideQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "50Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "100Gi", + } + + err = namespaceapi.UpdateNamespaceResourceQuotaAnnotation(standardUserClient, perq.cluster.ID, ns.Name, nil, validNamespaceOverrideQuota) + require.NoError(perq.T(), err) + + log.Info("Verify namespace ResourceQuota reflects overridden values.") + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, validNamespaceOverrideQuota) + require.NoError(perq.T(), err) + + log.Info("Create pod within overridden namespace quota.") + requests := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "40Gi", + } + limits := map[corev1.ResourceName]string{ + corev1.ResourceEphemeralStorage: "80Gi", + } + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns.Name, podapi.PauseImage, requests, limits, true) + require.NoError(perq.T(), err) + + log.Info("Verify ResourceQuota used values are updated correctly in the namespace.") + expectedUsed := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "40Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "80Gi", + } + err = namespaceapi.VerifyUsedNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, expectedUsed) + require.NoError(perq.T(), err) + + log.Info("Attempt to create a pod exceeding namespace quota.") + _, err = podapi.CreatePodWithResources(standardUserClient, perq.cluster.ID, ns.Name, podapi.PauseImage, requests, limits, false) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.ExceedededResourceQuotaErrorMessage) + + log.Info("Attempt to override namespace quota beyond project limits.") + invalidNamespaceQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "150Gi", + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "300Gi", + } + err = namespaceapi.UpdateNamespaceResourceQuotaAnnotation(standardUserClient, perq.cluster.ID, ns.Name, nil, invalidNamespaceQuota) + require.NoError(perq.T(), err) + + log.Info("Verify resource quota validation status in the namespace reflects exceeded project limits.") + err = namespaceapi.VerifyNamespaceResourceQuotaValidationStatus(standardUserClient, perq.cluster.ID, ns.Name, nil, invalidNamespaceQuota, false, "exceeds project limit") + require.NoError(perq.T(), err) + + log.Info("Verify namespace ResourceQuota remains unchanged after failed override.") + err = namespaceapi.VerifyNamespaceResourceQuota(standardUserClient, perq.cluster.ID, ns.Name, validNamespaceOverrideQuota) + require.NoError(perq.T(), err) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestProjectExtendedQuotaLessThanNamespaceQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project where project extended ephemeral-storage quota < namespace extended ephemeral-storage quota.") + projectExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaKey: "10Mi", + } + + namespaceExtendedQuota := map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaKey: "20Mi", + } + + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + _, _, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.NamespaceQuotaExceedsProjectQuotaErrorMessage) + + projectExtendedQuota = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "10Mi", + } + + namespaceExtendedQuota = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaLimit: "20Mi", + } + + projectTemplate = projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + _, _, err = projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.NamespaceQuotaExceedsProjectQuotaErrorMessage) + + projectExtendedQuota = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "10Mi", + } + + namespaceExtendedQuota = map[string]string{ + projectapi.ExtendedEphemeralStorageResourceQuotaRequest: "20Mi", + } + projectTemplate = projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, nil, projectExtendedQuota, nil, namespaceExtendedQuota) + _, _, err = projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.NamespaceQuotaExceedsProjectQuotaErrorMessage) +} + +func (perq *ProjectsExtendedResourceQuotaTestSuite) TestProjectExistingQuotaLessThanNamespaceQuota() { + subSession := perq.session.NewSession() + defer subSession.Cleanup() + + standardUserClient, _ := perq.setupUserForProject() + + log.Info("Create a project where project existing pod quota < namespace existing pod quota.") + projectExistingQuota := &v3.ResourceQuotaLimit{ + Pods: "1", + } + namespaceExistingQuota := &v3.ResourceQuotaLimit{ + Pods: "2", + } + projectTemplate := projectapi.NewProjectTemplate(perq.cluster.ID) + projectapi.ApplyProjectAndNamespaceResourceQuotas(projectTemplate, projectExistingQuota, nil, namespaceExistingQuota, nil) + _, _, err := projectapi.CreateProjectAndNamespaceWithTemplate(standardUserClient, perq.cluster.ID, projectTemplate) + require.Error(perq.T(), err) + require.Contains(perq.T(), err.Error(), projectapi.NamespaceQuotaExceedsProjectQuotaErrorMessage) +} + +func TestProjectsExtendedResourceQuotaTestSuite(t *testing.T) { + suite.Run(t, new(ProjectsExtendedResourceQuotaTestSuite)) +} diff --git a/validation/projects/projects_rbac_test.go b/validation/projects/projects_rbac_test.go index 8488f70f8..f622879a5 100644 --- a/validation/projects/projects_rbac_test.go +++ b/validation/projects/projects_rbac_test.go @@ -12,9 +12,8 @@ import ( "github.com/rancher/shepherd/extensions/defaults" namegen "github.com/rancher/shepherd/pkg/namegenerator" "github.com/rancher/shepherd/pkg/session" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" rbacapi "github.com/rancher/tests/actions/kubeapi/rbac" - "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -70,7 +69,7 @@ func (rbp *RbacProjectTestSuite) TestCreateProject() { assert.NoError(rbp.T(), err) log.Infof("As a %v, create a project in the downstream cluster.", tt.role.String()) - projectTemplate := projectsapi.NewProjectTemplate(rbp.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rbp.cluster.ID) if tt.role.String() == rbac.ClusterMember.String() { projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": standardUser.ID, @@ -101,7 +100,7 @@ func (rbp *RbacProjectTestSuite) TestListProject() { assert.NoError(rbp.T(), err) log.Infof("As a %v, create a project in the downstream cluster.", tt.role.String()) - projectTemplate := projectsapi.NewProjectTemplate(rbp.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rbp.cluster.ID) if tt.role.String() == rbac.ClusterMember.String() { projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": standardUser.ID, @@ -111,7 +110,7 @@ func (rbp *RbacProjectTestSuite) TestListProject() { assert.NoError(rbp.T(), err, "failed to create project") log.Infof("As a %v, get the project in the downstream cluster.", tt.role.String()) - err = projects.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) assert.NoError(rbp.T(), err) projectObj, err := standardUserClient.WranglerContext.Mgmt.Project().Get(rbp.cluster.ID, createdProject.Name, metav1.GetOptions{}) assert.NoError(rbp.T(), err, "Failed to get project.") @@ -139,7 +138,7 @@ func (rbp *RbacProjectTestSuite) TestUpdateProject() { assert.NoError(rbp.T(), err) log.Infof("As a %v, create a project in the downstream cluster.", tt.role.String()) - projectTemplate := projectsapi.NewProjectTemplate(rbp.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rbp.cluster.ID) if tt.role.String() == rbac.ClusterMember.String() { projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": standardUser.ID, @@ -149,7 +148,7 @@ func (rbp *RbacProjectTestSuite) TestUpdateProject() { assert.NoError(rbp.T(), err, "failed to create project") log.Infof("As a %v, get the project in the downstream cluster.", tt.role.String()) - err = projects.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) assert.NoError(rbp.T(), err) currentProject, err := standardUserClient.WranglerContext.Mgmt.Project().Get(rbp.cluster.ID, createdProject.Name, metav1.GetOptions{}) assert.NoError(rbp.T(), err, "Failed to get project.") @@ -189,7 +188,7 @@ func (rbp *RbacProjectTestSuite) TestDeleteProject() { assert.NoError(rbp.T(), err) log.Infof("As a %v, create a project in the downstream cluster.", tt.role.String()) - projectTemplate := projectsapi.NewProjectTemplate(rbp.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rbp.cluster.ID) if tt.role.String() == rbac.ClusterMember.String() { projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": standardUser.ID, @@ -199,7 +198,7 @@ func (rbp *RbacProjectTestSuite) TestDeleteProject() { assert.NoError(rbp.T(), err, "failed to create project") log.Infof("As a %v, get the project in the downstream cluster.", tt.role.String()) - err = projects.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) assert.NoError(rbp.T(), err) currentProject, err := standardUserClient.WranglerContext.Mgmt.Project().Get(rbp.cluster.ID, createdProject.Name, metav1.GetOptions{}) assert.NoError(rbp.T(), err, "Failed to get project.") @@ -226,7 +225,7 @@ func (rbp *RbacProjectTestSuite) TestCrossClusterResourceIsolation() { defer subSession.Cleanup() log.Info("Creating a project and associated namespace in the local cluster") - firstProject, firstNamespace, err := projects.CreateProjectAndNamespaceUsingWrangler(rbp.client, rbac.LocalCluster) + firstProject, firstNamespace, err := projectapi.CreateProjectAndNamespace(rbp.client, rbac.LocalCluster) require.NoError(rbp.T(), err) log.Info("Creating a standard user and assigning the cluster-member role in the downstream cluster") @@ -234,7 +233,7 @@ func (rbp *RbacProjectTestSuite) TestCrossClusterResourceIsolation() { require.NoError(rbp.T(), err, "Failed to add the user as a cluster owner to the downstream cluster") log.Infof("As %s, creating a project with the same name in the downstream cluster", rbac.ClusterMember.String()) - projectTemplate := projectsapi.NewProjectTemplate(rbp.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rbp.cluster.ID) projectTemplate.Name = firstProject.Name projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": standardUser.ID, diff --git a/validation/projects/projects_resource_quota_test.go b/validation/projects/projects_resource_quota_test.go index bc1bb3a7b..fdd3a1862 100644 --- a/validation/projects/projects_resource_quota_test.go +++ b/validation/projects/projects_resource_quota_test.go @@ -15,10 +15,10 @@ import ( "github.com/rancher/shepherd/pkg/session" "github.com/rancher/shepherd/pkg/wrangler" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - "github.com/rancher/tests/actions/kubeapi/namespaces" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" - quotas "github.com/rancher/tests/actions/kubeapi/resourcequotas" - "github.com/rancher/tests/actions/kubeapi/workloads/deployments" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" + quotaapi "github.com/rancher/tests/actions/kubeapi/resourcequotas" + deploymentapi "github.com/rancher/tests/actions/kubeapi/workloads/deployments" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/workloads/deployment" @@ -78,11 +78,11 @@ func (prq *ProjectsResourceQuotaTestSuite) TestProjectWithoutResourceQuota() { require.NoError(prq.T(), err) log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace does not have the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, false) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, false) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should not exist") log.Info("Create a deployment in the namespace with ten replicas.") @@ -107,11 +107,11 @@ func (prq *ProjectsResourceQuotaTestSuite) TestProjectWithResourceQuota() { require.Equal(prq.T(), projectPodLimit, createdProject.Spec.ResourceQuota.Limit.Pods, "Project pod limit mismatch") log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, firstNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, firstNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace has the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, firstNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, firstNamespace.Name, projectapi.ResourceQuotaAnnotation, true) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should exist") log.Info("Verify that the resource quota validation for the namespace is successful.") @@ -124,7 +124,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestProjectWithResourceQuota() { log.Info("Create another namespace in the project and verify that the resource quota validation for the namespace fails.") secondNamespaceName := namegen.AppendRandomString("testns-") - secondNamespace, err := namespaces.CreateNamespace(standardUserClient, prq.cluster.ID, createdProject.Name, secondNamespaceName, "", map[string]string{}, map[string]string{}) + secondNamespace, err := namespaceapi.CreateNamespace(standardUserClient, prq.cluster.ID, createdProject.Name, secondNamespaceName, "", map[string]string{}, map[string]string{}) require.NoError(prq.T(), err, "Failed to create namespace in the project") err = checkNamespaceResourceQuotaValidationStatus(standardUserClient, prq.cluster.ID, secondNamespace.Name, namespacePodLimit, false, "Resource quota [pods=4] exceeds project limit") require.NoError(prq.T(), err) @@ -168,10 +168,12 @@ func (prq *ProjectsResourceQuotaTestSuite) TestProjectWithResourceQuota() { require.NoError(prq.T(), err) log.Info("Verify that the second deployment created in the first namespace transitions to Active state.") - updatedDeploymentList, err := deployments.ListDeployments(standardUserClient, prq.cluster.ID, firstNamespace.Name, metav1.ListOptions{ + updatedDeploymentList, err := deploymentapi.ListDeployments(standardUserClient, prq.cluster.ID, firstNamespace.Name, metav1.ListOptions{ FieldSelector: "metadata.name=" + createdSecondDeployment.Name, }) + require.NoError(prq.T(), err) updatedSecondDeployment := updatedDeploymentList.Items[0] + err = charts.WatchAndWaitDeployments(standardUserClient, prq.cluster.ID, firstNamespace.Name, metav1.ListOptions{ FieldSelector: "metadata.name=" + updatedSecondDeployment.Name, }) @@ -195,11 +197,11 @@ func (prq *ProjectsResourceQuotaTestSuite) TestQuotaPropagationToExistingNamespa require.Equal(prq.T(), projectPodLimit, createdProject.Spec.ResourceQuota.Limit.Pods, "Project pod limit mismatch") log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace has the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, true) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should exist") log.Info("Verify that the resource quota validation for the namespace is successful.") @@ -213,7 +215,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestQuotaPropagationToExistingNamespa log.Info("Update the resource quota in the Project with new values.") namespacePodLimit = "5" projectPodLimit = "10" - projectList, err := projectsapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ + projectList, err := projectapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ FieldSelector: "metadata.name=" + createdProject.Name, }) require.NoError(prq.T(), err, "Failed to list project.") @@ -228,7 +230,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestQuotaPropagationToExistingNamespa require.Equal(prq.T(), projectPodLimit, updatedProject.Spec.ResourceQuota.Limit.Pods, "Project pod limit mismatch") log.Info("Verify that the namespace still has the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, true) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should exist") log.Info("Verify that the resource quota in the existing namespace has the pod limit in the resource quota still set to 2.") @@ -237,7 +239,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestQuotaPropagationToExistingNamespa log.Info("Create a new namespace in the project.") newNamespaceName := namegen.AppendRandomString("testns-") - newNamespace, err := namespaces.CreateNamespace(standardUserClient, prq.cluster.ID, updatedProject.Name, newNamespaceName, "", map[string]string{}, map[string]string{}) + newNamespace, err := namespaceapi.CreateNamespace(standardUserClient, prq.cluster.ID, updatedProject.Name, newNamespaceName, "", map[string]string{}, map[string]string{}) require.NoError(prq.T(), err, "Failed to create namespace in the project") log.Info("Verify that the resource quota validation for the namespace is successful.") @@ -266,11 +268,11 @@ func (prq *ProjectsResourceQuotaTestSuite) TestQuotaDeletionPropagationToExistin require.Equal(prq.T(), projectPodLimit, createdProject.Spec.ResourceQuota.Limit.Pods, "Project pod limit mismatch") log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace has the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, true) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should exist") log.Info("Verify that the resource quota validation for the namespace is successful.") @@ -285,7 +287,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestQuotaDeletionPropagationToExistin namespacePodLimit = "" projectPodLimit = "" - projectList, err := projectsapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ + projectList, err := projectapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ FieldSelector: "metadata.name=" + createdProject.Name, }) require.NoError(prq.T(), err, "Failed to list project.") @@ -301,7 +303,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestQuotaDeletionPropagationToExistin log.Info("Verify that the namespace does not have the annotation: field.cattle.io/resourceQuota.") err = kwait.Poll(defaults.FiveHundredMillisecondTimeout, defaults.TenSecondTimeout, func() (done bool, pollErr error) { - checkErr := checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, false) + checkErr := checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, false) if checkErr != nil { return false, checkErr } @@ -311,7 +313,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestQuotaDeletionPropagationToExistin require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should not exist") log.Info("Verify that the resource quota in the existing namespace is deleted.") - quotas, err := quotas.ListResourceQuotas(standardUserClient, prq.cluster.ID, createdNamespace.Name, metav1.ListOptions{}) + quotas, err := quotaapi.ListResourceQuotas(standardUserClient, prq.cluster.ID, createdNamespace.Name, metav1.ListOptions{}) require.NoError(prq.T(), err) require.Empty(prq.T(), quotas) @@ -337,11 +339,11 @@ func (prq *ProjectsResourceQuotaTestSuite) TestOverrideQuotaInNamespace() { require.Equal(prq.T(), projectPodLimit, createdProject.Spec.ResourceQuota.Limit.Pods, "Project pod limit mismatch") log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace has the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, true) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should exist") log.Info("Verify that the resource quota validation for the namespace is successful.") @@ -360,14 +362,14 @@ func (prq *ProjectsResourceQuotaTestSuite) TestOverrideQuotaInNamespace() { namespacePodLimit = "3" downstreamContext, err := clusterapi.GetClusterWranglerContext(prq.client, prq.cluster.ID) require.NoError(prq.T(), err) - currentNamespace, err := namespaces.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) + currentNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) require.NoError(prq.T(), err) - currentNamespace.Annotations[resourceQuotaAnnotation] = fmt.Sprintf(`{"limit": {"pods": "%s"}}`, namespacePodLimit) + currentNamespace.Annotations[projectapi.ResourceQuotaAnnotation] = fmt.Sprintf(`{"limit": {"pods": "%s"}}`, namespacePodLimit) updatedNamespace, err := downstreamContext.Core.Namespace().Update(currentNamespace) require.NoError(prq.T(), err) log.Info("Verify that the pod limit for the namespace is set to 3.") - limitData, err := getNamespaceLimit(standardUserClient, prq.cluster.ID, updatedNamespace.Name, resourceQuotaAnnotation) + limitData, err := getNamespaceLimit(standardUserClient, prq.cluster.ID, updatedNamespace.Name, projectapi.ResourceQuotaAnnotation) require.NoError(prq.T(), err) actualNamespacePodLimit := limitData["limit"].(map[string]interface{})["pods"] require.Equal(prq.T(), namespacePodLimit, actualNamespacePodLimit, "Namespace pod limit mismatch") @@ -388,9 +390,9 @@ func (prq *ProjectsResourceQuotaTestSuite) TestOverrideQuotaInNamespace() { log.Info("Increase the pod limit on the namespace from 3 to 4.") namespacePodLimit = "4" - currentNamespace, err = namespaces.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) + currentNamespace, err = namespaceapi.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) require.NoError(prq.T(), err) - currentNamespace.Annotations[resourceQuotaAnnotation] = fmt.Sprintf(`{"limit": {"pods": "%s"}}`, namespacePodLimit) + currentNamespace.Annotations[projectapi.ResourceQuotaAnnotation] = fmt.Sprintf(`{"limit": {"pods": "%s"}}`, namespacePodLimit) updatedNamespace, err = downstreamContext.Core.Namespace().Update(currentNamespace) require.NoError(prq.T(), err) @@ -412,11 +414,11 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceFromNoQuotaToQuotaPr require.NoError(prq.T(), err) log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace does not have the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, false) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, false) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should not exist") log.Info("Create a deployment in the namespace with ten replicas.") @@ -427,7 +429,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceFromNoQuotaToQuotaPr namespacePodLimit = "2" projectPodLimit = "3" - projectTemplate := projectsapi.NewProjectTemplate(prq.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(prq.cluster.ID) projectTemplate.Spec.NamespaceDefaultResourceQuota.Limit.Pods = namespacePodLimit projectTemplate.Spec.ResourceQuota.Limit.Pods = projectPodLimit createdProject2, err := standardUserClient.WranglerContext.Mgmt.Project().Create(projectTemplate) @@ -441,15 +443,15 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceFromNoQuotaToQuotaPr downstreamContext, err := clusterapi.GetClusterWranglerContext(prq.client, prq.cluster.ID) require.NoError(prq.T(), err) - updatedNamespace, err := namespaces.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) + updatedNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) require.NoError(prq.T(), err) - updatedNamespace.Annotations[projectsapi.ProjectIDAnnotation] = createdProject2.Namespace + ":" + createdProject2.Name + updatedNamespace.Annotations[namespaceapi.ProjectIDAnnotation] = createdProject2.Namespace + ":" + createdProject2.Name movedNamespace, err := downstreamContext.Core.Namespace().Update(updatedNamespace) require.NoError(prq.T(), err) log.Info("Verify that the namespace has the annotation: field.cattle.io/resourceQuota.") err = kwait.Poll(defaults.FiveHundredMillisecondTimeout, defaults.TenSecondTimeout, func() (bool, error) { - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, movedNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, movedNamespace.Name, projectapi.ResourceQuotaAnnotation, true) if err != nil { return false, err } @@ -499,11 +501,11 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceFromQuotaToNoQuotaPr require.NoError(prq.T(), err) log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace has the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, true) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should exist") log.Info("Verify that the resource quota object is created for the namespace and the pod limit in the resource quota is set to 2.") @@ -518,7 +520,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceFromQuotaToNoQuotaPr namespacePodLimit = "" projectPodLimit = "" - projectTemplate := projectsapi.NewProjectTemplate(prq.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(prq.cluster.ID) projectTemplate.Spec.NamespaceDefaultResourceQuota.Limit.Pods = namespacePodLimit projectTemplate.Spec.ResourceQuota.Limit.Pods = projectPodLimit createdProject2, err := standardUserClient.WranglerContext.Mgmt.Project().Create(projectTemplate) @@ -528,15 +530,15 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceFromQuotaToNoQuotaPr downstreamContext, err := clusterapi.GetClusterWranglerContext(prq.client, prq.cluster.ID) require.NoError(prq.T(), err) - updatedNamespace, err := namespaces.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) + updatedNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) require.NoError(prq.T(), err) - updatedNamespace.Annotations[projectsapi.ProjectIDAnnotation] = createdProject2.Namespace + ":" + createdProject2.Name + updatedNamespace.Annotations[namespaceapi.ProjectIDAnnotation] = createdProject2.Namespace + ":" + createdProject2.Name movedNamespace, err := downstreamContext.Core.Namespace().Update(updatedNamespace) require.NoError(prq.T(), err) log.Info("Verify that the namespace does not have the annotation: field.cattle.io/resourceQuota.") err = kwait.Poll(defaults.FiveHundredMillisecondTimeout, defaults.TenSecondTimeout, func() (done bool, pollErr error) { - checkErr := checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, movedNamespace.Name, resourceQuotaAnnotation, false) + checkErr := checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, movedNamespace.Name, projectapi.ResourceQuotaAnnotation, false) if checkErr != nil { return false, checkErr } @@ -572,11 +574,11 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceWithDeploymentTransi require.NoError(prq.T(), err) log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace has the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, true) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should exist") log.Info("Verify that the resource quota object is created for the namespace and the pod limit in the resource quota is set to 2.") @@ -602,7 +604,7 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceWithDeploymentTransi namespacePodLimit = "" projectPodLimit = "" - projectTemplate := projectsapi.NewProjectTemplate(prq.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(prq.cluster.ID) projectTemplate.Spec.NamespaceDefaultResourceQuota.Limit.Pods = namespacePodLimit projectTemplate.Spec.ResourceQuota.Limit.Pods = projectPodLimit createdProject2, err := standardUserClient.WranglerContext.Mgmt.Project().Create(projectTemplate) @@ -612,15 +614,15 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceWithDeploymentTransi downstreamContext, err := clusterapi.GetClusterWranglerContext(prq.client, prq.cluster.ID) require.NoError(prq.T(), err) - updatedNamespace, err := namespaces.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) + updatedNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) require.NoError(prq.T(), err) - updatedNamespace.Annotations[projectsapi.ProjectIDAnnotation] = createdProject2.Namespace + ":" + createdProject2.Name + updatedNamespace.Annotations[namespaceapi.ProjectIDAnnotation] = createdProject2.Namespace + ":" + createdProject2.Name movedNamespace, err := downstreamContext.Core.Namespace().Update(updatedNamespace) require.NoError(prq.T(), err) log.Info("Verify that the namespace does not have the annotation: field.cattle.io/resourceQuota.") err = kwait.Poll(defaults.FiveHundredMillisecondTimeout, defaults.TenSecondTimeout, func() (done bool, pollErr error) { - checkErr := checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, movedNamespace.Name, resourceQuotaAnnotation, false) + checkErr := checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, movedNamespace.Name, projectapi.ResourceQuotaAnnotation, false) if checkErr != nil { return false, checkErr } @@ -653,13 +655,13 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceBetweenProjectsWithN require.NoError(prq.T(), err) log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(prq.T(), err) - updatedNamespace, err := namespaces.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) + updatedNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, prq.cluster.ID, createdNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace does not have the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, false) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, false) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should not exist") log.Info("Create a deployment in the namespace with ten replicas.") @@ -667,29 +669,29 @@ func (prq *ProjectsResourceQuotaTestSuite) TestMoveNamespaceBetweenProjectsWithN require.NoError(prq.T(), err) log.Info("Create another project in the downstream cluster.") - projectTemplate := projectsapi.NewProjectTemplate(prq.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(prq.cluster.ID) createdProject2, err := standardUserClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(prq.T(), err, "Failed to create project") - err = projects.WaitForProjectFinalizerToUpdate(prq.client, createdProject2.Name, createdProject2.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(prq.client, createdProject2.Name, createdProject2.Namespace, 2) require.NoError(prq.T(), err) log.Info("Move the namespace from the first project to the second project.") - currentNamespace, err := namespaces.GetNamespaceByName(standardUserClient, prq.cluster.ID, updatedNamespace.Name) + currentNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, prq.cluster.ID, updatedNamespace.Name) require.NoError(prq.T(), err) downstreamContext, err := clusterapi.GetClusterWranglerContext(prq.client, prq.cluster.ID) require.NoError(prq.T(), err) - updatedNamespace.Annotations[projectsapi.ProjectIDAnnotation] = createdProject2.Namespace + ":" + createdProject2.Name + updatedNamespace.Annotations[namespaceapi.ProjectIDAnnotation] = createdProject2.Namespace + ":" + createdProject2.Name updatedNamespace.ResourceVersion = currentNamespace.ResourceVersion _, err = downstreamContext.Core.Namespace().Update(updatedNamespace) require.NoError(prq.T(), err) log.Info("Verify that the namespace has the correct label and annotation referencing the second project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject2.Name, updatedNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, prq.cluster.ID, createdProject2.Name, updatedNamespace.Name) require.NoError(prq.T(), err) log.Info("Verify that the namespace does not have the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, updatedNamespace.Name, resourceQuotaAnnotation, false) + err = checkAnnotationExistsInNamespace(standardUserClient, prq.cluster.ID, updatedNamespace.Name, projectapi.ResourceQuotaAnnotation, false) require.NoError(prq.T(), err, "'field.cattle.io/resourceQuota' annotation should not exist") log.Info("Verify that the deployment is in Active state and all pods in the deployment are in Running state.") diff --git a/validation/projects/projects_test.go b/validation/projects/projects_test.go index 27a2c9373..541cbfbf0 100644 --- a/validation/projects/projects_test.go +++ b/validation/projects/projects_test.go @@ -11,11 +11,12 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/pkg/session" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - "github.com/rancher/tests/actions/kubeapi/namespaces" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" + rbacapi "github.com/rancher/tests/actions/kubeapi/rbac" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" - deployment "github.com/rancher/tests/actions/workloads/deployment" + "github.com/rancher/tests/actions/workloads/deployment" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -55,10 +56,10 @@ func (pr *ProjectsTestSuite) TestProjectsCrudLocalCluster() { defer subSession.Cleanup() log.Info("Create a project in the local cluster and verify that the project can be listed.") - createdProject, err := projects.CreateProjectUsingWrangler(pr.client, projectsapi.LocalCluster) + createdProject, err := projectapi.CreateProject(pr.client, rbacapi.LocalCluster) require.NoError(pr.T(), err) - projectList, err := projectsapi.ListProjects(pr.client, createdProject.Namespace, metav1.ListOptions{ + projectList, err := projectapi.ListProjects(pr.client, createdProject.Namespace, metav1.ListOptions{ FieldSelector: "metadata.name=" + createdProject.Name, }) require.NoError(pr.T(), err) @@ -73,16 +74,16 @@ func (pr *ProjectsTestSuite) TestProjectsCrudLocalCluster() { _, err = pr.client.WranglerContext.Mgmt.Project().Update(¤tProject) require.NoError(pr.T(), err, "Failed to update project.") - updatedProjectList, err := projectsapi.ListProjects(pr.client, createdProject.Namespace, metav1.ListOptions{ + updatedProjectList, err := projectapi.ListProjects(pr.client, createdProject.Namespace, metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s=%s", "hello", "world"), }) require.NoError(pr.T(), err) require.Equal(pr.T(), 1, len(updatedProjectList.Items), "Expected one project in the list") log.Info("Delete the project.") - err = projectsapi.DeleteProject(pr.client, createdProject.Namespace, createdProject.Name) + err = projectapi.DeleteProject(pr.client, createdProject.Namespace, createdProject.Name) require.NoError(pr.T(), err, "Failed to delete project") - projectList, err = projectsapi.ListProjects(pr.client, createdProject.Namespace, metav1.ListOptions{ + projectList, err = projectapi.ListProjects(pr.client, createdProject.Namespace, metav1.ListOptions{ FieldSelector: "metadata.name=" + createdProject.Name, }) require.NoError(pr.T(), err) @@ -98,10 +99,10 @@ func (pr *ProjectsTestSuite) TestProjectsCrudDownstreamCluster() { require.NoError(pr.T(), err, "Failed to add the user as a cluster owner to the downstream cluster") log.Info("Create a project in the downstream cluster and verify that the project can be listed.") - createdProject, err := projects.CreateProjectUsingWrangler(pr.client, pr.cluster.ID) + createdProject, err := projectapi.CreateProject(pr.client, pr.cluster.ID) require.NoError(pr.T(), err, "Failed to create project") - projectList, err := projectsapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ + projectList, err := projectapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ FieldSelector: "metadata.name=" + createdProject.Name, }) require.NoError(pr.T(), err, "Failed to list project.") @@ -116,16 +117,16 @@ func (pr *ProjectsTestSuite) TestProjectsCrudDownstreamCluster() { _, err = standardUserClient.WranglerContext.Mgmt.Project().Update(¤tProject) require.NoError(pr.T(), err, "Failed to update project.") - updatedProjectList, err := projectsapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ + updatedProjectList, err := projectapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s=%s", "hello", "world"), }) require.NoError(pr.T(), err) require.Equal(pr.T(), 1, len(updatedProjectList.Items), "Expected one project in the list") log.Info("Delete the project.") - err = projectsapi.DeleteProject(standardUserClient, createdProject.Namespace, createdProject.Name) + err = projectapi.DeleteProject(standardUserClient, createdProject.Namespace, createdProject.Name) require.NoError(pr.T(), err, "Failed to delete project") - projectList, err = projectsapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ + projectList, err = projectapi.ListProjects(standardUserClient, createdProject.Namespace, metav1.ListOptions{ FieldSelector: "metadata.name=" + createdProject.Name, }) require.NoError(pr.T(), err, "Failed to list project.") @@ -137,27 +138,27 @@ func (pr *ProjectsTestSuite) TestDeleteSystemProject() { defer subSession.Cleanup() log.Info("Delete the System Project in the local cluster.") - projectList, err := projectsapi.ListProjects(pr.client, projectsapi.LocalCluster, metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", systemProjectLabel, "true"), + projectList, err := projectapi.ListProjects(pr.client, rbacapi.LocalCluster, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", projectapi.SystemProjectLabel, "true"), }) require.NoError(pr.T(), err) require.Equal(pr.T(), 1, len(projectList.Items), "Expected one project in the list") systemProjectName := projectList.Items[0].ObjectMeta.Name - err = projectsapi.DeleteProject(pr.client, projectsapi.LocalCluster, systemProjectName) + err = projectapi.DeleteProject(pr.client, rbacapi.LocalCluster, systemProjectName) require.Error(pr.T(), err, "Failed to delete project") expectedErrorMessage := "admission webhook \"rancher.cattle.io.projects.management.cattle.io\" denied the request: System Project cannot be deleted" require.Equal(pr.T(), expectedErrorMessage, err.Error()) log.Info("Delete the System Project in the downstream cluster.") - projectList, err = projectsapi.ListProjects(pr.client, pr.cluster.ID, metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", systemProjectLabel, "true"), + projectList, err = projectapi.ListProjects(pr.client, pr.cluster.ID, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", projectapi.SystemProjectLabel, "true"), }) require.NoError(pr.T(), err) require.Equal(pr.T(), 1, len(projectList.Items), "Expected one project in the list") systemProjectName = projectList.Items[0].ObjectMeta.Name - err = projectsapi.DeleteProject(pr.client, pr.cluster.ID, systemProjectName) + err = projectapi.DeleteProject(pr.client, pr.cluster.ID, systemProjectName) require.Error(pr.T(), err, "Failed to delete project") expectedErrorMessage = "admission webhook \"rancher.cattle.io.projects.management.cattle.io\" denied the request: System Project cannot be deleted" require.Equal(pr.T(), expectedErrorMessage, err.Error()) @@ -176,26 +177,26 @@ func (pr *ProjectsTestSuite) TestMoveNamespaceOutOfProject() { require.NoError(pr.T(), err) log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, pr.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, pr.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(pr.T(), err) log.Info("Move the namespace out of the project.") - updatedNamespace, err := namespaces.GetNamespaceByName(standardUserClient, pr.cluster.ID, createdNamespace.Name) + updatedNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, pr.cluster.ID, createdNamespace.Name) require.NoError(pr.T(), err) - delete(updatedNamespace.Labels, projectsapi.ProjectIDAnnotation) - delete(updatedNamespace.Annotations, projectsapi.ProjectIDAnnotation) + delete(updatedNamespace.Labels, namespaceapi.ProjectIDAnnotation) + delete(updatedNamespace.Annotations, namespaceapi.ProjectIDAnnotation) downstreamContext, err := clusterapi.GetClusterWranglerContext(pr.client, pr.cluster.ID) require.NoError(pr.T(), err) - currentNamespace, err := namespaces.GetNamespaceByName(standardUserClient, pr.cluster.ID, updatedNamespace.Name) + currentNamespace, err := namespaceapi.GetNamespaceByName(standardUserClient, pr.cluster.ID, updatedNamespace.Name) require.NoError(pr.T(), err) updatedNamespace.ResourceVersion = currentNamespace.ResourceVersion _, err = downstreamContext.Core.Namespace().Update(updatedNamespace) require.NoError(pr.T(), err, "Failed to move the namespace out of the project") log.Info("Verify that the namespace does not have the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, pr.cluster.ID, createdProject.Name, updatedNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, pr.cluster.ID, createdProject.Name, updatedNamespace.Name) require.Error(pr.T(), err) } @@ -214,7 +215,7 @@ func (pr *ProjectsTestSuite) TestProjectWithResourceQuotaAndContainerDefaultReso cpuReservation := "50m" memoryLimit := "64Mi" memoryReservation := "32Mi" - projectTemplate := projectsapi.NewProjectTemplate(pr.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(pr.cluster.ID) projectTemplate.Spec.ContainerDefaultResourceLimit.LimitsCPU = cpuLimit projectTemplate.Spec.ContainerDefaultResourceLimit.RequestsCPU = cpuReservation projectTemplate.Spec.ContainerDefaultResourceLimit.LimitsMemory = memoryLimit @@ -225,7 +226,7 @@ func (pr *ProjectsTestSuite) TestProjectWithResourceQuotaAndContainerDefaultReso require.NoError(pr.T(), err) log.Info("Verify that the namespace has the label and annotation referencing the project.") - err = projects.WaitForProjectIDUpdate(standardUserClient, pr.cluster.ID, createdProject.Name, createdNamespace.Name) + err = namespaceapi.WaitForProjectIDUpdate(standardUserClient, pr.cluster.ID, createdProject.Name, createdNamespace.Name) require.NoError(pr.T(), err) log.Info("Verify that the pod limits and container default resource limit in the Project spec is accurate.") @@ -238,7 +239,7 @@ func (pr *ProjectsTestSuite) TestProjectWithResourceQuotaAndContainerDefaultReso require.Equal(pr.T(), memoryReservation, projectSpec.ContainerDefaultResourceLimit.RequestsMemory, "Memory reservation mismatch") log.Info("Verify that the namespace has the annotation: field.cattle.io/resourceQuota.") - err = checkAnnotationExistsInNamespace(standardUserClient, pr.cluster.ID, createdNamespace.Name, resourceQuotaAnnotation, true) + err = checkAnnotationExistsInNamespace(standardUserClient, pr.cluster.ID, createdNamespace.Name, projectapi.ResourceQuotaAnnotation, true) require.NoError(pr.T(), err, "'field.cattle.io/resourceQuota' annotation should exist") log.Info("Verify that the resource quota validation for the namespace is successful.") diff --git a/validation/projects/rbac_terminating_project_test.go b/validation/projects/rbac_terminating_project_test.go index cfe6a17ff..f69f870a1 100644 --- a/validation/projects/rbac_terminating_project_test.go +++ b/validation/projects/rbac_terminating_project_test.go @@ -11,7 +11,8 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/extensions/users" "github.com/rancher/shepherd/pkg/session" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" + rbacapi "github.com/rancher/tests/actions/kubeapi/rbac" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rancherleader" "github.com/rancher/tests/actions/rbac" @@ -63,16 +64,16 @@ func (rtp *RbacTerminatingProjectTestSuite) TestUserAdditionToClusterWithTermina logCaptureStartTime := time.Now() log.Info("Simulate a project stuck in terminating state by adding a finalizer to the project.") - finalizer := append([]string{dummyFinalizer}, createdProject.Finalizers...) + finalizer := append([]string{projectapi.DummyFinalizer}, createdProject.Finalizers...) updatedProject, err := projects.UpdateProjectNamespaceFinalizer(rtp.client, createdProject, finalizer) require.NoError(rtp.T(), err, "Failed to update finalizer.") - err = projects.WaitForProjectFinalizerToUpdate(rtp.client, createdProject.Name, createdProject.Namespace, 3) + err = projectapi.WaitForProjectFinalizerToUpdate(rtp.client, createdProject.Name, createdProject.Namespace, 3) require.NoError(rtp.T(), err) log.Info("Delete the Project.") - err = projectsapi.DeleteProject(rtp.client, createdProject.Namespace, createdProject.Name) + err = projectapi.DeleteProject(rtp.client, createdProject.Namespace, createdProject.Name) require.Error(rtp.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(rtp.client, createdProject.Name, createdProject.Namespace, 1) + err = projectapi.WaitForProjectFinalizerToUpdate(rtp.client, createdProject.Name, createdProject.Namespace, 1) require.NoError(rtp.T(), err) leaderPodName, err := rancherleader.GetRancherLeaderPodName(rtp.client) require.NoError(rtp.T(), err) @@ -80,7 +81,7 @@ func (rtp *RbacTerminatingProjectTestSuite) TestUserAdditionToClusterWithTermina logCaptureStartTime = time.Now() log.Info("Verify that there are no errors in the Rancher logs related to role binding.") errorRegex := `\[ERROR\] error syncing '(.*?)': handler mgmt-auth-crtb-controller: .*? (?:not found|is forbidden), requeuing` - err = pod.CheckPodLogsForErrors(rtp.client, projectsapi.LocalCluster, leaderPodName, projectsapi.RancherNamespace, errorRegex, logCaptureStartTime) + err = pod.CheckPodLogsForErrors(rtp.client, rbacapi.LocalCluster, leaderPodName, rbac.RancherDeploymentNamespace, errorRegex, logCaptureStartTime) require.NoError(rtp.T(), err) logCaptureStartTime = time.Now() @@ -90,7 +91,7 @@ func (rtp *RbacTerminatingProjectTestSuite) TestUserAdditionToClusterWithTermina require.NoError(rtp.T(), err, "Failed to remove the finalizer.") log.Info("Verify that there are no errors in the Rancher logs related to role binding.") - err = pod.CheckPodLogsForErrors(rtp.client, projectsapi.LocalCluster, leaderPodName, projectsapi.RancherNamespace, errorRegex, logCaptureStartTime) + err = pod.CheckPodLogsForErrors(rtp.client, rbacapi.LocalCluster, leaderPodName, rbac.RancherDeploymentNamespace, errorRegex, logCaptureStartTime) require.NoError(rtp.T(), err) } @@ -99,7 +100,7 @@ func (rtp *RbacTerminatingProjectTestSuite) TestUserAdditionToProjectWithTermina defer subSession.Cleanup() log.Info("Create a standard user.") - createdUser, err := users.CreateUserWithRole(rtp.client, users.UserConfig(), projectsapi.StandardUser) + createdUser, err := users.CreateUserWithRole(rtp.client, users.UserConfig(), rbac.StandardUser.String()) require.NoError(rtp.T(), err) rtp.T().Logf("Created user: %v", createdUser.Username) @@ -108,16 +109,16 @@ func (rtp *RbacTerminatingProjectTestSuite) TestUserAdditionToProjectWithTermina require.NoError(rtp.T(), err) log.Info("Simulate a project stuck in terminating state by adding a finalizer to the project.") - finalizer := append([]string{dummyFinalizer}, createdProject.Finalizers...) + finalizer := append([]string{projectapi.DummyFinalizer}, createdProject.Finalizers...) updatedProject, err := projects.UpdateProjectNamespaceFinalizer(rtp.client, createdProject, finalizer) require.NoError(rtp.T(), err, "Failed to update finalizer.") - err = projects.WaitForProjectFinalizerToUpdate(rtp.client, createdProject.Name, createdProject.Namespace, 3) + err = projectapi.WaitForProjectFinalizerToUpdate(rtp.client, createdProject.Name, createdProject.Namespace, 3) require.NoError(rtp.T(), err) log.Info("Delete the Project.") - err = projectsapi.DeleteProject(rtp.client, createdProject.Namespace, createdProject.Name) + err = projectapi.DeleteProject(rtp.client, createdProject.Namespace, createdProject.Name) require.Error(rtp.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(rtp.client, createdProject.Name, createdProject.Namespace, 1) + err = projectapi.WaitForProjectFinalizerToUpdate(rtp.client, createdProject.Name, createdProject.Namespace, 1) require.NoError(rtp.T(), err) log.Info("Add the standard user to the project as project owner.") diff --git a/validation/rbac/configmaps/configmaps_rbac_test.go b/validation/rbac/configmaps/configmaps_rbac_test.go index 89e8f9dac..ba47c1919 100644 --- a/validation/rbac/configmaps/configmaps_rbac_test.go +++ b/validation/rbac/configmaps/configmaps_rbac_test.go @@ -12,10 +12,11 @@ import ( "github.com/rancher/shepherd/pkg/wrangler" "github.com/rancher/tests/actions/configmaps" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" - deployment "github.com/rancher/tests/actions/workloads/deployment" + "github.com/rancher/tests/actions/workloads/deployment" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -287,17 +288,17 @@ func (cm *ConfigmapsRBACTestSuite) TestCRUDConfigmapAsClusterMember() { standardUser, standardUserClient, err := rbac.AddUserWithRoleToCluster(cm.client, rbac.StandardUser.String(), rbac.ClusterMember.String(), cm.cluster, nil) require.NoError(cm.T(), err) - projectTemplate := projectsapi.NewProjectTemplate(cm.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(cm.cluster.ID) projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": standardUser.ID, } createdProject, err := standardUserClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(cm.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) require.NoError(cm.T(), err) - namespace, err := projects.CreateNamespaceUsingWrangler(standardUserClient, cm.cluster.ID, createdProject.Name, nil) + namespace, err := namespaceapi.CreateNamespaceUsingWrangler(standardUserClient, cm.cluster.ID, createdProject.Name, nil) require.NoError(cm.T(), err) configMapCreatedByAdmin, err := configmaps.CreateConfigmap(namespace.Name, cm.client, data, cm.cluster.ID) diff --git a/validation/rbac/psa/project_updatepsa_test.go b/validation/rbac/psa/project_updatepsa_test.go index a5cfd2e2e..497ece98e 100644 --- a/validation/rbac/psa/project_updatepsa_test.go +++ b/validation/rbac/psa/project_updatepsa_test.go @@ -11,7 +11,7 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/pkg/session" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - "github.com/rancher/tests/actions/kubeapi/namespaces" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" log "github.com/sirupsen/logrus" @@ -81,7 +81,7 @@ func (pu *ProjectUpdatePsaTestSuite) TestCreateNamespaceWithPsaLabelsWithoutUpda log.Infof("As %v, trying to create a namespace with PSA labels in project %v", tt.role.String(), adminProject.Name) psaLabels := generatePSALabels() - createdNamespace, err := projects.CreateNamespaceUsingWrangler(userClient, pu.cluster.ID, adminProject.Name, psaLabels) + createdNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, pu.cluster.ID, adminProject.Name, psaLabels) switch tt.role { case rbac.Admin, rbac.ClusterOwner: assert.NoError(pu.T(), err) @@ -128,7 +128,7 @@ func (pu *ProjectUpdatePsaTestSuite) TestCreateNamespaceWithPsaLabelsWithUpdateP log.Infof("As a %v, creating a namespace with PSA labels in the project %v", tt.role.String(), adminProject.Name) psaLabels := generatePSALabels() - createdNamespace, err := projects.CreateNamespaceUsingWrangler(userClient, pu.cluster.ID, adminProject.Name, psaLabels) + createdNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, pu.cluster.ID, adminProject.Name, psaLabels) assert.NoError(pu.T(), err, "Expected namespace creation to succeed for role %s", tt.role.String()) actualLabels := getPSALabelsFromNamespace(createdNamespace) assert.Equal(pu.T(), actualLabels, psaLabels) @@ -172,7 +172,7 @@ func (pu *ProjectUpdatePsaTestSuite) TestUpdateNamespaceWithPsaLabelsWithoutUpda ctx, err := clusterapi.GetClusterWranglerContext(userClient, pu.cluster.ID) assert.NoError(pu.T(), err) - currentNamespace, err := namespaces.GetNamespaceByName(pu.client, pu.cluster.ID, createdNamespace.Name) + currentNamespace, err := namespaceapi.GetNamespaceByName(pu.client, pu.cluster.ID, createdNamespace.Name) assert.NoError(pu.T(), err) currentNamespace.ObjectMeta.Labels = psaLabels updatedNamespace, err := ctx.Core.Namespace().Update(currentNamespace) @@ -224,7 +224,7 @@ func (pu *ProjectUpdatePsaTestSuite) TestUpdateNamespaceWithPsaLabelsWithUpdateP ctx, err := clusterapi.GetClusterWranglerContext(userClient, pu.cluster.ID) assert.NoError(pu.T(), err) - currentNamespace, err := namespaces.GetNamespaceByName(pu.client, pu.cluster.ID, createdNamespace.Name) + currentNamespace, err := namespaceapi.GetNamespaceByName(pu.client, pu.cluster.ID, createdNamespace.Name) assert.NoError(pu.T(), err) currentNamespace.ObjectMeta.Labels = psaLabels updatedNamespace, err := ctx.Core.Namespace().Update(currentNamespace) @@ -261,7 +261,7 @@ func (pu *ProjectUpdatePsaTestSuite) TestCreateNamespaceWithPsaLabelsAsStandardU log.Infof("As user %v, create a namespace with PSA labels in project %v", newUser.Username, adminProject.Name) psaLabels := generatePSALabels() - createdNamespace, err := projects.CreateNamespaceUsingWrangler(userClient, pu.cluster.ID, adminProject.Name, psaLabels) + createdNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, pu.cluster.ID, adminProject.Name, psaLabels) require.NoError(pu.T(), err) actualLabels := getPSALabelsFromNamespace(createdNamespace) @@ -297,7 +297,7 @@ func (pu *ProjectUpdatePsaTestSuite) TestVerifyCreateNamespaceWithPsaLabelsWithM log.Infof("As user %v, create a namespace with PSA labels in project %v", newUser.Username, adminProject.Name) psaLabels := generatePSALabels() - createdNamespace, err := projects.CreateNamespaceUsingWrangler(userClient, pu.cluster.ID, adminProject.Name, psaLabels) + createdNamespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, pu.cluster.ID, adminProject.Name, psaLabels) require.NoError(pu.T(), err) actualLabels := getPSALabelsFromNamespace(createdNamespace) diff --git a/validation/rbac/secrets/rbac_opaque_secrets_test.go b/validation/rbac/secrets/rbac_opaque_secrets_test.go index fbe5b5bdc..cd697ff22 100644 --- a/validation/rbac/secrets/rbac_opaque_secrets_test.go +++ b/validation/rbac/secrets/rbac_opaque_secrets_test.go @@ -10,7 +10,8 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/pkg/session" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/secrets" @@ -312,17 +313,17 @@ func (rbos *RbacOpaqueSecretTestSuite) TestCrudSecretAsClusterMember() { standardUser, standardUserClient, err := rbac.AddUserWithRoleToCluster(rbos.client, rbac.StandardUser.String(), role, rbos.cluster, nil) require.NoError(rbos.T(), err) - projectTemplate := projectsapi.NewProjectTemplate(rbos.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rbos.cluster.ID) projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": standardUser.ID, } createdProject, err := standardUserClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(rbos.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(standardUserClient, createdProject.Name, createdProject.Namespace, 2) require.NoError(rbos.T(), err) - namespace, err := projects.CreateNamespaceUsingWrangler(standardUserClient, rbos.cluster.ID, createdProject.Name, nil) + namespace, err := namespaceapi.CreateNamespaceUsingWrangler(standardUserClient, rbos.cluster.ID, createdProject.Name, nil) require.NoError(rbos.T(), err) log.Infof("As a %v, create a secret in the project %v", role, createdProject.Name) diff --git a/validation/rbac/workloads/rbac_cronjob_test.go b/validation/rbac/workloads/rbac_cronjob_test.go index e480a882e..631b02792 100644 --- a/validation/rbac/workloads/rbac_cronjob_test.go +++ b/validation/rbac/workloads/rbac_cronjob_test.go @@ -10,7 +10,8 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/pkg/session" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/workloads/cronjob" @@ -249,17 +250,17 @@ func (rcj *RbacCronJobTestSuite) TestCrudCronJobAsClusterMember() { user, userClient, err := rbac.AddUserWithRoleToCluster(rcj.client, rbac.StandardUser.String(), role, rcj.cluster, nil) require.NoError(rcj.T(), err) - projectTemplate := projectsapi.NewProjectTemplate(rcj.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rcj.cluster.ID) projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": user.ID, } createdProject, err := userClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(rcj.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) require.NoError(rcj.T(), err) - namespace, err := projects.CreateNamespaceUsingWrangler(userClient, rcj.cluster.ID, createdProject.Name, nil) + namespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, rcj.cluster.ID, createdProject.Name, nil) require.NoError(rcj.T(), err) log.Infof("As a %v, creating a cronjob in the namespace %v", role, namespace.Name) diff --git a/validation/rbac/workloads/rbac_daemonset_test.go b/validation/rbac/workloads/rbac_daemonset_test.go index 884404d54..effa56e1d 100644 --- a/validation/rbac/workloads/rbac_daemonset_test.go +++ b/validation/rbac/workloads/rbac_daemonset_test.go @@ -10,7 +10,8 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/pkg/session" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/workloads/daemonset" @@ -240,17 +241,17 @@ func (rds *RbacDaemonsetTestSuite) TestCrudDaemonsetAsClusterMember() { user, userClient, err := rbac.AddUserWithRoleToCluster(rds.client, rbac.StandardUser.String(), role, rds.cluster, nil) require.NoError(rds.T(), err) - projectTemplate := projectsapi.NewProjectTemplate(rds.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rds.cluster.ID) projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": user.ID, } createdProject, err := userClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(rds.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) require.NoError(rds.T(), err) - namespace, err := projects.CreateNamespaceUsingWrangler(userClient, rds.cluster.ID, createdProject.Name, nil) + namespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, rds.cluster.ID, createdProject.Name, nil) require.NoError(rds.T(), err) log.Infof("As a %v, create a daemonset in the namespace %v", role, namespace.Name) diff --git a/validation/rbac/workloads/rbac_deployment_test.go b/validation/rbac/workloads/rbac_deployment_test.go index baab709cd..b6995276a 100644 --- a/validation/rbac/workloads/rbac_deployment_test.go +++ b/validation/rbac/workloads/rbac_deployment_test.go @@ -10,7 +10,8 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/pkg/session" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/workloads/deployment" @@ -240,17 +241,17 @@ func (rd *RbacDeploymentTestSuite) TestCrudDeploymentAsClusterMember() { user, userClient, err := rbac.AddUserWithRoleToCluster(rd.client, rbac.StandardUser.String(), role, rd.cluster, nil) require.NoError(rd.T(), err) - projectTemplate := projectsapi.NewProjectTemplate(rd.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rd.cluster.ID) projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": user.ID, } createdProject, err := userClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(rd.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) require.NoError(rd.T(), err) - namespace, err := projects.CreateNamespaceUsingWrangler(userClient, rd.cluster.ID, createdProject.Name, nil) + namespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, rd.cluster.ID, createdProject.Name, nil) require.NoError(rd.T(), err) log.Infof("As a %v, create a deployment in the namespace %v", role, namespace.Name) diff --git a/validation/rbac/workloads/rbac_job_test.go b/validation/rbac/workloads/rbac_job_test.go index 9fa4c7c95..65ec53d63 100644 --- a/validation/rbac/workloads/rbac_job_test.go +++ b/validation/rbac/workloads/rbac_job_test.go @@ -10,7 +10,8 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/pkg/session" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/workloads/job" @@ -249,17 +250,17 @@ func (rj *RbacJobTestSuite) TestCrudJobAsClusterMember() { user, userClient, err := rbac.AddUserWithRoleToCluster(rj.client, rbac.StandardUser.String(), role, rj.cluster, nil) require.NoError(rj.T(), err) - projectTemplate := projectsapi.NewProjectTemplate(rj.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rj.cluster.ID) projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": user.ID, } createdProject, err := userClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(rj.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) require.NoError(rj.T(), err) - namespace, err := projects.CreateNamespaceUsingWrangler(userClient, rj.cluster.ID, createdProject.Name, nil) + namespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, rj.cluster.ID, createdProject.Name, nil) require.NoError(rj.T(), err) log.Infof("As a %v, creating a job in the namespace %v", role, namespace.Name) diff --git a/validation/rbac/workloads/rbac_statefulset_test.go b/validation/rbac/workloads/rbac_statefulset_test.go index 25b0389f7..51367c644 100644 --- a/validation/rbac/workloads/rbac_statefulset_test.go +++ b/validation/rbac/workloads/rbac_statefulset_test.go @@ -10,7 +10,8 @@ import ( "github.com/rancher/shepherd/extensions/clusters" "github.com/rancher/shepherd/pkg/session" clusterapi "github.com/rancher/tests/actions/kubeapi/clusters" - projectsapi "github.com/rancher/tests/actions/kubeapi/projects" + namespaceapi "github.com/rancher/tests/actions/kubeapi/namespaces" + projectapi "github.com/rancher/tests/actions/kubeapi/projects" "github.com/rancher/tests/actions/projects" "github.com/rancher/tests/actions/rbac" "github.com/rancher/tests/actions/workloads/pods" @@ -246,17 +247,17 @@ func (rs *RbacStatefulsetTestSuite) TestCrudStatefulsetAsClusterMember() { user, userClient, err := rbac.AddUserWithRoleToCluster(rs.client, rbac.StandardUser.String(), role, rs.cluster, nil) require.NoError(rs.T(), err) - projectTemplate := projectsapi.NewProjectTemplate(rs.cluster.ID) + projectTemplate := projectapi.NewProjectTemplate(rs.cluster.ID) projectTemplate.Annotations = map[string]string{ "field.cattle.io/creatorId": user.ID, } createdProject, err := userClient.WranglerContext.Mgmt.Project().Create(projectTemplate) require.NoError(rs.T(), err) - err = projects.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) + err = projectapi.WaitForProjectFinalizerToUpdate(userClient, createdProject.Name, createdProject.Namespace, 2) require.NoError(rs.T(), err) - namespace, err := projects.CreateNamespaceUsingWrangler(userClient, rs.cluster.ID, createdProject.Name, nil) + namespace, err := namespaceapi.CreateNamespaceUsingWrangler(userClient, rs.cluster.ID, createdProject.Name, nil) require.NoError(rs.T(), err) log.Infof("As a %v, create a statefulset in the namespace %v", role, namespace.Name)