From bb53fc9f820096c22cc35c3ec0884d866c674ecd Mon Sep 17 00:00:00 2001 From: David Desmarais-Michaud Date: Sun, 5 Jul 2026 21:36:55 -0400 Subject: [PATCH] atc: breaking change: update yoke release names of managed instances --- cmd/atc/compat.go | 110 ++++++++++++++++++++++++++++++ cmd/atc/main.go | 6 ++ cmd/atc/main_test.go | 2 +- internal/atc/reconciler_airway.go | 15 +--- 4 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 cmd/atc/compat.go diff --git a/cmd/atc/compat.go b/cmd/atc/compat.go new file mode 100644 index 00000000..69c6a7b0 --- /dev/null +++ b/cmd/atc/compat.go @@ -0,0 +1,110 @@ +package main + +import ( + "cmp" + "context" + "fmt" + "strings" + + "github.com/yokecd/yoke/internal" + "github.com/yokecd/yoke/internal/k8s" + "github.com/yokecd/yoke/pkg/apis/v1alpha1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" +) + +type ReleaseNameUpdater k8s.Client + +func (updater *ReleaseNameUpdater) Update(ctx context.Context) error { + airways, err := (*k8s.Client)(updater).AirwayIntf().List(ctx, metav1.ListOptions{}) + if err != nil { + return fmt.Errorf("failed to list airways: %w", err) + } + + for _, airway := range airways { + if err := updater.updateAirway(ctx, airway); err != nil { + return fmt.Errorf("failed to update airway %q: %w", airway.Name, err) + } + } + + return nil +} + +func (updater *ReleaseNameUpdater) updateAirway(ctx context.Context, airway *v1alpha1.Airway) error { + instanceIntf := func() dynamic.ResourceInterface { + version, _ := internal.Find(airway.Spec.Template.Versions, func(version apiextensionsv1.CustomResourceDefinitionVersion) bool { + return version.Storage + }) + intf := updater.Dynamic.Resource(schema.GroupVersionResource{ + Group: airway.Spec.Template.Group, + Version: version.Name, + Resource: airway.Spec.Template.Names.Plural, + }) + if airway.Spec.Template.Scope == apiextensionsv1.ClusterScoped { + return intf + } + return intf.Namespace("") + }() + + instances, err := instanceIntf.List(ctx, metav1.ListOptions{}) + if err != nil { + return fmt.Errorf("failed to list instances: %w", err) + } + + for _, instance := range instances.Items { + if err := updater.updateInstance(ctx, &instance); err != nil { + return fmt.Errorf("failed to update instance: %q: %w", internal.ResourceRef(&instance), err) + } + } + + return nil +} + +func (updater *ReleaseNameUpdater) updateInstance(ctx context.Context, instance *unstructured.Unstructured) error { + secretIntf := updater.Clientset.CoreV1().Secrets(cmp.Or(instance.GetNamespace(), "default")) + + selector := metav1.FormatLabelSelector( + &metav1.LabelSelector{ + MatchLabels: map[string]string{ + internal.LabelKind: "revision", + internal.LabelRelease: internal.SHA1HexFromString(deprecatedReleaseName(instance)), + }, + }, + ) + + revisions, err := secretIntf.List(ctx, metav1.ListOptions{LabelSelector: selector}) + if err != nil { + return fmt.Errorf("failed to list revision secrets: %w", err) + } + + ref := internal.ResourceRef(instance) + + for _, revision := range revisions.Items { + revision.Labels[internal.LabelRelease] = internal.SHA1HexFromString(ref) + revision.Annotations[internal.AnnotationReleaseName] = ref + if _, err := secretIntf.Update(ctx, &revision, metav1.UpdateOptions{FieldManager: "yoke"}); err != nil { + return fmt.Errorf("failed to update revision metadata on %q: %w", revision.Name, err) + } + } + + return nil +} + +func deprecatedReleaseName(resource *unstructured.Unstructured) string { + gvk := resource.GroupVersionKind() + elems := []string{ + gvk.Group, + gvk.Kind, + } + + if ns := resource.GetNamespace(); ns != "" { + elems = append(elems, ns) + } + + elems = append(elems, resource.GetName()) + + return strings.Join(elems, ".") +} diff --git a/cmd/atc/main.go b/cmd/atc/main.go index 5d29df67..e71325be 100644 --- a/cmd/atc/main.go +++ b/cmd/atc/main.go @@ -93,6 +93,12 @@ func run() (err error) { err = xerr.Join(err, teardown(context.Background())) }() + // TODO: remove when we move to version 1.0.0 + logger.Info("updating instance release names") + if err := (*ReleaseNameUpdater)(client).Update(ctx); err != nil { + return fmt.Errorf("failed to update release names: %w", err) + } + moduleCache := cache.NewModuleCache(cfg.CacheFS, cfg.ModuleAllowList, cfg.ModuleVerificationKeys) eventDispatcher := new(atc.EventDispatcher) flightStates := &xsync.Map[string, atc.InstanceState]{} diff --git a/cmd/atc/main_test.go b/cmd/atc/main_test.go index db6869f0..488f0a4a 100644 --- a/cmd/atc/main_test.go +++ b/cmd/atc/main_test.go @@ -2934,7 +2934,7 @@ func TestPruning(t *testing.T) { ) require.NoError(t, err) - expectedOwner := "default/examples.com.Test.test" + expectedOwner := "default//Test.examples.com:test" testutils.EventuallyNoErrorf( t, diff --git a/internal/atc/reconciler_airway.go b/internal/atc/reconciler_airway.go index 8585fb52..092c8966 100644 --- a/internal/atc/reconciler_airway.go +++ b/internal/atc/reconciler_airway.go @@ -6,7 +6,6 @@ import ( "fmt" "runtime" "slices" - "strings" "time" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" @@ -395,17 +394,5 @@ func (atc atc) Reconcile(ctx context.Context, event ctrl.Event) (result ctrl.Res } func ReleaseName(resource *unstructured.Unstructured) string { - gvk := resource.GroupVersionKind() - elems := []string{ - gvk.Group, - gvk.Kind, - } - - if ns := resource.GetNamespace(); ns != "" { - elems = append(elems, ns) - } - - elems = append(elems, resource.GetName()) - - return strings.Join(elems, ".") + return internal.ResourceRef(resource) }