mirror of https://github.com/kubernetes/kops.git
Add support for writing maps of literals, used by gce metadata
This commit is contained in:
parent
2a48c70ea8
commit
ef76409046
|
|
@ -394,28 +394,25 @@ func ShortenImageURL(defaultProject string, imageURL string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type terraformInstance struct {
|
type terraformInstance struct {
|
||||||
Name string `json:"name" cty:"name"`
|
Name string `json:"name" cty:"name"`
|
||||||
CanIPForward bool `json:"can_ip_forward" cty:"can_ip_forward"`
|
CanIPForward bool `json:"can_ip_forward" cty:"can_ip_forward"`
|
||||||
MachineType string `json:"machine_type,omitempty" cty:"machine_type"`
|
MachineType string `json:"machine_type,omitempty" cty:"machine_type"`
|
||||||
ServiceAccount *terraformServiceAccount `json:"service_account,omitempty" cty:"service_account"`
|
ServiceAccount *terraformServiceAccount `json:"service_account,omitempty" cty:"service_account"`
|
||||||
Scheduling *terraformScheduling `json:"scheduling,omitempty" cty:"scheduling"`
|
Scheduling *terraformScheduling `json:"scheduling,omitempty" cty:"scheduling"`
|
||||||
Disks []*terraformInstanceAttachedDisk `json:"disk,omitempty" cty:"disk"`
|
Disks []*terraformInstanceAttachedDisk `json:"disk,omitempty" cty:"disk"`
|
||||||
NetworkInterfaces []*terraformNetworkInterface `json:"network_interface,omitempty" cty:"network_interface"`
|
NetworkInterfaces []*terraformNetworkInterface `json:"network_interface,omitempty" cty:"network_interface"`
|
||||||
Metadata map[string]*terraform.Literal `json:"metadata,omitempty" cty:"metadata"`
|
Metadata map[string]*terraform.Literal `json:"metadata,omitempty" cty:"metadata"`
|
||||||
MetadataStartupScript *terraform.Literal `json:"metadata_startup_script,omitempty" cty:"metadata_startup_script"`
|
MetadataStartupScript *terraform.Literal `json:"metadata_startup_script,omitempty" cty:"metadata_startup_script"`
|
||||||
Tags []string `json:"tags,omitempty" cty:"tags"`
|
Tags []string `json:"tags,omitempty" cty:"tags"`
|
||||||
Zone string `json:"zone,omitempty" cty:"zone"`
|
Zone string `json:"zone,omitempty" cty:"zone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type terraformInstanceAttachedDisk struct {
|
type terraformInstanceAttachedDisk struct {
|
||||||
AutoDelete bool `json:"auto_delete,omitempty" cty:"auto_delete"`
|
AutoDelete bool `json:"auto_delete,omitempty" cty:"auto_delete"`
|
||||||
DeviceName string `json:"device_name,omitempty" cty:"device_name"`
|
DeviceName string `json:"device_name,omitempty" cty:"device_name"`
|
||||||
|
|
||||||
// DANGER - common but different meaning:
|
// 'pd-standard', 'pd-ssd', 'local-ssd' etc
|
||||||
// for an instance template this is scratch vs persistent
|
Type string `json:"type,omitempty" cty:"type"`
|
||||||
// for an instance this is 'pd-standard', 'pd-ssd', 'local-ssd' etc
|
|
||||||
Type string `json:"type,omitempty" cty:"type"`
|
|
||||||
Disk string `json:"disk,omitempty" cty:"disk"`
|
Disk string `json:"disk,omitempty" cty:"disk"`
|
||||||
Image string `json:"image,omitempty" cty:"image"`
|
Image string `json:"image,omitempty" cty:"image"`
|
||||||
Scratch bool `json:"scratch,omitempty" cty:"scratch"`
|
Scratch bool `json:"scratch,omitempty" cty:"scratch"`
|
||||||
|
|
|
||||||
|
|
@ -417,16 +417,16 @@ func (_ *InstanceTemplate) RenderGCE(t *gce.GCEAPITarget, a, e, changes *Instanc
|
||||||
}
|
}
|
||||||
|
|
||||||
type terraformInstanceTemplate struct {
|
type terraformInstanceTemplate struct {
|
||||||
NamePrefix string `json:"name_prefix" cty:"name_prefix"`
|
NamePrefix string `json:"name_prefix" cty:"name_prefix"`
|
||||||
CanIPForward bool `json:"can_ip_forward" cty:"can_ip_forward"`
|
CanIPForward bool `json:"can_ip_forward" cty:"can_ip_forward"`
|
||||||
MachineType string `json:"machine_type,omitempty" cty:"machine_type"`
|
MachineType string `json:"machine_type,omitempty" cty:"machine_type"`
|
||||||
ServiceAccount *terraformServiceAccount `json:"service_account,omitempty" cty:"service_account"`
|
ServiceAccount *terraformServiceAccount `json:"service_account,omitempty" cty:"service_account"`
|
||||||
Scheduling *terraformScheduling `json:"scheduling,omitempty" cty:"scheduling"`
|
Scheduling *terraformScheduling `json:"scheduling,omitempty" cty:"scheduling"`
|
||||||
Disks []*terraformInstanceTemplateAttachedDisk `json:"disk,omitempty" cty:"disk"`
|
Disks []*terraformInstanceTemplateAttachedDisk `json:"disk,omitempty" cty:"disk"`
|
||||||
NetworkInterfaces []*terraformNetworkInterface `json:"network_interface,omitempty" cty:"network_interface"`
|
NetworkInterfaces []*terraformNetworkInterface `json:"network_interface,omitempty" cty:"network_interface"`
|
||||||
Metadata map[string]*terraform.Literal `json:"metadata,omitempty" cty:"metadata"`
|
Metadata map[string]*terraform.Literal `json:"metadata,omitempty" cty:"metadata"`
|
||||||
MetadataStartupScript *terraform.Literal `json:"metadata_startup_script,omitempty" cty:"metadata_startup_script"`
|
MetadataStartupScript *terraform.Literal `json:"metadata_startup_script,omitempty" cty:"metadata_startup_script"`
|
||||||
Tags []string `json:"tags,omitempty" cty:"tags"`
|
Tags []string `json:"tags,omitempty" cty:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type terraformServiceAccount struct {
|
type terraformServiceAccount struct {
|
||||||
|
|
@ -444,12 +444,8 @@ type terraformInstanceTemplateAttachedDisk struct {
|
||||||
AutoDelete bool `json:"auto_delete,omitempty" cty:"auto_delete"`
|
AutoDelete bool `json:"auto_delete,omitempty" cty:"auto_delete"`
|
||||||
DeviceName string `json:"device_name,omitempty" cty:"device_name"`
|
DeviceName string `json:"device_name,omitempty" cty:"device_name"`
|
||||||
|
|
||||||
// DANGER - common but different meaning:
|
// scratch vs persistent
|
||||||
// for an instance template this is scratch vs persistent
|
Type string `json:"type,omitempty" cty:"type"`
|
||||||
// for an instance this is 'pd-standard', 'pd-ssd', 'local-ssd' etc
|
|
||||||
Type string `json:"type,omitempty" cty:"type"`
|
|
||||||
|
|
||||||
// These values are only for instance templates:
|
|
||||||
Boot bool `json:"boot,omitempty" cty:"boot"`
|
Boot bool `json:"boot,omitempty" cty:"boot"`
|
||||||
DiskName string `json:"disk_name,omitempty" cty:"disk_name"`
|
DiskName string `json:"disk_name,omitempty" cty:"disk_name"`
|
||||||
SourceImage string `json:"source_image,omitempty" cty:"source_image"`
|
SourceImage string `json:"source_image,omitempty" cty:"source_image"`
|
||||||
|
|
|
||||||
|
|
@ -40,5 +40,6 @@ go_test(
|
||||||
"//pkg/diff:go_default_library",
|
"//pkg/diff:go_default_library",
|
||||||
"//vendor/github.com/hashicorp/hcl/v2/hclwrite:go_default_library",
|
"//vendor/github.com/hashicorp/hcl/v2/hclwrite:go_default_library",
|
||||||
"//vendor/github.com/zclconf/go-cty/cty:go_default_library",
|
"//vendor/github.com/zclconf/go-cty/cty:go_default_library",
|
||||||
|
"//vendor/github.com/zclconf/go-cty/cty/gocty:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -177,17 +177,32 @@ func writeMap(body *hclwrite.Body, key string, values map[string]cty.Value) {
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
v := values[k]
|
|
||||||
tokens = append(tokens, []*hclwrite.Token{
|
tokens = append(tokens, []*hclwrite.Token{
|
||||||
{Type: hclsyntax.TokenOQuote, Bytes: []byte{'"'}, SpacesBefore: 1},
|
{Type: hclsyntax.TokenOQuote, Bytes: []byte{'"'}, SpacesBefore: 1},
|
||||||
{Type: hclsyntax.TokenQuotedLit, Bytes: []byte(k)},
|
{Type: hclsyntax.TokenQuotedLit, Bytes: []byte(k)},
|
||||||
{Type: hclsyntax.TokenCQuote, Bytes: []byte{'"'}, SpacesBefore: 1},
|
{Type: hclsyntax.TokenCQuote, Bytes: []byte{'"'}, SpacesBefore: 1},
|
||||||
{Type: hclsyntax.TokenEqual, Bytes: []byte("="), SpacesBefore: 1},
|
{Type: hclsyntax.TokenEqual, Bytes: []byte("="), SpacesBefore: 1},
|
||||||
{Type: hclsyntax.TokenOQuote, Bytes: []byte{'"'}, SpacesBefore: 1},
|
|
||||||
{Type: hclsyntax.TokenQuotedLit, Bytes: []byte(v.AsString())},
|
|
||||||
{Type: hclsyntax.TokenCQuote, Bytes: []byte{'"'}, SpacesBefore: 1},
|
|
||||||
{Type: hclsyntax.TokenNewline, Bytes: []byte("\n")},
|
|
||||||
}...)
|
}...)
|
||||||
|
|
||||||
|
v := values[k]
|
||||||
|
|
||||||
|
refLiteral := reflect.New(reflect.TypeOf(Literal{}))
|
||||||
|
err := gocty.FromCtyValue(v, refLiteral.Interface())
|
||||||
|
// If this is a map of literals then do not surround the value with quotes
|
||||||
|
if literal, ok := refLiteral.Interface().(*Literal); err == nil && ok {
|
||||||
|
// For maps of literals we currently only support file references
|
||||||
|
// If we ever need to support a map of strings to resource property references that can be added here
|
||||||
|
if literal.FilePath != "" {
|
||||||
|
tokens = append(tokens, &hclwrite.Token{Type: hclsyntax.TokenIdent, Bytes: []byte(fmt.Sprintf("file(%q)", literal.FilePath))})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tokens = append(tokens, []*hclwrite.Token{
|
||||||
|
{Type: hclsyntax.TokenOQuote, Bytes: []byte{'"'}, SpacesBefore: 1},
|
||||||
|
{Type: hclsyntax.TokenQuotedLit, Bytes: []byte(v.AsString())},
|
||||||
|
{Type: hclsyntax.TokenOQuote, Bytes: []byte{'"'}, SpacesBefore: 1},
|
||||||
|
}...)
|
||||||
|
}
|
||||||
|
tokens = append(tokens, &hclwrite.Token{Type: hclsyntax.TokenNewline, Bytes: []byte("\n")})
|
||||||
}
|
}
|
||||||
tokens = append(tokens,
|
tokens = append(tokens,
|
||||||
&hclwrite.Token{Type: hclsyntax.TokenCBrace, Bytes: []byte("}")},
|
&hclwrite.Token{Type: hclsyntax.TokenCBrace, Bytes: []byte("}")},
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
"github.com/zclconf/go-cty/cty/gocty"
|
||||||
"k8s.io/kops/pkg/diff"
|
"k8s.io/kops/pkg/diff"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -262,3 +263,53 @@ tags = {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWriteMapLiterals(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
values map[string]Literal
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "literal values",
|
||||||
|
values: map[string]Literal{
|
||||||
|
"key1": {FilePath: "${module.path}/path/to/value1"},
|
||||||
|
"key2": {FilePath: "${module.path}/path/to/value2"},
|
||||||
|
},
|
||||||
|
expected: `
|
||||||
|
metadata = {
|
||||||
|
"key1" = file("${module.path}/path/to/value1")
|
||||||
|
"key2" = file("${module.path}/path/to/value2")
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
literalMap := make(map[string]cty.Value)
|
||||||
|
for k, v := range tc.values {
|
||||||
|
literalType, err := gocty.ImpliedType(v)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
literalVal, err := gocty.ToCtyValue(v, literalType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
literalMap[k] = literalVal
|
||||||
|
}
|
||||||
|
|
||||||
|
f := hclwrite.NewEmptyFile()
|
||||||
|
root := f.Body()
|
||||||
|
writeMap(root, "metadata", literalMap)
|
||||||
|
actual := strings.TrimSpace(string(f.Bytes()))
|
||||||
|
expected := strings.TrimSpace(tc.expected)
|
||||||
|
if actual != expected {
|
||||||
|
diffString := diff.FormatDiff(expected, string(actual))
|
||||||
|
t.Logf("diff:\n%s\n", diffString)
|
||||||
|
t.Errorf("expected: '%s', got: '%s'\n", expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue