From 743fd5c59005210846e64e58cd47b742df3bea50 Mon Sep 17 00:00:00 2001 From: ederst Date: Wed, 21 Apr 2021 01:45:31 +0200 Subject: [PATCH 1/5] WIP: Add tests for TF render --- .../fi/cloudup/openstacktasks/network_test.go | 43 ++++++++++++ .../fi/cloudup/openstacktasks/render_test.go | 65 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 upup/pkg/fi/cloudup/openstacktasks/network_test.go create mode 100644 upup/pkg/fi/cloudup/openstacktasks/render_test.go diff --git a/upup/pkg/fi/cloudup/openstacktasks/network_test.go b/upup/pkg/fi/cloudup/openstacktasks/network_test.go new file mode 100644 index 0000000000000..95454c29631a5 --- /dev/null +++ b/upup/pkg/fi/cloudup/openstacktasks/network_test.go @@ -0,0 +1,43 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package openstacktasks + +import ( + "testing" + + "k8s.io/kops/upup/pkg/fi" +) + +func TestNetworkTerraformRender(t *testing.T) { + cases := []*renderTest{ + { + Resource: &Network{ + Name: fi.String("test"), + }, + Expected: `provider "openstack" { + region = "nova" + } + + resource "openstack_networking_network_v2" "test" { + name = "test" + } + `, + }, + } + + doRenderTests(t, "RenderTerraform", cases) +} diff --git a/upup/pkg/fi/cloudup/openstacktasks/render_test.go b/upup/pkg/fi/cloudup/openstacktasks/render_test.go new file mode 100644 index 0000000000000..464749b410ad7 --- /dev/null +++ b/upup/pkg/fi/cloudup/openstacktasks/render_test.go @@ -0,0 +1,65 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package openstacktasks + +import ( + "io/ioutil" + "os" + "testing" + + "k8s.io/kops/upup/pkg/fi/cloudup/openstack" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" +) + +type renderTest struct { + Resource interface{} + Expected string +} + +func doRenderTests(t *testing.T, method string, cases []*renderTest) { + outdir, err := ioutil.TempDir("", "kops-render-") + if err != nil { + t.Errorf("failed to create local render directory: %s", err) + t.FailNow() + } + defer func () { + err := os.RemoveAll(outdir) + if err != nil { + t.Errorf("failed to remove temp dir %q: %v", outdir, err) + } + }() + + for i, c := range cases { + var filename string + var target interface{} + + cloud := openstack.BuildMockOpenstackCloud("nova") + + switch method { + case "RenderTerraform": + target = terraform.NewTerraformTarget(cloud, "test", outdir, nil) + filename = "kubernetes.tf" + default: + t.Errorf("unknown render method: %s", method) + t.FailNow() + } + + t.Logf("do something with %s and %s", filename, target) + + t.Logf("case %d, expected: %s", i, c.Expected) + } +} \ No newline at end of file From d3cf3e8393f5a6278db22c7a5bc751a5427ef280 Mon Sep 17 00:00:00 2001 From: ederst Date: Wed, 21 Apr 2021 01:45:59 +0200 Subject: [PATCH 2/5] WIP: Add TF render for OpenStack network --- .../pkg/fi/cloudup/openstacktasks/BUILD.bazel | 1 + upup/pkg/fi/cloudup/openstacktasks/network.go | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel b/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel index 7e24f97908afa..fe2821d90136e 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel +++ b/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel @@ -41,6 +41,7 @@ go_library( "//pkg/pki:go_default_library", "//upup/pkg/fi:go_default_library", "//upup/pkg/fi/cloudup/openstack:go_default_library", + "//upup/pkg/fi/cloudup/terraform:go_default_library", "//util/pkg/vfs:go_default_library", "//vendor/github.com/gophercloud/gophercloud:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes:go_default_library", diff --git a/upup/pkg/fi/cloudup/openstacktasks/network.go b/upup/pkg/fi/cloudup/openstacktasks/network.go index 62f2d5b474ad7..71acc508e4494 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/network.go +++ b/upup/pkg/fi/cloudup/openstacktasks/network.go @@ -23,6 +23,7 @@ import ( "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/openstack" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" ) // +kops:fitask @@ -147,3 +148,33 @@ func (_ *Network) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes klog.V(2).Infof("Using an existing Openstack network, id=%s", fi.StringValue(e.ID)) return nil } + +type terraformNetwork struct { + Name *string `json:"name,omitempty" cty:"name"` + // LaunchConfigurationName *terraform.Literal `json:"launch_configuration,omitempty" cty:"launch_configuration"` + // LaunchTemplate *terraformAutoscalingLaunchTemplateSpecification `json:"launch_template,omitempty" cty:"launch_template"` + // MaxSize *int64 `json:"max_size,omitempty" cty:"max_size"` + // MinSize *int64 `json:"min_size,omitempty" cty:"min_size"` + // MixedInstancesPolicy []*terraformMixedInstancesPolicy `json:"mixed_instances_policy,omitempty" cty:"mixed_instances_policy"` + // VPCZoneIdentifier []*terraform.Literal `json:"vpc_zone_identifier,omitempty" cty:"vpc_zone_identifier"` + // Tags []*terraformASGTag `json:"tag,omitempty" cty:"tag"` + // MetricsGranularity *string `json:"metrics_granularity,omitempty" cty:"metrics_granularity"` + // EnabledMetrics []*string `json:"enabled_metrics,omitempty" cty:"enabled_metrics"` + // SuspendedProcesses []*string `json:"suspended_processes,omitempty" cty:"suspended_processes"` + // InstanceProtection *bool `json:"protect_from_scale_in,omitempty" cty:"protect_from_scale_in"` + // LoadBalancers []*terraform.Literal `json:"load_balancers,omitempty" cty:"load_balancers"` + // TargetGroupARNs []*terraform.Literal `json:"target_group_arns,omitempty" cty:"target_group_arns"` +} + +func (_ *Network) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *Network) error { + tf := &terraformNetwork{ + Name: e.Name, + } + + return t.RenderResource("openstack_networking_network_v2", *e.Name, tf) +} + +// TerraformLink fills in the property +func (e *Network) TerraformLink() *terraform.Literal { + return terraform.LiteralProperty("openstack_networking_network_v2", fi.StringValue(e.Name), "id") +} From 59a57100acfcff2cab38e6919d40388850dc27c2 Mon Sep 17 00:00:00 2001 From: ederst Date: Wed, 21 Apr 2021 02:21:59 +0200 Subject: [PATCH 3/5] WIP: Try first test --- .../fi/cloudup/openstacktasks/network_test.go | 18 +++++--- .../fi/cloudup/openstacktasks/render_test.go | 45 ++++++++++++++++++- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/upup/pkg/fi/cloudup/openstacktasks/network_test.go b/upup/pkg/fi/cloudup/openstacktasks/network_test.go index 95454c29631a5..583ed48eb203a 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/network_test.go +++ b/upup/pkg/fi/cloudup/openstacktasks/network_test.go @@ -29,13 +29,19 @@ func TestNetworkTerraformRender(t *testing.T) { Name: fi.String("test"), }, Expected: `provider "openstack" { - region = "nova" - } + region = "nova" +} + +resource "openstack_networking_network_v2" "test" { + name = "test" +} - resource "openstack_networking_network_v2" "test" { - name = "test" - } - `, +terraform { + required_version = ">= 0.12.26" + required_providers { + } +} +`, }, } diff --git a/upup/pkg/fi/cloudup/openstacktasks/render_test.go b/upup/pkg/fi/cloudup/openstacktasks/render_test.go index 464749b410ad7..744d9dcf4f62c 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/render_test.go +++ b/upup/pkg/fi/cloudup/openstacktasks/render_test.go @@ -19,8 +19,13 @@ package openstacktasks import ( "io/ioutil" "os" + "path" + "reflect" "testing" + + "k8s.io/kops/pkg/diff" + "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/openstack" "k8s.io/kops/upup/pkg/fi/cloudup/terraform" ) @@ -58,8 +63,44 @@ func doRenderTests(t *testing.T, method string, cases []*renderTest) { t.FailNow() } - t.Logf("do something with %s and %s", filename, target) + // @step: build the inputs for the methods - hopefully these don't change between them + var inputs []reflect.Value + for _, x := range []interface{}{target, c.Resource, c.Resource, c.Resource} { + inputs = append(inputs, reflect.ValueOf(x)) + } + + err := func() error { + // @step: invoke the rendering method of the target + resp := reflect.ValueOf(c.Resource).MethodByName(method).Call(inputs) + if err := resp[0].Interface(); err != nil { + return err.(error) + } + + // @step: invoke the target finish up + in := []reflect.Value{reflect.ValueOf(make(map[string]fi.Task))} + resp = reflect.ValueOf(target).MethodByName("Finish").Call(in) + if err := resp[0].Interface(); err != nil { + return err.(error) + } - t.Logf("case %d, expected: %s", i, c.Expected) + // @step: check the render is as expected + if c.Expected != "" { + content, err := ioutil.ReadFile(path.Join(outdir, filename)) + if err != nil { + return err + } + if c.Expected != string(content) { + diffString := diff.FormatDiff(c.Expected, string(content)) + t.Logf("diff:\n%s\n", diffString) + t.Errorf("case %d, expected: %s\n,got: %s\n", i, c.Expected, string(content)) + //assert.Equal(t, "", string(content)) + } + } + + return nil + }() + if err != nil { + t.Errorf("case %d, did not expect an error: %s", i, err) + } } } \ No newline at end of file From 6627e19800bafcb2c87a4b7444ee8206318cebcf Mon Sep 17 00:00:00 2001 From: ederst Date: Thu, 29 Apr 2021 09:51:54 +0200 Subject: [PATCH 4/5] WIP: Add openstack provider to hcl and json targets --- upup/pkg/fi/cloudup/openstacktasks/network.go | 14 +------------- upup/pkg/fi/cloudup/openstacktasks/network_test.go | 4 ++++ upup/pkg/fi/cloudup/terraform/target_hcl2.go | 5 +++++ upup/pkg/fi/cloudup/terraform/target_json.go | 8 ++++++++ 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/upup/pkg/fi/cloudup/openstacktasks/network.go b/upup/pkg/fi/cloudup/openstacktasks/network.go index 71acc508e4494..818de59b3a03c 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/network.go +++ b/upup/pkg/fi/cloudup/openstacktasks/network.go @@ -151,23 +151,11 @@ func (_ *Network) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, changes type terraformNetwork struct { Name *string `json:"name,omitempty" cty:"name"` - // LaunchConfigurationName *terraform.Literal `json:"launch_configuration,omitempty" cty:"launch_configuration"` - // LaunchTemplate *terraformAutoscalingLaunchTemplateSpecification `json:"launch_template,omitempty" cty:"launch_template"` - // MaxSize *int64 `json:"max_size,omitempty" cty:"max_size"` - // MinSize *int64 `json:"min_size,omitempty" cty:"min_size"` - // MixedInstancesPolicy []*terraformMixedInstancesPolicy `json:"mixed_instances_policy,omitempty" cty:"mixed_instances_policy"` - // VPCZoneIdentifier []*terraform.Literal `json:"vpc_zone_identifier,omitempty" cty:"vpc_zone_identifier"` - // Tags []*terraformASGTag `json:"tag,omitempty" cty:"tag"` - // MetricsGranularity *string `json:"metrics_granularity,omitempty" cty:"metrics_granularity"` - // EnabledMetrics []*string `json:"enabled_metrics,omitempty" cty:"enabled_metrics"` - // SuspendedProcesses []*string `json:"suspended_processes,omitempty" cty:"suspended_processes"` - // InstanceProtection *bool `json:"protect_from_scale_in,omitempty" cty:"protect_from_scale_in"` - // LoadBalancers []*terraform.Literal `json:"load_balancers,omitempty" cty:"load_balancers"` - // TargetGroupARNs []*terraform.Literal `json:"target_group_arns,omitempty" cty:"target_group_arns"` } func (_ *Network) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *Network) error { tf := &terraformNetwork{ + // ID: e.TerraformLink(), Name: e.Name, } diff --git a/upup/pkg/fi/cloudup/openstacktasks/network_test.go b/upup/pkg/fi/cloudup/openstacktasks/network_test.go index 583ed48eb203a..225b37960ab01 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/network_test.go +++ b/upup/pkg/fi/cloudup/openstacktasks/network_test.go @@ -39,6 +39,10 @@ resource "openstack_networking_network_v2" "test" { terraform { required_version = ">= 0.12.26" required_providers { + openstack = { + "source" = "terraform-provider-openstack/openstack" + "version" = ">= 1.40.0" + } } } `, diff --git a/upup/pkg/fi/cloudup/terraform/target_hcl2.go b/upup/pkg/fi/cloudup/terraform/target_hcl2.go index 4ab72f7640738..0cdf3875dc656 100644 --- a/upup/pkg/fi/cloudup/terraform/target_hcl2.go +++ b/upup/pkg/fi/cloudup/terraform/target_hcl2.go @@ -115,6 +115,11 @@ func (t *TerraformTarget) finishHCL2(taskMap map[string]fi.Task) error { "version": cty.StringVal(">= 1.33.0"), }) } + } else if t.Cloud.ProviderID() == kops.CloudProviderOpenstack { + writeMap(requiredProvidersBody, "openstack", map[string]cty.Value{ + "source": cty.StringVal("terraform-provider-openstack/openstack"), + "version": cty.StringVal(">= 1.40.0"), + }) } bytes := hclwrite.Format(f.Bytes()) diff --git a/upup/pkg/fi/cloudup/terraform/target_json.go b/upup/pkg/fi/cloudup/terraform/target_json.go index 5368c8c47431a..f60867c7573bd 100644 --- a/upup/pkg/fi/cloudup/terraform/target_json.go +++ b/upup/pkg/fi/cloudup/terraform/target_json.go @@ -97,6 +97,14 @@ func (t *TerraformTarget) finishJSON(taskMap map[string]fi.Task) error { requiredProviderAWS[k] = v } requiredProvidersByName["aws"] = requiredProviderAWS + } else if t.Cloud.ProviderID() == kops.CloudProviderOpenstack { + requiredProviderOpenStack := make(map[string]interface{}) + requiredProviderOpenStack["source"] = "terraform-provider-openstack/openstack" + requiredProviderOpenStack["version"] = ">= 1.40.0" + for k, v := range tfGetProviderExtraConfig(t.clusterSpecTarget) { + requiredProviderOpenStack[k] = v + } + requiredProvidersByName["openstack"] = requiredProviderOpenStack } if len(requiredProvidersByName) != 0 { From 79a3622218d3265367315d55488e8237c69a6d59 Mon Sep 17 00:00:00 2001 From: ederst Date: Thu, 29 Apr 2021 09:52:10 +0200 Subject: [PATCH 5/5] DNM: Use goenv to define go version --- .go-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .go-version diff --git a/.go-version b/.go-version new file mode 100644 index 0000000000000..4a02d2c3170bd --- /dev/null +++ b/.go-version @@ -0,0 +1 @@ +1.16.2