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

Commit f432fa4

Browse files
author
David Chung
authored
Template package cleanup + more tests (#366)
Signed-off-by: David Chung <[email protected]>
1 parent def9d15 commit f432fa4

File tree

7 files changed

+465
-79
lines changed

7 files changed

+465
-79
lines changed

pkg/rpc/instance/rpc_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ package instance
33
import (
44
"encoding/json"
55
"errors"
6+
"io/ioutil"
7+
"path"
68
"path/filepath"
79
"testing"
810

911
"github.com/docker/infrakit/pkg/plugin"
1012
rpc_server "github.com/docker/infrakit/pkg/rpc/server"
1113
"github.com/docker/infrakit/pkg/spi/instance"
1214
"github.com/stretchr/testify/require"
13-
"io/ioutil"
14-
"path"
1515
)
1616

1717
type testPlugin struct {

pkg/template/fetch_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package template
2+
3+
import (
4+
"encoding/json"
5+
"io/ioutil"
6+
"path"
7+
"path/filepath"
8+
"testing"
9+
10+
rpc "github.com/docker/infrakit/pkg/rpc/instance"
11+
rpc_server "github.com/docker/infrakit/pkg/rpc/server"
12+
"github.com/docker/infrakit/pkg/spi/instance"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
type testPlugin struct {
17+
// Validate performs local validation on a provision request.
18+
DoValidate func(req json.RawMessage) error
19+
20+
// Provision creates a new instance based on the spec.
21+
DoProvision func(spec instance.Spec) (*instance.ID, error)
22+
23+
// Destroy terminates an existing instance.
24+
DoDestroy func(instance instance.ID) error
25+
26+
// DescribeInstances returns descriptions of all instances matching all of the provided tags.
27+
DoDescribeInstances func(tags map[string]string) ([]instance.Description, error)
28+
}
29+
30+
func (t *testPlugin) Validate(req json.RawMessage) error {
31+
return t.DoValidate(req)
32+
}
33+
func (t *testPlugin) Provision(spec instance.Spec) (*instance.ID, error) {
34+
return t.DoProvision(spec)
35+
}
36+
func (t *testPlugin) Destroy(instance instance.ID) error {
37+
return t.DoDestroy(instance)
38+
}
39+
func (t *testPlugin) DescribeInstances(tags map[string]string) ([]instance.Description, error) {
40+
return t.DoDescribeInstances(tags)
41+
}
42+
43+
func tempSocket() string {
44+
dir, err := ioutil.TempDir("", "infrakit-test-")
45+
if err != nil {
46+
panic(err)
47+
}
48+
49+
return path.Join(dir, "instance-impl-test")
50+
}
51+
52+
func TestFetchSocket(t *testing.T) {
53+
socketPath := tempSocket()
54+
dir := filepath.Dir(socketPath)
55+
host := filepath.Base(socketPath)
56+
57+
url := "unix://" + host + "/info/api.json"
58+
59+
server, err := rpc_server.StartPluginAtPath(socketPath, rpc.PluginServer(&testPlugin{}))
60+
require.NoError(t, err)
61+
62+
buff, err := fetch(url, Options{SocketDir: dir})
63+
require.NoError(t, err)
64+
65+
decoded, err := FromJSON(buff)
66+
require.NoError(t, err)
67+
68+
result, err := QueryObject("Implements[].Name | [0]", decoded)
69+
require.NoError(t, err)
70+
require.Equal(t, "Instance", result)
71+
72+
server.Stop()
73+
}

pkg/template/funcs.go

Lines changed: 78 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,75 @@ import (
99
"github.com/jmespath/go-jmespath"
1010
)
1111

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

19-
"var": func(name, doc string, v ...interface{}) interface{} {
20-
if found, has := t.binds[name]; has {
21-
return found
22-
}
23-
return v // default
24-
},
21+
// SplitLines splits the input into a string slice.
22+
func SplitLines(o interface{}) ([]string, error) {
23+
ret := []string{}
24+
switch o := o.(type) {
25+
case string:
26+
return strings.Split(o, "\n"), nil
27+
case []byte:
28+
return strings.Split(string(o), "\n"), nil
29+
}
30+
return ret, fmt.Errorf("not-supported-value-type")
31+
}
2532

26-
"global": func(name string, v interface{}) interface{} {
27-
t.binds[name] = v
28-
return ""
29-
},
33+
// FromJSON decode the input JSON encoded as string or byte slice into a map.
34+
func FromJSON(o interface{}) (interface{}, error) {
35+
ret := map[string]interface{}{}
36+
switch o := o.(type) {
37+
case string:
38+
err := json.Unmarshal([]byte(o), &ret)
39+
return ret, err
40+
case []byte:
41+
err := json.Unmarshal(o, &ret)
42+
return ret, err
43+
}
44+
return ret, fmt.Errorf("not-supported-value-type")
45+
}
3046

31-
"q": func(q string, o interface{}) (interface{}, error) {
32-
query, err := jmespath.Compile(q)
33-
if err != nil {
34-
return nil, err
35-
}
36-
return query.Search(o)
37-
},
47+
// ToJSON encodes the input struct into a JSON string.
48+
func ToJSON(o interface{}) (string, error) {
49+
buff, err := json.MarshalIndent(o, "", " ")
50+
return string(buff), err
51+
}
3852

39-
"jsonEncode": func(o interface{}) (string, error) {
40-
buff, err := json.MarshalIndent(o, "", " ")
41-
return string(buff), err
42-
},
53+
// FromMap decodes map into raw struct
54+
func FromMap(m map[string]interface{}, raw interface{}) error {
55+
// The safest way, but the slowest, is to just marshal and unmarshal back
56+
buff, err := ToJSON(m)
57+
if err != nil {
58+
return err
59+
}
60+
return json.Unmarshal([]byte(buff), raw)
61+
}
4362

44-
"jsonDecode": func(o interface{}) (interface{}, error) {
45-
ret := map[string]interface{}{}
46-
switch o := o.(type) {
47-
case string:
48-
err := json.Unmarshal([]byte(o), &ret)
49-
return ret, err
50-
case []byte:
51-
err := json.Unmarshal(o, &ret)
52-
return ret, err
53-
}
54-
return ret, fmt.Errorf("not-supported-value-type")
55-
},
63+
// ToMap encodes the input as a map
64+
func ToMap(raw interface{}) (map[string]interface{}, error) {
65+
buff, err := ToJSON(raw)
66+
if err != nil {
67+
return nil, err
68+
}
69+
out, err := FromJSON(buff)
70+
return out.(map[string]interface{}), err
71+
}
72+
73+
// UnixTime returns a timestamp in unix time
74+
func UnixTime() interface{} {
75+
return time.Now().Unix()
76+
}
5677

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

81-
"lines": func(o interface{}) ([]string, error) {
82-
ret := []string{}
83-
switch o := o.(type) {
84-
case string:
85-
return strings.Split(o, "\n"), nil
86-
case []byte:
87-
return strings.Split(string(o), "\n"), nil
105+
"var": func(name, doc string, v ...interface{}) interface{} {
106+
if found, has := t.binds[name]; has {
107+
return found
88108
}
89-
return ret, fmt.Errorf("not-supported-value-type")
109+
return v // default
90110
},
111+
112+
"global": func(name string, v interface{}) interface{} {
113+
t.binds[name] = v
114+
return ""
115+
},
116+
117+
"q": QueryObject,
118+
"unixtime": UnixTime,
119+
"lines": SplitLines,
120+
"to_json": ToJSON,
121+
"from_json": FromJSON,
91122
}
92123
}

0 commit comments

Comments
 (0)