diff --git a/examples/ibm-sdsaas/README.md b/examples/ibm-sdsaas/README.md new file mode 100644 index 0000000000..3dc99623eb --- /dev/null +++ b/examples/ibm-sdsaas/README.md @@ -0,0 +1,97 @@ +# Examples for sdsaas + +These examples illustrate how to use the resources and data sources associated with sdsaas. + +The following resources are supported: +* ibm_sds_volume +* ibm_sds_host + +## Usage + +To run this example, execute the following commands: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Run `terraform destroy` when you don't need these resources. + +## sdsaas resources + +### Resource: ibm_sds_volume + +```hcl +resource "ibm_sds_volume" "sds_volume_instance" { + hostnqnstring = var.sds_volume_hostnqnstring + capacity = var.sds_volume_capacity + name = var.sds_volume_name +} +``` + +#### Inputs + +| Name | Description | Type | Required | +|------|-------------|------|---------| +| ibmcloud\_api\_key | IBM Cloud API key | `string` | true | +| hostnqnstring | The host nqn. | `string` | false | +| capacity | The capacity of the volume (in gigabytes). | `number` | true | +| name | The name of the volume. | `string` | false | + +#### Outputs + +| Name | Description | +|------|-------------| +| host_mappings | List of host details that volume is mapped to. | +| created_at | The date and time that the volume was created. | +| resource_type | The resource type of the volume. | +| status | The current status of the volume. | +| status_reasons | Reasons for the current status of the volume. | + +### Resource: ibm_sds_host + +```hcl +resource "ibm_sds_host" "sds_host_instance" { + name = var.sds_host_name + nqn = var.sds_host_nqn + volume_mappings = var.sds_host_volume_mappings +} +``` + +#### Inputs + +| Name | Description | Type | Required | +|------|-------------|------|---------| +| ibmcloud\_api\_key | IBM Cloud API key | `string` | true | +| name | The name for this host. The name must not be used by another host. If unspecified, the name will be a hyphenated list of randomly-selected words. | `string` | false | +| nqn | The NQN of the host configured in customer's environment. | `string` | true | +| volume_mappings | The host-to-volume map. | `list()` | false | + +#### Outputs + +| Name | Description | +|------|-------------| +| created_at | The date and time that the host was created. | +| service_instance_id | The service instance ID this host should be created in. | + + +## Assumptions + +1. TODO + +## Notes + +1. TODO + +## Requirements + +| Name | Version | +|------|---------| +| terraform | ~> 0.12 | + +## Providers + +| Name | Version | +|------|---------| +| ibm | 1.13.1 | diff --git a/examples/ibm-sdsaas/main.tf b/examples/ibm-sdsaas/main.tf new file mode 100644 index 0000000000..a3dbd43488 --- /dev/null +++ b/examples/ibm-sdsaas/main.tf @@ -0,0 +1,32 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key +} + +// Provision sds_volume resource instance +resource "ibm_sds_volume" "sds_volume_instance_1" { + hostnqnstring = var.sds_volume_hostnqnstring + capacity = var.sds_volume_capacity + name = var.sds_volume_name_1 +} + +// Provision sds_volume resource instance +resource "ibm_sds_volume" "sds_volume_instance_2" { + hostnqnstring = var.sds_volume_hostnqnstring + capacity = var.sds_volume_capacity + name = var.sds_volume_name_2 +} + +// Provision sds_host resource instance +resource "ibm_sds_host" "sds_host_instance" { + + name = var.sds_host_name + nqn = var.sds_host_nqn + volume_mappings { + volume_id = ibm_sds_volume.sds_volume_instance_1.id + volume_name = ibm_sds_volume.sds_volume_instance_1.name + } + volume_mappings { + volume_id = ibm_sds_volume.sds_volume_instance_2.id + volume_name = ibm_sds_volume.sds_volume_instance_2.name + } +} diff --git a/examples/ibm-sdsaas/outputs.tf b/examples/ibm-sdsaas/outputs.tf new file mode 100644 index 0000000000..b1bd2f53f2 --- /dev/null +++ b/examples/ibm-sdsaas/outputs.tf @@ -0,0 +1,12 @@ +// This output allows sds_volume data to be referenced by other resources and the terraform CLI +// Modify this output if only certain data should be exposed +output "ibm_sds_volume" { + value = [ibm_sds_volume.sds_volume_instance_1, ibm_sds_volume.sds_volume_instance_2] + description = "sds_volume resource instance" +} +// This output allows sds_host data to be referenced by other resources and the terraform CLI +// Modify this output if only certain data should be exposed +output "ibm_sds_host" { + value = ibm_sds_host.sds_host_instance + description = "sds_host resource instance" +} diff --git a/examples/ibm-sdsaas/variables.tf b/examples/ibm-sdsaas/variables.tf new file mode 100644 index 0000000000..ce8164efe0 --- /dev/null +++ b/examples/ibm-sdsaas/variables.tf @@ -0,0 +1,37 @@ +variable "ibmcloud_api_key" { + description = "IBM Cloud API key" + type = string +} + +variable "sds_volume_hostnqnstring" { + description = "The host nqn." + type = string + default = "nqn.2014-06.org:9345" +} +variable "sds_volume_capacity" { + description = "The capacity of the volume (in gigabytes)." + type = number + default = 10 +} +variable "sds_volume_name_1" { + description = "The name of the volume." + type = string + default = "demo-volume-1" +} + +variable "sds_volume_name_2" { + description = "The name of the volume." + type = string + default = "demo-volume-2" +} + +variable "sds_host_name" { + description = "The name for this host. The name must not be used by another host. If unspecified, the name will be a hyphenated list of randomly-selected words." + type = string + default = "demo-host" +} +variable "sds_host_nqn" { + description = "The NQN of the host configured in customer's environment." + type = string + default = "nqn.2014-06.org:9345" +} diff --git a/examples/ibm-sdsaas/versions.tf b/examples/ibm-sdsaas/versions.tf new file mode 100644 index 0000000000..2f2e2867ba --- /dev/null +++ b/examples/ibm-sdsaas/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.0" + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = "1.71.0-beta1" + } + } +} diff --git a/go.mod b/go.mod index cd2dee6acb..258d5d77e8 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/IBM/eventstreams-go-sdk v1.4.0 github.com/IBM/go-sdk-core v1.1.0 github.com/IBM/go-sdk-core/v3 v3.2.4 - github.com/IBM/go-sdk-core/v5 v5.18.0 + github.com/IBM/go-sdk-core/v5 v5.18.1 github.com/IBM/ibm-cos-sdk-go v1.10.3 github.com/IBM/ibm-cos-sdk-go-config/v2 v2.1.0 github.com/IBM/ibm-hpcs-tke-sdk v0.0.0-20211109141421-a4b61b05f7d1 @@ -37,6 +37,7 @@ require ( github.com/IBM/sarama v1.41.2 github.com/IBM/scc-go-sdk/v5 v5.4.1 github.com/IBM/schematics-go-sdk v0.3.0 + github.com/IBM/sds-go-sdk v0.0.0 github.com/IBM/secrets-manager-go-sdk/v2 v2.0.7 github.com/IBM/vmware-go-sdk v0.1.2 github.com/IBM/vpc-beta-go-sdk v0.8.0 @@ -63,7 +64,7 @@ require ( github.com/rook/rook v1.11.4 github.com/softlayer/softlayer-go v1.0.3 github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.28.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible k8s.io/api v0.26.3 @@ -103,7 +104,7 @@ require ( github.com/fatih/color v1.16.0 // indirect github.com/frankban/quicktest v1.14.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.5 // indirect + github.com/gabriel-vasile/mimetype v1.4.6 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -206,16 +207,16 @@ require ( github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.14.1 // indirect - go.mongodb.org/mongo-driver v1.16.1 // indirect + go.mongodb.org/mongo-driver v1.17.1 // indirect 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/net v0.29.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/sys v0.26.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 gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 03867c5b84..5ab48b72aa 100644 --- a/go.sum +++ b/go.sum @@ -188,6 +188,8 @@ github.com/IBM/scc-go-sdk/v5 v5.4.1 h1:RXIuxOo9/hxkWyHCI69ae+KIJgSbXcAkJwTEl+fO3 github.com/IBM/scc-go-sdk/v5 v5.4.1/go.mod h1:2xQTDgNXG5QMEfQxBDKB067z+5ha6OgcaKCTcdGDAo8= github.com/IBM/schematics-go-sdk v0.3.0 h1:Vwxw85SONflakiBsNHAfViKLyp9zJiH5/hh6SewOP5Q= github.com/IBM/schematics-go-sdk v0.3.0/go.mod h1:Tw2OSAPdpC69AxcwoyqcYYaGTTW6YpERF9uNEU+BFRQ= +github.com/IBM/sds-go-sdk v0.0.0 h1:N269p6vCIDSzJh2dy+hLeSVExH9ZB2yLmBnZZB+3HMs= +github.com/IBM/sds-go-sdk v0.0.0/go.mod h1:YMEsGRcyYRd1eYM0DEZPucVsRwPbzWoqVDczXIJInOY= github.com/IBM/secrets-manager-go-sdk/v2 v2.0.7 h1:5lKt1rHuKaAaiZtbPfsF8dgiko/gGbVgreiut3zU128= github.com/IBM/secrets-manager-go-sdk/v2 v2.0.7/go.mod h1:RglK3v6CPe3T1myRtQCD6z+nBygXvNJwufAon0qcZok= github.com/IBM/vmware-go-sdk v0.1.2 h1:5lKWFyInWz9e2hwGsoFTEoLa1jYkD30SReN0fQ10w9M= @@ -488,8 +490,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= -github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= +github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= +github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7/go.mod h1:GeIq9qoE43YdGnDXURnmKTnGg15pQz4mYkXSTChbneI= github.com/gammazero/workerpool v0.0.0-20190406235159-88d534f22b56/go.mod h1:w9RqFVO2BM3xwWEcAB8Fwp0OviTBBEiRmSBDfbXnd3w= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= @@ -1384,8 +1386,8 @@ github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJK github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -1698,8 +1700,8 @@ go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4x go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8= -go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= +go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= +go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1778,8 +1780,8 @@ golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1790,8 +1792,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1908,8 +1908,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2060,8 +2060,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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.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/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= @@ -2080,8 +2080,8 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2104,8 +2104,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/ibm/conns/config.go b/ibm/conns/config.go index 0c0d414f02..972815c6a7 100644 --- a/ibm/conns/config.go +++ b/ibm/conns/config.go @@ -85,6 +85,7 @@ import ( project "github.com/IBM/project-go-sdk/projectv1" "github.com/IBM/push-notifications-go-sdk/pushservicev1" schematicsv1 "github.com/IBM/schematics-go-sdk/schematicsv1" + "github.com/IBM/sds-go-sdk/sdsaasv1" "github.com/IBM/vmware-go-sdk/vmwarev1" vpcbeta "github.com/IBM/vpc-beta-go-sdk/vpcbetav1" "github.com/IBM/vpc-go-sdk/common" @@ -315,6 +316,7 @@ type ClientSession interface { MqcloudV1() (*mqcloudv1.MqcloudV1, error) VmwareV1() (*vmwarev1.VmwareV1, error) LogsV0() (*logsv0.LogsV0, error) + SdsaasV1() (*sdsaasv1.SdsaasV1, error) } type clientSession struct { @@ -667,6 +669,10 @@ type clientSession struct { // Logs Routing ibmCloudLogsRoutingClient *ibmcloudlogsroutingv0.IBMCloudLogsRoutingV0 ibmCloudLogsRoutingClientErr error + + // Software Defined Storage + sdsaasClient *sdsaasv1.SdsaasV1 + sdsaasClientErr error } // Usage Reports @@ -1262,6 +1268,11 @@ func (session clientSession) ProjectV1() (*project.ProjectV1, error) { return session.projectClient, session.projectClientErr } +// sdsaas +func (session clientSession) SdsaasV1() (*sdsaasv1.SdsaasV1, error) { + return session.sdsaasClient, session.sdsaasClientErr +} + // MQ on Cloud func (session clientSession) MqcloudV1() (*mqcloudv1.MqcloudV1, error) { if session.mqcloudClientErr != nil { @@ -3523,6 +3534,28 @@ func (c *Config) ClientSession() (interface{}, error) { session.codeEngineClientErr = fmt.Errorf("Error occurred while configuring Code Engine service: %q", err) } + // Construct an instance of the 'sdsaas' service. + if session.sdsaasClientErr == nil { + // Construct the service options. + sdsaasClientOptions := &sdsaasv1.SdsaasV1Options{ + URL: EnvFallBack([]string{"IBMCLOUD_SDS_ENDPOINT"}, ""), + Authenticator: authenticator, + } + + // Construct the service client. + session.sdsaasClient, err = sdsaasv1.NewSdsaasV1(sdsaasClientOptions) + if err == nil { + // Enable retries for API calls + session.sdsaasClient.Service.EnableRetries(c.RetryCount, c.RetryDelay) + // Add custom header for analytics + session.sdsaasClient.SetDefaultHeaders(gohttp.Header{ + "X-Original-User-Agent": {fmt.Sprintf("terraform-provider-ibm/%s", version.Version)}, + }) + } else { + session.sdsaasClientErr = fmt.Errorf("Error occurred while constructing 'sdsaas' service client: %q", err) + } + } + if os.Getenv("TF_LOG") != "" { logDestination := log.Writer() goLogger := log.New(logDestination, "", log.LstdFlags) diff --git a/ibm/provider/provider.go b/ibm/provider/provider.go index 2d572d2eff..80aa522f7b 100644 --- a/ibm/provider/provider.go +++ b/ibm/provider/provider.go @@ -62,6 +62,7 @@ import ( "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/satellite" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/scc" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/schematics" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/sdsaas" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/secretsmanager" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/transitgateway" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/usagereports" @@ -1305,6 +1306,10 @@ func Provider() *schema.Provider { "ibm_cdn": classicinfrastructure.ResourceIBMCDN(), "ibm_hardware_firewall_shared": classicinfrastructure.ResourceIBMFirewallShared(), + // Software Defined Storage as a Service + "ibm_sds_volume": sdsaas.ResourceIBMSdsVolume(), + "ibm_sds_host": sdsaas.ResourceIBMSdsHost(), + // Partner Center Sell "ibm_onboarding_registration": partnercentersell.ResourceIbmOnboardingRegistration(), "ibm_onboarding_product": partnercentersell.ResourceIbmOnboardingProduct(), @@ -2083,6 +2088,10 @@ func Validator() validate.ValidatorDict { // Added for Logs Router Service "ibm_logs_router_tenant": logsrouting.ResourceIBMLogsRouterTenantValidator(), + + // Added for Software Defined Storage as a Service + "ibm_sds_volume": sdsaas.ResourceIBMSdsVolumeValidator(), + "ibm_sds_host": sdsaas.ResourceIBMSdsHostValidator(), }, DataSourceValidatorDictionary: map[string]*validate.ResourceValidator{ "ibm_is_subnet": vpc.DataSourceIBMISSubnetValidator(), diff --git a/ibm/service/sdsaas/README.md b/ibm/service/sdsaas/README.md new file mode 100644 index 0000000000..6fc4517e90 --- /dev/null +++ b/ibm/service/sdsaas/README.md @@ -0,0 +1,11 @@ +# Terraform IBM Provider + +This area is primarily for IBM provider contributors and maintainers. For information on _using_ Terraform and the IBM provider, see the links below. + + +## Handy Links +* [Find out about contributing](../../../CONTRIBUTING.md) to the IBM provider! +* IBM Provider Docs: [Home](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs) +* IBM Provider Docs: [One of the resources](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/sds_volume) +* IBM API Docs: [IBM API Docs for ]() +* IBM SDK: [IBM SDK for ](https://github.com/IBM/appconfiguration-go-admin-sdk/tree/master/sdsaasv1) diff --git a/ibm/service/sdsaas/resource_ibm_sds_host.go b/ibm/service/sdsaas/resource_ibm_sds_host.go new file mode 100644 index 0000000000..d4208e3ff8 --- /dev/null +++ b/ibm/service/sdsaas/resource_ibm_sds_host.go @@ -0,0 +1,388 @@ +// Copyright IBM Corp. 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +/* + * IBM OpenAPI Terraform Generator Version: 3.96.0-d6dec9d7-20241008-212902 + */ + +package sdsaas + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate" + "github.com/IBM/go-sdk-core/v5/core" + "github.com/IBM/sds-go-sdk/sdsaasv1" +) + +func ResourceIBMSdsHost() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceIBMSdsHostCreate, + ReadContext: resourceIBMSdsHostRead, + UpdateContext: resourceIBMSdsHostUpdate, + DeleteContext: resourceIBMSdsHostDelete, + Importer: &schema.ResourceImporter{}, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.InvokeValidator("ibm_sds_host", "name"), + Description: "The name for this host. The name must not be used by another host. If unspecified, the name will be a hyphenated list of randomly-selected words.", + }, + "nqn": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.InvokeValidator("ibm_sds_host", "nqn"), + Description: "The NQN of the host configured in customer's environment.", + }, + "volume_mappings": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Description: "The host-to-volume map.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "status": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "The current status of a volume/host mapping attempt.", + }, + "volume_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "The volume ID that needs to be mapped with a host.", + }, + "volume_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "The volume name.", + }, + "storage_identifiers": &schema.Schema{ + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Description: "Storage network and ID information associated with a volume/host mapping.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "The storage ID associated with a volume/host mapping.", + }, + "namespace_id": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Description: "The namespace ID associated with a volume/host mapping.", + }, + "namespace_uuid": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "The namespace UUID associated with a volume/host mapping.", + }, + "network_info": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Description: "The IP and port for volume/host mappings.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "gateway_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Network information for volume/host mappings.", + }, + "port": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Description: "Network information for volume/host mappings.", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "created_at": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The date and time that the host was created.", + }, + "service_instance_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The service instance ID this host should be created in.", + }, + }, + } +} + +func ResourceIBMSdsHostValidator() *validate.ResourceValidator { + validateSchema := make([]validate.ValidateSchema, 0) + validateSchema = append(validateSchema, + validate.ValidateSchema{ + Identifier: "name", + ValidateFunctionIdentifier: validate.ValidateRegexpLen, + Type: validate.TypeString, + Optional: true, + Regexp: `^-?([a-z]|[a-z][-a-z0-9]*[a-z0-9]|[0-9][-a-z0-9]*([a-z]|[-a-z][-a-z0-9]*[a-z0-9]))$`, + MinValueLength: 1, + MaxValueLength: 64, + }, + validate.ValidateSchema{ + Identifier: "nqn", + ValidateFunctionIdentifier: validate.ValidateRegexpLen, + Type: validate.TypeString, + Required: true, + Regexp: `^nqn\.\d{4}-\d{2}\.[a-z0-9-]+(?:\.[a-z0-9-]+)*:[a-zA-Z0-9.\-:]+$`, + MinValueLength: 1, + MaxValueLength: 64, + }, + ) + + resourceValidator := validate.ResourceValidator{ResourceName: "ibm_sds_host", Schema: validateSchema} + return &resourceValidator +} + +func resourceIBMSdsHostCreate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sdsaasClient, err := meta.(conns.ClientSession).SdsaasV1() + if err != nil { + tfErr := flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "create", "initialize-client") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + hostCreateOptions := &sdsaasv1.HostCreateOptions{} + + hostCreateOptions.SetNqn(d.Get("nqn").(string)) + if _, ok := d.GetOk("name"); ok { + hostCreateOptions.SetName(d.Get("name").(string)) + } + if _, ok := d.GetOk("volume_mappings"); ok { + var volumeMappings []sdsaasv1.VolumeMappingIdentity + for _, v := range d.Get("volume_mappings").([]interface{}) { + value := v.(map[string]interface{}) + volumeMappingsItem, err := ResourceIBMSdsHostMapToVolumeMappingIdentity(value) + if err != nil { + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "create", "parse-volume_mappings").GetDiag() + } + volumeMappings = append(volumeMappings, *volumeMappingsItem) + } + hostCreateOptions.SetVolumeMappings(volumeMappings) + } + + host, _, err := sdsaasClient.HostCreateWithContext(context, hostCreateOptions) + if err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("HostCreateWithContext failed: %s", err.Error()), "ibm_sds_host", "create") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + d.SetId(*host.ID) + + return resourceIBMSdsHostRead(context, d, meta) +} + +func resourceIBMSdsHostRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sdsaasClient, err := meta.(conns.ClientSession).SdsaasV1() + if err != nil { + tfErr := flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "read", "initialize-client") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + hostOptions := &sdsaasv1.HostOptions{} + + hostOptions.SetHostID(d.Id()) + + host, response, err := sdsaasClient.HostWithContext(context, hostOptions) + if err != nil { + if response != nil && response.StatusCode == 404 { + d.SetId("") + return nil + } + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("HostWithContext failed: %s", err.Error()), "ibm_sds_host", "read") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + if !core.IsNil(host.Name) { + if err = d.Set("name", host.Name); err != nil { + err = fmt.Errorf("Error setting name: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "read", "set-name").GetDiag() + } + } + if err = d.Set("nqn", host.Nqn); err != nil { + err = fmt.Errorf("Error setting nqn: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "read", "set-nqn").GetDiag() + } + if !core.IsNil(host.VolumeMappings) { + volumeMappings := []map[string]interface{}{} + for _, volumeMappingsItem := range host.VolumeMappings { + volumeMappingsItemMap, err := ResourceIBMSdsHostVolumeMappingReferenceToMap(&volumeMappingsItem) // #nosec G601 + if err != nil { + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "read", "volume_mappings-to-map").GetDiag() + } + volumeMappings = append(volumeMappings, volumeMappingsItemMap) + } + if err = d.Set("volume_mappings", volumeMappings); err != nil { + err = fmt.Errorf("Error setting volume_mappings: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "read", "set-volume_mappings").GetDiag() + } + } + if !core.IsNil(host.CreatedAt) { + if err = d.Set("created_at", host.CreatedAt); err != nil { + err = fmt.Errorf("Error setting created_at: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "read", "set-created_at").GetDiag() + } + } + if !core.IsNil(host.ServiceInstanceID) { + if err = d.Set("service_instance_id", host.ServiceInstanceID); err != nil { + err = fmt.Errorf("Error setting service_instance_id: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "read", "set-service_instance_id").GetDiag() + } + } + + return nil +} + +func resourceIBMSdsHostUpdate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sdsaasClient, err := meta.(conns.ClientSession).SdsaasV1() + if err != nil { + tfErr := flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "update", "initialize-client") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + hostUpdateOptions := &sdsaasv1.HostUpdateOptions{} + + hostUpdateOptions.SetHostID(d.Id()) + + hasChange := false + + patchVals := &sdsaasv1.HostPatch{} + if d.HasChange("name") { + newName := d.Get("name").(string) + patchVals.Name = &newName + hasChange = true + } + + if hasChange { + // Fields with `nil` values are omitted from the generic map, + // so we need to re-add them to support removing arguments + // in merge-patch operations sent to the service. + hostUpdateOptions.HostPatch = ResourceIBMSdsHostHostPatchAsPatch(patchVals, d) + + _, _, err = sdsaasClient.HostUpdateWithContext(context, hostUpdateOptions) + if err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("HostUpdateWithContext failed: %s", err.Error()), "ibm_sds_host", "update") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + } + + return resourceIBMSdsHostRead(context, d, meta) +} + +func resourceIBMSdsHostDelete(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sdsaasClient, err := meta.(conns.ClientSession).SdsaasV1() + if err != nil { + tfErr := flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_host", "delete", "initialize-client") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + hostDeleteOptions := &sdsaasv1.HostDeleteOptions{} + + hostDeleteOptions.SetHostID(d.Id()) + + _, err = sdsaasClient.HostDeleteWithContext(context, hostDeleteOptions) + if err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("HostDeleteWithContext failed: %s", err.Error()), "ibm_sds_host", "delete") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + d.SetId("") + + return nil +} + +func ResourceIBMSdsHostMapToVolumeMappingIdentity(modelMap map[string]interface{}) (*sdsaasv1.VolumeMappingIdentity, error) { + model := &sdsaasv1.VolumeMappingIdentity{} + model.VolumeID = core.StringPtr(modelMap["volume_id"].(string)) + return model, nil +} + +func ResourceIBMSdsHostVolumeMappingReferenceToMap(model *sdsaasv1.VolumeMappingReference) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + if model.Status != nil { + modelMap["status"] = *model.Status + } + modelMap["volume_id"] = *model.VolumeID + modelMap["volume_name"] = *model.VolumeName + if model.StorageIdentifiers != nil { + storageIdentifiersMap, err := ResourceIBMSdsHostStorageIdentifiersReferenceToMap(model.StorageIdentifiers) + if err != nil { + return modelMap, err + } + modelMap["storage_identifiers"] = []map[string]interface{}{storageIdentifiersMap} + } + return modelMap, nil +} + +func ResourceIBMSdsHostStorageIdentifiersReferenceToMap(model *sdsaasv1.StorageIdentifiersReference) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + if model.ID != nil { + modelMap["id"] = *model.ID + } + if model.NamespaceID != nil { + modelMap["namespace_id"] = flex.IntValue(model.NamespaceID) + } + if model.NamespaceUUID != nil { + modelMap["namespace_uuid"] = *model.NamespaceUUID + } + if model.NetworkInfo != nil { + networkInfo := []map[string]interface{}{} + for _, networkInfoItem := range model.NetworkInfo { + networkInfoItemMap, err := ResourceIBMSdsHostNetworkInfoReferenceToMap(&networkInfoItem) // #nosec G601 + if err != nil { + return modelMap, err + } + networkInfo = append(networkInfo, networkInfoItemMap) + } + modelMap["network_info"] = networkInfo + } + return modelMap, nil +} + +func ResourceIBMSdsHostNetworkInfoReferenceToMap(model *sdsaasv1.NetworkInfoReference) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + if model.GatewayIP != nil { + modelMap["gateway_ip"] = *model.GatewayIP + } + if model.Port != nil { + modelMap["port"] = flex.IntValue(model.Port) + } + return modelMap, nil +} + +func ResourceIBMSdsHostHostPatchAsPatch(patchVals *sdsaasv1.HostPatch, d *schema.ResourceData) map[string]interface{} { + patch, _ := patchVals.AsPatch() + var path string + + path = "name" + if _, exists := d.GetOk(path); d.HasChange(path) && !exists { + patch["name"] = nil + } + + return patch +} diff --git a/ibm/service/sdsaas/resource_ibm_sds_host_test.go b/ibm/service/sdsaas/resource_ibm_sds_host_test.go new file mode 100644 index 0000000000..043261bd4c --- /dev/null +++ b/ibm/service/sdsaas/resource_ibm_sds_host_test.go @@ -0,0 +1,274 @@ +// Copyright IBM Corp. 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package sdsaas_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/sdsaas" + "github.com/IBM/go-sdk-core/v5/core" + "github.com/IBM/sds-go-sdk/sdsaasv1" + "github.com/stretchr/testify/assert" +) + +func TestAccIBMSdsHostBasic(t *testing.T) { + var conf sdsaasv1.Host + nqn := "nqn.2014-06.org:9345" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMSdsHostDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckIBMSdsHostConfigBasic(nqn), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMSdsHostExists("ibm_sds_host.sds_host_instance", conf), + resource.TestCheckResourceAttr("ibm_sds_host.sds_host_instance", "nqn", nqn), + ), + }, + }, + }) +} + +// NOTE: This test maps a volume to a host. When the test attempts to cleanup the host, the host is still mapped to +// a volume. At this time, the API doesn't support the unmapping between the host and volume at the same time as a delete meaning +// this can't be completed solely through terraform. + +// func TestAccIBMSdsHostAllArgs(t *testing.T) { +// var conf sdsaasv1.Host +// nqn := "nqn.2014-06.org:9345" +// name := "terraform-host-name" +// nameUpdate := "terraform-host-name-updated" + +// resource.Test(t, resource.TestCase{ +// PreCheck: func() { acc.TestAccPreCheck(t) }, +// Providers: acc.TestAccProviders, +// CheckDestroy: testAccCheckIBMSdsHostDestroy, +// Steps: []resource.TestStep{ +// resource.TestStep{ +// Config: testAccCheckIBMSdsHostConfig(name, nqn), +// Check: resource.ComposeAggregateTestCheckFunc( +// testAccCheckIBMSdsHostExists("ibm_sds_host.sds_host_instance", conf), +// resource.TestCheckResourceAttr("ibm_sds_host.sds_host_instance", "name", name), +// resource.TestCheckResourceAttr("ibm_sds_host.sds_host_instance", "nqn", nqn), +// ), +// }, +// resource.TestStep{ +// Config: testAccCheckIBMSdsHostConfig(nameUpdate, nqn), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr("ibm_sds_host.sds_host_instance", "name", nameUpdate), +// resource.TestCheckResourceAttr("ibm_sds_host.sds_host_instance", "nqn", nqn), +// ), +// }, +// resource.TestStep{ +// ResourceName: "ibm_sds_host.sds_host", +// ImportState: true, +// ImportStateVerify: true, +// }, +// }, +// }) +// } + +func testAccCheckIBMSdsHostConfigBasic(nqn string) string { + return fmt.Sprintf(` + resource "ibm_sds_volume" "sds_volume_instance" { + capacity = 10 + name = "my-volume" + } + resource "ibm_sds_host" "sds_host_instance" { + nqn = "%s" + name = "my-host" + } + `, nqn) +} + +func testAccCheckIBMSdsHostConfig(name string, nqn string) string { + return fmt.Sprintf(` + + output "ibm_sds_volume" { + value = [ibm_sds_volume.sds_volume_instance] + description = "sds_volume resource instance" + } + resource "ibm_sds_volume" "sds_volume_instance" { + capacity = 10 + name = "my-volume" + } + + resource "ibm_sds_host" "sds_host_instance" { + name = "%s" + nqn = "%s" + volume_mappings { + volume_name = ibm_sds_volume.sds_volume_instance.name + volume_id = ibm_sds_volume.sds_volume_instance.id + } + } + `, name, nqn) +} + +func testAccCheckIBMSdsHostExists(n string, obj sdsaasv1.Host) resource.TestCheckFunc { + + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + sdsaasClient, err := acc.TestAccProvider.Meta().(conns.ClientSession).SdsaasV1() + if err != nil { + return err + } + + hostOptions := &sdsaasv1.HostOptions{} + + hostOptions.SetHostID(rs.Primary.ID) + + host, _, err := sdsaasClient.Host(hostOptions) + if err != nil { + return err + } + + obj = *host + return nil + } +} + +func testAccCheckIBMSdsHostDestroy(s *terraform.State) error { + sdsaasClient, err := acc.TestAccProvider.Meta().(conns.ClientSession).SdsaasV1() + if err != nil { + return err + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "ibm_sds_host" { + continue + } + + hostOptions := &sdsaasv1.HostOptions{} + + hostOptions.SetHostID(rs.Primary.ID) + + // Try to find the key + _, response, err := sdsaasClient.Host(hostOptions) + + if err == nil { + return fmt.Errorf("sds_host still exists: %s", rs.Primary.ID) + } else if response.StatusCode != 404 { + return fmt.Errorf("Error checking for sds_host (%s) has been destroyed: %s", rs.Primary.ID, err) + } + } + + return nil +} + +func TestResourceIBMSdsHostVolumeMappingReferenceToMap(t *testing.T) { + checkResult := func(result map[string]interface{}) { + networkInfoReferenceModel := make(map[string]interface{}) + networkInfoReferenceModel["gateway_ip"] = "testString" + networkInfoReferenceModel["port"] = int(38) + + storageIdentifiersReferenceModel := make(map[string]interface{}) + storageIdentifiersReferenceModel["id"] = "testString" + storageIdentifiersReferenceModel["namespace_id"] = int(38) + storageIdentifiersReferenceModel["namespace_uuid"] = "testString" + storageIdentifiersReferenceModel["network_info"] = []map[string]interface{}{networkInfoReferenceModel} + + model := make(map[string]interface{}) + model["status"] = "testString" + model["volume_id"] = "testString" + model["volume_name"] = "testString" + model["storage_identifiers"] = []map[string]interface{}{storageIdentifiersReferenceModel} + + assert.Equal(t, result, model) + } + + networkInfoReferenceModel := new(sdsaasv1.NetworkInfoReference) + networkInfoReferenceModel.GatewayIP = core.StringPtr("testString") + networkInfoReferenceModel.Port = core.Int64Ptr(int64(38)) + + storageIdentifiersReferenceModel := new(sdsaasv1.StorageIdentifiersReference) + storageIdentifiersReferenceModel.ID = core.StringPtr("testString") + storageIdentifiersReferenceModel.NamespaceID = core.Int64Ptr(int64(38)) + storageIdentifiersReferenceModel.NamespaceUUID = core.StringPtr("testString") + storageIdentifiersReferenceModel.NetworkInfo = []sdsaasv1.NetworkInfoReference{*networkInfoReferenceModel} + + model := new(sdsaasv1.VolumeMappingReference) + model.Status = core.StringPtr("testString") + model.VolumeID = core.StringPtr("testString") + model.VolumeName = core.StringPtr("testString") + model.StorageIdentifiers = storageIdentifiersReferenceModel + + result, err := sdsaas.ResourceIBMSdsHostVolumeMappingReferenceToMap(model) + assert.Nil(t, err) + checkResult(result) +} + +func TestResourceIBMSdsHostStorageIdentifiersReferenceToMap(t *testing.T) { + checkResult := func(result map[string]interface{}) { + networkInfoReferenceModel := make(map[string]interface{}) + networkInfoReferenceModel["gateway_ip"] = "testString" + networkInfoReferenceModel["port"] = int(38) + + model := make(map[string]interface{}) + model["id"] = "testString" + model["namespace_id"] = int(38) + model["namespace_uuid"] = "testString" + model["network_info"] = []map[string]interface{}{networkInfoReferenceModel} + + assert.Equal(t, result, model) + } + + networkInfoReferenceModel := new(sdsaasv1.NetworkInfoReference) + networkInfoReferenceModel.GatewayIP = core.StringPtr("testString") + networkInfoReferenceModel.Port = core.Int64Ptr(int64(38)) + + model := new(sdsaasv1.StorageIdentifiersReference) + model.ID = core.StringPtr("testString") + model.NamespaceID = core.Int64Ptr(int64(38)) + model.NamespaceUUID = core.StringPtr("testString") + model.NetworkInfo = []sdsaasv1.NetworkInfoReference{*networkInfoReferenceModel} + + result, err := sdsaas.ResourceIBMSdsHostStorageIdentifiersReferenceToMap(model) + assert.Nil(t, err) + checkResult(result) +} + +func TestResourceIBMSdsHostNetworkInfoReferenceToMap(t *testing.T) { + checkResult := func(result map[string]interface{}) { + model := make(map[string]interface{}) + model["gateway_ip"] = "testString" + model["port"] = int(38) + + assert.Equal(t, result, model) + } + + model := new(sdsaasv1.NetworkInfoReference) + model.GatewayIP = core.StringPtr("testString") + model.Port = core.Int64Ptr(int64(38)) + + result, err := sdsaas.ResourceIBMSdsHostNetworkInfoReferenceToMap(model) + assert.Nil(t, err) + checkResult(result) +} + +func TestResourceIBMSdsHostMapToVolumeMappingIdentity(t *testing.T) { + checkResult := func(result *sdsaasv1.VolumeMappingIdentity) { + model := new(sdsaasv1.VolumeMappingIdentity) + model.VolumeID = core.StringPtr("testString") + + assert.Equal(t, result, model) + } + + model := make(map[string]interface{}) + model["volume_id"] = "testString" + + result, err := sdsaas.ResourceIBMSdsHostMapToVolumeMappingIdentity(model) + assert.Nil(t, err) + checkResult(result) +} diff --git a/ibm/service/sdsaas/resource_ibm_sds_volume.go b/ibm/service/sdsaas/resource_ibm_sds_volume.go new file mode 100644 index 0000000000..29e406bbd4 --- /dev/null +++ b/ibm/service/sdsaas/resource_ibm_sds_volume.go @@ -0,0 +1,330 @@ +// Copyright IBM Corp. 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +/* + * IBM OpenAPI Terraform Generator Version: 3.96.0-d6dec9d7-20241008-212902 + */ + +package sdsaas + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate" + "github.com/IBM/go-sdk-core/v5/core" + "github.com/IBM/sds-go-sdk/sdsaasv1" +) + +func ResourceIBMSdsVolume() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceIBMSdsVolumeCreate, + ReadContext: resourceIBMSdsVolumeRead, + UpdateContext: resourceIBMSdsVolumeUpdate, + DeleteContext: resourceIBMSdsVolumeDelete, + Importer: &schema.ResourceImporter{}, + + Schema: map[string]*schema.Schema{ + "hostnqnstring": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.InvokeValidator("ibm_sds_volume", "hostnqnstring"), + Description: "The host nqn.", + }, + "capacity": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + Description: "The capacity of the volume (in gigabytes).", + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.InvokeValidator("ibm_sds_volume", "name"), + Description: "The name of the volume.", + }, + "host_mappings": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "List of host details that volume is mapped to.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Unique identifer of the host.", + }, + "host_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Unique name of the host.", + }, + "host_nqn": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The NQN of the host configured in customer's environment.", + }, + }, + }, + }, + "created_at": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The date and time that the volume was created.", + }, + "resource_type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The resource type of the volume.", + }, + "status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The current status of the volume.", + }, + "status_reasons": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "Reasons for the current status of the volume.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func ResourceIBMSdsVolumeValidator() *validate.ResourceValidator { + validateSchema := make([]validate.ValidateSchema, 0) + validateSchema = append(validateSchema, + validate.ValidateSchema{ + Identifier: "hostnqnstring", + ValidateFunctionIdentifier: validate.ValidateRegexpLen, + Type: validate.TypeString, + Optional: true, + Regexp: `^nqn\.\d{4}-\d{2}\.[a-z0-9-]+(?:\.[a-z0-9-]+)*:[a-zA-Z0-9.\-:]+$`, + MinValueLength: 1, + MaxValueLength: 200, + }, + validate.ValidateSchema{ + Identifier: "name", + ValidateFunctionIdentifier: validate.ValidateRegexpLen, + Type: validate.TypeString, + Optional: true, + Regexp: `^.*$`, + MinValueLength: 1, + MaxValueLength: 63, + }, + ) + + resourceValidator := validate.ResourceValidator{ResourceName: "ibm_sds_volume", Schema: validateSchema} + return &resourceValidator +} + +func resourceIBMSdsVolumeCreate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sdsaasClient, err := meta.(conns.ClientSession).SdsaasV1() + if err != nil { + tfErr := flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "create", "initialize-client") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + volumeCreateOptions := &sdsaasv1.VolumeCreateOptions{} + + volumeCreateOptions.SetCapacity(int64(d.Get("capacity").(int))) + if _, ok := d.GetOk("name"); ok { + volumeCreateOptions.SetName(d.Get("name").(string)) + } + if _, ok := d.GetOk("hostnqnstring"); ok { + volumeCreateOptions.SetHostnqnstring(d.Get("hostnqnstring").(string)) + } + + volume, _, err := sdsaasClient.VolumeCreateWithContext(context, volumeCreateOptions) + if err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("VolumeCreateWithContext failed: %s", err.Error()), "ibm_sds_volume", "create") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + d.SetId(*volume.ID) + + return resourceIBMSdsVolumeRead(context, d, meta) +} + +func resourceIBMSdsVolumeRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sdsaasClient, err := meta.(conns.ClientSession).SdsaasV1() + if err != nil { + tfErr := flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "initialize-client") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + volumeOptions := &sdsaasv1.VolumeOptions{} + + volumeOptions.SetVolumeID(d.Id()) + + volume, response, err := sdsaasClient.VolumeWithContext(context, volumeOptions) + if err != nil { + if response != nil && response.StatusCode == 404 { + d.SetId("") + return nil + } + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("VolumeWithContext failed: %s", err.Error()), "ibm_sds_volume", "read") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + if err = d.Set("capacity", flex.IntValue(volume.Capacity)); err != nil { + err = fmt.Errorf("Error setting capacity: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "set-capacity").GetDiag() + } + if !core.IsNil(volume.Name) { + if err = d.Set("name", volume.Name); err != nil { + err = fmt.Errorf("Error setting name: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "set-name").GetDiag() + } + } + if !core.IsNil(volume.HostMappings) { + hostMappings := []map[string]interface{}{} + for _, hostMappingsItem := range volume.HostMappings { + hostMappingsItemMap, err := ResourceIBMSdsVolumeHostMappingToMap(&hostMappingsItem) // #nosec G601 + if err != nil { + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "host_mappings-to-map").GetDiag() + } + hostMappings = append(hostMappings, hostMappingsItemMap) + } + if err = d.Set("host_mappings", hostMappings); err != nil { + err = fmt.Errorf("Error setting host_mappings: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "set-host_mappings").GetDiag() + } + } + if !core.IsNil(volume.CreatedAt) { + if err = d.Set("created_at", volume.CreatedAt); err != nil { + err = fmt.Errorf("Error setting created_at: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "set-created_at").GetDiag() + } + } + if !core.IsNil(volume.ResourceType) { + if err = d.Set("resource_type", volume.ResourceType); err != nil { + err = fmt.Errorf("Error setting resource_type: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "set-resource_type").GetDiag() + } + } + if !core.IsNil(volume.Status) { + if err = d.Set("status", volume.Status); err != nil { + err = fmt.Errorf("Error setting status: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "set-status").GetDiag() + } + } + if !core.IsNil(volume.StatusReasons) { + if err = d.Set("status_reasons", volume.StatusReasons); err != nil { + err = fmt.Errorf("Error setting status_reasons: %s", err) + return flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "read", "set-status_reasons").GetDiag() + } + } + + return nil +} + +func resourceIBMSdsVolumeUpdate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sdsaasClient, err := meta.(conns.ClientSession).SdsaasV1() + if err != nil { + tfErr := flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "update", "initialize-client") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + volumeUpdateOptions := &sdsaasv1.VolumeUpdateOptions{} + + volumeUpdateOptions.SetVolumeID(d.Id()) + + hasChange := false + + patchVals := &sdsaasv1.VolumePatch{} + if d.HasChange("capacity") { + newCapacity := int64(d.Get("capacity").(int)) + patchVals.Capacity = &newCapacity + hasChange = true + } + if d.HasChange("name") { + newName := d.Get("name").(string) + patchVals.Name = &newName + hasChange = true + } + + if hasChange { + // Fields with `nil` values are omitted from the generic map, + // so we need to re-add them to support removing arguments + // in merge-patch operations sent to the service. + volumeUpdateOptions.VolumePatch = ResourceIBMSdsVolumeVolumePatchAsPatch(patchVals, d) + + _, _, err = sdsaasClient.VolumeUpdateWithContext(context, volumeUpdateOptions) + if err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("VolumeUpdateWithContext failed: %s", err.Error()), "ibm_sds_volume", "update") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + } + + return resourceIBMSdsVolumeRead(context, d, meta) +} + +func resourceIBMSdsVolumeDelete(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sdsaasClient, err := meta.(conns.ClientSession).SdsaasV1() + if err != nil { + tfErr := flex.DiscriminatedTerraformErrorf(err, err.Error(), "ibm_sds_volume", "delete", "initialize-client") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + volumeDeleteOptions := &sdsaasv1.VolumeDeleteOptions{} + + volumeDeleteOptions.SetVolumeID(d.Id()) + + _, err = sdsaasClient.VolumeDeleteWithContext(context, volumeDeleteOptions) + if err != nil { + tfErr := flex.TerraformErrorf(err, fmt.Sprintf("VolumeDeleteWithContext failed: %s", err.Error()), "ibm_sds_volume", "delete") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() + } + + d.SetId("") + + return nil +} + +func ResourceIBMSdsVolumeHostMappingToMap(model *sdsaasv1.HostMapping) (map[string]interface{}, error) { + modelMap := make(map[string]interface{}) + if model.HostID != nil { + modelMap["host_id"] = *model.HostID + } + if model.HostName != nil { + modelMap["host_name"] = *model.HostName + } + if model.HostNqn != nil { + modelMap["host_nqn"] = *model.HostNqn + } + return modelMap, nil +} + +func ResourceIBMSdsVolumeVolumePatchAsPatch(patchVals *sdsaasv1.VolumePatch, d *schema.ResourceData) map[string]interface{} { + patch, _ := patchVals.AsPatch() + var path string + + path = "capacity" + if _, exists := d.GetOk(path); d.HasChange(path) && !exists { + patch["capacity"] = nil + } + path = "name" + if _, exists := d.GetOk(path); d.HasChange(path) && !exists { + patch["name"] = nil + } + + return patch +} diff --git a/ibm/service/sdsaas/resource_ibm_sds_volume_test.go b/ibm/service/sdsaas/resource_ibm_sds_volume_test.go new file mode 100644 index 0000000000..e42e0ef0b4 --- /dev/null +++ b/ibm/service/sdsaas/resource_ibm_sds_volume_test.go @@ -0,0 +1,202 @@ +// Copyright IBM Corp. 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package sdsaas_test + +import ( + "fmt" + "testing" + "time" + + "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" + + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/service/sdsaas" + "github.com/IBM/go-sdk-core/v5/core" + "github.com/stretchr/testify/assert" + "github.com/IBM/sds-go-sdk/sdsaasv1" +) + +func TestAccIBMSdsVolumeBasic(t *testing.T) { + var conf sdsaasv1.Volume + capacity := fmt.Sprintf("%d", acctest.RandIntRange(1, 5)) + name := "terraform-test-1" + capacityUpdate := fmt.Sprintf("%d", acctest.RandIntRange(6, 10)) + nameUpdate := "terraform-test-name-updated" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMSdsVolumeDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckIBMSdsVolumeConfigBasic(capacity, name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMSdsVolumeExists("ibm_sds_volume.sds_volume_instance", conf), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "capacity", capacity), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "name", name), + ), + }, + resource.TestStep{ + Config: testAccCheckIBMSdsVolumeConfigBasic(capacityUpdate, name), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "capacity", capacityUpdate), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "name", name), + ), + }, + resource.TestStep{ + Config: testAccCheckIBMSdsVolumeConfigBasic(capacityUpdate, nameUpdate), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "capacity", capacityUpdate), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "name", nameUpdate), + ), + }, + }, + }) +} + +func TestAccIBMSdsVolumeAllArgs(t *testing.T) { + var conf sdsaasv1.Volume + hostnqnstring := "nqn.2014-06.org:9345" + capacity := fmt.Sprintf("%d", acctest.RandIntRange(1, 5)) + name := "terraform-test-name-1" + capacityUpdate := fmt.Sprintf("%d", acctest.RandIntRange(6, 10)) + nameUpdate := "terraform-test-name-updated" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMSdsVolumeDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckIBMSdsVolumeConfig(hostnqnstring, capacity, name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMSdsVolumeExists("ibm_sds_volume.sds_volume_instance", conf), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "hostnqnstring", hostnqnstring), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "capacity", capacity), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "name", name), + ), + }, + resource.TestStep{ + Config: testAccCheckIBMSdsVolumeConfig(hostnqnstring, capacityUpdate, name), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "hostnqnstring", hostnqnstring), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "capacity", capacityUpdate), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "name", name), + ), + }, + resource.TestStep{ + Config: testAccCheckIBMSdsVolumeConfig(hostnqnstring, capacityUpdate, nameUpdate), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "hostnqnstring", hostnqnstring), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "capacity", capacityUpdate), + resource.TestCheckResourceAttr("ibm_sds_volume.sds_volume_instance", "name", nameUpdate), + ), + }, + resource.TestStep{ + ResourceName: "ibm_sds_volume.sds_volume_instance", + ImportState: true, + }, + }, + }) +} + +func testAccCheckIBMSdsVolumeConfigBasic(capacity string, name string) string { + return fmt.Sprintf(` + resource "ibm_sds_volume" "sds_volume_instance" { + capacity = %s + name = "%s" + } + `, capacity, name) +} + +func testAccCheckIBMSdsVolumeConfig(hostnqnstring string, capacity string, name string) string { + return fmt.Sprintf(` + + resource "ibm_sds_volume" "sds_volume_instance" { + hostnqnstring = "%s" + capacity = %s + name = "%s" + } + `, hostnqnstring, capacity, name) +} + +func testAccCheckIBMSdsVolumeExists(n string, obj sdsaasv1.Volume) resource.TestCheckFunc { + + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + sdsaasClient, err := acc.TestAccProvider.Meta().(conns.ClientSession).SdsaasV1() + if err != nil { + return err + } + + volumeOptions := &sdsaasv1.VolumeOptions{} + + volumeOptions.SetVolumeID(rs.Primary.ID) + + volume, _, err := sdsaasClient.Volume(volumeOptions) + if err != nil { + return err + } + + obj = *volume + return nil + } +} + +func testAccCheckIBMSdsVolumeDestroy(s *terraform.State) error { + sdsaasClient, err := acc.TestAccProvider.Meta().(conns.ClientSession).SdsaasV1() + if err != nil { + return err + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "ibm_sds_volume" { + continue + } + + volumeOptions := &sdsaasv1.VolumeOptions{} + + volumeOptions.SetVolumeID(rs.Primary.ID) + + // Give time for the volume to fully delete before checking its state + time.Sleep(5 * time.Second) + + // Try to find the key + _, response, err := sdsaasClient.Volume(volumeOptions) + + if err == nil { + return fmt.Errorf("sds_volume still exists: %s", rs.Primary.ID) + } else if response.StatusCode != 404 { + return fmt.Errorf("Error checking for sds_volume (%s) has been destroyed: %s", rs.Primary.ID, err) + } + } + + return nil +} + +func TestResourceIBMSdsVolumeHostMappingToMap(t *testing.T) { + checkResult := func(result map[string]interface{}) { + model := make(map[string]interface{}) + model["host_id"] = "testString" + model["host_name"] = "testString" + model["host_nqn"] = "testString" + + assert.Equal(t, result, model) + } + + model := new(sdsaasv1.HostMapping) + model.HostID = core.StringPtr("testString") + model.HostName = core.StringPtr("testString") + model.HostNqn = core.StringPtr("testString") + + result, err := sdsaas.ResourceIBMSdsVolumeHostMappingToMap(model) + assert.Nil(t, err) + checkResult(result) +} diff --git a/website/docs/r/sds_host.html.markdown b/website/docs/r/sds_host.html.markdown new file mode 100644 index 0000000000..19910c17ac --- /dev/null +++ b/website/docs/r/sds_host.html.markdown @@ -0,0 +1,82 @@ +--- +layout: "ibm" +page_title: "IBM : ibm_sds_host" +description: |- + Manages sds_host. +subcategory: "sdsaas" +--- + +# ibm_sds_host + +Create, update, and delete sds_hosts with this resource. + +## Example Usage + +```hcl +resource "ibm_sds_host" "sds_host_instance" { + name = "my-host" + nqn = "nqn.2014-06.org:9345" + volume_mappings { + status = "status" + volume_id = "volume_id" + volume_name = "volume_name" + storage_identifiers { + id = "id" + namespace_id = 1 + namespace_uuid = "namespace_uuid" + network_info { + gateway_ip = "gateway_ip" + port = 1 + } + } + } +} +``` + +## Argument Reference + +You can specify the following arguments for this resource. + +* `name` - (Optional, String) The name for this host. The name must not be used by another host. If unspecified, the name will be a hyphenated list of randomly-selected words. + * Constraints: The maximum length is `64` characters. The minimum length is `1` character. +* `nqn` - (Required, String) The NQN of the host configured in customer's environment. +* `volume_mappings` - (Optional, List) The host-to-volume map. + * Constraints: The maximum length is `100` items. The minimum length is `0` items. +Nested schema for **volume_mappings**: + * `status` - (Optional, String) The current status of a volume/host mapping attempt. + * `storage_identifiers` - (Optional, List) Storage network and ID information associated with a volume/host mapping. + Nested schema for **storage_identifiers**: + * `id` - (Optional, String) The storage ID associated with a volume/host mapping. + * `namespace_id` - (Optional, Integer) The namespace ID associated with a volume/host mapping. + * `namespace_uuid` - (Optional, String) The namespace UUID associated with a volume/host mapping. + * `network_info` - (Optional, List) The IP and port for volume/host mappings. + * Constraints: The maximum length is `200` items. The minimum length is `1` item. + Nested schema for **network_info**: + * `gateway_ip` - (Optional, String) Network information for volume/host mappings. + * `port` - (Optional, Integer) Network information for volume/host mappings. + * `volume_id` - (Required, String) The volume ID that needs to be mapped with a host. + * `volume_name` - (Required, String) The volume name. + +## Attribute Reference + +After your resource is created, you can read values from the listed arguments and the following attributes. + +* `id` - The unique identifier of the sds_host. +* `created_at` - (String) The date and time that the host was created. +* `service_instance_id` - (String) The service instance ID this host should be created in. + * Constraints: The maximum length is `64` characters. The minimum length is `1` character. + + +## Import + +You can import the `ibm_sds_host` resource by using `id`. The unique identifier for this host. + +# Syntax +
+$ terraform import ibm_sds_host.sds_host <id> ++ +# Example +``` +$ terraform import ibm_sds_host.sds_host 1a6b7274-678d-4dfb-8981-c71dd9d4daa5 +``` diff --git a/website/docs/r/sds_volume.html.markdown b/website/docs/r/sds_volume.html.markdown new file mode 100644 index 0000000000..fdef20d734 --- /dev/null +++ b/website/docs/r/sds_volume.html.markdown @@ -0,0 +1,57 @@ +--- +layout: "ibm" +page_title: "IBM : ibm_sds_volume" +description: |- + Manages sds_volume. +subcategory: "sdsaas" +--- + +# ibm_sds_volume + +Create, update, and delete sds_volumes with this resource. + +## Example Usage + +```hcl +resource "ibm_sds_volume" "sds_volume_instance" { + capacity = 10 + hostnqnstring = "nqn.2024-07.org:1234" + name = "my-volume" +} +``` + +## Argument Reference + +You can specify the following arguments for this resource. + +* `capacity` - (Required, Integer) The capacity of the volume (in gigabytes). +* `hostnqnstring` - (Optional, String) The host nqn. + * Constraints: The maximum length is `200` characters. The minimum length is `1` character. The value must match regular expression `/^nqn\\.\\d{4}-\\d{2}\\.[a-z0-9-]+(?:\\.[a-z0-9-]+)*:[a-zA-Z0-9.\\-:]+$/`. +* `name` - (Optional, String) The name of the volume. + +## Attribute Reference + +After your resource is created, you can read values from the listed arguments and the following attributes. + +* `id` - The unique identifier of the sds_volume. +* `created_at` - (String) The date and time that the volume was created. +* `host_mappings` - (List) List of host details that volume is mapped to. + * Constraints: The maximum length is `200` items. The minimum length is `0` items. +Nested schema for **host_mappings**: + * `host_id` - (String) Unique identifer of the host. + * `host_name` - (String) Unique name of the host. + * `host_nqn` - (String) The NQN of the host configured in customer's environment. +* `resource_type` - (String) The resource type of the volume. +* `status` - (String) The current status of the volume. +* `status_reasons` - (List) Reasons for the current status of the volume. + * Constraints: The maximum length is `200` items. The minimum length is `0` items. + + +## Import + +You can import the `ibm_sds_volume` resource by using `id`. The volume profile id. + +# Syntax +
+$ terraform import ibm_sds_volume.sds_volume <id> +