From f46b6124c8b3291bc92025a51717e4802a3b00cd Mon Sep 17 00:00:00 2001 From: Arthur Outhenin-Chalandre Date: Sun, 26 Jun 2022 13:53:29 +0200 Subject: [PATCH] Add containerinfra nodegroup support (#1364) * Add containerinfra nodegroup resource Signed-off-by: Arthur Outhenin-Chalandre * Add containerinfra nodegroup data source Signed-off-by: Arthur Outhenin-Chalandre * Add containerinfra cluster zero node count support Signed-off-by: Arthur Outhenin-Chalandre * Add containerinfra nodegroup zero node count support Signed-off-by: Arthur Outhenin-Chalandre * Add containerinfra nodegroup resize node count support Signed-off-by: Arthur Outhenin-Chalandre * Fix containerinfra cluster mergelabels behavior When you add mergelabels and you actually add labels, the magnum API will actually return you the complete set of labels under the "labels" key. It means that if you want to add/override a couple of label while setting merge_labels the creation will succeed but then terraform will detect the rest of the labels as changed and will try to rebuid the cluster. This commit instead use the keys labels_{added,skipped,overriden} when merge labels which contains the labels that the user has overriden. Signed-off-by: Arthur Outhenin-Chalandre * Add containerinfra nodegroup support for mergelabels Signed-off-by: Arthur Outhenin-Chalandre * Fix container infra nodegroup golangci-lint errors Signed-off-by: Arthur Outhenin-Chalandre * Fix container infra nodegroup image_id and flavor_id Signed-off-by: Arthur Outhenin-Chalandre * Fix containerinfra cluster template image/flavor_id Previously the code set image_id and flavor_id which doesn't exist instead of image/flavor. It would be way more meaningful to have argument image_id and flavor_id instead but for backward compatibility let's not do that for now. Signed-off-by: Arthur Outhenin-Chalandre * Add OS_MAGNUM_IMAGE to containerinfra acceptance tests As fedora atomic is no more available, we remove it from the code and add OS_MAGNUM_IMAGE environment variable so that we can a supported image in the CI later on. Signed-off-by: Arthur Outhenin-Chalandre * Make container infra cluster aware of default worker nodegroup The node_count returned in magnum for the cluster is the sum of all nodegroup. So once you add a node_count, the correct value is actually the node_count of the default worker node. This commit take the node_count of this default worker node group in priority and if there are any errors it takes the node_count of the cluster instead. Signed-off-by: Arthur Outhenin-Chalandre * Fix container infra nodegroup acceptance tests Signed-off-by: Arthur Outhenin-Chalandre --- go.mod | 54 +-- go.sum | 89 ++++- openstack/containerinfra_shared_v1.go | 65 +++ ...penstack_containerinfra_cluster_v1_test.go | 5 +- ...e_openstack_containerinfra_nodegroup_v1.go | 133 +++++++ ...nstack_containerinfra_nodegroup_v1_test.go | 68 ++++ ...penstack_containerinfra_cluster_v1_test.go | 3 +- ...nstack_containerinfra_nodegroup_v1_test.go | 37 ++ openstack/provider.go | 2 + openstack/provider_test.go | 1 + ...rce_openstack_containerinfra_cluster_v1.go | 48 ++- ...penstack_containerinfra_cluster_v1_test.go | 37 +- ...stack_containerinfra_clustertemplate_v1.go | 6 +- ...e_openstack_containerinfra_nodegroup_v1.go | 372 ++++++++++++++++++ ...nstack_containerinfra_nodegroup_v1_test.go | 143 +++++++ .../containerinfra_nodegroup_v1.html.markdown | 64 +++ .../containerinfra_nodegroup_v1.html.markdown | 99 +++++ 17 files changed, 1139 insertions(+), 87 deletions(-) create mode 100644 openstack/data_source_openstack_containerinfra_nodegroup_v1.go create mode 100644 openstack/data_source_openstack_containerinfra_nodegroup_v1_test.go create mode 100644 openstack/import_openstack_containerinfra_nodegroup_v1_test.go create mode 100644 openstack/resource_openstack_containerinfra_nodegroup_v1.go create mode 100644 openstack/resource_openstack_containerinfra_nodegroup_v1_test.go create mode 100644 website/docs/d/containerinfra_nodegroup_v1.html.markdown create mode 100644 website/docs/r/containerinfra_nodegroup_v1.html.markdown diff --git a/go.mod b/go.mod index 543c54912..b20a68422 100644 --- a/go.mod +++ b/go.mod @@ -3,61 +3,61 @@ module github.com/terraform-provider-openstack/terraform-provider-openstack go 1.17 require ( - github.com/gophercloud/gophercloud v0.24.0 + github.com/gophercloud/gophercloud v0.25.0 github.com/gophercloud/utils v0.0.0-20220307143606-8e7800759d16 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.16.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/stretchr/testify v1.7.1 - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + github.com/stretchr/testify v1.7.2 gopkg.in/yaml.v2 v2.4.0 ) require ( - github.com/agext/levenshtein v1.2.2 // indirect + github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fatih/color v1.7.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.8 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect - github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-hclog v1.2.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/go-plugin v1.4.4 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.4.0 // indirect + github.com/hashicorp/go-version v1.5.0 // indirect github.com/hashicorp/hc-install v0.3.2 // indirect github.com/hashicorp/hcl/v2 v2.12.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.16.1 // indirect - github.com/hashicorp/terraform-json v0.13.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.9.0 // indirect - github.com/hashicorp/terraform-plugin-log v0.4.0 // indirect - github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 // indirect + github.com/hashicorp/terraform-json v0.14.0 // indirect + github.com/hashicorp/terraform-plugin-go v0.9.1 // indirect + github.com/hashicorp/terraform-plugin-log v0.4.1 // indirect + github.com/hashicorp/terraform-registry-address v0.0.0-20220510144317-d78f4a47ae27 // indirect github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect - github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect - github.com/mattn/go-colorable v0.1.4 // indirect - github.com/mattn/go-isatty v0.0.10 // indirect + github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/oklog/run v1.0.0 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect - github.com/vmihailenco/tagparser v0.1.1 // indirect + github.com/vmihailenco/tagparser v0.1.2 // indirect github.com/zclconf/go-cty v1.10.0 // indirect - golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect - golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect - golang.org/x/text v0.3.6 // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect - google.golang.org/grpc v1.45.0 // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + golang.org/x/net v0.0.0-20220615171555-694bf12d69de // indirect + golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220615141314-f1464d18c36b // indirect + google.golang.org/grpc v1.47.0 // indirect google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index be1faaaa8..7b8b32406 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= @@ -30,8 +32,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -43,10 +45,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -91,13 +95,15 @@ github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gophercloud/gophercloud v0.20.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= -github.com/gophercloud/gophercloud v0.24.0 h1:jDsIMGJ1KZpAjYfQgGI2coNQj5Q83oPzuiGJRFWgMzw= -github.com/gophercloud/gophercloud v0.24.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= +github.com/gophercloud/gophercloud v0.25.0 h1:C3Oae7y0fUVQGSsBrb3zliAjdX+riCSEh4lNMejFNI4= +github.com/gophercloud/gophercloud v0.25.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= github.com/gophercloud/utils v0.0.0-20220307143606-8e7800759d16 h1:slt/exMiitZNY+5OrKJ6ZvSogqN+SyzeYzAtyI6db9A= github.com/gophercloud/utils v0.0.0-20220307143606-8e7800759d16/go.mod h1:qOGlfG6OIJ193/c3Xt/XjOfHataNZdQcVgiu93LxBUM= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -109,18 +115,21 @@ github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBM github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= +github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= -github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ= +github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E= +github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hc-install v0.3.1/go.mod h1:3LCdWcCDS1gaHC9mhHCGbkYfoY6vdsKohGjugbZdZak= github.com/hashicorp/hc-install v0.3.2 h1:oiQdJZvXmkNcRcEOOfM5n+VTsvNjWQeOjfAoO6dKSH8= github.com/hashicorp/hc-install v0.3.2/go.mod h1:xMG6Tr8Fw1WFjlxH0A9v61cW15pFwgEGqEz0V4jisHs= @@ -130,22 +139,28 @@ github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.16.1 h1:NAwZFJW2L2SaCBVZoVaH8LPImLOGbPLkSHy0IYbs2uE= github.com/hashicorp/terraform-exec v0.16.1/go.mod h1:aj0lVshy8l+MHhFNoijNHtqTJQI3Xlowv5EOsEaGO7M= -github.com/hashicorp/terraform-json v0.13.0 h1:Li9L+lKD1FO5RVFRM1mMMIBDoUHslOniyEi5CM+FWGY= github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk= -github.com/hashicorp/terraform-plugin-go v0.9.0 h1:FvLY/3z4SNVatPZdoFcyrlNbCar+WyyOTv5X4Tp+WZc= -github.com/hashicorp/terraform-plugin-go v0.9.0/go.mod h1:EawBkgjBWNf7jiKnVoyDyF39OSV+u6KUX+Y73EPj3oM= -github.com/hashicorp/terraform-plugin-log v0.3.0/go.mod h1:EjueSP/HjlyFAsDqt+okpCPjkT4NDynAe32AeDC4vps= +github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= +github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= +github.com/hashicorp/terraform-plugin-go v0.9.1 h1:vXdHaQ6aqL+OF076nMSBV+JKPdmXlzG5mzVDD04WyPs= +github.com/hashicorp/terraform-plugin-go v0.9.1/go.mod h1:ItjVSlQs70otlzcCwlPcU8FRXLdO973oYFRZwAOxy8M= github.com/hashicorp/terraform-plugin-log v0.4.0 h1:F3eVnm8r2EfQCe2k9blPIiF/r2TT01SHijXnS7bujvc= github.com/hashicorp/terraform-plugin-log v0.4.0/go.mod h1:9KclxdunFownr4pIm1jdmwKRmE4d6HVG2c9XDq47rpg= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.16.0 h1:9fjPgCenJqnbjo95SDcbJ+YdLyEC1N35cwKWcRWhJTQ= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.16.0/go.mod h1:hLa0sTiySU/AWEgV2GxJh0/pQIqcCmm30IPja9N9lTg= +github.com/hashicorp/terraform-plugin-log v0.4.1 h1:xpbmVhvuU3mgHzLetOmx9pkOL2rmgpu302XxddON6eo= +github.com/hashicorp/terraform-plugin-log v0.4.1/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0 h1:Qr5fWNg1SPSfCRMtou67Y6Kcy9UnMYRNlIJTKRuUvXU= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0/go.mod h1:b+LFg8WpYgFgvEBP/6Htk5H9/pJp1V1E8NJAekfH2Ws= github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 h1:1FGtlkJw87UsTMg5s8jrekrHmUPUJaMcu6ELiVhQrNw= github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896/go.mod h1:bzBPnUIkI0RxauU8Dqo+2KrZZ28Cf48s8V6IHt3p4co= +github.com/hashicorp/terraform-registry-address v0.0.0-20220510144317-d78f4a47ae27 h1:IOawOnLgKntezAV3oJs17rkhXha+h0EF5OMjb2KFlYc= +github.com/hashicorp/terraform-registry-address v0.0.0-20220510144317-d78f4a47ae27/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= +github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -169,9 +184,15 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -182,6 +203,8 @@ github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -191,6 +214,8 @@ github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -211,8 +236,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -220,6 +245,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvC github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= +github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -242,6 +269,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -261,11 +290,15 @@ golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220615171555-694bf12d69de h1:ogOG2+P6LjO2j55AkRScrkB2BFpd+Z8TY2wcM0Z3MGo= +golang.org/x/net v0.0.0-20220615171555-694bf12d69de/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -284,16 +317,25 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -303,6 +345,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -319,6 +363,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -326,6 +372,8 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxtiIncJxFXeCyq84ixuKWVCaCAi9Oc= google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220615141314-f1464d18c36b h1:2LXbOcxY7BehyA9yu5hxYzaY67bLaJQhBX9O1zxxVis= +google.golang.org/genproto v0.0.0-20220615141314-f1464d18c36b/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -334,8 +382,10 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -366,7 +416,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/openstack/containerinfra_shared_v1.go b/openstack/containerinfra_shared_v1.go index 9fef21a33..080ccb133 100644 --- a/openstack/containerinfra_shared_v1.go +++ b/openstack/containerinfra_shared_v1.go @@ -19,11 +19,15 @@ import ( "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" ) const ( rsaPrivateKeyBlockType = "RSA PRIVATE KEY" certificateRequestBlockType = "CERTIFICATE REQUEST" + + containerInfraV1NodeGroupMinMicroversion = "1.9" + containerInfraV1ZeroNodeCountMicroversion = "1.10" ) func expandContainerInfraV1LabelsMap(v map[string]interface{}) (map[string]string, error) { @@ -55,6 +59,21 @@ func expandContainerInfraV1LabelsString(v map[string]interface{}) (string, error return formattedLabels, nil } +func containerInfraV1GetLabelsMerged(labelsAdded map[string]string, labelsSkipped map[string]string, labelsOverridden map[string]string, labels map[string]string) map[string]string { + m := make(map[string]string) + for key, val := range labelsAdded { + m[key] = val + } + for key, val := range labelsSkipped { + m[key] = val + } + for key := range labelsOverridden { + // We have to get the actual value here, not the one overridden + m[key] = labels[key] + } + return m +} + func containerInfraClusterTemplateV1AppendUpdateOpts(updateOpts []clustertemplates.UpdateOptsBuilder, attribute, value string) []clustertemplates.UpdateOptsBuilder { if value == "" { updateOpts = append(updateOpts, clustertemplates.UpdateOpts{ @@ -71,6 +90,22 @@ func containerInfraClusterTemplateV1AppendUpdateOpts(updateOpts []clustertemplat return updateOpts } +func containerInfraNodeGroupV1AppendUpdateOpts(updateOpts []nodegroups.UpdateOptsBuilder, attribute, value string) []nodegroups.UpdateOptsBuilder { + if value == "" { + updateOpts = append(updateOpts, nodegroups.UpdateOpts{ + Op: nodegroups.RemoveOp, + Path: strings.Join([]string{"/", attribute}, ""), + }) + } else { + updateOpts = append(updateOpts, nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: strings.Join([]string{"/", attribute}, ""), + Value: value, + }) + } + return updateOpts +} + // ContainerInfraClusterV1StateRefreshFunc returns a resource.StateRefreshFunc // that is used to watch a container infra Cluster. func containerInfraClusterV1StateRefreshFunc(client *gophercloud.ServiceClient, clusterID string) resource.StateRefreshFunc { @@ -101,6 +136,36 @@ func containerInfraClusterV1StateRefreshFunc(client *gophercloud.ServiceClient, } } +// ContainerInfraNodeGroupV1StateRefreshFunc returns a resource.StateRefreshFunc +// that is used to watch a container infra NodeGroup. +func containerInfraNodeGroupV1StateRefreshFunc(client *gophercloud.ServiceClient, clusterID string, nodeGroupID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + nodeGroup, err := nodegroups.Get(client, clusterID, nodeGroupID).Extract() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return nodeGroup, "DELETE_COMPLETE", nil + } + return nil, "", err + } + + errorStatuses := []string{ + "CREATE_FAILED", + "UPDATE_FAILED", + "DELETE_FAILED", + "RESUME_FAILED", + "ROLLBACK_FAILED", + } + for _, errorStatus := range errorStatuses { + if nodeGroup.Status == errorStatus { + err = fmt.Errorf("openstack_containerinfra_nodegroup_v1 is in an error state: %s", nodeGroup.StatusReason) + return nodeGroup, nodeGroup.Status, err + } + } + + return nodeGroup, nodeGroup.Status, nil + } +} + // containerInfraClusterV1Flavor will determine the flavor for a container infra // cluster based on either what was set in the configuration or environment // variable. diff --git a/openstack/data_source_openstack_containerinfra_cluster_v1_test.go b/openstack/data_source_openstack_containerinfra_cluster_v1_test.go index 19ba38a9a..769fdb1f7 100644 --- a/openstack/data_source_openstack_containerinfra_cluster_v1_test.go +++ b/openstack/data_source_openstack_containerinfra_cluster_v1_test.go @@ -12,7 +12,6 @@ import ( func TestAccContainerInfraV1ClusterDataSource_basic(t *testing.T) { resourceName := "openstack_containerinfra_cluster_v1.cluster_1" clusterName := acctest.RandomWithPrefix("tf-acc-cluster") - imageName := acctest.RandomWithPrefix("tf-acc-image") keypairName := acctest.RandomWithPrefix("tf-acc-keypair") clusterTemplateName := acctest.RandomWithPrefix("tf-acc-clustertemplate") @@ -25,11 +24,11 @@ func TestAccContainerInfraV1ClusterDataSource_basic(t *testing.T) { CheckDestroy: testAccCheckContainerInfraV1ClusterDestroy, Steps: []resource.TestStep{ { - Config: testAccContainerInfraV1ClusterBasic(imageName, keypairName, clusterTemplateName, clusterName), + Config: testAccContainerInfraV1ClusterBasic(keypairName, clusterTemplateName, clusterName), }, { Config: testAccContainerInfraV1ClusterDataSourceBasic( - testAccContainerInfraV1ClusterBasic(imageName, keypairName, clusterTemplateName, clusterName), + testAccContainerInfraV1ClusterBasic(keypairName, clusterTemplateName, clusterName), ), Check: resource.ComposeTestCheckFunc( testAccCheckContainerInfraV1ClusterDataSourceID(resourceName), diff --git a/openstack/data_source_openstack_containerinfra_nodegroup_v1.go b/openstack/data_source_openstack_containerinfra_nodegroup_v1.go new file mode 100644 index 000000000..ab7f06005 --- /dev/null +++ b/openstack/data_source_openstack_containerinfra_nodegroup_v1.go @@ -0,0 +1,133 @@ +package openstack + +import ( + "context" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" +) + +func dataSourceContainerInfraNodeGroupV1() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceContainerInfraNodeGroupRead, + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "cluster_id": { + Type: schema.TypeString, + Required: true, + }, + + "name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + + "project_id": { + Type: schema.TypeString, + Computed: true, + }, + + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + + "updated_at": { + Type: schema.TypeString, + Computed: true, + }, + + "docker_volume_size": { + Type: schema.TypeInt, + Computed: true, + }, + + "labels": { + Type: schema.TypeMap, + Computed: true, + }, + + "role": { + Type: schema.TypeString, + Computed: true, + }, + + "node_count": { + Type: schema.TypeInt, + Computed: true, + }, + + "min_node_count": { + Type: schema.TypeInt, + Computed: true, + }, + + "max_node_count": { + Type: schema.TypeInt, + Computed: true, + }, + + "image": { + Type: schema.TypeString, + Computed: true, + }, + + "flavor": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceContainerInfraNodeGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + containerInfraClient, err := config.ContainerInfraV1Client(GetRegion(d, config)) + if err != nil { + return diag.Errorf("Error creating OpenStack container infra client: %s", err) + } + + containerInfraClient.Microversion = containerInfraV1NodeGroupMinMicroversion + + clusterID := d.Get("cluster_id").(string) + name := d.Get("name").(string) + nodeGroup, err := nodegroups.Get(containerInfraClient, clusterID, name).Extract() + if err != nil { + return diag.Errorf("Error getting openstack_containerinfra_nodegroup_v1 %s: %s", name, err) + } + + d.SetId(nodeGroup.UUID) + + d.Set("project_id", nodeGroup.ProjectID) + d.Set("docker_volume_size", nodeGroup.DockerVolumeSize) + d.Set("role", nodeGroup.Role) + d.Set("node_count", nodeGroup.NodeCount) + d.Set("min_node_count", nodeGroup.MinNodeCount) + d.Set("max_node_count", nodeGroup.MaxNodeCount) + d.Set("image", nodeGroup.ImageID) + d.Set("flavor", nodeGroup.FlavorID) + + if err := d.Set("labels", nodeGroup.Labels); err != nil { + log.Printf("[DEBUG] Unable to set labels for openstack_containerinfra_nodegroup_v1 %s: %s", nodeGroup.UUID, err) + } + if err := d.Set("created_at", nodeGroup.CreatedAt.Format(time.RFC3339)); err != nil { + log.Printf("[DEBUG] Unable to set created_at for openstack_containerinfra_nodegroup_v1 %s: %s", nodeGroup.UUID, err) + } + if err := d.Set("updated_at", nodeGroup.UpdatedAt.Format(time.RFC3339)); err != nil { + log.Printf("[DEBUG] Unable to set updated_at for openstack_containerinfra_nodegroup_v1 %s: %s", nodeGroup.UUID, err) + } + + d.Set("region", GetRegion(d, config)) + + return nil +} diff --git a/openstack/data_source_openstack_containerinfra_nodegroup_v1_test.go b/openstack/data_source_openstack_containerinfra_nodegroup_v1_test.go new file mode 100644 index 000000000..c2c034c95 --- /dev/null +++ b/openstack/data_source_openstack_containerinfra_nodegroup_v1_test.go @@ -0,0 +1,68 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccContainerInfraV1NodeGroupDataSource_basic(t *testing.T) { + resourceName := "openstack_containerinfra_nodegroup_v1.nodegroup_1" + nodeGroupName := acctest.RandomWithPrefix("tf-acc-nodegroup") + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + keypairName := acctest.RandomWithPrefix("tf-acc-keypair") + clusterTemplateName := acctest.RandomWithPrefix("tf-acc-clustertemplate") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheckNonAdminOnly(t) + testAccPreCheckContainerInfra(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckContainerInfraV1ClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerInfraV1NodeGroupUpdate(keypairName, clusterTemplateName, clusterName, nodeGroupName, 1), + }, + { + Config: testAccContainerInfraV1NodeGroupDataSourceBasic( + testAccContainerInfraV1NodeGroupUpdate(keypairName, clusterTemplateName, clusterName, nodeGroupName, 1), + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckContainerInfraV1NodeGroupDataSourceID(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", nodeGroupName), + resource.TestCheckResourceAttr(resourceName, "node_count", "1"), + ), + }, + }, + }) +} + +func testAccCheckContainerInfraV1NodeGroupDataSourceID(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ct, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Can't find cluster data source: %s", n) + } + + if ct.Primary.ID == "" { + return fmt.Errorf("Cluster data source ID is not set") + } + + return nil + } +} + +func testAccContainerInfraV1NodeGroupDataSourceBasic(nodeGroupResource string) string { + return fmt.Sprintf(` +%s + +data "openstack_containerinfra_nodegroup_v1" "nodegroup_1" { + cluster_id = "${openstack_containerinfra_cluster_v1.cluster_1.name}" + name = "${openstack_containerinfra_nodegroup_v1.nodegroup_1.name}" +} +`, nodeGroupResource) +} diff --git a/openstack/import_openstack_containerinfra_cluster_v1_test.go b/openstack/import_openstack_containerinfra_cluster_v1_test.go index 26c8b01e3..4adc33e8c 100644 --- a/openstack/import_openstack_containerinfra_cluster_v1_test.go +++ b/openstack/import_openstack_containerinfra_cluster_v1_test.go @@ -10,7 +10,6 @@ import ( func TestAccContainerInfraV1ClusterImport_basic(t *testing.T) { resourceName := "openstack_containerinfra_cluster_v1.cluster_1" clusterName := acctest.RandomWithPrefix("tf-acc-cluster") - imageName := acctest.RandomWithPrefix("tf-acc-image") keypairName := acctest.RandomWithPrefix("tf-acc-keypair") clusterTemplateName := acctest.RandomWithPrefix("tf-acc-clustertemplate") @@ -24,7 +23,7 @@ func TestAccContainerInfraV1ClusterImport_basic(t *testing.T) { CheckDestroy: testAccCheckContainerInfraV1ClusterDestroy, Steps: []resource.TestStep{ { - Config: testAccContainerInfraV1ClusterBasic(imageName, keypairName, clusterTemplateName, clusterName), + Config: testAccContainerInfraV1ClusterBasic(keypairName, clusterTemplateName, clusterName), }, { ResourceName: resourceName, diff --git a/openstack/import_openstack_containerinfra_nodegroup_v1_test.go b/openstack/import_openstack_containerinfra_nodegroup_v1_test.go new file mode 100644 index 000000000..f073add38 --- /dev/null +++ b/openstack/import_openstack_containerinfra_nodegroup_v1_test.go @@ -0,0 +1,37 @@ +package openstack + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccContainerInfraV1NodeGroupImport_basic(t *testing.T) { + resourceName := "openstack_containerinfra_nodegroup_v1.nodegroup_1" + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + keypairName := acctest.RandomWithPrefix("tf-acc-keypair") + clusterTemplateName := acctest.RandomWithPrefix("tf-acc-clustertemplate") + nodeGroupName := acctest.RandomWithPrefix("tf-acc-cluster") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckNonAdminOnly(t) + testAccPreCheckContainerInfra(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckContainerInfraV1NodeGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerInfraV1NodeGroupUpdate(keypairName, clusterTemplateName, clusterName, nodeGroupName, 1), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"cluster_id"}, + }, + }, + }) +} diff --git a/openstack/provider.go b/openstack/provider.go index 6e6f718c7..5cd9c9cbe 100644 --- a/openstack/provider.go +++ b/openstack/provider.go @@ -270,6 +270,7 @@ func Provider() *schema.Provider { "openstack_compute_hypervisor_v2": dataSourceComputeHypervisorV2(), "openstack_compute_keypair_v2": dataSourceComputeKeypairV2(), "openstack_compute_quotaset_v2": dataSourceComputeQuotasetV2(), + "openstack_containerinfra_nodegroup_v1": dataSourceContainerInfraNodeGroupV1(), "openstack_containerinfra_clustertemplate_v1": dataSourceContainerInfraClusterTemplateV1(), "openstack_containerinfra_cluster_v1": dataSourceContainerInfraCluster(), "openstack_dns_zone_v2": dataSourceDNSZoneV2(), @@ -331,6 +332,7 @@ func Provider() *schema.Provider { "openstack_compute_floatingip_v2": resourceComputeFloatingIPV2(), "openstack_compute_floatingip_associate_v2": resourceComputeFloatingIPAssociateV2(), "openstack_compute_volume_attach_v2": resourceComputeVolumeAttachV2(), + "openstack_containerinfra_nodegroup_v1": resourceContainerInfraNodeGroupV1(), "openstack_containerinfra_clustertemplate_v1": resourceContainerInfraClusterTemplateV1(), "openstack_containerinfra_cluster_v1": resourceContainerInfraClusterV1(), "openstack_db_instance_v1": resourceDatabaseInstanceV1(), diff --git a/openstack/provider_test.go b/openstack/provider_test.go index ea79ca491..b064f4c24 100644 --- a/openstack/provider_test.go +++ b/openstack/provider_test.go @@ -27,6 +27,7 @@ var ( osImageID = os.Getenv("OS_IMAGE_ID") osImageName = os.Getenv("OS_IMAGE_NAME") osMagnumFlavor = os.Getenv("OS_MAGNUM_FLAVOR") + osMagnumImage = os.Getenv("OS_MAGNUM_IMAGE") osNetworkID = os.Getenv("OS_NETWORK_ID") osPoolName = os.Getenv("OS_POOL_NAME") osRegionName = os.Getenv("OS_REGION_NAME") diff --git a/openstack/resource_openstack_containerinfra_cluster_v1.go b/openstack/resource_openstack_containerinfra_cluster_v1.go index 712422204..153f1af3c 100644 --- a/openstack/resource_openstack_containerinfra_cluster_v1.go +++ b/openstack/resource_openstack_containerinfra_cluster_v1.go @@ -2,6 +2,7 @@ package openstack import ( "context" + "fmt" "log" "strconv" "strings" @@ -11,7 +12,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" ) func resourceContainerInfraClusterV1() *schema.Resource { @@ -160,7 +163,7 @@ func resourceContainerInfraClusterV1() *schema.Resource { Type: schema.TypeInt, Optional: true, ForceNew: false, - Computed: true, + Default: 1, }, "master_addresses": { @@ -274,8 +277,11 @@ func resourceContainerInfraClusterV1Create(ctx context.Context, d *schema.Resour } nodeCount := d.Get("node_count").(int) - if nodeCount > 0 { + if nodeCount >= 0 { createOpts.NodeCount = &nodeCount + if nodeCount == 0 { + containerInfraClient.Microversion = containerInfraV1ZeroNodeCountMicroversion + } } mergeLabels := d.Get("merge_labels").(bool) @@ -310,6 +316,29 @@ func resourceContainerInfraClusterV1Create(ctx context.Context, d *schema.Resour return resourceContainerInfraClusterV1Read(ctx, d, meta) } +func getDefaultNodegroupNodeCount(containerInfraClient *gophercloud.ServiceClient, clusterID string) (int, error) { + containerInfraClient.Microversion = containerInfraV1NodeGroupMinMicroversion + listOpts := nodegroups.ListOpts{} + + allPages, err := nodegroups.List(containerInfraClient, clusterID, listOpts).AllPages() + if err != nil { + return 0, err + } + + ngs, err := nodegroups.ExtractNodeGroups(allPages) + if err != nil { + return 0, err + } + + for _, ng := range ngs { + if ng.IsDefault && ng.Role != "master" { + return ng.NodeCount, nil + } + } + + return 0, fmt.Errorf("Default worker nodegroup not found") +} + func resourceContainerInfraClusterV1Read(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { config := meta.(*Config) containerInfraClient, err := config.ContainerInfraV1Client(GetRegion(d, config)) @@ -324,10 +353,21 @@ func resourceContainerInfraClusterV1Read(_ context.Context, d *schema.ResourceDa log.Printf("[DEBUG] Retrieved openstack_containerinfra_cluster_v1 %s: %#v", d.Id(), s) - if err := d.Set("labels", s.Labels); err != nil { + labels := s.Labels + if d.Get("merge_labels").(bool) { + labels = containerInfraV1GetLabelsMerged(s.LabelsAdded, s.LabelsSkipped, s.LabelsOverridden, s.Labels) + } + if err := d.Set("labels", labels); err != nil { return diag.Errorf("Unable to set openstack_containerinfra_cluster_v1 labels: %s", err) } + nodeCount, err := getDefaultNodegroupNodeCount(containerInfraClient, d.Id()) + if err != nil { + log.Printf("[DEBUG] Can't retrieve node_count of the default worker node group %s: %s", d.Id(), err) + + nodeCount = s.NodeCount + } + d.Set("name", s.Name) d.Set("api_address", s.APIAddress) d.Set("coe_version", s.COEVersion) @@ -340,7 +380,7 @@ func resourceContainerInfraClusterV1Read(_ context.Context, d *schema.ResourceDa d.Set("master_flavor", s.MasterFlavorID) d.Set("keypair", s.KeyPair) d.Set("master_count", s.MasterCount) - d.Set("node_count", s.NodeCount) + d.Set("node_count", nodeCount) d.Set("master_addresses", s.MasterAddresses) d.Set("node_addresses", s.NodeAddresses) d.Set("stack_id", s.StackID) diff --git a/openstack/resource_openstack_containerinfra_cluster_v1_test.go b/openstack/resource_openstack_containerinfra_cluster_v1_test.go index 98cba0dcb..1b7393e14 100644 --- a/openstack/resource_openstack_containerinfra_cluster_v1_test.go +++ b/openstack/resource_openstack_containerinfra_cluster_v1_test.go @@ -17,7 +17,6 @@ func TestAccContainerInfraV1Cluster_basic(t *testing.T) { resourceName := "openstack_containerinfra_cluster_v1.cluster_1" clusterName := acctest.RandomWithPrefix("tf-acc-cluster") - imageName := acctest.RandomWithPrefix("tf-acc-image") keypairName := acctest.RandomWithPrefix("tf-acc-keypair") clusterTemplateName := acctest.RandomWithPrefix("tf-acc-clustertemplate") @@ -31,7 +30,7 @@ func TestAccContainerInfraV1Cluster_basic(t *testing.T) { CheckDestroy: testAccCheckContainerInfraV1ClusterDestroy, Steps: []resource.TestStep{ { - Config: testAccContainerInfraV1ClusterBasic(imageName, keypairName, clusterTemplateName, clusterName), + Config: testAccContainerInfraV1ClusterBasic(keypairName, clusterTemplateName, clusterName), Check: resource.ComposeTestCheckFunc( testAccCheckContainerInfraV1ClusterExists(resourceName, &cluster), resource.TestCheckResourceAttr(resourceName, "name", clusterName), @@ -42,7 +41,7 @@ func TestAccContainerInfraV1Cluster_basic(t *testing.T) { ), }, { - Config: testAccContainerInfraV1ClusterUpdate(imageName, keypairName, clusterTemplateName, clusterName), + Config: testAccContainerInfraV1ClusterUpdate(keypairName, clusterTemplateName, clusterName), Check: resource.ComposeTestCheckFunc( testAccCheckContainerInfraV1ClusterExists(resourceName, &cluster), resource.TestCheckResourceAttr(resourceName, "name", clusterName), @@ -109,25 +108,15 @@ func testAccCheckContainerInfraV1ClusterDestroy(s *terraform.State) error { return nil } -func testAccContainerInfraV1ClusterBasic(imageName, keypairName, clusterTemplateName, clusterName string) string { +func testAccContainerInfraV1ClusterBasic(keypairName, clusterTemplateName, clusterName string) string { return fmt.Sprintf(` -resource "openstack_images_image_v2" "image_1" { - name = "%s" - image_source_url = "https://dl.fedoraproject.org/pub/fedora/linux/releases/27/CloudImages/x86_64/images/Fedora-Atomic-27-1.6.x86_64.qcow2" - container_format = "bare" - disk_format = "qcow2" - properties = { - os_distro = "fedora-atomic" - } -} - resource "openstack_compute_keypair_v2" "keypair_1" { name = "%s" } resource "openstack_containerinfra_clustertemplate_v1" "clustertemplate_1" { name = "%s" - image = "${openstack_images_image_v2.image_1.name}" + image = "%s" coe = "kubernetes" master_flavor = "%s" flavor = "%s" @@ -148,28 +137,18 @@ resource "openstack_containerinfra_cluster_v1" "cluster_1" { node_count = 1 keypair = "${openstack_compute_keypair_v2.keypair_1.name}" } -`, imageName, keypairName, clusterTemplateName, osMagnumFlavor, osMagnumFlavor, osExtGwID, clusterName) +`, keypairName, clusterTemplateName, osMagnumImage, osMagnumFlavor, osMagnumFlavor, osExtGwID, clusterName) } -func testAccContainerInfraV1ClusterUpdate(imageName, keypairName, clusterTemplateName, clusterName string) string { +func testAccContainerInfraV1ClusterUpdate(keypairName, clusterTemplateName, clusterName string) string { return fmt.Sprintf(` -resource "openstack_images_image_v2" "image_1" { - name = "%s" - image_source_url = "https://dl.fedoraproject.org/pub/fedora/linux/releases/27/CloudImages/x86_64/images/Fedora-Atomic-27-1.6.x86_64.qcow2" - container_format = "bare" - disk_format = "qcow2" - properties = { - os_distro = "fedora-atomic" - } -} - resource "openstack_compute_keypair_v2" "keypair_1" { name = "%s" } resource "openstack_containerinfra_clustertemplate_v1" "clustertemplate_1" { name = "%s" - image = "${openstack_images_image_v2.image_1.name}" + image = "%s" coe = "kubernetes" master_flavor = "%s" flavor = "%s" @@ -188,5 +167,5 @@ resource "openstack_containerinfra_cluster_v1" "cluster_1" { node_count = 2 keypair = "${openstack_compute_keypair_v2.keypair_1.name}" } -`, imageName, keypairName, clusterTemplateName, osMagnumFlavor, osMagnumFlavor, osExtGwID, clusterName) +`, keypairName, clusterTemplateName, osMagnumImage, osMagnumFlavor, osMagnumFlavor, osExtGwID, clusterName) } diff --git a/openstack/resource_openstack_containerinfra_clustertemplate_v1.go b/openstack/resource_openstack_containerinfra_clustertemplate_v1.go index 0412fa75a..6c98a08b1 100644 --- a/openstack/resource_openstack_containerinfra_clustertemplate_v1.go +++ b/openstack/resource_openstack_containerinfra_clustertemplate_v1.go @@ -330,12 +330,12 @@ func resourceContainerInfraClusterTemplateV1Read(_ context.Context, d *schema.Re d.Set("external_network_id", s.ExternalNetworkID) d.Set("fixed_network", s.FixedNetwork) d.Set("fixed_subnet", s.FixedSubnet) - d.Set("flavor_id", s.FlavorID) - d.Set("master_flavor_id", s.MasterFlavorID) + d.Set("flavor", s.FlavorID) + d.Set("master_flavor", s.MasterFlavorID) d.Set("floating_ip_enabled", s.FloatingIPEnabled) d.Set("http_proxy", s.HTTPProxy) d.Set("https_proxy", s.HTTPSProxy) - d.Set("image_id", s.ImageID) + d.Set("image", s.ImageID) d.Set("insecure_registry", s.InsecureRegistry) d.Set("keypair_id", s.KeyPairID) d.Set("master_lb_enabled", s.MasterLBEnabled) diff --git a/openstack/resource_openstack_containerinfra_nodegroup_v1.go b/openstack/resource_openstack_containerinfra_nodegroup_v1.go new file mode 100644 index 000000000..cff5cf1ef --- /dev/null +++ b/openstack/resource_openstack_containerinfra_nodegroup_v1.go @@ -0,0 +1,372 @@ +package openstack + +import ( + "context" + "fmt" + "log" + "strconv" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" +) + +func resourceContainerInfraNodeGroupV1() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceContainerInfraNodeGroupV1Create, + ReadContext: resourceContainerInfraNodeGroupV1Read, + UpdateContext: resourceContainerInfraNodeGroupV1Update, + DeleteContext: resourceContainerInfraNodeGroupV1Delete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "cluster_id": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "project_id": { + Type: schema.TypeString, + ForceNew: true, + Computed: true, + }, + + "created_at": { + Type: schema.TypeString, + ForceNew: false, + Computed: true, + }, + + "updated_at": { + Type: schema.TypeString, + ForceNew: false, + Computed: true, + }, + + "docker_volume_size": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "labels": { + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "merge_labels": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "role": { + Type: schema.TypeString, + ForceNew: true, + Computed: true, + }, + + "node_count": { + Type: schema.TypeInt, + Optional: true, + Default: 1, + }, + + "min_node_count": { + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + Computed: true, + }, + + "max_node_count": { + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + Computed: true, + }, + + "image_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "flavor_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + }, + } +} + +func resourceContainerInfraNodeGroupV1Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + containerInfraClient, err := config.ContainerInfraV1Client(GetRegion(d, config)) + if err != nil { + return diag.Errorf("Error creating OpenStack container infra client: %s", err) + } + + containerInfraClient.Microversion = containerInfraV1NodeGroupMinMicroversion + + // Get and check labels map. + rawLabels := d.Get("labels").(map[string]interface{}) + labels, err := expandContainerInfraV1LabelsMap(rawLabels) + if err != nil { + return diag.FromErr(err) + } + + createOpts := nodegroups.CreateOpts{ + Name: d.Get("name").(string), + Labels: labels, + MinNodeCount: d.Get("min_node_count").(int), + Role: d.Get("role").(string), + ImageID: d.Get("image_id").(string), + FlavorID: d.Get("flavor_id").(string), + } + + // Set int parameters that will be passed by reference. + dockerVolumeSize := d.Get("docker_volume_size").(int) + if dockerVolumeSize > 0 { + createOpts.DockerVolumeSize = &dockerVolumeSize + } + nodeCount := d.Get("node_count").(int) + if nodeCount >= 0 { + createOpts.NodeCount = &nodeCount + if nodeCount == 0 { + containerInfraClient.Microversion = containerInfraV1ZeroNodeCountMicroversion + } + } + maxNodeCount := d.Get("max_node_count").(int) + if maxNodeCount > 0 { + createOpts.MaxNodeCount = &maxNodeCount + } + + mergeLabels := d.Get("merge_labels").(bool) + if mergeLabels { + createOpts.MergeLabels = &mergeLabels + } + + log.Printf("[DEBUG] openstack_containerinfra_nodegroup_v1 create options: %#v", createOpts) + + clusterID := d.Get("cluster_id").(string) + nodeGroup, err := nodegroups.Create(containerInfraClient, clusterID, createOpts).Extract() + if err != nil { + return diag.Errorf("Error creating openstack_containerinfra_nodegroup_v1: %s", err) + } + + id := fmt.Sprintf("%s/%s", clusterID, nodeGroup.UUID) + d.SetId(id) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"CREATE_IN_PROGRESS"}, + Target: []string{"CREATE_COMPLETE"}, + Refresh: containerInfraNodeGroupV1StateRefreshFunc(containerInfraClient, clusterID, nodeGroup.UUID), + Timeout: d.Timeout(schema.TimeoutCreate), + Delay: 1 * time.Minute, + PollInterval: 20 * time.Second, + } + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.Errorf( + "Error waiting for openstack_containerinfra_nodegroup_v1 %s to become ready: %s", nodeGroup.UUID, err) + } + + log.Printf("[DEBUG] Created openstack_containerinfra_nodegroup_v1 %s", nodeGroup.UUID) + + return resourceContainerInfraNodeGroupV1Read(ctx, d, meta) +} + +func resourceContainerInfraNodeGroupV1Read(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + containerInfraClient, err := config.ContainerInfraV1Client(GetRegion(d, config)) + if err != nil { + return diag.Errorf("Error creating OpenStack container infra client: %s", err) + } + + containerInfraClient.Microversion = containerInfraV1NodeGroupMinMicroversion + + clusterID, nodeGroupID, err := parseNodeGroupID(d.Id()) + if err != nil { + return diag.FromErr(CheckDeleted(d, err, "Error parsing ID of openstack_containerinfra_nodegroup_v1")) + } + + nodeGroup, err := nodegroups.Get(containerInfraClient, clusterID, nodeGroupID).Extract() + if err != nil { + return diag.FromErr(CheckDeleted(d, err, "Error retrieving openstack_containerinfra_nodegroup_v1")) + } + + log.Printf("[DEBUG] Retrieved openstack_containerinfra_nodegroup_v1 %s: %#v", d.Id(), nodeGroup) + + labels := nodeGroup.Labels + if d.Get("merge_labels").(bool) { + labels = containerInfraV1GetLabelsMerged(nodeGroup.LabelsAdded, nodeGroup.LabelsSkipped, nodeGroup.LabelsOverridden, nodeGroup.Labels) + } + if err := d.Set("labels", labels); err != nil { + return diag.Errorf("Unable to set openstack_containerinfra_nodegroup_v1 labels: %s", err) + } + + d.Set("cluster_id", clusterID) + d.Set("region", GetRegion(d, config)) + d.Set("name", nodeGroup.Name) + d.Set("project_id", nodeGroup.ProjectID) + d.Set("role", nodeGroup.Role) + d.Set("node_count", nodeGroup.NodeCount) + d.Set("min_node_count", nodeGroup.NodeCount) + d.Set("max_node_count", nodeGroup.NodeCount) + d.Set("image_id", nodeGroup.ImageID) + d.Set("flavor_id", nodeGroup.FlavorID) + + if err := d.Set("created_at", nodeGroup.CreatedAt.Format(time.RFC3339)); err != nil { + log.Printf("[DEBUG] Unable to set openstack_containerinfra_nodegroup_v1 created_at: %s", err) + } + if err := d.Set("updated_at", nodeGroup.UpdatedAt.Format(time.RFC3339)); err != nil { + log.Printf("[DEBUG] Unable to set openstack_containerinfra_nodegroup_v1 updated_at: %s", err) + } + + return nil +} + +func resourceContainerInfraNodeGroupV1Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + containerInfraClient, err := config.ContainerInfraV1Client(GetRegion(d, config)) + if err != nil { + return diag.Errorf("Error creating OpenStack container infra client: %s", err) + } + + containerInfraClient.Microversion = containerInfraV1NodeGroupMinMicroversion + + clusterID, nodeGroupID, err := parseNodeGroupID(d.Id()) + if err != nil { + return diag.FromErr(CheckDeleted(d, err, "Error parsing ID of openstack_containerinfra_nodegroup_v1")) + } + + updateOpts := []nodegroups.UpdateOptsBuilder{} + + if d.HasChange("min_node_count") { + v := d.Get("min_node_count").(int) + minNodeCount := strconv.Itoa(v) + updateOpts = containerInfraNodeGroupV1AppendUpdateOpts( + updateOpts, "min_node_count", minNodeCount) + } + + if d.HasChange("max_node_count") { + v := d.Get("max_node_count").(int) + maxNodeCount := strconv.Itoa(v) + updateOpts = containerInfraNodeGroupV1AppendUpdateOpts( + updateOpts, "max_node_count", maxNodeCount) + } + + if len(updateOpts) > 0 { + log.Printf( + "[DEBUG] Updating openstack_containerinfra_nodegroup_v1 %s with options: %#v", d.Id(), updateOpts) + + _, err = nodegroups.Update(containerInfraClient, clusterID, nodeGroupID, updateOpts).Extract() + if err != nil { + return diag.Errorf("Error updating openstack_containerinfra_nodegroup_v1 %s: %s", d.Id(), err) + } + } + + if d.HasChange("node_count") { + v := d.Get("node_count").(int) + var resizeOpts = clusters.ResizeOpts{ + NodeCount: &v, + NodeGroup: nodeGroupID, + } + _, err = clusters.Resize(containerInfraClient, clusterID, resizeOpts).Extract() + if err != nil { + return diag.Errorf("Error resizing openstack_containerinfra_nodegroup_v1 %s: %s", d.Id(), err) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"UPDATE_IN_PROGRESS"}, + Target: []string{"UPDATE_COMPLETE"}, + Refresh: containerInfraNodeGroupV1StateRefreshFunc(containerInfraClient, clusterID, nodeGroupID), + Timeout: d.Timeout(schema.TimeoutUpdate), + Delay: 1 * time.Minute, + PollInterval: 20 * time.Second, + } + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.Errorf( + "Error waiting for openstack_containerinfra_node_group_v1 %s to become updated: %s", d.Id(), err) + } + } + return resourceContainerInfraNodeGroupV1Read(ctx, d, meta) +} + +func resourceContainerInfraNodeGroupV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + containerInfraClient, err := config.ContainerInfraV1Client(GetRegion(d, config)) + if err != nil { + return diag.Errorf("Error creating OpenStack container infra client: %s", err) + } + + containerInfraClient.Microversion = containerInfraV1NodeGroupMinMicroversion + + clusterID, nodeGroupID, err := parseNodeGroupID(d.Id()) + if err != nil { + return diag.FromErr(CheckDeleted(d, err, "Error parsing ID of openstack_containerinfra_nodegroup_v1")) + } + + if err := nodegroups.Delete(containerInfraClient, clusterID, nodeGroupID).ExtractErr(); err != nil { + return diag.FromErr(CheckDeleted(d, err, "Error deleting openstack_containerinfra_nodegroup_v1")) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"DELETE_IN_PROGRESS"}, + Target: []string{"DELETE_COMPLETE"}, + Refresh: containerInfraNodeGroupV1StateRefreshFunc(containerInfraClient, clusterID, nodeGroupID), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 30 * time.Second, + PollInterval: 10 * time.Second, + } + _, err = stateConf.WaitForStateContext(ctx) + if err != nil { + return diag.Errorf( + "Error waiting for openstack_containerinfra_nodegroup_v1 %s to become deleted: %s", d.Id(), err) + } + + return nil +} + +func parseNodeGroupID(id string) (string, string, error) { + idParts := strings.Split(id, "/") + if len(idParts) < 2 { + return "", "", fmt.Errorf("Unable to determine nodegroup ID %s", id) + } + + return idParts[0], idParts[1], nil +} diff --git a/openstack/resource_openstack_containerinfra_nodegroup_v1_test.go b/openstack/resource_openstack_containerinfra_nodegroup_v1_test.go new file mode 100644 index 000000000..d752e6700 --- /dev/null +++ b/openstack/resource_openstack_containerinfra_nodegroup_v1_test.go @@ -0,0 +1,143 @@ +package openstack + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" +) + +func TestAccContainerInfraV1NodeGroup_basic(t *testing.T) { + var nodeGroup nodegroups.NodeGroup + + resourceName := "openstack_containerinfra_nodegroup_v1.nodegroup_1" + clusterName := acctest.RandomWithPrefix("tf-acc-cluster") + keypairName := acctest.RandomWithPrefix("tf-acc-keypair") + clusterTemplateName := acctest.RandomWithPrefix("tf-acc-clustertemplate") + nodeGroupName := acctest.RandomWithPrefix("tf-acc-nodegroup") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckNonAdminOnly(t) + testAccPreCheckContainerInfra(t) + }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckContainerInfraV1NodeGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerInfraV1NodeGroupUpdate(keypairName, clusterTemplateName, clusterName, nodeGroupName, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckContainerInfraV1NodeGroupExists(resourceName, &nodeGroup), + resource.TestCheckResourceAttr(resourceName, "name", nodeGroupName), + resource.TestCheckResourceAttr(resourceName, "node_count", strconv.Itoa(1)), + ), + }, + { + Config: testAccContainerInfraV1NodeGroupUpdate(keypairName, clusterTemplateName, clusterName, nodeGroupName, 2), + Check: resource.ComposeTestCheckFunc( + testAccCheckContainerInfraV1NodeGroupExists(resourceName, &nodeGroup), + resource.TestCheckResourceAttr(resourceName, "name", nodeGroupName), + resource.TestCheckResourceAttr(resourceName, "node_count", strconv.Itoa(2)), + ), + }, + }, + }) +} + +func testAccCheckContainerInfraV1NodeGroupExists(n string, nodeGroup *nodegroups.NodeGroup) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + containerInfraClient, err := config.ContainerInfraV1Client(osRegionName) + if err != nil { + return fmt.Errorf("Error creating OpenStack container infra client: %s", err) + } + + containerInfraClient.Microversion = containerInfraV1NodeGroupMinMicroversion + clusterID, nodeGroupID, err := parseNodeGroupID(rs.Primary.ID) + if err != nil { + return err + } + found, err := nodegroups.Get(containerInfraClient, clusterID, nodeGroupID).Extract() + if err != nil { + return err + } + + if found.UUID != nodeGroupID { + return fmt.Errorf("Nodegroup not found") + } + + *nodeGroup = *found + + return nil + } +} + +func testAccCheckContainerInfraV1NodeGroupDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + containerInfraClient, err := config.ContainerInfraV1Client(osRegionName) + if err != nil { + return fmt.Errorf("Error creating OpenStack container infra client: %s", err) + } + + containerInfraClient.Microversion = containerInfraV1NodeGroupMinMicroversion + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_containerinfra_nodegroup_v1" { + continue + } + clusterID, nodeGroupID, err := parseVolumeTypeAccessID(rs.Primary.ID) + if err != nil { + return err + } + + _, err = nodegroups.Get(containerInfraClient, clusterID, nodeGroupID).Extract() + if err == nil { + return fmt.Errorf("node group still exists") + } + } + + return nil +} +func testAccContainerInfraV1NodeGroupUpdate(keypairName, clusterTemplateName, clusterName string, nodeGroupName string, nodeCount int) string { + return fmt.Sprintf(` +resource "openstack_compute_keypair_v2" "keypair_1" { + name = "%s" +} + +resource "openstack_containerinfra_clustertemplate_v1" "clustertemplate_1" { + name = "%s" + image = "%s" + coe = "kubernetes" + http_proxy = "127.0.0.1:8801" +} + +resource "openstack_containerinfra_cluster_v1" "cluster_1" { + name = "%s" + cluster_template_id = "${openstack_containerinfra_clustertemplate_v1.clustertemplate_1.id}" + master_count = 1 + node_count = 1 + keypair = "${openstack_compute_keypair_v2.keypair_1.name}" +} + +resource "openstack_containerinfra_nodegroup_v1" "nodegroup_1" { + name = "%s" + cluster_id = "${openstack_containerinfra_cluster_v1.cluster_1.id}" + node_count = %d +} +`, keypairName, clusterTemplateName, osMagnumImage, clusterName, nodeGroupName, nodeCount) +} diff --git a/website/docs/d/containerinfra_nodegroup_v1.html.markdown b/website/docs/d/containerinfra_nodegroup_v1.html.markdown new file mode 100644 index 000000000..f995b5b3e --- /dev/null +++ b/website/docs/d/containerinfra_nodegroup_v1.html.markdown @@ -0,0 +1,64 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_containerinfra_nodegroup_v1" +sidebar_current: "docs-openstack-datasource-containerinfra-nodegroup-v1" +description: |- + Get information on an OpenStack Magnum node group. +--- + +# openstack\_containerinfra\_nodegroup\_v1 + +Use this data source to get information of an available OpenStack Magnum node group. + +## Example Usage + +```hcl +data "openstack_containerinfra_nodegroup_v1" "nodegroup_1" { + cluster_id = "cluster_1" + name = "nodegroup_1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional) The region in which to obtain the V1 Container Infra + client. + If omitted, the `region` argument of the provider is used. + +* `cluster_id` - (Required) The name of the OpenStack Magnum cluster. + +* `name` - (Required) The name of the node group. + +## Attributes Reference + +`id` is set to the ID of the found node group. In addition, the following +attributes are exported: + +* `name` - See Argument Reference above. + +* `region` - See Argument Reference above. + +* `project_id` - The project of the node group. + +* `created_at` - The time at which the node group was created. + +* `updated_at` - The time at which the node group was updated. + +* `docker_volume_size` - The size (in GB) of the Docker volume. + +* `labels` - The list of key value pairs representing additional properties of + the node group. + +* `role` - The role of the node group. + +* `node_count` - The number of nodes for the node group. + +* `min_node_count` - The minimum number of nodes for the node group. + +* `max_node_count` - The maximum number of nodes for the node group. + +* `image` - The reference to an image that is used for nodes of the node group. + +* `flavor` - The flavor for the nodes of the node group. diff --git a/website/docs/r/containerinfra_nodegroup_v1.html.markdown b/website/docs/r/containerinfra_nodegroup_v1.html.markdown new file mode 100644 index 000000000..2bafa2942 --- /dev/null +++ b/website/docs/r/containerinfra_nodegroup_v1.html.markdown @@ -0,0 +1,99 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_containerinfra_cluster_v1" +sidebar_current: "docs-openstack-resource-containerinfra-cluster-v1" +description: |- + Manages a V1 Magnum node group resource within OpenStack. +--- + +# openstack\_containerinfra\_nodegroup\_v1 + +Manages a V1 Magnum node group resource within OpenStack. + +## Example Usage + +### Create a Cluster + +```hcl +resource "openstack_containerinfra_nodegroup_v1" "nodegroup_1" { + name = "nodegroup_1" + cluster_id = "b9a45c5c-cd03-4958-82aa-b80bf93cb922" + node_count = 5 +} +``` + +## Argument reference + +The following arguments are supported: + +* `region` - (Optional) The region in which to obtain the V1 Container Infra + client. A Container Infra client is needed to create a cluster. If omitted, + the `region` argument of the provider is used. Changing this creates a new + node group. + +* `name` - (Required) The name of the node group. Changing this creates a new + node group. + +* `project_id` - (Optional) The project of the node group. Required if admin + wants to create a cluster in another project. Changing this creates a new + node group. + +* `cluster_id` - (Required) The UUID of the V1 Container Infra cluster. + Changing this creates a new node group. + +* `docker_volume_size` - (Optional) The size (in GB) of the Docker volume. + Changing this creates a new node group. + +* `image` - (Required) The reference to an image that is used for nodes of the + node group. Can be set via the `OS_MAGNUM_IMAGE` environment variable. + Changing this updates the image attribute of the existing node group. + +* `flavor` - (Optional) The flavor for the nodes of the node group. Can be set + via the `OS_MAGNUM_FLAVOR` environment variable. Changing this creates a new + node group. + +* `labels` - (Optional) The list of key value pairs representing additional + properties of the node group. Changing this creates a new node group. + +* `merge_labels` - (Optional) Indicates whether the provided labels should be + merged with cluster labels. Changing this creates a new nodegroup. + +* `node_count` - (Optional) The number of nodes for the node group. Changing + this update the number of nodes of the node group. + +* `min_node_count` - (Optional) The minimum number of nodes for the node group. + Changing this update the minimum number of nodes of the node group. + +* `max_node_count` - (Optional) The maximum number of nodes for the node group. + Changing this update the maximum number of nodes of the node group. + +* `role` - (Optional) The role of nodes in the node group. Changing this + creates a new node group. + + +## Attributes reference + +The following attributes are exported: + +* `region` - See Argument Reference above. +* `name` - See Argument Reference above. +* `project_id` - See Argument Reference above. +* `created_at` - The time at which cluster was created. +* `updated_at` - The time at which cluster was created. +* `docker_volume_size` - See Argument Reference above. +* `role` - See Argument Reference above. +* `image` - See Argument Reference above. +* `flavor` - See Argument Reference above. +* `labels` - See Argument Reference above. +* `node_count` - See Argument Reference above. +* `min_node_count` - See Argument Reference above. +* `max_node_count` - See Argument Reference above. +* `role` - See Argument Reference above. + +## Import + +Node groups can be imported using the `id`, e.g. + +``` +$ terraform import openstack_containerinfra_nodegroup_v1.nodegroup_1 ce0f9463-dd25-474b-9fe8-94de63e5e42b +```