@@ -20,10 +20,15 @@ package e2e
2020
2121import (
2222 "context"
23+ "fmt"
24+ "io"
25+ "net/http"
26+ "strings"
2327
2428 . "github.com/onsi/ginkgo/v2"
2529 . "github.com/onsi/gomega"
2630 capi_e2e "sigs.k8s.io/cluster-api/test/e2e"
31+ capi_framework "sigs.k8s.io/cluster-api/test/framework"
2732 "sigs.k8s.io/cluster-api/test/framework/clusterctl"
2833
2934 shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared"
@@ -33,43 +38,72 @@ var (
3338 capoRelease012 string
3439 capoRelease013 string
3540 capoRelease014 string
41+ capiRelease110 string
3642 capiRelease112 string
3743)
3844
45+ // NOTE: clusterctl v1.10 cannot handle RuntimeExtensionProvider in the local
46+ // filesystem repository created by the CAPI v1.13 test framework. And clusterctl
47+ // v1.12 refuses to operate against v1beta1 management clusters. Therefore, we
48+ // cannot install ORC as a RuntimeExtension and have to use hooks instead.
49+ //
50+ // CAPO v0.13 was compiled against CAPI v1.11, and CAPO v0.14 against CAPI v1.12.
51+ // Both CAPI v1.11 and v1.12 use the v1beta2 contract and promote the IPAM API
52+ // from exp/ipam (v1alpha1) to api/ipam (v1beta2). This means neither v0.13 nor
53+ // v0.14 can run against CAPI v1.10 (the last v1beta1 release). Therefore:
54+ // - The v0.13 upgrade test uses CAPI v1.11 as the starting point.
55+ // - The v0.14 upgrade test uses CAPI v1.12 as the starting point.
56+
57+ // orcInitVersion is the ORC version installed alongside the old CAPO release in
58+ // PreInit. v1.0.2 is the version the v0.12/v0.13/v0.14 CAPO releases were tested
59+ // against (see the previous InitWithRuntimeExtensionProviders entry).
60+ const orcInitVersion = "v1.0.2"
61+
3962var _ = Describe ("When testing clusterctl upgrades for CAPO (v0.12=>current) and ORC (v1.0.2=>current)[clusterctl-upgrade]" , func () {
4063 BeforeEach (func (ctx context.Context ) {
4164 // Note: This gives the version without the 'v' prefix, so we need to add it below.
4265 capoRelease012 , err = clusterctl .ResolveRelease (ctx , "go://github.com/kubernetes-sigs/cluster-api-provider-openstack@v0.12" )
4366 Expect (err ).ToNot (HaveOccurred (), "failed to get stable release of CAPO" )
4467 capoRelease012 = "v" + capoRelease012
4568 // Note: This gives the version without the 'v' prefix, so we need to add it below.
46- capiRelease112 , err = capi_e2e .GetStableReleaseOfMinor (ctx , "1.12 " )
69+ capiRelease110 , err = capi_e2e .GetStableReleaseOfMinor (ctx , "1.10 " )
4770 Expect (err ).ToNot (HaveOccurred (), "failed to get stable release of CAPI" )
48- capiRelease112 = "v" + capiRelease112
71+ capiRelease110 = "v" + capiRelease110
4972 })
5073
5174 capi_e2e .ClusterctlUpgradeSpec (context .TODO (), func () capi_e2e.ClusterctlUpgradeSpecInput {
5275 return capi_e2e.ClusterctlUpgradeSpecInput {
53- E2EConfig : e2eCtx .E2EConfig ,
54- ClusterctlConfigPath : e2eCtx .Environment .ClusterctlConfigPath ,
55- BootstrapClusterProxy : e2eCtx .Environment .BootstrapClusterProxy ,
56- ArtifactFolder : e2eCtx .Settings .ArtifactFolder ,
57- SkipCleanup : false ,
58- InitWithBinary : "https://github.com/kubernetes-sigs/cluster-api/releases/download/" + capiRelease112 + "/clusterctl-{OS}-{ARCH}" ,
59- InitWithProvidersContract : "v1beta2" ,
60- InitWithInfrastructureProviders : []string {"openstack:" + capoRelease012 },
61- InitWithCoreProvider : "cluster-api:" + capiRelease112 ,
62- InitWithBootstrapProviders : []string {"kubeadm:" + capiRelease112 },
63- InitWithControlPlaneProviders : []string {"kubeadm:" + capiRelease112 },
76+ E2EConfig : e2eCtx .E2EConfig ,
77+ ClusterctlConfigPath : e2eCtx .Environment .ClusterctlConfigPath ,
78+ BootstrapClusterProxy : e2eCtx .Environment .BootstrapClusterProxy ,
79+ ArtifactFolder : e2eCtx .Settings .ArtifactFolder ,
80+ SkipCleanup : false ,
81+ InitWithBinary : "https://github.com/kubernetes-sigs/cluster-api/releases/download/" + capiRelease110 + "/clusterctl-{OS}-{ARCH}" ,
82+ InitWithProvidersContract : "v1beta1" ,
83+ InitWithInfrastructureProviders : []string {"openstack:" + capoRelease012 },
84+ InitWithCoreProvider : "cluster-api:" + capiRelease110 ,
85+ InitWithBootstrapProviders : []string {"kubeadm:" + capiRelease110 },
86+ InitWithControlPlaneProviders : []string {"kubeadm:" + capiRelease110 },
87+ // Explicit empty slice: we install ORC manually via PreInit/PreUpgrade
88+ // hooks rather than via clusterctl.
89+ InitWithRuntimeExtensionProviders : []string {},
6490 MgmtFlavor : shared .FlavorDefault ,
6591 WorkloadFlavor : shared .FlavorCapiV1Beta1 ,
6692 InitWithKubernetesVersion : e2eCtx .E2EConfig .MustGetVariable (shared .KubernetesKindVersion ),
67- InitWithRuntimeExtensionProviders : []string {"openstack-resource-controller:v1.0.2" },
6893 UseKindForManagementCluster : true ,
94+ // Install ORC v1.0.2 before clusterctl init
95+ PreInit : func (managementClusterProxy capi_framework.ClusterProxy ) {
96+ installORC (context .Background (), managementClusterProxy , orcInitVersion )
97+ },
98+ // Upgrade ORC to the current version before clusterctl upgrade
99+ PreUpgrade : func (managementClusterProxy capi_framework.ClusterProxy ) {
100+ installLatestORC (context .Background (), managementClusterProxy , e2eCtx .E2EConfig )
101+ },
69102 }
70103 })
71104})
72105
106+ // CAPO v0.13 is built against CAPI v1beta2 and therefore cannot run against CAPI v1.10.
73107var _ = Describe ("When testing clusterctl upgrades for CAPO (v0.13=>current) and ORC (v1.0.2=>current)[clusterctl-upgrade]" , func () {
74108 BeforeEach (func (ctx context.Context ) {
75109 // Note: This gives the version without the 'v' prefix, so we need to add it below.
@@ -90,15 +124,14 @@ var _ = Describe("When testing clusterctl upgrades for CAPO (v0.13=>current) and
90124 ArtifactFolder : e2eCtx .Settings .ArtifactFolder ,
91125 SkipCleanup : false ,
92126 InitWithBinary : "https://github.com/kubernetes-sigs/cluster-api/releases/download/" + capiRelease112 + "/clusterctl-{OS}-{ARCH}" ,
93- InitWithProvidersContract : "v1beta2" ,
94127 InitWithInfrastructureProviders : []string {"openstack:" + capoRelease013 },
95128 InitWithCoreProvider : "cluster-api:" + capiRelease112 ,
96129 InitWithBootstrapProviders : []string {"kubeadm:" + capiRelease112 },
97130 InitWithControlPlaneProviders : []string {"kubeadm:" + capiRelease112 },
131+ InitWithRuntimeExtensionProviders : []string {"openstack-resource-controller:v1.0.2" },
98132 MgmtFlavor : shared .FlavorDefault ,
99133 WorkloadFlavor : shared .FlavorCapiV1Beta1 ,
100134 InitWithKubernetesVersion : e2eCtx .E2EConfig .MustGetVariable (shared .KubernetesKindVersion ),
101- InitWithRuntimeExtensionProviders : []string {"openstack-resource-controller:v1.0.2" },
102135 UseKindForManagementCluster : true ,
103136 }
104137 })
@@ -124,16 +157,65 @@ var _ = Describe("When testing clusterctl upgrades for CAPO (v0.14=>current) and
124157 ArtifactFolder : e2eCtx .Settings .ArtifactFolder ,
125158 SkipCleanup : false ,
126159 InitWithBinary : "https://github.com/kubernetes-sigs/cluster-api/releases/download/" + capiRelease112 + "/clusterctl-{OS}-{ARCH}" ,
127- InitWithProvidersContract : "v1beta2" ,
128160 InitWithInfrastructureProviders : []string {"openstack:" + capoRelease014 },
129161 InitWithCoreProvider : "cluster-api:" + capiRelease112 ,
130162 InitWithBootstrapProviders : []string {"kubeadm:" + capiRelease112 },
131163 InitWithControlPlaneProviders : []string {"kubeadm:" + capiRelease112 },
164+ InitWithRuntimeExtensionProviders : []string {"openstack-resource-controller:v1.0.2" },
132165 MgmtFlavor : shared .FlavorDefault ,
133166 WorkloadFlavor : shared .FlavorCapiV1Beta1 ,
134167 InitWithKubernetesVersion : e2eCtx .E2EConfig .MustGetVariable (shared .KubernetesKindVersion ),
135- InitWithRuntimeExtensionProviders : []string {"openstack-resource-controller:v1.0.2" },
136168 UseKindForManagementCluster : true ,
137169 }
138170 })
139171})
172+
173+ // installLatestORC downloads and applies the install manifest for the current/latest version of
174+ // the OpenStack Resource Controller (ORC) to the management cluster.
175+ //
176+ // The ORC version is derived from the e2e config (the latest version with contract v1beta2), so no
177+ // version hardcoding is required here — updating the provider entry in the e2e config is sufficient.
178+ func installLatestORC (ctx context.Context , proxy capi_framework.ClusterProxy , e2eConfig * clusterctl.E2EConfig ) {
179+ // GetProviderLatestVersionsByContract returns strings in the format "provider-name:version",
180+ // e.g. "openstack-resource-controller:v2.5.0".
181+ orcVersionStrings := e2eConfig .GetProviderLatestVersionsByContract ("v1beta2" , "openstack-resource-controller" )
182+ Expect (orcVersionStrings ).ToNot (BeEmpty (),
183+ "No ORC version with v1beta2 contract found in e2e config; cannot install ORC for upgrade" )
184+
185+ parts := strings .SplitN (orcVersionStrings [0 ], ":" , 2 )
186+ Expect (parts ).To (HaveLen (2 ),
187+ "Unexpected ORC provider version string format (expected 'name:version'): %q" , orcVersionStrings [0 ])
188+ orcVersion := parts [1 ]
189+
190+ installORC (ctx , proxy , orcVersion )
191+ }
192+
193+ // installORC downloads and applies the upstream install manifest for the given version of the
194+ // OpenStack Resource Controller (ORC) to the management cluster.
195+ func installORC (ctx context.Context , proxy capi_framework.ClusterProxy , orcVersion string ) {
196+ By (fmt .Sprintf ("Installing ORC %s on the management cluster" , orcVersion ))
197+
198+ orcInstallURL := fmt .Sprintf (
199+ "https://github.com/k-orc/openstack-resource-controller/releases/download/%s/install.yaml" ,
200+ orcVersion ,
201+ )
202+ By (fmt .Sprintf ("Downloading ORC %s install manifest from %s" , orcVersion , orcInstallURL ))
203+
204+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , orcInstallURL , http .NoBody )
205+ Expect (err ).ToNot (HaveOccurred (), "Failed to create HTTP request for ORC install manifest" )
206+
207+ resp , err := http .DefaultClient .Do (req ) //nolint:bodyclose // closed below via defer
208+ Expect (err ).ToNot (HaveOccurred (),
209+ "Failed to download ORC %s install manifest from %s" , orcVersion , orcInstallURL )
210+ defer resp .Body .Close ()
211+
212+ Expect (resp .StatusCode ).To (Equal (http .StatusOK ),
213+ "Unexpected HTTP status %d when downloading ORC install manifest from %s" , resp .StatusCode , orcInstallURL )
214+
215+ orcManifest , err := io .ReadAll (resp .Body )
216+ Expect (err ).ToNot (HaveOccurred (), "Failed to read ORC install manifest response body" )
217+
218+ By (fmt .Sprintf ("Applying ORC %s install manifest to the management cluster" , orcVersion ))
219+ Expect (proxy .CreateOrUpdate (ctx , orcManifest )).To (Succeed (),
220+ "Failed to apply ORC %s install manifest to management cluster" , orcVersion )
221+ }
0 commit comments