@@ -22,6 +22,7 @@ import (
2222 "net/http"
2323 "os"
2424 "path/filepath"
25+ "strings"
2526 "sync"
2627 "time"
2728
@@ -34,6 +35,7 @@ import (
3435 "k8s.io/apimachinery/pkg/runtime/schema"
3536 "k8s.io/apimachinery/pkg/types"
3637 clusterv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
38+ "sigs.k8s.io/cluster-api/util"
3739 "sigs.k8s.io/cluster-api/util/conditions"
3840 "sigs.k8s.io/cluster-api/util/patch"
3941 "sigs.k8s.io/cluster-api/util/record"
@@ -60,7 +62,6 @@ type NodeImages struct {
6062const (
6163 metadataFileName = "metadata.yaml"
6264 nodeImagesFileName = "node-images.yaml"
63- maxNameLength = 63
6465 waitForOpenStackNodeImageReleasesBecomeReady = 30 * time .Second
6566 reconcileOpenStackNodeImageReleases = 3 * time .Minute
6667)
@@ -157,7 +158,18 @@ func (r *OpenStackClusterStackReleaseReconciler) Reconcile(ctx context.Context,
157158 ownerRef := generateOwnerReference (openstackclusterstackrelease )
158159
159160 for _ , openStackNodeImage := range nodeImages .OpenStackNodeImages {
160- osnirName := ensureMaxNameLength (fmt .Sprintf ("%s-%s" , openstackclusterstackrelease .Name , openStackNodeImage .CreateOpts .Name ))
161+ // OpenStackNodeImageRelease is composed as follows:
162+ // <release tag without version>-<image name>-<node-image-version>
163+ // e.g.: `openstack-ferrol-1-27-ubuntu-capi-image-v1.27.8-v2`
164+ // This ensures that multiple versions of one ClusterStack could share images
165+ nodeImageVersion := releaseAssets .Meta .Versions .Components .NodeImage
166+ nameWithoutVersion , err := cutOpenStackClusterStackReleaseVersionFromReleaseTag (openstackclusterstackrelease .Name )
167+ if err != nil {
168+ return ctrl.Result {}, fmt .Errorf ("failed to cut release tag: %w" , err )
169+ }
170+
171+ osnirName := fmt .Sprintf ("%s-%s-%s" , nameWithoutVersion , openStackNodeImage .CreateOpts .Name , nodeImageVersion )
172+
161173 if err := r .getOrCreateOpenStackNodeImageRelease (ctx , openstackclusterstackrelease , osnirName , openStackNodeImage , ownerRef ); err != nil {
162174 return ctrl.Result {}, fmt .Errorf ("failed to get or create OpenStackNodeImageRelease %s/%s: %w" , openstackclusterstackrelease .Namespace , osnirName , err )
163175 }
@@ -206,8 +218,19 @@ func (r *OpenStackClusterStackReleaseReconciler) getOrCreateOpenStackNodeImageRe
206218
207219 err := r .Get (ctx , types.NamespacedName {Name : osnirName , Namespace : openstackclusterstackrelease .Namespace }, openStackNodeImageRelease )
208220
209- // Nothing to do if the object exists
221+ // Update owner references if the object exists
210222 if err == nil {
223+ // Ensure owner reference
224+ openStackNodeImageRelease .SetOwnerReferences (util .EnsureOwnerRef (openStackNodeImageRelease .GetOwnerReferences (), * ownerRef ))
225+
226+ if err := r .Update (ctx , openStackNodeImageRelease ); err != nil {
227+ record .Eventf (openStackNodeImageRelease ,
228+ "ErrorOpenStackNodeImageRelease" ,
229+ "failed to update %s OpenStackNodeImageRelease: %s" , osnirName , err .Error (),
230+ )
231+ return fmt .Errorf ("failed to update OpenStackNodeImageRelease: %w" , err )
232+ }
233+
211234 return nil
212235 }
213236
@@ -221,7 +244,7 @@ func (r *OpenStackClusterStackReleaseReconciler) getOrCreateOpenStackNodeImageRe
221244 openStackNodeImageRelease .Namespace = openstackclusterstackrelease .Namespace
222245 openStackNodeImageRelease .TypeMeta = metav1.TypeMeta {
223246 Kind : "OpenStackNodeImageRelease" ,
224- APIVersion : "infrastructure.clusterstack.x-k8s.io/v1alpha1" ,
247+ APIVersion : apiv1alpha1 . GroupVersion . String () ,
225248 }
226249 openStackNodeImageRelease .SetOwnerReferences ([]metav1.OwnerReference {* ownerRef })
227250 openStackNodeImageRelease .Spec .Image = openStackNodeImage
@@ -254,7 +277,7 @@ func (r *OpenStackClusterStackReleaseReconciler) getOwnedOpenStackNodeImageRelea
254277 for i := range osnir .GetOwnerReferences () {
255278 ownerRef := osnir .GetOwnerReferences ()[i ]
256279 if matchOwnerReference (& ownerRef , openstackclusterstackrelease ) {
257- ownedOpenStackNodeImageReleases = append (ownedOpenStackNodeImageReleases , & osnirList . Items [ i ] )
280+ ownedOpenStackNodeImageReleases = append (ownedOpenStackNodeImageReleases , & osnir )
258281 break
259282 }
260283 }
@@ -318,13 +341,14 @@ func getNodeImagesFromLocal(localDownloadPath string) (*NodeImages, error) {
318341 return & nodeImages , nil
319342}
320343
321- // TODO: Ensure RFC 1123 compatibility.
322- // RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*').
323- func ensureMaxNameLength (base string ) string {
324- if len (base ) > maxNameLength {
325- return base [:maxNameLength ]
344+ // cutOpenStackClusterStackReleaseVersionFromReleaseTag returns a release tag without version,
345+ // e.g. from `openstack-ferrol-1-27-v2` returns `openstack-ferrol-1-27`.
346+ func cutOpenStackClusterStackReleaseVersionFromReleaseTag (releaseTag string ) (string , error ) {
347+ v := strings .Split (releaseTag , "-" )
348+ if len (v ) != 5 && len (v ) != 6 {
349+ return "" , fmt .Errorf ("invalid release tag %s" , releaseTag )
326350 }
327- return base
351+ return fmt . Sprintf ( "%s-%s-%s-%s" , v [ 0 ], v [ 1 ], v [ 2 ], v [ 3 ]), nil
328352}
329353
330354// SetupWithManager sets up the controller with the Manager.
0 commit comments