Skip to content

Commit

Permalink
Merge pull request #1452 from mesg-foundation/feature/instance-env-hash
Browse files Browse the repository at this point in the history
Add EnvHash to instance and calculate the hash based on it
  • Loading branch information
antho1404 committed Oct 31, 2019
2 parents f9ebe82 + 9360a4b commit 2386f10
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 123 deletions.
2 changes: 1 addition & 1 deletion cosmos/client.go
Expand Up @@ -87,7 +87,7 @@ func (c *Client) BuildAndBroadcastMsg(msg sdktypes.Msg, accName, accPassword str
return nil, errors.New("result data is not the right type")
}
if data.TxResult.Result.IsErr() {
return nil, errors.New("an error occurred in transaction")
return nil, fmt.Errorf("an error occurred in transaction: %s", data.TxResult.Result.Log)
}
return &data.TxResult.Result, nil
case <-ctx.Done():
Expand Down
6 changes: 5 additions & 1 deletion e2e/instance_test.go
Expand Up @@ -24,7 +24,10 @@ func testInstance(t *testing.T) {
acknowledgement.WaitForStreamToBeReady(stream)

ctx := metadata.NewOutgoingContext(context.Background(), passmd)
resp, err := client.InstanceClient.Create(ctx, &pb.CreateInstanceRequest{ServiceHash: testServiceHash})
resp, err := client.InstanceClient.Create(ctx, &pb.CreateInstanceRequest{
ServiceHash: testServiceHash,
Env: []string{"BAR=3", "REQUIRED=4"},
})
require.NoError(t, err)
testInstanceHash = resp.Hash

Expand All @@ -38,6 +41,7 @@ func testInstance(t *testing.T) {
require.NoError(t, err)
require.Equal(t, testInstanceHash, resp.Hash)
require.Equal(t, testServiceHash, resp.ServiceHash)
require.Equal(t, hash.Dump([]string{"BAR=3", "FOO=1", "REQUIRED=4"}), resp.EnvHash)
})

