Skip to content

Commit

Permalink
Implemenation of host_list_provider function.
Browse files Browse the repository at this point in the history
See documentation for detail on the notion of providers.

Signed-off-by: Vladimir Vivien <vivienv@vmware.com>
  • Loading branch information
vladimirvivien committed Jun 20, 2020
1 parent c49ad1e commit daf30cb
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 52 deletions.
18 changes: 11 additions & 7 deletions starlark/crashd_config.go
Expand Up @@ -5,6 +5,7 @@ package starlark

import (
"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)

// addDefaultCrashdConf initalizes a Starlark Dict with default
Expand All @@ -25,20 +26,23 @@ func addDefaultCrashdConf(thread *starlark.Thread) error {
return nil
}

// crashConfig is built-in starlark function that wraps the kwargs into a dictionary value.
// The result is also added to the thread for other built-in to access.
// crashConfig is built-in starlark function that saves and returns the kwargs as a struct value.
// Starlark format: crashd_config(conf0=val0, ..., confN=ValN)
func crashdConfigFn(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var dictionary *starlark.Dict
var dictionary starlark.StringDict
if kwargs != nil {
dict, err := tupleSliceToDict(kwargs)
dict, err := kwargsToStringDict(kwargs)
if err != nil {
return starlark.None, err
}
dictionary = dict
}

// save dict to be used as default
thread.SetLocal(identifiers.crashdCfg, dictionary)
structVal := starlarkstruct.FromStringDict(starlarkstruct.Default, dictionary)

return dictionary, nil
// save values to be used as default
thread.SetLocal(identifiers.crashdCfg, structVal)

// return values as a struct (i.e. config.arg0, ... , config.argN)
return starlark.None, nil
}
39 changes: 15 additions & 24 deletions starlark/crashd_config_test.go
Expand Up @@ -7,7 +7,7 @@ import (
"strings"
"testing"

"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)

func TestCrashdConfigNew(t *testing.T) {
Expand Down Expand Up @@ -39,19 +39,16 @@ func TestCrashdConfigFunc(t *testing.T) {
if data == nil {
t.Fatal("crashd_config not saved in thread local")
}
cfg, ok := data.(*starlark.Dict)
cfg, ok := data.(*starlarkstruct.Struct)
if !ok {
t.Fatalf("unexpected type for thread local key configs.crashd: %T", data)
}
if cfg.Len() != 2 {
t.Fatalf("unexpected item count in configs.crashd: %d", cfg.Len())
if len(cfg.AttrNames()) != 2 {
t.Fatalf("unexpected item count in configs.crashd: %d", len(cfg.AttrNames()))
}
val, found, err := cfg.Get(starlark.String("foo"))
val, err := cfg.Attr("foo")
if err != nil {
t.Fatal(err)
}
if !found {
t.Fatalf("key 'foo' not found in configs.crashd")
t.Fatalf("key 'foo' not found in crashd_config: %s", err)
}
if trimQuotes(val.String()) != "fooval" {
t.Fatalf("unexpected value for key 'foo': %s", val.String())
Expand All @@ -71,20 +68,17 @@ func TestCrashdConfigFunc(t *testing.T) {
if data == nil {
t.Fatal("crashd_config function not returning value")
}
cfg, ok := data.(*starlark.Dict)
cfg, ok := data.(*starlarkstruct.Struct)
if !ok {
t.Fatalf("unexpected type for thread local key configs.crashd: %T", data)
}
if cfg.Len() != 2 {
t.Fatalf("unexpected item count in configs.crashd: %d", cfg.Len())
if len(cfg.AttrNames()) != 2 {
t.Fatalf("unexpected item count in configs.crashd: %d", len(cfg.AttrNames()))
}
val, found, err := cfg.Get(starlark.String("foo"))
val, err := cfg.Attr("foo")
if err != nil {
t.Fatal(err)
}
if !found {
t.Fatalf("key 'foo' not found in configs.crashd")
}
if trimQuotes(val.String()) != "fooval" {
t.Fatalf("unexpected value for key %s in configs.crashd", val.String())
}
Expand All @@ -104,19 +98,16 @@ func TestCrashdConfigFunc(t *testing.T) {
t.Fatal("default crashd_config not saved in thread local")
}

cfg, ok := data.(*starlark.Dict)
cfg, ok := data.(*starlarkstruct.Struct)
if !ok {
t.Fatalf("unexpected type for thread local key crashd_config: %T", data)
}
if cfg.Len() != 4 {
t.Fatalf("unexpected item count in configs.crashd: %d", cfg.Len())
if len(cfg.AttrNames()) != 4 {
t.Fatalf("unexpected item count in configs.crashd: %d", len(cfg.AttrNames()))
}
val, found, err := cfg.Get(starlark.String("uid"))
val, err := cfg.Attr("uid")
if err != nil {
t.Fatal(err)
}
if !found {
t.Fatalf("key 'foo' not found in configs.crashd")
t.Fatalf("key 'foo' not found in configs.crashd: %s", err)
}
if trimQuotes(val.String()) != getUid() {
t.Fatalf("unexpected value for key %s in configs.crashd", val.String())
Expand Down
48 changes: 48 additions & 0 deletions starlark/hostlist_provider.go
@@ -0,0 +1,48 @@
// Copyright (c) 2020 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package starlark

import (
"fmt"

"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)

// hostListProvider is a built-in starlark function that collects compute resources as a list of host IPs
// Starlark format: host_list_provider(hosts=<host-list> [, ssh_config=ssh_config()])
func hostListProvider(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var dictionary starlark.StringDict
if kwargs != nil {
dict, err := kwargsToStringDict(kwargs)
if err != nil {
return starlark.None, err
}
dictionary = dict
}

return newHostListProvider(thread, dictionary)
}

// newHostListProvider returns a struct with host list provider info
func newHostListProvider(thread *starlark.Thread, dictionary starlark.StringDict) (*starlarkstruct.Struct, error) {
// validate args
if _, ok := dictionary["hosts"]; !ok {
return nil, fmt.Errorf("%s: missing hosts argument", identifiers.hostListProvider)
}

// augment args
dictionary["kind"] = starlark.String(identifiers.hostListProvider)
dictionary["transport"] = starlark.String("ssh")
if _, ok := dictionary[identifiers.sshCfg]; !ok {
data := thread.Local(identifiers.sshCfg)
sshcfg, ok := data.(starlark.StringDict)
if !ok {
return nil, fmt.Errorf("%s: default ssh_config not found", identifiers.hostListProvider)
}
dictionary[identifiers.sshCfg] = starlarkstruct.FromStringDict(starlarkstruct.Default, sshcfg)
}

return starlarkstruct.FromStringDict(starlarkstruct.Default, dictionary), nil
}
84 changes: 84 additions & 0 deletions starlark/hostlist_provider_test.go
@@ -0,0 +1,84 @@
// Copyright (c) 2020 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package starlark

import (
"strings"
"testing"

"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)

func TestHostListProvider(t *testing.T) {
tests := []struct {
name string
script string
eval func(t *testing.T, script string)
}{
{
name: "single host",
script: `provider = host_list_provider(hosts="foo.host")`,
eval: func(t *testing.T, script string) {
exe := New()
if err := exe.Exec("test.star", strings.NewReader(script)); err != nil {
t.Fatal(err)
}
data := exe.result["provider"]
if data == nil {
t.Fatalf("%s function not returning value", identifiers.hostListProvider)
}
provider, ok := data.(*starlarkstruct.Struct)
if !ok {
t.Fatalf("expecting *starlark.Struct, got %T", data)
}
if len(provider.AttrNames()) != 1 {
t.Fatalf("unexpected item count in configs.crashd: %d", len(provider.AttrNames()))
}
val, err := provider.Attr("hosts")
if err != nil {
t.Fatal(err)
}
if trimQuotes(val.String()) != "foo.host" {
t.Fatalf("unexpected value for key %s in configs.crashd", val.String())
}
},
},
{
name: "multiple hosts",
script: `provider = host_list_provider(hosts=["foo.host.1", "foo.host.2"])`,
eval: func(t *testing.T, script string) {
exe := New()
if err := exe.Exec("test.star", strings.NewReader(script)); err != nil {
t.Fatal(err)
}
data := exe.result["provider"]
if data == nil {
t.Fatalf("%s function not returning value", identifiers.hostListProvider)
}
provider, ok := data.(*starlarkstruct.Struct)
if !ok {
t.Fatalf("expecting *starlark.Struct, got %T", data)
}
if len(provider.AttrNames()) != 1 {
t.Fatalf("unexpected item %s: %d", identifiers.hostListProvider, len(provider.AttrNames()))
}
val, err := provider.Attr("hosts")
if err != nil {
t.Fatal(err)
}
list := val.(*starlark.List)
if list.Len() != 2 {
t.Fatalf("expecting %d items for argument 'hosts', got %d", 2, list.Len())
}
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
test.eval(t, test.script)
})
}
}
82 changes: 82 additions & 0 deletions starlark/resources.go
@@ -0,0 +1,82 @@
// Copyright (c) 2020 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package starlark

import (
"fmt"

"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)

// resourcesFunc is a built-in starlark function that prepares returns compute resources as a struct.
// Starlark format: resources(provider=<provider-function>)
func resourcesFunc(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var dictionary starlark.StringDict
if kwargs != nil {
dict, err := kwargsToStringDict(kwargs)
if err != nil {
return starlark.None, err
}
dictionary = dict
}

var provider *starlarkstruct.Struct
if hosts, ok := dictionary["hosts"]; ok {
prov, err := newHostListProvider(thread, starlark.StringDict{"hosts": hosts})
if err != nil {
return starlark.None, err
}
provider = prov
} else if prov, ok := dictionary["provider"]; ok {
provider = prov.(*starlarkstruct.Struct)
}

// enumerates resources
return enum(provider)
}

// enum returns a struct containing the fully enumerated compute resource
// info needed to execute commands.
func enum(provider *starlarkstruct.Struct) (*starlarkstruct.Struct, error) {
if provider == nil {
fmt.Errorf("missing provider")
}

var resStruct *starlarkstruct.Struct

kindVal, err := provider.Attr("kind")
if err != nil {
return nil, fmt.Errorf("provider missing field kind")
}

kind := trimQuotes(kindVal.String())

switch kind {
case identifiers.hostListProvider:
names, err := provider.Attr("hosts")
if err != nil {
return nil, fmt.Errorf("hosts not found in %s", identifiers.hostListProvider)
}
transport, err := provider.Attr("transport")
if err != nil {
return nil, fmt.Errorf("transport not found in %s", identifiers.hostListProvider)
}

sshCfg, err := provider.Attr(identifiers.sshCfg)
if err != nil {
return nil, fmt.Errorf("ssh_config not found in %s", identifiers.hostListProvider)
}

dict := starlark.StringDict{
"kind": starlark.String("host_list_resources"),
"names": names,
"ip_addresses": names,
"transport": transport,
"ssh_config": sshCfg,
}
resStruct = starlarkstruct.FromStringDict(starlarkstruct.Default, dict)
}
return resStruct, nil
}
16 changes: 9 additions & 7 deletions starlark/ssh_config.go
Expand Up @@ -5,6 +5,7 @@ package starlark

import (
"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)

// addDefaultSshConf initalizes a Starlark Dict with default
Expand All @@ -25,21 +26,22 @@ func addDefaultSSHConf(thread *starlark.Thread) error {
return nil
}

// sshConfigFn is the backing built-in function for the `ssh_config` configuration function.
// It creates and returns a dictionary from collected configs (as kwargs)
// It also saves the dict into the thread as the last known ssh config to be used as default.
// sshConfigFn is the backing built-in fn that saves and returns its argument as struct value.
// Starlark format: ssh_config(conf0=val0, ..., confN=valN)
func sshConfigFn(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var dictionary *starlark.Dict
var dictionary starlark.StringDict
if kwargs != nil {
dict, err := tupleSliceToDict(kwargs)
dict, err := kwargsToStringDict(kwargs)
if err != nil {
return starlark.None, err
}
dictionary = dict
}

structVal := starlarkstruct.FromStringDict(starlarkstruct.Default, dictionary)

// save to be used as default when needed
thread.SetLocal(identifiers.sshCfg, dictionary)
thread.SetLocal(identifiers.sshCfg, structVal)

return dictionary, nil
return structVal, nil
}

0 comments on commit daf30cb

Please sign in to comment.