cli: store upgrade files in versioned folders (#1929)

* upgrade versioning

* dont pass upgrade kind as boolean

* whitespace

* fix godot lint check

* clarify upgrade check directory suffix

* cli: dry-run Terraform migrations on `upgrade check` (#1942)

* dry-run Terraform migrations on upgrade check

* clean whole upgrade dir

* clean up check workspace after planning

* fix parsing

* extend upgrade check test

* rename unused parameters

* exclude false positives in test
This commit is contained in:
Moritz Sanft 2023-06-21 09:22:32 +02:00 committed by GitHub
parent f3c2198a9a
commit b25228d175
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 300 additions and 127 deletions

View file

@ -17,17 +17,13 @@ import (
"sigs.k8s.io/yaml"
)
var (
backupFolder = filepath.Join(constants.UpgradeDir, "backups") + string(filepath.Separator)
crdBackupFolder = filepath.Join(backupFolder, "crds") + string(filepath.Separator)
)
func (c *Client) backupCRDs(ctx context.Context) ([]apiextensionsv1.CustomResourceDefinition, error) {
func (c *Client) backupCRDs(ctx context.Context, upgradeID string) ([]apiextensionsv1.CustomResourceDefinition, error) {
crds, err := c.kubectl.GetCRDs(ctx)
if err != nil {
return nil, fmt.Errorf("getting CRDs: %w", err)
}
crdBackupFolder := c.crdBackupFolder(upgradeID)
if err := c.fs.MkdirAll(crdBackupFolder); err != nil {
return nil, fmt.Errorf("creating backup dir: %w", err)
}
@ -54,7 +50,7 @@ func (c *Client) backupCRDs(ctx context.Context) ([]apiextensionsv1.CustomResour
return crds, nil
}
func (c *Client) backupCRs(ctx context.Context, crds []apiextensionsv1.CustomResourceDefinition) error {
func (c *Client) backupCRs(ctx context.Context, crds []apiextensionsv1.CustomResourceDefinition, upgradeID string) error {
for _, crd := range crds {
for _, version := range crd.Spec.Versions {
gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: version.Name, Resource: crd.Spec.Names.Plural}
@ -63,6 +59,7 @@ func (c *Client) backupCRs(ctx context.Context, crds []apiextensionsv1.CustomRes
return fmt.Errorf("retrieving CR %s: %w", crd.Name, err)
}
backupFolder := c.backupFolder(upgradeID)
for _, cr := range crs {
targetFolder := filepath.Join(backupFolder, gvr.Group, gvr.Version, cr.GetNamespace(), cr.GetKind())
if err := c.fs.MkdirAll(targetFolder); err != nil {
@ -83,3 +80,11 @@ func (c *Client) backupCRs(ctx context.Context, crds []apiextensionsv1.CustomRes
}
return nil
}
func (c *Client) backupFolder(upgradeID string) string {
return filepath.Join(constants.UpgradeDir, upgradeID, "backups") + string(filepath.Separator)
}
func (c *Client) crdBackupFolder(upgradeID string) string {
return filepath.Join(c.backupFolder(upgradeID), "crds") + string(filepath.Separator)
}

View file

@ -24,16 +24,19 @@ import (
func TestBackupCRDs(t *testing.T) {
testCases := map[string]struct {
upgradeID string
crd string
expectedFile string
getCRDsError error
wantError bool
}{
"success": {
upgradeID: "1234",
crd: "apiVersion: \nkind: \nmetadata:\n name: foobar\n creationTimestamp: null\nspec:\n group: \"\"\n names:\n kind: \"somename\"\n plural: \"somenames\"\n scope: \"\"\n versions: null\nstatus:\n acceptedNames:\n kind: \"\"\n plural: \"\"\n conditions: null\n storedVersions: null\n",
expectedFile: "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n name: foobar\n creationTimestamp: null\nspec:\n group: \"\"\n names:\n kind: \"somename\"\n plural: \"somenames\"\n scope: \"\"\n versions: null\nstatus:\n acceptedNames:\n kind: \"\"\n plural: \"\"\n conditions: null\n storedVersions: null\n",
},
"api request fails": {
upgradeID: "1234",
getCRDsError: errors.New("api error"),
wantError: true,
},
@ -55,14 +58,14 @@ func TestBackupCRDs(t *testing.T) {
log: stubLog{},
}
_, err = client.backupCRDs(context.Background())
_, err = client.backupCRDs(context.Background(), tc.upgradeID)
if tc.wantError {
assert.Error(err)
return
}
assert.NoError(err)
data, err := afero.ReadFile(memFs, filepath.Join(crdBackupFolder, crd.Name+".yaml"))
data, err := afero.ReadFile(memFs, filepath.Join(client.crdBackupFolder(tc.upgradeID), crd.Name+".yaml"))
require.NoError(err)
assert.YAMLEq(tc.expectedFile, string(data))
})
@ -71,6 +74,7 @@ func TestBackupCRDs(t *testing.T) {
func TestBackupCRs(t *testing.T) {
testCases := map[string]struct {
upgradeID string
crd apiextensionsv1.CustomResourceDefinition
resource unstructured.Unstructured
expectedFile string
@ -78,6 +82,7 @@ func TestBackupCRs(t *testing.T) {
wantError bool
}{
"success": {
upgradeID: "1234",
crd: apiextensionsv1.CustomResourceDefinition{
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Names: apiextensionsv1.CustomResourceDefinitionNames{
@ -95,6 +100,7 @@ func TestBackupCRs(t *testing.T) {
expectedFile: "metadata:\n name: foobar\n",
},
"api request fails": {
upgradeID: "1234",
crd: apiextensionsv1.CustomResourceDefinition{
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Names: apiextensionsv1.CustomResourceDefinitionNames{
@ -126,14 +132,14 @@ func TestBackupCRs(t *testing.T) {
log: stubLog{},
}
err := client.backupCRs(context.Background(), []apiextensionsv1.CustomResourceDefinition{tc.crd})
err := client.backupCRs(context.Background(), []apiextensionsv1.CustomResourceDefinition{tc.crd}, tc.upgradeID)
if tc.wantError {
assert.Error(err)
return
}
assert.NoError(err)
data, err := afero.ReadFile(memFs, filepath.Join(backupFolder, tc.crd.Spec.Group, tc.crd.Spec.Versions[0].Name, tc.resource.GetNamespace(), tc.resource.GetKind(), tc.resource.GetName()+".yaml"))
data, err := afero.ReadFile(memFs, filepath.Join(client.backupFolder(tc.upgradeID), tc.crd.Spec.Group, tc.crd.Spec.Versions[0].Name, tc.resource.GetNamespace(), tc.resource.GetKind(), tc.resource.GetName()+".yaml"))
require.NoError(err)
assert.YAMLEq(tc.expectedFile, string(data))
})

View file

@ -102,7 +102,7 @@ func (c *Client) shouldUpgrade(releaseName, newVersion string) error {
// Upgrade runs a helm-upgrade on all deployments that are managed via Helm.
// If the CLI receives an interrupt signal it will cancel the context.
// Canceling the context will prompt helm to abort and roll back the ongoing upgrade.
func (c *Client) Upgrade(ctx context.Context, config *config.Config, timeout time.Duration, allowDestructive bool) error {
func (c *Client) Upgrade(ctx context.Context, config *config.Config, timeout time.Duration, allowDestructive bool, upgradeID string) error {
upgradeErrs := []error{}
upgradeReleases := []*chart.Chart{}
invalidUpgrade := &compatibility.InvalidUpgradeError{}
@ -138,11 +138,11 @@ func (c *Client) Upgrade(ctx context.Context, config *config.Config, timeout tim
return errors.Join(upgradeErrs...)
}
crds, err := c.backupCRDs(ctx)
crds, err := c.backupCRDs(ctx, upgradeID)
if err != nil {
return fmt.Errorf("creating CRD backup: %w", err)
}
if err := c.backupCRs(ctx, crds); err != nil {
if err := c.backupCRs(ctx, crds, upgradeID); err != nil {
return fmt.Errorf("creating CR backup: %w", err)
}