diff --git a/go.mod b/go.mod index 0c75214d20..516f6ef92b 100644 --- a/go.mod +++ b/go.mod @@ -76,6 +76,7 @@ require ( require ( cloud.google.com/go/kms v1.10.1 // indirect cloud.google.com/go/monitoring v1.13.0 // indirect + github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 // indirect github.com/Logicalis/asn1 v0.0.0-20190312173541-d60463189a56 // indirect github.com/PromonLogicalis/asn1 v0.0.0-20190312173541-d60463189a56 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect @@ -94,6 +95,7 @@ require ( github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect + github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/eapache/go-resiliency v1.4.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect @@ -126,6 +128,7 @@ require ( github.com/google/gnostic v0.6.9 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // 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 @@ -166,6 +169,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kardianos/govendor v1.0.9 // indirect github.com/klauspost/compress v1.16.7 // indirect github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20221122204822-d1a8c34382f1 // indirect github.com/leodido/go-urn v1.4.0 // indirect @@ -177,6 +181,8 @@ require ( 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/gox v1.0.1 // indirect + github.com/mitchellh/iochan v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -210,13 +216,16 @@ require ( go.opentelemetry.io/otel v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/mod v0.19.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.26.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.26.0 // indirect + golang.org/x/tools/cmd/cover v0.1.0-deprecated // indirect + golang.org/x/tools/go/vcs v0.1.0-deprecated // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect diff --git a/go.sum b/go.sum index ebd142a0a4..db75896c2e 100644 --- a/go.sum +++ b/go.sum @@ -105,6 +105,8 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 h1:xGHheKK44eC6K0u5X+DZW/fRaR1LnDdqPHMZMWx5fv8= +github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -393,6 +395,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU= github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0= +github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA= +github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU= github.com/denisenkom/go-mssqldb v0.0.0-20190412130859-3b1d194e553a/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba h1:p6poVbjHDkKa+wtC8frBMwQtT3BmqGYBjzMwJ63tuR4= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= @@ -785,6 +789,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -954,6 +960,7 @@ github.com/hashicorp/go-uuid v1.0.2-0.20191001231223-f32f5fe8d6a8/go.mod h1:6SBZ 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.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= @@ -1146,6 +1153,8 @@ github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVY github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc= +github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= @@ -1264,7 +1273,10 @@ github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp 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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= +github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -1825,8 +1837,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2058,8 +2070,8 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2197,6 +2209,10 @@ golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58 golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools/cmd/cover v0.1.0-deprecated h1:Rwy+mWYz6loAF+LnG1jHG/JWMHRMMC2/1XX3Ejkx9lA= +golang.org/x/tools/cmd/cover v0.1.0-deprecated/go.mod h1:hMDiIvlpN1NoVgmjLjUJE9tMHyxHjFX7RuQ+rW12mSA= +golang.org/x/tools/go/vcs v0.1.0-deprecated h1:cOIJqWBl99H1dH5LWizPa+0ImeeJq3t3cJjaeOWUAL4= +golang.org/x/tools/go/vcs v0.1.0-deprecated/go.mod h1:zUrvATBAvEI9535oC0yWYsLsHIV4Z7g63sNPVMtuBy8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/ibm/flex/structures.go b/ibm/flex/structures.go index dadae9c479..b20bc33097 100644 --- a/ibm/flex/structures.go +++ b/ibm/flex/structures.go @@ -2751,6 +2751,19 @@ func ApplyOnce(k, o, n string, d *schema.ResourceData) bool { } return true } + +func ApplyOnlyOnce(k, o, n string, d *schema.ResourceData) bool { + // For new resources, allow the first value to be set + if len(d.Id()) == 0 { + return false + } + + // For existing resources, don't allow changes (keep the original value) + if o == "" { + return false + } + return true +} func GetTagsUsingCRN(meta interface{}, resourceCRN string) (*schema.Set, error) { // Move the API to use globalsearch API instead of globalTags API due to rate limit taggingResult, err := GetGlobalTagsUsingSearchAPI(meta, resourceCRN, "", "user") diff --git a/ibm/service/vpc/resource_ibm_is_instance.go b/ibm/service/vpc/resource_ibm_is_instance.go index 73ea36f316..1e1e0636d8 100644 --- a/ibm/service/vpc/resource_ibm_is_instance.go +++ b/ibm/service/vpc/resource_ibm_is_instance.go @@ -218,11 +218,11 @@ func ResourceIBMISInstance() *schema.Resource { // cluster changes "cluster_network_attachments": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Computed: true, - DiffSuppressFunc: diffSuppressClusterNetworkAttachment, - Description: "The cluster network attachments for this virtual server instance.The cluster network attachments are ordered for consistent instance configuration.", + Type: schema.TypeList, + Optional: true, + Computed: true, + // DiffSuppressFunc: diffSuppressClusterNetworkAttachment, + Description: "The cluster network attachments for this virtual server instance.The cluster network attachments are ordered for consistent instance configuration.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "cluster_network_interface": &schema.Schema{ @@ -327,7 +327,7 @@ func ResourceIBMISInstance() *schema.Resource { "name": &schema.Schema{ Type: schema.TypeString, Required: true, - DiffSuppressFunc: flex.ApplyOnce, + DiffSuppressFunc: flex.ApplyOnlyOnce, Description: "The name for this cluster network attachment. Names must be unique within the instance the cluster network attachment resides in. If unspecified, the name will be a hyphenated list of randomly-selected words. Names starting with `ibm-` are reserved for provider-owned resources, and are not allowed.", }, "href": &schema.Schema{ @@ -5430,6 +5430,10 @@ func instanceUpdate(d *schema.ResourceData, meta interface{}) error { if err != nil { return err } + err = handleClusterNetworkAttachmentUpdate(d, instanceC) + if err != nil { + return err + } if d.HasChange("network_attachments") && !d.IsNewResource() { nacs := d.Get("network_attachments").([]interface{}) @@ -8157,6 +8161,7 @@ func setVolumePrototypesInState(d *schema.ResourceData, instance *vpcv1.Instance // to determine if there are actual changes that require an update func diffSuppressClusterNetworkAttachment(k, old, new string, d *schema.ResourceData) bool { // If values are equal, no changes needed + if old == new { return true } @@ -8193,45 +8198,6 @@ func diffSuppressClusterNetworkAttachment(k, old, new string, d *schema.Resource return true } -// compareInterfaces compares the key properties of cluster network interfaces -func compareInterfaces(old, new map[string]interface{}) bool { - // Compare auto_delete - if old["auto_delete"] != new["auto_delete"] { - return false - } - - // Compare name - if old["name"] != new["name"] { - return false - } - - // Compare primary_ip - oldPrimaryIP := old["primary_ip"].([]interface{}) - newPrimaryIP := new["primary_ip"].([]interface{}) - if len(oldPrimaryIP) != len(newPrimaryIP) { - return false - } - if len(oldPrimaryIP) > 0 { - if !comparePrimaryIP(oldPrimaryIP[0].(map[string]interface{}), newPrimaryIP[0].(map[string]interface{})) { - return false - } - } - - // Compare subnet - oldSubnet := old["subnet"].([]interface{}) - newSubnet := new["subnet"].([]interface{}) - if len(oldSubnet) != len(newSubnet) { - return false - } - if len(oldSubnet) > 0 { - if !compareSubnet(oldSubnet[0].(map[string]interface{}), newSubnet[0].(map[string]interface{})) { - return false - } - } - - return true -} - // comparePrimaryIP compares primary IP configurations func comparePrimaryIP(old, new map[string]interface{}) bool { return old["id"] == new["id"] && @@ -8245,50 +8211,46 @@ func compareSubnet(old, new map[string]interface{}) bool { return old["id"] == new["id"] } func handleClusterNetworkAttachmentUpdate(d *schema.ResourceData, instanceC *vpcv1.VpcV1) error { - if d.HasChange("cluster_network_attachments") && !d.IsNewResource() { + if d.HasChange("cluster_network_attachments") { old, new := d.GetChange("cluster_network_attachments") oldAttachments := old.([]interface{}) newAttachments := new.([]interface{}) - // Track attachments to remove, add, and update - toRemove := []string{} - toAdd := []map[string]interface{}{} - toUpdate := []map[string]interface{}{} - - // Build map of old attachments by name for easier lookup + // Build maps for both old and new attachments by name oldAttachMap := make(map[string]map[string]interface{}) + newAttachMap := make(map[string]map[string]interface{}) + + // Map old attachments by name for _, attachment := range oldAttachments { attach := attachment.(map[string]interface{}) - oldAttachMap[attach["name"].(string)] = attach + name := attach["name"].(string) + oldAttachMap[name] = attach } - // Analyze new attachments + // Map new attachments by name and identify additions + toAdd := []map[string]interface{}{} for _, attachment := range newAttachments { attach := attachment.(map[string]interface{}) name := attach["name"].(string) + newAttachMap[name] = attach - if oldAttach, exists := oldAttachMap[name]; exists { - // Check if update needed - if !compareInterfaces( - oldAttach["cluster_network_interface"].([]interface{})[0].(map[string]interface{}), - attach["cluster_network_interface"].([]interface{})[0].(map[string]interface{}), - ) { - toUpdate = append(toUpdate, attach) - } - delete(oldAttachMap, name) - } else { + // If name doesn't exist in old map, it's a new attachment + if _, exists := oldAttachMap[name]; !exists { toAdd = append(toAdd, attach) } } - // Remaining old attachments need to be removed - for _, attach := range oldAttachMap { - if id, ok := attach["id"].(string); ok { - toRemove = append(toRemove, id) + // Identify removals by checking old names not in new map + toRemove := []string{} + for name, attach := range oldAttachMap { + if _, exists := newAttachMap[name]; !exists { + if id, ok := attach["id"].(string); ok { + toRemove = append(toRemove, id) + } } } - // Process removals + // Process removals first instanceID := d.Id() for _, id := range toRemove { deleteOptions := &vpcv1.DeleteInstanceClusterNetworkAttachmentOptions{ @@ -8310,23 +8272,85 @@ func handleClusterNetworkAttachmentUpdate(d *schema.ResourceData, instanceC *vpc } } - // Process updates - for _, attach := range toUpdate { - updateOptions := buildUpdateClusterNetworkAttachmentOptions(d.Id(), attach) - _, _, err := instanceC.UpdateInstanceClusterNetworkAttachment(updateOptions) - if err != nil { - return fmt.Errorf("error updating cluster network attachment: %v", err) + // Identify and process updates for existing attachments + for name, newAttach := range newAttachMap { + if oldAttach, exists := oldAttachMap[name]; exists { + // Compare the interfaces to see if an update is needed + oldInterface := oldAttach["cluster_network_interface"].([]interface{})[0].(map[string]interface{}) + newInterface := newAttach["cluster_network_interface"].([]interface{})[0].(map[string]interface{}) + + if !compareInterfaces(oldInterface, newInterface) { + updateOptions := buildUpdateClusterNetworkAttachmentOptions(d.Id(), newAttach) + _, _, err := instanceC.UpdateInstanceClusterNetworkAttachment(updateOptions) + if err != nil { + return fmt.Errorf("error updating cluster network attachment: %v", err) + } + } } } } return nil } +// Helper function to compare interfaces +func compareInterfaces(old, new map[string]interface{}) bool { + // Compare name + if old["name"] != new["name"] { + return false + } + + // Compare auto_delete if present + oldAutoDelete, oldOk := old["auto_delete"].(bool) + newAutoDelete, newOk := new["auto_delete"].(bool) + if oldOk != newOk || (oldOk && oldAutoDelete != newAutoDelete) { + return false + } + + // Compare primary_ip if present + oldPrimaryIP, oldOk := old["primary_ip"].([]interface{}) + newPrimaryIP, newOk := new["primary_ip"].([]interface{}) + if oldOk != newOk { + return false + } + if oldOk && newOk { + if len(oldPrimaryIP) != len(newPrimaryIP) { + return false + } + if len(oldPrimaryIP) > 0 && len(newPrimaryIP) > 0 { + oldIP := oldPrimaryIP[0].(map[string]interface{}) + newIP := newPrimaryIP[0].(map[string]interface{}) + if oldIP["address"] != newIP["address"] || + oldIP["auto_delete"] != newIP["auto_delete"] || + oldIP["name"] != newIP["name"] { + return false + } + } + } + + // Compare subnet if present + oldSubnet, oldOk := old["subnet"].([]interface{}) + newSubnet, newOk := new["subnet"].([]interface{}) + if oldOk != newOk { + return false + } + if oldOk && newOk { + if len(oldSubnet) != len(newSubnet) { + return false + } + if len(oldSubnet) > 0 && len(newSubnet) > 0 { + oldSub := oldSubnet[0].(map[string]interface{}) + newSub := newSubnet[0].(map[string]interface{}) + if oldSub["id"] != newSub["id"] { + return false + } + } + } + + return true +} + func buildCreateClusterNetworkAttachmentOptions(instanceID string, attachment map[string]interface{}) *vpcv1.CreateClusterNetworkAttachmentOptions { - // Extract network interface data networkInterface := attachment["cluster_network_interface"].([]interface{})[0].(map[string]interface{}) - - // Build cluster network interface clusterNetworkInterface := &vpcv1.InstanceClusterNetworkAttachmentPrototypeClusterNetworkInterface{} if autoDelete, ok := networkInterface["auto_delete"].(bool); ok { @@ -8337,7 +8361,6 @@ func buildCreateClusterNetworkAttachmentOptions(instanceID string, attachment ma clusterNetworkInterface.Name = &name } - // Handle primary IP if present if primaryIPList, ok := networkInterface["primary_ip"].([]interface{}); ok && len(primaryIPList) > 0 { primaryIP := primaryIPList[0].(map[string]interface{}) primaryIPPrototype := &vpcv1.ClusterNetworkInterfacePrimaryIPPrototype{} @@ -8379,27 +8402,18 @@ func buildCreateClusterNetworkAttachmentOptions(instanceID string, attachment ma } func buildUpdateClusterNetworkAttachmentOptions(instanceID string, attachment map[string]interface{}) *vpcv1.UpdateInstanceClusterNetworkAttachmentOptions { - // Extract network interface data networkInterface := attachment["cluster_network_interface"].([]interface{})[0].(map[string]interface{}) - - // Build cluster network interface patch - clusterNetworkInterface := &vpcv1.InstanceClusterNetworkAttachmentPatch{} if name, ok := networkInterface["name"].(string); ok { clusterNetworkInterface.Name = &name } clusterNetworkInterfaceAsPatch, _ := clusterNetworkInterface.AsPatch() - - // Get attachment ID and name attachmentID := attachment["id"].(string) - - // Create the options struct updateOptions := &vpcv1.UpdateInstanceClusterNetworkAttachmentOptions{ InstanceID: &instanceID, ID: &attachmentID, InstanceClusterNetworkAttachmentPatch: clusterNetworkInterfaceAsPatch, } - return updateOptions }