1
1
Fork 0
mirror of https://github.com/mcuadros/ascode synced 2024-05-08 08:36:15 +02:00

starlark/types: extract Make* functions

This commit is contained in:
Máximo Cuadros 2020-03-25 19:31:21 +01:00
parent f95cb8c6ad
commit 0bf3031092
No known key found for this signature in database
GPG Key ID: 17A5DFEDC735AE4B
7 changed files with 243 additions and 190 deletions

View File

@ -31,27 +31,34 @@ func init() {
// type string
// [Backend type](https://www.terraform.io/docs/backends/types/index.html).
//
func BuiltinBackend(pm *terraform.PluginManager) starlark.Value {
return starlark.NewBuiltin("backend", func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var name starlark.String
switch len(args) {
case 1:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return nil, fmt.Errorf("expected string, got %s", args.Index(0).Type())
}
default:
return nil, fmt.Errorf("unexpected positional arguments count")
}
func BuiltinBackend() starlark.Value {
return starlark.NewBuiltin("backend", MakeBackend)
}
p, err := NewBackend(pm, name.GoString())
if err != nil {
return nil, err
}
// MakeBackend defines the Backend constructor.
func MakeBackend(
t *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple,
) (starlark.Value, error) {
return p, p.loadKeywordArgs(kwargs)
})
var name starlark.String
switch len(args) {
case 1:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return nil, fmt.Errorf("expected string, got %s", args.Index(0).Type())
}
default:
return nil, fmt.Errorf("unexpected positional arguments count")
}
pm := t.Local(PluginManagerLocal).(*terraform.PluginManager)
p, err := NewBackend(pm, name.GoString())
if err != nil {
return nil, err
}
return p, p.loadKeywordArgs(kwargs)
}
// Backend represent a Terraform Backend.
@ -106,7 +113,7 @@ func NewBackend(pm *terraform.PluginManager, typ string) (*Backend, error) {
return &Backend{
pm: pm,
b: b,
Resource: MakeResource("", typ, BackendKind, b.ConfigSchema(), nil, nil),
Resource: NewResource("", typ, BackendKind, b.ConfigSchema(), nil, nil),
}, nil
}
@ -290,7 +297,7 @@ func (s *State) initializeResource(p *Provider, r *states.Resource) error {
multi := r.EachMode != states.NoEach
for _, instance := range r.Instances {
r := MakeResource(name, typ, ResourceKind, schema.Block, p, p.Resource)
r := NewResource(name, typ, ResourceKind, schema.Block, p, p.Resource)
var val interface{}
if err := json.Unmarshal(instance.Current.AttrsJSON, &val); err != nil {
@ -298,7 +305,7 @@ func (s *State) initializeResource(p *Provider, r *states.Resource) error {
}
values, _ := util.Marshal(val)
if err := r.LoadDict(values.(*starlark.Dict)); err != nil {
if err := r.loadDict(values.(*starlark.Dict)); err != nil {
return err
}

View File

@ -93,7 +93,16 @@ func (c *ResourceCollection) LoadList(l *starlark.List) error {
return fmt.Errorf("%d: expected dict, got %s", i, l.Index(i).Type())
}
c.MakeResource("", dict)
r := NewResource("", c.typ, c.kind, c.block, c.provider, c.parent)
if dict != nil && dict.Len() != 0 {
if err := r.loadDict(dict); err != nil {
return err
}
}
if err := c.List.Append(r); err != nil {
return err
}
}
return nil
@ -137,99 +146,16 @@ func (c *ResourceCollection) Name() string {
}
// CallInternal honors the starlark.Callable interface.
func (c *ResourceCollection) CallInternal(thread *starlark.Thread, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
name, dict, err := c.unpackArgs(args, kwargs)
func (c *ResourceCollection) CallInternal(
t *starlark.Thread, args starlark.Tuple, kwargs []starlark.Tuple,
) (starlark.Value, error) {
r, err := MakeResource(c, t, nil, args, kwargs)
if err != nil {
return nil, err
}
return c.MakeResource(name, dict)
}
// MakeResource it makes a new resource and loads the dict on it.
func (c *ResourceCollection) MakeResource(name string, dict *starlark.Dict) (*Resource, error) {
if (c.kind == ResourceKind || c.kind == DataSourceKind) && name == "" {
name = NameGenerator()
}
resource := MakeResource(name, c.typ, c.kind, c.block, c.provider, c.parent)
if dict != nil && dict.Len() != 0 {
if err := resource.LoadDict(dict); err != nil {
return nil, err
}
}
if err := c.List.Append(resource); err != nil {
return nil, err
}
return resource, nil
}
func (c *ResourceCollection) unpackArgsWithKwargs(args starlark.Tuple, kwargs []starlark.Tuple) (string, *starlark.Dict, error) {
dict := starlark.NewDict(len(kwargs))
var name starlark.String
for _, kwarg := range kwargs {
dict.SetKey(kwarg.Index(0), kwarg.Index(1))
}
if len(args) == 1 {
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return "", nil, fmt.Errorf("resource: expected string, got %s", args.Index(0).Type())
}
}
if len(args) > 1 {
return "", nil, fmt.Errorf("resource: unexpected positional args mixed with kwargs")
}
return string(name), dict, nil
}
func (c *ResourceCollection) unpackArgs(args starlark.Tuple, kwargs []starlark.Tuple) (string, *starlark.Dict, error) {
var dict *starlark.Dict
var name starlark.String
if len(args) == 0 && len(kwargs) == 0 {
return "", nil, nil
}
if len(kwargs) != 0 {
return c.unpackArgsWithKwargs(args, kwargs)
}
switch len(args) {
case 0:
case 1:
switch v := args.Index(0).(type) {
case starlark.String:
return string(v), nil, nil
case *starlark.Dict:
return "", v, nil
default:
return "", nil, fmt.Errorf("resource: expected string or dict, got %s", args.Index(0).Type())
}
case 2:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return "", nil, fmt.Errorf("resource: expected string, got %s", args.Index(0).Type())
}
dict, ok = args.Index(1).(*starlark.Dict)
if !ok {
return "", nil, fmt.Errorf("resource: expected dict, got %s", args.Index(1).Type())
}
default:
if c.kind != NestedKind {
return "", nil, fmt.Errorf("resource: unexpected positional arguments count")
}
}
return string(name), dict, nil
return r, c.List.Append(r)
}
func (c *ResourceCollection) toDict() *starlark.List {
@ -376,65 +302,31 @@ func (c *ProviderCollection) Name() string {
}
// CallInternal honors the starlark.Callable interface.
func (c *ProviderCollection) CallInternal(thread *starlark.Thread, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
name, version, alias, err := c.unpackArgs(args)
func (c *ProviderCollection) CallInternal(
t *starlark.Thread, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
v, err := MakeProvider(t, nil, args, kwargs)
if err != nil {
return nil, err
}
return c.MakeProvider(name, version, alias, kwargs)
}
func (c *ProviderCollection) MakeProvider(name, version, alias string, kwargs []starlark.Tuple) (*Provider, error) {
n := starlark.String(name)
a := starlark.String(alias)
p := v.(*Provider)
n := starlark.String(p.typ)
a := starlark.String(p.name)
if _, ok, _ := c.Get(n); !ok {
c.SetKey(n, NewDict())
}
providers, _, _ := c.Get(n)
if _, ok, _ := providers.(*Dict).Get(a); ok {
return nil, fmt.Errorf("already exists a provider %q with the alias %q", name, alias)
return nil, fmt.Errorf("already exists a provider %q with the alias %q", p.typ, p.name)
}
p, err := NewProvider(c.pm, name, version, alias)
if err != nil {
if err := providers.(*Dict).SetKey(a, p); err != nil {
return nil, err
}
if err := providers.(*Dict).SetKey(starlark.String(p.Resource.name), p); err != nil {
return nil, err
}
return p, p.loadKeywordArgs(kwargs)
}
func (c *ProviderCollection) unpackArgs(args starlark.Tuple) (string, string, string, error) {
var name, version, alias starlark.String
switch len(args) {
case 3:
var ok bool
alias, ok = args.Index(2).(starlark.String)
if !ok {
return "", "", "", fmt.Errorf("expected string, got %s", args.Index(2).Type())
}
fallthrough
case 2:
var ok bool
version, ok = args.Index(1).(starlark.String)
if !ok {
return "", "", "", fmt.Errorf("expected string, got %s", args.Index(1).Type())
}
fallthrough
case 1:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return "", "", "", fmt.Errorf("expected string, got %s", args.Index(0).Type())
}
default:
return "", "", "", fmt.Errorf("unexpected positional arguments count")
}
return string(name), string(version), string(alias), nil
return v, nil
}

View File

@ -13,6 +13,50 @@ import (
"go.starlark.net/syntax"
)
const (
PluginManagerLocal = "plugin_manager"
)
// MakeProvider defines the Provider constructor.
func MakeProvider(
t *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple,
) (starlark.Value, error) {
var name, version, alias starlark.String
switch len(args) {
case 3:
var ok bool
alias, ok = args.Index(2).(starlark.String)
if !ok {
return nil, fmt.Errorf("expected string, got %s", args.Index(2).Type())
}
fallthrough
case 2:
var ok bool
version, ok = args.Index(1).(starlark.String)
if !ok {
return nil, fmt.Errorf("expected string, got %s", args.Index(1).Type())
}
fallthrough
case 1:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return nil, fmt.Errorf("expected string, got %s", args.Index(0).Type())
}
default:
return nil, fmt.Errorf("unexpected positional arguments count")
}
pm := t.Local(PluginManagerLocal).(*terraform.PluginManager)
p, err := NewProvider(pm, name.GoString(), version.GoString(), alias.GoString())
if err != nil {
return nil, err
}
return p, p.loadKeywordArgs(kwargs)
}
// Provider represents a provider as a starlark.Value.
//
// outline: types
@ -96,13 +140,43 @@ func NewProvider(pm *terraform.PluginManager, typ, version, name string) (*Provi
meta: meta,
}
p.Resource = MakeResource(name, typ, ProviderKind, response.Provider.Block, p, nil)
p.Resource = NewResource(name, typ, ProviderKind, response.Provider.Block, p, nil)
p.dataSources = NewResourceCollectionGroup(p, DataSourceKind, response.DataSources)
p.resources = NewResourceCollectionGroup(p, ResourceKind, response.ResourceTypes)
return p, nil
}
func (p *Provider) unpackArgs(args starlark.Tuple) (string, string, string, error) {
var name, version, alias starlark.String
switch len(args) {
case 3:
var ok bool
alias, ok = args.Index(2).(starlark.String)
if !ok {
return "", "", "", fmt.Errorf("expected string, got %s", args.Index(2).Type())
}
fallthrough
case 2:
var ok bool
version, ok = args.Index(1).(starlark.String)
if !ok {
return "", "", "", fmt.Errorf("expected string, got %s", args.Index(1).Type())
}
fallthrough
case 1:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return "", "", "", fmt.Errorf("expected string, got %s", args.Index(0).Type())
}
default:
return "", "", "", fmt.Errorf("unexpected positional arguments count")
}
return string(name), string(version), string(alias), nil
}
func (p *Provider) String() string {
return fmt.Sprintf("Provider<%s>", p.typ)
}

View File

@ -59,17 +59,18 @@ func doTest(t *testing.T, filename string) {
func doTestPrint(t *testing.T, filename string, print func(*starlark.Thread, string)) {
id = 0
pm := &terraform.PluginManager{".providers"}
log.SetOutput(ioutil.Discard)
thread := &starlark.Thread{Load: load, Print: print}
thread.SetLocal("base_path", "testdata")
thread.SetLocal(PluginManagerLocal, pm)
test.SetReporter(thread, t)
pm := &terraform.PluginManager{".providers"}
predeclared := starlark.StringDict{
"provisioner": BuiltinProvisioner(pm),
"backend": BuiltinBackend(pm),
"provisioner": BuiltinProvisioner(),
"backend": BuiltinBackend(),
"hcl": BuiltinHCL(),
"fn": BuiltinFunctionComputed(),
"evaluate": BuiltinEvaluate(),

View File

@ -21,27 +21,35 @@ import (
// type string
// Provisioner type.
//
func BuiltinProvisioner(pm *terraform.PluginManager) starlark.Value {
return starlark.NewBuiltin("provisioner", func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var name starlark.String
switch len(args) {
case 1:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return nil, fmt.Errorf("expected string, got %s", args.Index(0).Type())
}
default:
return nil, fmt.Errorf("unexpected positional arguments count")
}
func BuiltinProvisioner() starlark.Value {
return starlark.NewBuiltin("provisioner", MakeProvisioner)
}
p, err := NewProvisioner(pm, name.GoString())
if err != nil {
return nil, err
}
// MakeProvisioner defines the Provisioner constructor.
func MakeProvisioner(
t *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple,
) (starlark.Value, error) {
return p, p.loadKeywordArgs(kwargs)
})
pm := t.Local(PluginManagerLocal).(*terraform.PluginManager)
var name starlark.String
switch len(args) {
case 1:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return nil, fmt.Errorf("expected string, got %s", args.Index(0).Type())
}
default:
return nil, fmt.Errorf("unexpected positional arguments count")
}
p, err := NewProvisioner(pm, name.GoString())
if err != nil {
return nil, err
}
return p, p.loadKeywordArgs(kwargs)
}
// Provisioner represents a Terraform provider of a specif type.
@ -95,7 +103,7 @@ func NewProvisioner(pm *terraform.PluginManager, typ string) (*Provisioner, erro
provisioner: provisioner,
meta: meta,
Resource: MakeResource(NameGenerator(), typ, ProvisionerKind, response.Provisioner, nil, nil),
Resource: NewResource(NameGenerator(), typ, ProvisionerKind, response.Provisioner, nil, nil),
}, nil
}

View File

@ -50,6 +50,71 @@ const (
BackendKind Kind = "backend"
)
// MakeResource defines the Resource constructor.
func MakeResource(
c *ResourceCollection,
t *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple,
) (starlark.Value, error) {
name, dict, err := unpackResourceArgs(args, kwargs)
if err != nil {
return nil, err
}
if (c.kind == ResourceKind || c.kind == DataSourceKind) && name == "" {
name = NameGenerator()
}
r := NewResource(name, c.typ, c.kind, c.block, c.provider, c.parent)
if dict != nil && dict.Len() != 0 {
if err := r.loadDict(dict); err != nil {
return nil, err
}
}
return r, r.loadKeywordArgs(kwargs)
}
func unpackResourceArgs(
args starlark.Tuple, kwargs []starlark.Tuple,
) (string, *starlark.Dict, error) {
var dict *starlark.Dict
var name starlark.String
if len(args) == 0 && len(kwargs) == 0 {
return "", nil, nil
}
switch len(args) {
case 0:
case 1:
switch v := args.Index(0).(type) {
case starlark.String:
return string(v), nil, nil
case *starlark.Dict:
return "", v, nil
default:
return "", nil, fmt.Errorf("resource: expected string or dict, got %s", args.Index(0).Type())
}
case 2:
var ok bool
name, ok = args.Index(0).(starlark.String)
if !ok {
return "", nil, fmt.Errorf("resource: expected string, got %s", args.Index(0).Type())
}
dict, ok = args.Index(1).(*starlark.Dict)
if !ok {
return "", nil, fmt.Errorf("resource: expected dict, got %s", args.Index(1).Type())
}
default:
//if c.kind != NestedKind {
return "", nil, fmt.Errorf("resource: unexpected positional arguments count")
// }
}
return string(name), dict, nil
}
// Resource represents a resource as a starlark.Value, it can be of four kinds,
// provider, resource, data source or a nested resource.
//
@ -132,9 +197,9 @@ var _ starlark.HasAttrs = &Resource{}
var _ starlark.HasSetField = &Resource{}
var _ starlark.Comparable = &Resource{}
// MakeResource returns a new resource of the given kind, type based on the
// NewResource returns a new resource of the given kind, type based on the
// given configschema.Block.
func MakeResource(name, typ string, k Kind, b *configschema.Block, provider *Provider, parent *Resource) *Resource {
func NewResource(name, typ string, k Kind, b *configschema.Block, provider *Provider, parent *Resource) *Resource {
return &Resource{
name: name,
typ: typ,
@ -146,8 +211,7 @@ func MakeResource(name, typ string, k Kind, b *configschema.Block, provider *Pro
}
}
// LoadDict loads a dict in the resource.
func (r *Resource) LoadDict(d *starlark.Dict) error {
func (r *Resource) loadDict(d *starlark.Dict) error {
for _, k := range d.Keys() {
name := k.(starlark.String)
value, _, _ := d.Get(k)
@ -259,7 +323,7 @@ func (r *Resource) attrBlock(name string, b *configschema.NestedBlock) (starlark
return r.values.Set(name, MustValue(NewResourceCollection(name, NestedKind, &b.Block, r.provider, r))).Starlark(), nil
}
return r.values.Set(name, MustValue(MakeResource("", name, NestedKind, &b.Block, r.provider, r))).Starlark(), nil
return r.values.Set(name, MustValue(NewResource("", name, NestedKind, &b.Block, r.provider, r))).Starlark(), nil
}
func (r *Resource) attrValue(name string, attr *configschema.Attribute) (starlark.Value, error) {
@ -356,7 +420,7 @@ func (r *Resource) setFieldFromNestedBlock(name string, b *configschema.NestedBl
return fmt.Errorf("expected dict, got %s", v.Type())
}
return resource.LoadDict(v.(*starlark.Dict))
return resource.loadDict(v.(*starlark.Dict))
case *ResourceCollection:
if v.Type() != "list" {
return fmt.Errorf("expected list, got %s", v.Type())

View File

@ -148,6 +148,13 @@ assert.eq(baz.uid, 42)
assert.eq(baz.system, True)
assert.eq(str(baz.id), '"${data.ignition_user.baz.id}"')
# constructor from dict with name and kwargs
baz = ignition.data.user("baz", {"uid": 42, "system": True}, uid=84)
assert.eq(baz.uid, 84)
assert.eq(baz.system, True)
assert.eq(str(baz.id), '"${data.ignition_user.baz.id}"')
assert.eq(bar, foo)
assert.eq(foo, ignition.data.user(foo.__dict__))
@ -162,7 +169,7 @@ def consNameDict(): ignition.data.user("foo", 1)
assert.fails(consNameDict, "resource: expected dict, got int")
def consKwargsNonName(): ignition.data.user(1, uid=42)
assert.fails(consKwargsNonName, "resource: expected string, got int")
assert.fails(consKwargsNonName, "resource: expected string or dict, got int")
# full coverage
user = ignition.data.user()