Skip to content

Commit

Permalink
feat(*): enable parameters of type file in Porter manifest (#579)
Browse files Browse the repository at this point in the history
  • Loading branch information
vdice authored Sep 18, 2019
1 parent d26d6f5 commit 21bb79f
Show file tree
Hide file tree
Showing 22 changed files with 369 additions and 14 deletions.
7 changes: 4 additions & 3 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Gopkg.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[[constraint]]
name = "github.com/deislabs/cnab-go"
version = "v0.4.0-beta1"
source = "github.com/vdice/cnab-go"
branch = "fix/custom-validators"

[[override]]
name = "github.com/deislabs/cnab-go"
version = "v0.4.0-beta1"
source = "github.com/vdice/cnab-go"
branch = "fix/custom-validators"

# Using master until there is a release of cnab-to-oci
[[constraint]]
Expand Down
4 changes: 4 additions & 0 deletions pkg/cnab/config_adapter/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ func (c *ManifestConverter) generateBundleParameters(defs *definition.Definition
// (Both Params and Outputs may reference same Definition)
if _, exists := (*defs)[param.Name]; !exists {
def := param.Schema
if def.Type == "file" {
def.Type = "string"
def.ContentEncoding = "base64"
}
(*defs)[param.Name] = &def
}
params[param.Name] = p
Expand Down
14 changes: 14 additions & 0 deletions pkg/cnab/config_adapter/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,20 @@ func TestManifestConverter_generateBundleParametersSchema(t *testing.T) {
Default: `"myobject": { "foo": "true", "bar": [ 1, 2, 3 ] }`,
},
},
{
"afile",
bundle.Parameter{
Definition: "afile",
Destination: &bundle.Location{
Path: "/root/.kube/config",
},
Required: true,
},
definition.Schema{
Type: "string",
ContentEncoding: "base64",
},
},
}

for _, tc := range testcases {
Expand Down
3 changes: 3 additions & 0 deletions pkg/cnab/config_adapter/testdata/porter-with-parameters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ parameters:
3
]
}'
- name: afile
type: file
path: /root/.kube/config

mixins:
- exec
Expand Down
23 changes: 22 additions & 1 deletion pkg/cnab/provider/parameters.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package cnabprovider

import (
"encoding/base64"
"fmt"

"github.com/deislabs/cnab-go/bundle"
"github.com/deislabs/cnab-go/bundle/definition"
"github.com/deislabs/cnab-go/claim"
"github.com/pkg/errors"
)
Expand All @@ -25,7 +27,12 @@ func (d *Duffle) loadParameters(claim *claim.Claim, rawOverrides map[string]stri
return nil, fmt.Errorf("definition %s not defined in bundle", param.Definition)
}

value, err := def.ConvertValue(rawValue)
unconverted, err := d.getUnconvertedValueFromRaw(def, key, rawValue)
if err != nil {
return nil, err
}

value, err := def.ConvertValue(unconverted)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert parameter's %s value %s to the destination parameter type %s", key, rawValue, def.Type)
}
Expand Down Expand Up @@ -63,6 +70,20 @@ func (d *Duffle) loadParameters(claim *claim.Claim, rawOverrides map[string]stri
return bundle.ValuesOrDefaults(overrides, bun)
}

func (d *Duffle) getUnconvertedValueFromRaw(def *definition.Schema, key, rawValue string) (string, error) {
// the parameter value (via rawValue) may represent a file on the local filesystem
if def.Type == "string" && def.ContentEncoding == "base64" {
if _, err := d.FileSystem.Stat(rawValue); err == nil {
bytes, err := d.FileSystem.ReadFile(rawValue)
if err != nil {
return "", errors.Wrapf(err, "unable to read file parameter %s", key)
}
return base64.StdEncoding.EncodeToString(bytes), nil
}
}
return rawValue, nil
}

// TODO: remove in favor of cnab-go logic: https://github.com/deislabs/cnab-go/pull/99
func appliesToAction(action string, parameter bundle.Parameter) bool {
if len(parameter.ApplyTo) == 0 {
Expand Down
37 changes: 37 additions & 0 deletions pkg/cnab/provider/parameters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,40 @@ func Test_loadParameters_requiredButDoesNotApply(t *testing.T) {

require.Equal(t, "foo-claim-value", params["foo"], "expected param 'foo' to be the bundle default")
}

func Test_loadParameters_fileParameter(t *testing.T) {
c := config.NewTestConfig(t)
d := NewDuffle(c.Config)

c.TestContext.AddTestFile("testdata/file-param", "/path/to/file")

claim, err := claim.New("test")
require.NoError(t, err)

claim.Bundle = &bundle.Bundle{
Definitions: definition.Definitions{
"foo": &definition.Schema{
Type: "string",
ContentEncoding: "base64",
},
},
Parameters: map[string]bundle.Parameter{
"foo": bundle.Parameter{
Definition: "foo",
Required: true,
Destination: &bundle.Location{
Path: "/tmp/foo",
},
},
},
}

overrides := map[string]string{
"foo": "/path/to/file",
}

params, err := d.loadParameters(claim, overrides, "action")
require.NoError(t, err)

require.Equal(t, "SGVsbG8gV29ybGQh", params["foo"], "expected param 'foo' to be the base64-encoded file contents")
}
1 change: 1 addition & 0 deletions pkg/cnab/provider/testdata/file-param
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello World!
55 changes: 54 additions & 1 deletion pkg/config/manifest.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"fmt"
"io/ioutil"
"net/http"
"reflect"
Expand Down Expand Up @@ -88,6 +89,13 @@ func (m *Manifest) Validate() error {
}
}

for _, parameter := range m.Parameters {
err = parameter.Validate()
if err != nil {
result = multierror.Append(result, err)
}
}

return result
}

