Skip to content

Commit 63215d9

Browse files
committed
wip
Signed-off-by: Carolyn Van Slyck <[email protected]>
1 parent d815218 commit 63215d9

26 files changed

+1571
-44
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ require (
6060
github.com/spf13/viper v1.8.1
6161
github.com/stretchr/testify v1.7.1
6262
github.com/xeipuuv/gojsonschema v1.2.0
63+
github.com/yourbasic/graph v0.0.0-20210606180040-8ecfec1c2869
6364
go.mongodb.org/mongo-driver v1.7.1
6465
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0
6566
go.opentelemetry.io/otel v1.7.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
15781578
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
15791579
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
15801580
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
1581+
github.com/yourbasic/graph v0.0.0-20210606180040-8ecfec1c2869 h1:7v7L5lsfw4w8iqBBXETukHo4IPltmD+mWoLRYUmeGN8=
1582+
github.com/yourbasic/graph v0.0.0-20210606180040-8ecfec1c2869/go.mod h1:Rfzr+sqaDreiCaoQbFCu3sTXxeFq/9kXRuyOoSlGQHE=
15811583
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
15821584
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
15831585
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

pkg/cnab/config-adapter/adapter.go

+106-12
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package configadapter
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"path"
8+
"regexp"
79
"strings"
810

911
"get.porter.sh/porter/pkg/cnab"
1012
depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
13+
depsv2 "get.porter.sh/porter/pkg/cnab/dependencies/v2"
1114
"get.porter.sh/porter/pkg/config"
1215
"get.porter.sh/porter/pkg/experimental"
1316
"get.porter.sh/porter/pkg/manifest"
@@ -16,6 +19,7 @@ import (
1619
"github.com/Masterminds/semver/v3"
1720
"github.com/cnabio/cnab-go/bundle"
1821
"github.com/cnabio/cnab-go/bundle/definition"
22+
"github.com/pkg/errors"
1923
)
2024

2125
// ManifestConverter converts from a porter manifest to a CNAB bundle definition.
@@ -408,33 +412,33 @@ func (c *ManifestConverter) generateBundleImages() map[string]bundle.Image {
408412
}
409413

410414
func (c *ManifestConverter) generateDependencies() (interface{}, string, error) {
411-
if len(c.Manifest.Dependencies.RequiredDependencies) == 0 {
415+
if len(c.Manifest.Dependencies.Requires) == 0 {
412416
return nil, "", nil
413417
}
414418

415419
// Check if they are using v1 of the dependencies spec or v2
416420
if c.config.IsFeatureEnabled(experimental.FlagDependenciesV2) {
417-
panic("the dependencies-v2 experimental flag was specified but is not yet implemented")
421+
// Ok we are using v2!
422+
deps, err := c.generateDependenciesV2()
423+
return deps, cnab.DependenciesV2ExtensionKey, err
418424
}
419425

420-
deps, err := c.generateDependenciesV1()
421-
if err != nil {
422-
return nil, "", err
423-
}
426+
// Default to using v1 of deps
427+
deps := c.generateDependenciesV1()
424428
return deps, cnab.DependenciesV1ExtensionKey, nil
425429
}
426430

427-
func (c *ManifestConverter) generateDependenciesV1() (*depsv1.Dependencies, error) {
428-
if len(c.Manifest.Dependencies.RequiredDependencies) == 0 {
429-
return nil, nil
431+
func (c *ManifestConverter) generateDependenciesV1() *depsv1.Dependencies {
432+
if len(c.Manifest.Dependencies.Requires) == 0 {
433+
return nil
430434
}
431435

432436
deps := &depsv1.Dependencies{
433-
Sequence: make([]string, 0, len(c.Manifest.Dependencies.RequiredDependencies)),
434-
Requires: make(map[string]depsv1.Dependency, len(c.Manifest.Dependencies.RequiredDependencies)),
437+
Sequence: make([]string, 0, len(c.Manifest.Dependencies.Requires)),
438+
Requires: make(map[string]depsv1.Dependency, len(c.Manifest.Dependencies.Requires)),
435439
}
436440

437-
for _, dep := range c.Manifest.Dependencies.RequiredDependencies {
441+
for _, dep := range c.Manifest.Dependencies.Requires {
438442
dependencyRef := depsv1.Dependency{
439443
Name: dep.Name,
440444
Bundle: dep.Bundle.Reference,
@@ -454,9 +458,97 @@ func (c *ManifestConverter) generateDependenciesV1() (*depsv1.Dependencies, erro
454458
deps.Requires[dep.Name] = dependencyRef
455459
}
456460

461+
return deps
462+
}
463+
464+
func (c *ManifestConverter) generateDependenciesV2() (*depsv2.Dependencies, error) {
465+
deps := &depsv2.Dependencies{
466+
Requires: make(map[string]depsv2.Dependency, len(c.Manifest.Dependencies.Requires)),
467+
}
468+
469+
for _, dep := range c.Manifest.Dependencies.Requires {
470+
dependencyRef := depsv2.Dependency{
471+
Name: dep.Name,
472+
Bundle: dep.Bundle.Reference,
473+
Version: dep.Bundle.Version,
474+
}
475+
476+
if dep.Bundle.Interface != nil {
477+
if dep.Bundle.Interface.Reference != "" {
478+
dependencyRef.Interface.Reference = dep.Bundle.Interface.Reference
479+
}
480+
if dep.Bundle.Interface.Document != nil {
481+
bundleData, err := json.Marshal(dep.Bundle.Interface.Document)
482+
if err != nil {
483+
return nil, errors.Wrapf(err, "invalid bundle interface document for dependency %s", dep.Name)
484+
}
485+
rawMessage := &json.RawMessage{}
486+
err = rawMessage.UnmarshalJSON(bundleData)
487+
if err != nil {
488+
return nil, errors.Wrapf(err, "could not convert bundle interface document to a raw json message for dependency %s", dep.Name)
489+
}
490+
dependencyRef.Interface.Document = rawMessage
491+
}
492+
}
493+
494+
if dep.Installation != nil {
495+
dependencyRef.Installation = &depsv2.DependencyInstallation{
496+
Labels: dep.Installation.Labels,
497+
}
498+
if dep.Installation.Criteria != nil {
499+
dependencyRef.Installation.Criteria = &depsv2.InstallationCriteria{
500+
MatchInterface: dep.Installation.Criteria.MatchInterface,
501+
MatchNamespace: dep.Installation.Criteria.MatchNamespace,
502+
IgnoreLabels: dep.Installation.Criteria.IgnoreLabels,
503+
}
504+
}
505+
}
506+
507+
if len(dep.Parameters) > 0 {
508+
dependencyRef.Parameters = make(map[string]depsv2.DependencySource, len(dep.Parameters))
509+
for param, source := range dep.Parameters {
510+
dependencyRef.Parameters[param] = parseDependencySource(source)
511+
}
512+
}
513+
514+
if len(dep.Credentials) > 0 {
515+
dependencyRef.Credentials = make(map[string]depsv2.DependencySource, len(dep.Credentials))
516+
for cred, source := range dep.Credentials {
517+
dependencyRef.Credentials[cred] = parseDependencySource(source)
518+
}
519+
}
520+
521+
deps.Requires[dep.Name] = dependencyRef
522+
}
523+
457524
return deps, nil
458525
}
459526

527+
// TODO: is there a way to feature flag this stuff so that if it's flakey or implemented
528+
// incrementally we can just keep it off?
529+
func parseDependencySource(value string) depsv2.DependencySource {
530+
regex := regexp.MustCompile(`bundle(\.dependencies)?\.([^.]+)\.([^.]+)\.(.+)`)
531+
matches := regex.FindStringSubmatch(value)
532+
if matches == nil || len(matches) < 5 {
533+
return depsv2.DependencySource{Value: value}
534+
}
535+
536+
dependencyName := matches[2] // bundle.dependencies.DEPENDENCY_NAME
537+
itemType := matches[3] // bundle.dependencies.dependency_name.PARAMETERS.name or bundle.OUTPUTS.name
538+
itemName := matches[4] // bundle.dependencies.dependency_name.parameters.NAME or bundle.outputs.NAME
539+
540+
result := depsv2.DependencySource{Dependency: dependencyName}
541+
switch itemType {
542+
case "parameters":
543+
result.Parameter = itemName
544+
case "credentials":
545+
result.Credential = itemName
546+
case "outputs":
547+
result.Output = itemName
548+
}
549+
return result
550+
}
551+
460552
func (c *ManifestConverter) generateParameterSources(b *cnab.ExtendedBundle) cnab.ParameterSources {
461553
ps := cnab.ParameterSources{}
462554

@@ -643,6 +735,8 @@ func (c *ManifestConverter) generateRequiredExtensions(b cnab.ExtendedBundle) []
643735
// Add the appropriate dependencies key if applicable
644736
if b.HasDependenciesV1() {
645737
requiredExtensions = append(requiredExtensions, cnab.DependenciesV1ExtensionKey)
738+
} else if b.HasDependenciesV2() {
739+
requiredExtensions = append(requiredExtensions, cnab.DependenciesV2ExtensionKey)
646740
}
647741

648742
// Add the appropriate parameter sources key if applicable
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
schemaVersion: 1.0.0-alpha.1
2+
name: porter-hello
3+
description: "An example Porter configuration"
4+
version: 0.1.0
5+
registry: "localhost:5000"
6+
7+
credentials:
8+
- name: username
9+
description: Name of the database user
10+
required: false
11+
env: ROOT_USERNAME
12+
- name: password
13+
path: /tmp/password
14+
applyTo:
15+
- uninstall
16+
17+
dependencies:
18+
requires:
19+
- name: mysql
20+
bundle:
21+
reference: "getporter/azure-mysql:5.7"
22+
23+
mixins:
24+
- exec
25+
26+
install:
27+
- exec:
28+
description: "Say Hello"
29+
command: bash
30+
flags:
31+
c: echo Hello World
32+
33+
status:
34+
- exec:
35+
description: "Get World Status"
36+
command: bash
37+
flags:
38+
c: echo The world is on fire
39+
40+
uninstall:
41+
- exec:
42+
description: "Say Goodbye"
43+
command: bash
44+
flags:
45+
c: echo Goodbye World

pkg/cnab/dependencies/v2/types.go

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package v2
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// Dependencies describes the set of custom extension metadata associated with the dependencies spec
8+
// https://github.com/cnabio/cnab-spec/blob/master/500-CNAB-dependencies.md
9+
type Dependencies struct {
10+
// Requires is a list of bundles required by this bundle
11+
Requires map[string]Dependency `json:"requires,omitempty" mapstructure:"requires"`
12+
}
13+
14+
/*
15+
dependencies:
16+
requires: # dependencies are always created in the current namespace, never global though they can match globally?
17+
mysql:
18+
bundle:
19+
reference: getporter/mysql:v1.0.2
20+
version: 1.x
21+
interface: # Porter defaults the interface based on usage
22+
reference: getporter/generic-mysql-interface:v1.0.0 # point to an interface bundle to be more specific
23+
bundle: # add extra interface requirements
24+
outputs:
25+
- $id: "mysql-5.7-connection-string" # match on something other than name, so that outputs with different names can be reused
26+
installation:
27+
labels: # labels applied to the installation if created
28+
app: myapp
29+
installation: {{ installation.name }} # exclusive resource
30+
criteria: # criteria for reusing an existing installation, by default must be the same bundle, labels and allows global
31+
matchInterface: true # only match the interface, not the bundle too
32+
matchNamespace: true # must be in the same namespace, disallow global
33+
ignoreLabels: true # allow different labels
34+
*/
35+
36+
// Dependency describes a dependency on another bundle
37+
type Dependency struct {
38+
// Name of the dependency
39+
Name string
40+
41+
// Bundle is the location of the bundle in a registry, for example REGISTRY/NAME:TAG
42+
Bundle string `json:"bundle" mapstructure:"bundle"`
43+
44+
// Version is a set of allowed versions
45+
Version string `json:"version,omitempty" mapstructure:"version"`
46+
47+
Interface *DependencyInterface `json:"interface,omitempty" mapstructure:"interface,omitempty"`
48+
49+
Installation *DependencyInstallation `json:"installation,omitempty" mapstructure:"installation,omitempty"`
50+
51+
Parameters map[string]DependencySource `json:"parameters,omitempty" mapstructure:"parameters,omitempty"`
52+
Credentials map[string]DependencySource `json:"credentials,omitempty" mapstructure:"credentials,omitempty"`
53+
}
54+
55+
type DependencySource struct {
56+
Value string `json:"value,omitempty" mapstructure:"value,omitempty"`
57+
Dependency string `json:"dependency,omitempty" mapstructure:"dependency,omitempty"`
58+
Credential string `json:"credential,omitempty" mapstructure:"credential,omitempty"`
59+
Parameter string `json:"parameter,omitempty" mapstructure:"parameter,omitempty"`
60+
Output string `json:"output,omitempty" mapstructure:"output,omitempty"`
61+
}
62+
63+
type DependencyInstallation struct {
64+
Labels map[string]string `json:"labels,omitempty" mapstructure:"labels,omitempty"`
65+
Criteria *InstallationCriteria `json:"criteria,omitempty" mapstructure:"criteria,omitempty"`
66+
}
67+
68+
type InstallationCriteria struct {
69+
// MatchInterface specifies if the installation should use the same bundle or just needs to match the interface
70+
MatchInterface bool `json:"matchInterface,omitempty" mapstructure:"matchInterface,omitEmpty"`
71+
MatchNamespace bool `json:"matchNamespace,omitempty" mapstructure:"matchNamespace,omitEmpty"`
72+
IgnoreLabels bool `json:"ignoreLabels,omitempty" mapstructure:"ignoreLabels,omitempty"`
73+
}
74+
75+
type DependencyInterface struct {
76+
Reference string `json:"reference,omitempty" mapstructure:"reference,omitempty"`
77+
Document *json.RawMessage `json:"document,omitempty" mapstructure:"document,omitempty"`
78+
}

0 commit comments

Comments
 (0)