Skip to content
This repository was archived by the owner on Jan 21, 2020. It is now read-only.
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
4 changes: 2 additions & 2 deletions pkg/rpc/instance/rpc_test.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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,
}
}
Loading