Skip to content

Commit

Permalink
fix(bind): interpret bind defined as type []map[string]string (#302)
Browse files Browse the repository at this point in the history
As per the type definition of Layer, bind is of type Bind which is a
struct with fields Source and Dest.

But when we do UnmarshalYAML for type Bind, we are assuming that we
would always get the Bind as a []string and not
[]map[interface{}]interface{} (when making a call to
getStringOrStringSlice).

This PR fixes the getStringOrStringSlice to consider Bind defined
as a []map[string]string and returns appropriate bind string
in that case.

Fixes #301

Signed-off-by: Darshil Parikh <darshil.parikh@gmail.com>
  • Loading branch information
darshilgit authored Dec 14, 2022
1 parent 7c03f4d commit 73a37c9
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 8 deletions.
65 changes: 65 additions & 0 deletions test/binds.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
load helpers

function setup() {
stacker_setup
}

function teardown() {
cleanup
}

@test "bind as string slice" {
cat > stacker.yaml <<"EOF"
bind-test:
from:
type: oci
url: ${{CENTOS_OCI}}
binds:
- ${{bind_path}} -> /root/tree1/foo
run: |
touch /root/tree1/foo/bar
EOF
mkdir -p tree1/foo

# since we are creating directory as
# real root and then `touch`-ing a file
# where in user NS, need to have rw persmission
# for others
chmod +666 tree1/foo

bind_path=$(realpath tree1/foo)

out=$(stacker build --substitute bind_path=${bind_path} --substitute CENTOS_OCI=$CENTOS_OCI)

[[ "${out}" =~ ^(.*filesystem bind-test built successfully)$ ]]

stat tree1/foo/bar
}

@test "bind as struct" {
cat > stacker.yaml <<"EOF"
bind-test:
from:
type: oci
url: ${{CENTOS_OCI}}
binds:
- Source: ${{bind_path}}
Dest: /root/tree1/foo
run: |
touch /root/tree1/foo/bar
EOF
mkdir -p tree1/foo

# since we are creating directory as
# real root and then `touch`-ing a file
# where in user NS, need to have rw persmission
# for others
chmod +666 tree1/foo

bind_path=$(realpath tree1/foo)

out=$(stacker build --substitute bind_path=$bind_path --substitute CENTOS_OCI=$CENTOS_OCI)
[[ "${out}" =~ ^(.*filesystem bind-test built successfully)$ ]]

stat tree1/foo/bar
}
61 changes: 53 additions & 8 deletions types/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,67 @@ type OverlayDir struct {

type OverlayDirs []OverlayDir

func getStringOrStringSlice(iface interface{}, xform func(string) ([]string, error)) ([]string, error) {
func validateDataAsBind(i interface{}) (map[interface{}]interface{}, error) {
bindMap, ok := i.(map[interface{}]interface{})
if !ok {
return nil, errors.Errorf("unable to cast into map[interface{}]interface{}: %T", i)
}

// validations
bindSource, ok := bindMap["Source"]
if !ok {
return nil, errors.Errorf("bind source missing: %v", i)
}

_, ok = bindSource.(string)
if !ok {
return nil, errors.Errorf("unknown bind source type, expected string: %T", i)
}

bindDest, ok := bindMap["Dest"]
if !ok {
return nil, errors.Errorf("bind dest missing: %v", i)
}

_, ok = bindDest.(string)
if !ok {
return nil, errors.Errorf("unknown bind dest type, expected string: %T", i)
}

if bindSource == "" || bindDest == "" {
return nil, errors.Errorf("empty source or dest: %v", i)
}

return bindMap, nil
}

func getStringOrStringSlice(data interface{}, xform func(string) ([]string, error)) ([]string, error) {
// The user didn't supply run: at all, so let's not do anything.
if iface == nil {
if data == nil {
return []string{}, nil
}

// This is how the yaml decoder decodes it if it's:
// run:
// - foo
// - bar
ifs, ok := iface.([]interface{})
ifs, ok := data.([]interface{})
if ok {
strs := []string{}
for _, i := range ifs {
s, ok := i.(string)
if !ok {
s := ""
switch v := i.(type) {
case string:
s = v
case interface{}:
bindMap, err := validateDataAsBind(i)
if err != nil {
return nil, err
}

// validations passed, return as string in form: source -> dest
s = fmt.Sprintf("%s -> %s", bindMap["Source"], bindMap["Dest"])
default:
return nil, errors.Errorf("unknown run array type: %T", i)
}

Expand All @@ -80,20 +125,20 @@ func getStringOrStringSlice(iface interface{}, xform func(string) ([]string, err
// run: |
// echo hello world
// echo goodbye cruel world
line, ok := iface.(string)
line, ok := data.(string)
if ok {
return xform(line)
}

// This is how it is after we do our find replace and re-set it; as a
// convenience (so we don't have to re-wrap it in interface{}), let's
// handle []string
strs, ok := iface.([]string)
strs, ok := data.([]string)
if ok {
return strs, nil
}

return nil, errors.Errorf("unknown directive type: %T", iface)
return nil, errors.Errorf("unknown directive type: %T", data)
}

// StringList allows this type to be parsed from the yaml parser as either a
Expand Down

0 comments on commit 73a37c9

Please sign in to comment.