get.porter.sh/porter@v1.3.0/pkg/porter/explain_test.go (about) 1 package porter 2 3 import ( 4 "fmt" 5 "testing" 6 7 "get.porter.sh/porter/pkg/cnab" 8 depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1" 9 "get.porter.sh/porter/pkg/portercontext" 10 "get.porter.sh/porter/pkg/test" 11 "github.com/cnabio/cnab-go/bundle" 12 "github.com/cnabio/cnab-go/bundle/definition" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestExplain_ValidateReference(t *testing.T) { 18 const ref = "ghcr.io/getporter/examples/porter-hello:v0.2.0" 19 t.Run("--reference specified", func(t *testing.T) { 20 porterCtx := portercontext.NewTestContext(t) 21 opts := ExplainOpts{} 22 opts.Reference = ref 23 24 err := opts.Validate(nil, porterCtx.Context) 25 require.NoError(t, err, "Validate failed") 26 assert.Equal(t, ref, opts.Reference) 27 }) 28 t.Run("reference positional argument specified", func(t *testing.T) { 29 porterCtx := portercontext.NewTestContext(t) 30 opts := ExplainOpts{} 31 32 err := opts.Validate([]string{ref}, porterCtx.Context) 33 require.NoError(t, err, "Validate failed") 34 assert.Equal(t, ref, opts.Reference) 35 }) 36 } 37 38 func TestExplain_validateBadFormat(t *testing.T) { 39 p := NewTestPorter(t) 40 defer p.Close() 41 42 opts := ExplainOpts{} 43 opts.RawFormat = "vpml" 44 45 err := opts.Validate([]string{}, p.Context) 46 assert.EqualError(t, err, "invalid format: vpml") 47 } 48 49 func TestExplain_generateTable(t *testing.T) { 50 p := NewTestPorter(t) 51 defer p.Close() 52 53 p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle.json", "params-bundle.json") 54 b, err := p.CNAB.LoadBundle("params-bundle.json") 55 require.NoError(t, err) 56 57 pb, err := generatePrintable(b, "") 58 require.NoError(t, err) 59 opts := ExplainOpts{} 60 opts.RawFormat = "plaintext" 61 62 err = opts.Validate([]string{}, p.Context) 63 require.NoError(t, err) 64 65 err = p.printBundleExplain(opts, pb, b) 66 assert.NoError(t, err) 67 68 gotOutput := p.TestConfig.TestContext.GetOutput() 69 test.CompareGoldenFile(t, "testdata/explain/expected-table-output.txt", gotOutput) 70 } 71 72 func TestExplain_generateTableRequireDockerHostAccess(t *testing.T) { 73 p := NewTestPorter(t) 74 defer p.Close() 75 76 p.TestConfig.TestContext.AddTestFile("testdata/explain/bundle-docker.json", "bundle-docker.json") 77 b, err := p.CNAB.LoadBundle("bundle-docker.json") 78 require.NoError(t, err) 79 80 pb, err := generatePrintable(b, "") 81 require.NoError(t, err) 82 opts := ExplainOpts{} 83 opts.RawFormat = "plaintext" 84 85 err = opts.Validate([]string{}, p.Context) 86 require.NoError(t, err) 87 88 err = p.printBundleExplain(opts, pb, b) 89 assert.NoError(t, err) 90 gotOutput := p.TestConfig.TestContext.GetOutput() 91 p.CompareGoldenFile("testdata/explain/expected-table-output-docker.txt", gotOutput) 92 } 93 94 func TestExplain_generateJSON(t *testing.T) { 95 p := NewTestPorter(t) 96 defer p.Close() 97 98 p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle.json", "params-bundle.json") 99 b, err := p.CNAB.LoadBundle("params-bundle.json") 100 require.NoError(t, err) 101 102 pb, err := generatePrintable(b, "") 103 require.NoError(t, err) 104 opts := ExplainOpts{} 105 opts.RawFormat = "json" 106 107 err = opts.Validate([]string{}, p.Context) 108 require.NoError(t, err) 109 110 err = p.printBundleExplain(opts, pb, b) 111 assert.NoError(t, err) 112 gotOutput := p.TestConfig.TestContext.GetOutput() 113 p.CompareGoldenFile("testdata/explain/expected-json-output.json", gotOutput) 114 } 115 116 func TestExplain_generateYAML(t *testing.T) { 117 p := NewTestPorter(t) 118 defer p.Close() 119 120 p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle.json", "params-bundle.json") 121 b, err := p.CNAB.LoadBundle("params-bundle.json") 122 require.NoError(t, err) 123 124 pb, err := generatePrintable(b, "") 125 require.NoError(t, err) 126 127 opts := ExplainOpts{} 128 opts.RawFormat = "yaml" 129 130 err = opts.Validate([]string{}, p.Context) 131 require.NoError(t, err) 132 133 err = p.printBundleExplain(opts, pb, b) 134 assert.NoError(t, err) 135 gotOutput := p.TestConfig.TestContext.GetOutput() 136 p.CompareGoldenFile("testdata/explain/expected-yaml-output.yaml", gotOutput) 137 } 138 139 func TestExplain_generatePrintableBundleParams(t *testing.T) { 140 bun := cnab.NewBundle(bundle.Bundle{ 141 RequiredExtensions: []string{ 142 cnab.FileParameterExtensionKey, 143 }, 144 Definitions: definition.Definitions{ 145 "string": &definition.Schema{ 146 Type: "string", 147 Default: "clippy", 148 }, 149 "file": &definition.Schema{ 150 Type: "string", 151 ContentEncoding: "base64", 152 }, 153 }, 154 Parameters: map[string]bundle.Parameter{ 155 "debug": { 156 Definition: "string", 157 Required: true, 158 }, 159 "tfstate": { 160 Definition: "file", 161 }, 162 }, 163 Custom: map[string]interface{}{ 164 "sh.porter": map[string]interface{}{ 165 "manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0", 166 "version": "v0.30.0", 167 "commit": "3b7c85ba", 168 }, 169 }, 170 }) 171 172 pb, err := generatePrintable(bun, "") 173 require.NoError(t, err) 174 175 require.Equal(t, 2, len(pb.Parameters), "expected 2 parameters") 176 d := pb.Parameters[0] 177 require.Equal(t, "debug", d.Name) 178 assert.Equal(t, "clippy", fmt.Sprintf("%v", d.Default)) 179 assert.Equal(t, "string", d.Type) 180 f := pb.Parameters[1] 181 require.Equal(t, "tfstate", f.Name) 182 assert.Equal(t, "file", f.Type) 183 184 assert.Equal(t, 0, len(pb.Outputs)) 185 assert.Equal(t, 0, len(pb.Credentials)) 186 assert.Equal(t, 0, len(pb.Actions)) 187 } 188 189 func TestExplain_generatePrintableBundleParamsWithAction(t *testing.T) { 190 bun := cnab.NewBundle(bundle.Bundle{ 191 RequiredExtensions: []string{ 192 cnab.FileParameterExtensionKey, 193 }, 194 Definitions: definition.Definitions{ 195 "string": &definition.Schema{ 196 Type: "string", 197 Default: "clippy", 198 }, 199 "file": &definition.Schema{ 200 Type: "string", 201 ContentEncoding: "base64", 202 }, 203 }, 204 Parameters: map[string]bundle.Parameter{ 205 "debug": { 206 Definition: "string", 207 Required: true, 208 ApplyTo: []string{"install"}, 209 }, 210 "tfstate": { 211 Definition: "file", 212 }, 213 }, 214 Custom: map[string]interface{}{ 215 "sh.porter": map[string]interface{}{ 216 "manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0", 217 "version": "v0.30.0", 218 "commit": "3b7c85ba", 219 }, 220 }, 221 }) 222 223 t.Run("action applies", func(t *testing.T) { 224 pb, err := generatePrintable(bun, "install") 225 require.NoError(t, err) 226 227 require.Equal(t, 2, len(pb.Parameters), "expected 2 parameters") 228 229 d := pb.Parameters[0] 230 require.Equal(t, "debug", d.Name) 231 assert.Equal(t, "install", d.ApplyTo) 232 233 f := pb.Parameters[1] 234 require.Equal(t, "tfstate", f.Name) 235 assert.Equal(t, "All Actions", f.ApplyTo) 236 }) 237 238 t.Run("action does not apply", func(t *testing.T) { 239 pb, err := generatePrintable(bun, "upgrade") 240 require.NoError(t, err) 241 242 require.Equal(t, 1, len(pb.Parameters), "expected only 1 parameter since debug parameter doesn't apply to upgrade command") 243 require.Equal(t, "tfstate", pb.Parameters[0].Name) 244 }) 245 246 t.Run("all actions", func(t *testing.T) { 247 pb, err := generatePrintable(bun, "") 248 require.NoError(t, err) 249 250 require.Equal(t, 2, len(pb.Parameters), "expected 2 parameters") 251 252 d := pb.Parameters[0] 253 require.Equal(t, "debug", d.Name) 254 assert.Equal(t, "install", d.ApplyTo) 255 256 f := pb.Parameters[1] 257 require.Equal(t, "tfstate", f.Name) 258 assert.Equal(t, "All Actions", f.ApplyTo) 259 }) 260 } 261 262 func TestExplain_generatePrintableBundleOutputs(t *testing.T) { 263 bun := cnab.NewBundle(bundle.Bundle{ 264 Definitions: definition.Definitions{ 265 "string": &definition.Schema{ 266 Type: "string", 267 Default: "clippy", 268 }, 269 }, 270 Outputs: map[string]bundle.Output{ 271 "debug": { 272 Definition: "string", 273 }, 274 "someoutput": { 275 Definition: "string", 276 ApplyTo: []string{"install"}, 277 }, 278 }, 279 Custom: map[string]interface{}{ 280 "sh.porter": map[string]interface{}{ 281 "manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0", 282 "version": "v0.30.0", 283 "commit": "3b7c85ba", 284 }, 285 }, 286 }) 287 288 pb, err := generatePrintable(bun, "") 289 require.NoError(t, err) 290 291 require.Equal(t, 2, len(pb.Outputs), "expected someoutput to be included because the action is unset") 292 debugOutput := pb.Outputs[0] 293 assert.Equal(t, "string", fmt.Sprintf("%v", debugOutput.Type)) 294 assert.Equal(t, "debug", debugOutput.Name) 295 assert.Equal(t, "All Actions", debugOutput.ApplyTo) 296 297 someOutput := pb.Outputs[1] 298 assert.Equal(t, "string", fmt.Sprintf("%v", someOutput.Type)) 299 assert.Equal(t, "someoutput", someOutput.Name) 300 assert.Equal(t, "install", someOutput.ApplyTo) 301 302 assert.Equal(t, 0, len(pb.Parameters)) 303 assert.Equal(t, 0, len(pb.Credentials)) 304 assert.Equal(t, 0, len(pb.Actions)) 305 306 // Check outputs for install action 307 pb, err = generatePrintable(bun, "install") 308 require.NoError(t, err) 309 assert.Equal(t, 2, len(pb.Outputs), "expected someoutput to be included") 310 311 // Check outputs for upgrade action action (someoutput doesn't apply) 312 pb, err = generatePrintable(bun, "upgrade") 313 require.NoError(t, err) 314 assert.Equal(t, 1, len(pb.Outputs), "expected someoutput to be excluded by its applyTo") 315 } 316 317 func TestExplain_generatePrintableBundleCreds(t *testing.T) { 318 bun := cnab.NewBundle(bundle.Bundle{ 319 Credentials: map[string]bundle.Credential{ 320 "kubeconfig": { 321 Required: true, 322 Description: "a cred", 323 ApplyTo: []string{"install"}, 324 }, 325 "password": { 326 Description: "another cred", 327 }, 328 }, 329 Custom: map[string]interface{}{ 330 "sh.porter": map[string]interface{}{ 331 "manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0", 332 "version": "v0.30.0", 333 "commit": "3b7c85ba", 334 }, 335 }, 336 }) 337 338 t.Run("action applies", func(t *testing.T) { 339 pb, err := generatePrintable(bun, "install") 340 require.NoError(t, err) 341 342 require.Equal(t, 2, len(pb.Credentials), "expected 2 credentials") 343 344 d := pb.Credentials[0] 345 require.Equal(t, "kubeconfig", d.Name) 346 assert.Equal(t, "install", d.ApplyTo) 347 348 f := pb.Credentials[1] 349 require.Equal(t, "password", f.Name) 350 assert.Equal(t, "All Actions", f.ApplyTo) 351 }) 352 353 t.Run("action does not apply", func(t *testing.T) { 354 pb, err := generatePrintable(bun, "upgrade") 355 require.NoError(t, err) 356 357 require.Equal(t, 1, len(pb.Credentials), "expected only 1 credential since kubeconfig credential doesn't apply to upgrade command") 358 require.Equal(t, "password", pb.Credentials[0].Name) 359 }) 360 361 t.Run("all actions", func(t *testing.T) { 362 pb, err := generatePrintable(bun, "") 363 require.NoError(t, err) 364 365 require.Equal(t, 2, len(pb.Credentials), "expected 2 credentials") 366 367 d := pb.Credentials[0] 368 require.Equal(t, "kubeconfig", d.Name) 369 assert.Equal(t, "install", d.ApplyTo) 370 371 f := pb.Credentials[1] 372 require.Equal(t, "password", f.Name) 373 assert.Equal(t, "All Actions", f.ApplyTo) 374 }) 375 } 376 377 func TestExplain_generatePrintableBundlePorterVersion(t *testing.T) { 378 bun := cnab.NewBundle(bundle.Bundle{ 379 Definitions: definition.Definitions{ 380 "string": &definition.Schema{ 381 Type: "string", 382 Default: "clippy", 383 }, 384 }, 385 Custom: map[string]interface{}{ 386 "sh.porter": map[string]interface{}{ 387 "manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0", 388 "version": "v0.30.0", 389 "commit": "3b7c85ba", 390 }, 391 }, 392 }) 393 394 pb, err := generatePrintable(bun, "") 395 assert.NoError(t, err) 396 397 assert.Equal(t, "v0.30.0", pb.PorterVersion) 398 } 399 400 func TestExplain_generatePrintableBundlePorterVersionNonPorterBundle(t *testing.T) { 401 bun := cnab.NewBundle(bundle.Bundle{ 402 Definitions: definition.Definitions{ 403 "string": &definition.Schema{ 404 Type: "string", 405 Default: "clippy", 406 }, 407 }, 408 }) 409 410 pb, err := generatePrintable(bun, "") 411 assert.NoError(t, err) 412 413 assert.Equal(t, "", pb.PorterVersion) 414 } 415 416 func TestExplain_generatePrintableBundleDependencies(t *testing.T) { 417 sequenceMock := []string{"nginx", "storage", "mysql"} 418 bun := cnab.NewBundle(bundle.Bundle{ 419 Custom: map[string]interface{}{ 420 cnab.DependenciesV1ExtensionKey: depsv1ext.Dependencies{ 421 Sequence: sequenceMock, 422 Requires: map[string]depsv1ext.Dependency{ 423 "mysql": { 424 Name: "mysql", 425 Bundle: "somecloud/mysql:0.1.0", 426 }, 427 "storage": { 428 Name: "storage", 429 Bundle: "localhost:5000/blob-storage:0.1.0", 430 }, 431 "nginx": { 432 Name: "nginx", 433 Bundle: "localhost:5000/nginx:1.19", 434 }, 435 }, 436 }, 437 "sh.porter": map[string]interface{}{ 438 "manifest": "5040d45d0c44e7632563966c33f5e8980e83cfa7c0485f725b623b7604f072f0", 439 "version": "v0.30.0", 440 "commit": "3b7c85ba", 441 }, 442 }, 443 }) 444 445 pd, err := generatePrintable(bun, "") 446 assert.NoError(t, err) 447 assert.Equal(t, 3, len(pd.Dependencies)) 448 assert.Equal(t, 0, len(pd.Parameters)) 449 assert.Equal(t, 0, len(pd.Outputs)) 450 assert.Equal(t, 0, len(pd.Actions)) 451 assert.Equal(t, "nginx", pd.Dependencies[0].Alias) 452 assert.Equal(t, "somecloud/mysql:0.1.0", pd.Dependencies[2].Reference) 453 } 454 455 func TestExplain_generateJSONForDependencies(t *testing.T) { 456 p := NewTestPorter(t) 457 defer p.Close() 458 459 p.TestConfig.TestContext.AddTestFile("testdata/explain/dependencies-bundle.json", "dependencies-bundle.json") 460 b, err := p.CNAB.LoadBundle("dependencies-bundle.json") 461 require.NoError(t, err) 462 463 pb, err := generatePrintable(b, "") 464 require.NoError(t, err) 465 opts := ExplainOpts{} 466 opts.RawFormat = "json" 467 468 err = opts.Validate([]string{}, p.Context) 469 require.NoError(t, err) 470 471 err = p.printBundleExplain(opts, pb, b) 472 assert.NoError(t, err) 473 gotOutput := p.TestConfig.TestContext.GetOutput() 474 475 p.CompareGoldenFile("testdata/explain/expected-json-dependencies-output.json", gotOutput) 476 } 477 478 func TestExplain_generateTableNonPorterBundle(t *testing.T) { 479 p := NewTestPorter(t) 480 defer p.Close() 481 482 p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle-non-porter.json", "params-bundle.json") 483 b, err := p.CNAB.LoadBundle("params-bundle.json") 484 require.NoError(t, err) 485 486 pb, err := generatePrintable(b, "") 487 require.NoError(t, err) 488 opts := ExplainOpts{} 489 opts.RawFormat = "plaintext" 490 491 err = opts.Validate([]string{}, p.Context) 492 require.NoError(t, err) 493 494 err = p.printBundleExplain(opts, pb, b) 495 assert.NoError(t, err) 496 497 gotOutput := p.TestConfig.TestContext.GetOutput() 498 test.CompareGoldenFile(t, "testdata/explain/expected-table-output-non-porter.txt", gotOutput) 499 } 500 501 func TestExplain_generateTableBundleWithNoMixins(t *testing.T) { 502 p := NewTestPorter(t) 503 defer p.Close() 504 505 p.TestConfig.TestContext.AddTestFile("testdata/explain/params-bundle-no-mixins.json", "params-bundle.json") 506 b, err := p.CNAB.LoadBundle("params-bundle.json") 507 require.NoError(t, err) 508 509 pb, err := generatePrintable(b, "") 510 require.NoError(t, err) 511 opts := ExplainOpts{} 512 opts.RawFormat = "plaintext" 513 514 err = opts.Validate([]string{}, p.Context) 515 require.NoError(t, err) 516 517 err = p.printBundleExplain(opts, pb, b) 518 assert.NoError(t, err) 519 520 gotOutput := p.TestConfig.TestContext.GetOutput() 521 test.CompareGoldenFile(t, "testdata/explain/expected-table-output-no-mixins.txt", gotOutput) 522 }