mirror of
https://github.com/mcuadros/ascode
synced 2024-11-22 17:02:03 +01:00
starlark: provider aliasing support
Signed-off-by: Máximo Cuadros <mcuadros@gmail.com>
This commit is contained in:
parent
814a0bd668
commit
014a32752e
@ -16,3 +16,6 @@ root.size = 4 * 1024 * 1024
|
||||
home = disk.partition()
|
||||
home.start = root.size + root.start
|
||||
home.size = 4 * 1024 * 1024
|
||||
|
||||
|
||||
config = ignition.data.config(disks=[disk.id], users=[user.id])
|
1
main.go
1
main.go
@ -35,6 +35,7 @@ func main() {
|
||||
|
||||
out, err := starlark.ExecFile(thread, os.Args[1], nil, predeclared)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
if err, ok := err.(*starlark.EvalError); ok {
|
||||
log.Fatal(err.Backtrace())
|
||||
}
|
||||
|
@ -9,13 +9,13 @@ import (
|
||||
|
||||
type ResourceCollection struct {
|
||||
typ string
|
||||
kind ResourceKind
|
||||
kind Kind
|
||||
block *configschema.Block
|
||||
parent *Resource
|
||||
*starlark.List
|
||||
}
|
||||
|
||||
func NewResourceCollection(typ string, k ResourceKind, block *configschema.Block, parent *Resource) *ResourceCollection {
|
||||
func NewResourceCollection(typ string, k Kind, block *configschema.Block, parent *Resource) *ResourceCollection {
|
||||
return &ResourceCollection{
|
||||
typ: typ,
|
||||
kind: k,
|
||||
@ -64,7 +64,7 @@ func (c *ResourceCollection) CallInternal(thread *starlark.Thread, args starlark
|
||||
return nil, fmt.Errorf("resource: expected dict, go %s", args.Index(0).Type())
|
||||
}
|
||||
default:
|
||||
if c.kind != NestedK {
|
||||
if c.kind != NestedKind {
|
||||
return nil, fmt.Errorf("resource: unexpected positional arguments count")
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,8 @@ func NewComputed(r *Resource, t cty.Type, name string) *Computed {
|
||||
|
||||
child := r
|
||||
for {
|
||||
if child.parent == nil {
|
||||
name, err := child.Name()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
path = fmt.Sprintf("%s.%s.%s", child.kind, child.typ, name)
|
||||
if child.parent.kind == ProviderKind {
|
||||
path = fmt.Sprintf("%s.%s.%s", child.kind, child.typ, child.Name())
|
||||
break
|
||||
}
|
||||
|
||||
@ -51,7 +46,7 @@ func NewComputedWithPath(r *Resource, t cty.Type, name, path string) *Computed {
|
||||
t: t,
|
||||
name: name,
|
||||
path: path,
|
||||
sString: starlark.String(fmt.Sprintf("${%s}", path)),
|
||||
sString: starlark.String(fmt.Sprintf("$${%s}", path)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package types
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestComputed(t *testing.T) {
|
||||
test(t, "testdata/computed.star")
|
||||
|
@ -33,7 +33,9 @@ func BuiltinHCL() starlark.Value {
|
||||
|
||||
func (s *Provider) ToHCL(b *hclwrite.Body) {
|
||||
block := b.AppendNewBlock("provider", []string{s.name})
|
||||
block.Body().SetAttributeValue("alias", cty.StringVal(s.Name()))
|
||||
block.Body().SetAttributeValue("version", cty.StringVal(string(s.meta.Version)))
|
||||
s.Resource.doToHCLAttributes(block.Body())
|
||||
|
||||
s.dataSources.ToHCL(b)
|
||||
s.resources.ToHCL(b)
|
||||
@ -58,18 +60,24 @@ func (r *Resource) ToHCL(b *hclwrite.Body) {
|
||||
}
|
||||
|
||||
var block *hclwrite.Block
|
||||
if r.kind != NestedK {
|
||||
name, err := r.Name()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
block = b.AppendNewBlock(string(r.kind), []string{r.typ, name})
|
||||
if r.kind != NestedKind {
|
||||
block = b.AppendNewBlock(string(r.kind), []string{r.typ, r.Name()})
|
||||
} else {
|
||||
block = b.AppendNewBlock(r.typ, nil)
|
||||
}
|
||||
|
||||
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()),
|
||||
}})
|
||||
}
|
||||
|
||||
r.doToHCLAttributes(body)
|
||||
}
|
||||
|
||||
func (r *Resource) doToHCLAttributes(body *hclwrite.Body) {
|
||||
for k := range r.block.Attributes {
|
||||
v, ok := r.values[k]
|
||||
if !ok {
|
||||
@ -95,7 +103,7 @@ func (r *Resource) ToHCL(b *hclwrite.Body) {
|
||||
}
|
||||
|
||||
if collection, ok := v.Value().(HCLCompatible); ok {
|
||||
collection.ToHCL(block.Body())
|
||||
collection.ToHCL(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ type Provider struct {
|
||||
dataSources *MapSchema
|
||||
resources *MapSchema
|
||||
|
||||
r *Resource
|
||||
*Resource
|
||||
}
|
||||
|
||||
func MakeProvider(pm *terraform.PluginManager, name, version string) (*Provider, error) {
|
||||
@ -39,14 +39,18 @@ func MakeProvider(pm *terraform.PluginManager, name, version string) (*Provider,
|
||||
response := provider.GetSchema()
|
||||
|
||||
defer cli.Kill()
|
||||
return &Provider{
|
||||
name: name,
|
||||
provider: provider,
|
||||
meta: meta,
|
||||
r: MakeResource("", ResourceK, response.Provider.Block, nil),
|
||||
dataSources: NewMapSchema(name, DataResourceK, response.DataSources),
|
||||
resources: NewMapSchema(name, ResourceK, response.ResourceTypes),
|
||||
}, nil
|
||||
p := &Provider{
|
||||
name: name,
|
||||
provider: provider,
|
||||
meta: meta,
|
||||
|
||||
Resource: MakeResource(name, ProviderKind, response.Provider.Block, nil),
|
||||
}
|
||||
|
||||
p.dataSources = NewMapSchema(p, name, DataSourceKind, response.DataSources)
|
||||
p.resources = NewMapSchema(p, name, ResourceKind, response.ResourceTypes)
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *Provider) String() string {
|
||||
@ -57,9 +61,6 @@ func (p *Provider) Type() string {
|
||||
return "provider"
|
||||
}
|
||||
|
||||
func (p *Provider) Freeze() {}
|
||||
func (p *Provider) Truth() starlark.Bool { return true }
|
||||
func (p *Provider) Hash() (uint32, error) { return 1, nil }
|
||||
func (p *Provider) Attr(name string) (starlark.Value, error) {
|
||||
switch name {
|
||||
case "version":
|
||||
@ -70,22 +71,25 @@ func (p *Provider) Attr(name string) (starlark.Value, error) {
|
||||
return p.resources, nil
|
||||
}
|
||||
|
||||
return starlark.None, nil
|
||||
return p.Resource.Attr(name)
|
||||
}
|
||||
|
||||
func (p *Provider) AttrNames() []string {
|
||||
return []string{"data", "resource"}
|
||||
return append(p.Resource.AttrNames(), "data", "resource", "version")
|
||||
}
|
||||
|
||||
type MapSchema struct {
|
||||
p *Provider
|
||||
|
||||
prefix string
|
||||
kind ResourceKind
|
||||
kind Kind
|
||||
schemas map[string]providers.Schema
|
||||
collections map[string]*ResourceCollection
|
||||
}
|
||||
|
||||
func NewMapSchema(prefix string, k ResourceKind, schemas map[string]providers.Schema) *MapSchema {
|
||||
func NewMapSchema(p *Provider, prefix string, k Kind, schemas map[string]providers.Schema) *MapSchema {
|
||||
return &MapSchema{
|
||||
p: p,
|
||||
prefix: prefix,
|
||||
kind: k,
|
||||
schemas: schemas,
|
||||
@ -114,7 +118,7 @@ func (m *MapSchema) Attr(name string) (starlark.Value, error) {
|
||||
}
|
||||
|
||||
if schema, ok := m.schemas[name]; ok {
|
||||
m.collections[name] = NewResourceCollection(name, m.kind, schema.Block, nil)
|
||||
m.collections[name] = NewResourceCollection(name, m.kind, schema.Block, m.p.Resource)
|
||||
return m.collections[name], nil
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
var id int
|
||||
NameGenerator = func() string {
|
||||
id++
|
||||
return fmt.Sprintf("id_%d", id)
|
||||
}
|
||||
|
||||
// The tests make extensive use of these not-yet-standard features.
|
||||
resolve.AllowLambda = true
|
||||
resolve.AllowNestedDef = true
|
||||
|
@ -2,29 +2,48 @@ package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/oklog/ulid"
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/syntax"
|
||||
)
|
||||
|
||||
type ResourceKind string
|
||||
// NameGenerator function used to generate Resource names, by default is based
|
||||
// on a ULID generator.
|
||||
var NameGenerator = func() string {
|
||||
t := time.Now()
|
||||
entropy := ulid.Monotonic(rand.New(rand.NewSource(t.UnixNano())), 0)
|
||||
|
||||
return fmt.Sprintf("id_%s", ulid.MustNew(ulid.Timestamp(t), entropy))
|
||||
}
|
||||
|
||||
// Kind describes what kind of resource is represented by a Resource isntance.
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
ResourceK ResourceKind = "resource"
|
||||
DataResourceK ResourceKind = "data"
|
||||
NestedK ResourceKind = "nested"
|
||||
ProviderKind Kind = "provider"
|
||||
ResourceKind Kind = "resource"
|
||||
DataSourceKind Kind = "data"
|
||||
NestedKind Kind = "nested"
|
||||
)
|
||||
|
||||
// Resource represents a resource as a starlark.Value, it can be of four kinds,
|
||||
// provider, resource, data source or a nested resource.
|
||||
type Resource struct {
|
||||
name string
|
||||
typ string
|
||||
kind ResourceKind
|
||||
kind Kind
|
||||
block *configschema.Block
|
||||
parent *Resource
|
||||
values map[string]*Value
|
||||
}
|
||||
|
||||
func MakeResource(typ string, k ResourceKind, b *configschema.Block, parent *Resource) *Resource {
|
||||
// MakeResource returns a new resource of the given kind, type based on the
|
||||
// given configschema.Block.
|
||||
func MakeResource(typ string, k Kind, b *configschema.Block, parent *Resource) *Resource {
|
||||
return &Resource{
|
||||
typ: typ,
|
||||
kind: k,
|
||||
@ -76,17 +95,12 @@ func (r *Resource) Truth() starlark.Bool {
|
||||
func (r *Resource) Freeze() {}
|
||||
|
||||
// Name returns the resource name based on the hash.
|
||||
func (r *Resource) Name() (string, error) {
|
||||
if r.kind == NestedK {
|
||||
return "", fmt.Errorf("name is not supported on nested resources")
|
||||
func (r *Resource) Name() string {
|
||||
if r.name == "" {
|
||||
r.name = NameGenerator()
|
||||
}
|
||||
|
||||
hash, err := r.Hash()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("id_%d", hash), nil
|
||||
return r.name
|
||||
}
|
||||
|
||||
// Hash honors the starlark.Value interface.
|
||||
@ -128,11 +142,11 @@ func (r *Resource) Attr(name string) (starlark.Value, error) {
|
||||
func (r *Resource) attrBlock(name string, b *configschema.NestedBlock) (starlark.Value, error) {
|
||||
if b.MaxItems != 1 {
|
||||
if _, ok := r.values[name]; !ok {
|
||||
r.values[name] = MustValue(NewResourceCollection(name, NestedK, &b.Block, r))
|
||||
r.values[name] = MustValue(NewResourceCollection(name, NestedKind, &b.Block, r))
|
||||
}
|
||||
} else {
|
||||
if _, ok := r.values[name]; !ok {
|
||||
r.values[name] = MustValue(MakeResource(name, NestedK, &b.Block, r))
|
||||
r.values[name] = MustValue(MakeResource(name, NestedKind, &b.Block, r))
|
||||
}
|
||||
}
|
||||
|
||||
|
23
starlark/types/testdata/computed.star
vendored
23
starlark/types/testdata/computed.star
vendored
@ -6,27 +6,20 @@ aws = provider("aws", "2.13.0")
|
||||
web = aws.resource.instance()
|
||||
web.ami = aws.data.ami().id
|
||||
assert.eq(type(web.ami), "computed")
|
||||
assert.eq(str(web.ami), '"${data.aws_ami.id_8731.id}"')
|
||||
assert.eq(str(web.ami), '"$${data.aws_ami.id_1.id}"')
|
||||
|
||||
# compute of set
|
||||
table = aws.data.dynamodb_table()
|
||||
assert.eq(str(table.ttl), '"${data.aws_dynamodb_table.id_8731.ttl}"')
|
||||
assert.eq(str(table.ttl[0]), '"${data.aws_dynamodb_table.id_8731.ttl.0}"')
|
||||
assert.eq(str(table.ttl[0].attribute_name), '"${data.aws_dynamodb_table.id_8731.ttl.0.attribute_name}"')
|
||||
assert.eq(str(table.ttl), '"$${data.aws_dynamodb_table.id_2.ttl}"')
|
||||
assert.eq(str(table.ttl[0]), '"$${data.aws_dynamodb_table.id_2.ttl.0}"')
|
||||
assert.eq(str(table.ttl[0].attribute_name), '"$${data.aws_dynamodb_table.id_2.ttl.0.attribute_name}"')
|
||||
|
||||
# compute of list
|
||||
instance = aws.data.instance()
|
||||
assert.eq(str(instance.credit_specification), '"${data.aws_instance.id_8731.credit_specification}"')
|
||||
assert.eq(str(instance.credit_specification[0]), '"${data.aws_instance.id_8731.credit_specification.0}"')
|
||||
assert.eq(str(instance.credit_specification[0].cpu_credits), '"${data.aws_instance.id_8731.credit_specification.0.cpu_credits}"')
|
||||
assert.eq(str(instance.credit_specification), '"$${data.aws_instance.id_3.credit_specification}"')
|
||||
assert.eq(str(instance.credit_specification[0]), '"$${data.aws_instance.id_3.credit_specification.0}"')
|
||||
assert.eq(str(instance.credit_specification[0].cpu_credits), '"$${data.aws_instance.id_3.credit_specification.0.cpu_credits}"')
|
||||
|
||||
# compute of map
|
||||
# {resource.aws_instance.id_8731.root_block_device.volume_size}
|
||||
computed = str(aws.resource.instance().root_block_device.volume_size)
|
||||
parts = computed[3:len(computed)-2].split(".")
|
||||
|
||||
assert.eq(len(parts), 5)
|
||||
assert.eq(parts[0], "resource")
|
||||
assert.eq(parts[1], "aws_instance")
|
||||
assert.eq(parts[3], "root_block_device")
|
||||
assert.eq(parts[4], "volume_size")
|
||||
assert.eq(computed, '"$${resource.aws_instance.id_4.root_block_device.volume_size}"')
|
1
starlark/types/testdata/hcl.star
vendored
1
starlark/types/testdata/hcl.star
vendored
@ -1,6 +1,7 @@
|
||||
load("assert.star", "assert")
|
||||
|
||||
aws = provider("aws", "2.13.0")
|
||||
aws.region = "us-west-2"
|
||||
|
||||
ubuntu = aws.data.ami()
|
||||
ubuntu.most_recent = True
|
||||
|
3
starlark/types/testdata/provider.star
vendored
3
starlark/types/testdata/provider.star
vendored
@ -13,3 +13,6 @@ assert.eq(type(p.resource.instance), "collection")
|
||||
p.resource.instance()
|
||||
p.resource.instance()
|
||||
assert.eq(len(p.resource.instance), 2)
|
||||
|
||||
p.region = "us-west-2"
|
||||
assert.eq(p.region, "us-west-2")
|
2
starlark/types/testdata/resource.star
vendored
2
starlark/types/testdata/resource.star
vendored
@ -15,7 +15,7 @@ assert.fails(lambda: qux.foo, "data has no .foo field or method")
|
||||
|
||||
# attr id
|
||||
assert.eq(type(qux.id), "computed")
|
||||
assert.eq(str(qux.id), '"${data.ignition_user.id_3399129522.id}"')
|
||||
assert.eq(str(qux.id), '"$${data.ignition_user.id_5.id}"')
|
||||
|
||||
# attr output assignation
|
||||
aws = provider("aws", "2.13.0")
|
||||
|
@ -67,6 +67,8 @@ func (v *Value) Cty() cty.Value {
|
||||
}
|
||||
|
||||
return cty.ListVal(values)
|
||||
case "computed":
|
||||
return cty.StringVal(v.v.(*Computed).GoString())
|
||||
default:
|
||||
return cty.StringVal(fmt.Sprintf("unhandled: %s", v.t.typ))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user