Expand All @@ -104,6 +112,45 @@ type ParameterDefinition struct {
definition.Schema `yaml:",inline"`
}

func (pd *ParameterDefinition) Validate() error {
var result *multierror.Error

if pd.Name == "" {
result = multierror.Append(result, errors.New("parameter name is required"))
}

// Porter supports declaring a parameter of type: "file",
// which we will convert to the appropriate bundle.Parameter type in adapter.go
// Here, we copy the ParameterDefinition and make the same modification before validation
pdCopy := pd.DeepCopy()
if pdCopy.Type == "file" {
if pd.Destination.Path == "" {
result = multierror.Append(result, fmt.Errorf("no destination path supplied for parameter %s", pd.Name))
}
pdCopy.Type = "string"
pdCopy.ContentEncoding = "base64"
}

schemaValidationErrs, err := pdCopy.Schema.Validate(pdCopy)
if err != nil {
result = multierror.Append(result, errors.Wrapf(err, "encountered error while validating parameter %s", pdCopy.Name))
}
for _, schemaValidationErr := range schemaValidationErrs {
result = multierror.Append(result, errors.Wrapf(err, "encountered validation error(s) for parameter %s: %v", pdCopy.Name, schemaValidationErr))
}

return result.ErrorOrNil()
}

// DeepCopy copies a ParameterDefinition and returns the copy
func (pd *ParameterDefinition) DeepCopy() *ParameterDefinition {
var p2 ParameterDefinition
p2 = *pd
p2.ApplyTo = make([]string, len(pd.ApplyTo))
copy(p2.ApplyTo, pd.ApplyTo)
return &p2
}

type CredentialDefinition struct {
Name string `yaml:"name"`
Description string `yaml:"description,omitempty"`
Expand Down Expand Up @@ -239,7 +286,13 @@ func (od *OutputDefinition) Validate() error {
return errors.New("output name is required")
}

// TODO: Validate inline Schema
schemaValidationErrs, err := od.Schema.Validate(od)
if err != nil {
return errors.Wrapf(err, "encountered error while validating output %s", od.Name)
}
if len(schemaValidationErrs) != 0 {
return errors.Wrapf(err, "encountered validation error(s) for output %s: %v", od.Name, schemaValidationErrs)
}

return nil
}
Expand Down
23 changes: 23 additions & 0 deletions pkg/config/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"io/ioutil"
"testing"

"github.com/deislabs/cnab-go/bundle/definition"
"gopkg.in/yaml.v2"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -82,3 +83,25 @@ func TestMixinDeclaration_MarshalYAML(t *testing.T) {

assert.Equal(t, string(wantYaml), string(gotYaml))
}

func TestValidateParameterDefinition(t *testing.T) {
pd := ParameterDefinition{
Name: "myparam",
Schema: definition.Schema{
Type: "file",
},
}

pd.Destination = Location{}

err := pd.Validate()
assert.EqualError(t, err, `1 error occurred:
* no destination path supplied for parameter myparam
`)

pd.Destination.Path = "/path/to/file"

err = pd.Validate()
assert.NoError(t, err)
}
32 changes: 31 additions & 1 deletion pkg/config/runtime-manifest.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"encoding/base64"
"fmt"
"os"
"reflect"
Expand Down Expand Up @@ -85,7 +86,6 @@ func resolveParameter(pd ParameterDefinition) (string, error) {
return pd.Destination.Path, nil
}
return "", fmt.Errorf("parameter: %s is malformed", pd.Name)

}

func resolveCredential(cd CredentialDefinition) (string, error) {
Expand Down Expand Up @@ -275,3 +275,33 @@ func (m *RuntimeManifest) ResolveStep(step *Step) error {

return nil
}

// Prepare prepares the runtime environment prior to step execution
func (m *RuntimeManifest) Prepare() error {
// For parameters of type "file", we may need to decode files on the filesystem
// before execution of the step/action
for _, param := range m.Parameters {
if param.Type == "file" {
if param.Destination.Path == "" {
return fmt.Errorf("destination path is not supplied for parameter %s", param.Name)
}

// Porter by default places parameter value into file determined by Destination.Path
bytes, err := m.FileSystem.ReadFile(param.Destination.Path)
if err != nil {
return fmt.Errorf("unable to acquire value for parameter %s", param.Name)
}

decoded, err := base64.StdEncoding.DecodeString(string(bytes))
if err != nil {
return errors.Wrapf(err, "unable to decode parameter %s", param.Name)
}

err = m.FileSystem.WriteFile(param.Destination.Path, decoded, os.ModePerm)
if err != nil {
return errors.Wrapf(err, "unable to write decoded parameter %s", param.Name)
}
}
}
return nil
}
Loading

0 comments on commit 21bb79f

Please sign in to comment.