Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Credential.ApplyTo #240

Merged
merged 1 commit into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions bundle/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,20 @@ import "errors"
// Credential represents the definition of a CNAB credential
type Credential struct {
Location `yaml:",inline"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
ApplyTo []string `json:"applyTo,omitempty" yaml:"applyTo,omitempty"`
}

// GetApplyTo returns the list of actions that the Credential applies to.
func (c *Credential) GetApplyTo() []string {
return c.ApplyTo
}

// AppliesTo returns a boolean value specifying whether or not
// the Credential applies to the provided action
func (c *Credential) AppliesTo(action string) bool {
return AppliesTo(c, action)
}

// Validate a Credential
Expand Down
6 changes: 4 additions & 2 deletions bundle/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ func TestCompleteCredDefinition(t *testing.T) {
"something": {
"description" : "wicked this way comes",
"path" : "/cnab/app/a/credential",
"required" : true
"required" : true,
"applyTo" : ["install"]
}
}
}`
Expand All @@ -26,7 +27,8 @@ func TestCompleteCredDefinition(t *testing.T) {

assert.Equal(t, "/cnab/app/a/credential", something.Path, "did not contain the expected path")
assert.Equal(t, "wicked this way comes", something.Description, "did not contain the expected description")

assert.True(t, something.Required, "did not contain the expected required")
assert.Equal(t, []string{"install"}, something.ApplyTo, "did not contain the expected applyTo")
}

func TestHandleMultipleCreds(t *testing.T) {
Expand Down
17 changes: 7 additions & 10 deletions bundle/outputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@ type Output struct {
Path string `json:"path" yaml:"path"`
}

// GetApplyTo returns the list of actions that the Output applies to.
func (o Output) GetApplyTo() []string {
return o.ApplyTo
}

// AppliesTo returns a boolean value specifying whether or not
// the Output applies to the provided action
func (output *Output) AppliesTo(action string) bool {
if len(output.ApplyTo) == 0 {
return true
}
for _, act := range output.ApplyTo {
if action == act {
return true
}
}
return false
func (o Output) AppliesTo(action string) bool {
return AppliesTo(o, action)
}

// IsOutputSensitive is a convenience function that determines if an output's
Expand Down
15 changes: 6 additions & 9 deletions bundle/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@ type Parameter struct {
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
}

// GetApplyTo returns the list of actions that the Parameter applies to.
func (p *Parameter) GetApplyTo() []string {
return p.ApplyTo
}

// AppliesTo returns a boolean value specifying whether or not
// the Parameter applies to the provided action
func (p *Parameter) AppliesTo(action string) bool {
if len(p.ApplyTo) == 0 {
return true
}
for _, act := range p.ApplyTo {
if action == act {
return true
}
}
return false
return AppliesTo(p, action)
}

// Validate a Parameter
Expand Down
22 changes: 22 additions & 0 deletions bundle/scoped.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package bundle

// Scoped represents an item whose scope is limited to a set of actions.
type Scoped interface {
// GetApplyTo returns the list of applicable actions.
GetApplyTo() []string
}

// AppliesTo returns a boolean value specifying whether or not
// the scoped item applies to the provided action.
func AppliesTo(s Scoped, action string) bool {
applyTo := s.GetApplyTo()
if len(applyTo) == 0 {
return true
}
for _, act := range applyTo {
if action == act {
return true
}
}
return false
}
41 changes: 41 additions & 0 deletions bundle/scoped_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package bundle

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestAppliesTo(t *testing.T) {
makeTestTypes := func(applyTo []string) []Scoped {
return []Scoped{
&Credential{ApplyTo: applyTo},
Output{ApplyTo: applyTo},
&Parameter{ApplyTo: applyTo},
}
}

t.Run("empty", func(t *testing.T) {
testTypes := makeTestTypes(nil)

for _, tt := range testTypes {
assert.True(t, AppliesTo(tt, "install"), "%T.AppliesTo returned an incorrect result", tt)
}
})

t.Run("hit", func(t *testing.T) {
testTypes := makeTestTypes([]string{"install", "upgrade", "custom"})

for _, tt := range testTypes {
assert.True(t, AppliesTo(tt, "custom"), "%T.AppliesTo returned an incorrect result", tt)
}
})

t.Run("miss", func(t *testing.T) {
testTypes := makeTestTypes([]string{"install", "upgrade", "uninstall"})

for _, tt := range testTypes {
assert.False(t, AppliesTo(tt, "custom"), "%T.AppliesTo returned an incorrect result", tt)
}
})
}
7 changes: 6 additions & 1 deletion credentials/credentialset.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,16 @@ func Load(path string) (*CredentialSet, error) {
// This will result in an error only when the following conditions are true:
// - a credential in the spec is not present in the given set
// - the credential is required
// - the credential applies to the specified action
//
// It is allowed for spec to specify both an env var and a file. In such case, if
// the given set provides either, it will be considered valid.
func Validate(given valuesource.Set, spec map[string]bundle.Credential) error {
func Validate(given valuesource.Set, spec map[string]bundle.Credential, action string) error {
for name, cred := range spec {
if !cred.AppliesTo(action) {
continue
}

if !valuesource.IsValid(given, name) && cred.Required {
return fmt.Errorf("bundle requires credential for %s", name)
}
Expand Down
42 changes: 42 additions & 0 deletions credentials/credentialset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/cnabio/cnab-go/bundle"
"github.com/cnabio/cnab-go/schema"
"github.com/cnabio/cnab-go/secrets/host"
"github.com/cnabio/cnab-go/valuesource"
Expand Down Expand Up @@ -77,3 +78,44 @@ func TestNewCredentialSet(t *testing.T) {
assert.Equal(t, DefaultSchemaVersion, cs.SchemaVersion, "SchemaVersion was not set")
assert.Len(t, cs.Credentials, 1, "Credentials should be initialized with 1 value")
}

func TestValidate(t *testing.T) {
t.Run("valid - credential specified", func(t *testing.T) {
spec := map[string]bundle.Credential{
"kubeconfig": {},
}
values := valuesource.Set{
"kubeconfig": "top secret creds",
}
err := Validate(values, spec, "install")
require.NoError(t, err, "expected Validate to pass because the credential was specified")
})

t.Run("valid - credential not required", func(t *testing.T) {
spec := map[string]bundle.Credential{
"kubeconfig": {ApplyTo: []string{"install"}, Required: false},
}
values := valuesource.Set{}
err := Validate(values, spec, "install")
require.NoError(t, err, "expected Validate to pass because the credential isn't required")
})

t.Run("valid - missing inapplicable credential", func(t *testing.T) {
spec := map[string]bundle.Credential{
"kubeconfig": {ApplyTo: []string{"install"}, Required: true},
}
values := valuesource.Set{}
err := Validate(values, spec, "custom")
require.NoError(t, err, "expected Validate to pass because the credential isn't applicable to the custom action")
})

t.Run("invalid - missing required credential", func(t *testing.T) {
spec := map[string]bundle.Credential{
"kubeconfig": {ApplyTo: []string{"install"}, Required: true},
}
values := valuesource.Set{}
err := Validate(values, spec, "install")
require.Error(t, err, "expected Validate to fail because the credential applies to the specified action and is required")
assert.Contains(t, err.Error(), "bundle requires credential")
})
}
2 changes: 1 addition & 1 deletion schema/schema/claim.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@
],
"title": "CNAB Claims json schema",
"type": "object"
}
}