chore: Switch to new images (#830)
Co-authored-by: Oleksandr K <okozachenko@vexxhost.com>
Co-authored-by: okozachenko1203 <okozachenko1203@users.noreply.github.com>
diff --git a/.github/renovate.json b/.github/renovate.json
index 483a482..d9590f2 100644
--- a/.github/renovate.json
+++ b/.github/renovate.json
@@ -64,7 +64,6 @@
"quay.io/keycloak",
"quay.io/kiwigrid",
"quay.io/prometheus",
- "quay.io/skopeo",
"registry.k8s.io/"
]
},
diff --git a/Earthfile b/Earthfile
index 6be9fdb..76d7168 100644
--- a/Earthfile
+++ b/Earthfile
@@ -55,6 +55,14 @@
BUILD ./images/designate+image
BUILD ./images/glance+image
BUILD ./images/heat+image
+ BUILD ./images/horizon+image
+ BUILD ./images/ironic+image
+ BUILD ./images/keystone+image
+ BUILD ./images/magnum+image
+ BUILD ./images/manila+image
+ BUILD ./images/neutron+image
+ BUILD ./images/nova-ssh+image
+ BUILD ./images/nova+image
BUILD ./images/octavia+image
BUILD ./images/placement+image
BUILD ./images/senlin+image
@@ -63,7 +71,7 @@
FROM +build.venv.dev
COPY roles/defaults/vars/main.yml /defaults.yml
COPY build/pin-images.py /usr/local/bin/pin-images
- RUN /usr/local/bin/pin-images /defaults.yml /pinned.yml
+ RUN --no-cache /usr/local/bin/pin-images /defaults.yml /pinned.yml
SAVE ARTIFACT /pinned.yml AS LOCAL roles/defaults/vars/main.yml
gh:
diff --git a/cmd/atmosphere-ci/image.go b/cmd/atmosphere-ci/image.go
deleted file mode 100644
index 492d2b9..0000000
--- a/cmd/atmosphere-ci/image.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package main
-
-import (
- "github.com/spf13/cobra"
-)
-
-var imageCmd = &cobra.Command{
- Use: "image",
- Short: "Image sub-commands",
-}
-
-func init() {
- rootCmd.AddCommand(imageCmd)
-}
diff --git a/cmd/atmosphere-ci/image_repo.go b/cmd/atmosphere-ci/image_repo.go
deleted file mode 100644
index 2ab4d4a..0000000
--- a/cmd/atmosphere-ci/image_repo.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package main
-
-import (
- "github.com/spf13/cobra"
-)
-
-var imageRepoCmd = &cobra.Command{
- Use: "repo",
- Short: "Image repository sub-commands",
-}
-
-func init() {
- imageCmd.AddCommand(imageRepoCmd)
-}
diff --git a/cmd/atmosphere-ci/image_repo_init.go b/cmd/atmosphere-ci/image_repo_init.go
deleted file mode 100644
index b55f355..0000000
--- a/cmd/atmosphere-ci/image_repo_init.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package main
-
-import (
- "context"
-
- log "github.com/sirupsen/logrus"
- "github.com/spf13/cobra"
- "github.com/vexxhost/atmosphere/internal/pkg/image_repositories"
-)
-
-var (
- imageRepoInitCmd = &cobra.Command{
- Use: "init [project]",
- Short: "Initialize image repository",
- Args: cobra.MinimumNArgs(1),
-
- Run: func(cmd *cobra.Command, args []string) {
- ctx := context.TODO()
-
- repo := image_repositories.NewImageRepository(args[0])
- err := repo.CreateGithubRepository(ctx)
- if err != nil {
- log.Panic(err)
- }
-
- err = repo.UpdateGithubConfiguration(ctx)
- if err != nil {
- log.Panic(err)
- }
-
- err = repo.Synchronize(ctx, true)
- if err != nil {
- log.Panic(err)
- }
- },
- }
-)
-
-func init() {
- imageRepoCmd.AddCommand(imageRepoInitCmd)
-}
diff --git a/cmd/atmosphere-ci/image_repo_sync.go b/cmd/atmosphere-ci/image_repo_sync.go
deleted file mode 100644
index 87699f2..0000000
--- a/cmd/atmosphere-ci/image_repo_sync.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package main
-
-import (
- "context"
-
- log "github.com/sirupsen/logrus"
- "github.com/spf13/cobra"
- "github.com/vexxhost/atmosphere/internal/pkg/image_repositories"
-)
-
-var (
- admin bool
-
- imageRepoSyncCmd = &cobra.Command{
- Use: "sync [project]",
- Short: "Sync image repository",
- Args: cobra.MinimumNArgs(1),
-
- Run: func(cmd *cobra.Command, args []string) {
- ctx := context.TODO()
-
- repo := image_repositories.NewImageRepository(args[0])
-
- if admin {
- err := repo.UpdateGithubConfiguration(ctx)
- if err != nil {
- log.Panic(err)
- }
- }
-
- err := repo.Synchronize(ctx, admin)
- if err != nil {
- log.Panic(err)
- }
- },
- }
-)
-
-func init() {
- imageRepoCmd.PersistentFlags().BoolVar(&admin, "admin", false, "Run using admin PAT (will update repo configs)")
-
- imageRepoCmd.AddCommand(imageRepoSyncCmd)
-}
diff --git a/go.mod b/go.mod
index ef2a936..90397d4 100644
--- a/go.mod
+++ b/go.mod
@@ -6,8 +6,6 @@
require (
github.com/erikgeiser/promptkit v0.9.0
- github.com/go-git/go-billy/v5 v5.5.0
- github.com/go-git/go-git/v5 v5.11.0
github.com/goccy/go-yaml v1.11.2
github.com/google/go-github/v57 v57.0.0
github.com/gophercloud/gophercloud v1.8.0
@@ -20,7 +18,6 @@
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.4
- golang.org/x/oauth2 v0.15.0
gopkg.in/ini.v1 v1.67.0
gorm.io/driver/mysql v1.5.2
gorm.io/gorm v1.25.5
@@ -32,28 +29,21 @@
)
require (
- dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
- github.com/Microsoft/go-winio v0.6.1 // indirect
- github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/cert-manager/cert-manager v1.12.1 // indirect
github.com/charmbracelet/bubbles v0.16.1 // indirect
github.com/charmbracelet/bubbletea v0.24.2 // indirect
github.com/charmbracelet/lipgloss v0.7.1 // indirect
- github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
- github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
- github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/flosch/pongo2/v6 v6.0.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
- github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
@@ -61,7 +51,6 @@
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
@@ -75,12 +64,10 @@
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
- github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
@@ -101,18 +88,14 @@
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
- github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
- github.com/sergi/go-diff v1.1.0 // indirect
- github.com/skeema/knownhosts v1.2.1 // indirect
- github.com/xanzy/ssh-agent v0.3.3 // indirect
+ github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
- golang.org/x/crypto v0.16.0 // indirect
- golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.19.0 // indirect
+ golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
@@ -123,7 +106,6 @@
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
- gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
diff --git a/go.sum b/go.sum
index cd7f136..bbbd768 100644
--- a/go.sum
+++ b/go.sum
@@ -1,18 +1,9 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
-dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
-github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
-github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
-github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
-github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
@@ -21,7 +12,6 @@
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cert-manager/cert-manager v1.12.1 h1:QA8/diGdInzBRhqiyTITPC+wI9FaXbgOAAT3Dwe9KZE=
github.com/cert-manager/cert-manager v1.12.1/go.mod h1:ql0msU88JCcQSceN+PFjEY8U+AMe13y06vO2klJk8bs=
@@ -37,25 +27,17 @@
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
-github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
-github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
-github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
-github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
-github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikgeiser/promptkit v0.9.0 h1:3qL1mS/ntCrXdb8sTP/ka82CJ9kEQaGuYXNrYJkWYBc=
@@ -68,18 +50,8 @@
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/flosch/pongo2/v6 v6.0.0 h1:lsGru8IAzHgIAw6H2m4PCyleO58I40ow6apih0WprMU=
github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU=
-github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
-github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
-github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
-github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
-github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
-github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
-github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
-github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
@@ -109,8 +81,6 @@
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -170,8 +140,6 @@
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
@@ -182,11 +150,8 @@
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
-github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -252,22 +217,19 @@
github.com/percona/percona-xtradb-cluster-operator v1.13.0/go.mod h1:EuEh2c3STNlMTvuEMGeAkM6eDhKiIT5wtfcxBZLSjiA=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
-github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
-github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
+github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
-github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
+github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
+github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
-github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
-github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
@@ -277,11 +239,8 @@
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
-github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -290,17 +249,13 @@
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
-github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -318,10 +273,7 @@
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -331,9 +283,6 @@
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
-golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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=
@@ -346,9 +295,6 @@
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -360,16 +306,13 @@
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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
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=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -379,17 +322,12 @@
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -398,8 +336,6 @@
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@@ -413,7 +349,6 @@
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -445,17 +380,12 @@
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
-gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/images/Earthfile b/images/Earthfile
index b19d68b..4c0e423 100644
--- a/images/Earthfile
+++ b/images/Earthfile
@@ -17,3 +17,18 @@
useradd -u 42424 -g 42424 -M -d /var/lib/${PROJECT} -s /usr/sbin/nologin -c "${PROJECT} User" ${PROJECT} && \
mkdir -p /etc/${PROJECT} /var/log/${PROJECT} /var/lib/${PROJECT} /var/cache/${PROJECT} && \
chown -Rv ${PROJECT}:${PROJECT} /etc/${PROJECT} /var/log/${PROJECT} /var/lib/${PROJECT} /var/cache/${PROJECT}
+
+APPLY_PATCHES:
+ COMMAND
+ COPY --if-exists patches /patches
+ IF [ -d /patches ]
+ RUN \
+ apt-get update && \
+ apt-get install -y patch && \
+ for patch in /patches/*.patch; do \
+ patch -d /var/lib/openstack/lib/python3.10/site-packages/ -p1 < $patch; \
+ done && \
+ apt-get purge -y --auto-remove patch && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+ END
diff --git a/images/barbican/Earthfile b/images/barbican/Earthfile
index 3b37bea..af81b11 100644
--- a/images/barbican/Earthfile
+++ b/images/barbican/Earthfile
@@ -4,8 +4,12 @@
ARG PROJECT=barbican
ARG RELEASE=zed
ARG REF=7d6749fcb1ad16a3350de82cd8e523d5b55306f8
- ARG PIP_PACKAGES="pykmip"
- FROM ../openstack-service+image --PROJECT ${PROJECT} --RELEASE ${RELEASE} --PROJECT_REF ${REF} --PIP_PACKAGES "${PIP_PACKAGES}"
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "pykmip"
+ DO ../+APPLY_PATCHES
SAVE IMAGE --push \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/cinder/Earthfile b/images/cinder/Earthfile
index 1b85d72..62bb8e3 100644
--- a/images/cinder/Earthfile
+++ b/images/cinder/Earthfile
@@ -4,11 +4,15 @@
ARG PROJECT=cinder
ARG RELEASE=zed
ARG REF=002abc4ba004d0dc4fc8327751afec9cc7e5a326
- ARG PIP_PACKAGES="purestorage"
- FROM ../openstack-service+image --PROJECT ${PROJECT} --RELEASE ${RELEASE} --PROJECT_REF ${REF} --PIP_PACKAGES "${PIP_PACKAGES}"
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "purestorage"
DO \
../+APT_INSTALL \
--PACKAGES "ceph-common lsscsi nvme-cli python3-rados python3-rbd qemu-utils sysfsutils udev util-linux"
+ DO ../+APPLY_PATCHES
COPY ../kubernetes+image/kubectl /usr/local/bin/kubectl
SAVE IMAGE --push \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
diff --git a/images/cloud-archive-base/Earthfile b/images/cloud-archive-base/Earthfile
index 75e2c73..ab0f46a 100644
--- a/images/cloud-archive-base/Earthfile
+++ b/images/cloud-archive-base/Earthfile
@@ -2,7 +2,7 @@
image:
FROM ../base+image
- DO ../+APT_INSTALL --PACKAGES "ca-certificates lsb-release python3-distutils sudo ubuntu-cloud-keyring"
+ DO ../+APT_INSTALL --PACKAGES "ca-certificates libpython3.10 lsb-release python3-distutils sudo ubuntu-cloud-keyring"
ARG RELEASE
IF [ "$(lsb_release -sc)" = "jammy" ]
IF [ "${RELEASE}" = "yoga" ]
diff --git a/images/cloud-archive-builder/Earthfile b/images/cloud-archive-builder/Earthfile
deleted file mode 100644
index 1d9bfa1..0000000
--- a/images/cloud-archive-builder/Earthfile
+++ /dev/null
@@ -1,41 +0,0 @@
-VERSION 0.7
-
-image:
- ARG RELEASE
- FROM ../cloud-archive-base+image --RELEASE=${RELEASE}
- DO ../+APT_INSTALL --PACKAGES "\
- build-essential \
- curl \
- git \
- libssl-dev \
- libpcre3-dev \
- lsb-release \
- openssh-client \
- python3 \
- python3-dev \
- python3-pip \
- python3-venv"
- RUN --mount type=cache,target=/root/.cache \
- python3 -m venv --upgrade --system-site-packages /var/lib/openstack
- ENV UWSGI_PROFILE_OVERRIDE=ssl=true
- RUN --mount type=cache,target=/root/.cache \
- mkdir -p /wheels && \
- /var/lib/openstack/bin/pip3 wheel --wheel-dir /wheels uwsgi
- COPY ${RELEASE}/upper-constraints.txt /upper-constraints.txt
- ARG PROJECT
- ARG PROJECT_REF
- ARG PROJECT_REPO=https://opendev.org/openstack/${PROJECT}
- GIT CLONE --branch ${PROJECT_REF} ${PROJECT_REPO} /src
- # TODO(mnaser): patches
- ARG EXTRAS=""
- ARG PIP_PACKAGES=""
- RUN --mount=type=cache,target=/root/.cache \
- /var/lib/openstack/bin/pip3 install \
- --constraint /upper-constraints.txt \
- --find-links /wheels/ \
- pymysql \
- python-memcached \
- uwsgi \
- /src${EXTRAS} \
- ${PIP_PACKAGES}
- SAVE ARTIFACT /var/lib/openstack venv
diff --git a/images/cluster-api-provider-openstack/Earthfile b/images/cluster-api-provider-openstack/Earthfile
index 7a03308..92c0b2e 100644
--- a/images/cluster-api-provider-openstack/Earthfile
+++ b/images/cluster-api-provider-openstack/Earthfile
@@ -14,4 +14,4 @@
image:
FROM DOCKERFILE -f +clone/src/Dockerfile +clone/src/*
LABEL org.opencontainers.image.source=https://github.com/vexxhost/atmosphere
- SAVE IMAGE --push ghcr.io/vexxhost/atmosphere/cluster-api-provider-openstack:${CAPO_VERSION}-${EPOCH}
+ SAVE IMAGE --push ghcr.io/vexxhost/atmosphere/capi-openstack-controller:${CAPO_VERSION}-${EPOCH}
diff --git a/images/designate/Earthfile b/images/designate/Earthfile
index a26180b..50527b5 100644
--- a/images/designate/Earthfile
+++ b/images/designate/Earthfile
@@ -4,10 +4,14 @@
ARG PROJECT=designate
ARG RELEASE=zed
ARG REF=4012425843529b02486c39421dc7593fe1803367
- FROM ../openstack-service+image --PROJECT ${PROJECT} --RELEASE ${RELEASE} --PROJECT_REF ${REF}
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF}
DO \
../+APT_INSTALL \
--PACKAGES "bind9utils"
+ DO ../+APPLY_PATCHES
SAVE IMAGE --push \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/glance/Earthfile b/images/glance/Earthfile
index 55d6169..8e203c9 100644
--- a/images/glance/Earthfile
+++ b/images/glance/Earthfile
@@ -4,11 +4,15 @@
ARG PROJECT=glance
ARG RELEASE=zed
ARG REF=06a18202ab52c64803f044b8f848ed1c160905d2
- ARG PIP_PACKAGES="glance_store[cinder]"
- FROM ../openstack-service+image --PROJECT ${PROJECT} --RELEASE ${RELEASE} --PROJECT_REF ${REF} --PIP_PACKAGES "${PIP_PACKAGES}"
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "glance_store[cinder]"
DO \
../+APT_INSTALL \
--PACKAGES "ceph-common lsscsi nvme-cli python3-rados python3-rbd qemu-utils sysfsutils udev util-linux"
+ DO ../+APPLY_PATCHES
COPY ../kubernetes+image/kubectl /usr/local/bin/kubectl
SAVE IMAGE --push \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
diff --git a/images/heat/Earthfile b/images/heat/Earthfile
index 4fde452..87ee43b 100644
--- a/images/heat/Earthfile
+++ b/images/heat/Earthfile
@@ -4,10 +4,14 @@
ARG PROJECT=heat
ARG RELEASE=zed
ARG REF=a2b70a93658ecd2774f22c63a394c5629aefdbe7
- FROM ../openstack-service+image --PROJECT ${PROJECT} --RELEASE ${RELEASE} --PROJECT_REF ${REF}
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF}
DO \
../+APT_INSTALL \
- --PACKAGES "curl"
+ --PACKAGES "curl jq"
+ DO ../+APPLY_PATCHES
SAVE IMAGE --push \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/horizon/Earthfile b/images/horizon/Earthfile
new file mode 100644
index 0000000..6ff9199
--- /dev/null
+++ b/images/horizon/Earthfile
@@ -0,0 +1,18 @@
+VERSION 0.7
+
+image:
+ ARG PROJECT=horizon
+ ARG RELEASE=2023.2
+ ARG REF=3c6029cd94846235e25058b71522c13556f41f58
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "git+https://github.com/openstack/designate-dashboard.git@stable/${RELEASE} git+https://github.com/openstack/heat-dashboard.git@stable/${RELEASE} git+https://github.com/openstack/ironic-ui.git@stable/${RELEASE} git+https://github.com/openstack/magnum-ui.git@stable/${RELEASE} git+https://github.com/openstack/neutron-vpnaas-dashboard.git@stable/${RELEASE} git+https://github.com/openstack/octavia-dashboard.git@stable/${RELEASE} git+https://github.com/openstack/senlin-dashboard.git@stable/${RELEASE} git+https://github.com/openstack/manila-ui.git@stable/${RELEASE}"
+ DO \
+ ../+APT_INSTALL \
+ --PACKAGES "apache2 gettext libapache2-mod-wsgi-py3"
+ DO ../+APPLY_PATCHES
+ SAVE IMAGE --push \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/horizon/patches/0000-fix-ignore-errors-when-flavors-are-deleted.patch b/images/horizon/patches/0000-fix-ignore-errors-when-flavors-are-deleted.patch
new file mode 100644
index 0000000..50d68c9
--- /dev/null
+++ b/images/horizon/patches/0000-fix-ignore-errors-when-flavors-are-deleted.patch
@@ -0,0 +1,107 @@
+From c62527488bfeab588c4abbc8426688e4feef87a4 Mon Sep 17 00:00:00 2001
+From: okozachenko <okozachenko1203@gmail.com>
+Date: Thu, 2 Nov 2023 01:27:20 +1100
+Subject: [PATCH] fix: ignore errors when flavors are deleted
+
+The code used to list flavors when in the admin
+or project side was not consistent and raised
+alerts if viewing in the admin side but not in the
+project side.
+
+This patch moves their behaviour to be consistent
+and refactors the code to use the same code-base.
+
+Closes-Bug: #2042362
+Change-Id: I37cc02102285b1e83ec1343b710a57fb5ac4ba15
+(cherry picked from commit 40759aa9cdb9b2162b3f50df751c500db94943b3)
+---
+ .../dashboards/admin/instances/tests.py | 4 ----
+ .../dashboards/admin/instances/views.py | 17 +++++------------
+ .../dashboards/project/instances/tests.py | 1 +
+ .../dashboards/project/instances/views.py | 11 +++--------
+ 4 files changed, 9 insertions(+), 24 deletions(-)
+
+diff --git a/openstack_dashboard/dashboards/admin/instances/tests.py b/openstack_dashboard/dashboards/admin/instances/tests.py
+index 3630cb79ade..c6cf65e5dab 100644
+--- a/openstack_dashboard/dashboards/admin/instances/tests.py
++++ b/openstack_dashboard/dashboards/admin/instances/tests.py
+@@ -133,10 +133,6 @@ def test_index_flavor_get_exception(self):
+ res = self.client.get(INDEX_URL)
+ instances = res.context['table'].data
+ self.assertTemplateUsed(res, INDEX_TEMPLATE)
+- # Since error messages produced for each instance are identical,
+- # there will be only one error message for all instances
+- # (messages de-duplication).
+- self.assertMessageCount(res, error=1)
+ self.assertCountEqual(instances, servers)
+
+ self.assertEqual(self.mock_image_list_detailed.call_count, 4)
+diff --git a/openstack_dashboard/dashboards/admin/instances/views.py b/openstack_dashboard/dashboards/admin/instances/views.py
+index c35527fe465..efa28dd763e 100644
+--- a/openstack_dashboard/dashboards/admin/instances/views.py
++++ b/openstack_dashboard/dashboards/admin/instances/views.py
+@@ -33,6 +33,8 @@
+ from openstack_dashboard.dashboards.admin.instances \
+ import tables as project_tables
+ from openstack_dashboard.dashboards.admin.instances import tabs
++from openstack_dashboard.dashboards.project.instances \
++ import utils as instance_utils
+ from openstack_dashboard.dashboards.project.instances import views
+ from openstack_dashboard.dashboards.project.instances.workflows \
+ import update_instance
+@@ -215,18 +217,9 @@ def get_data(self):
+ else:
+ inst.image['name'] = _("-")
+
+- flavor_id = inst.flavor["id"]
+- try:
+- if flavor_id in flavor_dict:
+- inst.full_flavor = flavor_dict[flavor_id]
+- else:
+- # If the flavor_id is not in flavor_dict list,
+- # gets it via nova api.
+- inst.full_flavor = api.nova.flavor_get(
+- self.request, flavor_id)
+- except Exception:
+- msg = _('Unable to retrieve instance size information.')
+- exceptions.handle(self.request, msg)
++ inst.full_flavor = instance_utils.resolve_flavor(self.request,
++ inst, flavor_dict)
++
+ tenant = tenant_dict.get(inst.tenant_id, None)
+ inst.tenant_name = getattr(tenant, "name", None)
+ return instances
+diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py
+index 70d32bc4b3c..c44dedd5b5a 100644
+--- a/openstack_dashboard/dashboards/project/instances/tests.py
++++ b/openstack_dashboard/dashboards/project/instances/tests.py
+@@ -316,6 +316,7 @@ def test_index_flavor_list_exception(self):
+ self.mock_is_feature_available.return_value = True
+ self.mock_server_list_paged.return_value = [servers, False, False]
+ self.mock_servers_update_addresses.return_value = None
++ self.mock_flavor_get.side_effect = self.exceptions.nova
+ self.mock_flavor_list.side_effect = self.exceptions.nova
+ self.mock_image_list_detailed.return_value = (self.images.list(),
+ False, False)
+diff --git a/openstack_dashboard/dashboards/project/instances/views.py b/openstack_dashboard/dashboards/project/instances/views.py
+index badf540b830..b848f6fffd9 100644
+--- a/openstack_dashboard/dashboards/project/instances/views.py
++++ b/openstack_dashboard/dashboards/project/instances/views.py
+@@ -171,14 +171,9 @@ def get_data(self):
+ for instance in instances:
+ self._populate_image_info(instance, image_dict, volume_dict)
+
+- flavor_id = instance.flavor["id"]
+- if flavor_id in flavor_dict:
+- instance.full_flavor = flavor_dict[flavor_id]
+- else:
+- # If the flavor_id is not in flavor_dict,
+- # put info in the log file.
+- LOG.info('Unable to retrieve flavor "%s" for instance "%s".',
+- flavor_id, instance.id)
++ instance.full_flavor = instance_utils.resolve_flavor(self.request,
++ instance,
++ flavor_dict)
+
+ return instances
+
diff --git a/images/horizon/patches/0001-fix-disable-resizing-for-admins.patch b/images/horizon/patches/0001-fix-disable-resizing-for-admins.patch
new file mode 100644
index 0000000..aaa5058
--- /dev/null
+++ b/images/horizon/patches/0001-fix-disable-resizing-for-admins.patch
@@ -0,0 +1,112 @@
+From d3ac70fb12dc363a0fbed39bcfd3642e36f4515d Mon Sep 17 00:00:00 2001
+From: Mohammed Naser <mnaser@vexxhost.com>
+Date: Mon, 20 Feb 2023 00:55:14 +0000
+Subject: [PATCH] fix: disable resizing for admins
+
+By default, admins see all clusters and they are allowed to do all
+actions however the resize function will not work so we're displaying
+something for admins that they can't use.
+
+This will hide the resize button for clusters that don't match the
+project ID of the current user.
+
+Change-Id: If09c509abdd21a5a7b9bc374af52a06404fb0ff8
+(cherry picked from commit 345f853567d25f1b163025f0295c742582052748)
+---
+ .../clusters/resize/resize.service.js | 9 ++++---
+ .../clusters/resize/resize.service.spec.js | 27 ++++++++++++++-----
+ 2 files changed, 26 insertions(+), 10 deletions(-)
+
+diff --git a/magnum_ui/static/dashboard/container-infra/clusters/resize/resize.service.js b/magnum_ui/static/dashboard/container-infra/clusters/resize/resize.service.js
+index ebc6a961..b86833a0 100644
+--- a/magnum_ui/static/dashboard/container-infra/clusters/resize/resize.service.js
++++ b/magnum_ui/static/dashboard/container-infra/clusters/resize/resize.service.js
+@@ -32,6 +32,7 @@
+ resizeService.$inject = [
+ '$rootScope',
+ '$q',
++ 'horizon.app.core.openstack-service-api.userSession',
+ 'horizon.app.core.openstack-service-api.magnum',
+ 'horizon.framework.util.actions.action-result.service',
+ 'horizon.framework.util.i18n.gettext',
+@@ -43,8 +44,8 @@
+ ];
+
+ function resizeService(
+- $rootScope, $q, magnum, actionResult, gettext, $qExtensions, modal, toast, spinnerModal,
+- resourceType
++ $rootScope, $q, userSession, magnum, actionResult, gettext, $qExtensions,
++ modal, toast, spinnerModal, resourceType
+ ) {
+
+ var modalConfig, formModel;
+@@ -87,8 +88,8 @@
+ return deferred.promise;
+ }
+
+- function allowed() {
+- return $qExtensions.booleanAsPromise(true);
++ function allowed(selected) {
++ return userSession.isCurrentProject(selected.project_id);
+ }
+
+ function constructModalConfig(workerNodesList) {
+diff --git a/magnum_ui/static/dashboard/container-infra/clusters/resize/resize.service.spec.js b/magnum_ui/static/dashboard/container-infra/clusters/resize/resize.service.spec.js
+index 842df87d..27fa8064 100644
+--- a/magnum_ui/static/dashboard/container-infra/clusters/resize/resize.service.spec.js
++++ b/magnum_ui/static/dashboard/container-infra/clusters/resize/resize.service.spec.js
+@@ -19,16 +19,17 @@
+
+ describe('horizon.dashboard.container-infra.clusters.resize.service', function() {
+
+- var service, $scope, $q, deferred, magnum, spinnerModal, modalConfig;
++ var service, $scope, $q, deferred, magnum, spinnerModal, modalConfig, userSession;
+ var selected = {
+- id: 1
++ id: 1,
++ project_id: "f5ed2d21437644adb2669f9ade9c949b"
+ };
+ var modal = {
+ open: function(config) {
+ deferred = $q.defer();
+ deferred.resolve(config);
+ modalConfig = config;
+-
++``
+ return deferred.promise;
+ }
+ };
+@@ -50,6 +51,7 @@
+ 'horizon.dashboard.container-infra.clusters.resize.service');
+ magnum = $injector.get('horizon.app.core.openstack-service-api.magnum');
+ spinnerModal = $injector.get('horizon.framework.widgets.modal-wait-spinner.service');
++ userSession = $injector.get('horizon.app.core.openstack-service-api.userSession');
+
+ spyOn(spinnerModal, 'showModalSpinner').and.callFake(function() {});
+ spyOn(spinnerModal, 'hideModalSpinner').and.callFake(function() {});
+@@ -60,9 +62,22 @@
+ spyOn(modal, 'open').and.callThrough();
+ }));
+
+- it('should check the policy if the user is allowed to update cluster', function() {
+- var allowed = service.allowed();
+- expect(allowed).toBeTruthy();
++ it('should allow user to resize cluster if they are in the same project', async function() {
++ spyOn(userSession, 'get').and.returnValue({project_id: selected.project_id});
++
++ await service.allowed(selected);
++ });
++
++ it('should not allow user to resize cluster if they are in a different project', async function() {
++ spyOn(userSession, 'get').and.returnValue({project_id: 'different_project'});
++
++ try {
++ await service.allowed(selected);
++ } catch (err) {
++ return;
++ }
++
++ throw new Error('User should not be allowed to resize cluster');
+ });
+
+ it('should open the modal, hide the loading spinner and check the form model',
diff --git a/images/horizon/patches/0002-capi-avoid-going-through-heat-for-worker-list.patch b/images/horizon/patches/0002-capi-avoid-going-through-heat-for-worker-list.patch
new file mode 100644
index 0000000..5970ef2
--- /dev/null
+++ b/images/horizon/patches/0002-capi-avoid-going-through-heat-for-worker-list.patch
@@ -0,0 +1,68 @@
+From 6ecbb870f24f5c5c4a5b548166ac292801adda84 Mon Sep 17 00:00:00 2001
+From: Mohammed Naser <mnaser@vexxhost.com>
+Date: Sun, 19 Feb 2023 21:39:46 +0000
+Subject: [PATCH] [capi] Avoid going through Heat for worker list
+
+By default, Magnum UI goes through Heat to get the list of nodes
+which is not correct since it's making an assumption that Heat
+is always in use.
+
+The fix for this would be to make sure that Magnum has a list of
+all the VMs in it's database (or some sort of API call that
+returns them all from the driver) but that's quite a big amount
+of work to implement for now.
+
+So for now, if stack_id doesn't look like a UUID, we assume it
+is deployed using Clsuter API driver for Magnum and look up with
+that alternative method instead.
+
+(cherry picked from commit 6f31cc5cacf23398b76392922ee9863d50aa9e7e)
+(cherry picked from commit d44f16f13a89d7fb00d3d949a392d638ce2d0cc8)
+(cherry picked from commit 72122e350429590e9002058e7e35c4dcc94d2d4f)
+---
+ magnum_ui/api/rest/magnum.py | 18 ++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/magnum_ui/api/rest/magnum.py b/magnum_ui/api/rest/magnum.py
+index ba66e0e..bf331bc 100644
+--- a/magnum_ui/api/rest/magnum.py
++++ b/magnum_ui/api/rest/magnum.py
+@@ -17,6 +17,8 @@
+
+ from collections import defaultdict
+
++from oslo_utils import uuidutils
++
+ from django.conf import settings
+ from django.http import HttpResponse
+ from django.http import HttpResponseNotFound
+@@ -228,6 +230,19 @@ class ClusterResize(generic.View):
+
+ url_regex = r'container_infra/clusters/(?P<cluster_id>[^/]+)/resize$'
+
++ def _cluster_api_resize_get(self, request, cluster):
++ search_opts = {"name": "%s-" % cluster["stack_id"]}
++ servers = api.nova.server_list(request, search_opts=search_opts)[0]
++
++ worker_nodes = []
++ for server in servers:
++ control_plane_prefix = "%s-control-plane" % cluster["stack_id"]
++ if not server.name.startswith(control_plane_prefix):
++ worker_nodes.append({"name": server.name, "id": server.id})
++
++ return {"cluster": change_to_id(cluster),
++ "worker_nodes": worker_nodes}
++
+ @rest_utils.ajax()
+ def get(self, request, cluster_id):
+ """Get cluster details for resize"""
+@@ -237,6 +252,9 @@ def get(self, request, cluster_id):
+ print(e)
+ return HttpResponseNotFound()
+
++ if not uuidutils.is_uuid_like(cluster["stack_id"]):
++ return self._cluster_api_resize_get(request, cluster)
++
+ stack = heat.stack_get(request, cluster["stack_id"])
+ search_opts = {"name": "%s-" % stack.stack_name}
+ servers = api.nova.server_list(request, search_opts=search_opts)[0]
diff --git a/images/ironic/Earthfile b/images/ironic/Earthfile
new file mode 100644
index 0000000..74b2ed9
--- /dev/null
+++ b/images/ironic/Earthfile
@@ -0,0 +1,18 @@
+VERSION 0.7
+
+image:
+ ARG PROJECT=ironic
+ ARG RELEASE=zed
+ ARG REF=e38735cb95263b0c54f2fd719ff6b714efbddbb3
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "python-dracclient sushy"
+ DO \
+ ../+APT_INSTALL \
+ --PACKAGES "ethtool ipmitool iproute2 ipxe lshw qemu-utils tftpd-hpa"
+ DO ../+APPLY_PATCHES
+ SAVE IMAGE --push \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/keystone/Earthfile b/images/keystone/Earthfile
new file mode 100644
index 0000000..9478a03
--- /dev/null
+++ b/images/keystone/Earthfile
@@ -0,0 +1,30 @@
+VERSION 0.7
+
+image:
+ ARG PROJECT=keystone
+ ARG RELEASE=zed
+ ARG REF=72a4fc0f3ccf7a5ca9fc40e5364e14f881ec27b2
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "keystone-keycloak-backend==0.1.6" \
+ --EXTRAS "[ldap]"
+ DO \
+ ../+APT_INSTALL \
+ --PACKAGES "apache2 libapache2-mod-wsgi-py3"
+ DO ../+APPLY_PATCHES
+ ARG MOD_AUTH_OPENIDC_VERSION=2.4.12.1
+ ARG TARGETARCH
+ RUN \
+ apt-get update && \
+ apt-get install -y --no-install-recommends curl && \
+ curl -LO https://github.com/OpenIDC/mod_auth_openidc/releases/download/v${MOD_AUTH_OPENIDC_VERSION}/libapache2-mod-auth-openidc_${MOD_AUTH_OPENIDC_VERSION}-1.$(lsb_release -sc)_${TARGETARCH}.deb && \
+ apt-get install -y --no-install-recommends ./libapache2-mod-auth-openidc_${MOD_AUTH_OPENIDC_VERSION}-1.$(lsb_release -sc)_${TARGETARCH}.deb && \
+ a2enmod auth_openidc && \
+ apt-get purge -y --auto-remove curl && \
+ apt-get clean && \
+ rm -rfv /var/lib/apt/lists/* libapache2-mod-auth-openidc_${MOD_AUTH_OPENIDC_VERSION}-1.$(lsb_release -sc)_${TARGETARCH}.deb
+ SAVE IMAGE --push \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/kubernetes-entrypoint/Earthfile b/images/kubernetes-entrypoint/Earthfile
new file mode 100644
index 0000000..2191e77
--- /dev/null
+++ b/images/kubernetes-entrypoint/Earthfile
@@ -0,0 +1,32 @@
+VERSION 0.7
+ARG --global COMMIT=e8c2b17e1261c6a1b0fed1fcd5e1c337fc014219
+
+build:
+ FROM golang:1.21.5-bookworm
+ DO ../+APT_INSTALL --PACKAGES "patch"
+ GIT CLONE --branch ${COMMIT} https://opendev.org/airship/kubernetes-entrypoint /src
+ WORKDIR /src
+ RUN \
+ curl https://review.opendev.org/changes/airship%2Fkubernetes-entrypoint~904537/revisions/1/patch?download | \
+ base64 --decode | \
+ patch -p1
+ ARG GOARCH
+ RUN \
+ --mount=type=cache,mode=0755,target=/go/pkg/mod \
+ CGO_ENABLED=0 GOOS=linux go build -o /main
+ SAVE ARTIFACT /main
+
+platform-image:
+ FROM scratch
+ ARG TARGETARCH
+ COPY \
+ --platform=linux/amd64 \
+ (+build/main --GOARCH=$TARGETARCH) /kubernetes-entrypoint
+ USER 65534
+ ENTRYPOINT ["/kubernetes-entrypoint"]
+ SAVE IMAGE --push \
+ ghcr.io/vexxhost/atmosphere/kubernetes-entrypoint:${COMMIT} \
+ ghcr.io/vexxhost/atmosphere/kubernetes-entrypoint:latest
+
+image:
+ BUILD --platform linux/amd64 --platform linux/arm64 +platform-image
diff --git a/images/kubernetes/Earthfile b/images/kubernetes/Earthfile
index bf613b2..4a11937 100644
--- a/images/kubernetes/Earthfile
+++ b/images/kubernetes/Earthfile
@@ -2,6 +2,8 @@
image:
FROM curlimages/curl:7.78.0
+ ARG TARGETOS
+ ARG TARGETARCH
RUN curl -L "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/${TARGETOS}/${TARGETARCH}/kubectl" -o /tmp/kubectl
- RUN chmod +x /tmp/kubectl
+ RUN chmod +x /tmp/kubectl && /tmp/kubectl version --client=true
SAVE ARTIFACT /tmp/kubectl kubectl
diff --git a/images/magnum/Earthfile b/images/magnum/Earthfile
new file mode 100644
index 0000000..f7e189f
--- /dev/null
+++ b/images/magnum/Earthfile
@@ -0,0 +1,18 @@
+VERSION 0.7
+
+image:
+ ARG PROJECT=magnum
+ ARG RELEASE=zed
+ ARG REF=c671d8baf9d6f4705a1b832ae2d96980e5a58db6
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "magnum-cluster-api==0.13.3"
+ DO \
+ ../+APT_INSTALL \
+ --PACKAGES "haproxy"
+ DO ../+APPLY_PATCHES
+ SAVE IMAGE --push \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/magnum/patches/0000-containerd-cni-plugin-path-in-coreos-35.patch b/images/magnum/patches/0000-containerd-cni-plugin-path-in-coreos-35.patch
new file mode 100644
index 0000000..8d7c411
--- /dev/null
+++ b/images/magnum/patches/0000-containerd-cni-plugin-path-in-coreos-35.patch
@@ -0,0 +1,35 @@
+From 7f9f804a766083b65389b4cc2870fbb1a951b29e Mon Sep 17 00:00:00 2001
+From: Mohammed Naser <mnaser@vexxhost.com>
+Date: Thu, 9 Mar 2023 09:45:43 +0100
+Subject: [PATCH] Containerd cni plugin path in CoreOS 35 (#1)
+
+Task: 45387
+Story: 2010041
+
+In Fedora CoreOS 35 default containerd cni bin_dir is set to
+/usr/libexec/cni. Since we're installing our own in /opt/cni/bin need to
+override in containerd config.toml otherwise pods get stuck in
+ContainerCreating state looking for for ex. calico in wrong path.
+
+Change-Id: I3242b718e32c92942ac471bc7e182a42e803005b
+(cherry picked from commit fbfd3ce9a30fed291c96179f409821b7e016d2ba)
+
+Co-authored-by: Jakub Darmach <jakub@stackhpc.com>
+---
+ .../common/templates/kubernetes/fragments/install-cri.sh | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/magnum/drivers/common/templates/kubernetes/fragments/install-cri.sh b/magnum/drivers/common/templates/kubernetes/fragments/install-cri.sh
+index f60efe47a8..61204fe47a 100644
+--- a/magnum/drivers/common/templates/kubernetes/fragments/install-cri.sh
++++ b/magnum/drivers/common/templates/kubernetes/fragments/install-cri.sh
+@@ -10,6 +10,9 @@ ssh_cmd="ssh -F /srv/magnum/.ssh/config root@localhost"
+ if [ "${CONTAINER_RUNTIME}" = "containerd" ] ; then
+ $ssh_cmd systemctl disable docker.service docker.socket
+ $ssh_cmd systemctl stop docker.service docker.socket
++ if $ssh_cmd [ -f /etc/containerd/config.toml ] ; then
++ $ssh_cmd sed -i 's/bin_dir.*$/bin_dir\ =\ \""\/opt\/cni\/bin\/"\"/' /etc/containerd/config.toml
++ fi
+ if [ -z "${CONTAINERD_TARBALL_URL}" ] ; then
+ CONTAINERD_TARBALL_URL="https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/cri-containerd-cni-${CONTAINERD_VERSION}-linux-amd64.tar.gz"
+ fi
diff --git a/images/magnum/patches/0001-update-chart-metadata-version-to-reflect-breaking-change-in-helm-v3-5-2.patch b/images/magnum/patches/0001-update-chart-metadata-version-to-reflect-breaking-change-in-helm-v3-5-2.patch
new file mode 100644
index 0000000..9bee808
--- /dev/null
+++ b/images/magnum/patches/0001-update-chart-metadata-version-to-reflect-breaking-change-in-helm-v3-5-2.patch
@@ -0,0 +1,28 @@
+From 61592d46e7fc5644c4b5148c7ca6bf767131e504 Mon Sep 17 00:00:00 2001
+From: okozachenko1203 <okozachenko1203@gmail.com>
+Date: Fri, 31 Mar 2023 23:41:43 +1100
+Subject: [PATCH] Update chart.metadata.version to reflect breaking change in
+ helm v3.5.2
+
+https: //github.com/helm/helm/issues/9342
+Change-Id: I1dbe7b0b85380e713ebb5dcdd7ecbfc6a438b852
+(cherry picked from commit ebee3263b6b3d3fa213ea8f837911b89785a4700)
+---
+ .../templates/kubernetes/fragments/install-helm-modules.sh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/magnum/drivers/common/templates/kubernetes/fragments/install-helm-modules.sh b/magnum/drivers/common/templates/kubernetes/fragments/install-helm-modules.sh
+index 475e8dbf6c..a0b3f4bc75 100644
+--- a/magnum/drivers/common/templates/kubernetes/fragments/install-helm-modules.sh
++++ b/magnum/drivers/common/templates/kubernetes/fragments/install-helm-modules.sh
+@@ -72,8 +72,8 @@ else
+ cat << EOF > Chart.yaml
+ apiVersion: v1
+ name: magnum
+-version: metachart
+-appVersion: metachart
++version: 1.0.0
++appVersion: v1.0.0
+ description: Magnum Helm Charts
+ EOF
+ sed -i '1i\dependencies:' requirements.yaml
diff --git a/images/magnum/patches/0002-support-k8s-1-24.patch b/images/magnum/patches/0002-support-k8s-1-24.patch
new file mode 100644
index 0000000..bc69c96
--- /dev/null
+++ b/images/magnum/patches/0002-support-k8s-1-24.patch
@@ -0,0 +1,75 @@
+From f25b5c0f89dcc16918d5d8636355831ce0dc4091 Mon Sep 17 00:00:00 2001
+From: Daniel Meyerholt <dxm523@gmail.com>
+Date: Sat, 28 May 2022 12:43:45 +0200
+Subject: [PATCH] Support K8s 1.24+
+
+Only specify dockershim options when container runtime is not containerd.
+Those options were ignored in the past when using containerd but since 1.24
+kubelet refuses to start.
+
+Task: 45282
+Story: 2010028
+
+Signed-off-by: Daniel Meyerholt <dxm523@gmail.com>
+Change-Id: Ib44cc30285c8bd4219d4a45dc956696505ddd570
+(cherry picked from commit f7cd2928d6a84e869c87c333b814de76cae9a920)
+---
+ .../kubernetes/fragments/configure-kubernetes-master.sh | 3 ++-
+ .../kubernetes/fragments/configure-kubernetes-minion.sh | 3 ++-
+ .../notes/support-dockershim-removal-cad104d069f1a50b.yaml | 5 +++++
+ 3 files changed, 9 insertions(+), 2 deletions(-)
+ create mode 100644 releasenotes/notes/support-dockershim-removal-cad104d069f1a50b.yaml
+
+diff --git a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
+index 42267404a1..61ca0a7a59 100644
+--- a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
++++ b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
+@@ -454,7 +454,6 @@ if [ -f /etc/sysconfig/docker ] ; then
+ sed -i -E 's/^OPTIONS=("|'"'"')/OPTIONS=\1'"${DOCKER_OPTIONS}"' /' /etc/sysconfig/docker
+ fi
+
+-KUBELET_ARGS="${KUBELET_ARGS} --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
+ KUBELET_ARGS="${KUBELET_ARGS} --register-with-taints=node-role.kubernetes.io/master=:NoSchedule"
+ KUBELET_ARGS="${KUBELET_ARGS} --node-labels=magnum.openstack.org/role=${NODEGROUP_ROLE}"
+ KUBELET_ARGS="${KUBELET_ARGS} --node-labels=magnum.openstack.org/nodegroup=${NODEGROUP_NAME}"
+@@ -503,6 +502,8 @@ if [ ${CONTAINER_RUNTIME} = "containerd" ] ; then
+ KUBELET_ARGS="${KUBELET_ARGS} --container-runtime=remote"
+ KUBELET_ARGS="${KUBELET_ARGS} --runtime-request-timeout=15m"
+ KUBELET_ARGS="${KUBELET_ARGS} --container-runtime-endpoint=unix:///run/containerd/containerd.sock"
++else
++ KUBELET_ARGS="${KUBELET_ARGS} --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
+ fi
+
+ if [ -z "${KUBE_NODE_IP}" ]; then
+diff --git a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
+index 46055244ac..60fc1918bc 100644
+--- a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
++++ b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
+@@ -282,6 +282,8 @@ if [ ${CONTAINER_RUNTIME} = "containerd" ] ; then
+ KUBELET_ARGS="${KUBELET_ARGS} --container-runtime=remote"
+ KUBELET_ARGS="${KUBELET_ARGS} --runtime-request-timeout=15m"
+ KUBELET_ARGS="${KUBELET_ARGS} --container-runtime-endpoint=unix:///run/containerd/containerd.sock"
++else
++ KUBELET_ARGS="${KUBELET_ARGS} --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
+ fi
+
+ auto_healing_enabled=$(echo ${AUTO_HEALING_ENABLED} | tr '[:upper:]' '[:lower:]')
+@@ -290,7 +292,6 @@ if [[ "${auto_healing_enabled}" = "true" && "${autohealing_controller}" = "drain
+ KUBELET_ARGS="${KUBELET_ARGS} --node-labels=draino-enabled=true"
+ fi
+
+-KUBELET_ARGS="${KUBELET_ARGS} --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
+
+ sed -i '
+ /^KUBELET_ADDRESS=/ s/=.*/="--address=0.0.0.0"/
+diff --git a/releasenotes/notes/support-dockershim-removal-cad104d069f1a50b.yaml b/releasenotes/notes/support-dockershim-removal-cad104d069f1a50b.yaml
+new file mode 100644
+index 0000000000..f228db6321
+--- /dev/null
++++ b/releasenotes/notes/support-dockershim-removal-cad104d069f1a50b.yaml
+@@ -0,0 +1,5 @@
++---
++fixes:
++ - |
++ Support K8s 1.24 which removed support of dockershim. Needs containerd as
++ container runtime.
diff --git a/images/magnum/patches/0003-fix-kubelet-for-fedora-coreos-36-to-provide-real-resolvconf-to-containers.patch b/images/magnum/patches/0003-fix-kubelet-for-fedora-coreos-36-to-provide-real-resolvconf-to-containers.patch
new file mode 100644
index 0000000..a79d935
--- /dev/null
+++ b/images/magnum/patches/0003-fix-kubelet-for-fedora-coreos-36-to-provide-real-resolvconf-to-containers.patch
@@ -0,0 +1,48 @@
+From 34564ae02c1e7bef3b69967c7497f201058c82a5 Mon Sep 17 00:00:00 2001
+From: Dale Smith <dale@catalystcloud.nz>
+Date: Thu, 22 Dec 2022 16:06:07 +1300
+Subject: [PATCH] Fix kubelet for Fedora CoreOS 36 to provide real resolvconf
+ to containers.
+
+In Fedora CoreOS 36 CoreDNS cannot start correctly due to a loopback issue
+where /etc/resolv.conf is mounted and points to localhost.
+
+Tested on Fedora CoreOS 35,36,37, with Docker and containerd.
+
+https://coredns.io/plugins/loop/#troubleshooting-loops-in-kubernetes-clusters
+https://fedoraproject.org/wiki/Changes/systemd-resolved#Detailed_Description
+
+Story: 2010519
+Depends-On: I3242b718e32c92942ac471bc7e182a42e803005b
+
+Change-Id: I8106324ce71d6c22fa99e1a84b5a09743315811a
+(cherry picked from commit 5061dc5bb5c9aaba8fcfb3cb06404ada084a1908)
+---
+ .../kubernetes/fragments/configure-kubernetes-master.sh | 1 +
+ .../kubernetes/fragments/configure-kubernetes-minion.sh | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
+index 61ca0a7a59..24d7e48f4f 100644
+--- a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
++++ b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
+@@ -435,6 +435,7 @@ $ssh_cmd mkdir -p /etc/kubernetes/manifests
+ KUBELET_ARGS="--register-node=true --pod-manifest-path=/etc/kubernetes/manifests --hostname-override=${INSTANCE_NAME}"
+ KUBELET_ARGS="${KUBELET_ARGS} --pod-infra-container-image=${CONTAINER_INFRA_PREFIX:-gcr.io/google_containers/}pause:3.1"
+ KUBELET_ARGS="${KUBELET_ARGS} --cluster_dns=${DNS_SERVICE_IP} --cluster_domain=${DNS_CLUSTER_DOMAIN}"
++KUBELET_ARGS="${KUBELET_ARGS} --resolv-conf=/run/systemd/resolve/resolv.conf"
+ KUBELET_ARGS="${KUBELET_ARGS} --volume-plugin-dir=/var/lib/kubelet/volumeplugins"
+ KUBELET_ARGS="${KUBELET_ARGS} ${KUBELET_OPTIONS}"
+
+diff --git a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
+index 60fc1918bc..6508ac3ef0 100644
+--- a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
++++ b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-minion.sh
+@@ -250,6 +250,7 @@ mkdir -p /etc/kubernetes/manifests
+ KUBELET_ARGS="--pod-manifest-path=/etc/kubernetes/manifests --kubeconfig ${KUBELET_KUBECONFIG} --hostname-override=${INSTANCE_NAME}"
+ KUBELET_ARGS="${KUBELET_ARGS} --address=${KUBE_NODE_IP} --port=10250 --read-only-port=0 --anonymous-auth=false --authorization-mode=Webhook --authentication-token-webhook=true"
+ KUBELET_ARGS="${KUBELET_ARGS} --cluster_dns=${DNS_SERVICE_IP} --cluster_domain=${DNS_CLUSTER_DOMAIN}"
++KUBELET_ARGS="${KUBELET_ARGS} --resolv-conf=/run/systemd/resolve/resolv.conf"
+ KUBELET_ARGS="${KUBELET_ARGS} --volume-plugin-dir=/var/lib/kubelet/volumeplugins"
+ KUBELET_ARGS="${KUBELET_ARGS} --node-labels=magnum.openstack.org/role=${NODEGROUP_ROLE}"
+ KUBELET_ARGS="${KUBELET_ARGS} --node-labels=magnum.openstack.org/nodegroup=${NODEGROUP_NAME}"
diff --git a/images/magnum/patches/0004-adapt-cinder-csi-to-upstream-manifest.patch b/images/magnum/patches/0004-adapt-cinder-csi-to-upstream-manifest.patch
new file mode 100644
index 0000000..7d302cf
--- /dev/null
+++ b/images/magnum/patches/0004-adapt-cinder-csi-to-upstream-manifest.patch
@@ -0,0 +1,860 @@
+From b13335fc56d4938346619229bb2c23c128a1d58a Mon Sep 17 00:00:00 2001
+From: Michal Nasiadka <mnasiadka@gmail.com>
+Date: Fri, 11 Mar 2022 13:33:15 +0100
+Subject: [PATCH] Adapt Cinder CSI to upstream manifest
+
+- Bump also components to upstream manifest versions.
+- Add small tool to sync Cinder CSI manifests automatically
+
+Change-Id: Icd19b41d03b7aa200965a3357a8ddf8b4b40794a
+(cherry picked from commit ac5702c40653942634e259788434037e1e8c980a)
+---
+ doc/source/user/index.rst | 11 +
+ .../kubernetes/fragments/enable-cinder-csi.sh | 237 +++++++++---------
+ .../fragments/write-heat-params-master.sh | 1 +
+ .../drivers/heat/k8s_fedora_template_def.py | 1 +
+ .../templates/kubecluster.yaml | 19 +-
+ .../templates/kubemaster.yaml | 6 +
+ .../unit/drivers/test_template_definition.py | 6 +
+ tools/sync/cinder-csi | 162 ++++++++++++
+ 8 files changed, 322 insertions(+), 121 deletions(-)
+ create mode 100755 tools/sync/cinder-csi
+
+diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst
+index 20c56400f8..9d8d747204 100644
+--- a/doc/source/user/index.rst
++++ b/doc/source/user/index.rst
+@@ -1400,30 +1400,35 @@ _`cinder_csi_plugin_tag`
+ <https://hub.docker.com/r/k8scloudprovider/cinder-csi-plugin/tags>`_.
+ Train default: v1.16.0
+ Ussuri default: v1.18.0
++ Yoga default: v1.23.0
+
+ _`csi_attacher_tag`
+ This label allows users to override the default container tag for CSI attacher.
+ For additional tags, `refer to CSI attacher page
+ <https://quay.io/repository/k8scsi/csi-attacher?tab=tags>`_.
+ Ussuri-default: v2.0.0
++ Yoga-default: v3.3.0
+
+ _`csi_provisioner_tag`
+ This label allows users to override the default container tag for CSI provisioner.
+ For additional tags, `refer to CSI provisioner page
+ <https://quay.io/repository/k8scsi/csi-provisioner?tab=tags>`_.
+ Ussuri-default: v1.4.0
++ Yoga-default: v3.0.0
+
+ _`csi_snapshotter_tag`
+ This label allows users to override the default container tag for CSI snapshotter.
+ For additional tags, `refer to CSI snapshotter page
+ <https://quay.io/repository/k8scsi/csi-snapshotter?tab=tags>`_.
+ Ussuri-default: v1.2.2
++ Yoga-default: v4.2.1
+
+ _`csi_resizer_tag`
+ This label allows users to override the default container tag for CSI resizer.
+ For additional tags, `refer to CSI resizer page
+ <https://quay.io/repository/k8scsi/csi-resizer?tab=tags>`_.
+ Ussuri-default: v0.3.0
++ Yoga-default: v1.3.0
+
+ _`csi_node_driver_registrar_tag`
+ This label allows users to override the default container tag for CSI node
+@@ -1431,6 +1436,12 @@ _`csi_node_driver_registrar_tag`
+ page
+ <https://quay.io/repository/k8scsi/csi-node-driver-registrar?tab=tags>`_.
+ Ussuri-default: v1.1.0
++ Yoga-default: v2.4.0
++
++-`csi_liveness_probe_tag`
++ This label allows users to override the default container tag for CSI
++ liveness probe.
++ Yoga-default: v2.5.0
+
+ _`keystone_auth_enabled`
+ If this label is set to True, Kubernetes will support use Keystone for
+diff --git a/magnum/drivers/common/templates/kubernetes/fragments/enable-cinder-csi.sh b/magnum/drivers/common/templates/kubernetes/fragments/enable-cinder-csi.sh
+index b85258a5f3..524b5e98ed 100644
+--- a/magnum/drivers/common/templates/kubernetes/fragments/enable-cinder-csi.sh
++++ b/magnum/drivers/common/templates/kubernetes/fragments/enable-cinder-csi.sh
+@@ -12,15 +12,15 @@ if [ "${volume_driver}" = "cinder" ] && [ "${cinder_csi_enabled}" = "true" ]; th
+ echo "Writing File: $CINDER_CSI_DEPLOY"
+ mkdir -p $(dirname ${CINDER_CSI_DEPLOY})
+ cat << EOF > ${CINDER_CSI_DEPLOY}
+----
+ # This YAML file contains RBAC API objects,
+ # which are necessary to run csi controller plugin
+----
++
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: csi-cinder-controller-sa
+ namespace: kube-system
++
+ ---
+ # external attacher
+ kind: ClusterRole
+@@ -30,16 +30,20 @@ metadata:
+ rules:
+ - apiGroups: [""]
+ resources: ["persistentvolumes"]
+- verbs: ["get", "list", "watch", "update", "patch"]
+- - apiGroups: [""]
+- resources: ["nodes"]
++ verbs: ["get", "list", "watch", "patch"]
++ - apiGroups: ["storage.k8s.io"]
++ resources: ["csinodes"]
+ verbs: ["get", "list", "watch"]
+ - apiGroups: ["storage.k8s.io"]
+ resources: ["volumeattachments"]
+- verbs: ["get", "list", "watch", "update", "patch"]
++ verbs: ["get", "list", "watch", "patch"]
+ - apiGroups: ["storage.k8s.io"]
+- resources: ["csinodes"]
+- verbs: ["get", "list", "watch"]
++ resources: ["volumeattachments/status"]
++ verbs: ["patch"]
++ - apiGroups: ["coordination.k8s.io"]
++ resources: ["leases"]
++ verbs: ["get", "watch", "list", "delete", "update", "create"]
++
+ ---
+ kind: ClusterRoleBinding
+ apiVersion: rbac.authorization.k8s.io/v1
+@@ -53,6 +57,7 @@ roleRef:
+ kind: ClusterRole
+ name: csi-attacher-role
+ apiGroup: rbac.authorization.k8s.io
++
+ ---
+ # external Provisioner
+ kind: ClusterRole
+@@ -84,6 +89,12 @@ rules:
+ - apiGroups: ["snapshot.storage.k8s.io"]
+ resources: ["volumesnapshotcontents"]
+ verbs: ["get", "list"]
++ - apiGroups: ["storage.k8s.io"]
++ resources: ["volumeattachments"]
++ verbs: ["get", "list", "watch"]
++ - apiGroups: ["coordination.k8s.io"]
++ resources: ["leases"]
++ verbs: ["get", "watch", "list", "delete", "update", "create"]
+ ---
+ kind: ClusterRoleBinding
+ apiVersion: rbac.authorization.k8s.io/v1
+@@ -97,6 +108,7 @@ roleRef:
+ kind: ClusterRole
+ name: csi-provisioner-role
+ apiGroup: rbac.authorization.k8s.io
++
+ ---
+ # external snapshotter
+ kind: ClusterRole
+@@ -104,36 +116,28 @@ apiVersion: rbac.authorization.k8s.io/v1
+ metadata:
+ name: csi-snapshotter-role
+ rules:
+- - apiGroups: [""]
+- resources: ["persistentvolumes"]
+- verbs: ["get", "list", "watch"]
+- - apiGroups: [""]
+- resources: ["persistentvolumeclaims"]
+- verbs: ["get", "list", "watch"]
+- - apiGroups: ["storage.k8s.io"]
+- resources: ["storageclasses"]
+- verbs: ["get", "list", "watch"]
+ - apiGroups: [""]
+ resources: ["events"]
+ verbs: ["list", "watch", "create", "update", "patch"]
+- - apiGroups: [""]
+- resources: ["secrets"]
+- verbs: ["get", "list"]
++ # Secret permission is optional.
++ # Enable it if your driver needs secret.
++ # For example, `csi.storage.k8s.io/snapshotter-secret-name` is set in VolumeSnapshotClass.
++ # See https://kubernetes-csi.github.io/docs/secrets-and-credentials.html for more details.
++ # - apiGroups: [""]
++ # resources: ["secrets"]
++ # verbs: ["get", "list"]
+ - apiGroups: ["snapshot.storage.k8s.io"]
+ resources: ["volumesnapshotclasses"]
+ verbs: ["get", "list", "watch"]
+ - apiGroups: ["snapshot.storage.k8s.io"]
+ resources: ["volumesnapshotcontents"]
+- verbs: ["create", "get", "list", "watch", "update", "delete"]
++ verbs: ["create", "get", "list", "watch", "update", "delete", "patch"]
+ - apiGroups: ["snapshot.storage.k8s.io"]
+- resources: ["volumesnapshots"]
+- verbs: ["get", "list", "watch", "update"]
+- - apiGroups: ["snapshot.storage.k8s.io"]
+- resources: ["volumesnapshots/status"]
+- verbs: ["update"]
+- - apiGroups: ["apiextensions.k8s.io"]
+- resources: ["customresourcedefinitions"]
+- verbs: ["create", "list", "watch", "delete"]
++ resources: ["volumesnapshotcontents/status"]
++ verbs: ["update", "patch"]
++ - apiGroups: ["coordination.k8s.io"]
++ resources: ["leases"]
++ verbs: ["get", "watch", "list", "delete", "update", "create"]
+ ---
+ kind: ClusterRoleBinding
+ apiVersion: rbac.authorization.k8s.io/v1
+@@ -148,6 +152,7 @@ roleRef:
+ name: csi-snapshotter-role
+ apiGroup: rbac.authorization.k8s.io
+ ---
++
+ # External Resizer
+ kind: ClusterRole
+ apiVersion: rbac.authorization.k8s.io/v1
+@@ -161,19 +166,22 @@ rules:
+ # verbs: ["get", "list", "watch"]
+ - apiGroups: [""]
+ resources: ["persistentvolumes"]
+- verbs: ["get", "list", "watch", "update", "patch"]
++ verbs: ["get", "list", "watch", "patch"]
+ - apiGroups: [""]
+ resources: ["persistentvolumeclaims"]
+ verbs: ["get", "list", "watch"]
+ - apiGroups: [""]
+- resources: ["persistentvolumeclaims/status"]
+- verbs: ["update", "patch"]
+- - apiGroups: ["storage.k8s.io"]
+- resources: ["storageclasses"]
++ resources: ["pods"]
+ verbs: ["get", "list", "watch"]
++ - apiGroups: [""]
++ resources: ["persistentvolumeclaims/status"]
++ verbs: ["patch"]
+ - apiGroups: [""]
+ resources: ["events"]
+ verbs: ["list", "watch", "create", "update", "patch"]
++ - apiGroups: ["coordination.k8s.io"]
++ resources: ["leases"]
++ verbs: ["get", "watch", "list", "delete", "update", "create"]
+ ---
+ kind: ClusterRoleBinding
+ apiVersion: rbac.authorization.k8s.io/v1
+@@ -187,56 +195,24 @@ roleRef:
+ kind: ClusterRole
+ name: csi-resizer-role
+ apiGroup: rbac.authorization.k8s.io
+----
+-kind: Role
+-apiVersion: rbac.authorization.k8s.io/v1
+-metadata:
+- namespace: kube-system
+- name: external-resizer-cfg
+-rules:
+-- apiGroups: ["coordination.k8s.io"]
+- resources: ["leases"]
+- verbs: ["get", "watch", "list", "delete", "update", "create"]
+----
+-kind: RoleBinding
+-apiVersion: rbac.authorization.k8s.io/v1
+-metadata:
+- name: csi-resizer-role-cfg
+- namespace: kube-system
+-subjects:
+- - kind: ServiceAccount
+- name: csi-cinder-controller-sa
+- namespace: kube-system
+-roleRef:
+- kind: Role
+- name: external-resizer-cfg
+- apiGroup: rbac.authorization.k8s.io
++
+ ---
+ # This YAML file contains CSI Controller Plugin Sidecars
+ # external-attacher, external-provisioner, external-snapshotter
+----
+-kind: Service
+-apiVersion: v1
+-metadata:
+- name: csi-cinder-controller-service
+- namespace: kube-system
+- labels:
+- app: csi-cinder-controllerplugin
+-spec:
+- selector:
+- app: csi-cinder-controllerplugin
+- ports:
+- - name: dummy
+- port: 12345
+----
+-kind: StatefulSet
++# external-resize, liveness-probe
++
++kind: Deployment
+ apiVersion: apps/v1
+ metadata:
+ name: csi-cinder-controllerplugin
+ namespace: kube-system
+ spec:
+- serviceName: "csi-cinder-controller-service"
+ replicas: 1
++ strategy:
++ type: RollingUpdate
++ rollingUpdate:
++ maxUnavailable: 0
++ maxSurge: 1
+ selector:
+ matchLabels:
+ app: csi-cinder-controllerplugin
+@@ -246,6 +222,7 @@ spec:
+ app: csi-cinder-controllerplugin
+ spec:
+ serviceAccount: csi-cinder-controller-sa
++ hostNetwork: true
+ tolerations:
+ # Make sure the pod can be scheduled on master kubelet.
+ - effect: NoSchedule
+@@ -257,11 +234,11 @@ spec:
+ node-role.kubernetes.io/master: ""
+ containers:
+ - name: csi-attacher
+- image: ${CONTAINER_INFRA_PREFIX:-quay.io/k8scsi/}csi-attacher:${CSI_ATTACHER_TAG}
++ image: ${CONTAINER_INFRA_PREFIX:-k8s.gcr.io/sig-storage/}csi-attacher:${CSI_ATTACHER_TAG}
+ args:
+- - "--v=5"
+ - "--csi-address=\$(ADDRESS)"
+ - "--timeout=3m"
++ - "--leader-election=true"
+ resources:
+ requests:
+ cpu: 20m
+@@ -273,10 +250,14 @@ spec:
+ - name: socket-dir
+ mountPath: /var/lib/csi/sockets/pluginproxy/
+ - name: csi-provisioner
+- image: ${CONTAINER_INFRA_PREFIX:-quay.io/k8scsi/}csi-provisioner:${CSI_PROVISIONER_TAG}
++ image: ${CONTAINER_INFRA_PREFIX:-k8s.gcr.io/sig-storage/}csi-provisioner:${CSI_PROVISIONER_TAG}
+ args:
+ - "--csi-address=\$(ADDRESS)"
+ - "--timeout=3m"
++ - "--default-fstype=ext4"
++ - "--feature-gates=Topology=true"
++ - "--extra-create-metadata"
++ - "--leader-election=true"
+ resources:
+ requests:
+ cpu: 20m
+@@ -288,9 +269,12 @@ spec:
+ - name: socket-dir
+ mountPath: /var/lib/csi/sockets/pluginproxy/
+ - name: csi-snapshotter
+- image: ${CONTAINER_INFRA_PREFIX:-quay.io/k8scsi/}csi-snapshotter:${CSI_SNAPSHOTTER_TAG}
++ image: ${CONTAINER_INFRA_PREFIX:-k8s.gcr.io/sig-storage/}csi-snapshotter:${CSI_SNAPSHOTTER_TAG}
+ args:
+ - "--csi-address=\$(ADDRESS)"
++ - "--timeout=3m"
++ - "--extra-create-metadata"
++ - "--leader-election=true"
+ resources:
+ requests:
+ cpu: 20m
+@@ -302,10 +286,12 @@ spec:
+ - mountPath: /var/lib/csi/sockets/pluginproxy/
+ name: socket-dir
+ - name: csi-resizer
+- image: ${CONTAINER_INFRA_PREFIX:-quay.io/k8scsi/}csi-resizer:${CSI_RESIZER_TAG}
++ image: ${CONTAINER_INFRA_PREFIX:-k8s.gcr.io/sig-storage/}csi-resizer:${CSI_RESIZER_TAG}
+ args:
+- - "--v=5"
+ - "--csi-address=\$(ADDRESS)"
++ - "--timeout=3m"
++ - "--handle-volume-inuse-error=false"
++ - "--leader-election=true"
+ resources:
+ requests:
+ cpu: 20m
+@@ -316,22 +302,27 @@ spec:
+ volumeMounts:
+ - name: socket-dir
+ mountPath: /var/lib/csi/sockets/pluginproxy/
++ - name: liveness-probe
++ image: ${CONTAINER_INFRA_PREFIX:-k8s.gcr.io/sig-storage/}livenessprobe:${CSI_LIVENESS_PROBE_TAG}
++ args:
++ - "--csi-address=\$(ADDRESS)"
++ resources:
++ requests:
++ cpu: 20m
++ env:
++ - name: ADDRESS
++ value: /var/lib/csi/sockets/pluginproxy/csi.sock
++ volumeMounts:
++ - mountPath: /var/lib/csi/sockets/pluginproxy/
++ name: socket-dir
+ - name: cinder-csi-plugin
+ image: ${CONTAINER_INFRA_PREFIX:-docker.io/k8scloudprovider/}cinder-csi-plugin:${CINDER_CSI_PLUGIN_TAG}
+- args :
++ args:
+ - /bin/cinder-csi-plugin
+- - "--nodeid=\$(NODE_ID)"
+ - "--endpoint=\$(CSI_ENDPOINT)"
+ - "--cloud-config=\$(CLOUD_CONFIG)"
+ - "--cluster=\$(CLUSTER_NAME)"
+- resources:
+- requests:
+- cpu: 20m
+ env:
+- - name: NODE_ID
+- valueFrom:
+- fieldRef:
+- fieldPath: spec.nodeName
+ - name: CSI_ENDPOINT
+ value: unix://csi/csi.sock
+ - name: CLOUD_CONFIG
+@@ -339,6 +330,19 @@ spec:
+ - name: CLUSTER_NAME
+ value: kubernetes
+ imagePullPolicy: "IfNotPresent"
++ ports:
++ - containerPort: 9808
++ name: healthz
++ protocol: TCP
++ # The probe
++ livenessProbe:
++ failureThreshold: 5
++ httpGet:
++ path: /healthz
++ port: healthz
++ initialDelaySeconds: 10
++ timeoutSeconds: 10
++ periodSeconds: 60
+ volumeMounts:
+ - name: socket-dir
+ mountPath: /csi
+@@ -360,7 +364,7 @@ spec:
+ type: File
+ ---
+ # This YAML defines all API objects to create RBAC roles for csi node plugin.
+----
++
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+@@ -375,6 +379,7 @@ rules:
+ - apiGroups: [""]
+ resources: ["events"]
+ verbs: ["get", "list", "watch", "create", "update", "patch"]
++
+ ---
+ kind: ClusterRoleBinding
+ apiVersion: rbac.authorization.k8s.io/v1
+@@ -391,7 +396,7 @@ roleRef:
+ ---
+ # This YAML file contains driver-registrar & csi driver nodeplugin API objects,
+ # which are necessary to run csi nodeplugin for cinder.
+----
++
+ kind: DaemonSet
+ apiVersion: apps/v1
+ metadata:
+@@ -412,17 +417,10 @@ spec:
+ hostNetwork: true
+ containers:
+ - name: node-driver-registrar
+- image: ${CONTAINER_INFRA_PREFIX:-quay.io/k8scsi/}csi-node-driver-registrar:${CSI_NODE_DRIVER_REGISTRAR_TAG}
++ image: ${CONTAINER_INFRA_PREFIX:-k8s.gcr.io/sig-storage/}csi-node-driver-registrar:${CSI_NODE_DRIVER_REGISTRAR_TAG}
+ args:
+ - "--csi-address=\$(ADDRESS)"
+ - "--kubelet-registration-path=\$(DRIVER_REG_SOCK_PATH)"
+- resources:
+- requests:
+- cpu: 25m
+- lifecycle:
+- preStop:
+- exec:
+- command: ["/bin/sh", "-c", "rm -rf /registration/cinder.csi.openstack.org /registration/cinder.csi.openstack.org-reg.sock"]
+ env:
+ - name: ADDRESS
+ value: /csi/csi.sock
+@@ -438,6 +436,16 @@ spec:
+ mountPath: /csi
+ - name: registration-dir
+ mountPath: /registration
++ - name: liveness-probe
++ image: ${CONTAINER_INFRA_PREFIX:-k8s.gcr.io/sig-storage/}livenessprobe:${CSI_LIVENESS_PROBE_TAG}
++ args:
++ - --csi-address=/csi/csi.sock
++ resources:
++ requests:
++ cpu: 20m
++ volumeMounts:
++ - name: socket-dir
++ mountPath: /csi
+ - name: cinder-csi-plugin
+ securityContext:
+ privileged: true
+@@ -445,33 +453,35 @@ spec:
+ add: ["SYS_ADMIN"]
+ allowPrivilegeEscalation: true
+ image: ${CONTAINER_INFRA_PREFIX:-docker.io/k8scloudprovider/}cinder-csi-plugin:${CINDER_CSI_PLUGIN_TAG}
+- args :
++ args:
+ - /bin/cinder-csi-plugin
+- - "--nodeid=\$(NODE_ID)"
+ - "--endpoint=\$(CSI_ENDPOINT)"
+ - "--cloud-config=\$(CLOUD_CONFIG)"
+- resources:
+- requests:
+- cpu: 25m
+ env:
+- - name: NODE_ID
+- valueFrom:
+- fieldRef:
+- fieldPath: spec.nodeName
+ - name: CSI_ENDPOINT
+ value: unix://csi/csi.sock
+ - name: CLOUD_CONFIG
+ value: /etc/config/cloud-config
+ imagePullPolicy: "IfNotPresent"
++ ports:
++ - containerPort: 9808
++ name: healthz
++ protocol: TCP
++ # The probe
++ livenessProbe:
++ failureThreshold: 5
++ httpGet:
++ path: /healthz
++ port: healthz
++ initialDelaySeconds: 10
++ timeoutSeconds: 3
++ periodSeconds: 10
+ volumeMounts:
+ - name: socket-dir
+ mountPath: /csi
+ - name: kubelet-dir
+ mountPath: /var/lib/kubelet
+ mountPropagation: "Bidirectional"
+- - name: pods-cloud-data
+- mountPath: /var/lib/cloud/data
+- readOnly: true
+ - name: pods-probe-dir
+ mountPath: /dev
+ mountPropagation: "HostToContainer"
+@@ -494,9 +504,6 @@ spec:
+ hostPath:
+ path: /var/lib/kubelet
+ type: Directory
+- - name: pods-cloud-data
+- hostPath:
+- path: /var/lib/cloud/data
+ - name: pods-probe-dir
+ hostPath:
+ path: /dev
+diff --git a/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.sh b/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.sh
+index a50b184558..0cd02bf95b 100644
+--- a/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.sh
++++ b/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.sh
+@@ -143,6 +143,7 @@ CSI_PROVISIONER_TAG="$CSI_PROVISIONER_TAG"
+ CSI_SNAPSHOTTER_TAG="$CSI_SNAPSHOTTER_TAG"
+ CSI_RESIZER_TAG="$CSI_RESIZER_TAG"
+ CSI_NODE_DRIVER_REGISTRAR_TAG="$CSI_NODE_DRIVER_REGISTRAR_TAG"
++CSI_LIVENESS_PROBE_TAG="$CSI_LIVENESS_PROBE_TAG"
+ DRAINO_TAG="$DRAINO_TAG"
+ MAGNUM_AUTO_HEALER_TAG="$MAGNUM_AUTO_HEALER_TAG"
+ AUTOSCALER_TAG="$AUTOSCALER_TAG"
+diff --git a/magnum/drivers/heat/k8s_fedora_template_def.py b/magnum/drivers/heat/k8s_fedora_template_def.py
+index 659069bc28..a4ec6250ab 100644
+--- a/magnum/drivers/heat/k8s_fedora_template_def.py
++++ b/magnum/drivers/heat/k8s_fedora_template_def.py
+@@ -90,6 +90,7 @@ def get_params(self, context, cluster_template, cluster, **kwargs):
+ 'csi_attacher_tag', 'csi_provisioner_tag',
+ 'csi_snapshotter_tag', 'csi_resizer_tag',
+ 'csi_node_driver_registrar_tag',
++ 'csi_liveness_probe_tag',
+ 'etcd_tag', 'flannel_tag', 'flannel_cni_tag',
+ 'cloud_provider_tag',
+ 'prometheus_tag', 'grafana_tag',
+diff --git a/magnum/drivers/k8s_fedora_coreos_v1/templates/kubecluster.yaml b/magnum/drivers/k8s_fedora_coreos_v1/templates/kubecluster.yaml
+index 35ca781d42..15bfd9af25 100644
+--- a/magnum/drivers/k8s_fedora_coreos_v1/templates/kubecluster.yaml
++++ b/magnum/drivers/k8s_fedora_coreos_v1/templates/kubecluster.yaml
+@@ -866,32 +866,38 @@ parameters:
+ description: tag of cinder csi plugin
+ tag of the k8scloudprovider/cinder-csi-plugin container
+ https://hub.docker.com/r/k8scloudprovider/cinder-csi-plugin/tags/
+- default: v1.18.0
++ default: v1.23.0
+
+ csi_attacher_tag:
+ type: string
+ description: tag of csi attacher
+- default: v2.0.0
++ default: v3.3.0
+
+ csi_provisioner_tag:
+ type: string
+ description: tag of csi provisioner
+- default: v1.4.0
++ default: v3.0.0
+
+ csi_snapshotter_tag:
+ type: string
+ description: tag of csi snapshotter
+- default: v1.2.2
++ default: v4.2.1
+
+ csi_resizer_tag:
+ type: string
+ description: tag of csi resizer
+- default: v0.3.0
++ default: v1.3.0
+
+ csi_node_driver_registrar_tag:
+ type: string
+ description: tag of csi node driver registrar
+- default: v1.1.0
++ default: v2.4.0
++
++ csi_liveness_probe_tag:
++ type: string
++ description: tag of cinder csi liveness probe
++ tag of the k8s.gcr.io/sig-storage/liveness-probe container
++ default: v2.5.0
+
+ node_problem_detector_tag:
+ type: string
+@@ -1384,6 +1390,7 @@ resources:
+ csi_snapshotter_tag: {get_param: csi_snapshotter_tag}
+ csi_resizer_tag: {get_param: csi_resizer_tag}
+ csi_node_driver_registrar_tag: {get_param: csi_node_driver_registrar_tag}
++ csi_liveness_probe_tag: {get_param: csi_liveness_probe_tag}
+ draino_tag: {get_param: draino_tag}
+ autoscaler_tag: {get_param: autoscaler_tag}
+ min_node_count: {get_param: min_node_count}
+diff --git a/magnum/drivers/k8s_fedora_coreos_v1/templates/kubemaster.yaml b/magnum/drivers/k8s_fedora_coreos_v1/templates/kubemaster.yaml
+index a038f144d0..917f010db8 100644
+--- a/magnum/drivers/k8s_fedora_coreos_v1/templates/kubemaster.yaml
++++ b/magnum/drivers/k8s_fedora_coreos_v1/templates/kubemaster.yaml
+@@ -621,6 +621,11 @@ parameters:
+ type: string
+ description: tag of csi node driver registrar
+
++ csi_liveness_probe_tag:
++ type: string
++ description: >
++ Tag of liveness-probe for cinder csi.
++
+ node_problem_detector_tag:
+ type: string
+ description: tag of the node problem detector container
+@@ -910,6 +915,7 @@ resources:
+ "$CSI_SNAPSHOTTER_TAG": {get_param: csi_snapshotter_tag}
+ "$CSI_RESIZER_TAG": {get_param: csi_resizer_tag}
+ "$CSI_NODE_DRIVER_REGISTRAR_TAG": {get_param: csi_node_driver_registrar_tag}
++ "$CSI_LIVENESS_PROBE_TAG": {get_param: csi_liveness_probe_tag}
+ "$DRAINO_TAG": {get_param: draino_tag}
+ "$AUTOSCALER_TAG": {get_param: autoscaler_tag}
+ "$MIN_NODE_COUNT": {get_param: min_node_count}
+diff --git a/magnum/tests/unit/drivers/test_template_definition.py b/magnum/tests/unit/drivers/test_template_definition.py
+index b523744597..7b08196bf1 100644
+--- a/magnum/tests/unit/drivers/test_template_definition.py
++++ b/magnum/tests/unit/drivers/test_template_definition.py
+@@ -600,6 +600,8 @@ def test_k8s_get_params(self, mock_generate_csr_and_key,
+ 'csi_resizer_tag')
+ csi_node_driver_registrar_tag = mock_cluster.labels.get(
+ 'csi_node_driver_registrar_tag')
++ csi_liveness_probe_tag = mock_cluster.labels.get(
++ 'csi_liveness_probe_tag')
+ draino_tag = mock_cluster.labels.get('draino_tag')
+ autoscaler_tag = mock_cluster.labels.get('autoscaler_tag')
+ min_node_count = mock_cluster.labels.get('min_node_count')
+@@ -725,6 +727,7 @@ def test_k8s_get_params(self, mock_generate_csr_and_key,
+ 'csi_snapshotter_tag': csi_snapshotter_tag,
+ 'csi_resizer_tag': csi_resizer_tag,
+ 'csi_node_driver_registrar_tag': csi_node_driver_registrar_tag,
++ 'csi_liveness_probe_tag': csi_liveness_probe_tag,
+ 'draino_tag': draino_tag,
+ 'autoscaler_tag': autoscaler_tag,
+ 'min_node_count': min_node_count,
+@@ -1161,6 +1164,8 @@ def test_k8s_get_params_insecure(self, mock_generate_csr_and_key,
+ 'csi_resizer_tag')
+ csi_node_driver_registrar_tag = mock_cluster.labels.get(
+ 'csi_node_driver_registrar_tag')
++ csi_liveness_probe_tag = mock_cluster.labels.get(
++ 'csi_liveness_probe_tag')
+ draino_tag = mock_cluster.labels.get('draino_tag')
+ autoscaler_tag = mock_cluster.labels.get('autoscaler_tag')
+ min_node_count = mock_cluster.labels.get('min_node_count')
+@@ -1290,6 +1295,7 @@ def test_k8s_get_params_insecure(self, mock_generate_csr_and_key,
+ 'csi_snapshotter_tag': csi_snapshotter_tag,
+ 'csi_resizer_tag': csi_resizer_tag,
+ 'csi_node_driver_registrar_tag': csi_node_driver_registrar_tag,
++ 'csi_liveness_probe_tag': csi_liveness_probe_tag,
+ 'draino_tag': draino_tag,
+ 'autoscaler_tag': autoscaler_tag,
+ 'min_node_count': min_node_count,
+diff --git a/tools/sync/cinder-csi b/tools/sync/cinder-csi
+new file mode 100755
+index 0000000000..5789631d52
+--- /dev/null
++++ b/tools/sync/cinder-csi
+@@ -0,0 +1,162 @@
++#!/usr/bin/env python3.9
++
++import requests
++
++manifest_data = []
++
++files = requests.get("https://api.github.com/repos/kubernetes/cloud-provider-openstack/contents/manifests/cinder-csi-plugin").json()
++for file in files:
++ if file['name'] == 'csi-secret-cinderplugin.yaml':
++ continue
++
++ r = requests.get(file['download_url'])
++ manifest_data.append(r.text)
++
++manifests = "---\n".join(manifest_data)
++
++# Clean-ups
++manifests = manifests.replace(
++"""
++ # - name: cacert
++ # mountPath: /etc/cacert
++ # readOnly: true
++""",
++"""
++ - name: cacert
++ mountPath: /etc/kubernetes/ca-bundle.crt
++ readOnly: true
++""").replace(
++"""
++ secretName: cloud-config
++ # - name: cacert
++ # hostPath:
++ # path: /etc/cacert
++""",
++"""
++ secretName: cinder-csi-cloud-config
++ - name: cacert
++ hostPath:
++ path: /etc/kubernetes/ca-bundle.crt
++ type: File
++""").replace(
++"""
++ serviceAccount: csi-cinder-controller-sa
++""",
++"""
++ serviceAccount: csi-cinder-controller-sa
++ hostNetwork: true
++ tolerations:
++ # Make sure the pod can be scheduled on master kubelet.
++ - effect: NoSchedule
++ operator: Exists
++ # Mark the pod as a critical add-on for rescheduling.
++ - key: CriticalAddonsOnly
++ operator: Exists
++ nodeSelector:
++ node-role.kubernetes.io/master: ""
++""").replace(
++"""
++ - --csi-address=/csi/csi.sock
++""",
++"""
++ - --csi-address=/csi/csi.sock
++ resources:
++ requests:
++ cpu: 20m
++""").replace(
++"""
++ env:
++ - name: ADDRESS
++ value: /var/lib/csi/sockets/pluginproxy/csi.sock
++""",
++"""
++ resources:
++ requests:
++ cpu: 20m
++ env:
++ - name: ADDRESS
++ value: /var/lib/csi/sockets/pluginproxy/csi.sock
++""").replace(
++ "$(",
++ "\$("
++).replace(
++ "k8s.gcr.io/sig-storage/",
++ "${CONTAINER_INFRA_PREFIX:-k8s.gcr.io/sig-storage/}"
++).replace(
++ "docker.io/k8scloudprovider/",
++ "${CONTAINER_INFRA_PREFIX:-docker.io/k8scloudprovider/}",
++).replace(
++ "csi-attacher:v3.4.0",
++ "csi-attacher:${CSI_ATTACHER_TAG}",
++).replace(
++ "csi-provisioner:v3.1.0",
++ "csi-provisioner:${CSI_PROVISIONER_TAG}",
++).replace(
++ "csi-snapshotter:v6.0.1",
++ "csi-snapshotter:${CSI_SNAPSHOTTER_TAG}",
++).replace(
++ "csi-resizer:v1.4.0",
++ "csi-resizer:${CSI_RESIZER_TAG}",
++).replace(
++ "livenessprobe:v2.7.0",
++ "livenessprobe:${CSI_LIVENESS_PROBE_TAG}",
++).replace(
++ "cinder-csi-plugin:latest",
++ "cinder-csi-plugin:${CINDER_CSI_PLUGIN_TAG}",
++).replace(
++ "csi-node-driver-registrar:v2.5.1",
++ "csi-node-driver-registrar:${CSI_NODE_DRIVER_REGISTRAR_TAG}",
++).replace(
++ "/etc/config/cloud.conf",
++ "/etc/config/cloud-config"
++)
++
++template = f"""step="enable-cinder-csi"
++printf "Starting to run ${{step}}\\n"
++
++. /etc/sysconfig/heat-params
++
++volume_driver=$(echo "${{VOLUME_DRIVER}}" | tr '[:upper:]' '[:lower:]')
++cinder_csi_enabled=$(echo $CINDER_CSI_ENABLED | tr '[:upper:]' '[:lower:]')
++
++if [ "${{volume_driver}}" = "cinder" ] && [ "${{cinder_csi_enabled}}" = "true" ]; then
++ # Generate Cinder CSI manifest file
++ CINDER_CSI_DEPLOY=/srv/magnum/kubernetes/manifests/cinder-csi.yaml
++ echo "Writing File: $CINDER_CSI_DEPLOY"
++ mkdir -p $(dirname ${{CINDER_CSI_DEPLOY}})
++ cat << EOF > ${{CINDER_CSI_DEPLOY}}
++{manifests.strip()}
++EOF
++
++ echo "Waiting for Kubernetes API..."
++ until [ "ok" = "$(kubectl get --raw='/healthz')" ]
++ do
++ sleep 5
++ done
++
++ cat <<EOF | kubectl apply -f -
++---
++apiVersion: v1
++kind: Secret
++metadata:
++ name: cinder-csi-cloud-config
++ namespace: kube-system
++type: Opaque
++stringData:
++ cloud-config: |-
++ [Global]
++ auth-url=$AUTH_URL
++ user-id=$TRUSTEE_USER_ID
++ password=$TRUSTEE_PASSWORD
++ trust-id=$TRUST_ID
++ region=$REGION_NAME
++ ca-file=/etc/kubernetes/ca-bundle.crt
++EOF
++
++ kubectl apply -f ${{CINDER_CSI_DEPLOY}}
++fi
++printf "Finished running ${{step}}\\n"
++"""
++
++with open("magnum/drivers/common/templates/kubernetes/fragments/enable-cinder-csi.sh", "w") as fd:
++ fd.write(template)
diff --git a/images/magnum/patches/0005-secure-rbac.patch b/images/magnum/patches/0005-secure-rbac.patch
new file mode 100644
index 0000000..4f4ea6e
--- /dev/null
+++ b/images/magnum/patches/0005-secure-rbac.patch
@@ -0,0 +1,1969 @@
+From 7ffb23c87d04ea2c7f5b07a0af98573cb69379e0 Mon Sep 17 00:00:00 2001
+From: Rico Lin <ricolin@ricolky.com>
+Date: Tue, 11 Jul 2023 05:40:01 -0700
+Subject: [PATCH] Secure Rbac (#10)
+
+* Support enables rbac policies new defaults
+
+The Magnum service allow enables policies (RBAC) new defaults and scope by
+default. The Default value of config options ``[oslo_policy] enforce_scope``
+and ``[oslo_policy] oslo_policy.enforce_new_defaults`` are both to
+``False``, but will change to ``True`` in following cycles.
+
+To enable them then modify the below config options value in
+``magnum.conf`` file::
+
+ [oslo_policy]
+ enforce_new_defaults=True
+ enforce_scope=True
+
+reference tc goal for more detail:
+https://governance.openstack.org/tc/goals/selected/consistent-and-secure-rbac.html
+
+Related blueprint secure-rbac
+
+Change-Id: I249942a355577c4f1ef51b3988f0cc4979959d0b
+
+* Allow Admin to perform all API requests
+
+This propose changes is base on same concerns as this bug in neutron
+https://bugs.launchpad.net/neutron/+bug/1997089
+
+This propose to keep and make sure ADMIN can perform all API requests.
+
+Change-Id: I9a3003963bf13a591cc363fa04ec8e5719ae9114
+
+* Add policies unit tests (Part one)
+
+Add plicies unit test base function
+and tests for federation, quotas and stats.
+
+Change-Id: I0eb12bf77e0e786652e674c787b2821415bd4506
+
+* Add policies unit tests (Part two)
+
+Add plicies unit test base function
+and tests for certificate, and magnum service.
+
+Change-Id: Ib4047cb5a84647ff2848f06de71181673cc0627a
+
+* Add policies unit tests (Part three)
+
+Add plicies unit test base function
+and tests for cluster, cluster template, and nodegroup.
+
+Change-Id: I0555e557725b02f3ec9812f0adf84d283f7389b0
+---
+ magnum/api/hooks.py | 8 +-
+ magnum/common/context.py | 12 +-
+ magnum/common/policies/base.py | 169 +++++++++++++++++-
+ magnum/common/policies/certificate.py | 11 +-
+ magnum/common/policies/cluster.py | 27 ++-
+ magnum/common/policies/cluster_template.py | 20 ++-
+ magnum/common/policies/federation.py | 18 +-
+ magnum/common/policies/nodegroup.py | 15 +-
+ magnum/common/policies/quota.py | 3 +-
+ magnum/common/policies/stats.py | 3 +-
+ magnum/common/policy.py | 12 +-
+ magnum/tests/fakes.py | 2 +-
+ magnum/tests/unit/api/base.py | 16 ++
+ .../tests/unit/api/controllers/test_root.py | 4 +-
+ .../api/controllers/v1/test_certificate.py | 23 ++-
+ .../unit/api/controllers/v1/test_cluster.py | 34 ++--
+ .../controllers/v1/test_cluster_actions.py | 48 +++--
+ .../unit/api/controllers/v1/test_nodegroup.py | 12 +-
+ .../unit/api/controllers/v1/test_quota.py | 2 +-
+ .../unit/api/controllers/v1/test_stats.py | 15 +-
+ magnum/tests/unit/api/test_hooks.py | 10 +-
+ magnum/tests/unit/common/policies/__init__.py | 0
+ magnum/tests/unit/common/policies/base.py | 37 ++++
+ .../policies/test_certificate_policy.py | 72 ++++++++
+ .../common/policies/test_cluster_policy.py | 65 +++++++
+ .../policies/test_cluster_template_policy.py | 74 ++++++++
+ .../common/policies/test_federation_policy.py | 67 +++++++
+ .../policies/test_magnum_service_policy.py | 26 +++
+ .../common/policies/test_nodegroup_policy.py | 74 ++++++++
+ .../unit/common/policies/test_quota_policy.py | 74 ++++++++
+ .../unit/common/policies/test_stats_policy.py | 33 ++++
+ magnum/tests/unit/common/test_context.py | 43 ++---
+ ...dmin_perform_acitons-cc988655bb72b3f3.yaml | 9 +
+ ...ope-and-new-defaults-7e6e503f74283071.yaml | 13 ++
+ 36 files changed, 943 insertions(+), 124 deletions(-)
+ create mode 100644 magnum/tests/unit/common/policies/__init__.py
+ create mode 100644 magnum/tests/unit/common/policies/base.py
+ create mode 100644 magnum/tests/unit/common/policies/test_certificate_policy.py
+ create mode 100644 magnum/tests/unit/common/policies/test_cluster_policy.py
+ create mode 100644 magnum/tests/unit/common/policies/test_cluster_template_policy.py
+ create mode 100644 magnum/tests/unit/common/policies/test_federation_policy.py
+ create mode 100644 magnum/tests/unit/common/policies/test_magnum_service_policy.py
+ create mode 100644 magnum/tests/unit/common/policies/test_nodegroup_policy.py
+ create mode 100644 magnum/tests/unit/common/policies/test_quota_policy.py
+ create mode 100644 magnum/tests/unit/common/policies/test_stats_policy.py
+ create mode 100644 releasenotes/notes/allow_admin_perform_acitons-cc988655bb72b3f3.yaml
+ create mode 100644 releasenotes/notes/enable-enforce-scope-and-new-defaults-7e6e503f74283071.yaml
+
+diff --git a/magnum/api/hooks.py b/magnum/api/hooks.py
+index e0d36a9a88..f5a9049795 100644
+--- a/magnum/api/hooks.py
++++ b/magnum/api/hooks.py
+@@ -52,8 +52,8 @@ def before(self, state):
+ user_id = headers.get('X-User-Id')
+ project = headers.get('X-Project-Name')
+ project_id = headers.get('X-Project-Id')
+- domain_id = headers.get('X-User-Domain-Id')
+- domain_name = headers.get('X-User-Domain-Name')
++ user_domain_id = headers.get('X-User-Domain-Id')
++ user_domain_name = headers.get('X-User-Domain-Name')
+ auth_token = headers.get('X-Auth-Token')
+ roles = headers.get('X-Roles', '').split(',')
+ auth_token_info = state.request.environ.get('keystone.token_info')
+@@ -72,8 +72,8 @@ def before(self, state):
+ user_id=user_id,
+ project_name=project,
+ project_id=project_id,
+- domain_id=domain_id,
+- domain_name=domain_name,
++ user_domain_id=user_domain_id,
++ user_domain_name=user_domain_name,
+ roles=roles)
+
+
+diff --git a/magnum/common/context.py b/magnum/common/context.py
+index 547c9cc9b4..c2c3be1e23 100644
+--- a/magnum/common/context.py
++++ b/magnum/common/context.py
+@@ -42,7 +42,7 @@ def __init__(self, auth_token=None, auth_url=None, domain_id=None,
+ """
+ super(RequestContext, self).__init__(auth_token=auth_token,
+ user_id=user_name,
+- project_id=project_name,
++ project_id=project_id,
+ is_admin=is_admin,
+ read_only=read_only,
+ show_deleted=show_deleted,
+@@ -53,8 +53,12 @@ def __init__(self, auth_token=None, auth_url=None, domain_id=None,
+ self.user_id = user_id
+ self.project_name = project_name
+ self.project_id = project_id
+- self.domain_id = domain_id
+- self.domain_name = domain_name
++ # (ricolin) Rmove domain_id because oslo_policy use this args to
++ # judge if this request is a domain scope or not. We might be consider
++ # bring this back only if that judge in oslo_policy is no longer affect
++ # project scope enforce.
++ # self.domain_id = domain_id
++ # self.domain_name = domain_name
+ self.user_domain_id = user_domain_id
+ self.user_domain_name = user_domain_name
+ self.auth_url = auth_url
+@@ -71,8 +75,6 @@ def to_dict(self):
+ value = super(RequestContext, self).to_dict()
+ value.update({'auth_token': self.auth_token,
+ 'auth_url': self.auth_url,
+- 'domain_id': self.domain_id,
+- 'domain_name': self.domain_name,
+ 'user_domain_id': self.user_domain_id,
+ 'user_domain_name': self.user_domain_name,
+ 'user_name': self.user_name,
+diff --git a/magnum/common/policies/base.py b/magnum/common/policies/base.py
+index 44c75b7daf..05ac11728b 100644
+--- a/magnum/common/policies/base.py
++++ b/magnum/common/policies/base.py
+@@ -13,12 +13,79 @@
+ # under the License.
+ from oslo_policy import policy
+
+-ROLE_ADMIN = 'rule:context_is_admin'
++
+ RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
+-RULE_ADMIN_API = 'rule:admin_api'
++RULE_ADMIN_API = 'rule:context_is_admin'
+ RULE_ADMIN_OR_USER = 'rule:admin_or_user'
+ RULE_CLUSTER_USER = 'rule:cluster_user'
+ RULE_DENY_CLUSTER_USER = 'rule:deny_cluster_user'
++RULE_USER = "rule:is_user"
++# Generic check string for checking if a user is authorized on a particular
++# project, specifically with the member role.
++RULE_PROJECT_MEMBER = 'rule:project_member'
++# Generic check string for checking if a user is authorized on a particular
++# project but with read-only access. For example, this persona would be able to
++# list private images owned by a project but cannot make any writeable changes
++# to those images.
++RULE_PROJECT_READER = 'rule:project_reader'
++
++RULE_USER_OR_CLUSTER_USER = (
++ 'rule:user_or_cluster_user')
++RULE_ADMIN_OR_PROJECT_READER = (
++ 'rule:admin_or_project_reader')
++RULE_ADMIN_OR_PROJECT_MEMBER = (
++ 'rule:admin_or_project_member')
++RULE_ADMIN_OR_PROJECT_MEMBER_USER = (
++ 'rule:admin_or_project_member_user')
++RULE_ADMIN_OR_PROJECT_MEMBER_USER_OR_CLUSTER_USER = (
++ 'rule:admin_or_project_member_user_or_cluster_user')
++RULE_PROJECT_MEMBER_DENY_CLUSTER_USER = (
++ 'rule:project_member_deny_cluster_user')
++RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER = (
++ 'rule:admin_or_project_member_deny_cluster_user')
++RULE_PROJECT_READER_DENY_CLUSTER_USER = (
++ 'rule:project_reader_deny_cluster_user')
++RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER = (
++ 'rule:admin_or_project_reader_deny_cluster_user')
++RULE_ADMIN_OR_PROJECT_READER_USER_OR_CLUSTER_USER = (
++ 'rule:admin_or_project_reader_user_or_cluster_user')
++
++# ==========================================================
++# Deprecated Since OpenStack 2023.2(Magnum 17.0.0) and should be removed in
++# The following cycle.
++
++DEPRECATED_REASON = """
++The Magnum API now enforces scoped tokens and default reader and member roles.
++"""
++
++DEPRECATED_SINCE = 'OpenStack 2023.2(Magnum 17.0.0)'
++
++
++DEPRECATED_DENY_CLUSTER_USER = policy.DeprecatedRule(
++ name=RULE_DENY_CLUSTER_USER,
++ check_str='not domain_id:%(trustee_domain_id)s',
++ deprecated_reason=DEPRECATED_REASON,
++ deprecated_since=DEPRECATED_SINCE
++)
++
++DEPRECATED_RULE_ADMIN_OR_OWNER = policy.DeprecatedRule(
++ name=RULE_ADMIN_OR_OWNER,
++ check_str='is_admin:True or project_id:%(project_id)s',
++ deprecated_reason=DEPRECATED_REASON,
++ deprecated_since=DEPRECATED_SINCE
++)
++
++# Only used for DEPRECATED_RULE_ADMIN_OR_USER_OR_CLUSTER_USER
++RULE_ADMIN_OR_USER_OR_CLUSTER_USER = (
++ 'rule:admin_or_user_or_cluster_user')
++
++DEPRECATED_RULE_ADMIN_OR_USER_OR_CLUSTER_USER = policy.DeprecatedRule(
++ name=RULE_ADMIN_OR_USER_OR_CLUSTER_USER,
++ check_str=f"(({RULE_ADMIN_API}) or ({RULE_USER_OR_CLUSTER_USER}))",
++ deprecated_reason=DEPRECATED_REASON,
++ deprecated_since=DEPRECATED_SINCE
++)
++# ==========================================================
+
+ rules = [
+ policy.RuleDefault(
+@@ -29,14 +96,14 @@
+ name='admin_or_owner',
+ check_str='is_admin:True or project_id:%(project_id)s'
+ ),
+- policy.RuleDefault(
+- name='admin_api',
+- check_str='rule:context_is_admin'
+- ),
+ policy.RuleDefault(
+ name='admin_or_user',
+ check_str='is_admin:True or user_id:%(user_id)s'
+ ),
++ policy.RuleDefault(
++ name='is_user',
++ check_str='user_id:%(user_id)s'
++ ),
+ policy.RuleDefault(
+ name='cluster_user',
+ check_str='user_id:%(trustee_user_id)s'
+@@ -44,7 +111,95 @@
+ policy.RuleDefault(
+ name='deny_cluster_user',
+ check_str='not domain_id:%(trustee_domain_id)s'
+- )
++ ),
++ policy.RuleDefault(
++ name='project_member',
++ check_str='role:member and project_id:%(project_id)s'
++ ),
++ policy.RuleDefault(
++ name='project_reader',
++ check_str='role:reader and project_id:%(project_id)s'
++ ),
++ policy.RuleDefault(
++ name='admin_or_project_reader',
++ check_str=f"({RULE_ADMIN_API}) or ({RULE_PROJECT_READER})",
++ deprecated_rule=DEPRECATED_RULE_ADMIN_OR_OWNER
++ ),
++ policy.RuleDefault(
++ name='admin_or_project_member',
++ check_str=f"({RULE_ADMIN_API}) or ({RULE_PROJECT_MEMBER})",
++ deprecated_rule=DEPRECATED_RULE_ADMIN_OR_OWNER
++ ),
++ policy.RuleDefault(
++ name='admin_or_project_member_user',
++ check_str=(
++ f"({RULE_ADMIN_API}) or (({RULE_PROJECT_MEMBER}) and "
++ f"({RULE_USER}))"
++ )
++ ),
++ policy.RuleDefault(
++ name='user_or_cluster_user',
++ check_str=(
++ f"(({RULE_USER}) or ({RULE_CLUSTER_USER}))"
++ )
++ ),
++ policy.RuleDefault(
++ name='admin_or_user_or_cluster_user',
++ check_str=(
++ f"(({RULE_ADMIN_API}) or ({RULE_USER_OR_CLUSTER_USER}))"
++ )
++ ),
++ policy.RuleDefault(
++ name='admin_or_project_member_cluster_user',
++ check_str=(
++ f"({RULE_ADMIN_API}) or (({RULE_PROJECT_MEMBER}) "
++ f"and ({RULE_CLUSTER_USER}))"
++ )
++ ),
++ policy.RuleDefault(
++ name='admin_or_project_member_user_or_cluster_user',
++ check_str=(
++ f"({RULE_ADMIN_API}) or (({RULE_PROJECT_MEMBER}) and "
++ f"({RULE_USER_OR_CLUSTER_USER}))"
++ ),
++ deprecated_rule=DEPRECATED_RULE_ADMIN_OR_USER_OR_CLUSTER_USER
++ ),
++ policy.RuleDefault(
++ name='project_member_deny_cluster_user',
++ check_str=(
++ f"(({RULE_PROJECT_MEMBER}) and ({RULE_DENY_CLUSTER_USER}))"
++ ),
++ deprecated_rule=DEPRECATED_DENY_CLUSTER_USER
++ ),
++ policy.RuleDefault(
++ name='admin_or_project_member_deny_cluster_user',
++ check_str=(
++ f"({RULE_ADMIN_API}) or ({RULE_PROJECT_MEMBER_DENY_CLUSTER_USER})"
++ ),
++ deprecated_rule=DEPRECATED_DENY_CLUSTER_USER
++ ),
++ policy.RuleDefault(
++ name='project_reader_deny_cluster_user',
++ check_str=(
++ f"(({RULE_PROJECT_READER}) and ({RULE_DENY_CLUSTER_USER}))"
++ ),
++ deprecated_rule=DEPRECATED_DENY_CLUSTER_USER
++ ),
++ policy.RuleDefault(
++ name='admin_or_project_reader_deny_cluster_user',
++ check_str=(
++ f"({RULE_ADMIN_API}) or ({RULE_PROJECT_READER_DENY_CLUSTER_USER})"
++ ),
++ deprecated_rule=DEPRECATED_DENY_CLUSTER_USER
++ ),
++ policy.RuleDefault(
++ name='admin_or_project_reader_user_or_cluster_user',
++ check_str=(
++ f"({RULE_ADMIN_API}) or (({RULE_PROJECT_READER}) and "
++ f"({RULE_USER_OR_CLUSTER_USER}))"
++ ),
++ deprecated_rule=DEPRECATED_RULE_ADMIN_OR_USER_OR_CLUSTER_USER
++ ),
+ ]
+
+
+diff --git a/magnum/common/policies/certificate.py b/magnum/common/policies/certificate.py
+index 5e96b64f5b..32a7047a4b 100644
+--- a/magnum/common/policies/certificate.py
++++ b/magnum/common/policies/certificate.py
+@@ -16,13 +16,12 @@
+ from magnum.common.policies import base
+
+ CERTIFICATE = 'certificate:%s'
+-RULE_ADMIN_OR_USER_OR_CLUSTER_USER = base.RULE_ADMIN_OR_USER + " or " + \
+- base.RULE_CLUSTER_USER
+
+ rules = [
+ policy.DocumentedRuleDefault(
+ name=CERTIFICATE % 'create',
+- check_str=RULE_ADMIN_OR_USER_OR_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_USER_OR_CLUSTER_USER,
++ scope_types=["project"],
+ description='Sign a new certificate by the CA.',
+ operations=[
+ {
+@@ -33,7 +32,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CERTIFICATE % 'get',
+- check_str=RULE_ADMIN_OR_USER_OR_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_USER_OR_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve CA information about the given bay/cluster.',
+ operations=[
+ {
+@@ -44,7 +44,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CERTIFICATE % 'rotate_ca',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER,
++ scope_types=["project"],
+ description='Rotate the CA certificate on the given bay/cluster.',
+ operations=[
+ {
+diff --git a/magnum/common/policies/cluster.py b/magnum/common/policies/cluster.py
+index 15b63226b2..5e1864c377 100644
+--- a/magnum/common/policies/cluster.py
++++ b/magnum/common/policies/cluster.py
+@@ -20,7 +20,8 @@
+ rules = [
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'create',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Create a new cluster.',
+ operations=[
+ {
+@@ -31,7 +32,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'delete',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Delete a cluster.',
+ operations=[
+ {
+@@ -53,7 +55,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'detail',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve a list of clusters with detail.',
+ operations=[
+ {
+@@ -75,7 +78,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'get',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve information about the given cluster.',
+ operations=[
+ {
+@@ -98,7 +102,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'get_all',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve a list of clusters.',
+ operations=[
+ {
+@@ -120,7 +125,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'update',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Update an existing cluster.',
+ operations=[
+ {
+@@ -131,7 +137,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'update_health_status',
+- check_str=base.RULE_ADMIN_OR_USER + " or " + base.RULE_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_USER_OR_CLUSTER_USER,
++ scope_types=["project"],
+ description='Update the health status of an existing cluster.',
+ operations=[
+ {
+@@ -153,7 +160,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'resize',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Resize an existing cluster.',
+ operations=[
+ {
+@@ -164,7 +172,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER % 'upgrade',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Upgrade an existing cluster.',
+ operations=[
+ {
+diff --git a/magnum/common/policies/cluster_template.py b/magnum/common/policies/cluster_template.py
+index d9b51737ad..c0d8337051 100644
+--- a/magnum/common/policies/cluster_template.py
++++ b/magnum/common/policies/cluster_template.py
+@@ -20,18 +20,20 @@
+ rules = [
+ policy.DocumentedRuleDefault(
+ name=CLUSTER_TEMPLATE % 'create',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Create a new cluster template.',
+ operations=[
+ {
+ 'path': '/v1/clustertemplates',
+ 'method': 'POST'
+ }
+- ]
++ ],
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER_TEMPLATE % 'delete',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER,
++ scope_types=["project"],
+ description='Delete a cluster template.',
+ operations=[
+ {
+@@ -65,7 +67,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER_TEMPLATE % 'detail',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve a list of cluster templates with detail.',
+ operations=[
+ {
+@@ -76,7 +79,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER_TEMPLATE % 'get',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve information about the given cluster template.',
+ operations=[
+ {
+@@ -99,7 +103,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER_TEMPLATE % 'get_all',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve a list of cluster templates.',
+ operations=[
+ {
+@@ -121,7 +126,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=CLUSTER_TEMPLATE % 'update',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER,
++ scope_types=["project"],
+ description='Update an existing cluster template.',
+ operations=[
+ {
+diff --git a/magnum/common/policies/federation.py b/magnum/common/policies/federation.py
+index b78b1a1b1e..4c347993c3 100644
+--- a/magnum/common/policies/federation.py
++++ b/magnum/common/policies/federation.py
+@@ -20,7 +20,8 @@
+ rules = [
+ policy.DocumentedRuleDefault(
+ name=FEDERATION % 'create',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Create a new federation.',
+ operations=[
+ {
+@@ -31,7 +32,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=FEDERATION % 'delete',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Delete a federation.',
+ operations=[
+ {
+@@ -42,7 +44,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=FEDERATION % 'detail',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve a list of federations with detail.',
+ operations=[
+ {
+@@ -53,7 +56,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=FEDERATION % 'get',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve information about the given federation.',
+ operations=[
+ {
+@@ -64,7 +68,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=FEDERATION % 'get_all',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Retrieve a list of federations.',
+ operations=[
+ {
+@@ -75,7 +80,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=FEDERATION % 'update',
+- check_str=base.RULE_DENY_CLUSTER_USER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER_DENY_CLUSTER_USER,
++ scope_types=["project"],
+ description='Update an existing federation.',
+ operations=[
+ {
+diff --git a/magnum/common/policies/nodegroup.py b/magnum/common/policies/nodegroup.py
+index 64b2d670ea..25bad88579 100644
+--- a/magnum/common/policies/nodegroup.py
++++ b/magnum/common/policies/nodegroup.py
+@@ -24,7 +24,8 @@
+ rules = [
+ policy.DocumentedRuleDefault(
+ name=NODEGROUP % 'get',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER,
++ scope_types=["project"],
+ description='Retrieve information about the given nodegroup.',
+ operations=[
+ {
+@@ -35,7 +36,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=NODEGROUP % 'get_all',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER,
++ scope_types=["project"],
+ description='Retrieve a list of nodegroups that belong to a cluster.',
+ operations=[
+ {
+@@ -68,7 +70,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=NODEGROUP % 'create',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER,
++ scope_types=["project"],
+ description='Create a new nodegroup.',
+ operations=[
+ {
+@@ -79,7 +82,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=NODEGROUP % 'delete',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER,
++ scope_types=["project"],
+ description='Delete a nodegroup.',
+ operations=[
+ {
+@@ -90,7 +94,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=NODEGROUP % 'update',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_MEMBER,
++ scope_types=["project"],
+ description='Update an existing nodegroup.',
+ operations=[
+ {
+diff --git a/magnum/common/policies/quota.py b/magnum/common/policies/quota.py
+index 4baecf7d84..574857b1a4 100644
+--- a/magnum/common/policies/quota.py
++++ b/magnum/common/policies/quota.py
+@@ -42,7 +42,8 @@
+ ),
+ policy.DocumentedRuleDefault(
+ name=QUOTA % 'get',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER,
++ scope_types=["project"],
+ description='Retrieve Quota information for the given project_id.',
+ operations=[
+ {
+diff --git a/magnum/common/policies/stats.py b/magnum/common/policies/stats.py
+index c37164094b..64996443b7 100644
+--- a/magnum/common/policies/stats.py
++++ b/magnum/common/policies/stats.py
+@@ -20,7 +20,8 @@
+ rules = [
+ policy.DocumentedRuleDefault(
+ name=STATS % 'get_all',
+- check_str=base.RULE_ADMIN_OR_OWNER,
++ check_str=base.RULE_ADMIN_OR_PROJECT_READER,
++ scope_types=["project"],
+ description='Retrieve magnum stats.',
+ operations=[
+ {
+diff --git a/magnum/common/policy.py b/magnum/common/policy.py
+index d4bfff77b5..989676efb1 100644
+--- a/magnum/common/policy.py
++++ b/magnum/common/policy.py
+@@ -17,6 +17,7 @@
+
+ import decorator
+ from oslo_config import cfg
++from oslo_log import log as logging
+ from oslo_policy import opts
+ from oslo_policy import policy
+ from oslo_utils import importutils
+@@ -27,6 +28,7 @@
+ from magnum.common import policies
+
+
++LOG = logging.getLogger(__name__)
+ _ENFORCER = None
+ CONF = cfg.CONF
+
+@@ -105,8 +107,14 @@ def enforce(context, rule=None, target=None,
+ target = {'project_id': context.project_id,
+ 'user_id': context.user_id}
+ add_policy_attributes(target)
+- return enforcer.enforce(rule, target, credentials,
+- do_raise=do_raise, exc=exc, *args, **kwargs)
++
++ try:
++ result = enforcer.enforce(rule, target, credentials,
++ do_raise=do_raise, exc=exc, *args, **kwargs)
++ except policy.InvalidScope as ex:
++ LOG.debug(f"Invalide scope while enforce policy :{str(ex)}")
++ raise exc(action=rule)
++ return result
+
+
+ def add_policy_attributes(target):
+diff --git a/magnum/tests/fakes.py b/magnum/tests/fakes.py
+index 4407975306..3a64078ce8 100644
+--- a/magnum/tests/fakes.py
++++ b/magnum/tests/fakes.py
+@@ -25,7 +25,7 @@
+ 'X-Roles': 'role1,role2',
+ 'X-Auth-Url': 'fake_auth_url',
+ 'X-Identity-Status': 'Confirmed',
+- 'X-User-Domain-Name': 'domain',
++ 'X-User-Domain-Name': 'user_domain_name',
+ 'X-Project-Domain-Id': 'project_domain_id',
+ 'X-User-Domain-Id': 'user_domain_id',
+ 'OpenStack-API-Version': 'container-infra 1.0'
+diff --git a/magnum/tests/unit/api/base.py b/magnum/tests/unit/api/base.py
+index a4dd3fef63..ddf41277e4 100644
+--- a/magnum/tests/unit/api/base.py
++++ b/magnum/tests/unit/api/base.py
+@@ -128,6 +128,9 @@ def put_json(self, path, params, expect_errors=False, headers=None,
+ with the request
+ :param status: expected status code of response
+ """
++ # Provide member role for put request
++ if not headers:
++ headers = {"X-Roles": "member"}
+ return self._request_json(path=path, params=params,
+ expect_errors=expect_errors,
+ headers=headers, extra_environ=extra_environ,
+@@ -146,6 +149,9 @@ def post_json(self, path, params, expect_errors=False, headers=None,
+ with the request
+ :param status: expected status code of response
+ """
++ # Provide member role for post request
++ if not headers:
++ headers = {"X-Roles": "member"}
+ return self._request_json(path=path, params=params,
+ expect_errors=expect_errors,
+ headers=headers, extra_environ=extra_environ,
+@@ -164,6 +170,9 @@ def patch_json(self, path, params, expect_errors=False, headers=None,
+ with the request
+ :param status: expected status code of response
+ """
++ # Provide member role for patch request
++ if not headers:
++ headers = {"X-Roles": "member"}
+ return self._request_json(path=path, params=params,
+ expect_errors=expect_errors,
+ headers=headers, extra_environ=extra_environ,
+@@ -184,6 +193,9 @@ def delete(self, path, expect_errors=False, headers=None,
+ """
+ full_path = path_prefix + path
+ print('DELETE: %s' % (full_path))
++ # Provide member role for delete request
++ if not headers:
++ headers = {"X-Roles": "member"}
+ response = self.app.delete(str(full_path),
+ headers=headers,
+ status=status,
+@@ -215,6 +227,10 @@ def get_json(self, path, expect_errors=False, headers=None,
+ 'q.value': [],
+ 'q.op': [],
+ }
++
++ # Provide reader role for get request
++ if not headers:
++ headers = {"X-Roles": "reader"}
+ for query in q:
+ for name in ['field', 'op', 'value']:
+ query_params['q.%s' % name].append(query.get(name, ''))
+diff --git a/magnum/tests/unit/api/controllers/test_root.py b/magnum/tests/unit/api/controllers/test_root.py
+index e187715016..31700761fd 100644
+--- a/magnum/tests/unit/api/controllers/test_root.py
++++ b/magnum/tests/unit/api/controllers/test_root.py
+@@ -140,7 +140,9 @@ def test_noauth(self):
+ response = app.get('/v1/')
+ self.assertEqual(self.v1_expected, response.json)
+
+- response = app.get('/v1/clustertemplates')
++ response = app.get('/v1/clustertemplates',
++ headers={"X-Roles": "reader"}
++ )
+ self.assertEqual(200, response.status_int)
+
+ def test_auth_with_no_public_routes(self):
+diff --git a/magnum/tests/unit/api/controllers/v1/test_certificate.py b/magnum/tests/unit/api/controllers/v1/test_certificate.py
+index 02fcfb40a2..ecd14f0187 100644
+--- a/magnum/tests/unit/api/controllers/v1/test_certificate.py
++++ b/magnum/tests/unit/api/controllers/v1/test_certificate.py
+@@ -21,7 +21,14 @@
+ from magnum.tests.unit.objects import utils as obj_utils
+
+
+-HEADERS = {'OpenStack-API-Version': 'container-infra latest'}
++READER_HEADERS = {
++ 'OpenStack-API-Version': 'container-infra latest',
++ "X-Roles": "reader"
++}
++HEADERS = {
++ 'OpenStack-API-Version': 'container-infra latest',
++ "X-Roles": "member"
++}
+
+
+ class TestCertObject(base.TestCase):
+@@ -59,7 +66,7 @@ def test_get_one(self):
+ self.conductor_api.get_ca_certificate.return_value = mock_cert
+
+ response = self.get_json('/certificates/%s' % self.cluster.uuid,
+- headers=HEADERS)
++ headers=READER_HEADERS)
+
+ self.assertEqual(self.cluster.uuid, response['cluster_uuid'])
+ # check that bay is still valid as well
+@@ -74,7 +81,7 @@ def test_get_one_by_name(self):
+ self.conductor_api.get_ca_certificate.return_value = mock_cert
+
+ response = self.get_json('/certificates/%s' % self.cluster.name,
+- headers=HEADERS)
++ headers=READER_HEADERS)
+
+ self.assertEqual(self.cluster.uuid, response['cluster_uuid'])
+ # check that bay is still valid as well
+@@ -84,7 +91,8 @@ def test_get_one_by_name(self):
+
+ def test_get_one_by_name_not_found(self):
+ response = self.get_json('/certificates/not_found',
+- expect_errors=True, headers=HEADERS)
++ expect_errors=True,
++ headers=READER_HEADERS)
+
+ self.assertEqual(404, response.status_int)
+ self.assertEqual('application/json', response.content_type)
+@@ -97,7 +105,8 @@ def test_get_one_by_name_multiple_cluster(self):
+ uuid=uuidutils.generate_uuid())
+
+ response = self.get_json('/certificates/test_cluster',
+- expect_errors=True, headers=HEADERS)
++ expect_errors=True,
++ headers=READER_HEADERS)
+
+ self.assertEqual(409, response.status_int)
+ self.assertEqual('application/json', response.content_type)
+@@ -110,7 +119,7 @@ def test_links(self):
+ self.conductor_api.get_ca_certificate.return_value = mock_cert
+
+ response = self.get_json('/certificates/%s' % self.cluster.uuid,
+- headers=HEADERS)
++ headers=READER_HEADERS)
+
+ self.assertIn('links', response.keys())
+ self.assertEqual(2, len(response['links']))
+@@ -265,7 +274,7 @@ def test_policy_disallow_get_one(self):
+ self._common_policy_check(
+ "certificate:get", self.get_json,
+ '/certificates/%s' % cluster.uuid,
+- expect_errors=True, headers=HEADERS)
++ expect_errors=True, headers=READER_HEADERS)
+
+ def test_policy_disallow_create(self):
+ cluster = obj_utils.create_test_cluster(self.context)
+diff --git a/magnum/tests/unit/api/controllers/v1/test_cluster.py b/magnum/tests/unit/api/controllers/v1/test_cluster.py
+index 016f8cc173..9ff2439f36 100755
+--- a/magnum/tests/unit/api/controllers/v1/test_cluster.py
++++ b/magnum/tests/unit/api/controllers/v1/test_cluster.py
+@@ -494,7 +494,9 @@ def test_update_cluster_with_rollback_enabled(self):
+ '/clusters/%s/?rollback=True' % self.cluster_obj.uuid,
+ [{'path': '/node_count', 'value': node_count,
+ 'op': 'replace'}],
+- headers={'OpenStack-API-Version': 'container-infra 1.3'})
++ headers={'OpenStack-API-Version': 'container-infra 1.3',
++ "X-Roles": "member"
++ })
+
+ self.mock_cluster_update.assert_called_once_with(
+ mock.ANY, node_count, self.cluster_obj.health_status,
+@@ -507,7 +509,9 @@ def test_update_cluster_with_rollback_disabled(self):
+ '/clusters/%s/?rollback=False' % self.cluster_obj.uuid,
+ [{'path': '/node_count', 'value': node_count,
+ 'op': 'replace'}],
+- headers={'OpenStack-API-Version': 'container-infra 1.3'})
++ headers={'OpenStack-API-Version': 'container-infra 1.3',
++ "X-Roles": "member"
++ })
+
+ self.mock_cluster_update.assert_called_once_with(
+ mock.ANY, node_count, self.cluster_obj.health_status,
+@@ -520,7 +524,9 @@ def test_update_cluster_with_zero_node_count_fail(self):
+ '/clusters/%s' % self.cluster_obj.uuid,
+ [{'path': '/node_count', 'value': node_count,
+ 'op': 'replace'}],
+- headers={'OpenStack-API-Version': 'container-infra 1.9'},
++ headers={'OpenStack-API-Version': 'container-infra 1.9',
++ "X-Roles": "member"
++ },
+ expect_errors=True)
+
+ self.assertEqual(400, response.status_code)
+@@ -531,7 +537,9 @@ def test_update_cluster_with_zero_node_count(self):
+ '/clusters/%s' % self.cluster_obj.uuid,
+ [{'path': '/node_count', 'value': node_count,
+ 'op': 'replace'}],
+- headers={'OpenStack-API-Version': 'container-infra 1.10'})
++ headers={'OpenStack-API-Version': 'container-infra 1.10',
++ "X-Roles": "member"
++ })
+
+ self.mock_cluster_update.assert_called_once_with(
+ mock.ANY, node_count, self.cluster_obj.health_status,
+@@ -708,18 +716,24 @@ def test_create_cluster_with_cluster_template_name(self):
+ def test_create_cluster_with_zero_node_count_fail(self):
+ bdict = apiutils.cluster_post_data()
+ bdict['node_count'] = 0
+- response = self.post_json('/clusters', bdict, expect_errors=True,
+- headers={"Openstack-Api-Version":
+- "container-infra 1.9"})
++ response = self.post_json(
++ '/clusters', bdict, expect_errors=True,
++ headers={
++ "Openstack-Api-Version": "container-infra 1.9",
++ "X-Roles": "member"
++ })
+ self.assertEqual('application/json', response.content_type)
+ self.assertEqual(400, response.status_int)
+
+ def test_create_cluster_with_zero_node_count(self):
+ bdict = apiutils.cluster_post_data()
+ bdict['node_count'] = 0
+- response = self.post_json('/clusters', bdict,
+- headers={"Openstack-Api-Version":
+- "container-infra 1.10"})
++ response = self.post_json(
++ '/clusters', bdict,
++ headers={
++ "Openstack-Api-Version": "container-infra 1.10",
++ "X-Roles": "member"
++ })
+ self.assertEqual('application/json', response.content_type)
+ self.assertEqual(202, response.status_int)
+
+diff --git a/magnum/tests/unit/api/controllers/v1/test_cluster_actions.py b/magnum/tests/unit/api/controllers/v1/test_cluster_actions.py
+index ba9304fe1b..22baf556ce 100644
+--- a/magnum/tests/unit/api/controllers/v1/test_cluster_actions.py
++++ b/magnum/tests/unit/api/controllers/v1/test_cluster_actions.py
+@@ -46,7 +46,8 @@ def test_resize(self):
+ self.cluster_obj.uuid,
+ {"node_count": new_node_count},
+ headers={"Openstack-Api-Version":
+- "container-infra 1.7"})
++ "container-infra 1.7",
++ "X-Roles": "member"})
+ self.assertEqual(202, response.status_code)
+
+ response = self.get_json('/clusters/%s' % self.cluster_obj.uuid)
+@@ -69,7 +70,8 @@ def test_resize_with_nodegroup(self):
+ self.cluster_obj.uuid,
+ cluster_resize_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"})
++ "container-infra 1.9",
++ "X-Roles": "member"})
+ self.assertEqual(202, response.status_code)
+
+ response = self.get_json('/clusters/%s' % self.cluster_obj.uuid)
+@@ -89,7 +91,8 @@ def test_resize_with_master_nodegroup(self):
+ self.cluster_obj.uuid,
+ cluster_resize_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"},
++ "container-infra 1.9",
++ "X-Roles": "member"},
+ expect_errors=True)
+ self.assertEqual(400, response.status_code)
+
+@@ -106,7 +109,8 @@ def test_resize_with_node_count_greater_than_max(self):
+ self.cluster_obj.uuid,
+ cluster_resize_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"},
++ "container-infra 1.9",
++ "X-Roles": "member"},
+ expect_errors=True)
+ self.assertEqual(400, response.status_code)
+
+@@ -123,7 +127,8 @@ def test_resize_with_node_count_less_than_min(self):
+ self.cluster_obj.uuid,
+ cluster_resize_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"},
++ "container-infra 1.9",
++ "X-Roles": "member"},
+ expect_errors=True)
+ self.assertEqual(400, response.status_code)
+
+@@ -140,7 +145,8 @@ def test_resize_with_zero_node_count_fail(self):
+ self.cluster_obj.uuid,
+ cluster_resize_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"},
++ "container-infra 1.9",
++ "X-Roles": "member"},
+ expect_errors=True)
+ self.assertEqual(400, response.status_code)
+
+@@ -157,7 +163,8 @@ def test_resize_with_zero_node_count(self):
+ self.cluster_obj.uuid,
+ cluster_resize_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.10"})
++ "container-infra 1.10",
++ "X-Roles": "member"})
+ self.assertEqual(202, response.status_code)
+
+
+@@ -195,7 +202,8 @@ def test_upgrade(self):
+ self.cluster_obj.uuid,
+ cluster_upgrade_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.8"})
++ "container-infra 1.8",
++ "X-Roles": "member"})
+ self.assertEqual(202, response.status_code)
+
+ def test_upgrade_cluster_as_admin(self):
+@@ -226,7 +234,8 @@ def test_upgrade_cluster_as_admin(self):
+ '/clusters/%s/actions/upgrade' %
+ cluster_uuid,
+ cluster_upgrade_req,
+- headers={"Openstack-Api-Version": "container-infra 1.8"})
++ headers={"Openstack-Api-Version": "container-infra 1.8",
++ "X-Roles": "member"})
+
+ self.assertEqual(202, response.status_int)
+
+@@ -239,7 +248,8 @@ def test_upgrade_default_worker(self):
+ self.cluster_obj.uuid,
+ cluster_upgrade_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"})
++ "container-infra 1.9",
++ "X-Roles": "member"})
+ self.assertEqual(202, response.status_code)
+
+ def test_upgrade_default_master(self):
+@@ -251,7 +261,8 @@ def test_upgrade_default_master(self):
+ self.cluster_obj.uuid,
+ cluster_upgrade_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"})
++ "container-infra 1.9",
++ "X-Roles": "member"})
+ self.assertEqual(202, response.status_code)
+
+ def test_upgrade_non_default_ng(self):
+@@ -263,7 +274,8 @@ def test_upgrade_non_default_ng(self):
+ self.cluster_obj.uuid,
+ cluster_upgrade_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"})
++ "container-infra 1.9",
++ "X-Roles": "member"})
+ self.assertEqual(202, response.status_code)
+
+ def test_upgrade_cluster_not_found(self):
+@@ -273,7 +285,8 @@ def test_upgrade_cluster_not_found(self):
+ response = self.post_json('/clusters/not_there/actions/upgrade',
+ cluster_upgrade_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.8"},
++ "container-infra 1.8",
++ "X-Roles": "member"},
+ expect_errors=True)
+ self.assertEqual(404, response.status_code)
+
+@@ -285,7 +298,8 @@ def test_upgrade_ct_not_found(self):
+ self.cluster_obj.uuid,
+ cluster_upgrade_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.8"},
++ "container-infra 1.8",
++ "X-Roles": "member"},
+ expect_errors=True)
+ self.assertEqual(404, response.status_code)
+
+@@ -298,7 +312,8 @@ def test_upgrade_ng_not_found(self):
+ self.cluster_obj.uuid,
+ cluster_upgrade_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"},
++ "container-infra 1.9",
++ "X-Roles": "member"},
+ expect_errors=True)
+ self.assertEqual(404, response.status_code)
+
+@@ -311,6 +326,7 @@ def test_upgrade_non_default_ng_invalid_ct(self):
+ self.cluster_obj.uuid,
+ cluster_upgrade_req,
+ headers={"Openstack-Api-Version":
+- "container-infra 1.9"},
++ "container-infra 1.9",
++ "X-Roles": "member"},
+ expect_errors=True)
+ self.assertEqual(409, response.status_code)
+diff --git a/magnum/tests/unit/api/controllers/v1/test_nodegroup.py b/magnum/tests/unit/api/controllers/v1/test_nodegroup.py
+index a6f73d54b2..68304a10f6 100644
+--- a/magnum/tests/unit/api/controllers/v1/test_nodegroup.py
++++ b/magnum/tests/unit/api/controllers/v1/test_nodegroup.py
+@@ -47,24 +47,26 @@ def test_nodegroup_init(self):
+ class NodeGroupControllerTest(api_base.FunctionalTest):
+ headers = {"Openstack-Api-Version": "container-infra latest"}
+
+- def _add_headers(self, kwargs):
++ def _add_headers(self, kwargs, roles=None):
+ if 'headers' not in kwargs:
+ kwargs['headers'] = self.headers
++ if roles:
++ kwargs['headers']['X-Roles'] = ",".join(roles)
+
+ def get_json(self, *args, **kwargs):
+- self._add_headers(kwargs)
++ self._add_headers(kwargs, roles=['reader'])
+ return super(NodeGroupControllerTest, self).get_json(*args, **kwargs)
+
+ def post_json(self, *args, **kwargs):
+- self._add_headers(kwargs)
++ self._add_headers(kwargs, roles=['member'])
+ return super(NodeGroupControllerTest, self).post_json(*args, **kwargs)
+
+ def delete(self, *args, **kwargs):
+- self._add_headers(kwargs)
++ self._add_headers(kwargs, roles=['member'])
+ return super(NodeGroupControllerTest, self).delete(*args, **kwargs)
+
+ def patch_json(self, *args, **kwargs):
+- self._add_headers(kwargs)
++ self._add_headers(kwargs, roles=['member'])
+ return super(NodeGroupControllerTest, self).patch_json(*args, **kwargs)
+
+
+diff --git a/magnum/tests/unit/api/controllers/v1/test_quota.py b/magnum/tests/unit/api/controllers/v1/test_quota.py
+index b6b47c481a..07e78857ed 100644
+--- a/magnum/tests/unit/api/controllers/v1/test_quota.py
++++ b/magnum/tests/unit/api/controllers/v1/test_quota.py
+@@ -207,7 +207,7 @@ def test_get_all_non_admin(self, mock_policy):
+ project_id="proj-id-"+str(i))
+ quota_list.append(quota)
+
+- headers = {'X-Project-Id': 'proj-id-2'}
++ headers = {'X-Project-Id': 'proj-id-2', "X-Roles": "member"}
+ response = self.get_json('/quotas', headers=headers)
+ self.assertEqual(1, len(response['quotas']))
+ self.assertEqual('proj-id-2', response['quotas'][0]['project_id'])
+diff --git a/magnum/tests/unit/api/controllers/v1/test_stats.py b/magnum/tests/unit/api/controllers/v1/test_stats.py
+index bb7aac28f4..2e41222d34 100644
+--- a/magnum/tests/unit/api/controllers/v1/test_stats.py
++++ b/magnum/tests/unit/api/controllers/v1/test_stats.py
+@@ -21,7 +21,14 @@
+ class TestStatsController(api_base.FunctionalTest):
+
+ def setUp(self):
+- self.base_headers = {'OpenStack-API-Version': 'container-infra 1.4'}
++ self.base_headers = {
++ "X-Roles": "reader",
++ "OpenStack-API-Version": "container-infra 1.4"
++ }
++ self.base_admin_headers = {
++ "X-Roles": "admin",
++ "OpenStack-API-Version": "container-infra 1.4"
++ }
+ super(TestStatsController, self).setUp()
+ obj_utils.create_test_cluster_template(self.context)
+
+@@ -39,7 +46,7 @@ def test_admin_get_all_stats(self, mock_context, mock_policy):
+ obj_utils.create_test_cluster(self.context,
+ project_id=234,
+ uuid='uuid2')
+- response = self.get_json('/stats', headers=self.base_headers)
++ response = self.get_json('/stats', headers=self.base_admin_headers)
+ expected = {u'clusters': 2, u'nodes': 12}
+ self.assertEqual(expected, response)
+
+@@ -54,7 +61,7 @@ def test_admin_get_tenant_stats(self, mock_context, mock_policy):
+ uuid='uuid2')
+ self.context.is_admin = True
+ response = self.get_json('/stats?project_id=234',
+- headers=self.base_headers)
++ headers=self.base_admin_headers)
+ expected = {u'clusters': 1, u'nodes': 6}
+ self.assertEqual(expected, response)
+
+@@ -69,7 +76,7 @@ def test_admin_get_invalid_tenant_stats(self, mock_context, mock_policy):
+ uuid='uuid2')
+ self.context.is_admin = True
+ response = self.get_json('/stats?project_id=34',
+- headers=self.base_headers)
++ headers=self.base_admin_headers)
+ expected = {u'clusters': 0, u'nodes': 0}
+ self.assertEqual(expected, response)
+
+diff --git a/magnum/tests/unit/api/test_hooks.py b/magnum/tests/unit/api/test_hooks.py
+index 9332c93120..3cbfde4363 100644
+--- a/magnum/tests/unit/api/test_hooks.py
++++ b/magnum/tests/unit/api/test_hooks.py
+@@ -34,7 +34,8 @@ def setUp(self):
+ super(TestContextHook, self).setUp()
+ self.app = fakes.FakeApp()
+
+- def test_context_hook_before_method(self):
++ @mock.patch("magnum.common.policy.check_is_admin")
++ def test_context_hook_before_method(self, m_c):
+ state = mock.Mock(request=fakes.FakePecanRequest())
+ hook = hooks.ContextHook()
+ hook.before(state)
+@@ -51,12 +52,13 @@ def test_context_hook_before_method(self):
+ self.assertEqual(fakes.fakeAuthTokenHeaders['X-Roles'],
+ ','.join(ctx.roles))
+ self.assertEqual(fakes.fakeAuthTokenHeaders['X-User-Domain-Name'],
+- ctx.domain_name)
++ ctx.user_domain_name)
+ self.assertEqual(fakes.fakeAuthTokenHeaders['X-User-Domain-Id'],
+- ctx.domain_id)
++ ctx.user_domain_id)
+ self.assertIsNone(ctx.auth_token_info)
+
+- def test_context_hook_before_method_auth_info(self):
++ @mock.patch("magnum.common.policy.check_is_admin")
++ def test_context_hook_before_method_auth_info(self, c_m):
+ state = mock.Mock(request=fakes.FakePecanRequest())
+ state.request.environ['keystone.token_info'] = 'assert_this'
+ hook = hooks.ContextHook()
+diff --git a/magnum/tests/unit/common/policies/__init__.py b/magnum/tests/unit/common/policies/__init__.py
+new file mode 100644
+index 0000000000..e69de29bb2
+diff --git a/magnum/tests/unit/common/policies/base.py b/magnum/tests/unit/common/policies/base.py
+new file mode 100644
+index 0000000000..22572c0a46
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/base.py
+@@ -0,0 +1,37 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from oslo_config import cfg
++
++from magnum.tests.unit.api import base as api_base
++
++
++CONF = cfg.CONF
++
++
++class PolicyFunctionalTest(api_base.FunctionalTest):
++ def setUp(self):
++ super(PolicyFunctionalTest, self).setUp()
++ CONF.set_override('enforce_scope', True, group='oslo_policy')
++ CONF.set_override('enforce_new_defaults', True, group='oslo_policy')
++ self.reader_headers = {
++ "X-Roles": "reader",
++ }
++ self.member_headers = {
++ "X-Roles": "member",
++ }
++ self.admin_headers = {
++ "X-Roles": "admin",
++ }
++ self.foo_headers = {
++ "X-Roles": "foo",
++ }
+diff --git a/magnum/tests/unit/common/policies/test_certificate_policy.py b/magnum/tests/unit/common/policies/test_certificate_policy.py
+new file mode 100644
+index 0000000000..cc53a71645
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/test_certificate_policy.py
+@@ -0,0 +1,72 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from unittest import mock
++from webtest.app import AppError
++
++from magnum.tests.unit.api import utils as apiutils
++from magnum.tests.unit.common.policies import base
++from magnum.tests.unit.objects import utils as obj_utils
++
++READER_HEADERS = {
++ 'OpenStack-API-Version': 'container-infra latest',
++ "X-Roles": "reader"
++}
++HEADERS = {
++ 'OpenStack-API-Version': 'container-infra latest',
++ "X-Roles": "member"
++}
++
++
++class TestCertifiactePolicy(base.PolicyFunctionalTest):
++ def setUp(self):
++ super(TestCertifiactePolicy, self).setUp()
++ self.cluster = obj_utils.create_test_cluster(self.context)
++
++ conductor_api_patcher = mock.patch('magnum.conductor.api.API')
++ self.conductor_api_class = conductor_api_patcher.start()
++ self.conductor_api = mock.MagicMock()
++ self.conductor_api_class.return_value = self.conductor_api
++ self.addCleanup(conductor_api_patcher.stop)
++
++ self.conductor_api.sign_certificate.side_effect = self._fake_sign
++
++ @staticmethod
++ def _fake_sign(cluster, cert):
++ cert.pem = 'fake-pem'
++ return cert
++
++ def test_get_no_permission(self):
++ exc = self.assertRaises(
++ AppError,
++ self.get_json,
++ f"/certificates/{self.cluster.uuid}",
++ headers=HEADERS)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_create_no_permission(self):
++ new_cert = apiutils.cert_post_data(cluster_uuid=self.cluster.uuid)
++ del new_cert['pem']
++
++ exc = self.assertRaises(
++ AppError, self.post_json,
++ '/certificates', new_cert,
++ headers=READER_HEADERS)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_update_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.patch_json,
++ f"/certificates/{self.cluster.uuid}", {},
++ headers=READER_HEADERS
++ )
++ self.assertIn("403 Forbidden", str(exc))
+diff --git a/magnum/tests/unit/common/policies/test_cluster_policy.py b/magnum/tests/unit/common/policies/test_cluster_policy.py
+new file mode 100644
+index 0000000000..01cfd25c5c
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/test_cluster_policy.py
+@@ -0,0 +1,65 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from webtest.app import AppError
++
++from magnum.tests.unit.api import utils as apiutils
++from magnum.tests.unit.common.policies import base
++from magnum.tests.unit.objects import utils as obj_utils
++
++
++class TestClusterPolicy(base.PolicyFunctionalTest):
++ def setUp(self):
++ super(TestClusterPolicy, self).setUp()
++ self.cluster = obj_utils.create_test_cluster(
++ self.context, name='cluster_example_A', node_count=3
++ )
++
++ def test_get_all_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.get_json, '/clusters',
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_get_no_permission(self):
++ exc = self.assertRaises(
++ AppError,
++ self.get_json,
++ f"/clusters/{self.cluster.uuid}",
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_create_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.post_json,
++ '/clusters', apiutils.cluster_post_data(),
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_update_no_permission(self):
++ cluster_dict = [
++ {'path': '/node_count', 'value': 4, 'op': 'replace'}
++ ]
++ exc = self.assertRaises(
++ AppError, self.patch_json,
++ f"/clusters/{self.cluster.name}", cluster_dict,
++ headers=self.reader_headers
++ )
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_delete_no_permission(self):
++ # delete cluster
++ exc = self.assertRaises(
++ AppError, self.delete, f"/clusters/{self.cluster.uuid}",
++ headers=self.reader_headers
++ )
++ self.assertIn("403 Forbidden", str(exc))
+diff --git a/magnum/tests/unit/common/policies/test_cluster_template_policy.py b/magnum/tests/unit/common/policies/test_cluster_template_policy.py
+new file mode 100644
+index 0000000000..c6eb9b60a6
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/test_cluster_template_policy.py
+@@ -0,0 +1,74 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from webtest.app import AppError
++
++from magnum.tests.unit.api import utils as apiutils
++from magnum.tests.unit.common.policies import base
++from magnum.tests.unit.objects import utils as obj_utils
++
++
++class TestClusterTemplatePolicy(base.PolicyFunctionalTest):
++ def setUp(self):
++ super(TestClusterTemplatePolicy, self).setUp()
++ self.clustertemplate = obj_utils.create_test_cluster_template(
++ self.context
++ )
++
++ def test_get_all_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.get_json, '/clustertemplates',
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_get_detail_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.get_json,
++ '/clustertemplates/detail',
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_get_no_permission(self):
++ exc = self.assertRaises(
++ AppError,
++ self.get_json,
++ f"/clustertemplates/{self.clustertemplate.uuid}",
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_create_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.post_json,
++ '/clustertemplates',
++ apiutils.cluster_template_post_data(),
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_update_no_permission(self):
++ clustertemplate_data = [
++ {'path': '/dns_nameserver', 'op': 'remove'}]
++ exc = self.assertRaises(
++ AppError,
++ self.patch_json,
++ f"/clustertemplates/{self.clustertemplate.uuid}",
++ clustertemplate_data,
++ headers=self.reader_headers
++ )
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_delete_no_permission(self):
++ # delete clustertemplate
++ exc = self.assertRaises(
++ AppError, self.delete,
++ f"/clustertemplates/{self.clustertemplate.uuid}",
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
+diff --git a/magnum/tests/unit/common/policies/test_federation_policy.py b/magnum/tests/unit/common/policies/test_federation_policy.py
+new file mode 100644
+index 0000000000..68eb1d6212
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/test_federation_policy.py
+@@ -0,0 +1,67 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from oslo_utils import uuidutils
++from webtest.app import AppError
++
++from magnum.tests.unit.common.policies import base
++from magnum.tests.unit.objects import utils as obj_utils
++
++
++class TestFederationPolicy(base.PolicyFunctionalTest):
++ def setUp(self):
++ super(TestFederationPolicy, self).setUp()
++ self.create_frederation()
++
++ def create_frederation(self):
++ self.fake_uuid = uuidutils.generate_uuid()
++ self.federation = obj_utils.create_test_federation(
++ self.context, uuid=self.fake_uuid)
++
++ def test_get_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.get_json, '/federations',
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_get_reader(self):
++ response = self.get_json('/federations')
++ self.assertEqual(self.fake_uuid, response['federations'][0]['uuid'])
++
++ def test_create_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.post_json, '/federations', {},
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_update_no_permission(self):
++ new_member = obj_utils.create_test_cluster(self.context)
++ exc = self.assertRaises(
++ AppError, self.patch_json, '/federations/%s' % self.fake_uuid,
++ [{'path': '/member_ids', 'value': new_member.uuid, 'op': 'add'}],
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_delete_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.delete,
++ '/federations/%s' % self.fake_uuid,
++ headers=self.reader_headers
++ )
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_detail_list_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.get_json,
++ '/federations/detail',
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
+diff --git a/magnum/tests/unit/common/policies/test_magnum_service_policy.py b/magnum/tests/unit/common/policies/test_magnum_service_policy.py
+new file mode 100644
+index 0000000000..9f8153d3a4
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/test_magnum_service_policy.py
+@@ -0,0 +1,26 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from webtest.app import AppError
++
++from magnum.tests.unit.common.policies import base
++
++
++class TestMagnumServicePolicy(base.PolicyFunctionalTest):
++ def setUp(self):
++ super(TestMagnumServicePolicy, self).setUp()
++
++ def test_get_all_no_permission(self):
++ exc = self.assertRaises(AppError,
++ self.get_json, "/mservices",
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
+diff --git a/magnum/tests/unit/common/policies/test_nodegroup_policy.py b/magnum/tests/unit/common/policies/test_nodegroup_policy.py
+new file mode 100644
+index 0000000000..73f3e107e4
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/test_nodegroup_policy.py
+@@ -0,0 +1,74 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from oslo_utils import uuidutils
++from webtest.app import AppError
++
++from magnum import objects
++from magnum.tests.unit.api import utils as apiutils
++from magnum.tests.unit.common.policies import base
++from magnum.tests.unit.objects import utils as obj_utils
++
++
++class TestNodeGroupPolicy(base.PolicyFunctionalTest):
++ def setUp(self):
++ super(TestNodeGroupPolicy, self).setUp()
++ obj_utils.create_test_cluster_template(self.context)
++ self.cluster_uuid = uuidutils.generate_uuid()
++ obj_utils.create_test_cluster(
++ self.context, uuid=self.cluster_uuid)
++ self.cluster = objects.Cluster.get_by_uuid(self.context,
++ self.cluster_uuid)
++ self.nodegroup = obj_utils.create_test_nodegroup(
++ self.context, cluster_id=self.cluster.uuid, is_default=False)
++ self.url = f"/clusters/{self.cluster.uuid}/nodegroups/"
++ self.member = {"Openstack-Api-Version": "container-infra latest"}
++ self.member.update(self.member_headers)
++ self.reader = {"Openstack-Api-Version": "container-infra latest"}
++ self.reader.update(self.reader_headers)
++
++ def test_get_all_no_permission(self):
++ exc = self.assertRaises(AppError,
++ self.get_json, self.url,
++ headers=self.member)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_get_no_permission(self):
++ exc = self.assertRaises(
++ AppError,
++ self.get_json,
++ f"{self.url}foo",
++ headers=self.member)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_create_no_permission(self):
++ exc = self.assertRaises(AppError,
++ self.post_json, self.url,
++ apiutils.nodegroup_post_data(),
++ headers=self.reader)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_update_no_permission(self):
++ ng_dict = [
++ {'path': '/max_node_count', 'value': 4, 'op': 'replace'}]
++ exc = self.assertRaises(
++ AppError, self.patch_json,
++ self.url + self.nodegroup.uuid, ng_dict,
++ headers=self.reader)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_delete_no_permission(self):
++ # delete cluster
++ exc = self.assertRaises(
++ AppError, self.delete, self.url + self.nodegroup.uuid,
++ headers=self.reader)
++ self.assertIn("403 Forbidden", str(exc))
+diff --git a/magnum/tests/unit/common/policies/test_quota_policy.py b/magnum/tests/unit/common/policies/test_quota_policy.py
+new file mode 100644
+index 0000000000..48d4a09c2c
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/test_quota_policy.py
+@@ -0,0 +1,74 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from unittest import mock
++from webtest.app import AppError
++
++from magnum.common import clients
++from magnum.tests.unit.api import utils as apiutils
++from magnum.tests.unit.common.policies import base
++from magnum.tests.unit.objects import utils as obj_utils
++
++
++class TestQuotaPolicy(base.PolicyFunctionalTest):
++ def setUp(self):
++ super(TestQuotaPolicy, self).setUp()
++
++ def test_get_all_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.get_json, '/quotas',
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ def test_get_no_permission(self):
++ quota = obj_utils.create_test_quota(self.context)
++ exc = self.assertRaises(
++ AppError,
++ self.get_json,
++ f"/quotas/{quota['project_id']}/{quota['resource']}",
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ @mock.patch.object(clients.OpenStackClients, 'keystone')
++ def test_create_no_permission(self, mock_keystone):
++ exc = self.assertRaises(
++ AppError, self.post_json,
++ '/quotas', apiutils.quota_post_data(),
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ @mock.patch.object(clients.OpenStackClients, 'keystone')
++ def test_update_no_permission(self, mock_keystone):
++ with mock.patch("magnum.common.policy.enforce"):
++ quota_dict = apiutils.quota_post_data(hard_limit=5)
++ self.post_json('/quotas', quota_dict)
++ quota_dict['hard_limit'] = 20
++ exc = self.assertRaises(
++ AppError, self.patch_json, '/quotas', quota_dict,
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
++
++ @mock.patch.object(clients.OpenStackClients, 'keystone')
++ def test_delete_no_permission(self, mock_keystone):
++ with mock.patch("magnum.common.policy.enforce"):
++ quota_dict = apiutils.quota_post_data()
++ response = self.post_json('/quotas', quota_dict)
++ self.assertEqual('application/json', response.content_type)
++ self.assertEqual(201, response.status_int)
++
++ project_id = quota_dict['project_id']
++ resource = quota_dict['resource']
++ # delete quota
++ exc = self.assertRaises(
++ AppError, self.delete, f"/quotas/{project_id}/{resource}",
++ headers=self.reader_headers)
++ self.assertIn("403 Forbidden", str(exc))
+diff --git a/magnum/tests/unit/common/policies/test_stats_policy.py b/magnum/tests/unit/common/policies/test_stats_policy.py
+new file mode 100644
+index 0000000000..20cf1bee5c
+--- /dev/null
++++ b/magnum/tests/unit/common/policies/test_stats_policy.py
+@@ -0,0 +1,33 @@
++# Licensed under the Apache License, Version 2.0 (the "License"); you may
++# not use this file except in compliance with the License. You may obtain
++# a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++# License for the specific language governing permissions and limitations
++# under the License.
++
++from webtest.app import AppError
++
++from magnum.tests.unit.common.policies import base
++
++
++class TestStatsPolicy(base.PolicyFunctionalTest):
++ def test_stat_reader(self):
++ response = self.get_json('/stats', headers=self.reader_headers)
++ expected = {u'clusters': 0, u'nodes': 0}
++ self.assertEqual(expected, response)
++
++ def test_stat_admin(self):
++ response = self.get_json('/stats', headers=self.admin_headers)
++ expected = {u'clusters': 0, u'nodes': 0}
++ self.assertEqual(expected, response)
++
++ def test_stat_no_permission(self):
++ exc = self.assertRaises(
++ AppError, self.get_json, '/stats',
++ headers=self.member_headers)
++ self.assertIn("403 Forbidden", str(exc))
+diff --git a/magnum/tests/unit/common/test_context.py b/magnum/tests/unit/common/test_context.py
+index c72c2c763d..aed4d33ebd 100644
+--- a/magnum/tests/unit/common/test_context.py
++++ b/magnum/tests/unit/common/test_context.py
+@@ -19,29 +19,30 @@
+ class ContextTestCase(base.TestCase):
+
+ def _create_context(self, roles=None):
+- return magnum_context.RequestContext(auth_token='auth_token1',
+- auth_url='auth_url1',
+- domain_id='domain_id1',
+- domain_name='domain_name1',
+- user_name='user1',
+- user_id='user-id1',
+- project_name='tenant1',
+- project_id='tenant-id1',
+- roles=roles,
+- is_admin=True,
+- read_only=True,
+- show_deleted=True,
+- request_id='request_id1',
+- trust_id='trust_id1',
+- auth_token_info='token_info1')
++ return magnum_context.RequestContext(
++ auth_token='auth_token1',
++ auth_url='auth_url1',
++ user_domain_id='user_domain_id1',
++ user_domain_name='user_domain_name1',
++ user_name='user1',
++ user_id='user-id1',
++ project_name='tenant1',
++ project_id='tenant-id1',
++ roles=roles,
++ is_admin=True,
++ read_only=True,
++ show_deleted=True,
++ request_id='request_id1',
++ trust_id='trust_id1',
++ auth_token_info='token_info1')
+
+ def test_context(self):
+ ctx = self._create_context()
+
+ self.assertEqual("auth_token1", ctx.auth_token)
+ self.assertEqual("auth_url1", ctx.auth_url)
+- self.assertEqual("domain_id1", ctx.domain_id)
+- self.assertEqual("domain_name1", ctx.domain_name)
++ self.assertEqual("user_domain_id1", ctx.user_domain_id)
++ self.assertEqual("user_domain_name1", ctx.user_domain_name)
+ self.assertEqual("user1", ctx.user_name)
+ self.assertEqual("user-id1", ctx.user_id)
+ self.assertEqual("tenant1", ctx.project_name)
+@@ -59,8 +60,8 @@ def test_context_with_roles(self):
+
+ self.assertEqual("auth_token1", ctx.auth_token)
+ self.assertEqual("auth_url1", ctx.auth_url)
+- self.assertEqual("domain_id1", ctx.domain_id)
+- self.assertEqual("domain_name1", ctx.domain_name)
++ self.assertEqual("user_domain_id1", ctx.user_domain_id)
++ self.assertEqual("user_domain_name1", ctx.user_domain_name)
+ self.assertEqual("user1", ctx.user_name)
+ self.assertEqual("user-id1", ctx.user_id)
+ self.assertEqual("tenant1", ctx.project_name)
+@@ -80,8 +81,8 @@ def test_to_dict_from_dict(self):
+
+ self.assertEqual(ctx.auth_token, ctx2.auth_token)
+ self.assertEqual(ctx.auth_url, ctx2.auth_url)
+- self.assertEqual(ctx.domain_id, ctx2.domain_id)
+- self.assertEqual(ctx.domain_name, ctx2.domain_name)
++ self.assertEqual(ctx.user_domain_id, ctx2.user_domain_id)
++ self.assertEqual(ctx.user_domain_name, ctx2.user_domain_name)
+ self.assertEqual(ctx.user_name, ctx2.user_name)
+ self.assertEqual(ctx.user_id, ctx2.user_id)
+ self.assertEqual(ctx.project_id, ctx2.project_id)
+diff --git a/releasenotes/notes/allow_admin_perform_acitons-cc988655bb72b3f3.yaml b/releasenotes/notes/allow_admin_perform_acitons-cc988655bb72b3f3.yaml
+new file mode 100644
+index 0000000000..6cb516451c
+--- /dev/null
++++ b/releasenotes/notes/allow_admin_perform_acitons-cc988655bb72b3f3.yaml
+@@ -0,0 +1,9 @@
++---
++upgrade:
++ - |
++ To make sure better have backward compatibility,
++ we set specific rule to allow admin perform all actions.
++ This will apply on part of APIs in
++ * Cluster
++ * Cluster Template
++ * federation
+diff --git a/releasenotes/notes/enable-enforce-scope-and-new-defaults-7e6e503f74283071.yaml b/releasenotes/notes/enable-enforce-scope-and-new-defaults-7e6e503f74283071.yaml
+new file mode 100644
+index 0000000000..69b9fec5eb
+--- /dev/null
++++ b/releasenotes/notes/enable-enforce-scope-and-new-defaults-7e6e503f74283071.yaml
+@@ -0,0 +1,13 @@
++---
++upgrade:
++ - |
++ The Magnum service now allows enables policies (RBAC) new defaults
++ and scope checks. These are controlled by the following (default) config
++ options in ``magnum.conf`` file::
++
++ [oslo_policy]
++ enforce_new_defaults=False
++ enforce_scope=False
++
++ We will change the default to True in the following cycle.
++ If you want to enable them then modify both values to True.
diff --git a/images/manila/Earthfile b/images/manila/Earthfile
new file mode 100644
index 0000000..320fc8f
--- /dev/null
+++ b/images/manila/Earthfile
@@ -0,0 +1,17 @@
+VERSION 0.7
+
+image:
+ ARG PROJECT=manila
+ ARG RELEASE=zed
+ ARG REF=9ea49e2b9df7da16d5700810eee18710dc90e6a4
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF}
+ DO \
+ ../+APT_INSTALL \
+ --PACKAGES "iproute2 openvswitch-switch"
+ DO ../+APPLY_PATCHES
+ SAVE IMAGE --push \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/neutron/Earthfile b/images/neutron/Earthfile
new file mode 100644
index 0000000..e8fbf6d
--- /dev/null
+++ b/images/neutron/Earthfile
@@ -0,0 +1,21 @@
+VERSION 0.7
+
+platform-image:
+ ARG PROJECT=neutron
+ ARG RELEASE=zed
+ ARG REF=4575136fe99f67dc140987601c90493cf62c0330
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "git+https://github.com/openstack/neutron-vpnaas.git@256464aea691f8b4957ba668a117963353f34e4c"
+ DO \
+ ../+APT_INSTALL \
+ --PACKAGES "conntrack dnsmasq dnsmasq-utils ebtables ethtool haproxy iproute2 ipset iptables iputils-arping jq keepalived lshw openvswitch-switch strongswan uuid-runtime"
+ DO ../+APPLY_PATCHES
+ SAVE IMAGE --push \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
+
+image:
+ BUILD --platform linux/amd64 --platform linux/arm64 +platform-image
diff --git a/images/neutron/patches/0000-fix-netns-deletion-of-broken-namespaces.patch b/images/neutron/patches/0000-fix-netns-deletion-of-broken-namespaces.patch
new file mode 100644
index 0000000..a1540ad
--- /dev/null
+++ b/images/neutron/patches/0000-fix-netns-deletion-of-broken-namespaces.patch
@@ -0,0 +1,138 @@
+From f8130f36e3cdb67fd9be64a61ac22b487200e2bc Mon Sep 17 00:00:00 2001
+From: Felix Huettner <felix.huettner@mail.schwarz>
+Date: Fri, 22 Sep 2023 16:25:10 +0200
+Subject: [PATCH] fix netns deletion of broken namespaces
+
+normal network namespaces are bind-mounted to files under
+/var/run/netns. If a process deleting a network namespace gets killed
+during that operation there is the chance that the bind mount to the
+netns has been removed, but the file under /var/run/netns still exists.
+
+When the neutron-ovn-metadata-agent tries to clean up such network
+namespaces it first tires to validate that the network namespace is
+empty. For the cases described above this fails, as this network
+namespace no longer really exists, but is just a stray file laying
+around.
+
+To fix this we treat network namespaces where we get an `OSError` with
+errno 22 (Invalid Argument) as empty. The calls to pyroute2 to delete
+the namespace will then clean up the file.
+
+Additionally we add a guard to teardown_datapath to continue even if
+this fails. failing to remove a datapath is not critical and leaves in
+the worst case a process and a network namespace running, however
+previously it would have also prevented the creation of new datapaths
+which is critical for VM startup.
+
+Closes-Bug: #2037102
+Change-Id: I7c43812fed5903f98a2e491076c24a8d926a59b4
+---
+
+diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py
+index 9953729..196dc17 100644
+--- a/neutron/agent/linux/ip_lib.py
++++ b/neutron/agent/linux/ip_lib.py
+@@ -259,7 +259,22 @@
+ return ip
+
+ def namespace_is_empty(self):
+- return not self.get_devices()
++ try:
++ return not self.get_devices()
++ except OSError as e:
++ # This can happen if we previously got terminated in the middle of
++ # removing this namespace. In this case the bind mount of the
++ # namespace under /var/run/netns will be removed, but the namespace
++ # file is still there. As the bind mount is gone we can no longer
++ # access the namespace to validate that it is empty. But since it
++ # should have already been removed we are sure that the check has
++ # passed the last time and since the namespace is unuseable that
++ # can not have changed.
++ # Future calls to pyroute2 to remove that namespace will clean up
++ # the leftover file.
++ if e.errno == errno.EINVAL:
++ return True
++ raise e
+
+ def garbage_collect_namespace(self):
+ """Conditionally destroy the namespace if it is empty."""
+diff --git a/neutron/agent/ovn/metadata/agent.py b/neutron/agent/ovn/metadata/agent.py
+index 7a09145..888ab15 100644
+--- a/neutron/agent/ovn/metadata/agent.py
++++ b/neutron/agent/ovn/metadata/agent.py
+@@ -478,7 +478,10 @@
+ ns.startswith(NS_PREFIX) and
+ ns not in metadata_namespaces]
+ for ns in unused_namespaces:
+- self.teardown_datapath(self._get_datapath_name(ns))
++ try:
++ self.teardown_datapath(self._get_datapath_name(ns))
++ except Exception:
++ LOG.exception('Error unable to destroy namespace: %s', ns)
+
+ # resync all network namespaces based on the associated datapaths,
+ # even those that are already running. This is to make sure
+diff --git a/neutron/tests/unit/agent/linux/test_ip_lib.py b/neutron/tests/unit/agent/linux/test_ip_lib.py
+index c488e90..3956142 100644
+--- a/neutron/tests/unit/agent/linux/test_ip_lib.py
++++ b/neutron/tests/unit/agent/linux/test_ip_lib.py
+@@ -357,6 +357,23 @@
+ self.assertNotIn(mock.call().delete('ns'),
+ ip_ns_cmd_cls.mock_calls)
+
++ def test_garbage_collect_namespace_existing_broken(self):
++ with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls:
++ ip_ns_cmd_cls.return_value.exists.return_value = True
++
++ ip = ip_lib.IPWrapper(namespace='ns')
++
++ with mock.patch.object(ip, 'get_devices',
++ side_effect=OSError(errno.EINVAL, None)
++ ) as mock_get_devices:
++ self.assertTrue(ip.garbage_collect_namespace())
++
++ mock_get_devices.assert_called_once_with()
++ expected = [mock.call(ip),
++ mock.call().exists('ns'),
++ mock.call().delete('ns')]
++ self.assertEqual(ip_ns_cmd_cls.mock_calls, expected)
++
+ @mock.patch.object(priv_lib, 'create_interface')
+ def test_add_vlan(self, create):
+ retval = ip_lib.IPWrapper().add_vlan('eth0.1', 'eth0', '1')
+diff --git a/neutron/tests/unit/agent/ovn/metadata/test_agent.py b/neutron/tests/unit/agent/ovn/metadata/test_agent.py
+index 73487e1..cb6ee43 100644
+--- a/neutron/tests/unit/agent/ovn/metadata/test_agent.py
++++ b/neutron/tests/unit/agent/ovn/metadata/test_agent.py
+@@ -138,6 +138,31 @@
+ lnn.assert_called_once_with()
+ tdp.assert_called_once_with('3')
+
++ def test_sync_teardown_namespace_does_not_crash_on_error(self):
++ """Test that sync tears down unneeded metadata namespaces.
++ Even if that fails it continues to provision other datapaths
++ """
++ with mock.patch.object(
++ self.agent, 'provision_datapath') as pdp,\
++ mock.patch.object(
++ ip_lib, 'list_network_namespaces',
++ return_value=['ovnmeta-1', 'ovnmeta-2', 'ovnmeta-3',
++ 'ns1', 'ns2']) as lnn,\
++ mock.patch.object(
++ self.agent, 'teardown_datapath',
++ side_effect=Exception()) as tdp:
++ self.agent.sync()
++
++ pdp.assert_has_calls(
++ [
++ mock.call(p.datapath)
++ for p in self.ports
++ ],
++ any_order=True
++ )
++ lnn.assert_called_once_with()
++ tdp.assert_called_once_with('3')
++
+ def test_get_networks_datapaths(self):
+ """Test get_networks_datapaths returns only datapath objects for the
+ networks containing vif ports of type ''(blank) and 'external'.
diff --git a/images/nova-ssh/Earthfile b/images/nova-ssh/Earthfile
new file mode 100644
index 0000000..9988902
--- /dev/null
+++ b/images/nova-ssh/Earthfile
@@ -0,0 +1,15 @@
+VERSION 0.7
+
+clone:
+ FROM ../builder+image
+ GIT CLONE --branch a2e563b08ae633d75084c1bb40c97dbf1a539950 https://opendev.org/openstack/openstack-helm-images /src
+ WORKDIR /src
+ SAVE ARTIFACT /src
+
+platform-image:
+ FROM DOCKERFILE -f +clone/src/nova-ssh/Dockerfile.ubuntu_focal +clone/src/nova-ssh
+ LABEL org.opencontainers.image.source=https://github.com/vexxhost/atmosphere
+ SAVE IMAGE --push ghcr.io/vexxhost/atmosphere/nova-ssh:latest
+
+image:
+ BUILD --platform linux/amd64 --platform linux/arm64 +platform-image
diff --git a/images/nova/Earthfile b/images/nova/Earthfile
new file mode 100644
index 0000000..263ae99
--- /dev/null
+++ b/images/nova/Earthfile
@@ -0,0 +1,21 @@
+VERSION 0.7
+
+platform-image:
+ ARG PROJECT=nova
+ ARG RELEASE=zed
+ ARG REF=787839f6637f292fb5656725e5dae12fbe6e3c3e
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF}
+ DO \
+ ../+APT_INSTALL \
+ --PACKAGES "ceph-common genisoimage iproute2 libosinfo-bin lsscsi ndctl nvme-cli openssh-client ovmf python3-libvirt python3-rados python3-rbd qemu-efi-aarch64 qemu-utils sysfsutils udev util-linux"
+ DO ../+APPLY_PATCHES
+ GIT CLONE --branch v1.4.0 https://github.com/novnc/noVNC.git /usr/share/novnc
+ SAVE IMAGE --push \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
+ ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
+
+image:
+ BUILD --platform linux/amd64 --platform linux/arm64 +platform-image
diff --git a/images/octavia/Earthfile b/images/octavia/Earthfile
index 4847ee2..8b3ca58 100644
--- a/images/octavia/Earthfile
+++ b/images/octavia/Earthfile
@@ -4,11 +4,15 @@
ARG PROJECT=octavia
ARG RELEASE=zed
ARG REF=4c97b585ce6e519a5ff090c2cd97fab0e801be0c
- ARG PIP_PACKAGES="ovn-octavia-provider"
- FROM ../openstack-service+image --PROJECT ${PROJECT} --RELEASE ${RELEASE} --PROJECT_REF ${REF} --PIP_PACKAGES "${PIP_PACKAGES}"
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF} \
+ --PIP_PACKAGES "ovn-octavia-provider"
DO \
../+APT_INSTALL \
--PACKAGES "isc-dhcp-client"
+ DO ../+APPLY_PATCHES
SAVE IMAGE --push \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/cloud-archive-builder/2023.1/upper-constraints.txt b/images/openstack-service/2023.1/upper-constraints.txt
similarity index 98%
rename from images/cloud-archive-builder/2023.1/upper-constraints.txt
rename to images/openstack-service/2023.1/upper-constraints.txt
index a8eade5..29eab79 100644
--- a/images/cloud-archive-builder/2023.1/upper-constraints.txt
+++ b/images/openstack-service/2023.1/upper-constraints.txt
@@ -53,13 +53,13 @@
python-mistralclient==5.1.0
oslo.context==5.3.0
python-senlinclient==3.1.0
-rcssmin==1.1.2
+rcssmin===1.1.1
pycadf==3.1.1
grpcio==1.60.0
pysendfile==2.0.1
sniffio==1.3.0
fixtures==4.1.0
-neutron-lib==3.9.0
+neutron-lib==3.7.0
XStatic-FileSaver==1.3.2.0
oslo.metrics==0.7.0
storage-interfaces==1.0.5
@@ -117,7 +117,6 @@
requests-unixsocket==0.3.0
responses==0.24.1
croniter==1.4.1
-horizon==23.3.0
octavia-lib==3.4.0
python-watcherclient==4.2.0
MarkupSafe==2.1.3
@@ -204,7 +203,6 @@
python-3parclient==4.2.12
unittest2==1.1.0
django-compressor==4.4
-libvirt-python==8.10.0
python-zunclient==4.7.0
tzlocal==4.3.1
sphinxcontrib-jsmath==1.0.1
@@ -331,7 +329,7 @@
automaton==3.2.0
os-service-types==1.7.0
keyring==23.13.1
-elementpath==3.0.2
+elementpath==4.1.5
testscenarios==0.5.0
sphinxcontrib-pecanwsme==0.11.0
sadisplay==0.4.9
@@ -530,7 +528,7 @@
XStatic-Angular-lrdragndrop==1.0.2.6
ovsdbapp==2.5.0
aniso8601==9.0.1
-rjsmin==1.2.2
+rjsmin===1.2.1
icalendar==4.1.1
decorator==5.1.1
DateTimeRange==1.2.0
diff --git a/images/cloud-archive-builder/2023.2/upper-constraints.txt b/images/openstack-service/2023.2/upper-constraints.txt
similarity index 99%
rename from images/cloud-archive-builder/2023.2/upper-constraints.txt
rename to images/openstack-service/2023.2/upper-constraints.txt
index 3cefa30..8b87a9f 100644
--- a/images/cloud-archive-builder/2023.2/upper-constraints.txt
+++ b/images/openstack-service/2023.2/upper-constraints.txt
@@ -52,7 +52,7 @@
python-mistralclient==5.1.0
oslo.context==5.3.0
python-senlinclient==3.1.0
-rcssmin==1.1.2
+rcssmin===1.1.1
pycadf==3.1.1
grpcio==1.60.0
pysendfile==2.0.1
@@ -119,7 +119,6 @@
requests-unixsocket==0.3.0
responses==0.24.1
croniter==1.4.1
-horizon==23.3.0
octavia-lib==3.4.0
python-watcherclient==4.2.0
MarkupSafe==2.1.3
@@ -207,7 +206,6 @@
python-3parclient==4.2.12
unittest2==1.1.0
django-compressor==4.4
-libvirt-python==9.10.0
python-zunclient==4.7.0
tzlocal==4.3.1
sphinxcontrib-jsmath==1.0.1
@@ -539,7 +537,7 @@
XStatic-Angular-lrdragndrop==1.0.2.6
ovsdbapp==2.5.0
aniso8601==9.0.1
-rjsmin==1.2.2
+rjsmin===1.2.1
icalendar==5.0.11
decorator==5.1.1
DateTimeRange==2.2.0
diff --git a/images/openstack-service/Earthfile b/images/openstack-service/Earthfile
index 928694c..20c1f50 100644
--- a/images/openstack-service/Earthfile
+++ b/images/openstack-service/Earthfile
@@ -1,5 +1,50 @@
VERSION 0.7
+build:
+ ARG RELEASE
+ FROM ../cloud-archive-base+image --RELEASE=${RELEASE}
+ DO ../+APT_INSTALL --PACKAGES "\
+ build-essential \
+ curl \
+ git \
+ libldap2-dev \
+ libpcre3-dev \
+ libsasl2-dev \
+ libssl-dev \
+ lsb-release \
+ openssh-client \
+ python3 \
+ python3-dev \
+ python3-pip \
+ python3-venv"
+ RUN --mount type=cache,target=/root/.cache \
+ python3 -m venv --upgrade --system-site-packages /var/lib/openstack
+ ENV UWSGI_PROFILE_OVERRIDE=ssl=true
+ RUN --mount type=cache,target=/root/.cache \
+ mkdir -p /wheels && \
+ /var/lib/openstack/bin/pip3 wheel --wheel-dir /wheels uwsgi
+ COPY ${RELEASE}/upper-constraints.txt /upper-constraints.txt
+ ARG PROJECT
+ ARG PROJECT_REF
+ ARG PROJECT_REPO=https://opendev.org/openstack/${PROJECT}
+ GIT CLONE --branch ${PROJECT_REF} ${PROJECT_REPO} /src
+ RUN \
+ cd /src && \
+ git fetch --unshallow
+ ARG EXTRAS=""
+ ARG PIP_PACKAGES=""
+ RUN --mount=type=cache,target=/root/.cache \
+ /var/lib/openstack/bin/pip3 install \
+ --constraint /upper-constraints.txt \
+ --find-links /wheels/ \
+ pymysql \
+ python-memcached \
+ cryptography \
+ uwsgi \
+ /src${EXTRAS} \
+ ${PIP_PACKAGES}
+ SAVE ARTIFACT /var/lib/openstack venv
+
image:
ARG RELEASE
FROM ../cloud-archive-base+image --RELEASE=${RELEASE}
@@ -9,5 +54,6 @@
DO ../+CREATE_PROJECT_USER --PROJECT=${PROJECT}
ENV PATH=/var/lib/openstack/bin:$PATH
COPY \
- (../cloud-archive-builder+image/venv --RELEASE=${RELEASE} --PROJECT=${PROJECT} --PROJECT_REF=${PROJECT_REF} --PIP_PACKAGES=${PIP_PACKAGES}) \
+ (+build/venv --RELEASE=${RELEASE} --PROJECT=${PROJECT} --PROJECT_REF=${PROJECT_REF} --PIP_PACKAGES=${PIP_PACKAGES}) \
/var/lib/openstack
+ LABEL org.opencontainers.image.source=https://github.com/vexxhost/atmosphere
diff --git a/images/cloud-archive-builder/zed/upper-constraints.txt b/images/openstack-service/zed/upper-constraints.txt
similarity index 98%
rename from images/cloud-archive-builder/zed/upper-constraints.txt
rename to images/openstack-service/zed/upper-constraints.txt
index 3237f89..b580082 100644
--- a/images/cloud-archive-builder/zed/upper-constraints.txt
+++ b/images/openstack-service/zed/upper-constraints.txt
@@ -53,13 +53,13 @@
python-mistralclient==4.5.0
oslo.context==5.3.0
python-senlinclient==2.5.0
-rcssmin==1.1.2
+rcssmin===1.1.1
pycadf==3.1.1
grpcio==1.60.0
pysendfile==2.0.1
sniffio==1.3.0
fixtures==4.1.0
-neutron-lib==3.9.0
+neutron-lib==3.7.0
XStatic-FileSaver==1.3.2.0
oslo.metrics==0.7.0
storage-interfaces==1.0.5
@@ -116,7 +116,6 @@
requests-unixsocket==0.3.0
responses==0.24.1
croniter==1.4.1
-horizon==23.3.0
octavia-lib==3.4.0
python-watcherclient==4.2.0
MarkupSafe==2.1.3
@@ -203,7 +202,6 @@
python-3parclient==4.2.12
unittest2==1.1.0
django-compressor==4.4
-libvirt-python==8.10.0
python-zunclient==4.7.0
tzlocal==4.3.1
sphinxcontrib-jsmath==1.0.1
@@ -331,7 +329,7 @@
automaton==3.2.0
os-service-types==1.7.0
keyring==23.13.1
-elementpath==3.0.2
+elementpath==4.1.5
testscenarios==0.5.0
sphinxcontrib-pecanwsme==0.11.0
sadisplay==0.4.9
@@ -531,7 +529,7 @@
XStatic-Angular-lrdragndrop==1.0.2.6
ovsdbapp==2.5.0
aniso8601==9.0.1
-rjsmin==1.2.2
+rjsmin===1.2.1
icalendar==4.1.1
decorator==5.1.1
DateTimeRange==1.2.0
diff --git a/images/placement/Earthfile b/images/placement/Earthfile
index ed72871..81a3369 100644
--- a/images/placement/Earthfile
+++ b/images/placement/Earthfile
@@ -4,7 +4,11 @@
ARG PROJECT=placement
ARG RELEASE=zed
ARG REF=d7ced6bd2fc82caf458f20b5652888164b1bbb70
- FROM ../openstack-service+image --PROJECT ${PROJECT} --RELEASE ${RELEASE} --PROJECT_REF ${REF}
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF}
+ DO ../+APPLY_PATCHES
SAVE IMAGE --push \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/images/senlin/Earthfile b/images/senlin/Earthfile
index d2f2480..60da266 100644
--- a/images/senlin/Earthfile
+++ b/images/senlin/Earthfile
@@ -4,7 +4,11 @@
ARG PROJECT=senlin
ARG RELEASE=zed
ARG REF=b6ef17b0f787fb7a0609ba36dc13097882a6a3ff
- FROM ../openstack-service+image --PROJECT ${PROJECT} --RELEASE ${RELEASE} --PROJECT_REF ${REF}
+ FROM ../openstack-service+image \
+ --PROJECT ${PROJECT} \
+ --RELEASE ${RELEASE} \
+ --PROJECT_REF ${REF}
+ DO ../+APPLY_PATCHES
SAVE IMAGE --push \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${RELEASE} \
ghcr.io/vexxhost/atmosphere/${PROJECT}:${REF}
diff --git a/internal/pkg/image_repositories/build_workflow.go b/internal/pkg/image_repositories/build_workflow.go
deleted file mode 100644
index c1ad5dc..0000000
--- a/internal/pkg/image_repositories/build_workflow.go
+++ /dev/null
@@ -1,329 +0,0 @@
-package image_repositories
-
-import (
- "context"
- "fmt"
- "strings"
-)
-
-var FORKED_PROJECTS map[string]bool = map[string]bool{
- "horizon": true,
- "keystone": true,
- "magnum-ui": true,
- "magnum": true,
- "neutron": true,
-}
-var EXTRAS map[string]string = map[string]string{}
-var PROFILES map[string]string = map[string]string{
- "horizon": "apache",
- "ironic": "ipxe ipmi qemu tftp",
- "keystone": "apache ldap openidc",
- "neutron": "openvswitch vpn",
- "nova": "ceph openvswitch configdrive qemu migration",
-}
-var DIST_PACAKGES map[string]string = map[string]string{
- "designate": "bind9utils",
- "ironic": "ethtool lshw iproute2",
- "magnum": "haproxy",
- "manila": "iproute2 openvswitch-switch",
- "neutron": "jq ethtool lshw",
- "nova": "ovmf qemu-efi-aarch64 lsscsi nvme-cli sysfsutils udev util-linux ndctl python3-libvirt",
-}
-var PIP_PACKAGES map[string][]string = map[string][]string{
- "horizon": {"git+https://github.com/openstack/designate-dashboard.git@stable/${{ matrix.release }}", "git+https://github.com/openstack/heat-dashboard.git@stable/${{ matrix.release }}", "git+https://github.com/openstack/ironic-ui.git@stable/${{ matrix.release }}", "git+https://github.com/vexxhost/magnum-ui.git@stable/${{ matrix.release }} git+https://github.com/openstack/neutron-vpnaas-dashboard.git@stable/${{ matrix.release }} git+https://github.com/openstack/octavia-dashboard.git@stable/${{ matrix.release }} git+https://github.com/openstack/senlin-dashboard.git@stable/${{ matrix.release }}", "git+https://github.com/openstack/manila-ui.git@stable/${{ matrix.release }}"},
- "ironic": {"python-dracclient", "sushy"},
- "keystone": {"keystone-keycloak-backend==0.1.6"},
- "magnum": {"magnum-cluster-api==0.6.0"},
- "neutron": {"neutron-vpnaas"},
-}
-var PLATFORMS map[string]string = map[string]string{
- "nova": "linux/amd64,linux/arm64",
- "neutron": "linux/amd64,linux/arm64",
-}
-
-type ImageBuildArgs struct {
- BuilderImage string
- RuntimeImage string
- Release string
- Project string
- ProjectRepo string
- ProjectRef string
- Extras string
- Profiles string
- DistPackages string
- PipPackages []string
-}
-
-func (args *ImageBuildArgs) DeepCopy() *ImageBuildArgs {
- return &ImageBuildArgs{
- BuilderImage: args.BuilderImage,
- RuntimeImage: args.RuntimeImage,
- Release: args.Release,
- Project: args.Project,
- ProjectRepo: args.ProjectRepo,
- ProjectRef: args.ProjectRef,
- Extras: args.Extras,
- Profiles: args.Profiles,
- DistPackages: args.DistPackages,
- PipPackages: args.PipPackages,
- }
-}
-
-func (args *ImageBuildArgs) ToBuildArgs() []string {
- return []string{
- fmt.Sprintf("BUILDER_IMAGE=%s", args.BuilderImage),
- fmt.Sprintf("RUNTIME_IMAGE=%s", args.RuntimeImage),
- fmt.Sprintf("RELEASE=%s", args.Release),
- fmt.Sprintf("PROJECT=%s", args.Project),
- fmt.Sprintf("PROJECT_REPO=%s", args.ProjectRepo),
- fmt.Sprintf("PROJECT_REF=%s", args.ProjectRef),
- fmt.Sprintf("EXTRAS=%s", args.Extras),
- fmt.Sprintf("PROFILES=%s", args.Profiles),
- fmt.Sprintf("DIST_PACKAGES=%s", args.DistPackages),
- fmt.Sprintf("PIP_PACKAGES=%s", strings.Join(args.PipPackages, " ")),
- }
-}
-
-func (args *ImageBuildArgs) ToBuildArgsString() string {
- return strings.Join(args.ToBuildArgs(), "\n")
-}
-
-func NewBuildWorkflow(ctx context.Context, ir *ImageRepository) *GithubWorkflow {
- extras := ""
- project := ir.Project
- if val, ok := EXTRAS[project]; ok {
- extras = fmt.Sprintf("[%s]", val)
- }
-
- profiles := ""
- if val, ok := PROFILES[project]; ok {
- profiles = val
- }
-
- distPackages := ""
- if val, ok := DIST_PACAKGES[project]; ok {
- distPackages = val
- }
-
- pipPackages := []string{"cryptography", "python-binary-memcached"}
- if val, ok := PIP_PACKAGES[project]; ok {
- pipPackages = append(pipPackages, val...)
- }
-
- platforms := "linux/amd64"
- if val, ok := PLATFORMS[project]; ok {
- platforms = val
- }
-
- gitRepo := fmt.Sprintf("https://github.com/openstack/%s", project)
- if _, ok := FORKED_PROJECTS[project]; ok {
- gitRepo = fmt.Sprintf("https://github.com/vexxhost/%s", project)
- }
-
- builderImageTag, err := getImageTag(ctx, ir.githubClient, "docker-openstack-builder", "openstack-builder-focal")
- if err != nil {
- builderImageTag = "latest"
- }
-
- runtimeImageTag, err := getImageTag(ctx, ir.githubClient, "docker-openstack-runtime", "openstack-runtime-focal")
- if err != nil {
- runtimeImageTag = "latest"
- }
-
- imageBuildArgs := ImageBuildArgs{
- BuilderImage: fmt.Sprintf("quay.io/vexxhost/openstack-builder-${{ matrix.from }}:%s", builderImageTag),
- RuntimeImage: fmt.Sprintf("quay.io/vexxhost/openstack-runtime-${{ matrix.from }}:%s", runtimeImageTag),
- Release: "${{ matrix.release }}",
- Project: project,
- ProjectRepo: gitRepo,
- ProjectRef: "${{ env.PROJECT_REF }}",
- Extras: extras,
- Profiles: profiles,
- DistPackages: distPackages,
- PipPackages: pipPackages,
- }
- imageVerifyCmds := []string{
- fmt.Sprintf("cosign verify --certificate-oidc-issuer=https://token.actions.githubusercontent.com --certificate-identity=https://github.com/vexxhost/docker-openstack-builder/.github/workflows/build.yml@refs/heads/main quay.io/vexxhost/openstack-builder-${{ matrix.from }}:%s", builderImageTag),
- fmt.Sprintf("cosign verify --certificate-oidc-issuer=https://token.actions.githubusercontent.com --certificate-identity=https://github.com/vexxhost/docker-openstack-runtime/.github/workflows/build.yml@refs/heads/main quay.io/vexxhost/openstack-runtime-${{ matrix.from }}:%s", runtimeImageTag),
- }
-
- releases := []string{"wallaby", "xena", "yoga", "zed", "2023.1", "2023.2"}
- if project == "keystone" {
- releases = []string{"zed", "2023.1", "2023.2"}
- }
- if project == "magnum" {
- releases = []string{"yoga", "zed", "2023.1", "2023.2"}
- }
-
- workflow := &GithubWorkflow{
- Name: "build",
- Concurrency: GithubWorkflowConcurrency{
- Group: "${{ github.head_ref || github.run_id }}",
- CancelInProgress: true,
- },
- On: GithubWorkflowTrigger{
- PullRequest: GithubWorkflowPullRequest{
- Types: []string{"opened", "synchronize", "reopened"},
- },
- Push: GithubWorkflowPush{
- Branches: []string{"main"},
- },
- },
- Jobs: map[string]GithubWorkflowJob{
- "image": {
- RunsOn: "ubuntu-latest",
- Permissions: map[string]string{
- "actions": "read",
- "contents": "read",
- "id-token": "write",
- "security-events": "write",
- },
- Strategy: GithubWorkflowStrategy{
- Matrix: map[string]interface{}{
- "from": []string{"focal", "jammy"},
- "release": releases,
- "exclude": []map[string]string{
- {
- "from": "focal",
- "release": "zed",
- },
- {
- "from": "focal",
- "release": "2023.1",
- },
- {
- "from": "focal",
- "release": "2023.2",
- },
- {
- "from": "jammy",
- "release": "wallaby",
- },
- {
- "from": "jammy",
- "release": "xena",
- },
- },
- },
- },
- Steps: []GithubWorkflowStep{
- {
- Name: "Install QEMU static binaries",
- Uses: "docker/setup-qemu-action@v2",
- },
- {
- Name: "Configure Buildkit",
- Uses: "docker/setup-buildx-action@v2",
- },
- {
- Name: "Checkout project",
- Uses: "actions/checkout@v3",
- },
- {
- Name: "Setup environment variables",
- Run: "echo PROJECT_REF=$(cat manifest.yml | yq '.\"${{ matrix.release }}\".sha') >> $GITHUB_ENV",
- },
- {
- Name: "Authenticate with Quay.io",
- Uses: "docker/login-action@v2",
- If: "${{ github.event_name == 'push' }}",
- With: map[string]string{
- "registry": "quay.io",
- "username": "${{ secrets.QUAY_USERNAME }}",
- "password": "${{ secrets.QUAY_ROBOT_TOKEN }}",
- },
- },
- {
- Name: "Install cosign",
- Uses: "sigstore/cosign-installer@main",
- },
- {
- Name: "Verify images",
- Run: strings.Join(imageVerifyCmds, "\n"),
- },
- {
- Name: "Build image",
- Uses: "docker/build-push-action@v3",
- Environment: map[string]string{
- "DOCKER_CONTENT_TRUST": "1",
- },
- With: map[string]string{
- "context": ".",
- "cache-from": "type=gha,scope=${{ matrix.from }}-${{ matrix.release }}",
- "cache-to": "type=gha,mode=max,scope=${{ matrix.from }}-${{ matrix.release }}",
- "load": "true",
- "build-args": imageBuildArgs.ToBuildArgsString(),
- "tags": fmt.Sprintf("quay.io/vexxhost/%s:${{ env.PROJECT_REF }}-${{ matrix.from }}-${{ github.sha }}", project),
- },
- },
- {
- Name: "Scan image for vulnerabilities",
- Uses: "aquasecurity/trivy-action@master",
- With: map[string]string{
- "image-ref": fmt.Sprintf("quay.io/vexxhost/%s:${{ env.PROJECT_REF }}-${{ matrix.from }}-${{ github.sha }}", project),
- "format": "sarif",
- "output": "trivy-results.sarif",
- "ignore-unfixed": "true",
- },
- },
- {
- Name: "Upload scan result",
- Uses: "github/codeql-action/upload-sarif@v2",
- If: "always()",
- With: map[string]string{
- "category": "${{ env.PROJECT_REF }}-${{ matrix.from }}",
- "sarif_file": "trivy-results.sarif",
- },
- },
- {
- Name: "Build image",
- Uses: "docker/build-push-action@v3",
- Id: "push-step",
- Environment: map[string]string{
- "DOCKER_CONTENT_TRUST": "1",
- },
- With: map[string]string{
- "context": ".",
- "cache-from": "type=gha,scope=${{ matrix.from }}-${{ matrix.release }}",
- "cache-to": "type=gha,mode=max,scope=${{ matrix.from }}-${{ matrix.release }}",
- "platforms": platforms,
- "sbom": "true",
- "push": "${{ github.event_name == 'push' }}",
- "build-args": imageBuildArgs.ToBuildArgsString(),
- "tags": fmt.Sprintf("quay.io/vexxhost/%s:${{ env.PROJECT_REF }}-${{ matrix.from }}-${{ github.sha }}", project),
- },
- },
- {
- Name: "Promote image",
- Uses: "akhilerm/tag-push-action@v2.0.0",
- If: `github.event_name == 'push' && ((matrix.from == 'focal') || (matrix.from == 'jammy' && matrix.release != 'yoga'))`,
- With: map[string]string{
- "src": fmt.Sprintf("quay.io/vexxhost/%s:${{ env.PROJECT_REF }}-${{ matrix.from }}-${{ github.sha }}", project),
- "dst": fmt.Sprintf("quay.io/vexxhost/%s:${{ matrix.release }}", project),
- },
- },
- {
- Name: "Sign the container image",
- If: "${{ github.event_name == 'push' }}",
- Run: "cosign sign --yes quay.io/vexxhost/horizon@${{ steps.push-step.outputs.digest }}",
- },
- },
- },
- },
- }
-
- if project == "neutron" {
- infobloxImageBuildArgs := imageBuildArgs.DeepCopy()
- infobloxImageBuildArgs.PipPackages = append(infobloxImageBuildArgs.PipPackages, "networking-infoblox")
-
- workflow.Jobs["infoblox"] = workflow.Jobs["image"].DeepCopy()
- workflow.Jobs["infoblox"].Steps[10].With["cache-from"] += "-infoblox"
- workflow.Jobs["infoblox"].Steps[10].With["cache-to"] += "-infoblox"
- workflow.Jobs["infoblox"].Steps[10].With["tags"] = strings.ReplaceAll(workflow.Jobs["infoblox"].Steps[10].With["tags"], project, "neutron-infoblox")
- workflow.Jobs["infoblox"].Steps[10].With["build-args"] = infobloxImageBuildArgs.ToBuildArgsString()
- workflow.Jobs["infoblox"].Steps[11].With["src"] = strings.ReplaceAll(workflow.Jobs["infoblox"].Steps[11].With["src"], project, "neutron-infoblox")
- workflow.Jobs["infoblox"].Steps[11].With["dst"] = strings.ReplaceAll(workflow.Jobs["infoblox"].Steps[11].With["dst"], project, "neutron-infoblox")
- }
-
- return workflow
-}
diff --git a/internal/pkg/image_repositories/dockerfile.go b/internal/pkg/image_repositories/dockerfile.go
deleted file mode 100644
index 23df493..0000000
--- a/internal/pkg/image_repositories/dockerfile.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package image_repositories
-
-import (
- "context"
- _ "embed"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "text/template"
-
- "github.com/go-git/go-billy/v5"
- "github.com/google/go-github/v57/github"
-)
-
-//go:embed template/Dockerfile
-var dockerfileTemplate string
-
-type Dockerfile struct {
- Project string
-
- BindepImage string
- BindepImageTag string
-
- template *template.Template
-}
-
-func NewDockerfile(ctx context.Context, ir *ImageRepository) (*Dockerfile, error) {
- tmpl, err := template.New("Dockerfile").Parse(dockerfileTemplate)
- if err != nil {
- return nil, err
- }
-
- return &Dockerfile{
- Project: ir.Project,
-
- BindepImage: "quay.io/vexxhost/bindep-loci",
- BindepImageTag: "latest",
-
- template: tmpl,
- }, nil
-}
-
-type quayTagList struct {
- Tags []quayTag `json:"tags"`
- Page int `json:"page"`
- HasAdditional bool `json:"has_additional"`
-}
-
-type quayTag struct {
- Name string `json:"name"`
- Reversion bool `json:"reversion"`
- StartTimestamp int32 `json:"start_ts"`
- ManifestDigest string `json:"manifest_digest"`
- IsManifestList bool `json:"is_manifest_list"`
- Size int `json:"size"`
- LastModified string `json:"last_modified"`
-}
-
-func getImageTag(ctx context.Context, client *github.Client, repository string, image string) (string, error) {
- // Grab the latest SHA from the main branch
- commit, _, err := client.Repositories.GetCommitSHA1(ctx, "vexxhost", repository, "main", "")
- if err != nil {
- return "", err
- }
-
- // Check if the image exists in Quay.io
- url := fmt.Sprintf("https://quay.io/api/v1/repository/vexxhost/%s/tag/?specificTag=%s", image, commit)
- resp, err := http.Get(url)
- if err != nil {
- return "", err
- }
-
- // Decode the response
- var quayResponse quayTagList
- decoder := json.NewDecoder(resp.Body)
- err = decoder.Decode(&quayResponse)
- if err != nil {
- return "", err
- }
-
- // Check if the tag exists
- if len(quayResponse.Tags) == 0 {
- return "", fmt.Errorf("tag %s does not exist in quay.io/vexxhost/%s", commit, image)
- }
-
- return commit, nil
-}
-
-func (d *Dockerfile) Write(wr io.Writer) error {
- return d.template.Execute(wr, d)
-}
-
-func (d *Dockerfile) WriteFile(fs billy.Filesystem) error {
- f, err := fs.Create("Dockerfile")
- if err != nil {
- return err
- }
- defer f.Close()
-
- return d.Write(f)
-}
diff --git a/internal/pkg/image_repositories/dockerignore.go b/internal/pkg/image_repositories/dockerignore.go
deleted file mode 100644
index bc81f36..0000000
--- a/internal/pkg/image_repositories/dockerignore.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package image_repositories
-
-import (
- "io"
-
- "github.com/go-git/go-billy/v5"
-)
-
-type DockerIgnore struct {
-}
-
-func NewDockerIgnore() *DockerIgnore {
- return &DockerIgnore{}
-}
-
-func (d *DockerIgnore) Write(wr io.Writer) error {
- _, err := wr.Write([]byte("*\n"))
- return err
-}
-
-func (d *DockerIgnore) WriteFile(fs billy.Filesystem) error {
- f, err := fs.Create(".dockerignore")
- if err != nil {
- return err
- }
- defer f.Close()
-
- return d.Write(f)
-}
diff --git a/internal/pkg/image_repositories/github_workflow.go b/internal/pkg/image_repositories/github_workflow.go
deleted file mode 100644
index 2dc876d..0000000
--- a/internal/pkg/image_repositories/github_workflow.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package image_repositories
-
-import (
- "fmt"
- "io"
-
- "github.com/go-git/go-billy/v5"
- "github.com/goccy/go-yaml"
-)
-
-type GithubWorkflow struct {
- Name string `yaml:"name"`
- Concurrency GithubWorkflowConcurrency `yaml:"concurrency"`
- On GithubWorkflowTrigger `yaml:"on"`
- Jobs map[string]GithubWorkflowJob `yaml:"jobs"`
-}
-
-type GithubWorkflowTrigger struct {
- PullRequest GithubWorkflowPullRequest `yaml:"pull_request,omitempty"`
- Push GithubWorkflowPush `yaml:"push,omitempty"`
- Schedule []GithubWorkflowSchedule `yaml:"schedule,omitempty"`
- WorkflowDispatch GithubWorkflowDispatch `yaml:"workflow_dispatch,omitempty"`
-}
-
-type GithubWorkflowPullRequest struct {
- Types []string `yaml:"types,omitempty"`
-}
-
-type GithubWorkflowPush struct {
- Branches []string `yaml:"branches"`
-}
-
-type GithubWorkflowSchedule struct {
- Cron string `yaml:"cron"`
-}
-
-type GithubWorkflowDispatch struct {
- Inputs map[string]GithubWorkflowDispatchInput `yaml:"inputs,omitempty"`
-}
-
-type GithubWorkflowDispatchInput struct {
- Description string `yaml:"description"`
- Required bool `yaml:"required"`
- Default string `yaml:"default"`
-}
-
-type GithubWorkflowConcurrency struct {
- Group string `yaml:"group"`
- CancelInProgress bool `yaml:"cancel-in-progress"`
-}
-
-type GithubWorkflowJob struct {
- Permissions map[string]string `yaml:"permissions,omitempty"`
- RunsOn string `yaml:"runs-on"`
- Strategy GithubWorkflowStrategy `yaml:"strategy,omitempty"`
- Steps []GithubWorkflowStep `yaml:"steps"`
-}
-
-func (j GithubWorkflowJob) DeepCopy() GithubWorkflowJob {
- job := GithubWorkflowJob{}
- job.RunsOn = j.RunsOn
- job.Strategy = j.Strategy.DeepCopy()
-
- job.Steps = make([]GithubWorkflowStep, len(j.Steps))
- for i, step := range j.Steps {
- job.Steps[i] = step.DeepCopy()
- }
-
- return job
-}
-
-type GithubWorkflowStrategy struct {
- Matrix map[string]interface{} `yaml:"matrix"`
-}
-
-func (s *GithubWorkflowStrategy) DeepCopy() GithubWorkflowStrategy {
- strategy := *s
- strategy.Matrix = make(map[string]interface{})
- for k, v := range s.Matrix {
- strategy.Matrix[k] = v
- }
-
- return strategy
-}
-
-type GithubWorkflowStep struct {
- Name string `yaml:"name"`
- Id string `yaml:"id,omitempty"`
- Run string `yaml:"run,omitempty"`
- Uses string `yaml:"uses,omitempty"`
- If string `yaml:"if,omitempty"`
- With map[string]string `yaml:"with,omitempty"`
- Environment map[string]string `yaml:"env,omitempty"`
-}
-
-func (s *GithubWorkflowStep) DeepCopy() GithubWorkflowStep {
- step := *s
- step.With = make(map[string]string)
- for k, v := range s.With {
- step.With[k] = v
- }
-
- step.Environment = make(map[string]string)
- for k, v := range s.Environment {
- step.Environment[k] = v
- }
-
- return step
-}
-
-func (g *GithubWorkflow) Write(wr io.Writer) error {
- bytes, err := yaml.Marshal(g)
- if err != nil {
- return err
- }
-
- _, err = wr.Write(bytes)
- return err
-}
-
-func (g *GithubWorkflow) WriteFile(fs billy.Filesystem) error {
- file := fmt.Sprintf(".github/workflows/%s.yml", g.Name)
-
- f, err := fs.Create(file)
- if err != nil {
- return err
- }
- defer f.Close()
-
- return g.Write(f)
-}
diff --git a/internal/pkg/image_repositories/image_repository.go b/internal/pkg/image_repositories/image_repository.go
deleted file mode 100644
index b020281..0000000
--- a/internal/pkg/image_repositories/image_repository.go
+++ /dev/null
@@ -1,349 +0,0 @@
-package image_repositories
-
-import (
- "context"
- "fmt"
- "net/http"
- "os"
- "time"
-
- "github.com/go-git/go-billy/v5"
- "github.com/go-git/go-billy/v5/memfs"
- "github.com/go-git/go-git/v5"
- "github.com/go-git/go-git/v5/config"
- "github.com/go-git/go-git/v5/plumbing"
- "github.com/go-git/go-git/v5/plumbing/object"
- git_http "github.com/go-git/go-git/v5/plumbing/transport/http"
- "github.com/go-git/go-git/v5/storage/memory"
- "github.com/google/go-github/v57/github"
- log "github.com/sirupsen/logrus"
- "golang.org/x/oauth2"
-)
-
-type ImageRepository struct {
- Project string
-
- githubClient *github.Client
- githubProjectName string
- gitAuth *git_http.BasicAuth
-}
-
-func NewImageRepository(project string) *ImageRepository {
- githubToken := os.Getenv("GITHUB_TOKEN")
-
- ctx := context.TODO()
- ts := oauth2.StaticTokenSource(
- &oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
- )
- tc := oauth2.NewClient(ctx, ts)
-
- return &ImageRepository{
- Project: project,
-
- githubClient: github.NewClient(tc),
- githubProjectName: fmt.Sprintf("docker-openstack-%s", project),
- gitAuth: &git_http.BasicAuth{
- Username: "x-access-token",
- Password: githubToken,
- },
- }
-}
-
-func (i *ImageRepository) WriteFiles(ctx context.Context, fs billy.Filesystem) error {
- // .github/workflows/build.yml
- build := NewBuildWorkflow(ctx, i)
- err := build.WriteFile(fs)
- if err != nil {
- return err
- }
-
- // .github/workflows/sync.yml
- sync := NewSyncWorkflow(i.Project)
- err = sync.WriteFile(fs)
- if err != nil {
- return err
- }
-
- // .dockerignore
- di := NewDockerIgnore()
- err = di.WriteFile(fs)
- if err != nil {
- return err
- }
-
- // .pre-commit-config.yaml
- pcc := NewPreCommitConfig()
- err = pcc.WriteFile(fs)
- if err != nil {
- return err
- }
-
- // Dockerfile
- df, err := NewDockerfile(ctx, i)
- if err != nil {
- return err
- }
- err = df.WriteFile(fs)
- if err != nil {
- return err
- }
-
- // manifest.yml
- mf, err := NewImageManifest(i.Project, i.githubClient)
- if err != nil {
- return err
- }
- err = mf.WriteFile(fs)
- if err != nil {
- return err
- }
-
- // README.md
- rm, err := NewReadme(i.Project)
- if err != nil {
- return err
- }
- err = rm.WriteFile(fs)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (i *ImageRepository) CreateGithubRepository(ctx context.Context) error {
- repo := &github.Repository{
- Name: github.String(i.githubProjectName),
- AutoInit: github.Bool(true),
- }
-
- _, _, err := i.githubClient.Repositories.Create(ctx, "vexxhost", repo)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (i *ImageRepository) GetGitHubRepository(ctx context.Context, owner string) (*github.Repository, error) {
- repo, _, err := i.githubClient.Repositories.Get(ctx, owner, i.githubProjectName)
- if err != nil {
- return nil, err
- }
-
- return repo, nil
-}
-
-func (i *ImageRepository) ForkGitHubRepository(ctx context.Context) (*github.Repository, error) {
- repo, err := i.GetGitHubRepository(ctx, "vexxhost-bot")
- if err != nil {
- i.githubClient.Repositories.CreateFork(ctx, "vexxhost", i.githubProjectName, nil)
- time.Sleep(20 * time.Second)
- return i.GetGitHubRepository(ctx, "vexxhost-bot")
- }
-
- return repo, nil
-}
-
-func (i *ImageRepository) UpdateGithubConfiguration(ctx context.Context) error {
- // Description
- description := fmt.Sprintf("Docker image for OpenStack: %s", i.Project)
-
- // Updated repository
- repo := &github.Repository{
- AllowMergeCommit: github.Bool(false),
- AllowRebaseMerge: github.Bool(true),
- AllowSquashMerge: github.Bool(false),
- DeleteBranchOnMerge: github.Bool(true),
- Description: github.String(description),
- Visibility: github.String("public"),
- HasWiki: github.Bool(false),
- HasIssues: github.Bool(false),
- HasProjects: github.Bool(false),
- }
-
- // Update the repository with the correct settings
- repo, _, err := i.githubClient.Repositories.Edit(ctx, "vexxhost", i.githubProjectName, repo)
- if err != nil {
- return err
- }
-
- // Branch protection
- protection := &github.ProtectionRequest{
- RequiredPullRequestReviews: &github.PullRequestReviewsEnforcementRequest{
- RequiredApprovingReviewCount: 1,
- DismissStaleReviews: true,
- BypassPullRequestAllowancesRequest: &github.BypassPullRequestAllowancesRequest{
- Users: []string{"mnaser"},
- Teams: []string{},
- Apps: []string{},
- },
- },
- RequiredStatusChecks: &github.RequiredStatusChecks{
- Strict: true,
- Contexts: nil,
- Checks: []*github.RequiredStatusCheck{
- {
- Context: "image (wallaby)",
- },
- {
- Context: "image (xena)",
- },
- {
- Context: "image (yoga)",
- },
- {
- Context: "image (zed)",
- },
- },
- },
- RequiredConversationResolution: github.Bool(true),
- RequireLinearHistory: github.Bool(true),
- EnforceAdmins: false,
- AllowForcePushes: github.Bool(false),
- AllowDeletions: github.Bool(false),
- }
- _, _, err = i.githubClient.Repositories.UpdateBranchProtection(ctx, *repo.Owner.Login, *repo.Name, "main", protection)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (i *ImageRepository) Synchronize(ctx context.Context, admin bool) error {
- var githubRepo *github.Repository
- var err error
-
- if admin {
- githubRepo, err = i.GetGitHubRepository(ctx, "vexxhost")
- } else {
- githubRepo, err = i.ForkGitHubRepository(ctx)
- }
-
- if err != nil {
- return err
- }
-
- storer := memory.NewStorage()
- fs := memfs.New()
-
- upstreamUrl := fmt.Sprintf("https://github.com/vexxhost/%s.git", i.githubProjectName)
- repo, err := git.Clone(storer, fs, &git.CloneOptions{
- Auth: i.gitAuth,
- URL: upstreamUrl,
- RemoteName: "upstream",
- })
- if err != nil {
- return err
- }
-
- _, err = repo.CreateRemote(&config.RemoteConfig{
- Name: "origin",
- URLs: []string{*githubRepo.CloneURL},
- })
- if err != nil {
- return err
- }
-
- headRef, err := repo.Head()
- if err != nil {
- return err
- }
-
- ref := plumbing.NewHashReference("refs/heads/sync/atmosphere-ci", headRef.Hash())
- err = repo.Storer.SetReference(ref)
- if err != nil {
- return err
- }
-
- worktree, err := repo.Worktree()
- if err != nil {
- return err
- }
-
- err = worktree.Checkout(&git.CheckoutOptions{
- Branch: ref.Name(),
- })
- if err != nil {
- return err
- }
-
- err = i.WriteFiles(ctx, fs)
- if err != nil {
- return err
- }
-
- status, err := worktree.Status()
- if err != nil {
- return err
- }
-
- if status.IsClean() {
- log.Info("No changes to commit")
- return nil
- }
-
- _, err = worktree.Add(".")
- if err != nil {
- return err
- }
-
- commit, err := worktree.Commit("chore: sync using `atmosphere-ci`", &git.CommitOptions{
- All: true,
- Author: &object.Signature{
- Name: "vexxhost-bot",
- Email: "mnaser+bot@vexxhost.com",
- When: time.Now(),
- },
- })
- if err != nil {
- return err
- }
-
- err = repo.Push(&git.PushOptions{
- Auth: i.gitAuth,
- RefSpecs: []config.RefSpec{"refs/heads/*:refs/heads/*"},
- RemoteName: "origin",
- Force: true,
- })
- if err != nil {
- return err
- }
-
- err = i.CreatePullRequest(ctx, githubRepo, commit)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (i *ImageRepository) CreatePullRequest(ctx context.Context, repo *github.Repository, commit plumbing.Hash) error {
- head := fmt.Sprintf("%s:%s", *repo.Owner.Login, "sync/atmosphere-ci")
-
- newPR := &github.NewPullRequest{
- Title: github.String("⚙️ Automatic sync from `atmosphere-ci`"),
- Head: github.String(head),
- Base: github.String("main"),
- Body: github.String("This is an automatic pull request from `atmosphere-ci`"),
- }
-
- prs, _, err := i.githubClient.PullRequests.ListPullRequestsWithCommit(ctx, *repo.Owner.Login, *repo.Name, commit.String(), &github.ListOptions{})
- if err != nil {
- return err
- }
-
- if len(prs) > 0 {
- log.Info("Pull request already exists: ", prs[0].GetHTMLURL())
- return nil
- }
-
- pr, resp, err := i.githubClient.PullRequests.Create(ctx, "vexxhost", *repo.Name, newPR)
- if err != nil && resp.StatusCode != http.StatusUnprocessableEntity {
- return err
- }
-
- log.Info("PR created: ", pr.GetHTMLURL())
- return nil
-}
diff --git a/internal/pkg/image_repositories/manifest.go b/internal/pkg/image_repositories/manifest.go
deleted file mode 100644
index 9054090..0000000
--- a/internal/pkg/image_repositories/manifest.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package image_repositories
-
-import (
- "context"
- "fmt"
- "io"
-
- "github.com/go-git/go-billy/v5"
- "github.com/goccy/go-yaml"
- "github.com/google/go-github/v57/github"
-)
-
-type ReleaseManifest struct {
- SHA string `json:"sha"`
-}
-
-type ImageManifest struct {
- Wallaby *ReleaseManifest `yaml:"wallaby"`
- Xena *ReleaseManifest `yaml:"xena"`
- Yoga *ReleaseManifest `yaml:"yoga"`
- Zed *ReleaseManifest `yaml:"zed"`
- Antelope *ReleaseManifest `yaml:"2023.1"`
- Bobcat *ReleaseManifest `yaml:"2023.2"`
-}
-
-func NewImageManifest(project string, client *github.Client) (*ImageManifest, error) {
- wallaby, err := getReleaseManifest(client, project, "wallaby")
- if err != nil {
- return nil, err
- }
-
- xena, err := getReleaseManifest(client, project, "xena")
- if err != nil {
- return nil, err
- }
-
- yoga, err := getReleaseManifest(client, project, "yoga")
- if err != nil {
- return nil, err
- }
-
- zed, err := getReleaseManifest(client, project, "zed")
- if err != nil {
- return nil, err
- }
-
- antelope, err := getReleaseManifest(client, project, "2023.1")
- if err != nil {
- return nil, err
- }
-
- bobcat, err := getReleaseManifest(client, project, "2023.2")
- if err != nil {
- return nil, err
- }
-
- return &ImageManifest{
- Wallaby: wallaby,
- Xena: xena,
- Yoga: yoga,
- Zed: zed,
- Antelope: antelope,
- Bobcat: bobcat,
- }, nil
-}
-
-func (m *ImageManifest) Write(wr io.Writer) error {
- bytes, err := yaml.Marshal(m)
- if err != nil {
- return err
- }
-
- _, err = wr.Write(bytes)
- return err
-}
-
-func (m *ImageManifest) WriteFile(fs billy.Filesystem) error {
- f, err := fs.Create("manifest.yml")
- if err != nil {
- return err
- }
- defer f.Close()
-
- return m.Write(f)
-}
-
-func getReleaseManifest(client *github.Client, project, release string) (*ReleaseManifest, error) {
- branchName := fmt.Sprintf("stable/%s", release)
-
- gitOrg := "openstack"
- if _, ok := FORKED_PROJECTS[project]; ok {
- gitOrg = "vexxhost"
- }
-
- branch, _, err := client.Repositories.GetBranch(context.TODO(), gitOrg, project, branchName, 2)
- if err != nil {
- return nil, err
- }
-
- return &ReleaseManifest{
- SHA: branch.GetCommit().GetSHA(),
- }, nil
-}
diff --git a/internal/pkg/image_repositories/pre_commit_config.go b/internal/pkg/image_repositories/pre_commit_config.go
deleted file mode 100644
index 7c98576..0000000
--- a/internal/pkg/image_repositories/pre_commit_config.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package image_repositories
-
-import (
- "io"
-
- "github.com/go-git/go-billy/v5"
- "github.com/goccy/go-yaml"
-)
-
-type PreCommitConfig struct {
- Repositories []PreCommitRepository `yaml:"repos"`
-}
-
-type PreCommitRepository struct {
- Repository string `yaml:"repo"`
- Revision string `yaml:"rev"`
- Hooks []PreCommitHook `yaml:"hooks"`
-}
-
-type PreCommitHook struct {
- ID string `yaml:"id"`
- Stages []string `yaml:"stages"`
-}
-
-func NewPreCommitConfig() *PreCommitConfig {
- return &PreCommitConfig{
- Repositories: []PreCommitRepository{
- {
- Repository: "https://github.com/compilerla/conventional-pre-commit",
- Revision: "v2.0.0",
- Hooks: []PreCommitHook{
- {
- ID: "conventional-pre-commit",
- Stages: []string{"commit-msg"},
- },
- },
- },
- },
- }
-}
-
-func (c *PreCommitConfig) Write(wr io.Writer) error {
- bytes, err := yaml.Marshal(c)
- if err != nil {
- return err
- }
-
- _, err = wr.Write(bytes)
- return err
-}
-
-func (c *PreCommitConfig) WriteFile(fs billy.Filesystem) error {
- f, err := fs.Create(".pre-commit-config.yaml")
- if err != nil {
- return err
- }
- defer f.Close()
-
- return c.Write(f)
-}
diff --git a/internal/pkg/image_repositories/readme.go b/internal/pkg/image_repositories/readme.go
deleted file mode 100644
index 8bd3890..0000000
--- a/internal/pkg/image_repositories/readme.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package image_repositories
-
-import (
- _ "embed"
- "io"
- "text/template"
-
- "github.com/go-git/go-billy/v5"
-)
-
-//go:embed template/README.md
-var readmeTemplate string
-
-type Readme struct {
- Project string
-
- template *template.Template
-}
-
-func NewReadme(project string) (*Readme, error) {
- tmpl, err := template.New("README.md").Parse(readmeTemplate)
- if err != nil {
- return nil, err
- }
-
- return &Readme{
- Project: project,
-
- template: tmpl,
- }, nil
-}
-
-func (r *Readme) Write(wr io.Writer) error {
- return r.template.Execute(wr, r)
-}
-
-func (r *Readme) WriteFile(fs billy.Filesystem) error {
- f, err := fs.Create("README.md")
- if err != nil {
- return err
- }
- defer f.Close()
-
- return r.Write(f)
-}
diff --git a/internal/pkg/image_repositories/sync_workflow.go b/internal/pkg/image_repositories/sync_workflow.go
deleted file mode 100644
index 5e9eede..0000000
--- a/internal/pkg/image_repositories/sync_workflow.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package image_repositories
-
-import "fmt"
-
-func NewSyncWorkflow(project string) *GithubWorkflow {
- return &GithubWorkflow{
- Name: "sync",
- Concurrency: GithubWorkflowConcurrency{
- Group: "sync",
- CancelInProgress: true,
- },
- On: GithubWorkflowTrigger{
- WorkflowDispatch: GithubWorkflowDispatch{
- Inputs: map[string]GithubWorkflowDispatchInput{
- "ref": {
- Description: "Atmosphere branch, tag or SHA to checkout",
- Required: true,
- Default: "main",
- },
- },
- },
- Schedule: []GithubWorkflowSchedule{
- {
- Cron: "0 0 * * *",
- },
- },
- },
- Jobs: map[string]GithubWorkflowJob{
- "image": {
- RunsOn: "ubuntu-latest",
- Steps: []GithubWorkflowStep{
- {
- Name: "Checkout Atmosphere",
- Uses: "actions/checkout@v3",
- With: map[string]string{
- "repository": "vexxhost/atmosphere",
- "ref": "${{ inputs.ref || 'main' }}",
- },
- },
- {
- Name: "Setup Go",
- Uses: "actions/setup-go@v3",
- With: map[string]string{
- "go-version-file": "go.mod",
- "cache": "true",
- },
- },
- {
- Name: "Synchronize Image Repository",
- Run: fmt.Sprintf("go run ./cmd/atmosphere-ci image repo sync %s", project),
- Environment: map[string]string{
- "GITHUB_TOKEN": "${{ secrets.BOT_TOKEN }}",
- },
- },
- },
- },
- },
- }
-}
diff --git a/internal/pkg/image_repositories/template/Dockerfile b/internal/pkg/image_repositories/template/Dockerfile
deleted file mode 100644
index 830a633..0000000
--- a/internal/pkg/image_repositories/template/Dockerfile
+++ /dev/null
@@ -1,35 +0,0 @@
-# syntax=docker/dockerfile-upstream:master-labs
-
-ARG BUILDER_IMAGE
-ARG RUNTIME_IMAGE
-
-FROM {{ .BindepImage }}:{{ .BindepImageTag }} AS bindep
-
-FROM ${BUILDER_IMAGE} AS builder
-COPY --from=bindep --link /runtime-pip-packages /runtime-pip-packages
-
-FROM ${RUNTIME_IMAGE} AS runtime
-COPY --from=bindep --link /runtime-dist-packages /runtime-dist-packages
-COPY --from=builder --link /var/lib/openstack /var/lib/openstack
-
-{{- if eq .Project "nova" }}
-ADD https://github.com/novnc/novnc.git#v1.3.0 /usr/share/novnc
-{{- else if eq .Project "keystone" }}
-RUN <<EOF /bin/bash
- set -xe
- apt-get update
- apt-get install -y --no-install-recommends wget
- wget --no-check-certificate \
- https://github.com/zmartzone/mod_auth_openidc/releases/download/v2.4.12.1/libapache2-mod-auth-openidc_2.4.12.1-1.$(lsb_release -sc)_amd64.deb
- apt-get -y --no-install-recommends install \
- ./libapache2-mod-auth-openidc_2.4.12.1-1.$(lsb_release -sc)_amd64.deb
- rm -rfv \
- ./libapache2-mod-auth-openidc_2.4.12.1-1.$(lsb_release -sc)_amd64.deb
- apt-get purge -y wget
- apt-get clean
- rm -rf /var/lib/apt/lists/*
-EOF
-{{- else if eq .Project "magnum" }}
-COPY --from=docker.io/alpine/helm:3.11.2 /usr/bin/helm /usr/local/bin/helm
-COPY --from=gcr.io/go-containerregistry/crane /ko-app/crane /usr/local/bin/crane
-{{- end }}
diff --git a/internal/pkg/image_repositories/template/README.md b/internal/pkg/image_repositories/template/README.md
deleted file mode 100644
index 89a7faf..0000000
--- a/internal/pkg/image_repositories/template/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# OpenStack Image for `{{ .Project }}`
-
-This is an automatically generated and synchronzied repository for the
-`{{ .Project }}` project images. The repository is built using the
-`atmosphere-ci` tool that lives inside of the
-[Atmosphere](https://github.com/vexxhost/atmosphere) project.
-
-If you need to make any changes or files issues, please propose them to the
-[Atmosphere](https://github.com/vexxhost/atmosphere) repository or else they
-will be wiped out the next time the repository is synchronized.
diff --git a/roles/defaults/vars/main.yml b/roles/defaults/vars/main.yml
index 997e4f0..142263c 100644
--- a/roles/defaults/vars/main.yml
+++ b/roles/defaults/vars/main.yml
@@ -14,9 +14,9 @@
_atmosphere_images:
alertmanager: quay.io/prometheus/alertmanager:v0.26.0@sha256:361db356b33041437517f1cd298462055580585f26555c317df1a3caf2868552
- barbican_api: quay.io/vexxhost/barbican:zed@sha256:fa04a817738e72cf9cba1582728c5463c7e33acf796aed4b2978af1414701a63
- barbican_db_sync: quay.io/vexxhost/barbican:zed@sha256:fa04a817738e72cf9cba1582728c5463c7e33acf796aed4b2978af1414701a63
- bootstrap: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
+ barbican_api: ghcr.io/vexxhost/atmosphere/barbican:zed@sha256:68dc05c8b442139eea02db323361ea8be8cfe8b7ab672321bc27c2e1ff82a946
+ barbican_db_sync: ghcr.io/vexxhost/atmosphere/barbican:zed@sha256:68dc05c8b442139eea02db323361ea8be8cfe8b7ab672321bc27c2e1ff82a946
+ bootstrap: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
ceph_config_helper: quay.io/vexxhost/libvirtd:zed@sha256:480d8736954cdc01c1d6f0c625ba147935ce4e5af25828f6d3fbcd18e6dc283a
ceph: quay.io/ceph/ceph:v16.2.11@sha256:1b9803c8984bef8b82f05e233e8fe8ed8f0bba8e5cc2c57f6efaccbeea682add
cert_manager_cainjector: quay.io/jetstack/cert-manager-cainjector:v1.7.1@sha256:985743eeed2b62f68ee06e583f1d5a371e1c35af4b1980a1b2571d29174cce47
@@ -25,67 +25,67 @@
cert_manager_webhook: quay.io/jetstack/cert-manager-webhook:v1.7.1@sha256:a926d60b6f23553ca5d11ac9cd66bcc692136e838613c8bc0d60c6c35a3cbcfc
cilium_node: quay.io/cilium/cilium:v1.13.3@sha256:77176464a1e11ea7e89e984ac7db365e7af39851507e94f137dcf56c87746314
cilium_operator: quay.io/cilium/operator-generic:v1.13.3@sha256:fa7003cbfdf8358cb71786afebc711b26e5e44a2ed99bd4944930bba915b8910
- cinder_api: quay.io/vexxhost/cinder:zed@sha256:39f8d16322fc84a8e9a3084cb35e85450451a33583e121e8a73e38373eb6d582
- cinder_backup_storage_init: quay.io/vexxhost/cinder:zed@sha256:39f8d16322fc84a8e9a3084cb35e85450451a33583e121e8a73e38373eb6d582
- cinder_backup: quay.io/vexxhost/cinder:zed@sha256:39f8d16322fc84a8e9a3084cb35e85450451a33583e121e8a73e38373eb6d582
- cinder_db_sync: quay.io/vexxhost/cinder:zed@sha256:39f8d16322fc84a8e9a3084cb35e85450451a33583e121e8a73e38373eb6d582
- cinder_scheduler: quay.io/vexxhost/cinder:zed@sha256:39f8d16322fc84a8e9a3084cb35e85450451a33583e121e8a73e38373eb6d582
- cinder_storage_init: quay.io/vexxhost/cinder:zed@sha256:39f8d16322fc84a8e9a3084cb35e85450451a33583e121e8a73e38373eb6d582
- cinder_volume_usage_audit: quay.io/vexxhost/cinder:zed@sha256:39f8d16322fc84a8e9a3084cb35e85450451a33583e121e8a73e38373eb6d582
- cinder_volume: quay.io/vexxhost/cinder:zed@sha256:39f8d16322fc84a8e9a3084cb35e85450451a33583e121e8a73e38373eb6d582
+ cinder_api: ghcr.io/vexxhost/atmosphere/cinder:zed@sha256:df9d19fbe31ae2e2ebd5ad8c42311e0c4785fc422abaee68b42274b0b08ab040
+ cinder_backup_storage_init: ghcr.io/vexxhost/atmosphere/cinder:zed@sha256:df9d19fbe31ae2e2ebd5ad8c42311e0c4785fc422abaee68b42274b0b08ab040
+ cinder_backup: ghcr.io/vexxhost/atmosphere/cinder:zed@sha256:df9d19fbe31ae2e2ebd5ad8c42311e0c4785fc422abaee68b42274b0b08ab040
+ cinder_db_sync: ghcr.io/vexxhost/atmosphere/cinder:zed@sha256:df9d19fbe31ae2e2ebd5ad8c42311e0c4785fc422abaee68b42274b0b08ab040
+ cinder_scheduler: ghcr.io/vexxhost/atmosphere/cinder:zed@sha256:df9d19fbe31ae2e2ebd5ad8c42311e0c4785fc422abaee68b42274b0b08ab040
+ cinder_storage_init: ghcr.io/vexxhost/atmosphere/cinder:zed@sha256:df9d19fbe31ae2e2ebd5ad8c42311e0c4785fc422abaee68b42274b0b08ab040
+ cinder_volume_usage_audit: ghcr.io/vexxhost/atmosphere/cinder:zed@sha256:df9d19fbe31ae2e2ebd5ad8c42311e0c4785fc422abaee68b42274b0b08ab040
+ cinder_volume: ghcr.io/vexxhost/atmosphere/cinder:zed@sha256:df9d19fbe31ae2e2ebd5ad8c42311e0c4785fc422abaee68b42274b0b08ab040
cluster_api_controller: registry.k8s.io/cluster-api/cluster-api-controller:v1.5.1@sha256:5210087161fdc09c98e47f847c07ed3ff93470e774cb1d5a792e2f76eaa5cf12
cluster_api_kubeadm_bootstrap_controller: registry.k8s.io/cluster-api/kubeadm-bootstrap-controller:v1.5.1@sha256:6d73f991862d0df9724fab31a4a694681d9181e772c265d2c5b9b0b26b572479
cluster_api_kubeadm_control_plane_controller: registry.k8s.io/cluster-api/kubeadm-control-plane-controller:v1.5.1@sha256:8d926bcd3e0ca6be6cb9212f692f0ea6790f83862f4dc02fae0c7e0b35e76907
- cluster_api_openstack_controller: ghcr.io/vexxhost/atmosphere/cluster-api-provider-openstack:v0.8.0-1@sha256:1d0edaf4a1c3b3663ddf6efd4629cf5ee3075066ba406e0cfa1a068ea9268c56
+ cluster_api_openstack_controller: ghcr.io/vexxhost/atmosphere/capi-openstack-controller:v0.8.0-1@sha256:906a42a241c49980c2d66cfb14850eb4e1ad53087f23004bbdb6a89b627ae4ea
csi_node_driver_registrar: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.4.0@sha256:0174bf20d7ad8e9f131a045802ef1c43b4592a2ebc18ba07972b1ce8858d9cb7
csi_rbd_attacher: registry.k8s.io/sig-storage/csi-attacher:v3.4.0@sha256:adc2922c98c539f680c02af99042d968114746f973a49b529785d6b402134bbf
csi_rbd_plugin: quay.io/cephcsi/cephcsi:v3.5.1@sha256:28a674af1df2325fea415e32a7f93f083fce1f9c474912c45f025427fdc0aa10
csi_rbd_provisioner: registry.k8s.io/sig-storage/csi-provisioner:v3.1.0@sha256:92107bb668a9de58a09247596c337bc5b46a1d145685eb55ef489ae16952f5bd
csi_rbd_resizer: registry.k8s.io/sig-storage/csi-resizer:v1.3.0@sha256:35ec0c736ec8266bd4a46f9e942315f148f3139beed99879d0ad8b8e5074d641
csi_rbd_snapshotter: registry.k8s.io/sig-storage/csi-snapshotter:v4.2.0@sha256:bd7dafbd0d4fe81f23f01c9a7432de067bdf085f70d61492f5ffddd9c5264358
- db_drop: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- db_init: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- dep_check: quay.io/vexxhost/kubernetes-entrypoint:latest@sha256:ad430737657f9e10ae5ac5708cd7f94dcec6630cfbdd158520b99b7285e253da
- designate_api: quay.io/vexxhost/designate:zed@sha256:d65b4d717f81172c63b87bdf85a5db86aedd450f3510b2685ae384c2b114acc8
- designate_central: quay.io/vexxhost/designate:zed@sha256:d65b4d717f81172c63b87bdf85a5db86aedd450f3510b2685ae384c2b114acc8
- designate_db_sync: quay.io/vexxhost/designate:zed@sha256:d65b4d717f81172c63b87bdf85a5db86aedd450f3510b2685ae384c2b114acc8
- designate_mdns: quay.io/vexxhost/designate:zed@sha256:d65b4d717f81172c63b87bdf85a5db86aedd450f3510b2685ae384c2b114acc8
- designate_producer: quay.io/vexxhost/designate:zed@sha256:d65b4d717f81172c63b87bdf85a5db86aedd450f3510b2685ae384c2b114acc8
- designate_sink: quay.io/vexxhost/designate:zed@sha256:d65b4d717f81172c63b87bdf85a5db86aedd450f3510b2685ae384c2b114acc8
- designate_worker: quay.io/vexxhost/designate:zed@sha256:d65b4d717f81172c63b87bdf85a5db86aedd450f3510b2685ae384c2b114acc8
- glance_api: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:13024c9989fc27a63d17e467ca96ea6ec62fc34abaaccdfe05dd0445f10e0445
- glance_db_sync: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:13024c9989fc27a63d17e467ca96ea6ec62fc34abaaccdfe05dd0445f10e0445
- glance_metadefs_load: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:13024c9989fc27a63d17e467ca96ea6ec62fc34abaaccdfe05dd0445f10e0445
- glance_registry: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:13024c9989fc27a63d17e467ca96ea6ec62fc34abaaccdfe05dd0445f10e0445
- glance_storage_init: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:13024c9989fc27a63d17e467ca96ea6ec62fc34abaaccdfe05dd0445f10e0445
+ db_drop: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ db_init: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ dep_check: ghcr.io/vexxhost/atmosphere/kubernetes-entrypoint:latest@sha256:a1993b58da2afb16b44d4e510bd217ab872a4c10f598909edc39e72cda92f0b5
+ designate_api: ghcr.io/vexxhost/atmosphere/designate:zed@sha256:6c024e2e600745171c032794f807d6cea45104f10569086a3a198034c6e5145f
+ designate_central: ghcr.io/vexxhost/atmosphere/designate:zed@sha256:6c024e2e600745171c032794f807d6cea45104f10569086a3a198034c6e5145f
+ designate_db_sync: ghcr.io/vexxhost/atmosphere/designate:zed@sha256:6c024e2e600745171c032794f807d6cea45104f10569086a3a198034c6e5145f
+ designate_mdns: ghcr.io/vexxhost/atmosphere/designate:zed@sha256:6c024e2e600745171c032794f807d6cea45104f10569086a3a198034c6e5145f
+ designate_producer: ghcr.io/vexxhost/atmosphere/designate:zed@sha256:6c024e2e600745171c032794f807d6cea45104f10569086a3a198034c6e5145f
+ designate_sink: ghcr.io/vexxhost/atmosphere/designate:zed@sha256:6c024e2e600745171c032794f807d6cea45104f10569086a3a198034c6e5145f
+ designate_worker: ghcr.io/vexxhost/atmosphere/designate:zed@sha256:6c024e2e600745171c032794f807d6cea45104f10569086a3a198034c6e5145f
+ glance_api: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:e58a723cde1249008077635b2a34cd4073f0c0739c2d37e5eb3554820cc7f4db
+ glance_db_sync: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:e58a723cde1249008077635b2a34cd4073f0c0739c2d37e5eb3554820cc7f4db
+ glance_metadefs_load: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:e58a723cde1249008077635b2a34cd4073f0c0739c2d37e5eb3554820cc7f4db
+ glance_registry: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:e58a723cde1249008077635b2a34cd4073f0c0739c2d37e5eb3554820cc7f4db
+ glance_storage_init: ghcr.io/vexxhost/atmosphere/glance:zed@sha256:e58a723cde1249008077635b2a34cd4073f0c0739c2d37e5eb3554820cc7f4db
grafana_sidecar: quay.io/kiwigrid/k8s-sidecar:1.24.6@sha256:3b70b9f1a81e67c97e4cd32c9a918fa44fd2c9f66bdd0d28d8b82d7b502cb5e4
- grafana: docker.io/grafana/grafana:10.1.0@sha256:047c1c5aa6fef257b6c2516f95c8fcd9f28707c201c6413dd78328b6cbedff6f
+ grafana: docker.io/grafana/grafana:10.1.0@sha256:d04d48ecbe41e513dc934e57ccb4947034cd5005fa17fdf1331a8997e314beda
haproxy: docker.io/library/haproxy:2.5@sha256:489dcc4385fd45813f3e9252b2f1f440db5749e4845d560250ce5083cc45eeb0
- heat_api: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- heat_cfn: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- heat_cloudwatch: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- heat_db_sync: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- heat_engine_cleaner: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- heat_engine: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- heat_purge_deleted: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- horizon_db_sync: quay.io/vexxhost/horizon:2023.2@sha256:627d62e5b1675b9c129347da59916836aa7d34c02cff9f066944b687993aea1b
- horizon: quay.io/vexxhost/horizon:2023.2@sha256:627d62e5b1675b9c129347da59916836aa7d34c02cff9f066944b687993aea1b
+ heat_api: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ heat_cfn: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ heat_cloudwatch: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ heat_db_sync: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ heat_engine_cleaner: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ heat_engine: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ heat_purge_deleted: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ horizon_db_sync: ghcr.io/vexxhost/atmosphere/horizon:2023.2@sha256:1bd4fc4cd4ee8004297cc690468ee89966318920b8909705e7984150737c7225
+ horizon: ghcr.io/vexxhost/atmosphere/horizon:2023.2@sha256:1bd4fc4cd4ee8004297cc690468ee89966318920b8909705e7984150737c7225
ingress_nginx_controller: registry.k8s.io/ingress-nginx/controller:v1.1.1@sha256:e16123f3932f44a2bba8bc3cf1c109cea4495ee271d6d16ab99228b58766d3ab
ingress_nginx_default_backend: registry.k8s.io/defaultbackend-amd64:1.5@sha256:4dc5e07c8ca4e23bddb3153737d7b8c556e5fb2f29c4558b7cd6e6df99c512c7
ingress_nginx_kube_webhook_certgen: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:23a03c9c381fba54043d0f6148efeaf4c1ca2ed176e43455178b5c5ebf15ad70 # noqa: yaml[line-length]
keepalived: us-docker.pkg.dev/vexxhost-infra/openstack/keepalived:2.0.19@sha256:4fe20cd5c200e301e1a790c9aca8c3fc651c8461afea9d37c56a462d3bfa48bb
keycloak: quay.io/keycloak/keycloak:22.0.1-0@sha256:5b872e841ea9e394d89bdf250146434532d9c2001404540d46621d60f87494e7
- keystone_api: quay.io/vexxhost/keystone:zed@sha256:408b84f8e1bb237dfe8b84d52b5d77fd1c307b2538e77d7e2ab69ecb74385886
- keystone_credential_cleanup: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- keystone_credential_rotate: quay.io/vexxhost/keystone:zed@sha256:408b84f8e1bb237dfe8b84d52b5d77fd1c307b2538e77d7e2ab69ecb74385886
- keystone_credential_setup: quay.io/vexxhost/keystone:zed@sha256:408b84f8e1bb237dfe8b84d52b5d77fd1c307b2538e77d7e2ab69ecb74385886
- keystone_db_sync: quay.io/vexxhost/keystone:zed@sha256:408b84f8e1bb237dfe8b84d52b5d77fd1c307b2538e77d7e2ab69ecb74385886
- keystone_domain_manage: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- keystone_fernet_rotate: quay.io/vexxhost/keystone:zed@sha256:408b84f8e1bb237dfe8b84d52b5d77fd1c307b2538e77d7e2ab69ecb74385886
- keystone_fernet_setup: quay.io/vexxhost/keystone:zed@sha256:408b84f8e1bb237dfe8b84d52b5d77fd1c307b2538e77d7e2ab69ecb74385886
- ks_endpoints: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- ks_service: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- ks_user: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
+ keystone_api: ghcr.io/vexxhost/atmosphere/keystone:zed@sha256:af2244cef560dfad20d4ae1a49440db89e455bec95168ff210071e05c606de29
+ keystone_credential_cleanup: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ keystone_credential_rotate: ghcr.io/vexxhost/atmosphere/keystone:zed@sha256:af2244cef560dfad20d4ae1a49440db89e455bec95168ff210071e05c606de29
+ keystone_credential_setup: ghcr.io/vexxhost/atmosphere/keystone:zed@sha256:af2244cef560dfad20d4ae1a49440db89e455bec95168ff210071e05c606de29
+ keystone_db_sync: ghcr.io/vexxhost/atmosphere/keystone:zed@sha256:af2244cef560dfad20d4ae1a49440db89e455bec95168ff210071e05c606de29
+ keystone_domain_manage: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ keystone_fernet_rotate: ghcr.io/vexxhost/atmosphere/keystone:zed@sha256:af2244cef560dfad20d4ae1a49440db89e455bec95168ff210071e05c606de29
+ keystone_fernet_setup: ghcr.io/vexxhost/atmosphere/keystone:zed@sha256:af2244cef560dfad20d4ae1a49440db89e455bec95168ff210071e05c606de29
+ ks_endpoints: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ ks_service: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ ks_user: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
kube_apiserver: registry.k8s.io/kube-apiserver:v1.22.17@sha256:d88d1c8f972e10ff4b4176f3185434e2832d3805c457fa9e8816f1da2fdf3b93
kube_controller_manager: registry.k8s.io/kube-controller-manager:v1.22.17@sha256:c3e041c8c8c9ffd33d421c8c1de1f42da52b616bfcf61880498e9efc9ec88005
kube_coredns: registry.k8s.io/coredns/coredns:v1.8.4@sha256:10683d82b024a58cc248c468c2632f9d1b260500f7cd9bb8e73f751048d7d6d4
@@ -96,62 +96,62 @@
kubectl: docker.io/bitnami/kubectl:1.27.3@sha256:876cebc2d9272d9eb42c2128c9a08c7e7715dbfe4f2eb2f0b3612df977fdd6b7
libvirt: quay.io/vexxhost/libvirtd:zed@sha256:480d8736954cdc01c1d6f0c625ba147935ce4e5af25828f6d3fbcd18e6dc283a
libvirt_exporter: docker.io/vexxhost/libvirtd-exporter:latest@sha256:1a0fdf89f80060bfdbb8cf45213295c5d9fb1f7ea7dbfe2b331f0649cc98df8e
- local_path_provisioner_helper: docker.io/library/busybox:1.36.0@sha256:9e2bbca079387d7965c3a9cee6d0c53f4f4e63ff7637877a83c4c05f2a666112
+ local_path_provisioner_helper: docker.io/library/busybox:1.36.0@sha256:086417a48026173aaadca4ce43a1e4b385e8e62cc738ba79fc6637049674cac0
local_path_provisioner: docker.io/rancher/local-path-provisioner:v0.0.24@sha256:b7dea5221f06f6feed7788db0ad6b024a433c8f55533bd6cc792dc2079ff9ad2
loki_gateway: docker.io/nginxinc/nginx-unprivileged:1.19-alpine@sha256:bbd46452aae30a7cc7bc438f267af812c7a2b0f3b5bcd4cc55eb99669cea3f28
loki: docker.io/grafana/loki:2.7.3@sha256:8e3abbd89173066721fa07bddfee1c1a7a8fe59bed5b00a2fa09d2b3cef8758c
- magnum_api: quay.io/vexxhost/magnum-cluster-api:zed@sha256:6bfe28acf3648a2415658f2ea1e82ad5e08f66bc3620abd592e70e6aaebc0f73
- magnum_cluster_api_proxy: quay.io/vexxhost/magnum-cluster-api:zed@sha256:6bfe28acf3648a2415658f2ea1e82ad5e08f66bc3620abd592e70e6aaebc0f73
- magnum_conductor: quay.io/vexxhost/magnum-cluster-api:zed@sha256:6bfe28acf3648a2415658f2ea1e82ad5e08f66bc3620abd592e70e6aaebc0f73
- magnum_db_sync: quay.io/vexxhost/magnum-cluster-api:zed@sha256:6bfe28acf3648a2415658f2ea1e82ad5e08f66bc3620abd592e70e6aaebc0f73
- magnum_registry: quay.io/vexxhost/magnum-cluster-api-registry:main@sha256:1623169a1ea93d0edfda0b602de1dabad6df9232d20489453071af397d9e8d84
- manila_api: quay.io/vexxhost/manila:zed@sha256:59c3bc27b6cf0740a4c87cbbd23b883bf7fa536659ac90f5aa765ad931e53e9e
- manila_data: quay.io/vexxhost/manila:zed@sha256:59c3bc27b6cf0740a4c87cbbd23b883bf7fa536659ac90f5aa765ad931e53e9e
- manila_db_sync: quay.io/vexxhost/manila:zed@sha256:59c3bc27b6cf0740a4c87cbbd23b883bf7fa536659ac90f5aa765ad931e53e9e
- manila_scheduler: quay.io/vexxhost/manila:zed@sha256:59c3bc27b6cf0740a4c87cbbd23b883bf7fa536659ac90f5aa765ad931e53e9e
- manila_share: quay.io/vexxhost/manila:zed@sha256:59c3bc27b6cf0740a4c87cbbd23b883bf7fa536659ac90f5aa765ad931e53e9e
- memcached: docker.io/library/memcached:1.6.17@sha256:d20c577c08863b09b21ecd21d0384d0a800f39d82f37045b3608f677a0a9400f
+ magnum_api: ghcr.io/vexxhost/atmosphere/magnum:zed@sha256:117eadb53f4466fc89ef0938907a6d516352011f47e565d624f618f9899cbf1f
+ magnum_cluster_api_proxy: ghcr.io/vexxhost/atmosphere/magnum:zed@sha256:117eadb53f4466fc89ef0938907a6d516352011f47e565d624f618f9899cbf1f
+ magnum_conductor: ghcr.io/vexxhost/atmosphere/magnum:zed@sha256:117eadb53f4466fc89ef0938907a6d516352011f47e565d624f618f9899cbf1f
+ magnum_db_sync: ghcr.io/vexxhost/atmosphere/magnum:zed@sha256:117eadb53f4466fc89ef0938907a6d516352011f47e565d624f618f9899cbf1f
+ magnum_registry: quay.io/vexxhost/magnum-cluster-api-registry:main@sha256:0716680b280b2a723fd72740539419e7b1ae13efefe06651b08b88cca1129792
+ manila_api: ghcr.io/vexxhost/atmosphere/manila:zed@sha256:3d634658904c15beb5c5079b0b9026f5467eb1cd6f944f019a3057120e5e89be
+ manila_data: ghcr.io/vexxhost/atmosphere/manila:zed@sha256:3d634658904c15beb5c5079b0b9026f5467eb1cd6f944f019a3057120e5e89be
+ manila_db_sync: ghcr.io/vexxhost/atmosphere/manila:zed@sha256:3d634658904c15beb5c5079b0b9026f5467eb1cd6f944f019a3057120e5e89be
+ manila_scheduler: ghcr.io/vexxhost/atmosphere/manila:zed@sha256:3d634658904c15beb5c5079b0b9026f5467eb1cd6f944f019a3057120e5e89be
+ manila_share: ghcr.io/vexxhost/atmosphere/manila:zed@sha256:3d634658904c15beb5c5079b0b9026f5467eb1cd6f944f019a3057120e5e89be
+ memcached: docker.io/library/memcached:1.6.17@sha256:db45886d2d48f143be64f2d46407e224b0b61df3b0056b9d5b03e8bc6a7cd74e
netoffload: ghcr.io/vexxhost/netoffload:v1.0.1@sha256:60f092e5d5f156c2f933c199ea72274f80eb758d3e0dc2f2b1be62174c3f7183
- neutron_bagpipe_bgp: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_bgp_dragent: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
+ neutron_bagpipe_bgp: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_bgp_dragent: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
neutron_coredns: docker.io/coredns/coredns:1.9.3@sha256:bdb36ee882c13135669cfc2bb91c808a33926ad1a411fee07bd2dc344bb8f782
- neutron_db_sync: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_dhcp: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_ironic_agent: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_l2gw: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_l3: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_linuxbridge_agent: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_metadata: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_netns_cleanup_cron: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_openvswitch_agent: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_ovn_metadata: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_server: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_sriov_agent_init: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
- neutron_sriov_agent: quay.io/vexxhost/neutron:zed@sha256:4eec4493d3026abe2847d4c9efde292e4bb703bd04a1b4dc789cb008a3de68a9
+ neutron_db_sync: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_dhcp: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_ironic_agent: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_l2gw: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_l3: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_linuxbridge_agent: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_metadata: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_netns_cleanup_cron: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_openvswitch_agent: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_ovn_metadata: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_server: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_sriov_agent_init: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
+ neutron_sriov_agent: ghcr.io/vexxhost/atmosphere/neutron:zed@sha256:b1b9e693fb4372458303bd6583fcb7ba026954ae3a1d59b2dbad8bf4c36e807f
node_feature_discovery: registry.k8s.io/nfd/node-feature-discovery:v0.11.2@sha256:24b2abfb5956b6a2a9a0f4248232838d02235d65044078c43d8bdcf29344f141
- nova_api: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_archive_deleted_rows: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_cell_setup_init: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- nova_cell_setup: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_compute_ironic: quay.io/openstack.kolla/nova-compute-ironic:zed-ubuntu-jammy@sha256:01c6730107e7eaa40b85b91ca47ca43912703977a02ba4dc784b18b985740a87
- nova_compute_ssh: quay.io/vexxhost/nova-ssh:latest@sha256:70e2bfc6dc60c8901deb039e82b842eea4894a8e1f662e1fe7df80c46a6366e4
- nova_compute: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_conductor: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_consoleauth: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_db_sync: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_novncproxy_assets: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_novncproxy: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_placement: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_scheduler: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_service_cleaner: quay.io/vexxhost/cli:latest@sha256:d4c46e58abe61576658cb9b362c9d2f8775dba17a9ad062da8a5b9a12402ffd4
- nova_spiceproxy_assets: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- nova_spiceproxy: quay.io/vexxhost/nova:zed@sha256:b9299a89cfe97b692038ca569258df753d1bdf8540dadb0766afbfb1ca871e24
- octavia_api: quay.io/vexxhost/octavia:zed@sha256:7f054aca88ea461e9ccc32d994936d76c32add09aeca35912cf9dd6da61ccd10
- octavia_db_sync: quay.io/vexxhost/octavia:zed@sha256:7f054aca88ea461e9ccc32d994936d76c32add09aeca35912cf9dd6da61ccd10
- octavia_health_manager_init: quay.io/vexxhost/heat:zed@sha256:2413e1d669a899685d0cc89c3333222ad004c567be0d5ca605dcc6a59c12af64
- octavia_health_manager: quay.io/vexxhost/octavia:zed@sha256:7f054aca88ea461e9ccc32d994936d76c32add09aeca35912cf9dd6da61ccd10
- octavia_housekeeping: quay.io/vexxhost/octavia:zed@sha256:7f054aca88ea461e9ccc32d994936d76c32add09aeca35912cf9dd6da61ccd10
- octavia_worker: quay.io/vexxhost/octavia:zed@sha256:7f054aca88ea461e9ccc32d994936d76c32add09aeca35912cf9dd6da61ccd10
+ nova_api: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_archive_deleted_rows: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_cell_setup_init: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ nova_cell_setup: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_compute_ironic: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_compute_ssh: ghcr.io/vexxhost/atmosphere/nova-ssh:latest@sha256:11269bda5692cd7752622668f7d0422da18466c405cec248468d18f6275ce9eb
+ nova_compute: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_conductor: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_consoleauth: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_db_sync: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_novncproxy_assets: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_novncproxy: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_placement: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_scheduler: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_service_cleaner: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ nova_spiceproxy_assets: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ nova_spiceproxy: ghcr.io/vexxhost/atmosphere/nova:zed@sha256:eb23bc10de2517f779597cf3b7438ff4ac7849ebaf6a916a02c83bbd04a4c56b
+ octavia_api: ghcr.io/vexxhost/atmosphere/octavia:zed@sha256:7c543d0acc53e6a171186234aa16209d2264bb68edf02424ed923f18bb66bf15
+ octavia_db_sync: ghcr.io/vexxhost/atmosphere/octavia:zed@sha256:7c543d0acc53e6a171186234aa16209d2264bb68edf02424ed923f18bb66bf15
+ octavia_health_manager_init: ghcr.io/vexxhost/atmosphere/heat:zed@sha256:33a311d1108e5487de775829ef9713b86611dbe70eaac9154640e857fa3c3846
+ octavia_health_manager: ghcr.io/vexxhost/atmosphere/octavia:zed@sha256:7c543d0acc53e6a171186234aa16209d2264bb68edf02424ed923f18bb66bf15
+ octavia_housekeeping: ghcr.io/vexxhost/atmosphere/octavia:zed@sha256:7c543d0acc53e6a171186234aa16209d2264bb68edf02424ed923f18bb66bf15
+ octavia_worker: ghcr.io/vexxhost/atmosphere/octavia:zed@sha256:7c543d0acc53e6a171186234aa16209d2264bb68edf02424ed923f18bb66bf15
openvswitch_db_server: quay.io/vexxhost/openvswitch:3.1.0-55atmosphere1@sha256:b56cbb97d9216fdc14e4ce223c1a511c624f92eb463672620e86906c2f866bd9
openvswitch_vswitchd: quay.io/vexxhost/openvswitch:3.1.0-55atmosphere1@sha256:b56cbb97d9216fdc14e4ce223c1a511c624f92eb463672620e86906c2f866bd9
ovn_controller: quay.io/vexxhost/ovn-host:23.03.0@sha256:aa0e91ff1d7224e3aa7ae0e8b7b770410f9abdb15b7421cf060d3c231fbe23e5
@@ -163,8 +163,8 @@
percona_xtradb_cluster_operator: docker.io/percona/percona-xtradb-cluster-operator:1.13.0@sha256:c674d63242f1af521edfbaffae2ae02fb8d010c0557a67a9c42d2b4a50db5243
percona_xtradb_cluster: docker.io/percona/percona-xtradb-cluster:8.0.32-24.2@sha256:1f978ab8912e1b5fc66570529cb7e7a4ec6a38adbfce1ece78159b0fcfa7d47a
percona_version_service: docker.io/perconalab/version-service:main-3325140@sha256:b7928130fca1e35ce7feaeec326fef836229a8b4de2f6f6ea5b6d2c7a48cd071
- placement_db_sync: quay.io/vexxhost/placement:zed@sha256:ae9a7567e3619440b3a7a58b5ab407c5efad372627c06fb0ab0193a85c9d1c70
- placement: quay.io/vexxhost/placement:zed@sha256:ae9a7567e3619440b3a7a58b5ab407c5efad372627c06fb0ab0193a85c9d1c70
+ placement_db_sync: ghcr.io/vexxhost/atmosphere/placement:zed@sha256:fd4bf639de092ed9e6aec16b4c298987de7380cc28e59ce2f6a14afc6ec5f2f9
+ placement: ghcr.io/vexxhost/atmosphere/placement:zed@sha256:fd4bf639de092ed9e6aec16b4c298987de7380cc28e59ce2f6a14afc6ec5f2f9
prometheus_config_reloader: quay.io/prometheus-operator/prometheus-config-reloader:v0.67.1@sha256:0fe3cf36985e0e524801a0393f88fa4b5dd5ffdf0f091ff78ee02f2d281631b5
prometheus_ipmi_exporter: us-docker.pkg.dev/vexxhost-infra/openstack/ipmi-exporter:1.4.0@sha256:4898da9cc11961a56363e8b3f3437d0f45b46585b20c79e33e97fbe7232e05d2
prometheus_memcached_exporter: quay.io/prometheus/memcached-exporter:v0.10.0@sha256:fa5a2de1a4744da66fb369bee81232f5ea52208bc643e409a60f66d699ac27b2
@@ -181,13 +181,12 @@
rabbitmq_server: docker.io/library/rabbitmq:3.10.2-management@sha256:350ab6d773e3af45183466488fe3259df36cd6ade437b4366a59e8052458cc3a
rabbitmq_topology_operator: docker.io/rabbitmqoperator/messaging-topology-operator:1.6.0@sha256:5052e8bdb26996c62315f0707c6fb291fd84492e360cca7351e2c3fdf659be43
rook_ceph: docker.io/rook/ceph:v1.10.10@sha256:2a65f6678c3f4e368046ee10695dce2c265cc81cd6bfd6258fc670dd18fbad5b
- senlin_api: quay.io/vexxhost/senlin:zed@sha256:5cb3108dfdeb02a8d910aa2666d8865c772774e431ceaf69391959d87e2b0674
- senlin_conductor: quay.io/vexxhost/senlin:zed@sha256:5cb3108dfdeb02a8d910aa2666d8865c772774e431ceaf69391959d87e2b0674
- senlin_db_sync: quay.io/vexxhost/senlin:zed@sha256:5cb3108dfdeb02a8d910aa2666d8865c772774e431ceaf69391959d87e2b0674
- senlin_engine_cleaner: quay.io/vexxhost/senlin:zed@sha256:5cb3108dfdeb02a8d910aa2666d8865c772774e431ceaf69391959d87e2b0674
- senlin_engine: quay.io/vexxhost/senlin:zed@sha256:5cb3108dfdeb02a8d910aa2666d8865c772774e431ceaf69391959d87e2b0674
- senlin_health_manager: quay.io/vexxhost/senlin:zed@sha256:5cb3108dfdeb02a8d910aa2666d8865c772774e431ceaf69391959d87e2b0674
- skopeo: quay.io/skopeo/stable:latest@sha256:7bfd9719a3b5dbf059d148bc89305800f6128595e3e54263fc9bf8e30f3d109a
+ senlin_api: ghcr.io/vexxhost/atmosphere/senlin:zed@sha256:027b1599876cec909ae87d021aebbf0481a661938fa2fb6943d3f13ca3c947d0
+ senlin_conductor: ghcr.io/vexxhost/atmosphere/senlin:zed@sha256:027b1599876cec909ae87d021aebbf0481a661938fa2fb6943d3f13ca3c947d0
+ senlin_db_sync: ghcr.io/vexxhost/atmosphere/senlin:zed@sha256:027b1599876cec909ae87d021aebbf0481a661938fa2fb6943d3f13ca3c947d0
+ senlin_engine_cleaner: ghcr.io/vexxhost/atmosphere/senlin:zed@sha256:027b1599876cec909ae87d021aebbf0481a661938fa2fb6943d3f13ca3c947d0
+ senlin_engine: ghcr.io/vexxhost/atmosphere/senlin:zed@sha256:027b1599876cec909ae87d021aebbf0481a661938fa2fb6943d3f13ca3c947d0
+ senlin_health_manager: ghcr.io/vexxhost/atmosphere/senlin:zed@sha256:027b1599876cec909ae87d021aebbf0481a661938fa2fb6943d3f13ca3c947d0
staffeln_db_sync: ghcr.io/vexxhost/staffeln:v2.2.3@sha256:ee3d8ab2c17d21b4a64a48abfb089df98700b6bc7cee5db36b5ef9c357317736
staffeln_conductor: ghcr.io/vexxhost/staffeln:v2.2.3@sha256:ee3d8ab2c17d21b4a64a48abfb089df98700b6bc7cee5db36b5ef9c357317736
staffeln_api: ghcr.io/vexxhost/staffeln:v2.2.3@sha256:ee3d8ab2c17d21b4a64a48abfb089df98700b6bc7cee5db36b5ef9c357317736
diff --git a/roles/nova/vars/main.yml b/roles/nova/vars/main.yml
index 26fb282..1b9c802 100644
--- a/roles/nova/vars/main.yml
+++ b/roles/nova/vars/main.yml
@@ -33,6 +33,8 @@
flavors:
enabled: false
pod:
+ useHostNetwork:
+ novncproxy: false
replicas:
api_metadata: 3
osapi: 3
diff --git a/tests/unit/plugins/filter/test_openstack_helm_image_tags.py b/tests/unit/plugins/filter/test_openstack_helm_image_tags.py
index 93ec810..625af35 100644
--- a/tests/unit/plugins/filter/test_openstack_helm_image_tags.py
+++ b/tests/unit/plugins/filter/test_openstack_helm_image_tags.py
@@ -46,13 +46,13 @@
def test_openstack_helm_image_tags_for_magnum(atmosphere_images):
assert openstack_helm_image_tags(atmosphere_images, "magnum") == {
- "bootstrap": "quay.io/vexxhost/heat:zed",
- "db_drop": "quay.io/vexxhost/heat:zed",
- "db_init": "quay.io/vexxhost/heat:zed",
+ "bootstrap": "ghcr.io/vexxhost/atmosphere/heat:zed",
+ "db_drop": "ghcr.io/vexxhost/atmosphere/heat:zed",
+ "db_init": "ghcr.io/vexxhost/atmosphere/heat:zed",
"dep_check": "quay.io/vexxhost/kubernetes-entrypoint:latest",
- "ks_endpoints": "quay.io/vexxhost/heat:zed",
- "ks_service": "quay.io/vexxhost/heat:zed",
- "ks_user": "quay.io/vexxhost/heat:zed",
+ "ks_endpoints": "ghcr.io/vexxhost/atmosphere/heat:zed",
+ "ks_service": "ghcr.io/vexxhost/atmosphere/heat:zed",
+ "ks_user": "ghcr.io/vexxhost/atmosphere/heat:zed",
"magnum_api": "quay.io/vexxhost/magnum:zed",
"magnum_conductor": "quay.io/vexxhost/magnum:zed",
"magnum_db_sync": "quay.io/vexxhost/magnum:zed",