From d25c7b32a2ee0f783d2bd1ccf5e57e74dc92dacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ximo=20Cuadros?= Date: Tue, 23 Jul 2019 21:12:55 +0200 Subject: [PATCH] starlark/types: Resource.depends_on support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Máximo Cuadros --- starlark/types/hcl.go | 57 +++++++++++++++++++++++---- starlark/types/resource.go | 25 +++++++++++- starlark/types/testdata/hcl.star | 6 ++- starlark/types/testdata/resource.star | 17 +++++++- 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/starlark/types/hcl.go b/starlark/types/hcl.go index 1cc6a8a..a64970d 100644 --- a/starlark/types/hcl.go +++ b/starlark/types/hcl.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hashicorp/hcl2/hcl" + "github.com/hashicorp/hcl2/hcl/hclsyntax" "github.com/hashicorp/hcl2/hclwrite" "github.com/zclconf/go-cty/cty" "go.starlark.net/starlark" @@ -69,12 +70,14 @@ func (r *Resource) ToHCL(b *hclwrite.Body) { body := block.Body() if r.parent.kind == ProviderKind { - body.SetAttributeTraversal("provider", hcl.Traversal{hcl.TraverseRoot{ - Name: fmt.Sprintf("%s.%s", r.parent.typ, r.parent.Name()), - }}) + body.SetAttributeTraversal("provider", hcl.Traversal{ + hcl.TraverseRoot{Name: r.parent.typ}, + hcl.TraverseAttr{Name: r.parent.Name()}, + }) } r.doToHCLAttributes(body) + r.doToHCLDependencies(body) } func (r *Resource) doToHCLAttributes(body *hclwrite.Body) { @@ -84,11 +87,10 @@ func (r *Resource) doToHCLAttributes(body *hclwrite.Body) { continue } - // TODO(mcuadros): I don't know how to do this properly, meanwhile, this works. if c, ok := v.v.(*Computed); ok { - body.SetAttributeTraversal(k, hcl.Traversal{hcl.TraverseRoot{ - Name: c.String(), - }}) + body.SetAttributeTraversal(k, hcl.Traversal{ + hcl.TraverseRoot{Name: c.String()}, + }) continue } @@ -107,3 +109,44 @@ func (r *Resource) doToHCLAttributes(body *hclwrite.Body) { } } } + +func (r *Resource) doToHCLDependencies(body *hclwrite.Body) { + if len(r.dependenies) == 0 { + return + } + + toks := []*hclwrite.Token{} + toks = append(toks, &hclwrite.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte("depends_on"), + }) + + toks = append(toks, &hclwrite.Token{ + Type: hclsyntax.TokenEqual, Bytes: []byte{'='}, + }, &hclwrite.Token{ + Type: hclsyntax.TokenOBrack, Bytes: []byte{'['}, + }) + + l := len(r.dependenies) + for i, dep := range r.dependenies { + name := fmt.Sprintf("%s.%s", dep.typ, dep.Name()) + toks = append(toks, &hclwrite.Token{ + Type: hclsyntax.TokenIdent, Bytes: []byte(name), + }) + + if i+1 == l { + break + } + + toks = append(toks, &hclwrite.Token{ + Type: hclsyntax.TokenComma, Bytes: []byte{','}, + }) + } + + toks = append(toks, &hclwrite.Token{ + Type: hclsyntax.TokenCBrack, Bytes: []byte{']'}, + }) + + body.AppendUnstructuredTokens(toks) + body.AppendNewline() +} diff --git a/starlark/types/resource.go b/starlark/types/resource.go index 8172cd9..cba9e2a 100644 --- a/starlark/types/resource.go +++ b/starlark/types/resource.go @@ -37,8 +37,10 @@ type Resource struct { typ string kind Kind block *configschema.Block - parent *Resource values map[string]*Value + + parent *Resource + dependenies []*Resource } // MakeResource returns a new resource of the given kind, type based on the @@ -121,6 +123,8 @@ func (r *Resource) Hash() (uint32, error) { // Attr honors the starlark.HasAttrs interface. func (r *Resource) Attr(name string) (starlark.Value, error) { switch name { + case "depends_on": + return starlark.NewBuiltin("depends_on", r.dependsOn), nil case "__dict__": return r.toDict(), nil } @@ -233,6 +237,25 @@ func (r *Resource) toDict() *starlark.Dict { return d } +func (r *Resource) dependsOn(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, _ []starlark.Tuple) (starlark.Value, error) { + resources := make([]*Resource, len(args)) + for i, arg := range args { + resource, ok := arg.(*Resource) + if !ok || resource.kind != DataSourceKind && resource.kind != ResourceKind { + return nil, fmt.Errorf("expected Resource<[data|resource].*>, got %s", arg.Type()) + } + + if r == resource { + return nil, fmt.Errorf("can't depend on itself") + } + + resources[i] = resource + } + + r.dependenies = append(r.dependenies, resources...) + return starlark.None, nil +} + // CompareSameType honors starlark.Comprable interface. func (x *Resource) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) { y := y_.(*Resource) diff --git a/starlark/types/testdata/hcl.star b/starlark/types/testdata/hcl.star index e015c83..ae37edf 100644 --- a/starlark/types/testdata/hcl.star +++ b/starlark/types/testdata/hcl.star @@ -9,10 +9,12 @@ ubuntu.filter(name = "name", values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.0 ubuntu.filter(name = "virtualization-type", values = ["hvm"]) ubuntu.owners = ["099720109477"] +fedora = aws.data.ami("fedora") web = aws.resource.instance(instance_type = "t2.micro") +web.depends_on(ubuntu, fedora) -#web.instance_type = "t2.micro" -#web.ami = ami.id +web.instance_type = "t2.micro" +web.ami = ubuntu.id template = aws.resource.launch_template() template.name_prefix = "example" diff --git a/starlark/types/testdata/resource.star b/starlark/types/testdata/resource.star index f42fa86..6c76353 100644 --- a/starlark/types/testdata/resource.star +++ b/starlark/types/testdata/resource.star @@ -125,4 +125,19 @@ assert.eq(disk.__dict__, { "size": 4194304, "label": "home" }] -}) \ No newline at end of file +}) + + +# depends_on +userA = p.data.user() +userB = p.data.user() +userA.depends_on(userB) + +def dependsOnNonResource(): userA.depends_on(42) +assert.fails(dependsOnNonResource, "expected Resource<\\[data|resource\\].\\*>, got int") + +def dependsOnNestedResource(): userA.depends_on(disk.partition()) +assert.fails(dependsOnNestedResource, "expected Resource<\\[data|resource\\].\\*>, got Resource") + +def dependsOnItself(): userA.depends_on(userA) +assert.fails(dependsOnItself, "can't depend on itself") \ No newline at end of file