get.porter.sh/porter@v1.3.0/pkg/porter/helpers.go (about) 1 package porter 2 3 import ( 4 "context" 5 "fmt" 6 "math/rand" 7 "os" 8 "path/filepath" 9 "runtime" 10 "strings" 11 "testing" 12 "time" 13 14 "get.porter.sh/porter/pkg/build" 15 "get.porter.sh/porter/pkg/cache" 16 "get.porter.sh/porter/pkg/cnab" 17 cnabtooci "get.porter.sh/porter/pkg/cnab/cnab-to-oci" 18 cnabprovider "get.porter.sh/porter/pkg/cnab/provider" 19 "get.porter.sh/porter/pkg/config" 20 "get.porter.sh/porter/pkg/encoding" 21 "get.porter.sh/porter/pkg/manifest" 22 "get.porter.sh/porter/pkg/mixin" 23 "get.porter.sh/porter/pkg/plugins" 24 "get.porter.sh/porter/pkg/secrets" 25 "get.porter.sh/porter/pkg/signing" 26 "get.porter.sh/porter/pkg/storage" 27 "get.porter.sh/porter/pkg/tracing" 28 "get.porter.sh/porter/pkg/yaml" 29 "github.com/cnabio/cnab-go/bundle" 30 "github.com/stretchr/testify/require" 31 ) 32 33 type TestPorter struct { 34 *Porter 35 TestConfig *config.TestConfig 36 TestStore storage.TestStore 37 TestInstallations *storage.TestInstallationProvider 38 TestCredentials *storage.TestCredentialSetProvider 39 TestParameters *storage.TestParameterSetProvider 40 TestCache *cache.TestCache 41 TestRegistry *cnabtooci.TestRegistry 42 TestSecrets secrets.Store 43 TestSanitizer *storage.Sanitizer 44 45 // original directory where the test was being executed 46 TestDir string 47 48 // directory where the integration test is being executed 49 BundleDir string 50 51 // root of the repository 52 // Helps us avoid hard coding relative paths from test directories, which easily break when tests are moved 53 RepoRoot string 54 55 // The root test context created by NewTestPorter 56 RootContext context.Context 57 58 // The root log span created by NewTestPorter 59 RootSpan tracing.TraceLogger 60 } 61 62 // NewTestPorter initializes a porter test client, with the output buffered, and an in-memory file system. 63 func NewTestPorter(t *testing.T) *TestPorter { 64 tc := config.NewTestConfig(t) 65 testStore := storage.NewTestStore(tc) 66 testSecrets := secrets.NewTestSecretsProvider() 67 testSigner := signing.NewTestSigningProvider() 68 testCredentials := storage.NewTestCredentialProviderFor(t, testStore, testSecrets) 69 testParameters := storage.NewTestParameterProviderFor(t, testStore, testSecrets) 70 testCache := cache.NewTestCache(cache.New(tc.Config)) 71 testInstallations := storage.NewTestInstallationProviderFor(t, testStore) 72 testRegistry := cnabtooci.NewTestRegistry() 73 74 p := NewFor(tc.Config, testStore, testSecrets, testSigner) 75 p.Config = tc.Config 76 p.Mixins = mixin.NewTestMixinProvider() 77 p.Plugins = plugins.NewTestPluginProvider() 78 p.Cache = testCache 79 p.builder = NewTestBuildProvider() 80 p.Installations = testInstallations 81 p.Credentials = testCredentials 82 p.Parameters = testParameters 83 p.Secrets = testSecrets 84 p.CNAB = cnabprovider.NewTestRuntimeFor(tc, testInstallations, testCredentials, testParameters, testSecrets) 85 p.Registry = testRegistry 86 87 tp := TestPorter{ 88 Porter: p, 89 TestConfig: tc, 90 TestStore: testStore, 91 TestSecrets: testSecrets, 92 TestInstallations: testInstallations, 93 TestCredentials: testCredentials, 94 TestParameters: testParameters, 95 TestCache: testCache, 96 TestRegistry: testRegistry, 97 TestSanitizer: storage.NewSanitizer(testParameters, testSecrets), 98 RepoRoot: tc.TestContext.FindRepoRoot(), 99 } 100 101 // Start a tracing span for the test, so that we can capture logs 102 tp.RootContext, tp.RootSpan = p.StartRootSpan(context.Background(), t.Name()) 103 104 return &tp 105 } 106 107 func (p *TestPorter) Close() error { 108 err := p.TestStore.Close() 109 p.TestConfig.Close() 110 p.RootSpan.EndSpan() 111 return err 112 } 113 114 func (p *TestPorter) SetupIntegrationTest() context.Context { 115 t := p.TestConfig.TestContext.T 116 117 // Undo changes above to make a unit test friendly Porter, so we hit the host 118 p.Porter = NewFor(p.Config, p.TestStore, p.TestSecrets, p.Signer) 119 120 // Run the test in a temp directory 121 ctx, testDir, _ := p.TestConfig.SetupIntegrationTest() 122 p.TestDir = testDir 123 p.CreateBundleDir() 124 125 // Write out a storage schema so that we don't trigger a migration check 126 err := p.Storage.WriteSchema(ctx) 127 require.NoError(t, err, "failed to set the storage schema") 128 129 // Load test credentials, with KUBECONFIG replaced properly 130 kubeconfig := filepath.Join(p.RepoRoot, "kind.config") 131 if runtime.GOOS == "windows" { 132 kubeconfig = strings.Replace(kubeconfig, `\`, `\\`, -1) 133 } 134 ciCredsPath := filepath.Join(p.RepoRoot, "build/testdata/credentials/ci.json") 135 ciCredsB, err := p.FileSystem.ReadFile(ciCredsPath) 136 require.NoError(t, err, "could not read test credentials %s", ciCredsPath) 137 // update the kubeconfig reference in the credentials to match what's on people's dev machine 138 ciCredsB = []byte(strings.Replace(string(ciCredsB), "KUBECONFIGPATH", kubeconfig, -1)) 139 var testCreds storage.CredentialSet 140 err = encoding.UnmarshalJson(ciCredsB, &testCreds) 141 require.NoError(t, err, "could not unmarshal test credentials %s", ciCredsPath) 142 err = p.Credentials.UpsertCredentialSet(context.Background(), testCreds) 143 require.NoError(t, err, "could not save test credentials (ci)") 144 145 // Make a copy of the creds with a different name so that we can test out switching to different credential sets 146 testCreds.Name = "ci2" 147 err = p.Credentials.UpsertCredentialSet(context.Background(), testCreds) 148 require.NoError(t, err, "could not save test credentials (ci2)") 149 150 return ctx 151 } 152 153 func (p *TestPorter) AddTestFile(src string, dest string) { 154 if !filepath.IsAbs(src) { 155 src = filepath.Join(p.TestDir, src) 156 } 157 158 p.TestConfig.TestContext.AddTestFile(src, dest) 159 } 160 161 type TestDriver struct { 162 Name string 163 Filepath string 164 } 165 166 func (p *TestPorter) AddTestDriver(driver TestDriver) string { 167 if !filepath.IsAbs(driver.Filepath) { 168 driver.Filepath = filepath.Join(p.TestDir, driver.Filepath) 169 } 170 171 return p.TestConfig.TestContext.AddTestDriver(driver.Filepath, driver.Name) 172 } 173 174 func (p *TestPorter) CreateBundleDir() string { 175 bundleDir, err := os.MkdirTemp("", "bundle") 176 require.NoError(p.T(), err) 177 178 p.BundleDir = bundleDir 179 p.Chdir(bundleDir) 180 p.TestConfig.TestContext.AddCleanupDir(p.BundleDir) 181 182 return bundleDir 183 } 184 185 func (p *TestPorter) T() *testing.T { 186 return p.TestConfig.TestContext.T 187 } 188 189 func (p *TestPorter) ReadBundle(path string) cnab.ExtendedBundle { 190 bunD, err := os.ReadFile(path) 191 require.NoError(p.T(), err, "ReadFile failed for %s", path) 192 193 bun, err := bundle.Unmarshal(bunD) 194 require.NoError(p.T(), err, "Unmarshal failed for bundle at %s", path) 195 196 return cnab.NewBundle(*bun) 197 } 198 199 func (p *TestPorter) RandomString(len int) string { 200 rand.New(rand.NewSource((time.Now().UnixNano()))) 201 bytes := make([]byte, len) 202 for i := 0; i < len; i++ { 203 //A=97 and Z = 97+25 204 bytes[i] = byte(97 + rand.Intn(25)) 205 } 206 return string(bytes) 207 } 208 209 // AddTestBundleDir into the test bundle directory and give it a unique name 210 // to avoid collisions with other tests running in parallel. 211 func (p *TestPorter) AddTestBundleDir(bundleDir string, generateUniqueName bool) string { 212 if !filepath.IsAbs(bundleDir) { 213 bundleDir = filepath.Join(p.TestDir, bundleDir) 214 } 215 p.TestConfig.TestContext.AddTestDirectory(bundleDir, p.BundleDir) 216 217 testManifest := filepath.Join(p.BundleDir, config.Name) 218 m, err := manifest.LoadManifestFrom(p.RootContext, p.Config, testManifest) 219 require.NoError(p.T(), err) 220 221 if !generateUniqueName { 222 return m.Name 223 } 224 225 e := yaml.NewEditor(p.FileSystem) 226 err = e.ReadFile(testManifest) 227 require.NoError(p.T(), err) 228 229 uniqueName := fmt.Sprintf("%s-%s", m.Name, p.RandomString(5)) 230 err = e.SetValue("name", uniqueName) 231 require.NoError(p.T(), err) 232 233 err = e.WriteFile(testManifest) 234 require.NoError(p.T(), err) 235 236 return uniqueName 237 } 238 239 // CompareGoldenFile checks if the specified string matches the content of a golden test file. 240 // When they are different and PORTER_UPDATE_TEST_FILES is true, the file is updated to match 241 // the new test output. 242 func (p *TestPorter) CompareGoldenFile(goldenFile string, got string) { 243 p.TestConfig.TestContext.CompareGoldenFile(goldenFile, got) 244 } 245 246 // CreateInstallation saves an installation record into claim store and store 247 // sensitive parameters into secret store. 248 func (p *TestPorter) SanitizeParameters(raw []secrets.SourceMap, recordID string, bun cnab.ExtendedBundle) []secrets.SourceMap { 249 strategies, err := p.Sanitizer.CleanParameters(context.Background(), raw, bun, recordID) 250 require.NoError(p.T(), err) 251 252 return strategies 253 } 254 255 func (p *TestPorter) CreateOutput(o storage.Output, bun cnab.ExtendedBundle) storage.Output { 256 return p.TestInstallations.CreateOutput(o, func(o *storage.Output) { 257 output, err := p.TestSanitizer.CleanOutput(context.Background(), *o, bun) 258 require.NoError(p.T(), err) 259 *o = output 260 }) 261 } 262 263 type TestBuildProvider struct { 264 } 265 266 func NewTestBuildProvider() *TestBuildProvider { 267 return &TestBuildProvider{} 268 } 269 270 func (t *TestBuildProvider) BuildBundleImage(ctx context.Context, manifest *manifest.Manifest, opts build.BuildImageOptions) error { 271 return nil 272 } 273 274 func (t *TestBuildProvider) TagBundleImage(ctx context.Context, origTag, newTag string) error { 275 return nil 276 }