diff --git a/docs/index.md b/docs/index.md index 805917f57..e46b15cf9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -174,7 +174,7 @@ For questions or issues with the provider, open up an issue in the provider GitH - `api_key` (String, Sensitive) The Spectro Cloud API key. Can also be set with the `SPECTROCLOUD_APIKEY` environment variable. - `host` (String) The Spectro Cloud API host url. Can also be set with the `SPECTROCLOUD_HOST` environment variable. Defaults to https://api.spectrocloud.com -- `ignore_insecure_tls_error` (Boolean) Ignore insecure TLS errors for Spectro Cloud API endpoints. Defaults to false. +- `ignore_insecure_tls_error` (Boolean) Ignore insecure TLS errors for Spectro Cloud API endpoints. ⚠️ WARNING: Setting this to true disables SSL certificate verification and makes connections vulnerable to man-in-the-middle attacks. Only use this in development/testing environments or when connecting to self-signed certificates in trusted networks. Defaults to false. - `project_name` (String) The Palette project the provider will target. If no value is provided, the `Default` Palette project is used. The default value is `Default`. - `retry_attempts` (Number) Number of retry attempts. Can also be set with the `SPECTROCLOUD_RETRY_ATTEMPTS` environment variable. Defaults to 10. - `trace` (Boolean) Enable HTTP request tracing. Can also be set with the `SPECTROCLOUD_TRACE` environment variable. To enable Terraform debug logging, set `TF_LOG=DEBUG`. Visit the Terraform documentation to learn more about Terraform [debugging](https://developer.hashicorp.com/terraform/plugin/log/managing). diff --git a/docs/resources/appliance.md b/docs/resources/appliance.md index 83de04f03..ee9110bc7 100644 --- a/docs/resources/appliance.md +++ b/docs/resources/appliance.md @@ -21,6 +21,21 @@ resource "spectrocloud_appliance" "appliance" { } ``` +## Import + +Appliances can be imported using the appliance UID. This is a project-level resource. + +```bash +terraform import spectrocloud_appliance.example +``` + +Where `` is the appliance UID. + +The import will automatically populate all configuration fields from the Spectro Cloud API, including the UID, tags, and tunnel configuration settings. The `wait` field will be set to `false` by default for imported appliances. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a project-level resource, ensure your provider is configured with appropriate project-level credentials and access. + + ## Schema diff --git a/docs/resources/application.md b/docs/resources/application.md index eb48d6f97..e1c90ed8e 100644 --- a/docs/resources/application.md +++ b/docs/resources/application.md @@ -32,6 +32,16 @@ resource "spectrocloud_application" "application" { ``` +## Import + +Applications can be imported using their UID. The import will automatically detect the cluster context. + +```bash +terraform import spectrocloud_application.example 63444eb70807dc2c14a8ad59 +``` + +Note: During import, the application's configuration will be automatically populated from the Spectro Cloud API, including the correct cluster context. + ## Schema diff --git a/docs/resources/application_profile.md b/docs/resources/application_profile.md index 120fd4fcb..c18085eb9 100644 --- a/docs/resources/application_profile.md +++ b/docs/resources/application_profile.md @@ -349,6 +349,21 @@ resource "spectrocloud_application_profile" "app_profile_all_tiers" { ``` +``` +## Import + +# terraform import spectrocloud_application_profile.app_profile_all_tiers "profile_uid_here" +# +# Where: +# - profile_uid_here is the unique identifier of the application profile +# +# To import using import block: +# import { +# to = spectrocloud_application_profile.app_profile_all_tiers +# id = "profile_uid_here" +# } +``` + ## Schema diff --git a/docs/resources/backup_storage_location.md b/docs/resources/backup_storage_location.md index d31358b7a..649499519 100644 --- a/docs/resources/backup_storage_location.md +++ b/docs/resources/backup_storage_location.md @@ -44,6 +44,33 @@ resource "spectrocloud_backup_storage_location" "bsl2" { } ``` +## Import + +Backup Storage Locations can be imported using either a simple ID format or with explicit context specification. This resource supports both project and tenant contexts. + +### Simple Import (defaults to project context) + +```bash +terraform import spectrocloud_backup_storage_location.example :project +``` + +### Context-specific Import + +```bash +terraform import spectrocloud_backup_storage_location.example :project +terraform import spectrocloud_backup_storage_location.example :tenant +``` + +Where: +- `` is the Backup Storage Location ID +- `project` or `tenant` specifies the context where the backup storage location exists + +**Import behavior:** +- If no context is specified, it defaults to `project` context +- If the resource is not found in the specified context, the import will automatically try the other context +- The import will automatically populate all configuration fields from the Spectro Cloud API, including the correct context, storage provider, and all provider-specific settings + +After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. diff --git a/docs/resources/cloudaccount_azure.md b/docs/resources/cloudaccount_azure.md index b5e92b572..d5bca362c 100644 --- a/docs/resources/cloudaccount_azure.md +++ b/docs/resources/cloudaccount_azure.md @@ -34,12 +34,13 @@ resource "spectrocloud_cloudaccount_azure" "azure-1" { ### Optional - `cloud` (String) The Azure partition in which the cloud account is located. -Can be 'AzurePublicCloud' for standard Azure regions or 'AzureUSGovernmentCloud' for Azure GovCloud (US) regions. +Can be 'AzurePublicCloud' for standard Azure regions or 'AzureUSGovernmentCloud' for Azure GovCloud (US) regions or 'AzureUSSecretCloud' for Azure Secret Cloud regions. Default is 'AzurePublicCloud'. - `context` (String) The context of the Azure configuration. Defaults to `project`. If the `project` context is specified, the project name will sourced from the provider configuration parameter [`project_name`](https://registry.terraform.io/providers/spectrocloud/spectrocloud/latest/docs#schema). - `disable_properties_request` (Boolean) Disable properties request. This is a boolean value that indicates whether to disable properties request or not. If not specified, the default value is `false`. - `private_cloud_gateway_id` (String) ID of the private cloud gateway. This is the ID of the private cloud gateway that is used to connect to the private cluster endpoint. - `tenant_name` (String) The name of the tenant. This is the name of the tenant that is used to connect to the Azure cloud. +- `tls_cert` (String) TLS certificate for authentication. This field is only allowed when cloud is set to 'AzureUSSecretCloud'. ### Read-Only diff --git a/docs/resources/cluster_group.md b/docs/resources/cluster_group.md index 5d5594e25..bf9fcb0ec 100644 --- a/docs/resources/cluster_group.md +++ b/docs/resources/cluster_group.md @@ -40,6 +40,25 @@ resource "spectrocloud_cluster_group" "cg" { ``` +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) +to import the resource spectrocloud_cluster_group by using its `id` with the Palette `context` separated by a colon. For example: + +```terraform +import { + to = spectrocloud_cluster_group.example + id = "example_id:context" +} +``` + +Using `terraform import`, import the cluster group using the `id` colon separated with `context`. For example: + +```console +terraform import spectrocloud_cluster_group.example example_id:project +``` + +Refer to the [Import section](/docs#import) to learn more. ## Schema diff --git a/docs/resources/cluster_maas.md b/docs/resources/cluster_maas.md index 45e097ad1..9b628f825 100644 --- a/docs/resources/cluster_maas.md +++ b/docs/resources/cluster_maas.md @@ -169,6 +169,10 @@ Required: - `domain` (String) Domain name in which the cluster to be provisioned. +Optional: + +- `enable_lxd_vm` (Boolean) Whether to enable LXD VM. Default is `false`. + ### Nested Schema for `machine_pool` @@ -179,6 +183,7 @@ Required: - `count` (Number) Number of nodes in the machine pool. - `instance_type` (Block List, Min: 1, Max: 1) (see [below for nested schema](#nestedblock--machine_pool--instance_type)) - `name` (String) Name of the machine pool. +- `network` (Block List, Min: 1, Max: 1) (see [below for nested schema](#nestedblock--machine_pool--network)) - `placement` (Block List, Min: 1, Max: 1) (see [below for nested schema](#nestedblock--machine_pool--placement)) Optional: @@ -193,6 +198,7 @@ Optional: - `node_tags` (Set of String) Node tags to dynamically place nodes in a pool by using MAAS automatic tags. Specify the tag values that you want to apply to all nodes in the node pool. - `taints` (Block List) (see [below for nested schema](#nestedblock--machine_pool--taints)) - `update_strategy` (String) Update strategy for the machine pool. Valid values are `RollingUpdateScaleOut` and `RollingUpdateScaleIn`. +- `use_lxd_vm` (Boolean) Whether to use LXD VM. Default is `false`. ### Nested Schema for `machine_pool.instance_type` @@ -203,6 +209,19 @@ Required: - `min_memory_mb` (Number) Minimum memory in MB required for the machine pool node. + +### Nested Schema for `machine_pool.network` + +Required: + +- `network_name` (String) The name of the network in which VMs are created/located. + +Optional: + +- `parent_pool_uid` (String) The UID of the parent pool which allocates IPs for this IPPool. +- `static_ip` (Boolean) Whether to use static IP. Default is `false`. + + ### Nested Schema for `machine_pool.placement` diff --git a/docs/resources/datavolume.md b/docs/resources/datavolume.md index 51c78eb47..9c41de735 100644 --- a/docs/resources/datavolume.md +++ b/docs/resources/datavolume.md @@ -87,7 +87,6 @@ Read-Only: - `generation` (Number) A sequence number representing a specific generation of the desired state. - `resource_version` (String) An opaque value that represents the internal version of this DataVolume that can be used by clients to determine when DataVolume has changed. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency -- `self_link` (String) A URL representing this DataVolume. - `uid` (String) The unique in time and space value for this DataVolume. More info: http://kubernetes.io/docs/user-guide/identifiers#uids diff --git a/docs/resources/macro.md b/docs/resources/macro.md deleted file mode 100644 index 9c3033020..000000000 --- a/docs/resources/macro.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -page_title: "spectrocloud_macro Resource - terraform-provider-spectrocloud" -subcategory: "" -description: |- - A resource for creating and managing service output variables and macro. (Deprecated) ---- - -# spectrocloud_macro (Resource, Deprecated) - - A resource for creating and managing service output variables and macro. (Deprecated) - -## Example Usage - -```terraform -resource "spectrocloud_macro" "project_macro" { - name = "project1" - value = "project_val2" - project = "Default" -} - -resource "spectrocloud_macro" "tenant_macro" { - name = "tenant1" - value = "tenant_val1" -} -``` - - - -## Schema - -### Required - -- `name` (String) The name of the macro or service variable output. -- `value` (String) The value that the macro or service output variable will contain. - -### Optional - -- `project` (String) The Spectro Cloud project name. -- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) - -### Read-Only - -- `id` (String) The ID of this resource. - - -### Nested Schema for `timeouts` - -Optional: - -- `create` (String) -- `delete` (String) -- `update` (String) \ No newline at end of file diff --git a/docs/resources/privatecloudgateway_dns_map.md b/docs/resources/privatecloudgateway_dns_map.md index 2f398386b..74e04acbd 100644 --- a/docs/resources/privatecloudgateway_dns_map.md +++ b/docs/resources/privatecloudgateway_dns_map.md @@ -27,6 +27,23 @@ An example of creating an DNS Map for a Private Cloud Gateway using a search dom } ``` +## Import + +Private Cloud Gateway DNS maps can be imported using the composite ID format: `pcg_id:dns_map_id`. This is a tenant-level resource. + +```bash +terraform import spectrocloud_privatecloudgateway_dns_map.example : +``` + +Where: +- `` is the Private Cloud Gateway ID +- `` is the DNS map ID + +The import will automatically populate all configuration fields from the Spectro Cloud API, including the associated Private Cloud Gateway ID, search domain name, data center, and network. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a tenant-level resource, ensure your provider is configured with appropriate tenant-level credentials and access. + + ## Schema diff --git a/docs/resources/privatecloudgateway_ippool.md b/docs/resources/privatecloudgateway_ippool.md index 7d0a39808..107544a48 100644 --- a/docs/resources/privatecloudgateway_ippool.md +++ b/docs/resources/privatecloudgateway_ippool.md @@ -52,6 +52,22 @@ An example of creating an IP Pool for a Private Cloud Gateway using a subnet of } ``` +## Import + +Private Cloud Gateway IP pools can be imported using the composite ID format: `pcg_id:ippool_id`. This is a tenant-level resource. + +```bash +terraform import spectrocloud_privatecloudgateway_ippool.example : +``` + +Where: +- `` is the Private Cloud Gateway ID +- `` is the IP Pool ID + +The import will automatically populate all configuration fields from the Spectro Cloud API. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a tenant-level resource, ensure your provider is configured with appropriate tenant-level credentials and access. + ## Schema diff --git a/docs/resources/registry_helm.md b/docs/resources/registry_helm.md index bc4e412f1..eacad701e 100644 --- a/docs/resources/registry_helm.md +++ b/docs/resources/registry_helm.md @@ -24,6 +24,20 @@ resource "spectrocloud_registry_helm" "r1" { } ``` +## Import + +Helm registries can be imported using the registry ID. This is a tenant-level resource. + +```bash +terraform import spectrocloud_registry_helm.example +``` + +Where `` is the Helm registry ID. + +The import will automatically populate all configuration fields from the Spectro Cloud API, including the name, endpoint, privacy setting, and authentication credentials. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a tenant-level resource, ensure your provider is configured with appropriate tenant-level credentials and access. + ## Schema diff --git a/docs/resources/registry_oci.md b/docs/resources/registry_oci.md index fa2f9025c..ddc81ec71 100644 --- a/docs/resources/registry_oci.md +++ b/docs/resources/registry_oci.md @@ -25,6 +25,20 @@ resource "spectrocloud_registry_oci" "r1" { } ``` +## Import + +OCI registries can be imported using the registry ID. This is a tenant-level resource. + +```bash +terraform import spectrocloud_registry_oci.example +``` + +Where `` is the OCI registry ID. + +The import will automatically detect whether the registry is an ECR or basic type and populate all configuration fields from the Spectro Cloud API. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a tenant-level resource, ensure your provider is configured with appropriate tenant-level credentials and access. + ## Schema @@ -72,7 +86,7 @@ Optional: Optional: - `certificate` (String) Specifies the TLS certificate used for secure communication. Required for enabling SSL/TLS encryption. -- `insecure_skip_verify` (Boolean) Disables TLS certificate verification when set to true. Use with caution as it may expose connections to security risks. +- `insecure_skip_verify` (Boolean) Disables TLS certificate verification when set to true. ⚠️ WARNING: Setting this to true disables SSL certificate verification and makes connections vulnerable to man-in-the-middle attacks. Only use this when connecting to registries with self-signed certificates in trusted networks. diff --git a/docs/resources/ssh_key.md b/docs/resources/ssh_key.md index 522904755..8574bdcc9 100644 --- a/docs/resources/ssh_key.md +++ b/docs/resources/ssh_key.md @@ -58,6 +58,34 @@ resource "spectrocloud_ssh_key" "primary_key_1" { } ``` +## Import + +SSH keys can be imported using either a simple ID format or with explicit context specification. This resource supports both project and tenant contexts. + +### Simple Import (defaults to project context) + +```bash +terraform import spectrocloud_ssh_key.example +``` + +### Context-specific Import + +```bash +terraform import spectrocloud_ssh_key.example :project +terraform import spectrocloud_ssh_key.example :tenant +``` + +Where: +- `` is the SSH key ID +- `project` or `tenant` specifies the context where the SSH key exists + +**Import behavior:** +- If no context is specified, it defaults to `project` context +- If the resource is not found in the specified context, the import will automatically try the other context +- The import will automatically populate all configuration fields from the Spectro Cloud API, including the correct context, name, and SSH key content + +After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + ## Schema diff --git a/docs/resources/sso.md b/docs/resources/sso.md index 51cc21e0b..e3f328a65 100644 --- a/docs/resources/sso.md +++ b/docs/resources/sso.md @@ -102,7 +102,7 @@ Optional: - `default_team_ids` (Set of String) A set of default team IDs assigned to users. - `identity_provider_ca_certificate` (String) Certificate authority (CA) certificate for the identity provider. -- `insecure_skip_tls_verify` (Boolean) Boolean to skip TLS verification for identity provider communication. +- `insecure_skip_tls_verify` (Boolean) Boolean to skip TLS verification for identity provider communication. ⚠️ WARNING: Setting this to true disables SSL certificate verification and makes connections vulnerable to man-in-the-middle attacks. Only use this when connecting to identity providers with self-signed certificates in trusted networks. - `user_info_endpoint` (Block List, Max: 1) To allow Palette to query the OIDC userinfo endpoint using the provided Issuer URL. Palette will first attempt to retrieve role and group information from userInfo endpoint. If unavailable, Palette will fall back to using Required Claims as specified above. Use the following fields to specify what Required Claims Palette will include when querying the userinfo endpoint. (see [below for nested schema](#nestedblock--oidc--user_info_endpoint)) Read-Only: diff --git a/docs/resources/virtual_machine.md b/docs/resources/virtual_machine.md index 9772e9805..282e93726 100644 --- a/docs/resources/virtual_machine.md +++ b/docs/resources/virtual_machine.md @@ -819,7 +819,6 @@ Read-Only: - `generation` (Number) A sequence number representing a specific generation of the desired state. - `resource_version` (String) An opaque value that represents the internal version of this DataVolume that can be used by clients to determine when DataVolume has changed. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency -- `self_link` (String) A URL representing this DataVolume. - `uid` (String) The unique in time and space value for this DataVolume. More info: http://kubernetes.io/docs/user-guide/identifiers#uids diff --git a/examples/resources/spectrocloud_alert/resource.tf b/examples/resources/spectrocloud_alert/resource.tf index 410bf7bb3..1060dbd6e 100644 --- a/examples/resources/spectrocloud_alert/resource.tf +++ b/examples/resources/spectrocloud_alert/resource.tf @@ -28,3 +28,10 @@ resource "spectrocloud_alert" "alert_http" { type = "http" alert_all_users = true } + +# Import example: +# terraform import spectrocloud_alert.alert_email "alertUid:ClusterHealth" +# +# Where: +# - alertUid is the unique identifier of the alert +# - ClusterHealth is the component type diff --git a/examples/resources/spectrocloud_application_profile/resource.tf b/examples/resources/spectrocloud_application_profile/resource.tf index 8be341c39..98e8f70df 100644 --- a/examples/resources/spectrocloud_application_profile/resource.tf +++ b/examples/resources/spectrocloud_application_profile/resource.tf @@ -192,3 +192,15 @@ resource "spectrocloud_application_profile" "app_profile_all_tiers" { } } } + +# Import example: +# terraform import spectrocloud_application_profile.app_profile_all_tiers "profile_uid_here" +# +# Where: +# - profile_uid_here is the unique identifier of the application profile +# +# To import using import block: +# import { +# to = spectrocloud_application_profile.app_profile_all_tiers +# id = "profile_uid_here" +# } diff --git a/examples/resources/spectrocloud_cloudaccount_azure/resource.tf b/examples/resources/spectrocloud_cloudaccount_azure/resource.tf index 5f88cf8a6..3cd0d058f 100644 --- a/examples/resources/spectrocloud_cloudaccount_azure/resource.tf +++ b/examples/resources/spectrocloud_cloudaccount_azure/resource.tf @@ -31,3 +31,19 @@ resource "spectrocloud_cloudaccount_azure" "azure_government" { cloud = "AzureUSGovernmentCloud" context = "project" } + +# Example 3: Azure US Secret Cloud account with TLS certificate +resource "spectrocloud_cloudaccount_azure" "azure_secret" { + name = "azure-secret-account" + azure_tenant_id = var.azure_secret_tenant_id + azure_client_id = var.azure_secret_client_id + azure_client_secret = var.azure_secret_client_secret + + cloud = "AzureUSSecretCloud" + context = "project" + + # TLS certificate is only allowed when cloud is set to "AzureUSSecretCloud" + tls_cert = var.azure_secret_tls_cert + + tenant_name = "Secret Cloud Tenant" +} \ No newline at end of file diff --git a/examples/resources/spectrocloud_cloudaccount_azure/variables.tf b/examples/resources/spectrocloud_cloudaccount_azure/variables.tf index 2c0996aab..c9a0a1e28 100644 --- a/examples/resources/spectrocloud_cloudaccount_azure/variables.tf +++ b/examples/resources/spectrocloud_cloudaccount_azure/variables.tf @@ -68,3 +68,10 @@ variable "azure_secret_client_secret" { default = "" sensitive = true } + +variable "azure_secret_tls_cert" { + description = "TLS certificate for Azure US Secret Cloud authentication" + type = string + default = "" + sensitive = true +} \ No newline at end of file diff --git a/examples/resources/spectrocloud_cluster_group/resource.tf b/examples/resources/spectrocloud_cluster_group/resource.tf index 0515ce7fa..774e44a5b 100644 --- a/examples/resources/spectrocloud_cluster_group/resource.tf +++ b/examples/resources/spectrocloud_cluster_group/resource.tf @@ -19,4 +19,12 @@ resource "spectrocloud_cluster_group" "cg" { host_dns = "*.dev.spectrocloud.com" } -} \ No newline at end of file +} + +# terraform import spectrocloud_cluster_group.cg "cluster_group_id:tenant" + +# Or using the import block (Terraform 1.5+): +# import { +# to = spectrocloud_cluster_group.cg +# id = "cluster_group_id:context" +# } \ No newline at end of file diff --git a/examples/resources/spectrocloud_macro/providers.tf b/examples/resources/spectrocloud_macro/providers.tf deleted file mode 100644 index d33df6799..000000000 --- a/examples/resources/spectrocloud_macro/providers.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - spectrocloud = { - version = ">= 0.11.0" - source = "spectrocloud/spectrocloud" - } - } -} - -provider "spectrocloud" { - host = var.sc_host - api_key = var.sc_api_key - project_name = var.sc_project_name -} diff --git a/examples/resources/spectrocloud_macro/resource.tf b/examples/resources/spectrocloud_macro/resource.tf deleted file mode 100644 index 381bf45d3..000000000 --- a/examples/resources/spectrocloud_macro/resource.tf +++ /dev/null @@ -1,10 +0,0 @@ -resource "spectrocloud_macro" "project_macro" { - name = "project1" - value = "project_val2" - project = "Default" -} - -resource "spectrocloud_macro" "tenant_macro" { - name = "tenant1" - value = "tenant_val1" -} \ No newline at end of file diff --git a/examples/resources/spectrocloud_macro/terraform.template.tfvars b/examples/resources/spectrocloud_macro/terraform.template.tfvars deleted file mode 100644 index c7e9d50b8..000000000 --- a/examples/resources/spectrocloud_macro/terraform.template.tfvars +++ /dev/null @@ -1,4 +0,0 @@ -# Spectro Cloud credentials -sc_host = "{Enter Spectro Cloud API Host}" #e.g: api.spectrocloud.com (for SaaS) -sc_api_key = "{Enter Spectro Cloud API Key}" -sc_project_name = "{Enter Spectro Cloud Project Name}" #e.g: Default \ No newline at end of file diff --git a/examples/resources/spectrocloud_macro/variables.tf b/examples/resources/spectrocloud_macro/variables.tf deleted file mode 100644 index 60a70bf31..000000000 --- a/examples/resources/spectrocloud_macro/variables.tf +++ /dev/null @@ -1,13 +0,0 @@ -variable "sc_host" { - description = "Spectro Cloud Endpoint" - default = "api.spectrocloud.com" -} - -variable "sc_api_key" { - description = "Spectro Cloud API key" -} - -variable "sc_project_name" { - description = "Spectro Cloud Project (e.g: Default)" - default = "Default" -} diff --git a/go.mod b/go.mod index 3619b8f30..50232e98f 100644 --- a/go.mod +++ b/go.mod @@ -1,32 +1,30 @@ module github.com/spectrocloud/terraform-provider-spectrocloud -go 1.23.10 - -toolchain go1.24.0 +go 1.24.0 require ( - github.com/Masterminds/semver/v3 v3.1.1 + github.com/Masterminds/semver/v3 v3.2.0 github.com/go-openapi/strfmt v0.23.0 - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/gorilla/mux v1.8.0 - github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 + github.com/hashicorp/go-cty v1.5.0 github.com/hashicorp/terraform-plugin-docs v0.16.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 github.com/robfig/cron v1.2.0 - github.com/spectrocloud/palette-sdk-go v0.0.0-20250829121241-06ad6c5fc7b1 + github.com/spectrocloud/palette-sdk-go v0.0.0-20250907041328-61dd91d616cc github.com/stretchr/testify v1.10.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible - k8s.io/api v0.23.5 - k8s.io/apimachinery v0.23.5 + k8s.io/api v0.34.0 + k8s.io/apimachinery v0.34.0 kubevirt.io/api v0.59.0 kubevirt.io/containerized-data-importer-api v1.56.0 ) require ( github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect @@ -34,7 +32,8 @@ require ( github.com/bgentry/speakeasy v0.1.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -48,34 +47,34 @@ require ( github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.5.1 // indirect + github.com/hashicorp/go-plugin v1.6.3 // indirect + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/hc-install v0.6.1 // indirect - github.com/hashicorp/hcl/v2 v2.19.1 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/hc-install v0.9.2 // indirect + github.com/hashicorp/hcl/v2 v2.23.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.19.0 // indirect - github.com/hashicorp/terraform-json v0.17.1 // indirect - github.com/hashicorp/terraform-plugin-go v0.19.0 // indirect + github.com/hashicorp/terraform-exec v0.23.0 // indirect + github.com/hashicorp/terraform-json v0.25.0 // indirect + github.com/hashicorp/terraform-plugin-go v0.27.0 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect - github.com/hashicorp/terraform-registry-address v0.2.2 // indirect + github.com/hashicorp/terraform-registry-address v0.2.5 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect - github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect - github.com/huandu/xstrings v1.3.2 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.9.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/cli v1.1.5 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect @@ -83,7 +82,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/oklog/run v1.0.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/openshift/api v0.0.0-20211217221424-8779abfbd571 // indirect @@ -98,33 +97,36 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/zclconf/go-cty v1.14.1 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/zclconf/go-cty v1.16.3 // indirect go.mongodb.org/mongo-driver v1.17.1 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect - golang.org/x/crypto v0.36.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/crypto v0.38.0 // indirect golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect - google.golang.org/grpc v1.57.1 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.33.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect + google.golang.org/grpc v1.72.1 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.23.5 // indirect - k8s.io/klog/v2 v2.80.1 // indirect - k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) //replace github.com/spectrocloud/palette-sdk-go => ../palette-sdk-go diff --git a/go.sum b/go.sum index 680094637..e960df70e 100644 --- a/go.sum +++ b/go.sum @@ -52,22 +52,21 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= -github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -100,7 +99,6 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -110,7 +108,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -130,8 +127,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU= github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= @@ -160,8 +157,9 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -169,6 +167,8 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3 github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -176,10 +176,10 @@ github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8b github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= -github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= +github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -242,8 +242,9 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -270,8 +271,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -288,10 +289,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -340,17 +340,19 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= -github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0= +github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.5.1 h1:oGm7cWBaYIp3lJpx1RUEfLWophprE2EV/KUeqBYo+6k= -github.com/hashicorp/go-plugin v1.5.1/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -358,43 +360,44 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/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.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hc-install v0.6.1 h1:IGxShH7AVhPaSuSJpKtVi/EFORNjO+OYVJJrAtGG2mY= -github.com/hashicorp/hc-install v0.6.1/go.mod h1:0fW3jpg+wraYSnFDJ6Rlie3RvLf1bIqVIkzoon4KoVE= +github.com/hashicorp/hc-install v0.9.2 h1:v80EtNX4fCVHqzL9Lg/2xkp62bbvQMnvPQ0G+OmtO24= +github.com/hashicorp/hc-install v0.9.2/go.mod h1:XUqBQNnuT4RsxoxiM9ZaUk0NX8hi2h+Lb6/c0OZnC/I= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= -github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= +github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos= +github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/terraform-exec v0.19.0 h1:FpqZ6n50Tk95mItTSS9BjeOVUb4eg81SpgVtZNNtFSM= -github.com/hashicorp/terraform-exec v0.19.0/go.mod h1:tbxUpe3JKruE9Cuf65mycSIT8KiNPZ0FkuTE3H4urQg= -github.com/hashicorp/terraform-json v0.17.1 h1:eMfvh/uWggKmY7Pmb3T85u86E2EQg6EQHgyRwf3RkyA= -github.com/hashicorp/terraform-json v0.17.1/go.mod h1:Huy6zt6euxaY9knPAFKjUITn8QxUFIe9VuSzb4zn/0o= +github.com/hashicorp/terraform-exec v0.23.0 h1:MUiBM1s0CNlRFsCLJuM5wXZrzA3MnPYEsiXmzATMW/I= +github.com/hashicorp/terraform-exec v0.23.0/go.mod h1:mA+qnx1R8eePycfwKkCRk3Wy65mwInvlpAeOwmA7vlY= +github.com/hashicorp/terraform-json v0.25.0 h1:rmNqc/CIfcWawGiwXmRuiXJKEiJu1ntGoxseG1hLhoQ= +github.com/hashicorp/terraform-json v0.25.0/go.mod h1:sMKS8fiRDX4rVlR6EJUMudg1WcanxCMoWwTLkgZP/vc= github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFccGyBZn52KtMNsS12dI= github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA= -github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU= -github.com/hashicorp/terraform-plugin-go v0.19.0/go.mod h1:EhRSkEPNoylLQntYsk5KrDHTZJh9HQoumZXbOGOXmec= +github.com/hashicorp/terraform-plugin-go v0.27.0 h1:ujykws/fWIdsi6oTUT5Or4ukvEan4aN9lY+LOxVP8EE= +github.com/hashicorp/terraform-plugin-go v0.27.0/go.mod h1:FDa2Bb3uumkTGSkTFpWSOwWJDwA7bf3vdP3ltLDTH6o= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0 h1:X7vB6vn5tON2b49ILa4W7mFAsndeqJ7bZFOGbVO+0Cc= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0/go.mod h1:ydFcxbdj6klCqYEPkPvdvFKiNGKZLUs+896ODUXCyao= -github.com/hashicorp/terraform-registry-address v0.2.2 h1:lPQBg403El8PPicg/qONZJDC6YlgCVbWDtNmmZKtBno= -github.com/hashicorp/terraform-registry-address v0.2.2/go.mod h1:LtwNbCihUoUZ3RYriyS2wF/lGPB6gF9ICLRtuDk7hSo= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 h1:NFPMacTrY/IdcIcnUB+7hsore1ZaRWU9cnB6jFoBnIM= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0/go.mod h1:QYmYnLfsosrxjCnGY1p9c7Zj6n9thnEE+7RObeYs3fA= +github.com/hashicorp/terraform-registry-address v0.2.5 h1:2GTftHqmUhVOeuu9CW3kwDkRe4pcBDq0uuK5VJngU1M= +github.com/hashicorp/terraform-registry-address v0.2.5/go.mod h1:PpzXWINwB5kuVS5CA7m1+eO2f1jKb5ZDIxrOPfpnGkg= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -439,8 +442,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -452,13 +453,13 @@ github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUt github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -492,8 +493,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -531,8 +533,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= -github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -578,8 +580,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -591,15 +593,15 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= -github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spectrocloud/palette-sdk-go v0.0.0-20250829121241-06ad6c5fc7b1 h1:OI305NgUmRfuVnKyaHGMbs42dYsEUHqUk0vHIWPukas= -github.com/spectrocloud/palette-sdk-go v0.0.0-20250829121241-06ad6c5fc7b1/go.mod h1:wIt8g7I7cmcQvTo5ktwhSF0/bWq6uRdxGBs9dwTpleU= +github.com/spectrocloud/palette-sdk-go v0.0.0-20250907041328-61dd91d616cc h1:wZbQSodOSbp39S5wI8IQJu58I8Rf9rGpdeAVBlMy0Zs= +github.com/spectrocloud/palette-sdk-go v0.0.0-20250907041328-61dd91d616cc/go.mod h1:wIt8g7I7cmcQvTo5ktwhSF0/bWq6uRdxGBs9dwTpleU= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -612,8 +614,9 @@ github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t6 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -634,10 +637,12 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1 github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -649,8 +654,10 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= -github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk= +github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -675,21 +682,23 @@ go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUz go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= -go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -699,6 +708,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= golang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -713,10 +724,9 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= 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= @@ -756,9 +766,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -812,10 +821,8 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -842,9 +849,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -920,19 +926,14 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 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= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 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= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -942,11 +943,10 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= 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= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1014,9 +1014,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= 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= @@ -1049,8 +1048,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1096,8 +1096,8 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1120,8 +1120,8 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= -google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1135,8 +1135,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1165,7 +1165,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1185,14 +1184,16 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg= k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ= -k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= +k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE= +k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug= k8s.io/apiextensions-apiserver v0.23.5 h1:5SKzdXyvIJKu+zbfPc3kCbWpbxi+O+zdmAJBm26UJqI= k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ= k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc= k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= -k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= +k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0= +k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw= k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE= @@ -1205,15 +1206,15 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= -k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= kubevirt.io/api v0.59.0 h1:UDsJWklzd0x/w3EQjc48jafZc4p4vVxKUpmBhg2nVRk= kubevirt.io/api v0.59.0/go.mod h1:zts/6mioR8vGgvYmQ17Cb9XsUR9e/WjJcdokmrE38wY= kubevirt.io/containerized-data-importer-api v1.56.0 h1:Ehc6CbT3mG2uz+9s3t2N4HnpdK5GfQMt2DCCXCz2sDI= @@ -1225,13 +1226,16 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/spectrocloud/application_create_common.go b/spectrocloud/application_create_common.go index fe102ab1e..028901fb5 100644 --- a/spectrocloud/application_create_common.go +++ b/spectrocloud/application_create_common.go @@ -51,9 +51,9 @@ func toAppDeploymentTargetClusterLimits(d *schema.ResourceData) *models.V1AppDep limits := config["limits"].([]interface{})[i].(map[string]interface{}) if limits["cpu"] != nil && limits["memory"] != nil { return &models.V1AppDeploymentTargetClusterLimits{ - CPU: int32(limits["cpu"].(int)), - MemoryMiB: int32(limits["memory"].(int)), - StorageGiB: int32(limits["storage"].(int)), + CPU: SafeInt32(limits["cpu"].(int)), + MemoryMiB: SafeInt32(limits["memory"].(int)), + StorageGiB: SafeInt32(limits["storage"].(int)), } } } diff --git a/spectrocloud/cluster_common_hash.go b/spectrocloud/cluster_common_hash.go index c5d15f535..abf2dbd78 100644 --- a/spectrocloud/cluster_common_hash.go +++ b/spectrocloud/cluster_common_hash.go @@ -6,6 +6,7 @@ import ( "hash/fnv" "sort" "strings" + "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "gopkg.in/yaml.v3" @@ -467,7 +468,10 @@ func hash(s string) uint32 { func YamlContentHash(yamlContent string) string { canonicalContent := yamlContentToCanonicalString(yamlContent) h := fnv.New64a() - h.Write([]byte(canonicalContent)) + if _, err := h.Write([]byte(canonicalContent)); err != nil { + // If hash writing fails, return a fallback hash + return fmt.Sprintf("error_hash_%d", time.Now().UnixNano()) + } return fmt.Sprintf("%x", h.Sum64()) } diff --git a/spectrocloud/constants/constants.go b/spectrocloud/constants/constants.go new file mode 100644 index 000000000..36a4a77b0 --- /dev/null +++ b/spectrocloud/constants/constants.go @@ -0,0 +1,15 @@ +package constants + +const ( + // Int32MaxValue represents the maximum value for int32 type (2^31 - 1) + Int32MaxValue = 2147483647 + + // Int32MinValue represents the minimum value for int32 type (-2^31) + Int32MinValue = -2147483648 + + // UInt32MaxValue represents the maximum value for uint32 type (2^32 - 1) + UInt32MaxValue = 4294967295 + + // Int64MaxValue represents the maximum value for int64 type (2^63 - 1) + Int64MaxValue = 9223372036854775807 +) diff --git a/spectrocloud/kubevirt/schema/k8s/affinity_spec.go b/spectrocloud/kubevirt/schema/k8s/affinity_spec.go index df960dba5..a5628a2fd 100644 --- a/spectrocloud/kubevirt/schema/k8s/affinity_spec.go +++ b/spectrocloud/kubevirt/schema/k8s/affinity_spec.go @@ -1,6 +1,7 @@ package k8s import ( + "math" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -11,6 +12,17 @@ import ( "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/kubevirt/utils" ) +// safeInt32 converts int to int32 with bounds checking to prevent overflow +func safeInt32(value int) int32 { + if value > math.MaxInt32 { + return math.MaxInt32 + } + if value < math.MinInt32 { + return math.MinInt32 + } + return int32(value) +} + func affinityFields() map[string]*schema.Schema { return map[string]*schema.Schema{ "node_affinity": { @@ -437,7 +449,7 @@ func expandPreferredSchedulingTerms(t []interface{}) []v1.PreferredSchedulingTer for i, n := range t { in := n.(map[string]interface{}) if v, ok := in["weight"].(int); ok { - obj[i].Weight = int32(v) + obj[i].Weight = safeInt32(v) } if v, ok := in["preference"].([]interface{}); ok && len(v) > 0 { obj[i].Preference = *expandNodeSelectorTerm(v) @@ -474,7 +486,7 @@ func expandWeightedPodAffinityTerms(t []interface{}) []v1.WeightedPodAffinityTer for i, n := range t { in := n.(map[string]interface{}) if v, ok := in["weight"].(int); ok { - obj[i].Weight = int32(v) + obj[i].Weight = safeInt32(v) } if v, ok := in["pod_affinity_term"].([]interface{}); ok && len(v) > 0 { obj[i].PodAffinityTerm = expandPodAffinityTerms(v)[0] diff --git a/spectrocloud/kubevirt/schema/k8s/metadata.go b/spectrocloud/kubevirt/schema/k8s/metadata.go index bc82b1385..f0bb9afbf 100644 --- a/spectrocloud/kubevirt/schema/k8s/metadata.go +++ b/spectrocloud/kubevirt/schema/k8s/metadata.go @@ -2,6 +2,7 @@ package k8s import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/kubevirt/utils" @@ -44,11 +45,6 @@ func metadataFields(objectName string) map[string]*schema.Schema { Description: fmt.Sprintf("An opaque value that represents the internal version of this %s that can be used by clients to determine when %s has changed. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", objectName, objectName), Computed: true, }, - "self_link": { - Type: schema.TypeString, - Description: fmt.Sprintf("A URL representing this %s.", objectName), - Computed: true, - }, "uid": { Type: schema.TypeString, Description: fmt.Sprintf("The unique in time and space value for this %s. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", objectName), @@ -164,7 +160,6 @@ func FlattenMetadataDataVolume(meta metav1.ObjectMeta) []interface{} { m["labels"] = utils.FlattenStringMap(meta.Labels) m["name"] = meta.Name m["resource_version"] = meta.ResourceVersion - m["self_link"] = meta.SelfLink m["uid"] = fmt.Sprintf("%v", meta.UID) m["generation"] = meta.Generation @@ -192,9 +187,6 @@ func FlattenMetadata(meta metav1.ObjectMeta, resourceData *schema.ResourceData) if err = resourceData.Set("resource_version", meta.ResourceVersion); err != nil { return err } - if err = resourceData.Set("self_link", meta.SelfLink); err != nil { - return err - } if err = resourceData.Set("uid", fmt.Sprintf("%v", meta.UID)); err != nil { return err } diff --git a/spectrocloud/kubevirt/schema/k8s/persistent_volume_claim.go b/spectrocloud/kubevirt/schema/k8s/persistent_volume_claim.go index b4b7b8c68..24b43d7ae 100644 --- a/spectrocloud/kubevirt/schema/k8s/persistent_volume_claim.go +++ b/spectrocloud/kubevirt/schema/k8s/persistent_volume_claim.go @@ -102,7 +102,10 @@ func PersistentVolumeClaimSpecSchema() *schema.Schema { func FlattenPersistentVolumeClaimSpec(in v1.PersistentVolumeClaimSpec) []interface{} { att := make(map[string]interface{}) att["access_modes"] = flattenPersistentVolumeAccessModes(in.AccessModes) - att["resources"] = flattenResourceRequirements(in.Resources) + att["resources"] = flattenResourceRequirements(v1.ResourceRequirements{ + Limits: in.Resources.Limits, + Requests: in.Resources.Requests, + }) if in.Selector != nil { att["selector"] = flattenLabelSelector(in.Selector) } @@ -142,7 +145,10 @@ func ExpandPersistentVolumeClaimSpec(l []interface{}) (*v1.PersistentVolumeClaim return nil, err } obj.AccessModes = expandPersistentVolumeAccessModes(in["access_modes"].(*schema.Set).List()) - obj.Resources = *resourceRequirements + obj.Resources = v1.VolumeResourceRequirements{ + Limits: resourceRequirements.Limits, + Requests: resourceRequirements.Requests, + } if v, ok := in["selector"].([]interface{}); ok && len(v) > 0 { obj.Selector = expandLabelSelector(v) } diff --git a/spectrocloud/kubevirt/schema/virtualmachineinstance/domain_spec.go b/spectrocloud/kubevirt/schema/virtualmachineinstance/domain_spec.go index e9abc881c..3c45387b2 100644 --- a/spectrocloud/kubevirt/schema/virtualmachineinstance/domain_spec.go +++ b/spectrocloud/kubevirt/schema/virtualmachineinstance/domain_spec.go @@ -1,11 +1,14 @@ package virtualmachineinstance import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "k8s.io/apimachinery/pkg/api/resource" kubevirtapiv1 "kubevirt.io/api/core/v1" + "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/constants" "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/kubevirt/utils" ) @@ -286,12 +289,21 @@ func expandCPU(cpu map[string]interface{}) (kubevirtapiv1.CPU, error) { } if v, ok := cpu["cores"].(int); ok { + if v < 0 || v > constants.UInt32MaxValue { + return result, fmt.Errorf("cores value %d is out of range for uint32", v) + } result.Cores = uint32(v) } if v, ok := cpu["sockets"].(int); ok { + if v < 0 || v > constants.UInt32MaxValue { + return result, fmt.Errorf("sockets value %d is out of range for uint32", v) + } result.Sockets = uint32(v) } if v, ok := cpu["threads"].(int); ok { + if v < 0 || v > constants.UInt32MaxValue { + return result, fmt.Errorf("threads value %d is out of range for uint32", v) + } result.Threads = uint32(v) } diff --git a/spectrocloud/kubevirt/test_utils/expand_utils/expand_utils.go b/spectrocloud/kubevirt/test_utils/expand_utils/expand_utils.go index 05a5d9d07..fade923dd 100644 --- a/spectrocloud/kubevirt/test_utils/expand_utils/expand_utils.go +++ b/spectrocloud/kubevirt/test_utils/expand_utils/expand_utils.go @@ -37,7 +37,7 @@ func getDataVolumeSpec() cdiv1.DataVolumeSpec { }, PVC: &corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, - Resources: k8sv1.ResourceRequirements{ + Resources: k8sv1.VolumeResourceRequirements{ Requests: k8sv1.ResourceList{ "storage": requestStorage, }, diff --git a/spectrocloud/kubevirt/test_utils/flatten_utils/flatten_utils.go b/spectrocloud/kubevirt/test_utils/flatten_utils/flatten_utils.go index 56a683b04..be0977d3e 100644 --- a/spectrocloud/kubevirt/test_utils/flatten_utils/flatten_utils.go +++ b/spectrocloud/kubevirt/test_utils/flatten_utils/flatten_utils.go @@ -37,7 +37,7 @@ func getDataVolumeSpec() cdiv1.DataVolumeSpec { AccessModes: []k8sv1.PersistentVolumeAccessMode{ "ReadWriteOnce", }, - Resources: k8sv1.ResourceRequirements{ + Resources: k8sv1.VolumeResourceRequirements{ Requests: k8sv1.ResourceList{ "storage": (func() resource.Quantity { res, _ := resource.ParseQuantity("10Gi"); return res })(), }, @@ -305,7 +305,6 @@ func GetBaseOutputForVirtualMachine() interface{} { "labels": map[string]any(nil), "name": string(""), "resource_version": string(""), - "self_link": string(""), "uid": string(""), }, }, diff --git a/spectrocloud/provider.go b/spectrocloud/provider.go index 0cb546ba9..b68f056aa 100644 --- a/spectrocloud/provider.go +++ b/spectrocloud/provider.go @@ -60,7 +60,7 @@ func New(_ string) func() *schema.Provider { "ignore_insecure_tls_error": { Type: schema.TypeBool, Optional: true, - Description: "Ignore insecure TLS errors for Spectro Cloud API endpoints. Defaults to false.", + Description: "Ignore insecure TLS errors for Spectro Cloud API endpoints. ⚠️ WARNING: Setting this to true disables SSL certificate verification and makes connections vulnerable to man-in-the-middle attacks. Only use this in development/testing environments or when connecting to self-signed certificates in trusted networks. Defaults to false.", }, }, ResourcesMap: map[string]*schema.Resource{ @@ -68,8 +68,6 @@ func New(_ string) func() *schema.Provider { "spectrocloud_project": resourceProject(), - "spectrocloud_macro": resourceMacro(), - "spectrocloud_macros": resourceMacros(), "spectrocloud_filter": resourceFilter(), @@ -204,7 +202,9 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{} projectName := d.Get("project_name").(string) insecure := d.Get("ignore_insecure_tls_error").(bool) + if insecure { + // #nosec G402 http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} } diff --git a/spectrocloud/provider_resource_schema_test.go b/spectrocloud/provider_resource_schema_test.go index 3e5efed53..84a34b58a 100644 --- a/spectrocloud/provider_resource_schema_test.go +++ b/spectrocloud/provider_resource_schema_test.go @@ -19,12 +19,6 @@ func prepareProjectTestData(id string) *schema.ResourceData { return d } -func prepareMacroTestData(id string) *schema.ResourceData { - d := resourceMacro().TestResourceData() - d.SetId(id) - return d -} - func prepareApplicationProfileTestData(id string) *schema.ResourceData { d := resourceApplicationProfile().TestResourceData() d.SetId(id) @@ -263,11 +257,6 @@ func TestResourceProject(t *testing.T) { assert.Equal(t, "test-id", testData.Id()) } -func TestResourceMacro(t *testing.T) { - testData := prepareMacroTestData("test-id") - assert.Equal(t, "test-id", testData.Id()) -} - func TestResourceApplicationProfile(t *testing.T) { testData := prepareApplicationProfileTestData("test-id") assert.Equal(t, "test-id", testData.Id()) diff --git a/spectrocloud/resource_alert.go b/spectrocloud/resource_alert.go index 77d83a2b6..3e636ed5c 100644 --- a/spectrocloud/resource_alert.go +++ b/spectrocloud/resource_alert.go @@ -20,6 +20,9 @@ func resourceAlert() *schema.Resource { ReadContext: resourceAlertRead, UpdateContext: resourceAlertUpdate, DeleteContext: resourceAlertDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceAlertImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/spectrocloud/resource_alert_import.go b/spectrocloud/resource_alert_import.go new file mode 100644 index 000000000..51f423659 --- /dev/null +++ b/spectrocloud/resource_alert_import.go @@ -0,0 +1,33 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAlertImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + + idParts := d.Id() + context := "project" + c := getV1ClientWithResourceContext(m, context) + pjt, err := c.GetProject(ProviderInitProjectUid) + if err != nil { + return nil, err + } + if err := d.Set("project", pjt.Metadata.Name); err != nil { + return nil, err + } + if err := d.Set("component", "ClusterHealth"); err != nil { + return nil, err + } + d.SetId(idParts) + // Read all alert data to populate the state + diags := resourceAlertRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read alert for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} diff --git a/spectrocloud/resource_appliance.go b/spectrocloud/resource_appliance.go index ebb4d933b..3ff56e7db 100644 --- a/spectrocloud/resource_appliance.go +++ b/spectrocloud/resource_appliance.go @@ -4,11 +4,12 @@ import ( "context" "errors" "fmt" + "log" + "time" + "github.com/go-openapi/strfmt" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/spectrocloud/palette-sdk-go/api/apiutil/transport" - "log" - "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" @@ -25,6 +26,9 @@ func resourceAppliance() *schema.Resource { ReadContext: resourceApplianceRead, UpdateContext: resourceApplianceUpdate, DeleteContext: resourceApplianceDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceApplianceImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(20 * time.Minute), diff --git a/spectrocloud/resource_appliance_import.go b/spectrocloud/resource_appliance_import.go new file mode 100644 index 000000000..a26df62eb --- /dev/null +++ b/spectrocloud/resource_appliance_import.go @@ -0,0 +1,79 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourceApplianceImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonAppliance(d, m) + if err != nil { + return nil, err + } + + // Read all appliance data to populate the state + diags := resourceApplianceRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read appliance for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonAppliance(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // Appliances are project-level resources + c := getV1ClientWithResourceContext(m, "project") + + // The import ID should be the appliance UID + applianceUID := d.Id() + if applianceUID == "" { + return nil, fmt.Errorf("appliance import ID is required") + } + + // Validate that the appliance exists and we can access it + appliance, err := c.GetAppliance(applianceUID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve appliance: %s", err) + } + if appliance == nil { + return nil, fmt.Errorf("appliance with ID %s not found", applianceUID) + } + + // Set the required uid field (this is what the resource uses internally) + if err := d.Set("uid", appliance.Metadata.UID); err != nil { + return nil, err + } + + // Set optional fields if they exist + if len(appliance.Metadata.Labels) > 0 { + if err := d.Set("tags", appliance.Metadata.Labels); err != nil { + return nil, err + } + } + + // Set other optional fields with default values to prevent validation errors + if appliance.Spec != nil { + // Set wait to false as default (this is likely what users expect for import) + if err := d.Set("wait", false); err != nil { + return nil, err + } + + // Set remote shell access if configured + if appliance.Spec.TunnelConfig != nil { + if err := d.Set("remote_shell", appliance.Spec.TunnelConfig.RemoteSSH); err != nil { + return nil, err + } + if err := d.Set("temporary_shell_credentials", appliance.Spec.TunnelConfig.RemoteSSHTempUser); err != nil { + return nil, err + } + } + } + + // Set the ID to the appliance UID + d.SetId(applianceUID) + + return c, nil +} diff --git a/spectrocloud/resource_application.go b/spectrocloud/resource_application.go index 4d93c458a..45925226c 100644 --- a/spectrocloud/resource_application.go +++ b/spectrocloud/resource_application.go @@ -16,6 +16,9 @@ func resourceApplication() *schema.Resource { ReadContext: resourceApplicationRead, UpdateContext: resourceApplicationUpdate, DeleteContext: resourceApplicationDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceApplicationImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(60 * time.Minute), @@ -174,21 +177,90 @@ func resourceApplicationCreate(ctx context.Context, d *schema.ResourceData, m in //goland:noinspection GoUnhandledErrorResult func resourceApplicationRead(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - /*c := m.(*client.V1Client) + // Get the resource context from existing configuration, default to project + resourceContext := "project" + configList := d.Get("config") + if configList != nil && len(configList.([]interface{})) > 0 && configList.([]interface{})[0] != nil { + config := configList.([]interface{})[0].(map[string]interface{}) + if clusterContext, ok := config["cluster_context"].(string); ok && clusterContext != "" { + resourceContext = clusterContext + } + } + c := getV1ClientWithResourceContext(m, resourceContext) var diags diag.Diagnostics - uid := d.Get("cluster_uid").(string) - cluster, err := c.GetCluster(uid) + // Get the application deployment by ID + appDeployment, err := c.GetApplication(d.Id()) if err != nil { + // If not found in current context, try the other context + if resourceContext == "project" { + c = getV1ClientWithResourceContext(m, "tenant") + appDeployment, err = c.GetApplication(d.Id()) + if err == nil && appDeployment != nil { + resourceContext = "tenant" + } + } else { + c = getV1ClientWithResourceContext(m, "project") + appDeployment, err = c.GetApplication(d.Id()) + if err == nil && appDeployment != nil { + resourceContext = "project" + } + } + + if err != nil { + return handleReadError(d, err, diags) + } + } + + if appDeployment == nil { + // Deleted - Terraform will recreate it + d.SetId("") + return diags + } + + // Set basic fields + if err := d.Set("name", appDeployment.Metadata.Name); err != nil { return diag.FromErr(err) } - diagnostics, done := readCommonFields(c, d, cluster) - if done { - return diagnostics - }*/ - var diags diag.Diagnostics + if err := d.Set("tags", flattenTags(appDeployment.Metadata.Labels)); err != nil { + return diag.FromErr(err) + } + + // Set application profile UID + if appDeployment.Spec != nil && appDeployment.Spec.Profile != nil && appDeployment.Spec.Profile.Metadata != nil { + if err := d.Set("application_profile_uid", appDeployment.Spec.Profile.Metadata.UID); err != nil { + return diag.FromErr(err) + } + } + + // Set config based on deployment type + if appDeployment.Spec != nil && appDeployment.Spec.Config != nil && appDeployment.Spec.Config.Target != nil { + config := make(map[string]interface{}) + config["cluster_context"] = resourceContext + + // Check cluster reference + if clusterRef := appDeployment.Spec.Config.Target.ClusterRef; clusterRef != nil { + // Set cluster UID and name based on the available information + if clusterRef.UID != "" { + config["cluster_uid"] = clusterRef.UID + } + if clusterRef.Name != "" { + config["cluster_name"] = clusterRef.Name + } + } + + // Check environment reference for cluster group information + // if envRef := appDeployment.Spec.Config.Target.EnvRef; envRef != nil { + // // Environment references might contain cluster group information + // // This would need to be mapped based on the actual API response structure + // } + + if err := d.Set("config", []interface{}{config}); err != nil { + return diag.FromErr(err) + } + } return diags } diff --git a/spectrocloud/resource_application_import.go b/spectrocloud/resource_application_import.go new file mode 100644 index 000000000..1aa5f81b0 --- /dev/null +++ b/spectrocloud/resource_application_import.go @@ -0,0 +1,81 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourceApplicationImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonApplication(d, m) + if err != nil { + return nil, err + } + + // Read all application data to populate the state + diags := resourceApplicationRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read application for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonApplication(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // Applications can work in both tenant and project context, so we'll try project first + applicationID := d.Id() + if applicationID == "" { + return nil, fmt.Errorf("application ID is required for import") + } + + // Try project context first + c := getV1ClientWithResourceContext(m, "project") + + // Use the ID to retrieve the application data from the API + appDeployment, err := c.GetApplication(applicationID) + if err != nil { + // If not found in project context, try tenant context + c = getV1ClientWithResourceContext(m, "tenant") + appDeployment, err = c.GetApplication(applicationID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve application data in either project or tenant context: %s", err) + } + } + + if appDeployment == nil { + return nil, fmt.Errorf("application with ID %s not found", applicationID) + } + + // Set the application name from the retrieved application + if err := d.Set("name", appDeployment.Metadata.Name); err != nil { + return nil, err + } + + // Set placeholder values for required fields to prevent validation errors during import + // These will be properly populated by the read function + if appDeployment.Spec != nil && appDeployment.Spec.Profile != nil && appDeployment.Spec.Profile.Metadata != nil { + if err := d.Set("application_profile_uid", appDeployment.Spec.Profile.Metadata.UID); err != nil { + return nil, err + } + } + + // Set placeholder config with required cluster_context + // The resource context will be determined and set properly in the read function + if appDeployment.Spec != nil && appDeployment.Spec.Config != nil && appDeployment.Spec.Config.Target != nil { + config := make(map[string]interface{}) + // Default to project context, will be adjusted in read function if needed + config["cluster_context"] = "project" + + if err := d.Set("config", []interface{}{config}); err != nil { + return nil, err + } + } + + // Set the ID of the resource in the state. This ID is used to track the + // resource and must be set in the state during the import. + d.SetId(appDeployment.Metadata.UID) + + return c, nil +} diff --git a/spectrocloud/resource_application_profile.go b/spectrocloud/resource_application_profile.go index 87602a508..ac185dd0c 100644 --- a/spectrocloud/resource_application_profile.go +++ b/spectrocloud/resource_application_profile.go @@ -24,6 +24,9 @@ func resourceApplicationProfile() *schema.Resource { ReadContext: resourceApplicationProfileRead, UpdateContext: resourceApplicationProfileUpdate, DeleteContext: resourceApplicationProfileDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceApplicationProfileImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(20 * time.Second), @@ -189,8 +192,10 @@ func flattenAppPacks(c *client.V1Client, diagPacks []*models.V1PackManifestEntit if pt.Value != "********" { prop[pt.Name] = pt.Value } else { - ogProp := d.Get("pack").([]interface{})[i].(map[string]interface{})["properties"] - prop[pt.Name] = getValueInProperties(ogProp.(map[string]interface{}), pt.Name) + if _, ok := d.GetOk("pack"); ok { + ogProp := d.Get("pack").([]interface{})[i].(map[string]interface{})["properties"] + prop[pt.Name] = getValueInProperties(ogProp.(map[string]interface{}), pt.Name) + } } } diff --git a/spectrocloud/resource_application_profile_import.go b/spectrocloud/resource_application_profile_import.go new file mode 100644 index 000000000..541be4f19 --- /dev/null +++ b/spectrocloud/resource_application_profile_import.go @@ -0,0 +1,50 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceApplicationProfileImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + // Application profiles have a context, default to "project" + c := getV1ClientWithResourceContext(m, "project") + + // The import ID should be the application profile UID + profileUID := d.Id() + + // Validate that the application profile exists and we can access it + appProfile, err := c.GetApplicationProfile(profileUID) + if err != nil { + return nil, fmt.Errorf("could not retrieve application profile for import: %s", err) + } + if appProfile == nil { + return nil, fmt.Errorf("application profile with ID %s not found", profileUID) + } + + // Set the application profile name from the retrieved profile + if err := d.Set("name", appProfile.Metadata.Name); err != nil { + return nil, err + } + // Set the application profile version from the retrieved profile + if err := d.Set("version", appProfile.Spec.Version); err != nil { + return nil, err + } + // Set the cloud to all as default for import + if err := d.Set("cloud", "all"); err != nil { + return nil, err + } + // Set the context to project as default for import + if err := d.Set("context", "project"); err != nil { + return nil, err + } + + // Read all application profile data to populate the state + diags := resourceApplicationProfileRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read application profile for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} diff --git a/spectrocloud/resource_backup_storage_location.go b/spectrocloud/resource_backup_storage_location.go index dba48f353..1d7489c10 100644 --- a/spectrocloud/resource_backup_storage_location.go +++ b/spectrocloud/resource_backup_storage_location.go @@ -23,6 +23,9 @@ func resourceBackupStorageLocation() *schema.Resource { ReadContext: resourceBackupStorageLocationRead, UpdateContext: resourceBackupStorageLocationUpdate, DeleteContext: resourceBackupStorageLocationDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceBackupStorageLocationImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/spectrocloud/resource_backup_storage_location_import.go b/spectrocloud/resource_backup_storage_location_import.go new file mode 100644 index 000000000..c3106b81b --- /dev/null +++ b/spectrocloud/resource_backup_storage_location_import.go @@ -0,0 +1,107 @@ +package spectrocloud + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourceBackupStorageLocationImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonBackupStorageLocation(d, m) + if err != nil { + return nil, err + } + + // Read all backup storage location data to populate the state + diags := resourceBackupStorageLocationRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read backup storage location for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonBackupStorageLocation(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // Parse the import ID which can be either: + // 1. Simple format: bsl_id (defaults to project context) + // 2. Context format: context:bsl_id (explicit context) + importID := d.Id() + if importID == "" { + return nil, fmt.Errorf("backup storage location import ID is required") + } + + var context string + var bslID string + + // Check if the import ID contains context specification + parts := strings.Split(importID, ":") + if len(parts) == 2 { + // Format: context:bsl_id + bslID = parts[0] + context = parts[1] + + // Validate context + if context != "project" && context != "tenant" { + return nil, fmt.Errorf("invalid context '%s'. Expected 'project' or 'tenant'", context) + } + } else if len(parts) == 1 { + // Format: bsl_id (default to project context) + context = "project" + bslID = parts[0] + } else { + return nil, fmt.Errorf("invalid import ID format. Expected 'bsl_id' or 'context:bsl_id', got: %s", importID) + } + + // Try the specified context first + c := getV1ClientWithResourceContext(m, context) + bsl, err := c.GetBackupStorageLocation(bslID) + + if err != nil || bsl == nil { + if err != nil { + return nil, fmt.Errorf("unable to retrieve backup storage location in either project or tenant context: %s", err) + } + if bsl == nil { + return nil, fmt.Errorf("backup storage location with ID %s not found", bslID) + } + } + + // Set the required fields for the resource + if err := d.Set("name", bsl.Metadata.Name); err != nil { + return nil, err + } + + if err := d.Set("context", context); err != nil { + return nil, err + } + + // Set the storage provider by mapping from API type to Terraform constants + storageProvider := mapAPITypeToTerraformProvider(string(*bsl.Spec.Storage)) + if err := d.Set("storage_provider", storageProvider); err != nil { + return nil, err + } + + // Set the ID to the backup storage location ID + d.SetId(bslID) + + return c, nil +} + +// mapAPITypeToTerraformProvider maps API storage type values to Terraform provider constants +func mapAPITypeToTerraformProvider(apiType string) string { + switch apiType { + case "s3": + return "aws" // API uses "s3" but Terraform uses "aws" + case "gcp": + return "gcp" // Same in both + case "minio": + return "minio" // Same in both + case "azure": + return "azure" // Same in both + default: + // Default to aws if unknown type + return "aws" + } +} diff --git a/spectrocloud/resource_cloud_account_azure.go b/spectrocloud/resource_cloud_account_azure.go index ef51e193d..367aa36ec 100644 --- a/spectrocloud/resource_cloud_account_azure.go +++ b/spectrocloud/resource_cloud_account_azure.go @@ -71,16 +71,16 @@ func resourceCloudAccountAzure() *schema.Resource { Type: schema.TypeString, Optional: true, Default: "AzurePublicCloud", - ValidateFunc: validation.StringInSlice([]string{"AzurePublicCloud", "AzureUSGovernmentCloud"}, false), // need to add support for "AzureUSSecretCloud" + ValidateFunc: validation.StringInSlice([]string{"AzurePublicCloud", "AzureUSGovernmentCloud", "AzureUSSecretCloud"}, false), Description: `The Azure partition in which the cloud account is located. -Can be 'AzurePublicCloud' for standard Azure regions or 'AzureUSGovernmentCloud' for Azure GovCloud (US) regions. +Can be 'AzurePublicCloud' for standard Azure regions or 'AzureUSGovernmentCloud' for Azure GovCloud (US) regions or 'AzureUSSecretCloud' for Azure Secret Cloud regions. Default is 'AzurePublicCloud'.`, }, - // "tls_cert": { - // Type: schema.TypeString, - // Optional: true, - // Description: "TLS certificate for authentication. This field is only allowed when cloud is set to 'AzureUSSecretCloud'.", - // }, + "tls_cert": { + Type: schema.TypeString, + Optional: true, + Description: "TLS certificate for authentication. This field is only allowed when cloud is set to 'AzureUSSecretCloud'.", + }, }, } } @@ -93,9 +93,9 @@ func resourceCloudAccountAzureCreate(ctx context.Context, d *schema.ResourceData var diags diag.Diagnostics // Validate tls_cert is only used with AzureUSSecretCloud - // if err := validateTlsCertConfiguration(d); err != nil { - // return diag.FromErr(err) - // } + if err := validateTlsCertConfiguration(d); err != nil { + return diag.FromErr(err) + } account := toAzureAccount(d) @@ -163,11 +163,11 @@ func flattenCloudAccountAzure(d *schema.ResourceData, account *models.V1AzureAcc return diag.FromErr(err), true } } - // if account.Spec.TLS != nil && account.Spec.TLS.Cert != "" { - // if err := d.Set("tls_cert", account.Spec.TLS.Cert); err != nil { - // return diag.FromErr(err), true - // } - // } + if account.Spec.TLS != nil && account.Spec.TLS.Cert != "" { + if err := d.Set("tls_cert", account.Spec.TLS.Cert); err != nil { + return diag.FromErr(err), true + } + } return nil, false } @@ -179,9 +179,9 @@ func resourceCloudAccountAzureUpdate(ctx context.Context, d *schema.ResourceData var diags diag.Diagnostics // Validate tls_cert is only used with AzureUSSecretCloud - // if err := validateTlsCertConfiguration(d); err != nil { - // return diag.FromErr(err) - // } + if err := validateTlsCertConfiguration(d); err != nil { + return diag.FromErr(err) + } account := toAzureAccount(d) @@ -241,26 +241,26 @@ func toAzureAccount(d *schema.ResourceData) *models.V1AzureAccount { } // add TLS configuration if tls_cert is provided - // if tlsCert, ok := d.GetOk("tls_cert"); ok && tlsCert.(string) != "" { - // account.Spec.TLS = &models.V1AzureSecretTLSConfig{ - // Cert: tlsCert.(string), - // } - // } + if tlsCert, ok := d.GetOk("tls_cert"); ok && tlsCert.(string) != "" { + account.Spec.TLS = &models.V1AzureSecretTLSConfig{ + Cert: tlsCert.(string), + } + } return account } -// func validateTlsCertConfiguration(d *schema.ResourceData) error { -// cloud := d.Get("cloud").(string) -// tlsCert := d.Get("tls_cert").(string) +func validateTlsCertConfiguration(d *schema.ResourceData) error { + cloud := d.Get("cloud").(string) + tlsCert := d.Get("tls_cert").(string) -// // If tls_cert is provided but cloud is not AzureUSSecretCloud, return an error -// if tlsCert != "" && cloud != "AzureUSSecretCloud" { -// return fmt.Errorf("tls_cert can only be set when cloud is 'AzureUSSecretCloud', but cloud is set to '%s'", cloud) -// } + // If tls_cert is provided but cloud is not AzureUSSecretCloud, return an error + if tlsCert != "" && cloud != "AzureUSSecretCloud" { + return fmt.Errorf("tls_cert can only be set when cloud is 'AzureUSSecretCloud', but cloud is set to '%s'", cloud) + } -// return nil -// } + return nil +} func resourceAccountAzureImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { resourceContext := d.Get("context").(string) diff --git a/spectrocloud/resource_cloud_account_azure_test.go b/spectrocloud/resource_cloud_account_azure_test.go index a31f234f2..691ebbd60 100644 --- a/spectrocloud/resource_cloud_account_azure_test.go +++ b/spectrocloud/resource_cloud_account_azure_test.go @@ -121,225 +121,225 @@ func TestResourceCloudAccountAzureDelete(t *testing.T) { assert.Len(t, diags, 0) } -// // Test for validateTlsCertConfiguration function -// func TestValidateTlsCertConfiguration(t *testing.T) { -// tests := []struct { -// name string -// cloud string -// tlsCert string -// expectError bool -// errorMsg string -// }{ -// { -// name: "Valid: AzureUSSecretCloud with tls_cert", -// cloud: "AzureUSSecretCloud", -// tlsCert: "test-certificate-data", -// expectError: false, -// }, -// { -// name: "Valid: AzureUSSecretCloud without tls_cert", -// cloud: "AzureUSSecretCloud", -// tlsCert: "", -// expectError: false, -// }, -// { -// name: "Valid: AzurePublicCloud without tls_cert", -// cloud: "AzurePublicCloud", -// tlsCert: "", -// expectError: false, -// }, -// { -// name: "Invalid: AzurePublicCloud with tls_cert", -// cloud: "AzurePublicCloud", -// tlsCert: "test-certificate-data", -// expectError: true, -// errorMsg: "tls_cert can only be set when cloud is 'AzureUSSecretCloud', but cloud is set to 'AzurePublicCloud'", -// }, -// { -// name: "Invalid: AzureUSGovernmentCloud with tls_cert", -// cloud: "AzureUSGovernmentCloud", -// tlsCert: "test-certificate-data", -// expectError: true, -// errorMsg: "tls_cert can only be set when cloud is 'AzureUSSecretCloud', but cloud is set to 'AzureUSGovernmentCloud'", -// }, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// rd := resourceCloudAccountAzure().TestResourceData() -// rd.Set("cloud", tt.cloud) -// rd.Set("tls_cert", tt.tlsCert) - -// err := validateTlsCertConfiguration(rd) - -// if tt.expectError { -// assert.Error(t, err) -// assert.Equal(t, tt.errorMsg, err.Error()) -// } else { -// assert.NoError(t, err) -// } -// }) -// } -// } - -// // Test for toAzureAccount with TLS certificate -// func TestToAzureAccountWithTlsCert(t *testing.T) { -// rd := resourceCloudAccountAzure().TestResourceData() -// rd.Set("name", "azure_unit_test_acc") -// rd.Set("context", "tenant") -// rd.Set("azure_client_id", "test_client_id") -// rd.Set("azure_client_secret", "test_client_secret") -// rd.Set("azure_tenant_id", "test_tenant_id") -// rd.Set("tenant_name", "test_tenant_name") -// rd.Set("disable_properties_request", true) -// rd.Set("private_cloud_gateway_id", "12345") -// rd.Set("cloud", "AzureUSSecretCloud") -// rd.Set("tls_cert", "test-certificate-data") - -// acc := toAzureAccount(rd) - -// assert.Equal(t, rd.Get("name"), acc.Metadata.Name) -// assert.Equal(t, "tenant", acc.Metadata.Annotations["scope"]) -// assert.Equal(t, rd.Get("azure_client_id"), *acc.Spec.ClientID) -// assert.Equal(t, rd.Get("azure_client_secret"), *acc.Spec.ClientSecret) -// assert.Equal(t, rd.Get("azure_tenant_id"), *acc.Spec.TenantID) -// assert.Equal(t, rd.Get("tenant_name"), acc.Spec.TenantName) -// assert.Equal(t, rd.Get("disable_properties_request"), acc.Spec.Settings.DisablePropertiesRequest) -// assert.Equal(t, rd.Get("private_cloud_gateway_id"), acc.Metadata.Annotations[OverlordUID]) -// assert.Equal(t, rd.Get("cloud"), *acc.Spec.AzureEnvironment) -// assert.Equal(t, rd.Id(), acc.Metadata.UID) -// // Test TLS configuration -// assert.NotNil(t, acc.Spec.TLS) -// assert.Equal(t, "test-certificate-data", acc.Spec.TLS.Cert) -// } - -// // Test for toAzureAccount without TLS certificate -// func TestToAzureAccountWithoutTlsCert(t *testing.T) { -// rd := resourceCloudAccountAzure().TestResourceData() -// rd.Set("name", "azure_unit_test_acc") -// rd.Set("context", "tenant") -// rd.Set("azure_client_id", "test_client_id") -// rd.Set("azure_client_secret", "test_client_secret") -// rd.Set("azure_tenant_id", "test_tenant_id") -// rd.Set("tenant_name", "test_tenant_name") -// rd.Set("disable_properties_request", true) -// rd.Set("private_cloud_gateway_id", "12345") -// rd.Set("cloud", "AzurePublicCloud") -// rd.Set("tls_cert", "") // Empty TLS cert - -// acc := toAzureAccount(rd) - -// assert.Equal(t, rd.Get("name"), acc.Metadata.Name) -// assert.Equal(t, rd.Get("cloud"), *acc.Spec.AzureEnvironment) -// // Test TLS configuration should be nil when tls_cert is empty -// assert.Nil(t, acc.Spec.TLS) -// } - -// // Test for flattenCloudAccountAzure with TLS certificate -// func TestFlattenCloudAccountAzureWithTlsCert(t *testing.T) { -// rd := resourceCloudAccountAzure().TestResourceData() -// account := &models.V1AzureAccount{ -// Metadata: &models.V1ObjectMeta{ -// Name: "test_account", -// Annotations: map[string]string{OverlordUID: "12345"}, -// UID: "abcdef", -// }, -// Spec: &models.V1AzureCloudAccount{ -// ClientID: types.Ptr("test_client_id"), -// ClientSecret: types.Ptr("test_client_secret"), -// TenantID: types.Ptr("test_tenant_id"), -// TenantName: "test_tenant_name", -// Settings: &models.V1CloudAccountSettings{ -// DisablePropertiesRequest: true, -// }, -// AzureEnvironment: types.Ptr("AzureUSSecretCloud"), -// TLS: &models.V1AzureSecretTLSConfig{ -// Cert: "test-certificate-data", -// }, -// }, -// } - -// diags, hasError := flattenCloudAccountAzure(rd, account) - -// assert.Nil(t, diags) -// assert.False(t, hasError) -// assert.Equal(t, "test_account", rd.Get("name")) -// assert.Equal(t, "12345", rd.Get("private_cloud_gateway_id")) -// assert.Equal(t, "test_client_id", rd.Get("azure_client_id")) -// assert.Equal(t, "test_tenant_id", rd.Get("azure_tenant_id")) -// assert.Equal(t, "test_tenant_name", rd.Get("tenant_name")) -// assert.Equal(t, true, rd.Get("disable_properties_request")) -// assert.Equal(t, "AzureUSSecretCloud", rd.Get("cloud")) -// assert.Equal(t, "test-certificate-data", rd.Get("tls_cert")) -// } - -// // Test for flattenCloudAccountAzure without TLS certificate -// func TestFlattenCloudAccountAzureWithoutTlsCert(t *testing.T) { -// rd := resourceCloudAccountAzure().TestResourceData() -// account := &models.V1AzureAccount{ -// Metadata: &models.V1ObjectMeta{ -// Name: "test_account", -// Annotations: map[string]string{OverlordUID: "12345"}, -// UID: "abcdef", -// }, -// Spec: &models.V1AzureCloudAccount{ -// ClientID: types.Ptr("test_client_id"), -// ClientSecret: types.Ptr("test_client_secret"), -// TenantID: types.Ptr("test_tenant_id"), -// TenantName: "test_tenant_name", -// Settings: &models.V1CloudAccountSettings{ -// DisablePropertiesRequest: true, -// }, -// AzureEnvironment: types.Ptr("AzurePublicCloud"), -// TLS: nil, // No TLS config -// }, -// } - -// diags, hasError := flattenCloudAccountAzure(rd, account) - -// assert.Nil(t, diags) -// assert.False(t, hasError) -// assert.Equal(t, "test_account", rd.Get("name")) -// assert.Equal(t, "AzurePublicCloud", rd.Get("cloud")) -// // tls_cert should not be set when TLS config is nil -// assert.Equal(t, "", rd.Get("tls_cert")) -// } - -// // Test Create function with invalid TLS cert configuration -// func TestResourceCloudAccountAzureCreateWithInvalidTlsCert(t *testing.T) { -// rd := resourceCloudAccountAzure().TestResourceData() -// rd.Set("name", "test-azure-account") -// rd.Set("context", "project") -// rd.Set("azure_tenant_id", "tenant-azure-id") -// rd.Set("azure_client_id", "azure-client-id") -// rd.Set("azure_client_secret", "test-client-secret") -// rd.Set("cloud", "AzurePublicCloud") -// rd.Set("tls_cert", "invalid-cert-for-public-cloud") // This should fail validation - -// ctx := context.Background() -// diags := resourceCloudAccountAzureCreate(ctx, rd, unitTestMockAPIClient) - -// assert.Len(t, diags, 1) -// assert.True(t, diags.HasError()) -// assert.Contains(t, diags[0].Summary, "tls_cert can only be set when cloud is 'AzureUSSecretCloud'") -// } - -// // Test Update function with invalid TLS cert configuration -// func TestResourceCloudAccountAzureUpdateWithInvalidTlsCert(t *testing.T) { -// rd := resourceCloudAccountAzure().TestResourceData() -// rd.SetId("test-azure-account-id") -// rd.Set("name", "test-azure-account") -// rd.Set("context", "project") -// rd.Set("azure_tenant_id", "tenant-azure-id") -// rd.Set("azure_client_id", "azure-client-id") -// rd.Set("azure_client_secret", "test-client-secret") -// rd.Set("cloud", "AzureUSGovernmentCloud") -// rd.Set("tls_cert", "invalid-cert-for-gov-cloud") // This should fail validation - -// ctx := context.Background() -// diags := resourceCloudAccountAzureUpdate(ctx, rd, unitTestMockAPIClient) - -// assert.Len(t, diags, 1) -// assert.True(t, diags.HasError()) -// assert.Contains(t, diags[0].Summary, "tls_cert can only be set when cloud is 'AzureUSSecretCloud'") -// } +// Test for validateTlsCertConfiguration function +func TestValidateTlsCertConfiguration(t *testing.T) { + tests := []struct { + name string + cloud string + tlsCert string + expectError bool + errorMsg string + }{ + { + name: "Valid: AzureUSSecretCloud with tls_cert", + cloud: "AzureUSSecretCloud", + tlsCert: "test-certificate-data", + expectError: false, + }, + { + name: "Valid: AzureUSSecretCloud without tls_cert", + cloud: "AzureUSSecretCloud", + tlsCert: "", + expectError: false, + }, + { + name: "Valid: AzurePublicCloud without tls_cert", + cloud: "AzurePublicCloud", + tlsCert: "", + expectError: false, + }, + { + name: "Invalid: AzurePublicCloud with tls_cert", + cloud: "AzurePublicCloud", + tlsCert: "test-certificate-data", + expectError: true, + errorMsg: "tls_cert can only be set when cloud is 'AzureUSSecretCloud', but cloud is set to 'AzurePublicCloud'", + }, + { + name: "Invalid: AzureUSGovernmentCloud with tls_cert", + cloud: "AzureUSGovernmentCloud", + tlsCert: "test-certificate-data", + expectError: true, + errorMsg: "tls_cert can only be set when cloud is 'AzureUSSecretCloud', but cloud is set to 'AzureUSGovernmentCloud'", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rd := resourceCloudAccountAzure().TestResourceData() + rd.Set("cloud", tt.cloud) + rd.Set("tls_cert", tt.tlsCert) + + err := validateTlsCertConfiguration(rd) + + if tt.expectError { + assert.Error(t, err) + assert.Equal(t, tt.errorMsg, err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +// Test for toAzureAccount with TLS certificate +func TestToAzureAccountWithTlsCert(t *testing.T) { + rd := resourceCloudAccountAzure().TestResourceData() + rd.Set("name", "azure_unit_test_acc") + rd.Set("context", "tenant") + rd.Set("azure_client_id", "test_client_id") + rd.Set("azure_client_secret", "test_client_secret") + rd.Set("azure_tenant_id", "test_tenant_id") + rd.Set("tenant_name", "test_tenant_name") + rd.Set("disable_properties_request", true) + rd.Set("private_cloud_gateway_id", "12345") + rd.Set("cloud", "AzureUSSecretCloud") + rd.Set("tls_cert", "test-certificate-data") + + acc := toAzureAccount(rd) + + assert.Equal(t, rd.Get("name"), acc.Metadata.Name) + assert.Equal(t, "tenant", acc.Metadata.Annotations["scope"]) + assert.Equal(t, rd.Get("azure_client_id"), *acc.Spec.ClientID) + assert.Equal(t, rd.Get("azure_client_secret"), *acc.Spec.ClientSecret) + assert.Equal(t, rd.Get("azure_tenant_id"), *acc.Spec.TenantID) + assert.Equal(t, rd.Get("tenant_name"), acc.Spec.TenantName) + assert.Equal(t, rd.Get("disable_properties_request"), acc.Spec.Settings.DisablePropertiesRequest) + assert.Equal(t, rd.Get("private_cloud_gateway_id"), acc.Metadata.Annotations[OverlordUID]) + assert.Equal(t, rd.Get("cloud"), *acc.Spec.AzureEnvironment) + assert.Equal(t, rd.Id(), acc.Metadata.UID) + // Test TLS configuration + assert.NotNil(t, acc.Spec.TLS) + assert.Equal(t, "test-certificate-data", acc.Spec.TLS.Cert) +} + +// Test for toAzureAccount without TLS certificate +func TestToAzureAccountWithoutTlsCert(t *testing.T) { + rd := resourceCloudAccountAzure().TestResourceData() + rd.Set("name", "azure_unit_test_acc") + rd.Set("context", "tenant") + rd.Set("azure_client_id", "test_client_id") + rd.Set("azure_client_secret", "test_client_secret") + rd.Set("azure_tenant_id", "test_tenant_id") + rd.Set("tenant_name", "test_tenant_name") + rd.Set("disable_properties_request", true) + rd.Set("private_cloud_gateway_id", "12345") + rd.Set("cloud", "AzurePublicCloud") + rd.Set("tls_cert", "") // Empty TLS cert + + acc := toAzureAccount(rd) + + assert.Equal(t, rd.Get("name"), acc.Metadata.Name) + assert.Equal(t, rd.Get("cloud"), *acc.Spec.AzureEnvironment) + // Test TLS configuration should be nil when tls_cert is empty + assert.Nil(t, acc.Spec.TLS) +} + +// Test for flattenCloudAccountAzure with TLS certificate +func TestFlattenCloudAccountAzureWithTlsCert(t *testing.T) { + rd := resourceCloudAccountAzure().TestResourceData() + account := &models.V1AzureAccount{ + Metadata: &models.V1ObjectMeta{ + Name: "test_account", + Annotations: map[string]string{OverlordUID: "12345"}, + UID: "abcdef", + }, + Spec: &models.V1AzureCloudAccount{ + ClientID: types.Ptr("test_client_id"), + ClientSecret: types.Ptr("test_client_secret"), + TenantID: types.Ptr("test_tenant_id"), + TenantName: "test_tenant_name", + Settings: &models.V1CloudAccountSettings{ + DisablePropertiesRequest: true, + }, + AzureEnvironment: types.Ptr("AzureUSSecretCloud"), + TLS: &models.V1AzureSecretTLSConfig{ + Cert: "test-certificate-data", + }, + }, + } + + diags, hasError := flattenCloudAccountAzure(rd, account) + + assert.Nil(t, diags) + assert.False(t, hasError) + assert.Equal(t, "test_account", rd.Get("name")) + assert.Equal(t, "12345", rd.Get("private_cloud_gateway_id")) + assert.Equal(t, "test_client_id", rd.Get("azure_client_id")) + assert.Equal(t, "test_tenant_id", rd.Get("azure_tenant_id")) + assert.Equal(t, "test_tenant_name", rd.Get("tenant_name")) + assert.Equal(t, true, rd.Get("disable_properties_request")) + assert.Equal(t, "AzureUSSecretCloud", rd.Get("cloud")) + assert.Equal(t, "test-certificate-data", rd.Get("tls_cert")) +} + +// Test for flattenCloudAccountAzure without TLS certificate +func TestFlattenCloudAccountAzureWithoutTlsCert(t *testing.T) { + rd := resourceCloudAccountAzure().TestResourceData() + account := &models.V1AzureAccount{ + Metadata: &models.V1ObjectMeta{ + Name: "test_account", + Annotations: map[string]string{OverlordUID: "12345"}, + UID: "abcdef", + }, + Spec: &models.V1AzureCloudAccount{ + ClientID: types.Ptr("test_client_id"), + ClientSecret: types.Ptr("test_client_secret"), + TenantID: types.Ptr("test_tenant_id"), + TenantName: "test_tenant_name", + Settings: &models.V1CloudAccountSettings{ + DisablePropertiesRequest: true, + }, + AzureEnvironment: types.Ptr("AzurePublicCloud"), + TLS: nil, // No TLS config + }, + } + + diags, hasError := flattenCloudAccountAzure(rd, account) + + assert.Nil(t, diags) + assert.False(t, hasError) + assert.Equal(t, "test_account", rd.Get("name")) + assert.Equal(t, "AzurePublicCloud", rd.Get("cloud")) + // tls_cert should not be set when TLS config is nil + assert.Equal(t, "", rd.Get("tls_cert")) +} + +// Test Create function with invalid TLS cert configuration +func TestResourceCloudAccountAzureCreateWithInvalidTlsCert(t *testing.T) { + rd := resourceCloudAccountAzure().TestResourceData() + rd.Set("name", "test-azure-account") + rd.Set("context", "project") + rd.Set("azure_tenant_id", "tenant-azure-id") + rd.Set("azure_client_id", "azure-client-id") + rd.Set("azure_client_secret", "test-client-secret") + rd.Set("cloud", "AzurePublicCloud") + rd.Set("tls_cert", "invalid-cert-for-public-cloud") // This should fail validation + + ctx := context.Background() + diags := resourceCloudAccountAzureCreate(ctx, rd, unitTestMockAPIClient) + + assert.Len(t, diags, 1) + assert.True(t, diags.HasError()) + assert.Contains(t, diags[0].Summary, "tls_cert can only be set when cloud is 'AzureUSSecretCloud'") +} + +// Test Update function with invalid TLS cert configuration +func TestResourceCloudAccountAzureUpdateWithInvalidTlsCert(t *testing.T) { + rd := resourceCloudAccountAzure().TestResourceData() + rd.SetId("test-azure-account-id") + rd.Set("name", "test-azure-account") + rd.Set("context", "project") + rd.Set("azure_tenant_id", "tenant-azure-id") + rd.Set("azure_client_id", "azure-client-id") + rd.Set("azure_client_secret", "test-client-secret") + rd.Set("cloud", "AzureUSGovernmentCloud") + rd.Set("tls_cert", "invalid-cert-for-gov-cloud") // This should fail validation + + ctx := context.Background() + diags := resourceCloudAccountAzureUpdate(ctx, rd, unitTestMockAPIClient) + + assert.Len(t, diags, 1) + assert.True(t, diags.HasError()) + assert.Contains(t, diags[0].Summary, "tls_cert can only be set when cloud is 'AzureUSSecretCloud'") +} diff --git a/spectrocloud/resource_cluster_aks.go b/spectrocloud/resource_cluster_aks.go index e980b76f7..59bfdfd7b 100644 --- a/spectrocloud/resource_cluster_aks.go +++ b/spectrocloud/resource_cluster_aks.go @@ -683,22 +683,22 @@ func toMachinePoolAks(machinePool interface{}) *models.V1AzureMachinePoolConfigE labels = append(labels, "worker") } - min := int32(m["count"].(int)) - max := int32(m["count"].(int)) + min := SafeInt32(m["count"].(int)) + max := SafeInt32(m["count"].(int)) if m["min"] != nil { - min = int32(m["min"].(int)) + min = SafeInt32(m["min"].(int)) } if m["max"] != nil { - max = int32(m["max"].(int)) + max = SafeInt32(m["max"].(int)) } mp := &models.V1AzureMachinePoolConfigEntity{ CloudConfig: &models.V1AzureMachinePoolCloudConfigEntity{ InstanceType: m["instance_type"].(string), OsDisk: &models.V1AzureOSDisk{ - DiskSizeGB: int32(m["disk_size_gb"].(int)), + DiskSizeGB: SafeInt32(m["disk_size_gb"].(int)), ManagedDisk: &models.V1ManagedDisk{ StorageAccountType: m["storage_account_type"].(string), }, @@ -715,7 +715,7 @@ func toMachinePoolAks(machinePool interface{}) *models.V1AzureMachinePoolConfigE IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, diff --git a/spectrocloud/resource_cluster_aws.go b/spectrocloud/resource_cluster_aws.go index 4d0c7757b..961d05f2a 100644 --- a/spectrocloud/resource_cluster_aws.go +++ b/spectrocloud/resource_cluster_aws.go @@ -696,15 +696,15 @@ func toMachinePoolAws(machinePool interface{}, vpcId string) (*models.V1AwsMachi azs = append(azs, az.(string)) } } - min := int32(m["count"].(int)) - max := int32(m["count"].(int)) + min := SafeInt32(m["count"].(int)) + max := SafeInt32(m["count"].(int)) if m["min"] != nil { - min = int32(m["min"].(int)) + min = SafeInt32(m["min"].(int)) } if m["max"] != nil { - max = int32(m["max"].(int)) + max = SafeInt32(m["max"].(int)) } mp := &models.V1AwsMachinePoolConfigEntity{ @@ -712,7 +712,7 @@ func toMachinePoolAws(machinePool interface{}, vpcId string) (*models.V1AwsMachi Azs: azs, InstanceType: types.Ptr(m["instance_type"].(string)), CapacityType: &capacityType, - RootDeviceSize: int64(m["disk_size_gb"].(int)), + RootDeviceSize: SafeInt64(m["disk_size_gb"].(int)), Subnets: azSubnetsConfigs, }, PoolConfig: &models.V1MachinePoolConfigEntity{ @@ -721,7 +721,7 @@ func toMachinePoolAws(machinePool interface{}, vpcId string) (*models.V1AwsMachi IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, @@ -736,7 +736,7 @@ func toMachinePoolAws(machinePool interface{}, vpcId string) (*models.V1AwsMachi if m["node_repave_interval"] != nil { nodeRepaveInterval = m["node_repave_interval"].(int) } - mp.PoolConfig.NodeRepaveInterval = int32(nodeRepaveInterval) + mp.PoolConfig.NodeRepaveInterval = SafeInt32(nodeRepaveInterval) } else { err := ValidationNodeRepaveIntervalForControlPlane(m["node_repave_interval"].(int)) if err != nil { diff --git a/spectrocloud/resource_cluster_azure.go b/spectrocloud/resource_cluster_azure.go index a4af9e394..ddd8f1bb1 100644 --- a/spectrocloud/resource_cluster_azure.go +++ b/spectrocloud/resource_cluster_azure.go @@ -797,7 +797,7 @@ func toMachinePoolAzure(machinePool interface{}) (*models.V1AzureMachinePoolConf Azs: azs, InstanceType: m["instance_type"].(string), OsDisk: &models.V1AzureOSDisk{ - DiskSizeGB: int32(diskSize), + DiskSizeGB: SafeInt32(diskSize), ManagedDisk: &models.V1ManagedDisk{ StorageAccountType: diskType, }, @@ -814,7 +814,7 @@ func toMachinePoolAzure(machinePool interface{}) (*models.V1AzureMachinePoolConf IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, @@ -827,7 +827,7 @@ func toMachinePoolAzure(machinePool interface{}) (*models.V1AzureMachinePoolConf if m["node_repave_interval"] != nil { nodeRepaveInterval = m["node_repave_interval"].(int) } - mp.PoolConfig.NodeRepaveInterval = int32(nodeRepaveInterval) + mp.PoolConfig.NodeRepaveInterval = SafeInt32(nodeRepaveInterval) } else { err := ValidationNodeRepaveIntervalForControlPlane(m["node_repave_interval"].(int)) if err != nil { diff --git a/spectrocloud/resource_cluster_edge_native.go b/spectrocloud/resource_cluster_edge_native.go index 934aabdeb..678acc94f 100644 --- a/spectrocloud/resource_cluster_edge_native.go +++ b/spectrocloud/resource_cluster_edge_native.go @@ -690,7 +690,7 @@ func toMachinePoolEdgeNative(machinePool interface{}) (*models.V1EdgeNativeMachi IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(len(cloudConfig.EdgeHosts))), + Size: types.Ptr(SafeInt32(len(cloudConfig.EdgeHosts))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, @@ -703,7 +703,7 @@ func toMachinePoolEdgeNative(machinePool interface{}) (*models.V1EdgeNativeMachi nodeRepaveInterval = m["node_repave_interval"].(int) } if !controlPlane { - mp.PoolConfig.NodeRepaveInterval = int32(nodeRepaveInterval) + mp.PoolConfig.NodeRepaveInterval = SafeInt32(nodeRepaveInterval) } else { err := ValidationNodeRepaveIntervalForControlPlane(nodeRepaveInterval) if err != nil { diff --git a/spectrocloud/resource_cluster_edge_vsphere.go b/spectrocloud/resource_cluster_edge_vsphere.go index ef06c26e2..5f213f60c 100644 --- a/spectrocloud/resource_cluster_edge_vsphere.go +++ b/spectrocloud/resource_cluster_edge_vsphere.go @@ -22,6 +22,9 @@ func resourceClusterEdgeVsphere() *schema.Resource { ReadContext: resourceClusterEdgeVsphereRead, UpdateContext: resourceClusterEdgeVsphereUpdate, DeleteContext: resourceClusterDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceClusterEdgeVsphereImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(180 * time.Minute), @@ -622,9 +625,9 @@ func toMachinePoolEdgeVsphere(machinePool interface{}) (*models.V1VsphereMachine ins := m["instance_type"].([]interface{})[0].(map[string]interface{}) instanceType := models.V1VsphereInstanceType{ - DiskGiB: types.Ptr(int32(ins["disk_size_gb"].(int))), - MemoryMiB: types.Ptr(int64(ins["memory_mb"].(int))), - NumCPUs: types.Ptr(int32(ins["cpu"].(int))), + DiskGiB: types.Ptr(SafeInt32(ins["disk_size_gb"].(int))), + MemoryMiB: types.Ptr(SafeInt64(ins["memory_mb"].(int))), + NumCPUs: types.Ptr(SafeInt32(ins["cpu"].(int))), } mp := &models.V1VsphereMachinePoolConfigEntity{ @@ -638,7 +641,7 @@ func toMachinePoolEdgeVsphere(machinePool interface{}) (*models.V1VsphereMachine IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, @@ -651,7 +654,7 @@ func toMachinePoolEdgeVsphere(machinePool interface{}) (*models.V1VsphereMachine if m["node_repave_interval"] != nil { nodeRepaveInterval = m["node_repave_interval"].(int) } - mp.PoolConfig.NodeRepaveInterval = int32(nodeRepaveInterval) + mp.PoolConfig.NodeRepaveInterval = SafeInt32(nodeRepaveInterval) } else { err := ValidationNodeRepaveIntervalForControlPlane(m["node_repave_interval"].(int)) if err != nil { diff --git a/spectrocloud/resource_cluster_edge_vsphere_import.go b/spectrocloud/resource_cluster_edge_vsphere_import.go new file mode 100644 index 000000000..bedf5bfcc --- /dev/null +++ b/spectrocloud/resource_cluster_edge_vsphere_import.go @@ -0,0 +1,31 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceClusterEdgeVsphereImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + c, err := GetCommonCluster(d, m) + if err != nil { + return nil, err + } + + diags := resourceClusterEdgeVsphereRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read cluster for import: %v", diags) + } + + // cluster profile and common default cluster attribute is get set here + err = flattenCommonAttributeForClusterImport(c, d) + if err != nil { + return nil, err + } + + // Return the resource data. In most cases, this method is only used to + // import one resource at a time, so you should return the resource data + // in a slice with a single element. + return []*schema.ResourceData{d}, nil +} diff --git a/spectrocloud/resource_cluster_eks.go b/spectrocloud/resource_cluster_eks.go index d7244a812..3cba41adb 100644 --- a/spectrocloud/resource_cluster_eks.go +++ b/spectrocloud/resource_cluster_eks.go @@ -905,15 +905,15 @@ func toMachinePoolEks(machinePool interface{}) *models.V1EksMachinePoolConfigEnt capacityType = m["capacity_type"].(string) } - min := int32(m["count"].(int)) - max := int32(m["count"].(int)) + min := SafeInt32(m["count"].(int)) + max := SafeInt32(m["count"].(int)) if m["min"] != nil { - min = int32(m["min"].(int)) + min = SafeInt32(m["min"].(int)) } if m["max"] != nil { - max = int32(m["max"].(int)) + max = SafeInt32(m["max"].(int)) } instanceType := "" if val, ok := m["instance_type"]; ok { @@ -923,9 +923,9 @@ func toMachinePoolEks(machinePool interface{}) *models.V1EksMachinePoolConfigEnt if val, ok := m["ami_type"]; ok { amiType = val.(string) } - diskSizeGb := int64(0) + diskSizeGb := SafeInt64(0) if dVal, ok := m["disk_size_gb"]; ok { - diskSizeGb = int64(dVal.(int)) + diskSizeGb = SafeInt64(dVal.(int)) } mp := &models.V1EksMachinePoolConfigEntity{ CloudConfig: &models.V1EksMachineCloudConfigEntity{ @@ -942,7 +942,7 @@ func toMachinePoolEks(machinePool interface{}) *models.V1EksMachinePoolConfigEnt IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, diff --git a/spectrocloud/resource_cluster_gcp.go b/spectrocloud/resource_cluster_gcp.go index a3aa2a4c5..a664b59e4 100644 --- a/spectrocloud/resource_cluster_gcp.go +++ b/spectrocloud/resource_cluster_gcp.go @@ -541,7 +541,7 @@ func toMachinePoolGcp(machinePool interface{}) (*models.V1GcpMachinePoolConfigEn CloudConfig: &models.V1GcpMachinePoolCloudConfigEntity{ Azs: azs, InstanceType: types.Ptr(m["instance_type"].(string)), - RootDeviceSize: int64(m["disk_size_gb"].(int)), + RootDeviceSize: SafeInt64(m["disk_size_gb"].(int)), }, PoolConfig: &models.V1MachinePoolConfigEntity{ AdditionalLabels: toAdditionalNodePoolLabels(m), @@ -549,7 +549,7 @@ func toMachinePoolGcp(machinePool interface{}) (*models.V1GcpMachinePoolConfigEn IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, @@ -562,7 +562,7 @@ func toMachinePoolGcp(machinePool interface{}) (*models.V1GcpMachinePoolConfigEn if m["node_repave_interval"] != nil { nodeRepaveInterval = m["node_repave_interval"].(int) } - mp.PoolConfig.NodeRepaveInterval = int32(nodeRepaveInterval) + mp.PoolConfig.NodeRepaveInterval = SafeInt32(nodeRepaveInterval) } else { err := ValidationNodeRepaveIntervalForControlPlane(m["node_repave_interval"].(int)) if err != nil { diff --git a/spectrocloud/resource_cluster_gke.go b/spectrocloud/resource_cluster_gke.go index 107af7d88..377253c04 100644 --- a/spectrocloud/resource_cluster_gke.go +++ b/spectrocloud/resource_cluster_gke.go @@ -488,13 +488,13 @@ func toMachinePoolGke(machinePool interface{}) (*models.V1GcpMachinePoolConfigEn mp := &models.V1GcpMachinePoolConfigEntity{ CloudConfig: &models.V1GcpMachinePoolCloudConfigEntity{ InstanceType: types.Ptr(m["instance_type"].(string)), - RootDeviceSize: int64(m["disk_size_gb"].(int)), + RootDeviceSize: SafeInt64(m["disk_size_gb"].(int)), }, PoolConfig: &models.V1MachinePoolConfigEntity{ AdditionalLabels: toAdditionalNodePoolLabels(m), Taints: toClusterTaints(m), Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, diff --git a/spectrocloud/resource_cluster_group.go b/spectrocloud/resource_cluster_group.go index 1d8bc2adc..aff7b0ae9 100644 --- a/spectrocloud/resource_cluster_group.go +++ b/spectrocloud/resource_cluster_group.go @@ -20,6 +20,9 @@ func resourceClusterGroup() *schema.Resource { ReadContext: resourceClusterGroupRead, UpdateContext: resourceClusterGroupUpdate, DeleteContext: resourceClusterGroupDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceClusterGroupImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(60 * time.Minute), @@ -96,12 +99,11 @@ func resourceClusterGroup() *schema.Resource { Default: "", }, "k8s_distribution": { - Type: schema.TypeString, - Optional: true, - Default: "k3s", - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"k3s", "cncf_k8s"}, false), - Description: "The Kubernetes distribution, allowed values are `k3s` and `cncf_k8s`.", + Type: schema.TypeString, + Optional: true, + Default: "k3s", + ForceNew: true, + Description: "The Kubernetes distribution, allowed values are `k3s` and `cncf_k8s`.", }, }, }, @@ -387,10 +389,10 @@ func toClusterGroupLimitConfig(resources map[string]interface{}) *models.V1Clust ret := &models.V1ClusterGroupLimitConfig{ - CPUMilliCore: int32(cpu_milli), - MemoryMiB: int32(mem_in_mb), - StorageGiB: int32(storage_in_gb), - OverSubscription: int32(oversubscription), + CPUMilliCore: SafeInt32(cpu_milli), + MemoryMiB: SafeInt32(mem_in_mb), + StorageGiB: SafeInt32(storage_in_gb), + OverSubscription: SafeInt32(oversubscription), } return ret diff --git a/spectrocloud/resource_cluster_group_import.go b/spectrocloud/resource_cluster_group_import.go new file mode 100644 index 000000000..e0a079444 --- /dev/null +++ b/spectrocloud/resource_cluster_group_import.go @@ -0,0 +1,58 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourceClusterGroupImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonClusterGroup(d, m) + if err != nil { + return nil, err + } + + // Read all cluster group data to populate the state + diags := resourceClusterGroupRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read cluster group for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonClusterGroup(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // Parse resource ID and scope + resourceContext, clusterGroupID, err := ParseResourceID(d) + if err != nil { + return nil, err + } + c := getV1ClientWithResourceContext(m, resourceContext) + + // Use the ID to retrieve the cluster group data from the API + clusterGroup, err := c.GetClusterGroup(clusterGroupID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve cluster group data: %s", err) + } + if clusterGroup == nil { + return nil, fmt.Errorf("cluster group with ID %s not found", clusterGroupID) + } + + // Set the cluster group name from the retrieved cluster group + if err := d.Set("name", clusterGroup.Metadata.Name); err != nil { + return nil, err + } + + // Set the context from the cluster group metadata + if err := d.Set("context", clusterGroup.Metadata.Annotations["scope"]); err != nil { + return nil, err + } + + // Set the ID of the resource in the state. This ID is used to track the + // resource and must be set in the state during the import. + d.SetId(clusterGroup.Metadata.UID) + + return c, nil +} diff --git a/spectrocloud/resource_cluster_group_test.go b/spectrocloud/resource_cluster_group_test.go index 590b18ddb..9fc735c76 100644 --- a/spectrocloud/resource_cluster_group_test.go +++ b/spectrocloud/resource_cluster_group_test.go @@ -252,7 +252,7 @@ func TestFlattenClusterGroup(t *testing.T) { func TestToClusterRef(t *testing.T) { d, err := prepareClusterGroupTestData() if err != nil { - t.Errorf(err.Error()) + t.Errorf("%s", err.Error()) } cluster := d.Get("clusters").([]interface{})[0].(map[string]interface{}) ret := toClusterRef(cluster) @@ -262,7 +262,7 @@ func TestToClusterRef(t *testing.T) { func TestToHostClusterConfigs(t *testing.T) { d, err := prepareClusterGroupTestData() if err != nil { - t.Errorf(err.Error()) + t.Errorf("%s", err.Error()) } hostConfigs := d.Get("clusters").([]interface{}) clusterUid := hostConfigs[0].(map[string]interface{})["cluster_uid"] diff --git a/spectrocloud/resource_cluster_maas.go b/spectrocloud/resource_cluster_maas.go index ba46148de..b5e43c7f3 100644 --- a/spectrocloud/resource_cluster_maas.go +++ b/spectrocloud/resource_cluster_maas.go @@ -145,6 +145,12 @@ func resourceClusterMaas() *schema.Resource { Required: true, Description: "Domain name in which the cluster to be provisioned.", }, + "enable_lxd_vm": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether to enable LXD VM. Default is `false`.", + }, }, }, }, @@ -268,6 +274,37 @@ func resourceClusterMaas() *schema.Resource { }, }, }, + "use_lxd_vm": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether to use LXD VM. Default is `false`.", + }, + "network": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "network_name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the network in which VMs are created/located.", + }, + "parent_pool_uid": { + Type: schema.TypeString, + Optional: true, + Description: "The UID of the parent pool which allocates IPs for this IPPool.", + }, + "static_ip": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether to use static IP. Default is `false`.", + }, + }, + }, + }, }, }, }, @@ -401,6 +438,9 @@ func flattenClusterConfigsMaas(config *models.V1MaasCloudConfig) []interface{} { if config.Spec.ClusterConfig.Domain != nil { m["domain"] = *config.Spec.ClusterConfig.Domain } + if config.Spec.ClusterConfig.EnableLxdVM { + m["enable_lxd_vm"] = true + } return []interface{}{m} } @@ -444,6 +484,16 @@ func flattenMachinePoolConfigsMaas(machinePools []*models.V1MaasMachinePoolConfi } } oi["node_tags"] = machinePool.Tags + oi["use_lxd_vm"] = machinePool.UseLxdVM + + if machinePool.Network != nil { + network := make(map[string]interface{}) + network["network_name"] = *machinePool.Network.NetworkName + network["parent_pool_uid"] = machinePool.Network.ParentPoolRef.UID + network["static_ip"] = machinePool.Network.StaticIP + oi["network"] = []interface{}{network} + } + ois[i] = oi } @@ -562,7 +612,8 @@ func toMaasCluster(c *client.V1Client, d *schema.ResourceData) (*models.V1Spectr Profiles: profiles, Policies: toPolicies(d), CloudConfig: &models.V1MaasClusterConfig{ - Domain: &DomainVal, + Domain: &DomainVal, + EnableLxdVM: d.Get("enable_lxd_vm").(bool), }, }, } @@ -603,15 +654,15 @@ func toMachinePoolMaas(machinePool interface{}) (*models.V1MaasMachinePoolConfig InstanceType := m["instance_type"].([]interface{})[0].(map[string]interface{}) log.Printf("Create machine pool %s", InstanceType) - min := int32(m["count"].(int)) - max := int32(m["count"].(int)) + min := SafeInt32(m["count"].(int)) + max := SafeInt32(m["count"].(int)) if m["min"] != nil { - min = int32(m["min"].(int)) + min = SafeInt32(m["min"].(int)) } if m["max"] != nil { - max = int32(m["max"].(int)) + max = SafeInt32(m["max"].(int)) } var nodePoolTags []string for _, nt := range m["node_tags"].(*schema.Set).List() { @@ -622,10 +673,11 @@ func toMachinePoolMaas(machinePool interface{}) (*models.V1MaasMachinePoolConfig CloudConfig: &models.V1MaasMachinePoolCloudConfigEntity{ Azs: azs, InstanceType: &models.V1MaasInstanceType{ - MinCPU: int32(InstanceType["min_cpu"].(int)), - MinMemInMB: int32(InstanceType["min_memory_mb"].(int)), + MinCPU: SafeInt32(InstanceType["min_cpu"].(int)), + MinMemInMB: SafeInt32(InstanceType["min_memory_mb"].(int)), }, - Tags: nodePoolTags, + Tags: nodePoolTags, + UseLxdVM: m["use_lxd_vm"].(bool), }, PoolConfig: &models.V1MachinePoolConfigEntity{ AdditionalLabels: toAdditionalNodePoolLabels(m), @@ -633,7 +685,7 @@ func toMachinePoolMaas(machinePool interface{}) (*models.V1MaasMachinePoolConfig IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, @@ -642,6 +694,17 @@ func toMachinePoolMaas(machinePool interface{}) (*models.V1MaasMachinePoolConfig MaxSize: max, }, } + + if len(m["network"].([]interface{})) > 0 { + network := m["network"].([]interface{})[0].(map[string]interface{}) + net := &models.V1MaasNetworkConfigEntity{ + NetworkName: types.Ptr(network["network_name"].(string)), + StaticIP: network["static_ip"].(bool), + ParentPoolUID: network["parent_pool_uid"].(string), + } + mp.CloudConfig.Network = net + } + if len(m["placement"].([]interface{})) > 0 { Placement := m["placement"].([]interface{})[0].(map[string]interface{}) mp.CloudConfig.ResourcePool = types.Ptr(Placement["resource_pool"].(string)) @@ -655,7 +718,7 @@ func toMachinePoolMaas(machinePool interface{}) (*models.V1MaasMachinePoolConfig if m["node_repave_interval"] != nil { nodeRepaveInterval = m["node_repave_interval"].(int) } - mp.PoolConfig.NodeRepaveInterval = int32(nodeRepaveInterval) + mp.PoolConfig.NodeRepaveInterval = SafeInt32(nodeRepaveInterval) } else { err := ValidationNodeRepaveIntervalForControlPlane(m["node_repave_interval"].(int)) if err != nil { diff --git a/spectrocloud/resource_cluster_mass_test.go b/spectrocloud/resource_cluster_mass_test.go index 654ab9a89..ad4003370 100644 --- a/spectrocloud/resource_cluster_mass_test.go +++ b/spectrocloud/resource_cluster_mass_test.go @@ -52,6 +52,7 @@ func TestFlattenMachinePoolConfigsMaas(t *testing.T) { Type: "RollingUpdateScaleOut", }, UseControlPlaneAsWorker: true, + UseLxdVM: false, } mockMachinePools = append(mockMachinePools, mp) config := &models.V1MaasClusterConfig{ @@ -77,8 +78,9 @@ func TestFlattenMachinePoolConfigsMaas(t *testing.T) { "min_cpu": 2, }, }, - "azs": []string{"zone1", "zone2"}, - "node_tags": []string{"test"}, + "azs": []string{"zone1", "zone2"}, + "node_tags": []string{"test"}, + "use_lxd_vm": false, "placement": []interface{}{ map[string]interface{}{ "resource_pool": "maas_resource_pool", @@ -121,8 +123,16 @@ func TestToMachinePoolMaas(t *testing.T) { "resource_pool": "test_resource_pool", }, }, - "azs": schema.NewSet(schema.HashString, []interface{}{"zone1", "zone2"}), - "node_tags": schema.NewSet(schema.HashString, []interface{}{"test"}), + "azs": schema.NewSet(schema.HashString, []interface{}{"zone1", "zone2"}), + "node_tags": schema.NewSet(schema.HashString, []interface{}{"test"}), + "use_lxd_vm": false, + "network": []interface{}{ + map[string]interface{}{ + "network_name": "test_network", + "parent_pool_uid": "test_pool_uid", + "static_ip": false, + }, + }, } rp := "test_resource_pool" size := int32(2) @@ -133,6 +143,12 @@ func TestToMachinePoolMaas(t *testing.T) { InstanceType: &models.V1MaasInstanceType{MinCPU: 2, MinMemInMB: 500}, ResourcePool: &rp, Tags: []string{"test"}, + UseLxdVM: false, + Network: &models.V1MaasNetworkConfigEntity{ + NetworkName: types.Ptr("test_network"), + ParentPoolUID: "test_pool_uid", + StaticIP: false, + }, }, PoolConfig: &models.V1MachinePoolConfigEntity{ AdditionalLabels: map[string]string{"TF": "test_label"}, diff --git a/spectrocloud/resource_cluster_openstack.go b/spectrocloud/resource_cluster_openstack.go index 88996d73f..d854964b9 100644 --- a/spectrocloud/resource_cluster_openstack.go +++ b/spectrocloud/resource_cluster_openstack.go @@ -612,7 +612,7 @@ func toMachinePoolOpenStack(machinePool interface{}) (*models.V1OpenStackMachine IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, @@ -625,7 +625,7 @@ func toMachinePoolOpenStack(machinePool interface{}) (*models.V1OpenStackMachine if m["node_repave_interval"] != nil { nodeRepaveInterval = m["node_repave_interval"].(int) } - mp.PoolConfig.NodeRepaveInterval = int32(nodeRepaveInterval) + mp.PoolConfig.NodeRepaveInterval = SafeInt32(nodeRepaveInterval) } else { err := ValidationNodeRepaveIntervalForControlPlane(m["node_repave_interval"].(int)) if err != nil { diff --git a/spectrocloud/resource_cluster_profile.go b/spectrocloud/resource_cluster_profile.go index 64131c8f8..6873ac2e3 100644 --- a/spectrocloud/resource_cluster_profile.go +++ b/spectrocloud/resource_cluster_profile.go @@ -224,7 +224,7 @@ func resourceClusterProfileUpdate(ctx context.Context, d *schema.ResourceData, m } } - if d.HasChanges("name") || d.HasChanges("tags") || d.HasChanges("pack") { + if d.HasChanges("name") || d.HasChanges("tags") || d.HasChanges("pack") || d.HasChanges("description") { log.Printf("Updating packs") cp, err := c.GetClusterProfile(d.Id()) if err != nil { @@ -527,7 +527,9 @@ func toClusterProfilePackUpdateWithResolution(pSrc interface{}, packs []*models. pUID = resolvedUID } case models.V1PackTypeManifest: - pUID = "spectro-manifest-pack" + if pUID == "" { + pUID = "spectro-manifest-pack" + } } pack := &models.V1PackManifestUpdateEntity{ diff --git a/spectrocloud/resource_cluster_profile_import_feature.go b/spectrocloud/resource_cluster_profile_import_feature.go index 6e6fbe356..78700b796 100644 --- a/spectrocloud/resource_cluster_profile_import_feature.go +++ b/spectrocloud/resource_cluster_profile_import_feature.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "os" + "path/filepath" + "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -57,6 +59,11 @@ func resourceClusterProfileImportFeatureCreate(ctx context.Context, d *schema.Re func toClusterProfileImportCreate(d *schema.ResourceData) (*os.File, error) { importFilePath := d.Get("import_file").(string) + // Validate file path to prevent directory traversal attacks + if !isValidFilePath(importFilePath) { + return nil, fmt.Errorf("invalid file path: %s", importFilePath) + } + // #nosec G304 importFile, err := os.Open(importFilePath) if err != nil { return nil, fmt.Errorf("error opening import file: %s", err) @@ -71,6 +78,28 @@ func toClusterProfileImportCreate(d *schema.ResourceData) (*os.File, error) { return importFile, nil } +// isValidFilePath checks if the file path is safe and doesn't contain directory traversal attempts +func isValidFilePath(filePath string) bool { + // Check for directory traversal patterns + if strings.Contains(filePath, "..") || strings.Contains(filePath, "//") { + return false + } + + // Ensure the path is absolute or relative to current directory + absPath, err := filepath.Abs(filePath) + if err != nil { + return false + } + + // Check if the resolved path is within the current working directory + cwd, err := os.Getwd() + if err != nil { + return false + } + + return strings.HasPrefix(absPath, cwd) +} + func resourceClusterProfileImportFeatureRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { resourceContext := d.Get("context").(string) var diags diag.Diagnostics diff --git a/spectrocloud/resource_cluster_virtual.go b/spectrocloud/resource_cluster_virtual.go index 44b7df121..50c0a8c4c 100644 --- a/spectrocloud/resource_cluster_virtual.go +++ b/spectrocloud/resource_cluster_virtual.go @@ -21,7 +21,10 @@ func resourceClusterVirtual() *schema.Resource { ReadContext: resourceClusterVirtualRead, UpdateContext: resourceClusterVirtualUpdate, DeleteContext: resourceClusterDelete, - Description: "A resource to manage a Palette Virtual Cluster.", + Importer: &schema.ResourceImporter{ + StateContext: resourceClusterVirtualImport, + }, + Description: "A resource to manage a Palette Virtual Cluster.", Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(60 * time.Minute), @@ -60,9 +63,9 @@ func resourceClusterVirtual() *schema.Resource { Description: "The description of the cluster. Default value is empty string.", }, "host_cluster_uid": { - Type: schema.TypeString, - Optional: true, - ExactlyOneOf: []string{"host_cluster_uid", "cluster_group_uid"}, + Type: schema.TypeString, + Optional: true, + // ExactlyOneOf: []string{"host_cluster_uid", "cluster_group_uid"}, ValidateFunc: validation.StringNotInSlice([]string{""}, false), }, "cluster_group_uid": { @@ -463,12 +466,12 @@ func toMachinePoolVirtual(resources map[string]interface{}) *models.V1VirtualMac mp := &models.V1VirtualMachinePoolConfigEntity{ CloudConfig: &models.V1VirtualMachinePoolCloudConfigEntity{ InstanceType: &models.V1VirtualInstanceType{ - MaxCPU: int32(maxCpu), - MaxMemInMiB: int32(maxMemInMb), - MaxStorageGiB: int32(maxStorageInGb), - MinCPU: int32(minCpu), - MinMemInMiB: int32(minMemInMb), - MinStorageGiB: int32(minStorageInGb), + MaxCPU: SafeInt32(maxCpu), + MaxMemInMiB: SafeInt32(maxMemInMb), + MaxStorageGiB: SafeInt32(maxStorageInGb), + MinCPU: SafeInt32(minCpu), + MinMemInMiB: SafeInt32(minMemInMb), + MinStorageGiB: SafeInt32(minStorageInGb), }, }, } @@ -485,12 +488,12 @@ func toVirtualClusterResize(resources map[string]interface{}) *models.V1VirtualC minStorageInGb := resources["min_storage_in_gb"].(int) VCResize := &models.V1VirtualClusterResize{ InstanceType: &models.V1VirtualInstanceType{ - MaxCPU: int32(maxCpu), - MaxMemInMiB: int32(maxMemInMb), - MaxStorageGiB: int32(maxStorageInGb), - MinCPU: int32(minCpu), - MinMemInMiB: int32(minMemInMb), - MinStorageGiB: int32(minStorageInGb), + MaxCPU: SafeInt32(maxCpu), + MaxMemInMiB: SafeInt32(maxMemInMb), + MaxStorageGiB: SafeInt32(maxStorageInGb), + MinCPU: SafeInt32(minCpu), + MinMemInMiB: SafeInt32(minMemInMb), + MinStorageGiB: SafeInt32(minStorageInGb), }, } return VCResize diff --git a/spectrocloud/resource_cluster_virtual_import.go b/spectrocloud/resource_cluster_virtual_import.go new file mode 100644 index 000000000..5e2bc4e26 --- /dev/null +++ b/spectrocloud/resource_cluster_virtual_import.go @@ -0,0 +1,70 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceClusterVirtualImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + // Virtual clusters have a context, default to "project" + c := getV1ClientWithResourceContext(m, "project") + + // The import ID should be the cluster UID + virtualClusterUID := d.Id() + + // Validate that the cluster exists and we can access it + cluster, err := c.GetCluster(virtualClusterUID) + if err != nil { + return nil, fmt.Errorf("could not retrieve virtual cluster for import: %s", err) + } + if cluster == nil { + return nil, fmt.Errorf("virtual cluster with ID %s not found", virtualClusterUID) + } + + // Set the cluster name from the retrieved cluster + if err := d.Set("name", cluster.Metadata.Name); err != nil { + return nil, err + } + + // Set the context to project as default for import + if err := d.Set("context", "project"); err != nil { + return nil, err + } + + if err := d.Set("cluster_group_uid", cluster.Spec.ClusterConfig.HostClusterConfig.ClusterGroup.UID); err != nil { + return nil, err + } + + // setting up default settings for import + if err := d.Set("host_cluster_uid", cluster.Spec.ClusterConfig.HostClusterConfig.HostCluster.UID); err != nil { + return nil, err + } + if err := d.Set("apply_setting", "DownloadAndInstall"); err != nil { + return nil, err + } + if err := d.Set("force_delete", false); err != nil { + return nil, err + } + if err := d.Set("force_delete_delay", 20); err != nil { + return nil, err + } + if err := d.Set("skip_completion", false); err != nil { + return nil, err + } + if err := d.Set("os_patch_on_boot", false); err != nil { + return nil, err + } + if err := d.Set("pause_cluster", false); err != nil { + return nil, err + } + + // Read all cluster data to populate the state + diags := resourceClusterVirtualRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read virtual cluster for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} diff --git a/spectrocloud/resource_cluster_vsphere.go b/spectrocloud/resource_cluster_vsphere.go index 6c7f85a46..ead5f6c61 100644 --- a/spectrocloud/resource_cluster_vsphere.go +++ b/spectrocloud/resource_cluster_vsphere.go @@ -15,6 +15,7 @@ import ( "github.com/spectrocloud/palette-sdk-go/api/models" "github.com/spectrocloud/palette-sdk-go/client" + "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/constants" "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/schemas" "github.com/spectrocloud/terraform-provider-spectrocloud/types" ) @@ -664,7 +665,7 @@ func resourceClusterVsphereUpdate(ctx context.Context, d *schema.ResourceData, m oraw, nraw := d.GetChange("machine_pool") if oraw != nil && nraw != nil { if ok, err := ValidateMachinePoolChange(oraw, nraw); ok { - return diag.Errorf(err.Error()) + return diag.Errorf("%s", err.Error()) } } if oraw == nil { @@ -848,20 +849,44 @@ func toMachinePoolVsphere(machinePool interface{}) (*models.V1VsphereMachinePool } ins := m["instance_type"].([]interface{})[0].(map[string]interface{}) + + // Check bounds before conversion + diskSizeInt := ins["disk_size_gb"].(int) + memoryInt := ins["memory_mb"].(int) + cpuInt := ins["cpu"].(int) + + if diskSizeInt > constants.Int32MaxValue || memoryInt > constants.Int64MaxValue || cpuInt > constants.Int32MaxValue { + return nil, fmt.Errorf("instance type values out of range: disk_size_gb=%d, memory_mb=%d, cpu=%d", diskSizeInt, memoryInt, cpuInt) + } + instanceType := models.V1VsphereInstanceType{ - DiskGiB: types.Ptr(int32(ins["disk_size_gb"].(int))), - MemoryMiB: types.Ptr(int64(ins["memory_mb"].(int))), - NumCPUs: types.Ptr(int32(ins["cpu"].(int))), + DiskGiB: types.Ptr(SafeInt32(diskSizeInt)), + MemoryMiB: types.Ptr(SafeInt64(memoryInt)), + NumCPUs: types.Ptr(SafeInt32(cpuInt)), + } + + countInt := m["count"].(int) + if countInt > constants.Int32MaxValue { + return nil, fmt.Errorf("count value %d is out of range for int32", countInt) } - min := int32(m["count"].(int)) - max := int32(m["count"].(int)) + + min := SafeInt32(countInt) + max := SafeInt32(countInt) if m["min"] != nil { - min = int32(m["min"].(int)) + minInt := m["min"].(int) + if minInt > constants.Int32MaxValue { + return nil, fmt.Errorf("min value %d is out of range for int32", minInt) + } + min = SafeInt32(minInt) } if m["max"] != nil { - max = int32(m["max"].(int)) + maxInt := m["max"].(int) + if maxInt > constants.Int32MaxValue { + return nil, fmt.Errorf("max value %d is out of range for int32", maxInt) + } + max = SafeInt32(maxInt) } mp := &models.V1VsphereMachinePoolConfigEntity{ @@ -875,7 +900,7 @@ func toMachinePoolVsphere(machinePool interface{}) (*models.V1VsphereMachinePool IsControlPlane: controlPlane, Labels: labels, Name: types.Ptr(m["name"].(string)), - Size: types.Ptr(int32(m["count"].(int))), + Size: types.Ptr(SafeInt32(m["count"].(int))), UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, @@ -890,9 +915,16 @@ func toMachinePoolVsphere(machinePool interface{}) (*models.V1VsphereMachinePool if m["node_repave_interval"] != nil { nodeRepaveInterval = m["node_repave_interval"].(int) } - mp.PoolConfig.NodeRepaveInterval = int32(nodeRepaveInterval) + if nodeRepaveInterval > constants.Int32MaxValue { + return nil, fmt.Errorf("node_repave_interval value %d is out of range for int32", nodeRepaveInterval) + } + mp.PoolConfig.NodeRepaveInterval = SafeInt32(nodeRepaveInterval) } else { - err := ValidationNodeRepaveIntervalForControlPlane(m["node_repave_interval"].(int)) + nodeRepaveInterval := m["node_repave_interval"].(int) + if nodeRepaveInterval > constants.Int32MaxValue { + return nil, fmt.Errorf("node_repave_interval value %d is out of range for int32", nodeRepaveInterval) + } + err := ValidationNodeRepaveIntervalForControlPlane(nodeRepaveInterval) if err != nil { return mp, err } diff --git a/spectrocloud/resource_developer_setting.go b/spectrocloud/resource_developer_setting.go index 326691314..62783272a 100644 --- a/spectrocloud/resource_developer_setting.go +++ b/spectrocloud/resource_developer_setting.go @@ -3,11 +3,13 @@ package spectrocloud import ( "context" "fmt" + "time" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/spectrocloud/palette-sdk-go/api/models" - "time" + "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/constants" ) func resourceDeveloperSetting() *schema.Resource { @@ -66,17 +68,34 @@ func resourceDeveloperSetting() *schema.Resource { } func toDeveloperSetting(d *schema.ResourceData) (*models.V1DeveloperCredit, *models.V1TenantEnableClusterGroup) { + cpuInt := d.Get("cpu").(int) + memoryInt := d.Get("memory").(int) + storageInt := d.Get("storage").(int) + virtualClustersLimitInt := d.Get("virtual_clusters_limit").(int) + + // Check bounds for int32 conversion + if cpuInt > constants.Int32MaxValue || memoryInt > constants.Int32MaxValue || storageInt > constants.Int32MaxValue || virtualClustersLimitInt > constants.Int32MaxValue { + // Return default values if any value is out of range + return &models.V1DeveloperCredit{ + CPU: 12, + MemoryGiB: 16, + StorageGiB: 20, + VirtualClustersLimit: 2, + }, &models.V1TenantEnableClusterGroup{ + HideSystemClusterGroups: false, + } + } + devCredit := &models.V1DeveloperCredit{ - CPU: int32(d.Get("cpu").(int)), - MemoryGiB: int32(d.Get("memory").(int)), - StorageGiB: int32(d.Get("storage").(int)), - VirtualClustersLimit: int32(d.Get("virtual_clusters_limit").(int)), + CPU: SafeInt32(cpuInt), + MemoryGiB: SafeInt32(memoryInt), + StorageGiB: SafeInt32(storageInt), + VirtualClustersLimit: SafeInt32(virtualClustersLimitInt), } sysClusterGroupPref := &models.V1TenantEnableClusterGroup{ HideSystemClusterGroups: d.Get("hide_system_cluster_group").(bool), } return devCredit, sysClusterGroupPref - } func toDeveloperSettingDefault(d *schema.ResourceData) (*models.V1DeveloperCredit, *models.V1TenantEnableClusterGroup) { diff --git a/spectrocloud/resource_filter.go b/spectrocloud/resource_filter.go index dac6f6090..57e422e5a 100644 --- a/spectrocloud/resource_filter.go +++ b/spectrocloud/resource_filter.go @@ -16,7 +16,10 @@ func resourceFilter() *schema.Resource { ReadContext: resourceFilterRead, UpdateContext: resourceFilterUpdate, DeleteContext: resourceFilterDelete, - Description: "A resource for creating and managing filters.", + Importer: &schema.ResourceImporter{ + StateContext: resourceFilterImport, + }, + Description: "A resource for creating and managing filters.", Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/spectrocloud/resource_filter_import.go b/spectrocloud/resource_filter_import.go new file mode 100644 index 000000000..9ea110fd4 --- /dev/null +++ b/spectrocloud/resource_filter_import.go @@ -0,0 +1,44 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceFilterImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + c := getV1ClientWithResourceContext(m, "") + + // The import ID should be the filter UID + filterUID := d.Id() + + // Validate that the filter exists and we can access it + tagFilter, err := c.GetTagFilter(filterUID) + if err != nil { + return nil, fmt.Errorf("could not retrieve filter for import: %s", err) + } + if tagFilter == nil { + return nil, fmt.Errorf("filter with ID %s not found", filterUID) + } + + // Set the filter name from the retrieved filter metadata + if tagFilter.Metadata != nil && tagFilter.Metadata.Name != "" { + metadata := []interface{}{ + map[string]interface{}{ + "name": tagFilter.Metadata.Name, + }, + } + if err := d.Set("metadata", metadata); err != nil { + return nil, err + } + } + + // Read all filter data to populate the state + diags := resourceFilterRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read filter for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} diff --git a/spectrocloud/resource_macro.go b/spectrocloud/resource_macro.go deleted file mode 100644 index 6e59ac14d..000000000 --- a/spectrocloud/resource_macro.go +++ /dev/null @@ -1,170 +0,0 @@ -package spectrocloud - -import ( - "context" - "time" - - "github.com/spectrocloud/palette-sdk-go/client/apiutil" - - "github.com/spectrocloud/palette-sdk-go/api/models" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourceMacro() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceMacroCreate, - ReadContext: resourceMacroRead, - UpdateContext: resourceMacroUpdate, - DeleteContext: resourceMacroDelete, - Description: "A resource for creating and managing service output variables and macro. (Deprecated)", - - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(10 * time.Minute), - Update: schema.DefaultTimeout(10 * time.Minute), - Delete: schema.DefaultTimeout(10 * time.Minute), - }, - - SchemaVersion: 2, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The name of the macro or service variable output.", - }, - "value": { - Type: schema.TypeString, - Required: true, - Description: "The value that the macro or service output variable will contain.", - }, - "project": { - Type: schema.TypeString, - Optional: true, - Description: "The Spectro Cloud project name.", - }, - }, - } -} - -func resourceMacroCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - - c := getV1ClientWithResourceContext(m, "") - var diags diag.Diagnostics - uid := "" - var err error - if v, ok := d.GetOk("project"); ok && v.(string) != "" { //if project name is set it's a project scope - uid, err = c.GetProjectUID(v.(string)) - if err != nil { - return diag.FromErr(err) - } - } - err = c.CreateMacro(uid, toMacro(d)) - if err != nil { - return diag.FromErr(err) - } - name := d.Get("name").(string) - d.SetId(getMacroId(uid, name)) - return diags -} - -func resourceMacroRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - - c := getV1ClientWithResourceContext(m, "") - var diags diag.Diagnostics - var macro *models.V1Macro - var err error - uid := "" - - if v, ok := d.GetOk("project"); ok && v.(string) != "" { //if project name is set it's a project scope - uid, err = c.GetProjectUID(v.(string)) - if err != nil { - return handleReadError(d, err, diags) - } - } - - macro, err = c.GetMacro(d.Get("name").(string), uid) - if err != nil { - return handleReadError(d, err, diags) - } else if macro == nil { - // Deleted - Terraform will recreate it - d.SetId("") - return diags - } - - d.SetId(getMacroId(uid, d.Get("name").(string))) - - if err := d.Set("name", macro.Name); err != nil { - return diag.FromErr(err) - } - if err := d.Set("value", macro.Value); err != nil { - return diag.FromErr(err) - } - return diags -} - -func resourceMacroUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - - c := getV1ClientWithResourceContext(m, "") - var diags diag.Diagnostics - var err error - uid := "" - if v, ok := d.GetOk("project"); ok && v.(string) != "" { //if project name is set it's a project scope - uid, err = c.GetProjectUID(v.(string)) - if err != nil { - return diag.FromErr(err) - } - } - if d.HasChange("value") && !d.HasChange("name") { - err = c.UpdateMacro(uid, toMacro(d)) - if err != nil { - return diag.FromErr(err) - } - - } - return diags -} - -func resourceMacroDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - - c := getV1ClientWithResourceContext(m, "") - var diags diag.Diagnostics - var err error - uid := "" - - if v, ok := d.GetOk("project"); ok && v.(string) != "" { //if project name is set it's a project scope - uid, err = c.GetProjectUID(v.(string)) - if err != nil { - return diag.FromErr(err) - } - } - err = c.DeleteMacro(uid, toMacro(d)) - if err != nil { - return diag.FromErr(err) - } - return diags -} - -func toMacro(d *schema.ResourceData) *models.V1Macros { - - var macro []*models.V1Macro - macro = append(macro, &models.V1Macro{ - Name: d.Get("name").(string), - Value: d.Get("value").(string), - }) - retMacros := &models.V1Macros{ - Macros: macro, - } - return retMacros -} - -func getMacroId(uid, name string) string { - var hash string - if uid != "" { - hash = apiutil.StringHash(name + uid) - } else { - hash = apiutil.StringHash(name + "%tenant") - } - return hash -} diff --git a/spectrocloud/resource_pcg_dns_map.go b/spectrocloud/resource_pcg_dns_map.go index e94c789a2..fe7751153 100644 --- a/spectrocloud/resource_pcg_dns_map.go +++ b/spectrocloud/resource_pcg_dns_map.go @@ -2,12 +2,13 @@ package spectrocloud import ( "context" + "regexp" + "time" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/spectrocloud/palette-sdk-go/api/models" - "regexp" - "time" ) func resourcePrivateCloudGatewayDNSMap() *schema.Resource { @@ -16,7 +17,10 @@ func resourcePrivateCloudGatewayDNSMap() *schema.Resource { ReadContext: resourcePCGDNSMapRead, UpdateContext: resourcePCGDNSMapUpdate, DeleteContext: resourcePCGDNSMapDelete, - Description: "This resource allows for the management of DNS mappings for private cloud gateways. This helps ensure proper DNS resolution for resources within the private cloud environment.", + Importer: &schema.ResourceImporter{ + StateContext: resourcePrivateCloudGatewayDNSMapImport, + }, + Description: "This resource allows for the management of DNS mappings for private cloud gateways. This helps ensure proper DNS resolution for resources within the private cloud environment.", Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/spectrocloud/resource_pcg_dns_map_import.go b/spectrocloud/resource_pcg_dns_map_import.go new file mode 100644 index 000000000..63abdb6b0 --- /dev/null +++ b/spectrocloud/resource_pcg_dns_map_import.go @@ -0,0 +1,93 @@ +package spectrocloud + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourcePrivateCloudGatewayDNSMapImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonPrivateCloudGatewayDNSMap(d, m) + if err != nil { + return nil, err + } + + // Read all DNS map data to populate the state + diags := resourcePCGDNSMapRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read PCG DNS map for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonPrivateCloudGatewayDNSMap(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // DNS maps are tenant-level resources, so use tenant context + c := getV1ClientWithResourceContext(m, "tenant") + + // Parse the import ID to extract PCG ID and DNS map ID + // Expected format: pcg_id:dns_map_id + importID := d.Id() + if importID == "" { + return nil, fmt.Errorf("DNS map import ID is required") + } + + parts := strings.Split(importID, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid import ID format. Expected format: pcg_id:dns_map_id, got: %s", importID) + } + + pcgID := parts[0] + dnsMapID := parts[1] + + // Validate that the PCG exists + pcg, err := c.GetPCGByID(pcgID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve private cloud gateway: %s", err) + } + if pcg == nil { + return nil, fmt.Errorf("private cloud gateway with ID %s not found", pcgID) + } + + // Validate that the DNS map exists + dnsMap, err := c.GetVsphereDNSMap(dnsMapID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve DNS map: %s", err) + } + if dnsMap == nil { + return nil, fmt.Errorf("DNS map with ID %s not found", dnsMapID) + } + + // Set the required fields for the resource from the retrieved DNS map + if err := d.Set("private_cloud_gateway_id", pcgID); err != nil { + return nil, err + } + + if dnsMap.Spec != nil { + if dnsMap.Spec.DNSName != nil && *dnsMap.Spec.DNSName != "" { + if err := d.Set("search_domain_name", *dnsMap.Spec.DNSName); err != nil { + return nil, err + } + } + + if dnsMap.Spec.Datacenter != nil && *dnsMap.Spec.Datacenter != "" { + if err := d.Set("data_center", *dnsMap.Spec.Datacenter); err != nil { + return nil, err + } + } + + if dnsMap.Spec.Network != nil && *dnsMap.Spec.Network != "" { + if err := d.Set("network", *dnsMap.Spec.Network); err != nil { + return nil, err + } + } + } + + // Set the ID to just the DNS map ID (the read function will use private_cloud_gateway_id) + d.SetId(dnsMapID) + + return c, nil +} diff --git a/spectrocloud/resource_pcg_ippool.go b/spectrocloud/resource_pcg_ippool.go index cdd94087c..6bb7f3e5d 100644 --- a/spectrocloud/resource_pcg_ippool.go +++ b/spectrocloud/resource_pcg_ippool.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spectrocloud/palette-sdk-go/api/models" + "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/constants" ) func resourcePrivateCloudGatewayIpPool() *schema.Resource { @@ -19,7 +20,10 @@ func resourcePrivateCloudGatewayIpPool() *schema.Resource { ReadContext: resourceIpPoolRead, UpdateContext: resourceIpPoolUpdate, DeleteContext: resourceIpPoolDelete, - Description: "A Resource to manage IP pools for Private Cloud Gateway.", + Importer: &schema.ResourceImporter{ + StateContext: resourcePrivateCloudGatewayIpPoolImport, + }, + Description: "A Resource to manage IP pools for Private Cloud Gateway.", Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), @@ -201,10 +205,16 @@ func resourceIpPoolDelete(ctx context.Context, d *schema.ResourceData, m interfa } func toIpPool(d *schema.ResourceData) *models.V1IPPoolInputEntity { + prefixInt := d.Get("prefix").(int) + if prefixInt > constants.Int32MaxValue { + // This should not happen in practice as prefix is typically 0-32 for CIDR notation + prefixInt = 24 // Default to /24 if out of range + } + pool := &models.V1Pool{ Gateway: d.Get("gateway").(string), Nameserver: &models.V1Nameserver{}, - Prefix: int32(d.Get("prefix").(int)), + Prefix: SafeInt32(prefixInt), } if d.Get("network_type").(string) == "range" { diff --git a/spectrocloud/resource_pcg_ippool_import.go b/spectrocloud/resource_pcg_ippool_import.go new file mode 100644 index 000000000..76c1564ea --- /dev/null +++ b/spectrocloud/resource_pcg_ippool_import.go @@ -0,0 +1,86 @@ +package spectrocloud + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourcePrivateCloudGatewayIpPoolImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonPrivateCloudGatewayIpPool(d, m) + if err != nil { + return nil, err + } + + // Read all IP pool data to populate the state + diags := resourceIpPoolRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read IP pool for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonPrivateCloudGatewayIpPool(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // IP pools are tenant-level resources, so use tenant context + c := getV1ClientWithResourceContext(m, "tenant") + + // Parse the import ID to extract PCG ID and IP pool ID + // Expected format: pcg_id:ippool_id + importID := d.Id() + if importID == "" { + return nil, fmt.Errorf("IP pool import ID is required") + } + + parts := strings.Split(importID, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid import ID format. Expected format: pcg_id:ippool_id, got: %s", importID) + } + + pcgID := parts[0] + ipPoolID := parts[1] + + // Validate that the PCG exists + pcg, err := c.GetPCGByID(pcgID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve private cloud gateway: %s", err) + } + if pcg == nil { + return nil, fmt.Errorf("private cloud gateway with ID %s not found", pcgID) + } + + // Validate that the IP pool exists within the PCG + ipPool, err := c.GetIPPool(pcgID, ipPoolID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve IP pool: %s", err) + } + if ipPool == nil { + return nil, fmt.Errorf("IP pool with ID %s not found in PCG %s", ipPoolID, pcgID) + } + + // Set the required fields for the resource + if err := d.Set("private_cloud_gateway_id", pcgID); err != nil { + return nil, err + } + + if err := d.Set("name", ipPool.Metadata.Name); err != nil { + return nil, err + } + + // Set the network type based on the pool configuration + networkType := "range" // default + if ipPool.Spec != nil && ipPool.Spec.Pool != nil && len(ipPool.Spec.Pool.Subnet) > 0 { + networkType = "subnet" + } + if err := d.Set("network_type", networkType); err != nil { + return nil, err + } + + // Set the ID to just the IP pool ID (the read function will use private_cloud_gateway_id) + d.SetId(ipPoolID) + + return c, nil +} diff --git a/spectrocloud/resource_platform_setting.go b/spectrocloud/resource_platform_setting.go index b9166ce2a..e739d24cf 100644 --- a/spectrocloud/resource_platform_setting.go +++ b/spectrocloud/resource_platform_setting.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/spectrocloud/palette-sdk-go/api/models" + "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/constants" ) func resourcePlatformSetting() *schema.Resource { @@ -140,8 +141,12 @@ func updatePlatformSettings(d *schema.ResourceData, m interface{}) diag.Diagnost if platformSettingContext == tenantString { // session timeout if sessionTime, ok := d.GetOk("session_timeout"); ok { + sessionTimeInt := sessionTime.(int) + if sessionTimeInt > constants.Int32MaxValue { + return diag.FromErr(fmt.Errorf("session_timeout value %d is out of range for int32", sessionTimeInt)) + } err = c.UpdateSessionTimeout(tenantUID, - &models.V1AuthTokenSettings{ExpiryTimeMinutes: int32(sessionTime.(int))}) + &models.V1AuthTokenSettings{ExpiryTimeMinutes: SafeInt32(sessionTimeInt)}) if err != nil { return diag.FromErr(err) } @@ -391,8 +396,12 @@ func resourcePlatformSettingUpdate(ctx context.Context, d *schema.ResourceData, // session timeout if d.HasChange("session_timeout") { if sessionTime, ok := d.GetOk("session_timeout"); ok { + sessionTimeInt := sessionTime.(int) + if sessionTimeInt > constants.Int32MaxValue { + return diag.FromErr(fmt.Errorf("session_timeout value %d is out of range for int32", sessionTimeInt)) + } err = c.UpdateSessionTimeout(tenantUID, - &models.V1AuthTokenSettings{ExpiryTimeMinutes: int32(sessionTime.(int))}) + &models.V1AuthTokenSettings{ExpiryTimeMinutes: SafeInt32(sessionTimeInt)}) if err != nil { return diag.FromErr(err) } @@ -508,7 +517,7 @@ func updatePlatformSettingsDefault(d *schema.ResourceData, m interface{}) diag.D if platformSettingContext == tenantString { // session timeout err = c.UpdateSessionTimeout(tenantUID, - &models.V1AuthTokenSettings{ExpiryTimeMinutes: int32(240)}) + &models.V1AuthTokenSettings{ExpiryTimeMinutes: SafeInt32(240)}) if err != nil { return diag.FromErr(err) } diff --git a/spectrocloud/resource_project.go b/spectrocloud/resource_project.go index bf94005d3..210436387 100644 --- a/spectrocloud/resource_project.go +++ b/spectrocloud/resource_project.go @@ -15,7 +15,10 @@ func resourceProject() *schema.Resource { ReadContext: resourceProjectRead, UpdateContext: resourceProjectUpdate, DeleteContext: resourceProjectDelete, - Description: "Create and manage projects in Palette.", + Importer: &schema.ResourceImporter{ + StateContext: resourceProjectImport, + }, + Description: "Create and manage projects in Palette.", Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/spectrocloud/resource_project_import.go b/spectrocloud/resource_project_import.go new file mode 100644 index 000000000..3698dfa6e --- /dev/null +++ b/spectrocloud/resource_project_import.go @@ -0,0 +1,37 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceProjectImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + c := getV1ClientWithResourceContext(m, "") + + // The import ID should be the project UID + projectUID := d.Id() + + // Validate that the project exists and we can access it + project, err := c.GetProject(projectUID) + if err != nil { + return nil, fmt.Errorf("could not retrieve project for import: %s", err) + } + if project == nil { + return nil, fmt.Errorf("project with ID %s not found", projectUID) + } + + // Set the project name from the retrieved project + if err := d.Set("name", project.Metadata.Name); err != nil { + return nil, err + } + + // Read all project data to populate the state + diags := resourceProjectRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read project for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} diff --git a/spectrocloud/resource_registry_helm.go b/spectrocloud/resource_registry_helm.go index 2cbcd40e3..bb2f82704 100644 --- a/spectrocloud/resource_registry_helm.go +++ b/spectrocloud/resource_registry_helm.go @@ -19,6 +19,9 @@ func resourceRegistryHelm() *schema.Resource { ReadContext: resourceRegistryHelmRead, UpdateContext: resourceRegistryHelmUpdate, DeleteContext: resourceRegistryHelmDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceRegistryHelmImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/spectrocloud/resource_registry_helm_import.go b/spectrocloud/resource_registry_helm_import.go new file mode 100644 index 000000000..c379ef598 --- /dev/null +++ b/spectrocloud/resource_registry_helm_import.go @@ -0,0 +1,68 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourceRegistryHelmImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonRegistryHelm(d, m) + if err != nil { + return nil, err + } + + // Read all registry data to populate the state + diags := resourceRegistryHelmRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read Helm registry for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonRegistryHelm(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // Helm registries are tenant-level resources only + c := getV1ClientWithResourceContext(m, "tenant") + + // The import ID should be the registry UID + registryUID := d.Id() + if registryUID == "" { + return nil, fmt.Errorf("helm registry import ID is required") + } + + // Validate that the registry exists and we can access it + registry, err := c.GetHelmRegistry(registryUID) + if err != nil { + return nil, fmt.Errorf("unable to retrieve Helm registry: %s", err) + } + if registry == nil { + return nil, fmt.Errorf("helm registry with ID %s not found", registryUID) + } + + // Set the required fields for the resource + if err := d.Set("name", registry.Metadata.Name); err != nil { + return nil, err + } + + // Set the endpoint URL + if registry.Spec != nil && registry.Spec.Endpoint != nil && *registry.Spec.Endpoint != "" { + if err := d.Set("endpoint", *registry.Spec.Endpoint); err != nil { + return nil, err + } + } + + // Set the is_private field from the registry specification + if registry.Spec != nil { + if err := d.Set("is_private", registry.Spec.IsPrivate); err != nil { + return nil, err + } + } + + // Set the ID to the registry ID + d.SetId(registryUID) + + return c, nil +} diff --git a/spectrocloud/resource_registry_oci_ecr.go b/spectrocloud/resource_registry_oci_ecr.go index 9415abd62..d8c5bd817 100644 --- a/spectrocloud/resource_registry_oci_ecr.go +++ b/spectrocloud/resource_registry_oci_ecr.go @@ -4,9 +4,10 @@ import ( "context" "errors" "fmt" - "github.com/spectrocloud/palette-sdk-go/client" "time" + "github.com/spectrocloud/palette-sdk-go/client" + "github.com/go-openapi/strfmt" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -21,6 +22,9 @@ func resourceRegistryOciEcr() *schema.Resource { ReadContext: resourceRegistryEcrRead, UpdateContext: resourceRegistryEcrUpdate, DeleteContext: resourceRegistryEcrDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceRegistryOciImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), @@ -140,7 +144,7 @@ func resourceRegistryOciEcr() *schema.Resource { Type: schema.TypeBool, Optional: true, Default: false, - Description: "Disables TLS certificate verification when set to true. Use with caution as it may expose connections to security risks.", + Description: "Disables TLS certificate verification when set to true. ⚠️ WARNING: Setting this to true disables SSL certificate verification and makes connections vulnerable to man-in-the-middle attacks. Only use this when connecting to registries with self-signed certificates in trusted networks.", }, }, }, diff --git a/spectrocloud/resource_registry_oci_import.go b/spectrocloud/resource_registry_oci_import.go new file mode 100644 index 000000000..f966cb518 --- /dev/null +++ b/spectrocloud/resource_registry_oci_import.go @@ -0,0 +1,79 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourceRegistryOciImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonRegistryOci(d, m) + if err != nil { + return nil, err + } + + // Read all registry data to populate the state + diags := resourceRegistryEcrRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read OCI registry for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonRegistryOci(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // OCI registries are tenant-level resources only + c := getV1ClientWithResourceContext(m, "tenant") + + // The import ID should be the registry UID + registryUID := d.Id() + if registryUID == "" { + return nil, fmt.Errorf("OCI registry import ID is required") + } + + // Try to retrieve the registry as ECR first (most common) + registry, err := c.GetOciEcrRegistry(registryUID) + if err != nil { + // If ECR retrieval fails, try basic OCI registry + basicRegistry, basicErr := c.GetOciBasicRegistry(registryUID) + if basicErr != nil { + return nil, fmt.Errorf("unable to retrieve OCI registry as either ECR or basic type: ECR error: %s, Basic error: %s", err, basicErr) + } + if basicRegistry == nil { + return nil, fmt.Errorf("OCI registry with ID %s not found", registryUID) + } + + // Set required fields for basic registry + if err := d.Set("name", basicRegistry.Metadata.Name); err != nil { + return nil, err + } + if err := d.Set("type", "basic"); err != nil { + return nil, err + } + // Basic registries are typically private if they have authentication + isPrivate := basicRegistry.Spec.Auth != nil + if err := d.Set("is_private", isPrivate); err != nil { + return nil, err + } + } else if registry == nil { + return nil, fmt.Errorf("OCI registry with ID %s not found", registryUID) + } else { + // Set required fields for ECR registry + if err := d.Set("name", registry.Metadata.Name); err != nil { + return nil, err + } + if err := d.Set("type", "ecr"); err != nil { + return nil, err + } + if err := d.Set("is_private", registry.Spec.IsPrivate); err != nil { + return nil, err + } + } + + // Set the ID to the registry ID + d.SetId(registryUID) + + return c, nil +} diff --git a/spectrocloud/resource_ssh_key.go b/spectrocloud/resource_ssh_key.go index 6da72953d..653432eeb 100644 --- a/spectrocloud/resource_ssh_key.go +++ b/spectrocloud/resource_ssh_key.go @@ -16,7 +16,10 @@ func resourceSSHKey() *schema.Resource { ReadContext: resourceSSHKeyRead, UpdateContext: resourceSSHKeyUpdate, DeleteContext: resourceSSHKeyDelete, - Description: "The SSH key resource allows you to manage SSH keys in Palette.", + Importer: &schema.ResourceImporter{ + StateContext: resourceSSHKeyImport, + }, + Description: "The SSH key resource allows you to manage SSH keys in Palette.", Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/spectrocloud/resource_ssh_key_import.go b/spectrocloud/resource_ssh_key_import.go new file mode 100644 index 000000000..5f85deb7a --- /dev/null +++ b/spectrocloud/resource_ssh_key_import.go @@ -0,0 +1,101 @@ +package spectrocloud + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func resourceSSHKeyImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + _, err := GetCommonSSHKey(d, m) + if err != nil { + return nil, err + } + + // Read all SSH key data to populate the state + diags := resourceSSHKeyRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read SSH key for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} + +func GetCommonSSHKey(d *schema.ResourceData, m interface{}) (*client.V1Client, error) { + // Parse the import ID which can be either: + // 1. Simple format: ssh_key_id (defaults to project context) + // 2. Context format: ssh_key_id:context (explicit context) + importID := d.Id() + if importID == "" { + return nil, fmt.Errorf("SSH key import ID is required") + } + + var context string + var sshKeyID string + + // Check if the import ID contains context specification + parts := strings.Split(importID, ":") + if len(parts) == 2 { + // Format: ssh_key_id:context + sshKeyID = parts[0] + context = parts[1] + + // Validate context + if context != "project" && context != "tenant" { + return nil, fmt.Errorf("invalid context '%s'. Expected 'project' or 'tenant'", context) + } + } else if len(parts) == 1 { + // Format: ssh_key_id (default to project context) + context = "project" + sshKeyID = parts[0] + } else { + return nil, fmt.Errorf("invalid import ID format. Expected 'ssh_key_id' or 'ssh_key_id:context', got: %s", importID) + } + + // Try the specified context first + c := getV1ClientWithResourceContext(m, context) + sshKey, err := c.GetSSHKey(sshKeyID) + + if err != nil || sshKey == nil { + // If not found in specified context, try the other context + otherContext := "tenant" + if context == "tenant" { + otherContext = "project" + } + + c = getV1ClientWithResourceContext(m, otherContext) + sshKey, err = c.GetSSHKey(sshKeyID) + + if err != nil { + return nil, fmt.Errorf("unable to retrieve SSH key in either project or tenant context: %s", err) + } + if sshKey == nil { + return nil, fmt.Errorf("SSH key with ID %s not found in either project or tenant context", sshKeyID) + } + + // Update context to the one where we found the resource + context = otherContext + } + + // Set the required fields for the resource + if err := d.Set("name", sshKey.Metadata.Name); err != nil { + return nil, err + } + + if err := d.Set("context", context); err != nil { + return nil, err + } + + // Note: We don't set the 'ssh_key' field during import because it's marked as sensitive. + // This follows the same pattern as other sensitive fields (like credentials in cloud accounts). + // The user will need to provide the ssh_key value in their Terraform configuration after import. + // This is a security best practice to prevent sensitive data from being stored in state during import. + + // Set the ID to the SSH key ID + d.SetId(sshKeyID) + + return c, nil +} diff --git a/spectrocloud/resource_sso.go b/spectrocloud/resource_sso.go index 4572edfb3..d67e3c689 100644 --- a/spectrocloud/resource_sso.go +++ b/spectrocloud/resource_sso.go @@ -94,7 +94,7 @@ func resourceSSO() *schema.Resource { Type: schema.TypeBool, Optional: true, Default: false, - Description: "Boolean to skip TLS verification for identity provider communication.", + Description: "Boolean to skip TLS verification for identity provider communication. ⚠️ WARNING: Setting this to true disables SSL certificate verification and makes connections vulnerable to man-in-the-middle attacks. Only use this when connecting to identity providers with self-signed certificates in trusted networks.", }, "client_id": { Type: schema.TypeString, diff --git a/spectrocloud/resource_team.go b/spectrocloud/resource_team.go index 2bf0ecc2d..53d65c6bb 100644 --- a/spectrocloud/resource_team.go +++ b/spectrocloud/resource_team.go @@ -21,6 +21,9 @@ func resourceTeam() *schema.Resource { ReadContext: resourceTeamRead, UpdateContext: resourceTeamUpdate, DeleteContext: resourceTeamDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceTeamImport, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/spectrocloud/resource_team_import.go b/spectrocloud/resource_team_import.go new file mode 100644 index 000000000..5ce0dc21e --- /dev/null +++ b/spectrocloud/resource_team_import.go @@ -0,0 +1,37 @@ +package spectrocloud + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceTeamImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + c := getV1ClientWithResourceContext(m, "tenant") + + // The import ID should be the team UID + teamUID := d.Id() + + // Validate that the team exists and we can access it + team, err := c.GetTeam(teamUID) + if err != nil { + return nil, fmt.Errorf("could not retrieve team for import: %s", err) + } + if team == nil { + return nil, fmt.Errorf("team with ID %s not found", teamUID) + } + + // Set the team name from the retrieved team + if err := d.Set("name", team.Metadata.Name); err != nil { + return nil, err + } + + // Read all team data to populate the state + diags := resourceTeamRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read team for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} diff --git a/spectrocloud/resource_workspace.go b/spectrocloud/resource_workspace.go index f6115eb58..96923eb43 100644 --- a/spectrocloud/resource_workspace.go +++ b/spectrocloud/resource_workspace.go @@ -96,7 +96,10 @@ func resourceWorkspaceCreate(ctx context.Context, d *schema.ResourceData, m inte var diags diag.Diagnostics - workspace := toWorkspace(d, c) + workspace, err := toWorkspace(d, c) + if err != nil { + return diag.FromErr(err) + } uid, err := c.CreateWorkspace(workspace) if err != nil { @@ -203,7 +206,10 @@ func resourceWorkspaceUpdate(ctx context.Context, d *schema.ResourceData, m inte if d.HasChange("clusters") || d.HasChange("workspace_quota") { // resource allocation should go first because clusters are inside. - namespaces := toUpdateWorkspaceNamespaces(d, c) + namespaces, err := toUpdateWorkspaceNamespaces(d, c) + if err != nil { + return diag.FromErr(err) + } if err := c.UpdateWorkspaceResourceAllocation(d.Id(), namespaces); err != nil { return diag.FromErr(err) } @@ -219,7 +225,11 @@ func resourceWorkspaceUpdate(ctx context.Context, d *schema.ResourceData, m inte } } if d.HasChange("namespaces") { - if err := c.UpdateWorkspaceResourceAllocation(d.Id(), toUpdateWorkspaceNamespaces(d, c)); err != nil { + namespaces, err := toUpdateWorkspaceNamespaces(d, c) + if err != nil { + return diag.FromErr(err) + } + if err := c.UpdateWorkspaceResourceAllocation(d.Id(), namespaces); err != nil { return diag.FromErr(err) } } @@ -269,12 +279,17 @@ func resourceWorkspaceDelete(ctx context.Context, d *schema.ResourceData, m inte return diags } -func toWorkspace(d *schema.ResourceData, c *client.V1Client) *models.V1WorkspaceEntity { +func toWorkspace(d *schema.ResourceData, c *client.V1Client) (*models.V1WorkspaceEntity, error) { annotations := make(map[string]string) if len(d.Get("description").(string)) > 0 { annotations["description"] = d.Get("description").(string) } + quota, err := toQuota(d) + if err != nil { + return nil, err + } + workspace := &models.V1WorkspaceEntity{ Metadata: &models.V1ObjectMeta{ Name: d.Get("name").(string), @@ -287,11 +302,11 @@ func toWorkspace(d *schema.ResourceData, c *client.V1Client) *models.V1Workspace ClusterRbacs: toWorkspaceRBACs(d), ClusterRefs: toClusterRefs(d, c), Policies: toWorkspacePolicies(d), - Quota: toQuota(d), + Quota: quota, }, } - return workspace + return workspace, nil } func resourceWorkspaceImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { diff --git a/spectrocloud/resource_workspace_test.go b/spectrocloud/resource_workspace_test.go index 025522dd7..31f1a8a90 100644 --- a/spectrocloud/resource_workspace_test.go +++ b/spectrocloud/resource_workspace_test.go @@ -91,7 +91,8 @@ func TestToWorkspace(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Initialize resource data with input d := schema.TestResourceDataRaw(t, resourceWorkspace().Schema, tt.input) - result := toWorkspace(d, nil) // nil client for unit test + result, err := toWorkspace(d, nil) // nil client for unit test + assert.NoError(t, err) // Compare the expected and actual result assert.Equal(t, tt.expected.Metadata.Name, result.Metadata.Name) diff --git a/spectrocloud/scripts/spectro-tf-format b/spectrocloud/scripts/spectro-tf-format.py similarity index 95% rename from spectrocloud/scripts/spectro-tf-format rename to spectrocloud/scripts/spectro-tf-format.py index af7e2847c..2c5dc02d4 100755 --- a/spectrocloud/scripts/spectro-tf-format +++ b/spectrocloud/scripts/spectro-tf-format.py @@ -15,8 +15,13 @@ import os import re import shutil +import platform from typing import Dict, List, Any, Tuple, Optional +# Platform detection for cross-platform compatibility +IS_WINDOWS = platform.system() == 'Windows' +IS_POSIX = os.name == 'posix' + # Built-in templating configuration for Spectro Cloud BUILTIN_TEMPLATING_CONFIG = { @@ -664,8 +669,10 @@ def _apply_yaml_extraction(self, tf_content: str, tf_file_path: Path) -> Tuple[s self._write_yaml_file(yaml_file, final_yaml_content) created_files.append(str(yaml_file)) - # Create file reference + # Create file reference (ensure forward slashes for Terraform compatibility) relative_path = os.path.relpath(yaml_file, tf_file_path.parent) + if IS_WINDOWS: + relative_path = relative_path.replace('\\', '/') file_reference = f'file("{relative_path}")' # Replace the values assignment @@ -701,8 +708,10 @@ def _apply_yaml_extraction(self, tf_content: str, tf_file_path: Path) -> Tuple[s self._write_yaml_file(yaml_file, final_yaml_content) created_files.append(str(yaml_file)) - # Create file reference + # Create file reference (ensure forward slashes for Terraform compatibility) relative_path = os.path.relpath(yaml_file, tf_file_path.parent) + if IS_WINDOWS: + relative_path = relative_path.replace('\\', '/') file_reference = f'file("{relative_path}")' # Replace the node_pool_config assignment @@ -1176,38 +1185,85 @@ def _inject_machine_pool_overrides_for_resource(self, tf_content: str, overrides return modified_content + def _get_terraform_commands(self) -> List[str]: + """Get list of terraform commands to try for the current platform""" + if IS_WINDOWS: + return ['terraform.exe', 'terraform'] + else: + return ['terraform'] + def _format_terraform_file(self, tf_path: Path) -> None: - """Format the Terraform file using terraform fmt""" - try: - print(f"📝 Formatting Terraform file...") - result = subprocess.run( - ['terraform', 'fmt', str(tf_path)], - cwd=tf_path.parent, - capture_output=True, - text=True, - timeout=30 - ) - - if result.returncode == 0: - print("✅ Terraform file formatted successfully") - else: - print(f"⚠️ Terraform fmt warning: {result.stderr.strip()}") + """Format the Terraform file using terraform fmt (cross-platform)""" + print(f"📝 Formatting Terraform file...") + + commands_to_try = self._get_terraform_commands() + + for terraform_cmd in commands_to_try: + try: + result = subprocess.run( + [terraform_cmd, 'fmt', str(tf_path)], + cwd=tf_path.parent, + capture_output=True, + text=True, + timeout=30, + shell=IS_WINDOWS # Use shell on Windows for better PATH resolution + ) - except subprocess.TimeoutExpired: - print("⚠️ Terraform fmt timed out") - except FileNotFoundError: + if result.returncode == 0: + print("✅ Terraform file formatted successfully") + return + else: + print(f"⚠️ Terraform fmt warning: {result.stderr.strip()}") + return + + except subprocess.TimeoutExpired: + print(f"⚠️ Terraform fmt timed out with command: {terraform_cmd}") + return + except FileNotFoundError: + # Try next command in the list + continue + except Exception as e: + print(f"⚠️ Error running {terraform_cmd}: {e}") + continue + + # If we get here, none of the commands worked + if IS_WINDOWS: + print("⚠️ terraform command not found - ensure terraform.exe is in your PATH") + print(" Download from: https://www.terraform.io/downloads.html") + else: print("⚠️ terraform command not found - skipping formatting") - except Exception as e: - print(f"⚠️ Could not format Terraform file: {e}") def main(): """Main entry point""" + # Platform-specific examples + if IS_WINDOWS: + platform_examples = """ +Windows Examples: + # Using batch wrapper (recommended) + spectro-tf-format.bat generated.tf + + # Using PowerShell wrapper + .\\spectro-tf-format.ps1 generated.tf + + # Direct Python execution + python spectro-tf-format generated.tf""" + else: + platform_examples = """ +Unix/Linux Examples: + # Make executable first + chmod +x spectro-tf-format + + # Run directly + ./spectro-tf-format generated.tf""" + parser = argparse.ArgumentParser( - description='Spectro Terraform Formatter - Built-in YAML processing for Spectro Cloud', + description='Spectro Terraform Formatter - Built-in YAML processing for Spectro Cloud (Cross-Platform)', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=f""" -Examples: +{platform_examples} + +General Examples: # Process terraform file with defaults (YAML extraction only) spectro-tf-format generated.tf @@ -1231,6 +1287,7 @@ def main(): Output Directory: {BUILTIN_TEMPLATING_CONFIG["output_dir"]} Backup Files: {BUILTIN_TEMPLATING_CONFIG["backup"]} Rules: {', '.join(BUILTIN_TEMPLATING_CONFIG["rules"])} + Platform: {platform.system()} ({platform.machine()}) """ ) diff --git a/spectrocloud/utils.go b/spectrocloud/utils.go index 840893057..c33225368 100644 --- a/spectrocloud/utils.go +++ b/spectrocloud/utils.go @@ -1,6 +1,37 @@ package spectrocloud -import "time" +import ( + "math" + "time" +) + +// SafeInt32 converts int to int32 with bounds checking to prevent overflow +func SafeInt32(value int) int32 { + if value > math.MaxInt32 { + return math.MaxInt32 + } + if value < math.MinInt32 { + return math.MinInt32 + } + return int32(value) +} + +// SafeInt64 converts int to int64 with bounds checking to prevent overflow +func SafeInt64(value int) int64 { + + return int64(value) +} + +// SafeUint32 converts int to uint32 with bounds checking to prevent overflow +func SafeUint32(value int) uint32 { + if value < 0 { + return 0 + } + if value > math.MaxUint32 { + return math.MaxUint32 + } + return uint32(value) +} func expandStringList(configured []interface{}) []string { vs := make([]string, 0) diff --git a/spectrocloud/workspace_namespace.go b/spectrocloud/workspace_namespace.go index b1db7ad54..74173a0a3 100644 --- a/spectrocloud/workspace_namespace.go +++ b/spectrocloud/workspace_namespace.go @@ -1,6 +1,7 @@ package spectrocloud import ( + "fmt" "math" "regexp" "strconv" @@ -9,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spectrocloud/palette-sdk-go/api/models" "github.com/spectrocloud/palette-sdk-go/client" + "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/constants" ) // Helper function to create V1WorkspaceResourceAllocation from resource allocation map @@ -29,18 +31,19 @@ func toWorkspaceResourceAllocation(resourceAllocation map[string]interface{}) (* } // Handle GPU configuration if specified - if gpuLimit, exists := resourceAllocation["gpu_limit"]; exists && gpuLimit.(string) != "" { - gpuVal, err := strconv.Atoi(gpuLimit.(string)) - if err == nil && gpuVal > 0 { - provider := "nvidia" // Default provider for cluster allocations - // gpu_provider is optional - mainly used for default resource allocations - if gpuProvider, providerExists := resourceAllocation["gpu_provider"]; providerExists && gpuProvider.(string) != "" { - provider = gpuProvider.(string) - } - resource_alloc.GpuConfig = &models.V1GpuConfig{ - Limit: int32(gpuVal), - Provider: &provider, - } + if gpuVal, exists := resourceAllocation["gpu"]; exists && gpuVal.(int) > 0 { + gpuInt := gpuVal.(int) + if gpuInt > constants.Int32MaxValue { + return nil, fmt.Errorf("gpu value %d is out of range for int32", gpuInt) + } + provider := "nvidia" // Default provider for cluster allocations + // gpu_provider is optional - mainly used for default resource allocations + if gpuProvider, providerExists := resourceAllocation["gpu_provider"]; providerExists && gpuProvider.(string) != "" { + provider = gpuProvider.(string) + } + resource_alloc.GpuConfig = &models.V1GpuConfig{ + Limit: SafeInt32(gpuInt), + Provider: &provider, } } @@ -140,12 +143,17 @@ func IsRegex(name string) bool { } -func toUpdateWorkspaceNamespaces(d *schema.ResourceData, c *client.V1Client) *models.V1WorkspaceClusterNamespacesEntity { +func toUpdateWorkspaceNamespaces(d *schema.ResourceData, c *client.V1Client) (*models.V1WorkspaceClusterNamespacesEntity, error) { + quota, err := toQuota(d) + if err != nil { + return nil, err + } + return &models.V1WorkspaceClusterNamespacesEntity{ ClusterNamespaces: toWorkspaceNamespaces(d), ClusterRefs: toClusterRefs(d, c), - Quota: toQuota(d), - } + Quota: quota, + }, nil } // Helper function to flatten V1WorkspaceResourceAllocation to resource allocation map @@ -153,12 +161,34 @@ func toUpdateWorkspaceNamespaces(d *schema.ResourceData, c *client.V1Client) *mo func flattenWorkspaceResourceAllocation(resourceAlloc *models.V1WorkspaceResourceAllocation, includeProvider bool) map[string]interface{} { result := make(map[string]interface{}) - result["cpu_cores"] = strconv.Itoa(int(math.Round(resourceAlloc.CPUCores))) - result["memory_MiB"] = strconv.Itoa(int(math.Round(resourceAlloc.MemoryMiB))) + // Convert CPU cores with bounds checking to prevent integer overflow + cpuCoresRounded := math.Round(resourceAlloc.CPUCores) + if cpuCoresRounded > math.MaxInt || cpuCoresRounded < math.MinInt { + // Fallback to string representation if out of int range + result["cpu_cores"] = fmt.Sprintf("%.0f", cpuCoresRounded) + } else { + result["cpu_cores"] = strconv.Itoa(int(cpuCoresRounded)) + } + + // Convert memory with bounds checking to prevent integer overflow + memoryMiBRounded := math.Round(resourceAlloc.MemoryMiB) + if memoryMiBRounded > math.MaxInt || memoryMiBRounded < math.MinInt { + // Fallback to string representation if out of int range + result["memory_MiB"] = fmt.Sprintf("%.0f", memoryMiBRounded) + } else { + result["memory_MiB"] = strconv.Itoa(int(memoryMiBRounded)) + } // Handle GPU configuration if present if resourceAlloc.GpuConfig != nil { - result["gpu_limit"] = strconv.Itoa(int(resourceAlloc.GpuConfig.Limit)) + // Convert GPU limit with bounds checking to prevent integer overflow + gpuLimit := int64(resourceAlloc.GpuConfig.Limit) + if gpuLimit > math.MaxInt || gpuLimit < math.MinInt { + // Fallback to string representation if out of int range + result["gpu_limit"] = fmt.Sprintf("%d", gpuLimit) + } else { + result["gpu_limit"] = strconv.Itoa(int(gpuLimit)) + } // Only include gpu_provider for default resource allocations, not cluster-specific ones if includeProvider { if resourceAlloc.GpuConfig.Provider != nil { diff --git a/spectrocloud/workspace_rbac.go b/spectrocloud/workspace_rbac.go index 1df443021..a7952d60a 100644 --- a/spectrocloud/workspace_rbac.go +++ b/spectrocloud/workspace_rbac.go @@ -1,8 +1,11 @@ package spectrocloud import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spectrocloud/palette-sdk-go/api/models" + "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/constants" ) func toWorkspaceRBACs(d *schema.ResourceData) []*models.V1ClusterRbac { @@ -18,7 +21,7 @@ func toWorkspaceRBACs(d *schema.ResourceData) []*models.V1ClusterRbac { return workspace_rbacs } -func toQuota(d *schema.ResourceData) *models.V1WorkspaceQuota { +func toQuota(d *schema.ResourceData) (*models.V1WorkspaceQuota, error) { wsQuota, ok := d.GetOk("workspace_quota") if !ok || len(wsQuota.([]interface{})) == 0 { return &models.V1WorkspaceQuota{ @@ -26,7 +29,7 @@ func toQuota(d *schema.ResourceData) *models.V1WorkspaceQuota { CPUCores: 0, MemoryMiB: 0, }, - } + }, nil } q := wsQuota.([]interface{})[0].(map[string]interface{}) @@ -37,14 +40,18 @@ func toQuota(d *schema.ResourceData) *models.V1WorkspaceQuota { // Handle GPU configuration if specified if gpuVal, exists := q["gpu"]; exists && gpuVal.(int) > 0 { + gpuInt := gpuVal.(int) + if gpuInt > constants.Int32MaxValue { + return nil, fmt.Errorf("gpu value %d is out of range for int32", gpuInt) + } provider := "nvidia" // Default to nvidia as it's the only supported provider resourceAllocation.GpuConfig = &models.V1GpuConfig{ - Limit: int32(gpuVal.(int)), + Limit: SafeInt32(gpuInt), Provider: &provider, } } return &models.V1WorkspaceQuota{ ResourceAllocation: resourceAllocation, - } + }, nil } diff --git a/templates/resources/appliance.md.tmpl b/templates/resources/appliance.md.tmpl index 849fd18ba..ee831975f 100644 --- a/templates/resources/appliance.md.tmpl +++ b/templates/resources/appliance.md.tmpl @@ -21,4 +21,19 @@ resource "spectrocloud_appliance" "appliance" { } ``` +## Import + +Appliances can be imported using the appliance UID. This is a project-level resource. + +```bash +terraform import spectrocloud_appliance.example +``` + +Where `` is the appliance UID. + +The import will automatically populate all configuration fields from the Spectro Cloud API, including the UID, tags, and tunnel configuration settings. The `wait` field will be set to `false` by default for imported appliances. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a project-level resource, ensure your provider is configured with appropriate project-level credentials and access. + + {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/application.md.tmpl b/templates/resources/application.md.tmpl index 01b379bef..48b928cac 100644 --- a/templates/resources/application.md.tmpl +++ b/templates/resources/application.md.tmpl @@ -32,5 +32,15 @@ resource "spectrocloud_application" "application" { ``` +## Import + +Applications can be imported using their UID. The import will automatically detect the cluster context. + +```bash +terraform import spectrocloud_application.example 63444eb70807dc2c14a8ad59 +``` + +Note: During import, the application's configuration will be automatically populated from the Spectro Cloud API, including the correct cluster context. + {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/application_profile.md.tmpl b/templates/resources/application_profile.md.tmpl index d89b9e938..aaf171d5f 100644 --- a/templates/resources/application_profile.md.tmpl +++ b/templates/resources/application_profile.md.tmpl @@ -349,4 +349,19 @@ resource "spectrocloud_application_profile" "app_profile_all_tiers" { ``` +``` +## Import + +# terraform import spectrocloud_application_profile.app_profile_all_tiers "profile_uid_here" +# +# Where: +# - profile_uid_here is the unique identifier of the application profile +# +# To import using import block: +# import { +# to = spectrocloud_application_profile.app_profile_all_tiers +# id = "profile_uid_here" +# } +``` + {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/backup_storage_location.md.tmpl b/templates/resources/backup_storage_location.md.tmpl index 002141ac8..de52d7f2d 100644 --- a/templates/resources/backup_storage_location.md.tmpl +++ b/templates/resources/backup_storage_location.md.tmpl @@ -44,6 +44,33 @@ resource "spectrocloud_backup_storage_location" "bsl2" { } ``` +## Import + +Backup Storage Locations can be imported using either a simple ID format or with explicit context specification. This resource supports both project and tenant contexts. + +### Simple Import (defaults to project context) + +```bash +terraform import spectrocloud_backup_storage_location.example :project +``` + +### Context-specific Import + +```bash +terraform import spectrocloud_backup_storage_location.example :project +terraform import spectrocloud_backup_storage_location.example :tenant +``` + +Where: +- `` is the Backup Storage Location ID +- `project` or `tenant` specifies the context where the backup storage location exists + +**Import behavior:** +- If no context is specified, it defaults to `project` context +- If the resource is not found in the specified context, the import will automatically try the other context +- The import will automatically populate all configuration fields from the Spectro Cloud API, including the correct context, storage provider, and all provider-specific settings + +After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/cluster_group.md.tmpl b/templates/resources/cluster_group.md.tmpl index ddd6d14bc..ff2b84dd1 100644 --- a/templates/resources/cluster_group.md.tmpl +++ b/templates/resources/cluster_group.md.tmpl @@ -40,5 +40,24 @@ resource "spectrocloud_cluster_group" "cg" { ``` +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) +to import the resource {{ .Name }} by using its `id` with the Palette `context` separated by a colon. For example: + +```terraform +import { + to = {{ .Name }}.example + id = "example_id:context" +} +``` + +Using `terraform import`, import the cluster group using the `id` colon separated with `context`. For example: + +```console +terraform import {{ .Name }}.example example_id:project +``` + +Refer to the [Import section](/docs#import) to learn more. {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/macro.md.tmpl b/templates/resources/macro.md.tmpl deleted file mode 100644 index c6fb25828..000000000 --- a/templates/resources/macro.md.tmpl +++ /dev/null @@ -1,28 +0,0 @@ ---- -page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" -subcategory: "" -description: |- -{{ .Description | plainmarkdown | trimspace | prefixlines " " }} ---- - -# {{.Name}} ({{.Type}}, Deprecated) - -{{ .Description | plainmarkdown | trimspace | prefixlines " " }} - -## Example Usage - -```terraform -resource "spectrocloud_macro" "project_macro" { - name = "project1" - value = "project_val2" - project = "Default" -} - -resource "spectrocloud_macro" "tenant_macro" { - name = "tenant1" - value = "tenant_val1" -} -``` - - -{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/privatecloudgateway_dns_map.md.tmpl b/templates/resources/privatecloudgateway_dns_map.md.tmpl index 6402f1c2c..ecabb2ff8 100644 --- a/templates/resources/privatecloudgateway_dns_map.md.tmpl +++ b/templates/resources/privatecloudgateway_dns_map.md.tmpl @@ -27,4 +27,21 @@ An example of creating an DNS Map for a Private Cloud Gateway using a search dom } ``` +## Import + +Private Cloud Gateway DNS maps can be imported using the composite ID format: `pcg_id:dns_map_id`. This is a tenant-level resource. + +```bash +terraform import spectrocloud_privatecloudgateway_dns_map.example : +``` + +Where: +- `` is the Private Cloud Gateway ID +- `` is the DNS map ID + +The import will automatically populate all configuration fields from the Spectro Cloud API, including the associated Private Cloud Gateway ID, search domain name, data center, and network. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a tenant-level resource, ensure your provider is configured with appropriate tenant-level credentials and access. + + {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/privatecloudgateway_ippool.md.tmpl b/templates/resources/privatecloudgateway_ippool.md.tmpl index 236f54cac..9ea1baf70 100644 --- a/templates/resources/privatecloudgateway_ippool.md.tmpl +++ b/templates/resources/privatecloudgateway_ippool.md.tmpl @@ -52,5 +52,21 @@ An example of creating an IP Pool for a Private Cloud Gateway using a subnet of } ``` +## Import + +Private Cloud Gateway IP pools can be imported using the composite ID format: `pcg_id:ippool_id`. This is a tenant-level resource. + +```bash +terraform import spectrocloud_privatecloudgateway_ippool.example : +``` + +Where: +- `` is the Private Cloud Gateway ID +- `` is the IP Pool ID + +The import will automatically populate all configuration fields from the Spectro Cloud API. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a tenant-level resource, ensure your provider is configured with appropriate tenant-level credentials and access. + {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/registry_helm.md.tmpl b/templates/resources/registry_helm.md.tmpl index 547ca52a7..0c86a29e7 100644 --- a/templates/resources/registry_helm.md.tmpl +++ b/templates/resources/registry_helm.md.tmpl @@ -24,5 +24,19 @@ resource "spectrocloud_registry_helm" "r1" { } ``` +## Import + +Helm registries can be imported using the registry ID. This is a tenant-level resource. + +```bash +terraform import spectrocloud_registry_helm.example +``` + +Where `` is the Helm registry ID. + +The import will automatically populate all configuration fields from the Spectro Cloud API, including the name, endpoint, privacy setting, and authentication credentials. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a tenant-level resource, ensure your provider is configured with appropriate tenant-level credentials and access. + {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/registry_oci.md.tmpl b/templates/resources/registry_oci.md.tmpl index d86b92105..515209920 100644 --- a/templates/resources/registry_oci.md.tmpl +++ b/templates/resources/registry_oci.md.tmpl @@ -25,5 +25,19 @@ resource "spectrocloud_registry_oci" "r1" { } ``` +## Import + +OCI registries can be imported using the registry ID. This is a tenant-level resource. + +```bash +terraform import spectrocloud_registry_oci.example +``` + +Where `` is the OCI registry ID. + +The import will automatically detect whether the registry is an ECR or basic type and populate all configuration fields from the Spectro Cloud API. After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + +**Note**: Since this is a tenant-level resource, ensure your provider is configured with appropriate tenant-level credentials and access. + {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/resources/ssh_key.md.tmpl b/templates/resources/ssh_key.md.tmpl index da2e21390..d2145be4b 100644 --- a/templates/resources/ssh_key.md.tmpl +++ b/templates/resources/ssh_key.md.tmpl @@ -58,5 +58,33 @@ resource "spectrocloud_ssh_key" "primary_key_1" { } ``` +## Import + +SSH keys can be imported using either a simple ID format or with explicit context specification. This resource supports both project and tenant contexts. + +### Simple Import (defaults to project context) + +```bash +terraform import spectrocloud_ssh_key.example +``` + +### Context-specific Import + +```bash +terraform import spectrocloud_ssh_key.example :project +terraform import spectrocloud_ssh_key.example :tenant +``` + +Where: +- `` is the SSH key ID +- `project` or `tenant` specifies the context where the SSH key exists + +**Import behavior:** +- If no context is specified, it defaults to `project` context +- If the resource is not found in the specified context, the import will automatically try the other context +- The import will automatically populate all configuration fields from the Spectro Cloud API, including the correct context, name, and SSH key content + +After import, you can run `terraform plan` to see the current configuration and make any necessary adjustments. + {{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/tests/mockApiServer/apiServerMock.go b/tests/mockApiServer/apiServerMock.go index b62738676..c7f2f54ce 100644 --- a/tests/mockApiServer/apiServerMock.go +++ b/tests/mockApiServer/apiServerMock.go @@ -4,6 +4,7 @@ import ( "encoding/json" "log" "net/http" + "time" "github.com/gorilla/mux" "github.com/spectrocloud/terraform-provider-spectrocloud/tests/mockApiServer/routes" @@ -45,15 +46,29 @@ func main() { // Start servers on different ports go func() { log.Println("Starting server on :8088...") - if err := http.ListenAndServeTLS(":8088", "mock_server.crt", "mock_server.key", router8088); err != nil { + server := &http.Server{ + Addr: ":8088", + Handler: router8088, + ReadTimeout: 15 * time.Second, + WriteTimeout: 15 * time.Second, + IdleTimeout: 60 * time.Second, + } + if err := server.ListenAndServeTLS("mock_server.crt", "mock_server.key"); err != nil { log.Fatalf("Server failed to start on port 8088: %v", err) } }() log.Println("Starting server on :8888...") - if err := http.ListenAndServeTLS(":8888", "mock_server.crt", "mock_server.key", router8888); err != nil { - log.Fatalf("Server failed to start on port 8088: %v", err) + server := &http.Server{ + Addr: ":8888", + Handler: router8888, + ReadTimeout: 15 * time.Second, + WriteTimeout: 15 * time.Second, + IdleTimeout: 60 * time.Second, + } + if err := server.ListenAndServeTLS("mock_server.crt", "mock_server.key"); err != nil { + log.Fatalf("Server failed to start on port 8888: %v", err) } } diff --git a/tests/mockApiServer/routes/mockCloudAccounts.go b/tests/mockApiServer/routes/mockCloudAccounts.go index 778e9fd0c..9f049203e 100644 --- a/tests/mockApiServer/routes/mockCloudAccounts.go +++ b/tests/mockApiServer/routes/mockCloudAccounts.go @@ -376,8 +376,7 @@ func CloudAccountsRoutes() []Route { UID: "test-gcp-account-id-1", }, Spec: &models.V1GcpAccountSpec{ - JSONCredentials: "test-json-cred", - JSONCredentialsFileName: "test-json", + JSONCredentials: "test-json-cred", }, Status: &models.V1CloudAccountStatus{ State: "Running",