Skip to content

Commit

Permalink
Enable custom values for porter build command
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuabezaleel committed Feb 8, 2022
1 parent 79ee7c3 commit 16041b1
Show file tree
Hide file tree
Showing 15 changed files with 392 additions and 4 deletions.
3 changes: 3 additions & 0 deletions cmd/porter/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Porter uses the docker driver as the default build driver, an alternate driver m
porter build --version 0.1.0
porter build --file path/to/porter.yaml
porter build --dir path/to/build/context
porter build --custom version=0.2.0
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
return opts.Validate(p)
Expand All @@ -81,6 +82,8 @@ Porter uses the docker driver as the default build driver, an alternate driver m
"Path to the build context directory where all bundle assets are located.")
f.StringVar(&opts.Driver, "driver", porter.BuildDriverDefault,
fmt.Sprintf("Experimental. Driver for building the invocation image. Allowed values are: %s", strings.Join(porter.BuildDriverAllowedValues, ", ")))
f.StringSliceVar(&opts.Customs, "custom", nil,
"Define an individual key-value pair for custom section in the form of NAME=VALUE. May be specified multiple times.")

// Allow configuring the --driver flag with build-driver, to avoid conflicts with other commands
cmd.Flag("driver").Annotations = map[string][]string{
Expand Down
91 changes: 91 additions & 0 deletions pkg/build/buildkit/buildkit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package buildkit

import (
"testing"

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

func TestConvertMap(t *testing.T) {
tt := []struct {
desc string
inp map[string]interface{}
out map[string]string
err bool
}{
{
desc: "one pair",
inp: map[string]interface{}{
"key": "value",
},
out: map[string]string{
"key": "value",
},
err: false,
},
{
desc: "nested input",
inp: map[string]interface{}{
"key": map[string]string{
"nestedKey": "value",
},
},
out: map[string]string{
"key.nestedKey": "value",
},
err: false,
},
{
desc: "nested input",
inp: map[string]interface{}{
"key1": map[string]interface{}{
"key2": map[string]string{
"key3": "value",
},
},
},
out: map[string]string{
"key1.key2.key3": "value",
},
err: false,
},
{
desc: "multiple nested input",
inp: map[string]interface{}{
"key11": map[string]interface{}{
"key12": map[string]string{
"key13": "value1",
},
},
"key21": map[string]string{
"key22": "value2",
},
},
out: map[string]string{
"key11.key12.key13": "value1",
"key21.key22": "value2",
},
err: false,
},
{
desc: "empty interface value other than map[string]interface{}, map[string]string or string",
inp: map[string]interface{}{
"a": 1,
},
err: true,
},
}

for _, tc := range tt {
t.Run(tc.desc, func(t *testing.T) {
out, err := convertMap(tc.inp)
if tc.err {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, out, tc.out)
})
}
}
45 changes: 41 additions & 4 deletions pkg/build/buildkit/buildx.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"

"get.porter.sh/porter/pkg/config"
"get.porter.sh/porter/pkg/experimental"
Expand Down Expand Up @@ -85,6 +86,19 @@ func (b *Builder) BuildInvocationImage(ctx context.Context, manifest *manifest.M
},
}

buildArgs := make(map[string]string)
buildArgs["BUNDLE_DIR"] = build.BUNDLE_DIR

convertedCustomInput := make(map[string]string)
convertedCustomInput, err = convertMap(manifest.Custom)
if err != nil {
return err
}

for k, v := range convertedCustomInput {
buildArgs[strings.ToUpper(strings.Replace(k, ".", "_", -1))] = v
}

opts := map[string]buildx.Options{
"default": {
Tags: []string{manifest.Image},
Expand All @@ -93,10 +107,8 @@ func (b *Builder) BuildInvocationImage(ctx context.Context, manifest *manifest.M
DockerfilePath: filepath.Join(b.Getwd(), build.DOCKER_FILE),
InStream: b.In,
},
BuildArgs: map[string]string{
"BUNDLE_DIR": build.BUNDLE_DIR,
},
Session: []session.Attachable{authprovider.NewDockerAuthProvider(b.Err)},
BuildArgs: buildArgs,
Session: []session.Attachable{authprovider.NewDockerAuthProvider(b.Err)},
},
}

Expand Down Expand Up @@ -207,3 +219,28 @@ func (b *Builder) TagInvocationImage(ctx context.Context, origTag, newTag string
}
return nil
}

func convertMap(mapInput map[string]interface{}) (map[string]string, error) {
out := make(map[string]string)
for key, value := range mapInput {
switch v := value.(type) {
case string:
out[key] = v
case map[string]interface{}:
tmp, err := convertMap(v)
if err != nil {
return nil, err
}
for innerKey, innerValue := range tmp {
out[key+"."+innerKey] = innerValue
}
case map[string]string:
for innerKey, innerValue := range v {
out[key+"."+innerKey] = innerValue
}
default:
return nil, errors.Errorf("Unknown type %#v: %t", v, v)
}
}
return out, nil
}
41 changes: 41 additions & 0 deletions pkg/build/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"