t.Run("list", func(t *testing.T) {
Expand Down
10 changes: 8 additions & 2 deletions e2e/testdata/test-service.json
@@ -1,7 +1,13 @@
{
"sid": "test-service",
"name": "test-service",
"configuration": {},
"configuration": {
"env": [
"FOO=1",
"BAR=2",
"REQUIRED"
]
},
"dependencies": [],
"tasks": [
{
Expand Down Expand Up @@ -85,5 +91,5 @@
]
}
],
"source": "QmNhfbBnYYTYJVFeBBPMgBtCMGKe2xXjiSdAFgDQQ3JaMz"
"source": "QmPG1Ze96pH1EgVMWsGKM33jXoG63rigMncSEqZXP7oncq"
}
7 changes: 7 additions & 0 deletions e2e/testdata/test-service/mesg.yml
@@ -1,5 +1,12 @@
name: test-service
sid: test-service

configuration:
env:
- FOO=1
- BAR=2
- REQUIRED

tasks:
ping:
inputs:
Expand Down
17 changes: 10 additions & 7 deletions instance/instance.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions protobuf/types/instance.proto
Expand Up @@ -15,6 +15,13 @@ message Instance {
];

bytes serviceHash = 2 [
(gogoproto.moretags) = 'hash:"name:2"',
(gogoproto.customtype) = "github.com/mesg-foundation/engine/hash.Hash",
(gogoproto.nullable) = false
];

bytes envHash = 3 [
(gogoproto.moretags) = 'hash:"name:3"',
(gogoproto.customtype) = "github.com/mesg-foundation/engine/hash.Hash",
(gogoproto.nullable) = false
];
Expand Down
22 changes: 10 additions & 12 deletions sdk/instance/instance.go
Expand Up @@ -102,32 +102,30 @@ func (i *Instance) Create(serviceHash hash.Hash, env []string) (*instance.Instan
if err != nil {
return nil, err
}

// calculate the final env vars by overwriting user defined one's with defaults.
instanceEnv := xos.EnvMergeMaps(xos.EnvSliceToMap(srv.Configuration.Env), xos.EnvSliceToMap(env))
instanceEnv := xos.EnvMergeSlices(srv.Configuration.Env, env)

// calculate instance's hash.
h := hash.New()
h.Write(srv.Hash)
h.Write([]byte(xos.EnvMapToString(instanceEnv)))
instanceHash := h.Sum(nil)
inst := &instance.Instance{
ServiceHash: srv.Hash,
EnvHash: hash.Dump(instanceEnv),
}
inst.Hash = hash.Dump(inst)

// check if instance already exists
if exist, err := i.instanceDB.Exist(instanceHash); err != nil {
if exist, err := i.instanceDB.Exist(inst.Hash); err != nil {
return nil, err
} else if exist {
return nil, &AlreadyExistsError{Hash: instanceHash}
return nil, &AlreadyExistsError{Hash: inst.Hash}
}

// save & start instance.
inst := &instance.Instance{
Hash: instanceHash,
ServiceHash: srv.Hash,
}
if err := i.instanceDB.Save(inst); err != nil {
return nil, err
}

_, err = i.start(inst, imageHash, xos.EnvMapToSlice(instanceEnv))
_, err = i.start(inst, imageHash, instanceEnv)
return inst, err
}

Expand Down
62 changes: 17 additions & 45 deletions x/xos/env.go
Expand Up @@ -5,55 +5,27 @@ import (
"strings"
)

// EnvMapToSlice transform a map of key value to a slice of env in the form "key=value".
// Env vars are sorted by names to get an accurate order while testing.
func EnvMapToSlice(values map[string]string) []string {
env := make([]string, 0, len(values))
for k, v := range values {
env = append(env, k+"="+v)
}
sort.Stable(sort.StringSlice(env))
return env
}

// EnvMapToString transform a map of key value to a string in the form "key=value;key1=value1".
// Env vars are sorted by names to get an accurate order while testing.
func EnvMapToString(values map[string]string) string {
env := EnvMapToSlice(values)
return strings.Join(env, ";")
}

// EnvSliceToMap transform a slice of key=value to a map.
func EnvSliceToMap(values []string) map[string]string {
env := make(map[string]string, len(values))
for _, v := range values {
if e := strings.SplitN(v, "=", 2); len(e) == 1 {
env[e[0]] = ""
} else {
env[e[0]] = e[1]
// EnvMergeSlices merges multiple slices into single one.
// If the same key exist multiple time, it will be added in occurrence order.
func EnvMergeSlices(values ...[]string) []string {
envs := make(map[string]string)
for _, value := range values {
for _, v := range value {
if e := strings.SplitN(v, "=", 2); len(e) == 1 {
envs[e[0]] = ""
} else {
envs[e[0]] = e[1]
}
}
}
return env
}

// EnvMergeMaps merges multiple maps into single one.
// If the same key exist multiple time, it will be overwritten by the latest occurrence.
func EnvMergeMaps(values ...map[string]string) map[string]string {
env := make(map[string]string)
for _, e := range values {
for k, v := range e {
env[k] = v
}
env := make([]string, 0, len(values))
for k, v := range envs {
env = append(env, k+"="+v)
}
return env
}

// EnvMergeSlices merges multiple slices into single one.
// If the same key exist multiple time, it will be added in occurrence order.
func EnvMergeSlices(values ...[]string) []string {
env := make([]string, 0)
for _, v := range values {
env = append(env, v...)
}
// Make sure envs are sorted to give repeatable output
// It is important for hash instance calculation
sort.Stable(sort.StringSlice(env))
return env
}
56 changes: 2 additions & 54 deletions x/xos/env_test.go
Expand Up @@ -2,67 +2,15 @@ package xos

import (
"testing"

"github.com/mesg-foundation/engine/x/xstrings"
)

func TestEnvMapToSlice(t *testing.T) {
env := EnvMapToSlice(map[string]string{
"a": "1",
"b": "2",
})
for _, v := range []string{"a=1", "b=2"} {
if !xstrings.SliceContains(env, v) {
t.Errorf("env slice dosen't contain %s", v)
}
}
}

func TestEnvMapToString(t *testing.T) {
got := EnvMapToString(map[string]string{
"b": "2",
"a": "1",
})
want := "a=1;b=2"
if got != want {
t.Errorf("invalid env string - got %s, want %s", got, want)
}
}

func TestEnvSliceToMap(t *testing.T) {
env := EnvSliceToMap([]string{"a=1", "b=2"})
for k, v := range map[string]string{"a": "1", "b": "2"} {
if env[k] != v {
t.Errorf("env map dosen't contain %s=%v", k, v)
}
}
}

func TestEnvMergeMaps(t *testing.T) {
values := []map[string]string{
{
"a": "1",
"b": "2",
},
{
"a": "2",
"c": "3",
},
}
env := EnvMergeMaps(values...)
for k, v := range map[string]string{"a": "2", "b": "2", "c": "3"} {
if env[k] != v {
t.Errorf("env map dosen't contain %s=%s", k, v)
}
}
}
func TestEnvMergeSlices(t *testing.T) {
values := [][]string{
{"a=1", "b=2"},
{"a=2", "c=3"},
{"c=3", "a=2"},
}
env := EnvMergeSlices(values...)
for i, v := range []string{"a=1", "b=2", "a=2", "c=3"} {
for i, v := range []string{"a=2", "b=2", "c=3"} {
if env[i] != v {
t.Errorf("env slice dosen't contain %s", v)
}
Expand Down
6 changes: 5 additions & 1 deletion x/xvalidator/validator.go
Expand Up @@ -41,7 +41,11 @@ func IsPortMapping(fl validator.FieldLevel) bool {
}

// IsEnv validates if given field is valid env variable declaration.
// The valid formats are:
// - ENV
// - ENV=
// - ENV=value
func IsEnv(fl validator.FieldLevel) bool {
e := strings.Split(fl.Field().String(), envSeparator)
return len(e) == 2 && envNameRegexp.MatchString(e[0])
return (len(e) == 1 || len(e) == 2) && envNameRegexp.MatchString(e[0])
}

0 comments on commit 2386f10

Please sign in to comment.