-
Notifications
You must be signed in to change notification settings - Fork 104
Rename OperatorVersion CRs from name-<operatorVersion> to name-<appVersion>-<operatorVersion>
#1684
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 20 commits
d45938c
8d231ff
90399cb
83e386c
d2a04f5
0bd2cc9
7683c13
88701b4
4733914
be11d6d
0fe273c
d54c556
d2aab43
b1f5b4f
c5f7030
ec415a5
108c854
553a1fa
971404a
11dd956
13ffcd7
48304db
9c8b72b
45366f1
fb3f2a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| //nolint:golint,stylecheck | ||
| package _01_migrate_ov_names | ||
|
ANeumann82 marked this conversation as resolved.
Outdated
|
||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
|
|
||
| v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
|
||
| kudoapi "github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1" | ||
| "github.com/kudobuilder/kudo/pkg/kudoctl/clog" | ||
| "github.com/kudobuilder/kudo/pkg/kudoctl/kube" | ||
| "github.com/kudobuilder/kudo/pkg/kudoctl/kudoinit/migration" | ||
| ) | ||
|
|
||
| var ( | ||
| _ migration.Migrater = &MigrateOvNames{} | ||
| ) | ||
|
|
||
| type MigrateOvNames struct { | ||
| client *kube.Client | ||
| dryRun bool | ||
| ctx context.Context | ||
|
|
||
| migratedOVs map[string]*kudoapi.OperatorVersion | ||
| } | ||
|
|
||
| func New(client *kube.Client, dryRun bool) *MigrateOvNames { | ||
| return &MigrateOvNames{ | ||
| client: client, | ||
| dryRun: dryRun, | ||
| ctx: context.TODO(), | ||
| migratedOVs: map[string]*kudoapi.OperatorVersion{}, | ||
| } | ||
| } | ||
|
|
||
| func (m *MigrateOvNames) CanMigrate() error { | ||
| // No migrate check required for this migration | ||
| return nil | ||
| } | ||
|
|
||
| func (m *MigrateOvNames) Migrate() error { | ||
| if !m.dryRun { | ||
| clog.V(0).Printf("Migrate OperatorVersion names") | ||
| } | ||
| return migration.ForEachNamespace(m.client, m.migrateInNamespace) | ||
| } | ||
|
|
||
| func (m *MigrateOvNames) migrateInNamespace(ns string) error { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so this will be run when you upgrade to 0.17 but will this be run again when migrating from 0.17 to 0.18? I don't think we want to do that
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would, yes - And it doesn't matter, the migrations are (or should be) idempotent. You can re-run them again and again, they shouldn't modify anything after the first run. |
||
| clog.V(1).Printf("Run OperatorVersion name migration on namespace %q", ns) | ||
| if err := migration.ForEachOperatorVersion(m.client, ns, m.migrateOperatorVersion); err != nil { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not really atomic because before you migrate instance, it has no OV linked or the link is rather broken :/
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so what if this fails in the middle? we'll leave the cluster in broken state with no easy way to fix this :/
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe the re-run of upgrade would actually fix itself? 🤔 but will it even work if manager is down and everything is "in between"?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah - the migration could have been fixed to be completely reentrant, the code that you reviewed wasn't though. I've removed the migration now, but we should keep that in mind for future migrations. |
||
| return fmt.Errorf("failed to migrate OperatorVersions in namespace %q: %v", ns, err) | ||
| } | ||
| if err := migration.ForEachInstance(m.client, ns, m.migrateInstance); err != nil { | ||
| return fmt.Errorf("failed to migrate Instance ownership in namespace %q: %v", ns, err) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (m *MigrateOvNames) migrateOperatorVersion(ov *kudoapi.OperatorVersion) error { | ||
| expectedName := ov.FullyQualifiedName() | ||
| if ov.Name == expectedName { | ||
| // Nothing to migrate | ||
| return nil | ||
| } | ||
|
|
||
| oldName := ov.Name | ||
| clog.V(0).Printf("Migrate OperatorVersion from %q to %q", ov.Name, ov.FullyQualifiedName()) | ||
|
|
||
| ov.Name = expectedName | ||
|
|
||
| // Reset Read-Only fields | ||
| ov.Status = kudoapi.OperatorVersionStatus{} | ||
| ov.ResourceVersion = "" | ||
| ov.Generation = 0 | ||
| ov.UID = "" | ||
| ov.CreationTimestamp = v1.Time{} | ||
|
|
||
| if !m.dryRun { | ||
| var err error | ||
| clog.V(4).Printf("Create copy of OperatorVersion with name %q", ov.Name) | ||
| ov, err = m.client.KudoClient.KudoV1beta1().OperatorVersions(ov.Namespace).Create(m.ctx, ov, v1.CreateOptions{}) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to create copy of operator version: %v", err) | ||
| } | ||
| } | ||
|
|
||
| // Store migrated operator versions for instance migration | ||
| m.migratedOVs[oldName] = ov | ||
|
|
||
| if !m.dryRun { | ||
| clog.V(4).Printf("Delete old OperatorVersion with name %q", oldName) | ||
| if err := m.client.KudoClient.KudoV1beta1().OperatorVersions(ov.Namespace).Delete(m.ctx, oldName, v1.DeleteOptions{}); err != nil { | ||
| return fmt.Errorf("failed to delete old OperatorVersion %q: %v", oldName, err) | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func (m *MigrateOvNames) migrateInstance(i *kudoapi.Instance) error { | ||
| newOwner, ovWasMigrated := m.migratedOVs[i.Spec.OperatorVersion.Name] | ||
| if ovWasMigrated { | ||
| clog.V(1).Printf("Adjust OperatorVersion of Instance %q", i.Name) | ||
|
|
||
| // Set OperatorVersion | ||
| i.Spec.OperatorVersion.Name = newOwner.Name | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I'm not sure operators are prepared to deal with such change at runtime. If
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good point. I didn't even think about the "OperatorName" variable -.- I have removed the migration for now - We'll still have to test an upgrade with more complex operators that use dependencies. |
||
|
|
||
| // Save update | ||
| if !m.dryRun { | ||
| clog.V(4).Printf("Update instance %q with new owner reference", i.Name) | ||
| _, err := m.client.KudoClient.KudoV1beta1().Instances(i.Namespace).Update(m.ctx, i, v1.UpdateOptions{}) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to update Instance %q with new operator version: %v", i.Name, err) | ||
| } | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,13 @@ | ||
| package migration | ||
|
|
||
| import "github.com/kudobuilder/kudo/pkg/kudoctl/kube" | ||
| // Migrater is the base interface for migrations. | ||
|
|
||
|
ANeumann82 marked this conversation as resolved.
Outdated
|
||
| type Migrater interface { | ||
| CanMigrate(client *kube.Client) error | ||
| Migrate(client *kube.Client) error | ||
| // CanMigrate checks if there are any conditions that would prevent this migration to run | ||
| // This function should only return an error if it is sure that the migration can not be executed | ||
| CanMigrate() error | ||
|
|
||
| // Migrate executes the migration. The call must be idempotent and ignore already migrated resources | ||
| // It can be called multiple times on the same cluster and encounter migrated and unmigrated resources. | ||
| Migrate() error | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // This package contains helper functions that can be reused by all migrations | ||
| package migration | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
|
|
||
| v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
|
||
| kudoapi "github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1" | ||
| "github.com/kudobuilder/kudo/pkg/kudoctl/kube" | ||
| ) | ||
|
|
||
| // ForEachNamespace calls the given function for all namespaces in the cluster | ||
| func ForEachNamespace(client *kube.Client, f func(ns string) error) error { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: These functions would make sense in
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeaaaah.... The usage of the different clients is something that really should be refactored and cleaned up. Same with the loggers. We should have established rules which client(s) and which logger(s) is used where, (i.e. CLI code, Manager code and shared code), but that's a discussion for a different time :) |
||
| l, err := client.KubeClient.CoreV1().Namespaces().List(context.TODO(), v1.ListOptions{}) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to fetch namespaces: %v", err) | ||
| } | ||
| for _, ns := range l.Items { | ||
| if err := f(ns.Name); err != nil { | ||
| return fmt.Errorf("failed to run function for namespace %q: %v", ns.Name, err) | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // ForEachOperatorVersion calls the given function for all operatorversions in the given namespace | ||
| func ForEachOperatorVersion(client *kube.Client, ns string, f func(ov *kudoapi.OperatorVersion) error) error { | ||
| l, err := client.KudoClient.KudoV1beta1().OperatorVersions(ns).List(context.TODO(), v1.ListOptions{}) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to fetch OperatorVersions from namespace %q: %v", ns, err) | ||
| } | ||
| for _, ov := range l.Items { | ||
| ov := ov | ||
| if err := f(&ov); err != nil { | ||
| return fmt.Errorf("failed to run function for OperatorVersion \"%s/%s\": %v", ov.Namespace, ov.Name, err) | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // ForEachInstance calls the given function for all instances in the given namespace | ||
| func ForEachInstance(client *kube.Client, ns string, f func(ov *kudoapi.Instance) error) error { | ||
| l, err := client.KudoClient.KudoV1beta1().Instances(ns).List(context.TODO(), v1.ListOptions{}) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to fetch Instances from namespace %q: %v", ns, err) | ||
| } | ||
| for _, i := range l.Items { | ||
| i := i | ||
| if err := f(&i); err != nil { | ||
| return fmt.Errorf("failed to run function for Instance \"%s/%s\": %v", i.Namespace, i.Name, err) | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will need to be removed before merging - It requires changes on the operator tests