mirror of
https://github.com/mcuadros/ascode
synced 2024-11-26 06:01:08 +01:00
starlark/types: Computed renamed to Attribute, added documentation
This commit is contained in:
parent
be125c9f03
commit
c3cd97a5fc
@ -63,7 +63,7 @@ func NewRuntime(pm *terraform.PluginManager) *Runtime {
|
|||||||
"provisioner": types.BuiltinProvisioner(),
|
"provisioner": types.BuiltinProvisioner(),
|
||||||
"backend": types.BuiltinBackend(),
|
"backend": types.BuiltinBackend(),
|
||||||
"hcl": types.BuiltinHCL(),
|
"hcl": types.BuiltinHCL(),
|
||||||
"fn": types.BuiltinFunctionComputed(),
|
"fn": types.BuiltinFunctionAttribute(),
|
||||||
"evaluate": types.BuiltinEvaluate(),
|
"evaluate": types.BuiltinEvaluate(),
|
||||||
"struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
|
"struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
|
||||||
"module": starlark.NewBuiltin("module", starlarkstruct.MakeModule),
|
"module": starlark.NewBuiltin("module", starlarkstruct.MakeModule),
|
||||||
|
205
starlark/types/attribute.go
Normal file
205
starlark/types/attribute.go
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
"go.starlark.net/starlark"
|
||||||
|
)
|
||||||
|
|
||||||
|
// sTring alias required to avoid name collision with the method String.
|
||||||
|
type sString = starlark.String
|
||||||
|
|
||||||
|
// Attribute is a reference to an argument of a Resource. Used mainly
|
||||||
|
// for Computed arguments of Resources.
|
||||||
|
//
|
||||||
|
// outline: types
|
||||||
|
// types:
|
||||||
|
// Attribute
|
||||||
|
// Attribute is a reference to an argument of a Resource. Used mainly
|
||||||
|
// for Computed arguments of Resources.
|
||||||
|
//
|
||||||
|
// Attribute behaves as type of the argument represented, this means
|
||||||
|
// that their can be assigned to other resource arguments of the same
|
||||||
|
// type. And, if the type is a list are indexables.
|
||||||
|
//
|
||||||
|
// examples:
|
||||||
|
// attribute.star
|
||||||
|
//
|
||||||
|
// fields:
|
||||||
|
// __resource__ Resource
|
||||||
|
// Resource of the attribute.
|
||||||
|
// __type__ string
|
||||||
|
// Type of the attribute. Eg.: `string`
|
||||||
|
type Attribute struct {
|
||||||
|
r *Resource
|
||||||
|
t cty.Type
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
|
||||||
|
sString
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ starlark.Value = &Attribute{}
|
||||||
|
var _ starlark.HasAttrs = &Attribute{}
|
||||||
|
var _ starlark.Indexable = &Attribute{}
|
||||||
|
var _ starlark.Comparable = &Attribute{}
|
||||||
|
|
||||||
|
// NewAttribute returns a new Attribute for a given value or block of a Resource.
|
||||||
|
func NewAttribute(r *Resource, t cty.Type, name string) *Attribute {
|
||||||
|
var parts []string
|
||||||
|
var path string
|
||||||
|
|
||||||
|
child := r
|
||||||
|
|
||||||
|
for {
|
||||||
|
if child.parent.kind == ProviderKind {
|
||||||
|
if child.kind == ResourceKind {
|
||||||
|
path = fmt.Sprintf("%s.%s", child.typ, child.Name())
|
||||||
|
} else {
|
||||||
|
path = fmt.Sprintf("%s.%s.%s", child.kind, child.typ, child.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = append(parts, child.typ)
|
||||||
|
child = child.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(parts) - 1; i >= 0; i-- {
|
||||||
|
path += "." + parts[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// handling of MaxItems equals 1
|
||||||
|
block, ok := r.parent.block.BlockTypes[r.typ]
|
||||||
|
if ok && block.MaxItems == 1 {
|
||||||
|
name = "0." + name
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewAttributeWithPath(r, t, name, path+"."+name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAttributeWithPath(r *Resource, t cty.Type, name, path string) *Attribute {
|
||||||
|
return &Attribute{
|
||||||
|
r: r,
|
||||||
|
t: t,
|
||||||
|
name: name,
|
||||||
|
path: path,
|
||||||
|
sString: starlark.String(fmt.Sprintf("${%s}", path)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type honors the starlark.Value interface.
|
||||||
|
func (c *Attribute) Type() string {
|
||||||
|
return fmt.Sprintf("Attribute<%s>", MustTypeFromCty(c.t).Starlark())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Attribute) InnerType() *Type {
|
||||||
|
t, _ := NewTypeFromCty(c.t)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attr honors the starlark.HasAttrs interface.
|
||||||
|
func (c *Attribute) Attr(name string) (starlark.Value, error) {
|
||||||
|
switch name {
|
||||||
|
case "__resource__":
|
||||||
|
return c.r, nil
|
||||||
|
case "__type__":
|
||||||
|
return starlark.String(MustTypeFromCty(c.t).Starlark()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.t.IsObjectType() {
|
||||||
|
return nil, fmt.Errorf("%s it's not a object", c.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.t.HasAttribute(name) {
|
||||||
|
errmsg := fmt.Sprintf("%s has no .%s field", c.Type(), name)
|
||||||
|
return nil, starlark.NoSuchAttrError(errmsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s.%s", c.path, name)
|
||||||
|
return NewAttributeWithPath(c.r, c.t.AttributeType(name), name, path), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttrNames honors the starlark.HasAttrs interface.
|
||||||
|
func (c *Attribute) AttrNames() []string {
|
||||||
|
return []string{"__resource__", "__type__"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Attribute) doNested(name, path string, t cty.Type, index int) *Attribute {
|
||||||
|
return &Attribute{
|
||||||
|
r: c.r,
|
||||||
|
t: t,
|
||||||
|
name: c.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index honors the starlark.Indexable interface.
|
||||||
|
func (c *Attribute) Index(i int) starlark.Value {
|
||||||
|
path := fmt.Sprintf("%s.%d", c.path, i)
|
||||||
|
|
||||||
|
if c.t.IsSetType() {
|
||||||
|
return NewAttributeWithPath(c.r, *c.t.SetElementType(), c.name, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.t.IsListType() {
|
||||||
|
return NewAttributeWithPath(c.r, *c.t.ListElementType(), c.name, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return starlark.None
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len honors the starlark.Indexable interface.
|
||||||
|
func (c *Attribute) Len() int {
|
||||||
|
if !c.t.IsSetType() && !c.t.IsListType() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuiltinFunctionAttribute returns a built-in function that wraps Attributes
|
||||||
|
// in HCL functions.
|
||||||
|
//
|
||||||
|
// outline: types
|
||||||
|
// functions:
|
||||||
|
// fn(name, target) Attribute
|
||||||
|
// Fn wraps an Attribute in a HCL function. Since the Attributes value
|
||||||
|
// are only available in the `apply` phase of Terraform, the only method
|
||||||
|
// to manipulate this values is using the Terraform
|
||||||
|
// [HCL functions](https://www.terraform.io/docs/configuration/functions.html).
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// params:
|
||||||
|
// name string
|
||||||
|
// Name of the HCL function to be applied. Eg.: `base64encode`
|
||||||
|
// target Attribute
|
||||||
|
// Target Attribute of the HCL function.
|
||||||
|
//
|
||||||
|
func BuiltinFunctionAttribute() starlark.Value {
|
||||||
|
// TODO(mcuadros): implement multiple arguments support.
|
||||||
|
return starlark.NewBuiltin("fn", func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
|
var function starlark.String
|
||||||
|
var computed *Attribute
|
||||||
|
switch len(args) {
|
||||||
|
case 2:
|
||||||
|
var ok bool
|
||||||
|
function, ok = args.Index(0).(starlark.String)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("expected string, got %s", args.Index(0).Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
computed, ok = args.Index(1).(*Attribute)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("expected Attribute, got %s", args.Index(1).Type())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected positional arguments count")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s(%s)", function.GoString(), computed.path)
|
||||||
|
return NewAttributeWithPath(computed.r, computed.t, computed.name, path), nil
|
||||||
|
})
|
||||||
|
}
|
9
starlark/types/attribute_test.go
Normal file
9
starlark/types/attribute_test.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAttribute(t *testing.T) {
|
||||||
|
doTest(t, "testdata/attribute.star")
|
||||||
|
}
|
@ -68,6 +68,7 @@ var _ starlark.HasAttrs = &ResourceCollection{}
|
|||||||
var _ starlark.Callable = &ResourceCollection{}
|
var _ starlark.Callable = &ResourceCollection{}
|
||||||
var _ starlark.Comparable = &ResourceCollection{}
|
var _ starlark.Comparable = &ResourceCollection{}
|
||||||
|
|
||||||
|
// NewResourceCollection returns a new ResourceCollection for the given values.
|
||||||
func NewResourceCollection(
|
func NewResourceCollection(
|
||||||
typ string, k Kind, block *configschema.Block, provider *Provider, parent *Resource,
|
typ string, k Kind, block *configschema.Block, provider *Provider, parent *Resource,
|
||||||
) *ResourceCollection {
|
) *ResourceCollection {
|
||||||
@ -271,6 +272,7 @@ var _ starlark.HasAttrs = &ProviderCollection{}
|
|||||||
var _ starlark.Callable = &ProviderCollection{}
|
var _ starlark.Callable = &ProviderCollection{}
|
||||||
var _ starlark.Comparable = &ProviderCollection{}
|
var _ starlark.Comparable = &ProviderCollection{}
|
||||||
|
|
||||||
|
// NewProviderCollection returns a new ProviderCollection.
|
||||||
func NewProviderCollection(pm *terraform.PluginManager) *ProviderCollection {
|
func NewProviderCollection(pm *terraform.PluginManager) *ProviderCollection {
|
||||||
return &ProviderCollection{
|
return &ProviderCollection{
|
||||||
pm: pm,
|
pm: pm,
|
||||||
|
@ -1,162 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
"go.starlark.net/starlark"
|
|
||||||
)
|
|
||||||
|
|
||||||
type sString = starlark.String
|
|
||||||
|
|
||||||
type Computed struct {
|
|
||||||
r *Resource
|
|
||||||
t cty.Type
|
|
||||||
name string
|
|
||||||
path string
|
|
||||||
|
|
||||||
sString
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ starlark.Value = &Computed{}
|
|
||||||
var _ starlark.HasAttrs = &Computed{}
|
|
||||||
var _ starlark.Indexable = &Computed{}
|
|
||||||
var _ starlark.Comparable = &Computed{}
|
|
||||||
|
|
||||||
func NewComputed(r *Resource, t cty.Type, name string) *Computed {
|
|
||||||
var parts []string
|
|
||||||
var path string
|
|
||||||
|
|
||||||
child := r
|
|
||||||
|
|
||||||
for {
|
|
||||||
if child.parent.kind == ProviderKind {
|
|
||||||
if child.kind == ResourceKind {
|
|
||||||
path = fmt.Sprintf("%s.%s", child.typ, child.Name())
|
|
||||||
} else {
|
|
||||||
path = fmt.Sprintf("%s.%s.%s", child.kind, child.typ, child.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
parts = append(parts, child.typ)
|
|
||||||
child = child.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := len(parts) - 1; i >= 0; i-- {
|
|
||||||
path += "." + parts[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// handling of MaxItems equals 1
|
|
||||||
block, ok := r.parent.block.BlockTypes[r.typ]
|
|
||||||
if ok && block.MaxItems == 1 {
|
|
||||||
name = "0." + name
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewComputedWithPath(r, t, name, path+"."+name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewComputedWithPath(r *Resource, t cty.Type, name, path string) *Computed {
|
|
||||||
return &Computed{
|
|
||||||
r: r,
|
|
||||||
t: t,
|
|
||||||
name: name,
|
|
||||||
path: path,
|
|
||||||
sString: starlark.String(fmt.Sprintf("${%s}", path)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type honors the starlark.Value interface.
|
|
||||||
func (c *Computed) Type() string {
|
|
||||||
return fmt.Sprintf("Computed<%s>", MustTypeFromCty(c.t).Starlark())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Computed) InnerType() *Type {
|
|
||||||
t, _ := NewTypeFromCty(c.t)
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attr honors the starlark.HasAttrs interface.
|
|
||||||
func (c *Computed) Attr(name string) (starlark.Value, error) {
|
|
||||||
switch name {
|
|
||||||
case "__resource__":
|
|
||||||
return c.r, nil
|
|
||||||
case "__type__":
|
|
||||||
return starlark.String(MustTypeFromCty(c.t).Starlark()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.t.IsObjectType() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.t.HasAttribute(name) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
path := fmt.Sprintf("%s.%s", c.path, name)
|
|
||||||
return NewComputedWithPath(c.r, c.t.AttributeType(name), name, path), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttrNames honors the starlark.HasAttrs interface.
|
|
||||||
func (c *Computed) AttrNames() []string {
|
|
||||||
return []string{"__resource__", "__type__"}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Computed) doNested(name, path string, t cty.Type, index int) *Computed {
|
|
||||||
return &Computed{
|
|
||||||
r: c.r,
|
|
||||||
t: t,
|
|
||||||
name: c.name,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index honors the starlark.Indexable interface.
|
|
||||||
func (c *Computed) Index(i int) starlark.Value {
|
|
||||||
path := fmt.Sprintf("%s.%d", c.path, i)
|
|
||||||
|
|
||||||
if c.t.IsSetType() {
|
|
||||||
return NewComputedWithPath(c.r, *c.t.SetElementType(), c.name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.t.IsListType() {
|
|
||||||
return NewComputedWithPath(c.r, *c.t.ListElementType(), c.name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return starlark.None
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len honors the starlark.Indexable interface.
|
|
||||||
func (c *Computed) Len() int {
|
|
||||||
if !c.t.IsSetType() && !c.t.IsListType() {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
func BuiltinFunctionComputed() starlark.Value {
|
|
||||||
return starlark.NewBuiltin("fn", func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
|
||||||
var function starlark.String
|
|
||||||
var computed *Computed
|
|
||||||
switch len(args) {
|
|
||||||
case 2:
|
|
||||||
var ok bool
|
|
||||||
function, ok = args.Index(0).(starlark.String)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected string, got %s", args.Index(0).Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
computed, ok = args.Index(1).(*Computed)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected Computed, got %s", args.Index(1).Type())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unexpected positional arguments count")
|
|
||||||
}
|
|
||||||
|
|
||||||
path := fmt.Sprintf("%s(%s)", function.GoString(), computed.path)
|
|
||||||
return NewComputedWithPath(computed.r, computed.t, computed.name, path), nil
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestComputed(t *testing.T) {
|
|
||||||
doTest(t, "testdata/computed.star")
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||||||
"go.starlark.net/starlark"
|
"go.starlark.net/starlark"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HCLCompatible defines if the struct is suitable of by encoded in HCL.
|
||||||
type HCLCompatible interface {
|
type HCLCompatible interface {
|
||||||
ToHCL(b *hclwrite.Body)
|
ToHCL(b *hclwrite.Body)
|
||||||
}
|
}
|
||||||
@ -44,6 +45,7 @@ func BuiltinHCL() starlark.Value {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToHCL honors the HCLCompatible interface.
|
||||||
func (s *Terraform) ToHCL(b *hclwrite.Body) {
|
func (s *Terraform) ToHCL(b *hclwrite.Body) {
|
||||||
if s.b != nil {
|
if s.b != nil {
|
||||||
s.b.ToHCL(b)
|
s.b.ToHCL(b)
|
||||||
@ -52,6 +54,7 @@ func (s *Terraform) ToHCL(b *hclwrite.Body) {
|
|||||||
s.p.ToHCL(b)
|
s.p.ToHCL(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToHCL honors the HCLCompatible interface.
|
||||||
func (s *Dict) ToHCL(b *hclwrite.Body) {
|
func (s *Dict) ToHCL(b *hclwrite.Body) {
|
||||||
for _, v := range s.Keys() {
|
for _, v := range s.Keys() {
|
||||||
p, _, _ := s.Get(v)
|
p, _, _ := s.Get(v)
|
||||||
@ -64,6 +67,7 @@ func (s *Dict) ToHCL(b *hclwrite.Body) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToHCL honors the HCLCompatible interface.
|
||||||
func (s *Provider) ToHCL(b *hclwrite.Body) {
|
func (s *Provider) ToHCL(b *hclwrite.Body) {
|
||||||
block := b.AppendNewBlock("provider", []string{s.typ})
|
block := b.AppendNewBlock("provider", []string{s.typ})
|
||||||
|
|
||||||
@ -76,11 +80,13 @@ func (s *Provider) ToHCL(b *hclwrite.Body) {
|
|||||||
b.AppendNewline()
|
b.AppendNewline()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToHCL honors the HCLCompatible interface.
|
||||||
func (s *Provisioner) ToHCL(b *hclwrite.Body) {
|
func (s *Provisioner) ToHCL(b *hclwrite.Body) {
|
||||||
block := b.AppendNewBlock("provisioner", []string{s.typ})
|
block := b.AppendNewBlock("provisioner", []string{s.typ})
|
||||||
s.Resource.doToHCLAttributes(block.Body())
|
s.Resource.doToHCLAttributes(block.Body())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToHCL honors the HCLCompatible interface.
|
||||||
func (s *Backend) ToHCL(b *hclwrite.Body) {
|
func (s *Backend) ToHCL(b *hclwrite.Body) {
|
||||||
parent := b.AppendNewBlock("terraform", nil)
|
parent := b.AppendNewBlock("terraform", nil)
|
||||||
|
|
||||||
@ -89,6 +95,7 @@ func (s *Backend) ToHCL(b *hclwrite.Body) {
|
|||||||
b.AppendNewline()
|
b.AppendNewline()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToHCL honors the HCLCompatible interface.
|
||||||
func (t *ResourceCollectionGroup) ToHCL(b *hclwrite.Body) {
|
func (t *ResourceCollectionGroup) ToHCL(b *hclwrite.Body) {
|
||||||
names := make(sort.StringSlice, len(t.collections))
|
names := make(sort.StringSlice, len(t.collections))
|
||||||
var i int
|
var i int
|
||||||
@ -103,12 +110,14 @@ func (t *ResourceCollectionGroup) ToHCL(b *hclwrite.Body) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToHCL honors the HCLCompatible interface.
|
||||||
func (c *ResourceCollection) ToHCL(b *hclwrite.Body) {
|
func (c *ResourceCollection) ToHCL(b *hclwrite.Body) {
|
||||||
for i := 0; i < c.Len(); i++ {
|
for i := 0; i < c.Len(); i++ {
|
||||||
c.Index(i).(*Resource).ToHCL(b)
|
c.Index(i).(*Resource).ToHCL(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToHCL honors the HCLCompatible interface.
|
||||||
func (r *Resource) ToHCL(b *hclwrite.Body) {
|
func (r *Resource) ToHCL(b *hclwrite.Body) {
|
||||||
if len(b.Blocks()) != 0 || len(b.Attributes()) != 0 {
|
if len(b.Blocks()) != 0 || len(b.Attributes()) != 0 {
|
||||||
b.AppendNewline()
|
b.AppendNewline()
|
||||||
@ -142,7 +151,7 @@ func (r *Resource) doToHCLAttributes(body *hclwrite.Body) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if c, ok := v.v.(*Computed); ok {
|
if c, ok := v.v.(*Attribute); ok {
|
||||||
body.SetAttributeTraversal(v.Name, hcl.Traversal{
|
body.SetAttributeTraversal(v.Name, hcl.Traversal{
|
||||||
hcl.TraverseRoot{Name: c.String()},
|
hcl.TraverseRoot{Name: c.String()},
|
||||||
})
|
})
|
||||||
@ -168,7 +177,7 @@ func (r *Resource) doToHCLAttributes(body *hclwrite.Body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) doToHCLDependencies(body *hclwrite.Body) {
|
func (r *Resource) doToHCLDependencies(body *hclwrite.Body) {
|
||||||
if len(r.dependenies) == 0 {
|
if len(r.dependencies) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,8 +193,8 @@ func (r *Resource) doToHCLDependencies(body *hclwrite.Body) {
|
|||||||
Type: hclsyntax.TokenOBrack, Bytes: []byte{'['},
|
Type: hclsyntax.TokenOBrack, Bytes: []byte{'['},
|
||||||
})
|
})
|
||||||
|
|
||||||
l := len(r.dependenies)
|
l := len(r.dependencies)
|
||||||
for i, dep := range r.dependenies {
|
for i, dep := range r.dependencies {
|
||||||
name := fmt.Sprintf("%s.%s", dep.typ, dep.Name())
|
name := fmt.Sprintf("%s.%s", dep.typ, dep.Name())
|
||||||
toks = append(toks, &hclwrite.Token{
|
toks = append(toks, &hclwrite.Token{
|
||||||
Type: hclsyntax.TokenIdent, Bytes: []byte(name),
|
Type: hclsyntax.TokenIdent, Bytes: []byte(name),
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// PluginManagerLocal is the key of the terraform.PluginManager in the thread.
|
||||||
PluginManagerLocal = "plugin_manager"
|
PluginManagerLocal = "plugin_manager"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -206,15 +207,15 @@ func (p *Provider) AttrNames() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CompareSameType honors starlark.Comparable interface.
|
// CompareSameType honors starlark.Comparable interface.
|
||||||
func (x *Provider) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) {
|
func (p *Provider) CompareSameType(op syntax.Token, yv starlark.Value, depth int) (bool, error) {
|
||||||
y := y_.(*Provider)
|
y := yv.(*Provider)
|
||||||
switch op {
|
switch op {
|
||||||
case syntax.EQL:
|
case syntax.EQL:
|
||||||
return x == y, nil
|
return p == y, nil
|
||||||
case syntax.NEQ:
|
case syntax.NEQ:
|
||||||
return x != y, nil
|
return p != y, nil
|
||||||
default:
|
default:
|
||||||
return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type())
|
return false, fmt.Errorf("%s %s %s not implemented", p.Type(), op, y.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ func doTestPrint(t *testing.T, filename string, print func(*starlark.Thread, str
|
|||||||
"provisioner": BuiltinProvisioner(),
|
"provisioner": BuiltinProvisioner(),
|
||||||
"backend": BuiltinBackend(),
|
"backend": BuiltinBackend(),
|
||||||
"hcl": BuiltinHCL(),
|
"hcl": BuiltinHCL(),
|
||||||
"fn": BuiltinFunctionComputed(),
|
"fn": BuiltinFunctionAttribute(),
|
||||||
"evaluate": BuiltinEvaluate(),
|
"evaluate": BuiltinEvaluate(),
|
||||||
"tf": NewTerraform(pm),
|
"tf": NewTerraform(pm),
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ func (k Kind) IsProviderRelated() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resource Kind constants.
|
||||||
const (
|
const (
|
||||||
ProviderKind Kind = "provider"
|
ProviderKind Kind = "provider"
|
||||||
ProvisionerKind Kind = "provisioner"
|
ProvisionerKind Kind = "provisioner"
|
||||||
@ -188,7 +189,7 @@ type Resource struct {
|
|||||||
|
|
||||||
provider *Provider
|
provider *Provider
|
||||||
parent *Resource
|
parent *Resource
|
||||||
dependenies []*Resource
|
dependencies []*Resource
|
||||||
provisioners []*Provisioner
|
provisioners []*Provisioner
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +330,7 @@ func (r *Resource) attrBlock(name string, b *configschema.NestedBlock) (starlark
|
|||||||
func (r *Resource) attrValue(name string, attr *configschema.Attribute) (starlark.Value, error) {
|
func (r *Resource) attrValue(name string, attr *configschema.Attribute) (starlark.Value, error) {
|
||||||
if attr.Computed {
|
if attr.Computed {
|
||||||
if !r.values.Has(name) {
|
if !r.values.Has(name) {
|
||||||
return NewComputed(r, attr.Type, name), nil
|
return NewAttribute(r, attr.Type, name), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,7 +469,7 @@ func (r *Resource) dependsOn(_ *starlark.Thread, _ *starlark.Builtin, args starl
|
|||||||
resources[i] = resource
|
resources[i] = resource
|
||||||
}
|
}
|
||||||
|
|
||||||
r.dependenies = append(r.dependenies, resources...)
|
r.dependencies = append(r.dependencies, resources...)
|
||||||
return starlark.None, nil
|
return starlark.None, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,30 +489,30 @@ func (r *Resource) addProvisioner(_ *starlark.Thread, _ *starlark.Builtin, args
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CompareSameType honors starlark.Comparable interface.
|
// CompareSameType honors starlark.Comparable interface.
|
||||||
func (x *Resource) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) {
|
func (r *Resource) CompareSameType(op syntax.Token, yv starlark.Value, depth int) (bool, error) {
|
||||||
y := y_.(*Resource)
|
y := yv.(*Resource)
|
||||||
switch op {
|
switch op {
|
||||||
case syntax.EQL:
|
case syntax.EQL:
|
||||||
ok, err := x.doCompareSameType(y, depth)
|
ok, err := r.doCompareSameType(y, depth)
|
||||||
return ok, err
|
return ok, err
|
||||||
case syntax.NEQ:
|
case syntax.NEQ:
|
||||||
ok, err := x.doCompareSameType(y, depth)
|
ok, err := r.doCompareSameType(y, depth)
|
||||||
return !ok, err
|
return !ok, err
|
||||||
default:
|
default:
|
||||||
return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type())
|
return false, fmt.Errorf("%s %s %s not implemented", r.Type(), op, y.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Resource) doCompareSameType(y *Resource, depth int) (bool, error) {
|
func (r *Resource) doCompareSameType(y *Resource, depth int) (bool, error) {
|
||||||
if x.typ != y.typ {
|
if r.typ != y.typ {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if x.values.Len() != y.values.Len() {
|
if r.values.Len() != y.values.Len() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, xval := range x.values.List() {
|
for _, xval := range r.values.List() {
|
||||||
yval := y.values.Get(xval.Name)
|
yval := y.values.Get(xval.Name)
|
||||||
if yval == nil {
|
if yval == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -4,10 +4,10 @@ aws = tf.provider("aws", "2.13.0")
|
|||||||
|
|
||||||
ami = aws.data.ami()
|
ami = aws.data.ami()
|
||||||
|
|
||||||
# compute of scalar
|
# attribute of scalar
|
||||||
web = aws.resource.instance()
|
web = aws.resource.instance()
|
||||||
web.ami = ami.id
|
web.ami = ami.id
|
||||||
assert.eq(type(web.ami), "Computed<string>")
|
assert.eq(type(web.ami), "Attribute<string>")
|
||||||
assert.eq(str(web.ami), '"${data.aws_ami.id_2.id}"')
|
assert.eq(str(web.ami), '"${data.aws_ami.id_2.id}"')
|
||||||
assert.eq(web.ami.__resource__, ami)
|
assert.eq(web.ami.__resource__, ami)
|
||||||
assert.eq(web.ami.__type__, "string")
|
assert.eq(web.ami.__type__, "string")
|
||||||
@ -16,33 +16,37 @@ assert.eq(web.ami.__type__, "string")
|
|||||||
assert.eq("__resource__" in dir(web.ami), True)
|
assert.eq("__resource__" in dir(web.ami), True)
|
||||||
assert.eq("__type__" in dir(web.ami), True)
|
assert.eq("__type__" in dir(web.ami), True)
|
||||||
|
|
||||||
# compute of set
|
# attribute of set
|
||||||
table = aws.data.dynamodb_table()
|
table = aws.data.dynamodb_table()
|
||||||
assert.eq(str(table.ttl), '"${data.aws_dynamodb_table.id_4.ttl}"')
|
assert.eq(str(table.ttl), '"${data.aws_dynamodb_table.id_4.ttl}"')
|
||||||
assert.eq(str(table.ttl[0]), '"${data.aws_dynamodb_table.id_4.ttl.0}"')
|
assert.eq(str(table.ttl[0]), '"${data.aws_dynamodb_table.id_4.ttl.0}"')
|
||||||
assert.eq(str(table.ttl[0].attribute_name), '"${data.aws_dynamodb_table.id_4.ttl.0.attribute_name}"')
|
assert.eq(str(table.ttl[0].attribute_name), '"${data.aws_dynamodb_table.id_4.ttl.0.attribute_name}"')
|
||||||
|
|
||||||
# compute of list
|
# attribute of list
|
||||||
instance = aws.data.instance()
|
instance = aws.data.instance()
|
||||||
assert.eq(str(instance.credit_specification), '"${data.aws_instance.id_5.credit_specification}"')
|
assert.eq(str(instance.credit_specification), '"${data.aws_instance.id_5.credit_specification}"')
|
||||||
assert.eq(str(instance.credit_specification[0]), '"${data.aws_instance.id_5.credit_specification.0}"')
|
assert.eq(str(instance.credit_specification[0]), '"${data.aws_instance.id_5.credit_specification.0}"')
|
||||||
assert.eq(str(instance.credit_specification[0].cpu_credits), '"${data.aws_instance.id_5.credit_specification.0.cpu_credits}"')
|
assert.eq(str(instance.credit_specification[0].cpu_credits), '"${data.aws_instance.id_5.credit_specification.0.cpu_credits}"')
|
||||||
|
|
||||||
# compute of map
|
# attribute of block
|
||||||
computed = str(aws.resource.instance().root_block_device.volume_size)
|
attribute = str(aws.resource.instance().root_block_device.volume_size)
|
||||||
assert.eq(computed, '"${aws_instance.id_6.root_block_device.0.volume_size}"')
|
assert.eq(attribute, '"${aws_instance.id_6.root_block_device.0.volume_size}"')
|
||||||
|
|
||||||
# compute on data source
|
# attribute on data source
|
||||||
assert.eq(str(aws.resource.instance().id), '"${aws_instance.id_7.id}"')
|
assert.eq(str(aws.resource.instance().id), '"${aws_instance.id_7.id}"')
|
||||||
|
|
||||||
# compute on resource
|
# attribute on resource
|
||||||
assert.eq(str(aws.data.ami().id), '"${data.aws_ami.id_8.id}"')
|
assert.eq(str(aws.data.ami().id), '"${data.aws_ami.id_8.id}"')
|
||||||
|
|
||||||
gcp = tf.provider("google", "3.13.0")
|
gcp = tf.provider("google", "3.13.0")
|
||||||
|
|
||||||
# computed on list with MaxItem:1
|
# attribute on list with MaxItem:1
|
||||||
cluster = gcp.resource.container_cluster("foo")
|
cluster = gcp.resource.container_cluster("foo")
|
||||||
assert.eq(str(cluster.master_auth.client_certificate), '"${google_container_cluster.foo.master_auth.0.client_certificate}"')
|
assert.eq(str(cluster.master_auth.client_certificate), '"${google_container_cluster.foo.master_auth.0.client_certificate}"')
|
||||||
|
|
||||||
|
# attr non-object
|
||||||
|
assert.fails(lambda: web.ami.foo, "Attribute<string> it's not a object")
|
||||||
|
|
||||||
|
|
||||||
# fn wrapping
|
# fn wrapping
|
||||||
assert.eq(str(fn("base64encode", web.ami)), '"${base64encode(data.aws_ami.id_2.id)}"')
|
assert.eq(str(fn("base64encode", web.ami)), '"${base64encode(data.aws_ami.id_2.id)}"')
|
14
starlark/types/testdata/examples/attribute.star
vendored
Normal file
14
starlark/types/testdata/examples/attribute.star
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# When a Resource has an Attribute means that the value it's only available
|
||||||
|
# during the `apply` phase of Terraform. So in, AsCode an attribute behaves
|
||||||
|
# like a poor-man pointer.
|
||||||
|
|
||||||
|
aws = tf.provider("aws")
|
||||||
|
|
||||||
|
ami = aws.resource.ami("ubuntu")
|
||||||
|
|
||||||
|
instance = aws.resource.instance("foo")
|
||||||
|
instance.ami = ami.id
|
||||||
|
|
||||||
|
print(instance.ami)
|
||||||
|
# Output:
|
||||||
|
# "${aws_ami.ubuntu.id}"
|
2
starlark/types/testdata/resource.star
vendored
2
starlark/types/testdata/resource.star
vendored
@ -31,7 +31,7 @@ assert.eq(qux.name, None)
|
|||||||
assert.fails(lambda: qux.foo, "Resource<data> has no .foo field or method")
|
assert.fails(lambda: qux.foo, "Resource<data> has no .foo field or method")
|
||||||
|
|
||||||
# attr id
|
# attr id
|
||||||
assert.eq(type(qux.id), "Computed<string>")
|
assert.eq(type(qux.id), "Attribute<string>")
|
||||||
assert.eq(str(qux.id), '"${data.ignition_user.id_2.id}"')
|
assert.eq(str(qux.id), '"${data.ignition_user.id_2.id}"')
|
||||||
aws = tf.provider("aws", "2.13.0")
|
aws = tf.provider("aws", "2.13.0")
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ func NewTypeFromStarlark(typ string) (*Type, error) {
|
|||||||
t.cty = cty.List(cty.NilType)
|
t.cty = cty.List(cty.NilType)
|
||||||
case "dict", "Resource":
|
case "dict", "Resource":
|
||||||
t.cty = cty.Map(cty.NilType)
|
t.cty = cty.Map(cty.NilType)
|
||||||
case "Computed":
|
case "Attribute":
|
||||||
t.cty = cty.String
|
t.cty = cty.String
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unexpected %q type", typ)
|
return nil, fmt.Errorf("unexpected %q type", typ)
|
||||||
@ -108,7 +108,7 @@ func (t *Type) Cty() cty.Type {
|
|||||||
return t.cty
|
return t.cty
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates a value againts the type.
|
// Validate validates a value against the type.
|
||||||
func (t *Type) Validate(v starlark.Value) error {
|
func (t *Type) Validate(v starlark.Value) error {
|
||||||
switch v.(type) {
|
switch v.(type) {
|
||||||
case starlark.String:
|
case starlark.String:
|
||||||
@ -123,12 +123,12 @@ func (t *Type) Validate(v starlark.Value) error {
|
|||||||
if t.cty == cty.Bool {
|
if t.cty == cty.Bool {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case *Computed:
|
case *Attribute:
|
||||||
if t.cty == v.(*Computed).t {
|
if t.cty == v.(*Attribute).t {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
vt := v.(*Computed).InnerType().Starlark()
|
vt := v.(*Attribute).InnerType().Starlark()
|
||||||
return fmt.Errorf("expected %s, got %s", t.typ, vt)
|
return fmt.Errorf("expected %s, got %s", t.typ, vt)
|
||||||
case *starlark.List:
|
case *starlark.List:
|
||||||
if t.cty.IsListType() || t.cty.IsSetType() {
|
if t.cty.IsListType() || t.cty.IsSetType() {
|
||||||
|
@ -78,8 +78,8 @@ func (v *Value) Cty() cty.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return cty.MapVal(values)
|
return cty.MapVal(values)
|
||||||
case "Computed":
|
case "Attribute":
|
||||||
return cty.StringVal(v.v.(*Computed).GoString())
|
return cty.StringVal(v.v.(*Attribute).GoString())
|
||||||
default:
|
default:
|
||||||
return cty.StringVal(fmt.Sprintf("unhandled: %s", v.t.typ))
|
return cty.StringVal(fmt.Sprintf("unhandled: %s", v.t.typ))
|
||||||
}
|
}
|
||||||
@ -264,10 +264,12 @@ func (a Values) Cty(schema *configschema.Block) cty.Value {
|
|||||||
return cty.ObjectVal(values)
|
return cty.ObjectVal(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dict is a starlark.Dict HCLCompatible.
|
||||||
type Dict struct {
|
type Dict struct {
|
||||||
*starlark.Dict
|
*starlark.Dict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDict returns a new empty Dict.
|
||||||
func NewDict() *Dict {
|
func NewDict() *Dict {
|
||||||
return &Dict{starlark.NewDict(0)}
|
return &Dict{starlark.NewDict(0)}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PluginManager is a wrapper arround the terraform tools to download and execute
|
// PluginManager is a wrapper around the terraform tools to download and execute
|
||||||
// terraform plugins, like providers and provisioners.
|
// terraform plugins, like providers and provisioners.
|
||||||
type PluginManager struct {
|
type PluginManager struct {
|
||||||
Path string
|
Path string
|
||||||
@ -28,7 +28,7 @@ func (m *PluginManager) Provider(provider, version string, forceLocal bool) (*pl
|
|||||||
meta, ok := m.getLocal("provider", provider, version)
|
meta, ok := m.getLocal("provider", provider, version)
|
||||||
if !ok && !forceLocal {
|
if !ok && !forceLocal {
|
||||||
var err error
|
var err error
|
||||||
meta, ok, err = m.getProviderRemote(provider, version)
|
meta, _, err = m.getProviderRemote(provider, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, discovery.PluginMeta{}, err
|
return nil, discovery.PluginMeta{}, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user