get.porter.sh/porter@v1.3.0/pkg/storage/migrations/migration_test.go (about)

     1  package migrations
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  	"time"
     9  
    10  	"get.porter.sh/porter/pkg/config"
    11  	"get.porter.sh/porter/pkg/portercontext"
    12  	testmigrations "get.porter.sh/porter/pkg/storage/migrations/testhelpers"
    13  
    14  	"get.porter.sh/porter/pkg/cnab"
    15  	"get.porter.sh/porter/pkg/secrets"
    16  	"get.porter.sh/porter/pkg/storage"
    17  	"get.porter.sh/porter/pkg/test"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestConvertInstallation(t *testing.T) {
    23  	inst := convertInstallation("mybuns")
    24  	assert.Empty(t, inst.ID, "the installation id should start off uninitialized so that later we can set it using the claim")
    25  	assert.Empty(t, inst.Namespace, "by default installations are migrated into the global namespace")
    26  	assert.Equal(t, "mybuns", inst.Name, "incorrect name")
    27  	assert.Equal(t, storage.DefaultInstallationSchemaVersion, inst.SchemaVersion, "incorrect schema version")
    28  }
    29  
    30  func TestConvertClaimToRun(t *testing.T) {
    31  	c := portercontext.NewTestContext(t)
    32  
    33  	inst := storage.NewInstallation("", "mybuns")
    34  
    35  	claimData, err := os.ReadFile(filepath.Join(c.FindRepoRoot(), "tests/testdata/porter_home/v0/claims/hello1/01G1VJGY43HT3KZN82DS6DDPWK.json"))
    36  	require.NoError(t, err, "could not read testdata")
    37  
    38  	run, err := convertClaimToRun(inst, claimData)
    39  	require.NoError(t, err, "error converting claim")
    40  
    41  	assert.Equal(t, "01G1VJGY43HT3KZN82DS6DDPWK", run.ID, "incorrect run id")
    42  	assert.Equal(t, storage.DefaultInstallationSchemaVersion, run.SchemaVersion, "incorrect schema version, should be the current schema supported by porter")
    43  	assert.Equal(t, "hello1", run.Installation, "incorrect installation name")
    44  	assert.Equal(t, "01G1VJGY43HT3KZN82DSJY4NNB", run.Revision, "incorrect revision")
    45  	assert.Equal(t, "2022-04-29T16:09:42.65907-05:00", run.Created.Format(time.RFC3339Nano), "incorrect created timestamp")
    46  	assert.Equal(t, "install", run.Action, "incorrect action")
    47  	assert.NotEmpty(t, run.Bundle, "bundle was not populated")
    48  	assert.Len(t, run.Parameters.Parameters, 1, "incorrect parameters")
    49  
    50  	param := run.Parameters.Parameters[0]
    51  	assert.Equal(t, param.Name, "porter-debug", "incorrect parameter name")
    52  	assert.Equal(t, param.Source.Strategy, "value", "incorrect parameter source key")
    53  	assert.Equal(t, param.Source.Hint, "false", "incorrect parameter source value")
    54  }
    55  
    56  func TestConvertResult(t *testing.T) {
    57  	c := portercontext.NewTestContext(t)
    58  
    59  	run := storage.NewRun("myns", "mybuns")
    60  
    61  	resultData, err := os.ReadFile(filepath.Join(c.FindRepoRoot(), "tests/testdata/porter_home/v0/results/01G1VJGY43HT3KZN82DS6DDPWK/01G1VJH2HP97B5B0N5S37KYMVG.json"))
    62  	require.NoError(t, err, "could not read testdata result")
    63  
    64  	result, err := convertResult(run, resultData)
    65  	require.NoError(t, err, "failed to convert result")
    66  
    67  	assert.Equal(t, storage.DefaultInstallationSchemaVersion, result.SchemaVersion, "incorrect schema version")
    68  	assert.Equal(t, run.Namespace, result.Namespace, "incorrect namespace")
    69  	assert.Equal(t, run.Installation, result.Installation, "incorrect installation name")
    70  	assert.Equal(t, "yay!", result.Message, "incorrect message")
    71  	assert.Equal(t, "boop!", result.Custom, "incorrect custom data")
    72  	assert.Equal(t, "01G1VJH2HP97B5B0N5S37KYMVG", result.ID, "incorrect id")
    73  	assert.Equal(t, run.ID, result.RunID, "incorrect run id")
    74  	assert.Equal(t, "2022-04-29T16:09:47.190534-05:00", result.Created.Format(time.RFC3339Nano), "incorrect created timestamp")
    75  	assert.Equal(t, "succeeded", result.Status, "incorrect status")
    76  	assert.Len(t, result.OutputMetadata, 1, "expected one output populated")
    77  
    78  	contentDigest, _ := result.OutputMetadata.GetContentDigest(cnab.OutputInvocationImageLogs)
    79  	assert.Equal(t, "sha256:28ccd0529aa1edefb0e771a28c31c0193f656718af985fed197235ba98fc5696", contentDigest, "incorrect output content digest")
    80  
    81  	generatedFlag, _ := result.OutputMetadata.GetGeneratedByBundle(cnab.OutputInvocationImageLogs)
    82  	assert.False(t, generatedFlag, "incorrect generated output flag")
    83  }
    84  
    85  func TestConvertOutput(t *testing.T) {
    86  	c := portercontext.NewTestContext(t)
    87  
    88  	outputData, err := os.ReadFile(filepath.Join(c.FindRepoRoot(), "tests/testdata/porter_home/v0/outputs/01G1VJH2HP97B5B0N5S37KYMVG/01G1VJH2HP97B5B0N5S37KYMVG-io.cnab.outputs.invocationImageLogs"))
    89  	require.NoError(t, err, "error reading testdata output file")
    90  
    91  	run := storage.NewRun("myns", "mybuns")
    92  	result := run.NewResult("succeeded")
    93  
    94  	output, err := convertOutput(result, "01G1VJH2HP97B5B0N5S37KYMVG-io.cnab.outputs.invocationImageLogs", outputData)
    95  	require.NoError(t, err, "error converting output")
    96  
    97  	require.Equal(t, storage.DefaultInstallationSchemaVersion, output.SchemaVersion, "incorrect schema version")
    98  	require.Equal(t, result.Namespace, output.Namespace, "incorrect namespace")
    99  	require.Equal(t, result.Installation, output.Installation, "incorrect installation")
   100  	require.Equal(t, "io.cnab.outputs.invocationImageLogs", output.Name, "incorrect name")
   101  	require.Equal(t, result.ID, output.ResultID, "incorrect result id")
   102  	require.Equal(t, result.RunID, output.RunID, "incorrect run id")
   103  	require.Equal(t, outputData, output.Value, "incorrect output value")
   104  }
   105  
   106  func TestMigration_Migrate(t *testing.T) {
   107  	c := testmigrations.CreateLegacyPorterHome(t)
   108  	defer c.Close()
   109  	home, err := c.GetHomeDir()
   110  	require.NoError(t, err, "could not get the home directory")
   111  	ctx, _, _ := c.SetupIntegrationTest()
   112  
   113  	destStore := storage.NewTestStore(c)
   114  	testSecrets := secrets.NewTestSecretsProvider()
   115  	testParams := storage.NewTestParameterProviderFor(t, destStore, testSecrets)
   116  	testSanitizer := storage.NewSanitizer(testParams, testSecrets)
   117  
   118  	opts := storage.MigrateOptions{
   119  		OldHome:           home,
   120  		OldStorageAccount: "src",
   121  		NewNamespace:      "myns",
   122  	}
   123  	m := NewMigration(c.Config, opts, destStore, testSanitizer)
   124  	defer m.Close()
   125  
   126  	err = m.Connect(ctx)
   127  	require.NoError(t, err, "connect failed")
   128  
   129  	updatedSchema, err := m.Migrate(ctx)
   130  	require.NoError(t, err, "migrate installations failed")
   131  	assert.Equal(t, storage.NewSchema(), updatedSchema, "incorrect schema was applied after the migration")
   132  
   133  	validateMigratedInstallations(ctx, t, c, destStore, opts)
   134  	validateMigratedCredentialSets(ctx, t, destStore, opts)
   135  	validateMigratedParameterSets(ctx, t, destStore, opts)
   136  }
   137  
   138  func validateMigratedInstallations(ctx context.Context, t *testing.T, c *config.TestConfig, destStore storage.TestStore, opts storage.MigrateOptions) {
   139  	is := storage.NewInstallationStore(destStore)
   140  	installations, err := is.ListInstallations(ctx, storage.ListOptions{Namespace: opts.NewNamespace})
   141  	require.NoError(t, err, "could not list installations in the destination database")
   142  	assert.Len(t, installations, 4, "expected 3 installation to be migrated")
   143  
   144  	// Validate that the installation as migrated correctly
   145  	inst, err := is.GetInstallation(ctx, opts.NewNamespace, "hello1")
   146  	require.NoError(t, err, "could not retrieve the hello1 test installation")
   147  
   148  	assert.Equal(t, storage.DefaultInstallationSchemaVersion, inst.SchemaVersion, "incorrect installation schema")
   149  	assert.Equal(t, "01G1VJGY43HT3KZN82DS6DDPWH", inst.ID, "the installation should set its installation id to the id of the earliest claim so that it's consistently generated")
   150  	assert.Equal(t, "hello1", inst.Name, "incorrect installation name")
   151  	assert.Equal(t, opts.NewNamespace, inst.Namespace, "installation namespace should be set to the destination namespace")
   152  	assert.Empty(t, inst.Bundle, "We didn't track the bundle reference in v0, so this can't be populated")
   153  	assert.Empty(t, inst.Custom, "We didn't allow setting custom metadata on installations in v0, so this can't be populated")
   154  	assert.Empty(t, inst.Labels, "We didn't allow setting labels on installations in v0, so this can't be populated")
   155  	assert.Empty(t, inst.CredentialSets, "We didn't track credential sets used when running a bundle in v0, so this can't be populated")
   156  	assert.Empty(t, inst.ParameterSets, "We didn't track parameter sets used when running a bundle in v0, so this can't be populated")
   157  	assert.Empty(t, inst.Parameters.Parameters, "We didn't track manually specified parameters when running a bundle in v0, so this can't be populated")
   158  
   159  	// Validate the installation status, which is calculated based on the runs and their results
   160  	assert.Equal(t, "2022-04-28T16:09:42.65907-05:00", inst.Status.Created.Format(time.RFC3339Nano), "Created timestamp should be set to the timestamp of the first run")
   161  	assert.Equal(t, "2022-04-29T16:13:20.48026-05:00", inst.Status.Modified.Format(time.RFC3339Nano), "Modified timestamp should be set to the timestamp of the last run")
   162  	require.NotNil(t, inst.Status.Installed, "the install timestamp should be set")
   163  	assert.Equal(t, "2022-04-29T16:09:47.190534-05:00", inst.Status.Installed.Format(time.RFC3339Nano), "the install timestamp should be set to the timestamp of the successful install result")
   164  	assert.NotNil(t, inst.Status.Uninstalled, "the uninstall timestamp should be set")
   165  	assert.Equal(t, "2022-04-29T16:13:21.802457-05:00", inst.Status.Uninstalled.Format(time.RFC3339Nano), "the uninstalled timestamp should be set to the timestamp of the successful uninstall result")
   166  	assert.Equal(t, "01G1VJQJV0RN5AW5BSZHNTVYTV", inst.Status.RunID, "incorrect last run id set on the installation status")
   167  	assert.Equal(t, "01G1VJQM4AVN7SCXC8WV3M0D7N", inst.Status.ResultID, "incorrect last result id set on the installation status")
   168  	assert.Equal(t, "succeeded", inst.Status.ResultStatus, "the installation status should be successful")
   169  	assert.Equal(t, "0.1.1", inst.Status.BundleVersion, "incorrect installation bundle version")
   170  	assert.Empty(t, inst.Status.BundleReference, "We didn't track bundle reference in v0, so this can't be populated")
   171  	assert.Empty(t, inst.Status.BundleDigest, "We didn't track bundle digest in v0, so this can't be populated")
   172  
   173  	runs, results, err := is.ListRuns(ctx, opts.NewNamespace, inst.Name)
   174  	require.NoError(t, err, "could not list runs in the destination database")
   175  	assert.Len(t, runs, 5, "expected 5 runs") // dry-run, failed install, successful install, upgrade, uninstall
   176  
   177  	lastRun := runs[4]
   178  	assert.Equal(t, storage.DefaultInstallationSchemaVersion, lastRun.SchemaVersion, "incorrect run schema version")
   179  	assert.Equal(t, "01G1VJQJV0RN5AW5BSZHNTVYTV", lastRun.ID, "incorrect run id")
   180  	assert.Equal(t, "01G1VJQJV0RN5AW5BSZNJ1G6R7", lastRun.Revision, "incorrect run revision")
   181  	assert.Equal(t, inst.Namespace, lastRun.Namespace, "incorrect run namespace")
   182  	assert.Equal(t, inst.Name, lastRun.Installation, "incorrect run installation name")
   183  	assert.Empty(t, lastRun.BundleReference, "We didn't track bundle reference in v0, so this can't be populated")
   184  	assert.Empty(t, lastRun.BundleDigest, "We didn't track bundle digest in v0, so this can't be populated")
   185  	assert.Equal(t, "uninstall", lastRun.Action, "incorrect run action")
   186  	assert.Empty(t, lastRun.Custom, "We didn't set custom datadata on claims in v0, so this can't be populated")
   187  	assert.Equal(t, "2022-04-29T16:13:20.48026-05:00", lastRun.Created.Format(time.RFC3339Nano), "incorrect run created timestamp")
   188  	assert.Empty(t, lastRun.ParameterSets, "We didn't track run parameter sets in v0, so this can't be populated")
   189  	assert.Empty(t, lastRun.ParameterOverrides, "We didn't track run parameter overrides in v0, so this can't be populated")
   190  	assert.Empty(t, lastRun.CredentialSets, "We didn't track run credential sets in v0, so this can't be populated")
   191  	assert.Len(t, lastRun.Parameters.Parameters, 1, "expected one parameter set on the run")
   192  	params := lastRun.Parameters.Parameters
   193  	assert.Equal(t, "porter-debug", params[0].Name, "expected the porter-debug parameter to be set on the run")
   194  	assert.Equal(t, "value", params[0].Source.Strategy, "expected the porter-debug parameter to be a hard-coded value")
   195  	assert.Equal(t, "true", params[0].Source.Hint, "expected the porter-debug parameter to be false")
   196  
   197  	runResults := results[lastRun.ID]
   198  	assert.Len(t, runResults, 2, "expected 2 results for the last run")
   199  
   200  	lastResult := runResults[1]
   201  	assert.Equal(t, "01G1VJQM4AVN7SCXC8WV3M0D7N", lastResult.ID, "incorrect result id")
   202  	assert.Equal(t, lastRun.ID, lastResult.RunID, "incorrect result id")
   203  	assert.Equal(t, inst.Name, lastResult.Installation, "incorrect result installation name")
   204  	assert.Equal(t, inst.Namespace, lastResult.Namespace, "incorrect result namespace")
   205  	assert.Equal(t, "succeeded", lastResult.Status, "incorrect result status")
   206  	assert.Equal(t, "yipee", lastResult.Message, "incorrect result message")
   207  	assert.Empty(t, lastResult.Custom, "We didn't track custom metadata on results in v0, so this can't be populated")
   208  	assert.Equal(t, "2022-04-29T16:13:21.802457-05:00", lastResult.Created.Format(time.RFC3339Nano), "invalid result created timestamp")
   209  	assert.Contains(t, lastResult.OutputMetadata, cnab.OutputInvocationImageLogs, "expected the logs to be captured as an output")
   210  	digest, _ := lastResult.OutputMetadata.GetContentDigest(cnab.OutputInvocationImageLogs)
   211  	assert.Equal(t, "sha256:a7fdc86f826691ae1c6bf6bbcba43abbb67cf45b45093652a98327521a62d69c", digest, "incorrect content digest for logs output")
   212  	generatedFlag, ok := lastResult.OutputMetadata.GetGeneratedByBundle(cnab.OutputInvocationImageLogs)
   213  	assert.True(t, ok, "expected a generated flag to be set on the logs output")
   214  	assert.False(t, generatedFlag, "incorrect content digest for logs output")
   215  
   216  	logsOutput, err := is.GetLastOutput(ctx, inst.Namespace, inst.Name, cnab.OutputInvocationImageLogs)
   217  	require.NoError(t, err, "error retrieving the last set of logs for the installation")
   218  
   219  	wantFile := filepath.Join(c.TestContext.FindRepoRoot(), "tests/testdata/porter_home/v0/outputs/01G1VJQM4AVN7SCXC8WV3M0D7N/01G1VJQM4AVN7SCXC8WV3M0D7N-io.cnab.outputs.invocationImageLogs")
   220  	test.CompareGoldenFile(t, wantFile, string(logsOutput.Value))
   221  }
   222  
   223  func validateMigratedCredentialSets(ctx context.Context, t *testing.T, destStore storage.TestStore, opts storage.MigrateOptions) {
   224  	store := storage.NewCredentialStore(destStore, nil)
   225  	credentialSets, err := store.ListCredentialSets(ctx, storage.ListOptions{Namespace: opts.NewNamespace})
   226  	require.NoError(t, err, "could not list credentialSets in the destination database")
   227  	assert.Len(t, credentialSets, 1, "expected 1 credential set to be migrated")
   228  
   229  	// Validate that the credential sets are migrated correctly
   230  	creds, err := store.GetCredentialSet(ctx, opts.NewNamespace, "credentials-tutorial")
   231  	require.NoError(t, err, "could not retrieve the migrated credentials-tutorial credential set")
   232  
   233  	assert.Equal(t, storage.DefaultCredentialSetSchemaVersion, creds.SchemaVersion, "incorrect schema version")
   234  	assert.Equal(t, "myns", creds.Namespace, "incorrect namespace")
   235  	assert.Equal(t, "credentials-tutorial", creds.Name, "incorrect name")
   236  	assert.Equal(t, "2022-06-06T16:06:52.099455-05:00", creds.Status.Created.Format(time.RFC3339Nano), "incorrect created timestamp")
   237  	assert.Equal(t, "2022-06-06T16:07:52.099455-05:00", creds.Status.Modified.Format(time.RFC3339Nano), "incorrect modified timestamp")
   238  	assert.Empty(t, creds.Labels, "incorrect labels")
   239  	require.Len(t, creds.Credentials, 1, "incorrect number of credentials migrated")
   240  
   241  	cred := creds.Credentials[0]
   242  	assert.Equal(t, "github-token", cred.Name, "incorrect credential name")
   243  	assert.Equal(t, "env", cred.Source.Strategy, "incorrect credential source key")
   244  	assert.Equal(t, "GITHUB_TOKEN", cred.Source.Hint, "incorrect credential source value")
   245  }
   246  
   247  func validateMigratedParameterSets(ctx context.Context, t *testing.T, destStore storage.TestStore, opts storage.MigrateOptions) {
   248  	store := storage.NewParameterStore(destStore, nil)
   249  	parameterSets, err := store.ListParameterSets(ctx, storage.ListOptions{Namespace: opts.NewNamespace})
   250  	require.NoError(t, err, "could not list parameterSets in the destination database")
   251  	assert.Len(t, parameterSets, 1, "expected 1 parameter set to be migrated")
   252  
   253  	// Validate that the parameter sets are migrated correctly
   254  	ps, err := store.GetParameterSet(ctx, opts.NewNamespace, "hello-llama")
   255  	require.NoError(t, err, "could not retrieve the migrated hello-llama parameter set")
   256  
   257  	assert.Equal(t, storage.DefaultParameterSetSchemaVersion, ps.SchemaVersion, "incorrect schema version")
   258  	assert.Equal(t, "myns", ps.Namespace, "incorrect namespace")
   259  	assert.Equal(t, "hello-llama", ps.Name, "incorrect name")
   260  	assert.Equal(t, "2022-06-06T16:06:21.635528-05:00", ps.Status.Created.Format(time.RFC3339Nano), "incorrect created timestamp")
   261  	assert.Equal(t, "2022-06-06T17:06:21.635528-05:00", ps.Status.Modified.Format(time.RFC3339Nano), "incorrect modified timestamp")
   262  	assert.Empty(t, ps.Labels, "incorrect labels")
   263  	require.Len(t, ps.Parameters, 1, "incorrect number of parameters migrated")
   264  
   265  	param := ps.Parameters[0]
   266  	assert.Equal(t, "name", param.Name, "incorrect parameter name")
   267  	assert.Equal(t, "env", param.Source.Strategy, "incorrect parameter source key")
   268  	assert.Equal(t, "USER", param.Source.Hint, "incorrect parameter source value")
   269  }