From 1c9d11d2449648b0946b2dec46e9ca99fe5229c5 Mon Sep 17 00:00:00 2001 From: schristoff <28318173+schristoff@users.noreply.github.com> Date: Tue, 28 Nov 2023 00:42:51 -0700 Subject: [PATCH] forward progress Signed-off-by: schristoff <28318173+schristoff@users.noreply.github.com> --- build/testdata/bundles/wordpress/porter.yaml | 20 +- .../testdata/bundles/wordpressv2/porter.yaml | 5 +- .../testdata/mybuns-depsv2.bundle.json | 1 - pkg/cnab/dependencies_v2.go | 3 + pkg/cnab/extended_bundle.go | 3 +- pkg/porter/dependencies.go | 3 +- tests/integration/dependencies_test.go | 63 ---- tests/integration/dependenciesv2_test.go | 274 ++++++++++++++++++ 8 files changed, 293 insertions(+), 79 deletions(-) create mode 100644 tests/integration/dependenciesv2_test.go diff --git a/build/testdata/bundles/wordpress/porter.yaml b/build/testdata/bundles/wordpress/porter.yaml index abcf4e443..9ac15de46 100644 --- a/build/testdata/bundles/wordpress/porter.yaml +++ b/build/testdata/bundles/wordpress/porter.yaml @@ -64,13 +64,13 @@ uninstall: arguments: - uninstalled -outputs: - - name: wordpress-password - description: "The Wordpress installation password" - type: string - default: "default-password" - applyTo: - - "install" - - "upgrade" - sensitive: true - path: /cnab/app/outputs/wordpress-password +# outputs: +# - name: wordpress-password +# description: "The Wordpress installation password" +# type: string +# default: "default-password" +# applyTo: +# - "install" +# - "upgrade" +# sensitive: true +# path: /cnab/app/outputs/wordpress-password diff --git a/build/testdata/bundles/wordpressv2/porter.yaml b/build/testdata/bundles/wordpressv2/porter.yaml index 169fd2da7..eddf7305c 100644 --- a/build/testdata/bundles/wordpressv2/porter.yaml +++ b/build/testdata/bundles/wordpressv2/porter.yaml @@ -1,4 +1,4 @@ -schemaVersion: 1.0.1 +schemaVersion: 1.1.0 name: wordpress version: 0.1.4 registry: "localhost:5000" @@ -14,8 +14,7 @@ dependencies: requires: - name: mysql bundle: - reference: localhost:5000/mysql - version: v0.1.0 + reference: localhost:5000/mysql:v0.1.0 sharing: mode: true group: diff --git a/pkg/cnab/config-adapter/testdata/mybuns-depsv2.bundle.json b/pkg/cnab/config-adapter/testdata/mybuns-depsv2.bundle.json index a033c6195..99b64e261 100644 --- a/pkg/cnab/config-adapter/testdata/mybuns-depsv2.bundle.json +++ b/pkg/cnab/config-adapter/testdata/mybuns-depsv2.bundle.json @@ -453,7 +453,6 @@ "db": { "bundle": "localhost:5000/mydb:v0.1.0", "sharing": { - "mode": "group", "group": {} }, "parameters": { diff --git a/pkg/cnab/dependencies_v2.go b/pkg/cnab/dependencies_v2.go index cbce05588..6e1541ac4 100644 --- a/pkg/cnab/dependencies_v2.go +++ b/pkg/cnab/dependencies_v2.go @@ -61,6 +61,9 @@ func (b ExtendedBundle) DependencyV2Reader() (interface{}, error) { return nil, fmt.Errorf("could not marshal the untyped %s extension data %q: %w", DependenciesV2ExtensionKey, string(dataB), err) } + //Note: For depedency.Name to be set properly ReadDependencyV2 + // *must* be called. + //todo: make it so that ReadDependencyV2 is only able to be exported. deps := v2.Dependencies{} err = json.Unmarshal(dataB, &deps) if err != nil { diff --git a/pkg/cnab/extended_bundle.go b/pkg/cnab/extended_bundle.go index 487fcbfef..9735d95f5 100644 --- a/pkg/cnab/extended_bundle.go +++ b/pkg/cnab/extended_bundle.go @@ -268,7 +268,8 @@ func (b *ExtendedBundle) ResolveSharedDeps(bun ExtendedBundle) ([]DependencyLock } q := make([]DependencyLock, 0, len(v2.Requires)) - for _, d := range v2.Requires { + for name, d := range v2.Requires { + d.Name = name //todo(schristoff): We should 100% move this logic into // the bundle validator area diff --git a/pkg/porter/dependencies.go b/pkg/porter/dependencies.go index 7911db9e8..4f096634a 100644 --- a/pkg/porter/dependencies.go +++ b/pkg/porter/dependencies.go @@ -148,7 +148,9 @@ func (e *dependencyExecutioner) identifyDependencies(ctx context.Context) error } bun = cachedBundle.Definition + } else if e.parentOpts.Name != "" { + c, err := e.Installations.GetLastRun(ctx, e.parentOpts.Namespace, e.parentOpts.Name) if err != nil { return err @@ -317,7 +319,6 @@ func (e *dependencyExecutioner) executeDependencyv2(ctx context.Context, dep *qu if err != nil { if errors.Is(err, storage.ErrNotFound{}) { depInstallation = storage.NewInstallation(e.parentOpts.Namespace, dep.Alias) - //tood(schristoff): not sure if we still want to have parents here depInstallation.SetLabel("sh.porter.parentInstallation", e.parentArgs.Installation.String()) depInstallation.SetLabel("sh.porter.SharingGroup", dep.SharingGroup) diff --git a/tests/integration/dependencies_test.go b/tests/integration/dependencies_test.go index e5e17d5b0..d34a536b4 100644 --- a/tests/integration/dependencies_test.go +++ b/tests/integration/dependencies_test.go @@ -9,7 +9,6 @@ import ( "testing" "get.porter.sh/porter/pkg/cnab" - "get.porter.sh/porter/pkg/experimental" "get.porter.sh/porter/pkg/porter" "get.porter.sh/porter/pkg/secrets" "get.porter.sh/porter/pkg/storage" @@ -246,33 +245,6 @@ func uninstallWordpressBundle(ctx context.Context, p *porter.TestPorter, namespa } -func TestSharedDependencies(t *testing.T) { - t.Parallel() - - p := porter.NewTestPorter(t) - p.Config.SetExperimentalFlags(experimental.FlagDependenciesV2) - - bunDir, err := os.MkdirTemp("", "porter-mysqlv2-") - require.NoError(p.T(), err, "could not create temp directory at all") - - defer os.RemoveAll(bunDir) - - mysqldir := p.AddTestBundleDir(p.RepoRoot+"/build/testdata/bundles/mysql", false) - - wpdir := p.AddTestBundleDir(p.RepoRoot+"/build/testdata/bundles/wordpress", false) - ctx := p.SetupIntegrationTest() - - namespace := p.RandomString(10) - p.Chdir(mysqldir) - publishMySQLBundle(ctx, p) - - installMySQLbundle(ctx, p, namespace) - - p.Chdir(wpdir) - installWordpressBundlev2(ctx, p, namespace, "mysql") - -} - func installMySQLbundle(ctx context.Context, p *porter.TestPorter, namespace string) { p.CopyDirectory(filepath.Join(p.RepoRoot, "build/testdata/bundles/mysql"), ".", false) installOpts := porter.NewInstallOptions() @@ -292,38 +264,3 @@ func installMySQLbundle(ctx context.Context, p *porter.TestPorter, namespace str mysqlinst.SetLabel("sh.porter.SharingGroup", "myapp") } - -func installWordpressBundlev2(ctx context.Context, p *porter.TestPorter, namespace string, mysqlName string) { - - // Install the bundle that has dependencies - err := p.CopyDirectory(filepath.Join(p.RepoRoot, "build/testdata/bundles/wordpressv2"), ".", false) - require.NoError(p.T(), err, "copy of build/testdata/bundles/wordpressv2 failed") - - namespace = p.RandomString(10) - installOpts := porter.NewInstallOptions() - installOpts.Namespace = namespace - installOpts.CredentialIdentifiers = []string{"ci"} // Use the ci credential set, porter should remember this for later - installOpts.Params = []string{ - "wordpress-password=mypassword", - "namespace=" + namespace, - "mysql#namespace=" + namespace, - } - - // Add a supplemental parameter set to vet dep param resolution - installOpts.ParameterSets = []string{"myparam"} - testParamSets := storage.NewParameterSet(namespace, "myparam", secrets.SourceMap{ - Name: "mysql#probe-timeout", - Source: secrets.Source{ - Strategy: host.SourceValue, - Hint: "2", - }, - }) - - p.TestParameters.InsertParameterSet(ctx, testParamSets) - - err = installOpts.Validate(ctx, []string{}, p.Porter) - require.NoError(p.T(), err, "validation of install opts for root bundle failed") - - err = p.InstallBundle(ctx, installOpts) - require.NoError(p.T(), err, "install of root bundle failed namespace %s", namespace) -} diff --git a/tests/integration/dependenciesv2_test.go b/tests/integration/dependenciesv2_test.go new file mode 100644 index 000000000..1b40b9562 --- /dev/null +++ b/tests/integration/dependenciesv2_test.go @@ -0,0 +1,274 @@ +//go:build integration + +package integration + +import ( + "context" + "os" + "path/filepath" + "testing" + + "get.porter.sh/porter/pkg/cnab" + "get.porter.sh/porter/pkg/experimental" + "get.porter.sh/porter/pkg/porter" + "get.porter.sh/porter/pkg/secrets" + "get.porter.sh/porter/pkg/storage" + "github.com/cnabio/cnab-go/secrets/host" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSharedDependencies(t *testing.T) { + t.Parallel() + + p := porter.NewTestPorter(t) + p.Config.SetExperimentalFlags(experimental.FlagDependenciesV2) + + bunDir, err := os.MkdirTemp("", "porter-mysqlv2-") + require.NoError(p.T(), err, "could not create temp directory at all") + + defer os.RemoveAll(bunDir) + + ctx := p.SetupIntegrationTest() + + namespace := p.RandomString(10) + setupMysql(ctx, p, namespace) + + setupWordpress_v2(ctx, p, namespace) + defer cleanupWordpressBundle_v2(ctx, p, namespace) + +} + +func setupMysql(ctx context.Context, p *porter.TestPorter, namespace string) { + bunDir, err := os.MkdirTemp("", "porter-mysql") + require.NoError(p.T(), err, "could not create temp directory to publish the mysql bundle") + + // Rebuild the bundle from a temp directory so that we don't modify the source directory + // and leave modified files around. + p.TestConfig.TestContext.AddTestDirectory(filepath.Join(p.RepoRoot, "build/testdata/bundles/mysql"), bunDir) + pwd := p.Getwd() + p.Chdir(bunDir) + defer p.Chdir(pwd) + + publishOpts := porter.PublishOptions{} + publishOpts.Force = true + err = publishOpts.Validate(p.Config) + require.NoError(p.T(), err, "validation of publish opts for dependent bundle failed") + + err = p.Publish(ctx, publishOpts) + require.NoError(p.T(), err, "publish of dependent bundle failed") + installOpts := porter.NewInstallOptions() + + installOpts.Namespace = namespace + installOpts.CredentialIdentifiers = []string{"ci"} // Use the ci credential set, porter should remember this for later + + err = installOpts.Validate(ctx, []string{}, p.Porter) + require.NoError(p.T(), err, "validation of install opts for shared mysql bundle failed") + + err = p.InstallBundle(ctx, installOpts) + require.NoError(p.T(), err, "install of shared mysql bundle failed namespace %s", namespace) + + mysqlinst, err := p.Installations.GetInstallation(ctx, namespace, "mysql") + require.NoError(p.T(), err, "could not fetch installation status for the dependency") + + //Set the label on the installaiton so Porter knows to grab it + mysqlinst.SetLabel("sh.porter.SharingGroup", "myapp") + +} + +func setupWordpress_v2(ctx context.Context, p *porter.TestPorter, namespace string) { + bunDir, err := os.MkdirTemp("", "porter-wordpress") + require.NoError(p.T(), err, "could not create temp directory to publish the mysql bundle") + + // Rebuild the bundle from a temp directory so that we don't modify the source directory + // and leave modified files around. + p.TestConfig.TestContext.AddTestDirectory(filepath.Join(p.RepoRoot, "build/testdata/bundles/wordpressv2"), bunDir) + pwd := p.Getwd() + p.Chdir(bunDir) + defer p.Chdir(pwd) + + publishOpts := porter.PublishOptions{} + publishOpts.Force = true + err = publishOpts.Validate(p.Config) + require.NoError(p.T(), err, "validation of publish opts for dependent bundle failed") + + err = p.Publish(ctx, publishOpts) + require.NoError(p.T(), err, "publish of dependent bundle failed") + + installOpts := porter.NewInstallOptions() + installOpts.Namespace = namespace + installOpts.CredentialIdentifiers = []string{"ci"} // Use the ci credential set, porter should remember this for later + installOpts.Params = []string{ + "wordpress-password=mypassword", + "namespace=" + namespace, + "mysql#namespace=" + namespace, + } + + // Add a supplemental parameter set to vet dep param resolution + installOpts.ParameterSets = []string{"myparam"} + testParamSets := storage.NewParameterSet(namespace, "myparam", secrets.SourceMap{ + Name: "mysql#probe-timeout", + Source: secrets.Source{ + Strategy: host.SourceValue, + Hint: "2", + }, + }) + + p.TestParameters.InsertParameterSet(ctx, testParamSets) + + err = installOpts.Validate(ctx, []string{}, p.Porter) + require.NoError(p.T(), err, "validation of install opts for root bundle failed") + + err = p.InstallBundle(ctx, installOpts) + require.NoError(p.T(), err, "install of root bundle failed namespace %s", namespace) + + i, err := p.Installations.GetInstallation(ctx, namespace, "mysql") + require.NoError(p.T(), err, "could not fetch installation status for the dependency") + assert.Equal(p.T(), cnab.StatusSucceeded, i.Status.ResultStatus, "the dependency wasn't recorded as being installed successfully") + c, err := p.Installations.GetLastRun(ctx, namespace, "wordpress") + require.NoError(p.T(), err, "GetLastRun failed") + resolvedParameters, err := p.Sanitizer.RestoreParameterSet(ctx, c.Parameters, cnab.ExtendedBundle{Bundle: c.Bundle}) + require.NoError(p.T(), err, "Resolve run failed") + assert.Equal(p.T(), "porter-ci-mysql", resolvedParameters["mysql-name"], "the dependency param value for 'mysql-name' is incorrect") + assert.Equal(p.T(), 2, resolvedParameters["probe-timeout"], "the dependency param value for 'probe-timeout' is incorrect") + + // TODO(carolynvs): compare with last parameters set on the installation once we start tracking that + + // Verify that the bundle claim is present + i, err = p.Installations.GetInstallation(ctx, namespace, "wordpress") + require.NoError(p.T(), err, "could not fetch claim for the root bundle") + assert.Equal(p.T(), cnab.StatusSucceeded, i.Status.ResultStatus, "the root bundle wasn't recorded as being installed successfully") + +} + +func cleanupWordpressBundle_v2(ctx context.Context, p *porter.TestPorter, namespace string) { + uninstallOptions := porter.NewUninstallOptions() + uninstallOptions.Namespace = namespace + uninstallOptions.CredentialIdentifiers = []string{"ci"} + uninstallOptions.Delete = true + err := uninstallOptions.Validate(ctx, []string{}, p.Porter) + require.NoError(p.T(), err, "validation of uninstall opts for root bundle failed") + + err = p.UninstallBundle(ctx, uninstallOptions) + require.NoError(p.T(), err, "uninstall of root bundle failed") + + // Verify that the dependency installation is deleted + i, err := p.Installations.GetInstallation(ctx, namespace, "wordpress-mysql") + require.ErrorIs(p.T(), err, storage.ErrNotFound{}) + require.Equal(p.T(), storage.Installation{}, i) + + // Verify that the root installation is deleted + i, err = p.Installations.GetInstallation(ctx, namespace, "wordpress") + require.ErrorIs(p.T(), err, storage.ErrNotFound{}) + require.Equal(p.T(), storage.Installation{}, i) +} + +func upgradeWordpressBundle_v2(ctx context.Context, p *porter.TestPorter, namespace string) { + upgradeOpts := porter.NewUpgradeOptions() + upgradeOpts.Namespace = namespace + // do not specify credential sets, porter should reuse what was specified from install + upgradeOpts.Params = []string{ + "wordpress-password=mypassword", + "namespace=" + namespace, + "mysql#namespace=" + namespace, + } + err := upgradeOpts.Validate(ctx, []string{}, p.Porter) + require.NoError(p.T(), err, "validation of upgrade opts for root bundle failed") + + err = p.UpgradeBundle(ctx, upgradeOpts) + require.NoError(p.T(), err, "upgrade of root bundle failed") + + // Verify that the dependency claim is upgraded + i, err := p.Installations.GetInstallation(ctx, namespace, "wordpress-mysql") + require.NoError(p.T(), err, "could not fetch claim for the dependency") + c, err := p.Installations.GetLastRun(ctx, i.Namespace, i.Name) + require.NoError(p.T(), err, "GetLastClaim failed") + assert.Equal(p.T(), cnab.ActionUpgrade, c.Action, "the dependency wasn't recorded as being upgraded") + assert.Equal(p.T(), cnab.StatusSucceeded, i.Status.ResultStatus, "the dependency wasn't recorded as being upgraded successfully") + + // Verify that the bundle claim is upgraded + i, err = p.Installations.GetInstallation(ctx, namespace, "wordpress") + require.NoError(p.T(), err, "could not fetch claim for the root bundle") + c, err = p.Installations.GetLastRun(ctx, i.Namespace, i.Name) + require.NoError(p.T(), err, "GetLastClaim failed") + assert.Equal(p.T(), cnab.ActionUpgrade, c.Action, "the root bundle wasn't recorded as being upgraded") + assert.Equal(p.T(), cnab.StatusSucceeded, i.Status.ResultStatus, "the root bundle wasn't recorded as being upgraded successfully") + + // Check that we are using the original credential set specified during install + require.Len(p.T(), i.CredentialSets, 1, "expected only one credential set associated to the installation") + assert.Equal(p.T(), "ci", i.CredentialSets[0], "expected to use the alternate credential set") +} + +func invokeWordpressBundle_v2(ctx context.Context, p *porter.TestPorter, namespace string) { + invokeOpts := porter.NewInvokeOptions() + invokeOpts.Namespace = namespace + invokeOpts.Action = "ping" + // Use a different set of creds to run this rando command + invokeOpts.CredentialIdentifiers = []string{"ci2"} + invokeOpts.Params = []string{ + "wordpress-password=mypassword", + "namespace=" + namespace, + } + err := invokeOpts.Validate(ctx, []string{}, p.Porter) + require.NoError(p.T(), err, "validation of invoke opts for root bundle failed") + + err = p.InvokeBundle(ctx, invokeOpts) + require.NoError(p.T(), err, "invoke of root bundle failed") + + // Verify that the dependency claim is invoked + i, err := p.Installations.GetInstallation(ctx, namespace, "wordpress-mysql") + require.NoError(p.T(), err, "could not fetch claim for the dependency") + c, err := p.Installations.GetLastRun(ctx, i.Namespace, i.Name) + require.NoError(p.T(), err, "GetLastClaim failed") + assert.Equal(p.T(), "ping", c.Action, "the dependency wasn't recorded as being invoked") + assert.Equal(p.T(), cnab.StatusSucceeded, i.Status.ResultStatus, "the dependency wasn't recorded as being invoked successfully") + + // Verify that the bundle claim is invoked + i, err = p.Installations.GetInstallation(ctx, namespace, "wordpress") + require.NoError(p.T(), err, "could not fetch claim for the root bundle") + c, err = p.Installations.GetLastRun(ctx, i.Namespace, i.Name) + require.NoError(p.T(), err, "GetLastClaim failed") + assert.Equal(p.T(), "ping", c.Action, "the root bundle wasn't recorded as being invoked") + assert.Equal(p.T(), cnab.StatusSucceeded, i.Status.ResultStatus, "the root bundle wasn't recorded as being invoked successfully") + + // Check that we are now using the alternate credentials with the bundle + require.Len(p.T(), i.CredentialSets, 1, "expected only one credential set associated to the installation") + assert.Equal(p.T(), "ci2", i.CredentialSets[0], "expected to use the alternate credential set") +} + +func uninstallWordpressBundle_v2(ctx context.Context, p *porter.TestPorter, namespace string) { + uninstallOptions := porter.NewUninstallOptions() + uninstallOptions.Namespace = namespace + // Now go back to using the original set of credentials + uninstallOptions.CredentialIdentifiers = []string{"ci"} + uninstallOptions.Params = []string{ + "namespace=" + namespace, + "mysql#namespace=" + namespace, + } + err := uninstallOptions.Validate(ctx, []string{}, p.Porter) + require.NoError(p.T(), err, "validation of uninstall opts for root bundle failed") + + err = p.UninstallBundle(ctx, uninstallOptions) + require.NoError(p.T(), err, "uninstall of root bundle failed") + + // Verify that the dependency claim is uninstalled + i, err := p.Installations.GetInstallation(ctx, namespace, "wordpress-mysql") + require.NoError(p.T(), err, "could not fetch installation for the dependency") + c, err := p.Installations.GetLastRun(ctx, i.Namespace, i.Name) + require.NoError(p.T(), err, "GetLastClaim failed") + assert.Equal(p.T(), cnab.ActionUninstall, c.Action, "the dependency wasn't recorded as being uninstalled") + assert.Equal(p.T(), cnab.StatusSucceeded, i.Status.ResultStatus, "the dependency wasn't recorded as being uninstalled successfully") + + // Verify that the bundle claim is uninstalled + i, err = p.Installations.GetInstallation(ctx, namespace, "wordpress") + require.NoError(p.T(), err, "could not fetch installation for the root bundle") + c, err = p.Installations.GetLastRun(ctx, i.Namespace, i.Name) + require.NoError(p.T(), err, "GetLastClaim failed") + assert.Equal(p.T(), cnab.ActionUninstall, c.Action, "the root bundle wasn't recorded as being uninstalled") + assert.Equal(p.T(), cnab.StatusSucceeded, i.Status.ResultStatus, "the root bundle wasn't recorded as being uninstalled successfully") + + // Check that we are now using the original credentials with the bundle + require.Len(p.T(), i.CredentialSets, 1, "expected only one credential set associated to the installation") + assert.Equal(p.T(), "ci", i.CredentialSets[0], "expected to use the alternate credential set") + +}