"get.porter.sh/porter/pkg/build"
portercontext "get.porter.sh/porter/pkg/context"
Expand All @@ -30,6 +31,20 @@ func NewBuilder(cxt *portercontext.Context) *Builder {
}

func (b *Builder) BuildInvocationImage(ctx context.Context, manifest *manifest.Manifest) error {
buildArgs := make(map[string]*string)
buildArgs["BUNDLE_DIR"] = &build.BUNDLE_DIR

convertedCustomInput := make(map[string]string)
convertedCustomInput, err := convertMap(manifest.Custom)
if err != nil {
return err
}

for k, v := range convertedCustomInput {
v := v
buildArgs[strings.ToUpper(strings.Replace(k, ".", "_", -1))] = &v
}

fmt.Fprintf(b.Out, "\nStarting Invocation Image Build (%s) =======> \n", manifest.Image)
buildOptions := types.ImageBuildOptions{
SuppressOutput: false,
Expand Down Expand Up @@ -96,3 +111,29 @@ func (b *Builder) TagInvocationImage(ctx context.Context, origTag, newTag string
}
return nil
}

func convertMap(mapInput map[string]interface{}) (map[string]string, error) {
out := make(map[string]string)

for key, value := range mapInput {
switch v := value.(type) {
case string:
out[key] = v
case map[string]interface{}:
tmp, err := convertMap(v)
if err != nil {
return nil, err
}
for innerKey, innerValue := range tmp {
out[key+"."+innerKey] = innerValue
}
case map[string]string:
for innerKey, innerValue := range v {
out[key+"."+innerKey] = innerValue
}
default:
return nil, errors.Errorf("Unknown type %#v: %t", v, v)
}
}
return out, nil
}
91 changes: 91 additions & 0 deletions pkg/build/docker/docker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package docker

import (
"testing"

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

func TestConvertMap(t *testing.T) {
tt := []struct {
desc string
inp map[string]interface{}
out map[string]string
err bool
}{
{
desc: "one pair",
inp: map[string]interface{}{
"key": "value",
},
out: map[string]string{
"key": "value",
},
err: false,
},
{
desc: "nested input",
inp: map[string]interface{}{
"key": map[string]string{
"nestedKey": "value",
},
},
out: map[string]string{
"key.nestedKey": "value",
},
err: false,
},
{
desc: "nested input",
inp: map[string]interface{}{
"key1": map[string]interface{}{
"key2": map[string]string{
"key3": "value",
},
},
},
out: map[string]string{
"key1.key2.key3": "value",
},
err: false,
},
{
desc: "multiple nested input",
inp: map[string]interface{}{
"key11": map[string]interface{}{
"key12": map[string]string{
"key13": "value1",
},
},
"key21": map[string]string{
"key22": "value2",
},
},
out: map[string]string{
"key11.key12.key13": "value1",
"key21.key22": "value2",
},
err: false,
},
{
desc: "empty interface value other than map[string]interface{}, map[string]string or string",
inp: map[string]interface{}{
"a": 1,
},
err: true,
},
}

for _, tc := range tt {
t.Run(tc.desc, func(t *testing.T) {
out, err := convertMap(tc.inp)
if tc.err {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, out, tc.out)
})
}
}
23 changes: 23 additions & 0 deletions pkg/porter/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
configadapter "get.porter.sh/porter/pkg/cnab/config-adapter"
"get.porter.sh/porter/pkg/config"
"get.porter.sh/porter/pkg/mixin"
"get.porter.sh/porter/pkg/parameters"
"get.porter.sh/porter/pkg/printer"
"github.com/Masterminds/semver/v3"
"github.com/opencontainers/go-digest"
Expand All @@ -26,6 +27,12 @@ type BuildOptions struct {

// Driver to use when building the invocation image.
Driver string

// Custom is the unparsed list of NAME=VALUE custom inputs set on the command line.
Customs []string

// parsedCustoms is the parsed set of custom inputs from Customs.
parsedCustoms map[string]string
}

const BuildDriverDefault = config.BuildDriverDocker
Expand Down Expand Up @@ -53,6 +60,11 @@ func (o *BuildOptions) Validate(p *Porter) error {
// This would be less awkward if we didn't do an automatic build during publish
p.Data.BuildDriver = o.Driver

err := o.parseCustomInputs()
if err != nil {
return err
}

return o.bundleFileOptions.Validate(p.Context)
}

Expand All @@ -65,6 +77,17 @@ func stringSliceContains(allowedValues []string, value string) bool {
return false
}

func (o *BuildOptions) parseCustomInputs() error {
p, err := parameters.ParseVariableAssignments(o.Customs)
if err != nil {
return err
}

o.parsedCustoms = p

return nil
}

func (p *Porter) Build(ctx context.Context, opts BuildOptions) error {
opts.Apply(p.Context)

Expand Down
Loading

0 comments on commit 16041b1

Please sign in to comment.