// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package oss_test import ( "context" "testing" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/testing/dbtest" wrappingKms "github.com/hashicorp/go-kms-wrapping/extras/kms/v2" wrapping "github.com/hashicorp/go-kms-wrapping/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestMigrations_OplogKeyConversion: in this test we'll create a "deprecated" // kms (where all the deks are in one set of tables), then we'll create keys in // that kms. After that, we'll create a new style kms (where the oplog deks are // in separate tables), and we'll read the existing deks. Finally, we'll ensure // that the orig deks created using the deprecated kms match the deks read from // the new style kms. If that's successful, we've converted the oplog deks from // a shared table table space into their own set of tables. func TestMigrations_OplogKeyConversion(t *testing.T) { const ( priorMigration = 76001 currentMigration = 77004 ) t.Parallel() testCtx := context.Background() dialect := dbtest.Postgres c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1)) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, c()) }) d, err := common.SqlOpen(dialect, u) require.NoError(t, err) // migration to the prior migration (before the one we want to test) m, err := schema.NewManager(testCtx, schema.Dialect(dialect), d, schema.WithEditions( schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}), )) require.NoError(t, err) _, err = m.ApplyMigrations(testCtx) require.NoError(t, err) state, err := m.CurrentState(testCtx) require.NoError(t, err) want := &schema.State{ Initialized: true, Editions: []schema.EditionState{ { Name: "oss", BinarySchemaVersion: priorMigration, DatabaseSchemaVersion: priorMigration, DatabaseSchemaState: schema.Equal, }, }, } require.Equal(t, want, state) // Get a connection dbType, err := db.StringToDbType(dialect) require.NoError(t, err) conn, err := db.Open(testCtx, dbType, u) require.NoError(t, err) rootWrapper := db.TestWrapper(t) // recreate the deprecated kms, where all the DEKs are in the same tables. deprecatedKms := deprecatedKms(t, conn, rootWrapper) deprecatedCreateKeys(t, deprecatedKms, "global") origDeks := make([]wrapping.Wrapper, 0, len(kms.ValidDekPurposes())) for _, p := range kms.ValidDekPurposes() { w, err := deprecatedKms.GetWrapper(testCtx, "global", wrappingKms.KeyPurpose(p.String())) require.NoError(t, err) origDeks = append(origDeks, w) } // now we're ready for the migration we want to test. m, err = schema.NewManager(testCtx, schema.Dialect(dialect), d, schema.WithEditions( schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}), )) require.NoError(t, err) _, err = m.ApplyMigrations(testCtx) require.NoError(t, err) state, err = m.CurrentState(testCtx) require.NoError(t, err) want = &schema.State{ Initialized: true, Editions: []schema.EditionState{ { Name: "oss", BinarySchemaVersion: currentMigration, DatabaseSchemaVersion: currentMigration, DatabaseSchemaState: schema.Equal, }, }, } require.Equal(t, want, state) // create a new internal kms, where the oplog deks are in their own tables. // Then we'll read the existing DEKs and make sure they match the original DEKs kmsCache := kms.TestKms(t, conn, rootWrapper) newDeks := make([]wrapping.Wrapper, 0, len(kms.ValidDekPurposes())) for _, p := range kms.ValidDekPurposes() { w, err := kmsCache.GetWrapper(testCtx, "global", p) require.NoError(t, err) newDeks = append(newDeks, w) } assert.Equal(t, origDeks, newDeks) } // deprecatedKms will create a kms that mimics the deprecated kms where all // the DEKs are in the same tables func deprecatedKms(t *testing.T, conn *db.DB, rootWrapper wrapping.Wrapper) *wrappingKms.Kms { t.Helper() purposes := make([]wrappingKms.KeyPurpose, 0, len(kms.ValidDekPurposes())) for _, p := range kms.ValidDekPurposes() { purposes = append(purposes, wrappingKms.KeyPurpose(p.String())) } purposes = append(purposes, wrappingKms.KeyPurpose(kms.KeyPurposeWorkerAuth.String()), wrappingKms.KeyPurpose(kms.KeyPurposeWorkerAuthStorage.String()), wrappingKms.KeyPurpose(kms.KeyPurposeRecovery.String()), wrappingKms.KeyPurpose(kms.KeyPurposeBsr.String()), ) rw := db.New(conn) k, err := wrappingKms.New(db.NewChangeSafeDbwReader(rw), db.NewChangeSafeDbwWriter(rw), purposes) require.NoError(t, err) err = k.AddExternalWrapper(context.Background(), wrappingKms.KeyPurposeRootKey, rootWrapper) require.NoError(t, err) return k } // deprecatedCreateKeys will create deks that are all in the same table func deprecatedCreateKeys(t *testing.T, k *wrappingKms.Kms, scopeId string) { t.Helper() require.NotEmpty(t, scopeId) purposes := make([]wrappingKms.KeyPurpose, 0, len(kms.ValidDekPurposes())) for _, p := range kms.ValidDekPurposes() { purposes = append(purposes, wrappingKms.KeyPurpose(p.String())) } err := k.CreateKeys(context.Background(), scopeId, purposes) require.NoError(t, err) }