From e26994b39646e7c7a5cfe240e273b738cf53060d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ximo=20Cuadros?= Date: Thu, 27 Jun 2019 09:50:17 +0200 Subject: [PATCH] ResourceInstance: pointer handling and dict loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Máximo Cuadros --- examples/aws.star | 37 +++++++++++++ examples/ignition.star | 8 +-- plugins.go | 2 +- resource.go | 121 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 examples/aws.star diff --git a/examples/aws.star b/examples/aws.star new file mode 100644 index 0000000..6c04611 --- /dev/null +++ b/examples/aws.star @@ -0,0 +1,37 @@ +aws = provider("aws") +print(dir(aws)) + +ami = aws.data.ami("ubuntu") +ami.most_recent = True +ami.filter("name", values=["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]) +ami.filter("virtualization-type", values=["hvm"]) +ami.owners = ["099720109477"] +print(ami.__dict__) + + +web = aws.resource.instance("web", instance_type="t2.micro") + +#web.instance_type = "t2.micro" +#web.ami = ami.id + + +template = aws.resource.launch_template("example") +template.name_prefix = "example" +template.instance_type = "c5.larger" + +group = aws.resource.autoscaling_group("example") +group.availability_zones = ["us-east-1a"] +group.desired_capacity = 1 +group.max_size = 1 +group.min_size = 1 +#group.mixed_instances_policy.launch_template.launch_template_specification.launch_template_id = "foo" + +group.mixed_instances_policy = { + "launch_template": { + "launch_template_specification": { + "launch_template_id": "bar" + } + } +} + +print(group.__dict__) \ No newline at end of file diff --git a/examples/ignition.star b/examples/ignition.star index 055031b..3461ded 100644 --- a/examples/ignition.star +++ b/examples/ignition.star @@ -1,15 +1,15 @@ ignition = provider("ignition") -print("provider --->", dir(ignition)) -user = ignition.user("test") +user = ignition.data.user("test") user.name = "foo" user.uid = 42 user.groups = ["foo", "bar"] user.system = True +print(user.__dict__) -disk = ignition.disk("foo") +disk = ignition.data.disk("foo") disk.device = "/dev/sda" root = disk.partition("root") @@ -20,4 +20,4 @@ home = disk.partition("home") home.start = root.size + root.start home.size = 4 * 1024 * 1024 -print(home.start) \ No newline at end of file +print(disk.__dict__) \ No newline at end of file diff --git a/plugins.go b/plugins.go index 963532a..6ee8351 100644 --- a/plugins.go +++ b/plugins.go @@ -59,11 +59,11 @@ func (m *PluginManager) getRemote(provider, version string) (discovery.PluginMet func (m *PluginManager) getLocal(provider, version string) (discovery.PluginMeta, bool) { set := discovery.FindPlugins("provider", []string{m.Path}) + set = set.WithName(provider) if len(set) == 0 { return discovery.PluginMeta{}, false } - set = set.WithName(provider) if version != "" { // set = set.WithVersion(version) } diff --git a/resource.go b/resource.go index 9644727..cf09955 100644 --- a/resource.go +++ b/resource.go @@ -17,24 +17,60 @@ type ResourceInstance struct { values map[string]starlark.Value } -func NewResourceInstanceConstructor(typ string, b *configschema.Block) starlark.Value { +func NewResourceInstanceConstructor(typ string, b *configschema.Block, ptrs *PointerList) starlark.Value { return starlark.NewBuiltin( fmt.Sprintf("_%s", typ), func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { name := args.Index(0).(starlark.String) - return MakeResourceInstance(string(name), typ, b) + resource, err := MakeResourceInstance(string(name), typ, b, kwargs) + if err != nil { + return nil, err + } + + if ptrs != nil { + if err := ptrs.Append(resource); err != nil { + return nil, err + } + } + + return resource, nil }, ) } -func MakeResourceInstance(name, typ string, b *configschema.Block) (*ResourceInstance, error) { - return &ResourceInstance{ +func MakeResourceInstance(name, typ string, b *configschema.Block, kwargs []starlark.Tuple) (*ResourceInstance, error) { + r := &ResourceInstance{ name: name, typ: typ, block: b, values: make(map[string]starlark.Value), - }, nil + } + + return r, r.loadKeywordArgs(kwargs) +} + +func (r *ResourceInstance) loadDict(d *starlark.Dict) error { + for _, k := range d.Keys() { + name := k.(starlark.String) + value, _, _ := d.Get(k) + if err := r.SetField(string(name), value); err != nil { + return err + } + } + + return nil +} + +func (r *ResourceInstance) loadKeywordArgs(kwargs []starlark.Tuple) error { + for _, kwarg := range kwargs { + name := kwarg.Index(0).(starlark.String) + if err := r.SetField(string(name), kwarg.Index(1)); err != nil { + return err + } + } + + return nil } // String honors the starlark.Value interface. @@ -62,8 +98,22 @@ func (r *ResourceInstance) Hash() (uint32, error) { // Attr honors the starlark.HasAttrs interface. func (r *ResourceInstance) Attr(name string) (starlark.Value, error) { + if name == "__dict__" { + return r.toDict(), nil + } + if b, ok := r.block.BlockTypes[name]; ok { - return NewResourceInstanceConstructor(name, &b.Block), nil + if b.MaxItems != 1 { + if _, ok := r.values[name]; !ok { + r.values[name] = NewPointerList() + } + + return NewResourceInstanceConstructor(name, &b.Block, r.values[name].(*PointerList)), nil + } + + if _, ok := r.values[name]; !ok { + r.values[name], _ = MakeResourceInstance("", name, &b.Block, nil) + } } if v, ok := r.values[name]; ok { @@ -79,7 +129,7 @@ func (r *ResourceInstance) getNestedBlockAttr(name string, b *configschema.Neste } var err error - r.values[name], err = MakeResourceInstance("", name, &b.Block) + r.values[name], err = MakeResourceInstance("", name, &b.Block, nil) return r.values[name], err } @@ -95,6 +145,7 @@ func (r *ResourceInstance) AttrNames() []string { for k := range r.block.BlockTypes { names[i] = k + i++ } return names @@ -102,6 +153,10 @@ func (r *ResourceInstance) AttrNames() []string { // SetField honors the starlark.HasSetField interface. func (r *ResourceInstance) SetField(name string, v starlark.Value) error { + if b, ok := r.block.BlockTypes[name]; ok { + return r.setFieldFromNestedBlock(name, b, v) + } + attr, ok := r.block.Attributes[name] if !ok { errmsg := fmt.Sprintf("%s has no .%s field or method", r.typ, name) @@ -116,6 +171,52 @@ func (r *ResourceInstance) SetField(name string, v starlark.Value) error { return nil } +func (r *ResourceInstance) setFieldFromNestedBlock(name string, b *configschema.NestedBlock, v starlark.Value) error { + switch v.Type() { + case "dict": + resource, _ := r.Attr(name) + return resource.(*ResourceInstance).loadDict(v.(*starlark.Dict)) + } + + return fmt.Errorf("expected dict or list, got %s", v.Type()) +} + +func (r *ResourceInstance) toDict() *starlark.Dict { + d := starlark.NewDict(len(r.values)) + for k, v := range r.values { + if r, ok := v.(*ResourceInstance); ok { + d.SetKey(starlark.String(k), r.toDict()) + continue + } + + if r, ok := v.(*PointerList); ok { + d.SetKey(starlark.String(k), r.toDict()) + continue + } + + d.SetKey(starlark.String(k), v) + } + + return d +} + +type PointerList struct { + *starlark.List +} + +func NewPointerList() *PointerList { + return &PointerList{List: starlark.NewList(nil)} +} + +func (l *PointerList) toDict() *starlark.List { + values := make([]starlark.Value, l.Len()) + for i := 0; i < l.Len(); i++ { + values[i] = l.Index(i).(*ResourceInstance).toDict() + } + + return starlark.NewList(values) +} + /* NoneType # the type of None bool # True or False @@ -169,7 +270,7 @@ func ValidateType(v starlark.Value, expected cty.Type) error { return nil } case *starlark.List: - if expected.IsListType() { + if expected.IsListType() || expected.IsSetType() { return ValidateListType(v.(*starlark.List), expected.ElementType()) } } @@ -191,5 +292,9 @@ func ToStarlarkType(t cty.Type) string { return "list" } + if t.IsSetType() { + return "set" + } + return "(unknown)" }