Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Commit

Permalink
Template package cleanup + more tests (#366)
Browse files Browse the repository at this point in the history
Signed-off-by: David Chung <david.chung@docker.com>
  • Loading branch information
David Chung committed Jan 17, 2017
1 parent def9d15 commit f432fa4
Show file tree
Hide file tree
Showing 7 changed files with 465 additions and 79 deletions.
4 changes: 2 additions & 2 deletions pkg/rpc/instance/rpc_test.go
Expand Up @@ -3,15 +3,15 @@ package instance
import (
"encoding/json"
"errors"
"io/ioutil"
"path"
"path/filepath"
"testing"

"github.com/docker/infrakit/pkg/plugin"
rpc_server "github.com/docker/infrakit/pkg/rpc/server"
"github.com/docker/infrakit/pkg/spi/instance"
"github.com/stretchr/testify/require"
"io/ioutil"
"path"
)

type testPlugin struct {
Expand Down
73 changes: 73 additions & 0 deletions pkg/template/fetch_test.go
@@ -0,0 +1,73 @@
package template

import (
"encoding/json"
"io/ioutil"
"path"
"path/filepath"
"testing"

rpc "github.com/docker/infrakit/pkg/rpc/instance"
rpc_server "github.com/docker/infrakit/pkg/rpc/server"
"github.com/docker/infrakit/pkg/spi/instance"
"github.com/stretchr/testify/require"
)

type testPlugin struct {
// Validate performs local validation on a provision request.
DoValidate func(req json.RawMessage) error

// Provision creates a new instance based on the spec.
DoProvision func(spec instance.Spec) (*instance.ID, error)

// Destroy terminates an existing instance.
DoDestroy func(instance instance.ID) error

// DescribeInstances returns descriptions of all instances matching all of the provided tags.
DoDescribeInstances func(tags map[string]string) ([]instance.Description, error)
}

func (t *testPlugin) Validate(req json.RawMessage) error {
return t.DoValidate(req)
}
func (t *testPlugin) Provision(spec instance.Spec) (*instance.ID, error) {
return t.DoProvision(spec)
}
func (t *testPlugin) Destroy(instance instance.ID) error {
return t.DoDestroy(instance)
}
func (t *testPlugin) DescribeInstances(tags map[string]string) ([]instance.Description, error) {
return t.DoDescribeInstances(tags)
}

func tempSocket() string {
dir, err := ioutil.TempDir("", "infrakit-test-")
if err != nil {
panic(err)
}

return path.Join(dir, "instance-impl-test")
}

func TestFetchSocket(t *testing.T) {
socketPath := tempSocket()
dir := filepath.Dir(socketPath)
host := filepath.Base(socketPath)

url := "unix://" + host + "/info/api.json"

server, err := rpc_server.StartPluginAtPath(socketPath, rpc.PluginServer(&testPlugin{}))
require.NoError(t, err)

buff, err := fetch(url, Options{SocketDir: dir})
require.NoError(t, err)

decoded, err := FromJSON(buff)
require.NoError(t, err)

result, err := QueryObject("Implements[].Name | [0]", decoded)
require.NoError(t, err)
require.Equal(t, "Instance", result)

server.Stop()
}
125 changes: 78 additions & 47 deletions pkg/template/funcs.go
Expand Up @@ -9,51 +9,75 @@ import (
"github.com/jmespath/go-jmespath"
)

// DefaultFuncs returns a list of default functions for binding in the template
func (t *Template) DefaultFuncs() map[string]interface{} {
return map[string]interface{}{
"unixtime": func() interface{} {
return time.Now().Unix()
},
// QueryObject applies a JMESPath query specified by the expression, against the target object.
func QueryObject(exp string, target interface{}) (interface{}, error) {
query, err := jmespath.Compile(exp)
if err != nil {
return nil, err
}
return query.Search(target)
}

"var": func(name, doc string, v ...interface{}) interface{} {
if found, has := t.binds[name]; has {
return found
}
return v // default
},
// SplitLines splits the input into a string slice.
func SplitLines(o interface{}) ([]string, error) {
ret := []string{}
switch o := o.(type) {
case string:
return strings.Split(o, "\n"), nil
case []byte:
return strings.Split(string(o), "\n"), nil
}
return ret, fmt.Errorf("not-supported-value-type")
}

"global": func(name string, v interface{}) interface{} {
t.binds[name] = v
return ""
},
// FromJSON decode the input JSON encoded as string or byte slice into a map.
func FromJSON(o interface{}) (interface{}, error) {
ret := map[string]interface{}{}
switch o := o.(type) {
case string:
err := json.Unmarshal([]byte(o), &ret)
return ret, err
case []byte:
err := json.Unmarshal(o, &ret)
return ret, err
}
return ret, fmt.Errorf("not-supported-value-type")
}

"q": func(q string, o interface{}) (interface{}, error) {
query, err := jmespath.Compile(q)
if err != nil {
return nil, err
}
return query.Search(o)
},
// ToJSON encodes the input struct into a JSON string.
func ToJSON(o interface{}) (string, error) {
buff, err := json.MarshalIndent(o, "", " ")
return string(buff), err
}

"jsonEncode": func(o interface{}) (string, error) {
buff, err := json.MarshalIndent(o, "", " ")
return string(buff), err
},
// FromMap decodes map into raw struct
func FromMap(m map[string]interface{}, raw interface{}) error {
// The safest way, but the slowest, is to just marshal and unmarshal back
buff, err := ToJSON(m)
if err != nil {
return err
}
return json.Unmarshal([]byte(buff), raw)
}

"jsonDecode": func(o interface{}) (interface{}, error) {
ret := map[string]interface{}{}
switch o := o.(type) {
case string:
err := json.Unmarshal([]byte(o), &ret)
return ret, err
case []byte:
err := json.Unmarshal(o, &ret)
return ret, err
}
return ret, fmt.Errorf("not-supported-value-type")
},
// ToMap encodes the input as a map
func ToMap(raw interface{}) (map[string]interface{}, error) {
buff, err := ToJSON(raw)
if err != nil {
return nil, err
}
out, err := FromJSON(buff)
return out.(map[string]interface{}), err
}

// UnixTime returns a timestamp in unix time
func UnixTime() interface{} {
return time.Now().Unix()
}

// DefaultFuncs returns a list of default functions for binding in the template
func (t *Template) DefaultFuncs() map[string]interface{} {
return map[string]interface{}{
"include": func(p string, opt ...interface{}) (string, error) {
var o interface{}
if len(opt) > 0 {
Expand All @@ -78,15 +102,22 @@ func (t *Template) DefaultFuncs() map[string]interface{} {
return included.Render(o)
},

"lines": func(o interface{}) ([]string, error) {
ret := []string{}
switch o := o.(type) {
case string:
return strings.Split(o, "\n"), nil
case []byte:
return strings.Split(string(o), "\n"), nil
"var": func(name, doc string, v ...interface{}) interface{} {
if found, has := t.binds[name]; has {
return found
}
return ret, fmt.Errorf("not-supported-value-type")
return v // default
},

"global": func(name string, v interface{}) interface{} {
t.binds[name] = v
return ""
},

"q": QueryObject,
"unixtime": UnixTime,
"lines": SplitLines,
"to_json": ToJSON,
"from_json": FromJSON,
}
}

0 comments on commit f432fa4

Please sign in to comment.