get.porter.sh/porter@v1.3.0/pkg/cnab/config-adapter/adapter.go (about) 1 package configadapter 2 3 import ( 4 "context" 5 "fmt" 6 "path" 7 "strings" 8 9 "get.porter.sh/porter/pkg/cnab" 10 depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1" 11 depsv2ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v2" 12 "get.porter.sh/porter/pkg/config" 13 "get.porter.sh/porter/pkg/experimental" 14 "get.porter.sh/porter/pkg/manifest" 15 "get.porter.sh/porter/pkg/mixin" 16 "get.porter.sh/porter/pkg/tracing" 17 "github.com/Masterminds/semver/v3" 18 "github.com/cnabio/cnab-go/bundle" 19 "github.com/cnabio/cnab-go/bundle/definition" 20 ) 21 22 // ManifestConverter converts from a porter manifest to a CNAB bundle definition. 23 type ManifestConverter struct { 24 config *config.Config 25 Manifest *manifest.Manifest 26 ImageDigests map[string]string 27 InstalledMixins []mixin.Metadata 28 PreserveTags bool 29 } 30 31 func NewManifestConverter( 32 config *config.Config, 33 manifest *manifest.Manifest, 34 imageDigests map[string]string, 35 mixins []mixin.Metadata, 36 preserveTags bool, 37 ) *ManifestConverter { 38 return &ManifestConverter{ 39 config: config, 40 Manifest: manifest, 41 ImageDigests: imageDigests, 42 InstalledMixins: mixins, 43 PreserveTags: preserveTags, 44 } 45 } 46 47 func (c *ManifestConverter) ToBundle(ctx context.Context) (cnab.ExtendedBundle, error) { 48 ctx, span := tracing.StartSpan(ctx) 49 defer span.EndSpan() 50 51 stamp, err := c.GenerateStamp(ctx, c.PreserveTags) 52 if err != nil { 53 return cnab.ExtendedBundle{}, span.Error(err) 54 } 55 56 b := cnab.NewBundle(bundle.Bundle{ 57 SchemaVersion: cnab.BundleSchemaVersion(), 58 Name: c.Manifest.Name, 59 Description: c.Manifest.Description, 60 Version: c.Manifest.Version, 61 Maintainers: c.generateBundleMaintainers(), 62 Custom: make(map[string]interface{}, 1), 63 }) 64 image := bundle.InvocationImage{ 65 BaseImage: bundle.BaseImage{ 66 Image: c.Manifest.Image, 67 ImageType: "docker", 68 Digest: c.ImageDigests[c.Manifest.Image], 69 }, 70 } 71 72 b.Actions = c.generateCustomActionDefinitions() 73 b.Definitions = make(definition.Definitions, len(c.Manifest.Parameters)+len(c.Manifest.Outputs)+len(c.Manifest.StateBag)) 74 b.InvocationImages = []bundle.InvocationImage{image} 75 b.Parameters = c.generateBundleParameters(ctx, &b.Definitions) 76 b.Outputs = c.generateBundleOutputs(ctx, &b.Definitions) 77 b.Credentials = c.generateBundleCredentials() 78 b.Images = c.generateBundleImages() 79 custom, err := c.generateCustomExtensions(ctx, &b) 80 if err != nil { 81 return cnab.ExtendedBundle{}, err 82 } 83 b.Custom = custom 84 b.RequiredExtensions = c.generateRequiredExtensions(b) 85 86 b.Custom[config.CustomPorterKey] = stamp 87 88 return b, nil 89 } 90 91 func (c *ManifestConverter) generateBundleMaintainers() []bundle.Maintainer { 92 m := make([]bundle.Maintainer, len(c.Manifest.Maintainers)) 93 for i, item := range c.Manifest.Maintainers { 94 m[i] = bundle.Maintainer{ 95 Name: item.Name, 96 Email: item.Email, 97 URL: item.Url, 98 } 99 } 100 return m 101 } 102 103 func (c *ManifestConverter) generateCustomActionDefinitions() map[string]bundle.Action { 104 if len(c.Manifest.CustomActions) == 0 { 105 return nil 106 } 107 108 defs := make(map[string]bundle.Action, len(c.Manifest.CustomActions)) 109 for action, def := range c.Manifest.CustomActionDefinitions { 110 def := bundle.Action{ 111 Description: def.Description, 112 Modifies: def.ModifiesResources, 113 Stateless: def.Stateless, 114 } 115 defs[action] = def 116 } 117 118 // If they used a custom action but didn't define it, default it to a safe action definition 119 for action := range c.Manifest.CustomActions { 120 if _, ok := c.Manifest.CustomActionDefinitions[action]; !ok { 121 defs[action] = c.generateDefaultAction(action) 122 } 123 } 124 125 return defs 126 } 127 128 func (c *ManifestConverter) generateDefaultAction(action string) bundle.Action { 129 // See https://github.com/cnabio/cnab-spec/blob/master/804-well-known-custom-actions.md 130 switch action { 131 case "dry-run", "io.cnab.dry-run": 132 return bundle.Action{ 133 Description: "Execute the installation in a dry-run mode, allowing to see what would happen with the given set of parameter values", 134 Modifies: false, 135 Stateless: true, 136 } 137 case "help", "io.cnab.help": 138 return bundle.Action{ 139 Description: "Print a help message to the standard output", 140 Modifies: false, 141 Stateless: true, 142 } 143 case "log", "io.cnab.log": 144 return bundle.Action{ 145 Description: "Print logs of the installed system to the standard output", 146 Modifies: false, 147 Stateless: false, 148 } 149 case "status", "io.cnab.status": 150 return bundle.Action{ 151 Description: "Print a human readable status message to the standard output", 152 Modifies: false, 153 Stateless: false, 154 } 155 case "status+json", "io.cnab.status+json": 156 return bundle.Action{ 157 Description: "Print a json payload describing the detailed status with the following the CNAB status schema", 158 Modifies: false, 159 Stateless: false, 160 } 161 default: 162 // By default assume that any custom action could modify state 163 return bundle.Action{ 164 Description: action, 165 Modifies: true, 166 Stateless: false, 167 } 168 } 169 } 170 171 func (c *ManifestConverter) generateBundleParameters(ctx context.Context, defs *definition.Definitions) map[string]bundle.Parameter { 172 params := make(map[string]bundle.Parameter, len(c.Manifest.Parameters)) 173 174 for _, p := range c.Manifest.Parameters { 175 params[p.Name] = c.generateParameterDefinition(ctx, defs, p) 176 } 177 178 for _, p := range c.buildDefaultPorterParameters() { 179 params[p.Name] = c.generateParameterDefinition(ctx, defs, p) 180 } 181 182 return params 183 } 184 185 func (c *ManifestConverter) generateBundleOutputs(ctx context.Context, defs *definition.Definitions) map[string]bundle.Output { 186 187 outputs := make(map[string]bundle.Output, len(c.Manifest.Outputs)) 188 189 for _, o := range c.Manifest.Outputs { 190 outputs[o.Name] = c.generateOutputDefinition(ctx, defs, o) 191 } 192 193 for _, o := range c.buildDefaultPorterOutputs() { 194 outputs[o.Name] = c.generateOutputDefinition(ctx, defs, o) 195 } 196 197 return outputs 198 } 199 200 func (c *ManifestConverter) generateOutputDefinition(ctx context.Context, defs *definition.Definitions, 201 sourceOutput manifest.OutputDefinition) bundle.Output { 202 log := tracing.LoggerFromContext(ctx) 203 204 destinationOutput := bundle.Output{ 205 Definition: sourceOutput.Name, 206 Description: sourceOutput.Description, 207 ApplyTo: sourceOutput.ApplyTo, 208 // must be a standard Unix path as this will be inside of the container 209 // (only linux containers supported currently) 210 Path: path.Join(config.BundleOutputsDir, sourceOutput.Name), 211 } 212 213 if sourceOutput.Sensitive { 214 sourceOutput.Schema.WriteOnly = toBool(true) 215 } 216 217 if sourceOutput.Type == nil { 218 // Default to a file type if the param is stored in a file 219 if sourceOutput.Path != "" { 220 sourceOutput.Type = "file" 221 } else { 222 // Assume it's a string otherwise 223 sourceOutput.Type = "string" 224 } 225 log.Debugf("Defaulting the type of output %s to %s", sourceOutput.Name, sourceOutput.Type) 226 } 227 228 // Create a definition that matches the output if one isn't already defined 229 if _, ok := (*defs)[sourceOutput.Name]; !ok { 230 kind := "output" 231 if sourceOutput.IsState { 232 kind = "state" 233 } 234 235 defName := c.addDefinition(sourceOutput.Name, kind, sourceOutput.Schema, defs) 236 destinationOutput.Definition = defName 237 } 238 return destinationOutput 239 240 } 241 242 func (c *ManifestConverter) generateParameterDefinition(ctx context.Context, defs *definition.Definitions, 243 sourceParam manifest.ParameterDefinition) bundle.Parameter { 244 log := tracing.LoggerFromContext(ctx) 245 246 // Update ApplyTo per parameter definition and manifest 247 sourceParam.UpdateApplyTo(c.Manifest) 248 249 p := bundle.Parameter{ 250 Definition: sourceParam.Name, 251 ApplyTo: sourceParam.ApplyTo, 252 Description: sourceParam.Description, 253 } 254 255 // If the default is empty, set required to true. 256 // State parameters are always optional, and don't have a default 257 if sourceParam.Default == nil && !sourceParam.IsState { 258 p.Required = true 259 } 260 261 if sourceParam.Sensitive { 262 sourceParam.Schema.WriteOnly = toBool(true) 263 } 264 265 if !sourceParam.Destination.IsEmpty() { 266 p.Destination = &bundle.Location{ 267 EnvironmentVariable: sourceParam.Destination.EnvironmentVariable, 268 Path: manifest.ResolvePath(sourceParam.Destination.Path), 269 } 270 } else { 271 p.Destination = &bundle.Location{ 272 EnvironmentVariable: manifest.ParamToEnvVar(sourceParam.Name), 273 } 274 } 275 276 if sourceParam.Type == nil { 277 // Default to a file type if the param is stored in a file 278 if sourceParam.Destination.Path != "" { 279 sourceParam.Type = "file" 280 } else { 281 // Assume it's a string otherwise 282 sourceParam.Type = "string" 283 } 284 285 log.Debugf("Defaulting the type of parameter %s to %s", sourceParam.Name, sourceParam.Type) 286 } 287 288 // Create a definition that matches the parameter if one isn't already defined 289 if _, ok := (*defs)[sourceParam.Name]; !ok { 290 kind := "parameter" 291 if sourceParam.IsState { 292 kind = "state" 293 } 294 295 defName := c.addDefinition(sourceParam.Name, kind, sourceParam.Schema, defs) 296 p.Definition = defName 297 } 298 return p 299 300 } 301 302 func (c *ManifestConverter) generateCredentialDefinition(sourceCred manifest.CredentialDefinition) bundle.Credential { 303 destCred := bundle.Credential{ 304 Description: sourceCred.Description, 305 Required: sourceCred.Required, 306 // we are intentionally not mapping location and applyto 307 // it is not relevant to a bundle interface 308 // for example, they are only relevant inside the bundle 309 // they don't impact your ability to swap out one bundle for another 310 } 311 return destCred 312 } 313 314 func (c *ManifestConverter) addDefinition(name string, kind string, def definition.Schema, defs *definition.Definitions) string { 315 defName := name 316 if !strings.HasSuffix(name, "-"+kind) { 317 defName = name + "-" + kind 318 } 319 320 // file is a porter specific type, swap it out for something CNAB understands 321 if def.Type == "file" { 322 def.Type = "string" 323 def.ContentEncoding = "base64" 324 } 325 326 (*defs)[defName] = &def 327 328 return defName 329 } 330 331 func (c *ManifestConverter) buildDefaultPorterParameters() []manifest.ParameterDefinition { 332 return []manifest.ParameterDefinition{ 333 { 334 Name: "porter-debug", 335 Destination: manifest.Location{ 336 EnvironmentVariable: "PORTER_DEBUG", 337 }, 338 Schema: definition.Schema{ 339 ID: "https://porter.sh/generated-bundle/#porter-debug", 340 Description: "Print debug information from Porter when executing the bundle", 341 Type: "boolean", 342 Default: false, 343 Comment: cnab.PorterInternal, 344 }, 345 }, 346 { 347 Name: "porter-state", 348 IsState: true, 349 Destination: manifest.Location{ 350 Path: "/porter/state.tgz", 351 }, 352 Schema: definition.Schema{ 353 ID: "https://porter.sh/generated-bundle/#porter-state", 354 Description: "Supports persisting state for bundles. Porter internal parameter that should not be set manually.", 355 Type: "string", 356 ContentEncoding: "base64", 357 Comment: cnab.PorterInternal, 358 }, 359 }, 360 } 361 } 362 363 func (c *ManifestConverter) buildDefaultPorterOutputs() []manifest.OutputDefinition { 364 return []manifest.OutputDefinition{ 365 { 366 Name: "porter-state", 367 IsState: true, 368 Path: "/cnab/app/outputs/porter-state.tgz", 369 Schema: definition.Schema{ 370 ID: "https://porter.sh/generated-bundle/#porter-state", 371 Description: "Supports persisting state for bundles. Porter internal parameter that should not be set manually.", 372 Type: "string", 373 ContentEncoding: "base64", 374 Comment: cnab.PorterInternal, 375 }, 376 }, 377 } 378 } 379 380 func (c *ManifestConverter) generateBundleCredentials() map[string]bundle.Credential { 381 params := map[string]bundle.Credential{} 382 for _, cred := range c.Manifest.Credentials { 383 l := bundle.Credential{ 384 Description: cred.Description, 385 Required: cred.Required, 386 Location: bundle.Location{ 387 Path: manifest.ResolvePath(cred.Path), 388 EnvironmentVariable: cred.EnvironmentVariable, 389 }, 390 ApplyTo: cred.ApplyTo, 391 } 392 params[cred.Name] = l 393 } 394 return params 395 } 396 397 func (c *ManifestConverter) generateBundleImages() map[string]bundle.Image { 398 images := make(map[string]bundle.Image, len(c.Manifest.ImageMap)) 399 400 for i, refImage := range c.Manifest.ImageMap { 401 imgRefStr := refImage.Repository 402 if refImage.Digest != "" { 403 imgRefStr = fmt.Sprintf("%s@%s", imgRefStr, refImage.Digest) 404 } else if refImage.Tag != "" { 405 imgRefStr = fmt.Sprintf("%s:%s", imgRefStr, refImage.Tag) 406 } else { // default to `latest` if no tag is provided 407 imgRefStr = fmt.Sprintf("%s:latest", imgRefStr) 408 409 } 410 imgType := refImage.ImageType 411 if imgType == "" { 412 imgType = "docker" 413 } 414 img := bundle.Image{ 415 Description: refImage.Description, 416 BaseImage: bundle.BaseImage{ 417 Image: imgRefStr, 418 Digest: refImage.Digest, 419 ImageType: imgType, 420 MediaType: refImage.MediaType, 421 Size: refImage.Size, 422 Labels: refImage.Labels, 423 }, 424 } 425 images[i] = img 426 } 427 428 return images 429 } 430 431 func (c *ManifestConverter) generateDependencies(ctx context.Context, defs *definition.Definitions) (interface{}, string, error) { 432 if len(c.Manifest.Dependencies.Requires) == 0 { 433 return nil, "", nil 434 } 435 436 // Check if they are using v1 of the dependencies spec or v2 437 if c.config.IsFeatureEnabled(experimental.FlagDependenciesV2) { 438 // Ok we are using v2! 439 440 deps, err := c.generateDependenciesV2(ctx, defs) 441 return deps, cnab.DependenciesV2ExtensionKey, err 442 } 443 444 // Default to using v1 of deps 445 deps := c.generateDependenciesV1(ctx) 446 return deps, cnab.DependenciesV1ExtensionKey, nil 447 } 448 449 func (c *ManifestConverter) generateDependenciesV1(ctx context.Context) *depsv1ext.Dependencies { 450 if len(c.Manifest.Dependencies.Requires) == 0 { 451 return nil 452 } 453 454 deps := &depsv1ext.Dependencies{ 455 Sequence: make([]string, 0, len(c.Manifest.Dependencies.Requires)), 456 Requires: make(map[string]depsv1ext.Dependency, len(c.Manifest.Dependencies.Requires)), 457 } 458 459 for _, dep := range c.Manifest.Dependencies.Requires { 460 dependencyRef := depsv1ext.Dependency{ 461 Name: dep.Name, 462 Bundle: dep.Bundle.Reference, 463 } 464 if len(dep.Bundle.Version) > 0 { 465 dependencyRef.Version = &depsv1ext.DependencyVersion{ 466 Ranges: []string{dep.Bundle.Version}, 467 } 468 469 // If we can detect that prereleases are used in the version, then set AllowPrereleases to true 470 v, err := semver.NewVersion(dep.Bundle.Version) 471 if err == nil { 472 dependencyRef.Version.AllowPrereleases = v.Prerelease() != "" 473 } 474 } 475 deps.Sequence = append(deps.Sequence, dep.Name) 476 deps.Requires[dep.Name] = dependencyRef 477 } 478 479 return deps 480 } 481 482 func (c *ManifestConverter) generateDependenciesV2(ctx context.Context, defs *definition.Definitions) (*depsv2ext.Dependencies, error) { 483 deps := &depsv2ext.Dependencies{ 484 Requires: make(map[string]depsv2ext.Dependency, len(c.Manifest.Dependencies.Requires)), 485 } 486 487 if c.Manifest.Dependencies.Provides != nil { 488 deps.Provides = &depsv2ext.DependencyProvider{ 489 Interface: depsv2ext.InterfaceDeclaration{ 490 ID: c.Manifest.Dependencies.Provides.Interface.ID}, 491 } 492 } 493 494 for _, dep := range c.Manifest.Dependencies.Requires { 495 dependencyRef := depsv2ext.Dependency{ 496 Name: dep.Name, 497 Bundle: dep.Bundle.Reference, 498 Version: dep.Bundle.Version, 499 Sharing: depsv2ext.SharingCriteria{ 500 Mode: dep.Sharing.GetEffectiveMode(), 501 Group: depsv2ext.SharingGroup{ 502 Name: dep.Sharing.Group.Name, 503 }, 504 }, 505 Parameters: dep.Parameters, 506 Credentials: dep.Credentials, 507 Outputs: dep.Outputs, 508 } 509 510 if dep.Bundle.Interface != nil { 511 dependencyRef.Interface = &depsv2ext.DependencyInterface{ 512 ID: dep.Bundle.Interface.ID, 513 Reference: dep.Bundle.Interface.Reference, 514 } 515 516 // Porter doesn't let you embed a random bundle.json document into your porter.yaml 517 // While the CNAB spec lets the document be anything, we constrain the interface to a porter representation of the bundle's parameters, credentials and outputs. 518 519 // TODO(PEP003): Convert the parameters, credentials and outputs defined on manifest.BundleInterfaceDocument and create an (incomplete) bundle.json from it 520 // See https://github.com/getporter/porter/issues/2548 521 522 if dep.Bundle.Interface.Document != nil { 523 524 depsMaps := depsv2ext.DependencyInterfaceDocument{ 525 Outputs: make(map[string]bundle.Output, len(dep.Bundle.Interface.Document.Outputs)), 526 Parameters: make(map[string]bundle.Parameter, len(dep.Bundle.Interface.Document.Parameters)), 527 Credentials: make(map[string]bundle.Credential, len(dep.Bundle.Interface.Document.Credentials)), 528 } 529 dependencyRef.Interface.Document = depsMaps 530 531 for _, o := range dep.Bundle.Interface.Document.Outputs { 532 depsMaps.Outputs[o.Name] = c.generateOutputDefinition(ctx, defs, o) 533 } 534 535 for _, p := range dep.Bundle.Interface.Document.Parameters { 536 depsMaps.Parameters[p.Name] = c.generateParameterDefinition(ctx, defs, p) 537 } 538 539 for _, cred := range dep.Bundle.Interface.Document.Credentials { 540 depsMaps.Credentials[cred.Name] = c.generateCredentialDefinition(cred) 541 542 } 543 } 544 } 545 546 deps.Requires[dep.Name] = dependencyRef 547 } 548 549 return deps, nil 550 } 551 552 func (c *ManifestConverter) generateParameterSources(b *cnab.ExtendedBundle) cnab.ParameterSources { 553 ps := cnab.ParameterSources{} 554 555 // Parameter sources come from three places 556 // 1. indirectly from our template wiring 557 // 2. indirectly from state variables 558 // 3. directly when they use `source` on a parameter 559 560 // Directly wired outputs to parameters 561 for _, p := range c.Manifest.Parameters { 562 // Skip parameters that aren't set from an output 563 if p.Source.Output == "" { 564 continue 565 } 566 567 var pso cnab.ParameterSource 568 if p.Source.Dependency == "" { 569 pso = c.generateOutputParameterSource(p.Source.Output) 570 } else { 571 ref := manifest.DependencyOutputReference{ 572 Dependency: p.Source.Dependency, 573 Output: p.Source.Output, 574 } 575 pso = c.generateDependencyOutputParameterSource(ref) 576 } 577 ps[p.Name] = pso 578 } 579 580 // Directly wired state variables 581 // All state variables are persisted in a single file, porter-state.tgz 582 ps["porter-state"] = c.generateOutputParameterSource("porter-state") 583 584 // bundle.outputs.OUTPUT 585 for _, outputDef := range c.Manifest.GetTemplatedOutputs() { 586 wiringName, p, def := c.generateOutputWiringParameter(*b, outputDef.Name) 587 if b.Parameters == nil { 588 b.Parameters = make(map[string]bundle.Parameter, 1) 589 } 590 b.Parameters[wiringName] = p 591 b.Definitions[wiringName] = &def 592 593 pso := c.generateOutputParameterSource(outputDef.Name) 594 ps[wiringName] = pso 595 } 596 597 // bundle.dependencies.DEP.outputs.OUTPUT 598 for _, ref := range c.Manifest.GetTemplatedDependencyOutputs() { 599 wiringName, p, def := c.generateDependencyOutputWiringParameter(ref) 600 if b.Parameters == nil { 601 b.Parameters = make(map[string]bundle.Parameter, 1) 602 } 603 b.Parameters[wiringName] = p 604 b.Definitions[wiringName] = &def 605 606 pso := c.generateDependencyOutputParameterSource(ref) 607 ps[wiringName] = pso 608 } 609 610 return ps 611 } 612 613 // generateOutputWiringParameter creates an internal parameter used only by porter, it won't be visible to the user. 614 // The parameter exists solely so that Porter can inject an output back into the bundle, using a parameter source. 615 // The parameter's definition is a copy of the output's definition, with the ID set so we know that it was generated by porter. 616 func (c *ManifestConverter) generateOutputWiringParameter(b cnab.ExtendedBundle, outputName string) (string, bundle.Parameter, definition.Schema) { 617 wiringName := manifest.GetParameterSourceForOutput(outputName) 618 619 paramDesc := fmt.Sprintf("Wires up the %s output for use as a parameter. Porter internal parameter that should not be set manually.", outputName) 620 wiringParam := c.generateWiringParameter(wiringName, paramDesc) 621 622 // Copy the output definition for use with the wiring parameter 623 // and identify the definition as a porter internal structure 624 outputDefName := b.Outputs[outputName].Definition 625 outputDef := b.Definitions[outputDefName] 626 wiringDef := *outputDef 627 wiringDef.ID = "https://porter.sh/generated-bundle/#porter-parameter-source-definition" 628 wiringDef.Comment = cnab.PorterInternal 629 630 return wiringName, wiringParam, wiringDef 631 } 632 633 // generateDependencyOutputWiringParameter creates an internal parameter used only by porter, it won't be visible to 634 // the user. The parameter exists solely so that Porter can inject a dependency output into the bundle. 635 func (c *ManifestConverter) generateDependencyOutputWiringParameter(reference manifest.DependencyOutputReference) (string, bundle.Parameter, definition.Schema) { 636 wiringName := manifest.GetParameterSourceForDependency(reference) 637 638 paramDesc := fmt.Sprintf("Wires up the %s dependency %s output for use as a parameter. Porter internal parameter that should not be set manually.", reference.Dependency, reference.Output) 639 wiringParam := c.generateWiringParameter(wiringName, paramDesc) 640 641 wiringDef := definition.Schema{ 642 ID: "https://porter.sh/generated-bundle/#porter-parameter-source-definition", 643 Comment: cnab.PorterInternal, 644 // any type, the dependency's bundle definition is not available at buildtime 645 } 646 647 return wiringName, wiringParam, wiringDef 648 } 649 650 // generateWiringParameter builds an internal Porter-only parameter for connecting a parameter source to a parameter. 651 func (g *ManifestConverter) generateWiringParameter(wiringName string, description string) bundle.Parameter { 652 return bundle.Parameter{ 653 Definition: wiringName, 654 Description: description, 655 Required: false, 656 Destination: &bundle.Location{ 657 EnvironmentVariable: manifest.ParamToEnvVar(wiringName), 658 }, 659 } 660 } 661 662 // generateOutputParameterSource builds a parameter source that connects a bundle output to a parameter. 663 func (c *ManifestConverter) generateOutputParameterSource(outputName string) cnab.ParameterSource { 664 return cnab.ParameterSource{ 665 Priority: []string{cnab.ParameterSourceTypeOutput}, 666 Sources: map[string]cnab.ParameterSourceDefinition{ 667 cnab.ParameterSourceTypeOutput: cnab.OutputParameterSource{ 668 OutputName: outputName, 669 }, 670 }, 671 } 672 } 673 674 // generateDependencyOutputParameterSource builds a parameter source that connects a dependency output to a parameter. 675 func (c *ManifestConverter) generateDependencyOutputParameterSource(ref manifest.DependencyOutputReference) cnab.ParameterSource { 676 return cnab.ParameterSource{ 677 Priority: []string{cnab.ParameterSourceTypeDependencyOutput}, 678 Sources: map[string]cnab.ParameterSourceDefinition{ 679 cnab.ParameterSourceTypeDependencyOutput: cnab.DependencyOutputParameterSource{ 680 Dependency: ref.Dependency, 681 OutputName: ref.Output, 682 }, 683 }, 684 } 685 } 686 687 func toBool(value bool) *bool { 688 return &value 689 } 690 691 func toInt(v int) *int { 692 return &v 693 } 694 695 func toFloat(v float64) *float64 { 696 return &v 697 } 698 699 func (c *ManifestConverter) generateCustomExtensions(ctx context.Context, b *cnab.ExtendedBundle) (map[string]interface{}, error) { 700 customExtensions := map[string]interface{}{ 701 cnab.FileParameterExtensionKey: struct{}{}, 702 } 703 704 // Add custom metadata defined in the manifest 705 for key, value := range c.Manifest.Custom { 706 customExtensions[key] = value 707 } 708 709 // Add the dependency extension 710 deps, depsExtKey, err := c.generateDependencies(ctx, &b.Definitions) 711 if err != nil { 712 return nil, err 713 } 714 if depsExtKey != "" { 715 customExtensions[depsExtKey] = deps 716 } 717 718 // Add the parameter sources extension 719 ps := c.generateParameterSources(b) 720 if len(ps) > 0 { 721 customExtensions[cnab.ParameterSourcesExtensionKey] = ps 722 } 723 724 // Add entries for user-specified required extensions, like docker 725 for _, ext := range c.Manifest.Required { 726 customExtensions[lookupExtensionKey(ext.Name)] = ext.Config 727 } 728 729 return customExtensions, nil 730 } 731 732 func (c *ManifestConverter) generateRequiredExtensions(b cnab.ExtendedBundle) []string { 733 requiredExtensions := []string{cnab.FileParameterExtensionKey} 734 735 // Add the appropriate dependencies key if applicable 736 if b.HasDependenciesV1() { 737 requiredExtensions = append(requiredExtensions, cnab.DependenciesV1ExtensionKey) 738 } else if b.HasDependenciesV2() { 739 requiredExtensions = append(requiredExtensions, cnab.DependenciesV2ExtensionKey) 740 } 741 742 // Add the appropriate parameter sources key if applicable 743 if b.HasParameterSources() { 744 requiredExtensions = append(requiredExtensions, cnab.ParameterSourcesExtensionKey) 745 } 746 747 // Add all under required section of manifest 748 for _, ext := range c.Manifest.Required { 749 requiredExtensions = append(requiredExtensions, lookupExtensionKey(ext.Name)) 750 } 751 752 return requiredExtensions 753 } 754 755 // lookupExtensionKey is a helper method to return a full key matching a 756 // supported extension, if applicable 757 func lookupExtensionKey(name string) string { 758 759 key := name 760 // If an official supported extension, we grab the full key 761 762 supportedExt, err := cnab.GetSupportedExtension(name) 763 if err != nil { 764 // TODO: Issue linter warning 765 } else { 766 key = supportedExt.Key 767 } 768 return key 769 }