From 913dd6ccfd5d2a69d4a720933aed6a7b522b6c5d Mon Sep 17 00:00:00 2001 From: Vaughn Dice Date: Fri, 26 Jul 2019 09:35:09 -0600 Subject: [PATCH] feat(bundle.go): add RequiredExtensions --- bundle/bundle.go | 33 ++++++++++++++++++++------------- bundle/bundle_test.go | 31 ++++++++++++++++++++++++++++++- testdata/bundles/foo.json | 5 ++++- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/bundle/bundle.go b/bundle/bundle.go index fd7a2595..4edae7b0 100644 --- a/bundle/bundle.go +++ b/bundle/bundle.go @@ -16,19 +16,20 @@ 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"` + 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"` + 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. @@ -191,6 +192,12 @@ func (b Bundle) Validate() error { return errors.New("'latest' is not a valid bundle version") } + for _, requiredExtension := range b.RequiredExtensions { + if _, exists := b.Custom[requiredExtension]; !exists { + return fmt.Errorf("required extension '%s' is not defined in the Custom section of the bundle", requiredExtension) + } + } + for _, img := range b.InvocationImages { err := img.Validate() if err != nil { diff --git a/bundle/bundle_test.go b/bundle/bundle_test.go index 2702cc0f..bce7adc8 100644 --- a/bundle/bundle_test.go +++ b/bundle/bundle_test.go @@ -339,7 +339,31 @@ 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", + }, + } + + err := b.Validate() + is.EqualError(err, "required extension 'my.custom.extension' is not defined in the Custom section of the bundle") + + b.Custom = map[string]interface{}{ + "my.custom.extension": true, + } + + err = b.Validate() + is.NoError(err) +} + +func TestReadCustomAndRequiredExtensions(t *testing.T) { data, err := ioutil.ReadFile("../testdata/bundles/foo.json") if err != nil { t.Errorf("cannot read bundle file: %v", err) @@ -375,6 +399,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) { diff --git a/testdata/bundles/foo.json b/testdata/bundles/foo.json index 8bc23029..2ee51257 100644 --- a/testdata/bundles/foo.json +++ b/testdata/bundles/foo.json @@ -77,5 +77,8 @@ "path": "/cnab/is/go" } } - } + }, + "requiredExtensions": [ + "com.example.duffle-bag" + ] }