Skip to content

Commit

Permalink
feat(bundle.go): add RequiredExtensions
Browse files Browse the repository at this point in the history
  • Loading branch information
vdice committed Jul 30, 2019
1 parent 4f3db84 commit 3290dcc
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 16 deletions.
45 changes: 31 additions & 14 deletions bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@ import (

// Bundle is a CNAB metadata document
type Bundle struct {
SchemaVersion string `json:"schemaVersion" mapstructure:"schemaVersion"`
Name string `json:"name" mapstructure:"name"`
Version string `json:"version" mapstructure:"version"`
Description string `json:"description" mapstructure:"description"`
Keywords []string `json:"keywords,omitempty" mapstructure:"keywords"`
Maintainers []Maintainer `json:"maintainers,omitempty" mapstructure:"maintainers"`
InvocationImages []InvocationImage `json:"invocationImages" mapstructure:"invocationImages"`
Images map[string]Image `json:"images,omitempty" mapstructure:"images"`
Actions map[string]Action `json:"actions,omitempty" mapstructure:"actions"`
Parameters *ParametersDefinition `json:"parameters,omitempty" mapstructure:"parameters"`
Credentials map[string]Credential `json:"credentials,omitempty" mapstructure:"credentials"`
Outputs *OutputsDefinition `json:"outputs,omitempty" mapstructure:"outputs"`
Definitions definition.Definitions `json:"definitions,omitempty" mapstructure:"definitions"`
License string `json:"license,omitempty" mapstructure:"license"`
SchemaVersion string `json:"schemaVersion" mapstructure:"schemaVersion"`
Name string `json:"name" mapstructure:"name"`
Version string `json:"version" mapstructure:"version"`
Description string `json:"description" mapstructure:"description"`
Keywords []string `json:"keywords,omitempty" mapstructure:"keywords"`
Maintainers []Maintainer `json:"maintainers,omitempty" mapstructure:"maintainers"`
InvocationImages []InvocationImage `json:"invocationImages" mapstructure:"invocationImages"`
Images map[string]Image `json:"images,omitempty" mapstructure:"images"`
Actions map[string]Action `json:"actions,omitempty" mapstructure:"actions"`
Parameters *ParametersDefinition `json:"parameters,omitempty" mapstructure:"parameters"`
Credentials map[string]Credential `json:"credentials,omitempty" mapstructure:"credentials"`
Outputs *OutputsDefinition `json:"outputs,omitempty" mapstructure:"outputs"`
Definitions definition.Definitions `json:"definitions,omitempty" mapstructure:"definitions"`
License string `json:"license,omitempty" mapstructure:"license"`
RequiredExtensions []string `json:"requiredExtensions,omitempty" mapstructure:"requiredExtensions"`

// Custom extension metadata is a named collection of auxiliary data whose
// meaning is defined outside of the CNAB specification.
Expand Down Expand Up @@ -192,6 +193,22 @@ func (b Bundle) Validate() error {
return errors.New("'latest' is not a valid bundle version")
}

reqExt := make(map[string]bool, len(b.RequiredExtensions))
for _, requiredExtension := range b.RequiredExtensions {
// Verify the custom extension declared as required exists
if _, exists := b.Custom[requiredExtension]; !exists {
return fmt.Errorf("required extension '%s' is not defined in the Custom section of the bundle", requiredExtension)
}

// Check for duplicate entries
if _, exists := reqExt[requiredExtension]; exists {
return fmt.Errorf("required extension '%s' is already declared", requiredExtension)
}

// Populate map with required extension, for duplicate check above
reqExt[requiredExtension] = true
}

for _, img := range b.InvocationImages {
err := img.Validate()
if err != nil {
Expand Down
39 changes: 38 additions & 1 deletion bundle/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,39 @@ func TestValidateBundle_RequiresInvocationImage(t *testing.T) {
}
}

func TestReadCustomExtensions(t *testing.T) {
func TestValidateRequiredExtensions(t *testing.T) {
is := assert.New(t)

img := InvocationImage{BaseImage{}}
b := Bundle{
Version: "0.1.0",
SchemaVersion: "99.98",
InvocationImages: []InvocationImage{img},
RequiredExtensions: []string{
"my.custom.extension",
},
}

// Verify the error when a required extension is not present in custom
err := b.Validate()
is.EqualError(err, "required extension 'my.custom.extension' is not defined in the Custom section of the bundle")

// Add corresponding entry in custom
b.Custom = map[string]interface{}{
"my.custom.extension": true,
}

err = b.Validate()
is.NoError(err)

// Add duplicate required extension
b.RequiredExtensions = append(b.RequiredExtensions, "my.custom.extension")

err = b.Validate()
is.EqualError(err, "required extension 'my.custom.extension' is already declared")
}

func TestReadCustomAndRequiredExtensions(t *testing.T) {
data, err := ioutil.ReadFile("../testdata/bundles/foo.json")
if err != nil {
t.Errorf("cannot read bundle file: %v", err)
Expand Down Expand Up @@ -375,6 +407,11 @@ func TestReadCustomExtensions(t *testing.T) {
}
assert.Equal(t, true, backupExt["enabled"])
assert.Equal(t, "daily", backupExt["frequency"])

if len(bundle.RequiredExtensions) != 1 {
t.Errorf("Expected 1 required extension, got %d", len(bundle.RequiredExtensions))
}
assert.Equal(t, "com.example.duffle-bag", bundle.RequiredExtensions[0])
}

func TestOutputs_Marshall(t *testing.T) {
Expand Down
5 changes: 4 additions & 1 deletion testdata/bundles/foo.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,8 @@
"path": "/cnab/is/go"
}
}
}
},
"requiredExtensions": [
"com.example.duffle-bag"
]
}

0 comments on commit 3290dcc

Please sign in to comment.