From d6749b6ce329abe89ad2a4f128fca15140d34e6a Mon Sep 17 00:00:00 2001
From: Marcel Jacek <72880145+marceljk@users.noreply.github.com>
Date: Fri, 4 Apr 2025 14:18:16 +0200
Subject: [PATCH] fix(STACKITTPR-168): improve error messages (#762)
* remove deprecated argus resources
* improve error messages
---
docs/data-sources/argus_instance.md | 154 --
docs/data-sources/argus_scrapeconfig.md | 70 -
docs/resources/argus_credential.md | 77 -
docs/resources/argus_instance.md | 235 --
docs/resources/argus_scrapeconfig.md | 180 --
.../stackit_argus_instance/data-source.tf | 4 -
.../stackit_argus_scrapeconfig/data-source.tf | 5 -
.../data-source.tf | 2 +-
.../stackit_argus_credential/resource.tf | 4 -
.../stackit_argus_instance/resource.tf | 9 -
.../stackit_argus_scrapeconfig/resource.tf | 17 -
.../internal/services/argus/argus_acc_test.go | 896 -------
.../services/argus/credential/const.go | 24 -
.../services/argus/credential/resource.go | 264 --
.../argus/credential/resource_test.go | 77 -
.../internal/services/argus/instance/const.go | 34 -
.../services/argus/instance/datasource.go | 442 ----
.../services/argus/instance/resource.go | 2221 -----------------
.../services/argus/instance/resource_test.go | 1562 ------------
.../services/argus/scrapeconfig/const.go | 50 -
.../services/argus/scrapeconfig/datasource.go | 243 --
.../services/argus/scrapeconfig/resource.go | 880 -------
.../argus/scrapeconfig/resource_test.go | 504 ----
.../services/dns/recordset/datasource.go | 14 +-
.../internal/services/dns/zone/datasource.go | 14 +-
.../services/iaas/affinitygroup/datasource.go | 19 +-
.../services/iaas/image/datasource.go | 19 +-
.../services/iaas/keypair/datasource.go | 25 +-
.../services/iaas/network/datasource.go | 18 +-
.../services/iaas/networkarea/datasource.go | 19 +-
.../iaas/networkarearoute/datasource.go | 19 +-
.../iaas/networkinterface/datasource.go | 18 +-
.../services/iaas/publicip/datasource.go | 19 +-
.../iaas/publicipranges/datasource.go | 19 +-
.../services/iaas/securitygroup/datasource.go | 19 +-
.../iaas/securitygrouprule/datasource.go | 18 +-
.../services/iaas/server/datasource.go | 19 +-
.../services/iaas/volume/datasource.go | 18 +-
.../loadbalancer/loadbalancer/datasource.go | 17 +-
.../services/logme/credential/datasource.go | 18 +-
.../services/logme/instance/datasource.go | 19 +-
.../services/mariadb/credential/datasource.go | 18 +-
.../services/mariadb/instance/datasource.go | 19 +-
.../mongodbflex/instance/datasource.go | 18 +-
.../services/mongodbflex/user/datasource.go | 18 +-
.../objectstorage/bucket/datasource.go | 17 +-
.../objectstorage/credential/datasource.go | 22 +-
.../credentialsgroup/datasource.go | 2 +-
.../observability/credential/resource.go | 55 +-
.../observability/instance/datasource.go | 18 +-
.../observability/instance/resource.go | 60 -
.../observability/scrapeconfig/datasource.go | 18 +-
.../observability/scrapeconfig/resource.go | 43 -
.../opensearch/credential/datasource.go | 18 +-
.../opensearch/instance/datasource.go | 19 +-
.../postgresflex/database/datasource.go | 17 +-
.../postgresflex/instance/datasource.go | 17 +-
.../services/postgresflex/user/datasource.go | 17 +-
.../rabbitmq/credential/datasource.go | 18 +-
.../services/rabbitmq/instance/datasource.go | 18 +-
.../services/redis/credential/datasource.go | 18 +-
.../services/redis/instance/datasource.go | 19 +-
.../resourcemanager/project/datasource.go | 20 +-
.../secretsmanager/instance/datasource.go | 18 +-
.../secretsmanager/user/datasource.go | 18 +-
.../schedule/schedule_datasource.go | 17 +-
.../schedule/schedules_datasource.go | 17 +-
.../schedule/schedule_datasource.go | 17 +-
.../schedule/schedules_datasource.go | 17 +-
.../serviceaccount/account/datasource.go | 13 +-
.../services/ske/cluster/datasource.go | 17 +-
.../services/ske/kubeconfig/resource.go | 23 +-
.../sqlserverflex/instance/datasource.go | 17 +-
.../services/sqlserverflex/user/datasource.go | 17 +-
stackit/internal/utils/utils.go | 30 +
stackit/provider.go | 8 -
76 files changed, 600 insertions(+), 8403 deletions(-)
delete mode 100644 docs/data-sources/argus_instance.md
delete mode 100644 docs/data-sources/argus_scrapeconfig.md
delete mode 100644 docs/resources/argus_credential.md
delete mode 100644 docs/resources/argus_instance.md
delete mode 100644 docs/resources/argus_scrapeconfig.md
delete mode 100644 examples/data-sources/stackit_argus_instance/data-source.tf
delete mode 100644 examples/data-sources/stackit_argus_scrapeconfig/data-source.tf
delete mode 100644 examples/resources/stackit_argus_credential/resource.tf
delete mode 100644 examples/resources/stackit_argus_instance/resource.tf
delete mode 100644 examples/resources/stackit_argus_scrapeconfig/resource.tf
delete mode 100644 stackit/internal/services/argus/argus_acc_test.go
delete mode 100644 stackit/internal/services/argus/credential/const.go
delete mode 100644 stackit/internal/services/argus/credential/resource.go
delete mode 100644 stackit/internal/services/argus/credential/resource_test.go
delete mode 100644 stackit/internal/services/argus/instance/const.go
delete mode 100644 stackit/internal/services/argus/instance/datasource.go
delete mode 100644 stackit/internal/services/argus/instance/resource.go
delete mode 100644 stackit/internal/services/argus/instance/resource_test.go
delete mode 100644 stackit/internal/services/argus/scrapeconfig/const.go
delete mode 100644 stackit/internal/services/argus/scrapeconfig/datasource.go
delete mode 100644 stackit/internal/services/argus/scrapeconfig/resource.go
delete mode 100644 stackit/internal/services/argus/scrapeconfig/resource_test.go
diff --git a/docs/data-sources/argus_instance.md b/docs/data-sources/argus_instance.md
deleted file mode 100644
index 450f481d..00000000
--- a/docs/data-sources/argus_instance.md
+++ /dev/null
@@ -1,154 +0,0 @@
----
-# generated by https://github.com/hashicorp/terraform-plugin-docs
-page_title: "stackit_argus_instance Data Source - stackit"
-subcategory: ""
-description: |-
- Argus instance data source schema. Must have a region specified in the provider configuration.
- !> The stackit_argus_instance data source has been deprecated and will be removed after February 26th 2025. Please use stackit_observability_instance instead, which offers the exact same functionality.
----
-
-# stackit_argus_instance (Data Source)
-
-Argus instance data source schema. Must have a `region` specified in the provider configuration.
-
-!> The `stackit_argus_instance` data source has been deprecated and will be removed after February 26th 2025. Please use `stackit_observability_instance` instead, which offers the exact same functionality.
-
-## Example Usage
-
-```terraform
-data "stackit_argus_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
-```
-
-
-## Schema
-
-### Required
-
-- `instance_id` (String) The Argus instance ID.
-- `project_id` (String) STACKIT project ID to which the instance is associated.
-
-### Read-Only
-
-- `acl` (Set of String) The access control list for this instance. Each entry is an IP address range that is permitted to access, in CIDR notation.
-- `alert_config` (Attributes) Alert configuration for the instance. (see [below for nested schema](#nestedatt--alert_config))
-- `alerting_url` (String) Specifies Alerting URL.
-- `dashboard_url` (String) Specifies Argus instance dashboard URL.
-- `grafana_initial_admin_password` (String, Sensitive) Specifies an initial Grafana admin password.
-- `grafana_initial_admin_user` (String) Specifies an initial Grafana admin username.
-- `grafana_public_read_access` (Boolean) If true, anyone can access Grafana dashboards without logging in.
-- `grafana_url` (String) Specifies Grafana URL.
-- `id` (String) Terraform's internal data source ID. It is structured as "`project_id`,`instance_id`".
-- `is_updatable` (Boolean) Specifies if the instance can be updated.
-- `jaeger_traces_url` (String)
-- `jaeger_ui_url` (String)
-- `logs_push_url` (String) Specifies URL for pushing logs.
-- `logs_url` (String) Specifies Logs URL.
-- `metrics_push_url` (String) Specifies URL for pushing metrics.
-- `metrics_retention_days` (Number) Specifies for how many days the raw metrics are kept.
-- `metrics_retention_days_1h_downsampling` (Number) Specifies for how many days the 1h downsampled metrics are kept. must be less than the value of the 5m downsampling retention. Default is set to `0` (disabled).
-- `metrics_retention_days_5m_downsampling` (Number) Specifies for how many days the 5m downsampled metrics are kept. must be less than the value of the general retention. Default is set to `0` (disabled).
-- `metrics_url` (String) Specifies metrics URL.
-- `name` (String) The name of the Argus instance.
-- `otlp_traces_url` (String)
-- `parameters` (Map of String) Additional parameters.
-- `plan_id` (String) The Argus plan ID.
-- `plan_name` (String) Specifies the Argus plan. E.g. `Monitoring-Medium-EU01`.
-- `targets_url` (String) Specifies Targets URL.
-- `zipkin_spans_url` (String)
-
-
-### Nested Schema for `alert_config`
-
-Read-Only:
-
-- `global` (Attributes) Global configuration for the alerts. (see [below for nested schema](#nestedatt--alert_config--global))
-- `receivers` (Attributes List) List of alert receivers. (see [below for nested schema](#nestedatt--alert_config--receivers))
-- `route` (Attributes) The route for the alert. (see [below for nested schema](#nestedatt--alert_config--route))
-
-
-### Nested Schema for `alert_config.global`
-
-Read-Only:
-
-- `opsgenie_api_key` (String, Sensitive) The API key for OpsGenie.
-- `opsgenie_api_url` (String) The host to send OpsGenie API requests to. Must be a valid URL
-- `resolve_timeout` (String) The default value used by alertmanager if the alert does not include EndsAt. After this time passes, it can declare the alert as resolved if it has not been updated. This has no impact on alerts from Prometheus, as they always include EndsAt.
-- `smtp_auth_identity` (String) SMTP authentication information. Must be a valid email address
-- `smtp_auth_password` (String, Sensitive) SMTP Auth using LOGIN and PLAIN.
-- `smtp_auth_username` (String) SMTP Auth using CRAM-MD5, LOGIN and PLAIN. If empty, Alertmanager doesn't authenticate to the SMTP server.
-- `smtp_from` (String) The default SMTP From header field. Must be a valid email address
-- `smtp_smart_host` (String) The default SMTP smarthost used for sending emails, including port number. Port number usually is 25, or 587 for SMTP over TLS (sometimes referred to as STARTTLS).
-
-
-
-### Nested Schema for `alert_config.receivers`
-
-Read-Only:
-
-- `email_configs` (Attributes List) List of email configurations. (see [below for nested schema](#nestedatt--alert_config--receivers--email_configs))
-- `name` (String) Name of the receiver.
-- `opsgenie_configs` (Attributes List) List of OpsGenie configurations. (see [below for nested schema](#nestedatt--alert_config--receivers--opsgenie_configs))
-- `webhooks_configs` (Attributes List) List of Webhooks configurations. (see [below for nested schema](#nestedatt--alert_config--receivers--webhooks_configs))
-
-
-### Nested Schema for `alert_config.receivers.email_configs`
-
-Read-Only:
-
-- `auth_identity` (String) SMTP authentication information. Must be a valid email address
-- `auth_password` (String) SMTP authentication password.
-- `auth_username` (String) SMTP authentication username.
-- `from` (String) The sender email address. Must be a valid email address
-- `smart_host` (String) The SMTP host through which emails are sent.
-- `to` (String) The email address to send notifications to. Must be a valid email address
-
-
-
-### Nested Schema for `alert_config.receivers.opsgenie_configs`
-
-Read-Only:
-
-- `api_key` (String) The API key for OpsGenie.
-- `api_url` (String) The host to send OpsGenie API requests to. Must be a valid URL
-- `tags` (String) Comma separated list of tags attached to the notifications.
-
-
-
-### Nested Schema for `alert_config.receivers.webhooks_configs`
-
-Read-Only:
-
-- `ms_teams` (Boolean) Microsoft Teams webhooks require special handling, set this to true if the webhook is for Microsoft Teams.
-- `url` (String) The endpoint to send HTTP POST requests to. Must be a valid URL
-
-
-
-
-### Nested Schema for `alert_config.route`
-
-Read-Only:
-
-- `group_by` (List of String) The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.
-- `group_interval` (String) How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)
-- `group_wait` (String) How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.) .
-- `match` (Map of String) A set of equality matchers an alert has to fulfill to match the node.
-- `match_regex` (Map of String) A set of regex-matchers an alert has to fulfill to match the node.
-- `receiver` (String) The name of the receiver to route the alerts to.
-- `repeat_interval` (String) How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more).
-- `routes` (Attributes List) List of child routes. (see [below for nested schema](#nestedatt--alert_config--route--routes))
-
-
-### Nested Schema for `alert_config.route.routes`
-
-Read-Only:
-
-- `group_by` (List of String) The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.
-- `group_interval` (String) How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)
-- `group_wait` (String) How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.)
-- `match` (Map of String) A set of equality matchers an alert has to fulfill to match the node.
-- `match_regex` (Map of String) A set of regex-matchers an alert has to fulfill to match the node.
-- `receiver` (String) The name of the receiver to route the alerts to.
-- `repeat_interval` (String) How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more).
diff --git a/docs/data-sources/argus_scrapeconfig.md b/docs/data-sources/argus_scrapeconfig.md
deleted file mode 100644
index f7def53d..00000000
--- a/docs/data-sources/argus_scrapeconfig.md
+++ /dev/null
@@ -1,70 +0,0 @@
----
-# generated by https://github.com/hashicorp/terraform-plugin-docs
-page_title: "stackit_argus_scrapeconfig Data Source - stackit"
-subcategory: ""
-description: |-
- Argus scrape config data source schema. Must have a region specified in the provider configuration.
- !> The stackit_argus_scrapeconfig data source has been deprecated and will be removed after February 26th 2025. Please use stackit_observability_scrapeconfig instead, which offers the exact same functionality.
----
-
-# stackit_argus_scrapeconfig (Data Source)
-
-Argus scrape config data source schema. Must have a `region` specified in the provider configuration.
-
-!> The `stackit_argus_scrapeconfig` data source has been deprecated and will be removed after February 26th 2025. Please use `stackit_observability_scrapeconfig` instead, which offers the exact same functionality.
-
-## Example Usage
-
-```terraform
-data "stackit_argus_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- job_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
-```
-
-
-## Schema
-
-### Required
-
-- `instance_id` (String) Argus instance ID to which the scraping job is associated.
-- `name` (String) Specifies the name of the scraping job
-- `project_id` (String) STACKIT project ID to which the scraping job is associated.
-
-### Read-Only
-
-- `basic_auth` (Attributes) A basic authentication block. (see [below for nested schema](#nestedatt--basic_auth))
-- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`instance_id`,`name`".
-- `metrics_path` (String) Specifies the job scraping url path.
-- `saml2` (Attributes) A SAML2 configuration block. (see [below for nested schema](#nestedatt--saml2))
-- `sample_limit` (Number) Specifies the scrape sample limit.
-- `scheme` (String) Specifies the http scheme.
-- `scrape_interval` (String) Specifies the scrape interval as duration string.
-- `scrape_timeout` (String) Specifies the scrape timeout as duration string.
-- `targets` (Attributes List) The targets list (specified by the static config). (see [below for nested schema](#nestedatt--targets))
-
-
-### Nested Schema for `basic_auth`
-
-Read-Only:
-
-- `password` (String, Sensitive) Specifies basic auth password.
-- `username` (String) Specifies basic auth username.
-
-
-
-### Nested Schema for `saml2`
-
-Read-Only:
-
-- `enable_url_parameters` (Boolean) Specifies if URL parameters are enabled
-
-
-
-### Nested Schema for `targets`
-
-Read-Only:
-
-- `labels` (Map of String) Specifies labels.
-- `urls` (List of String) Specifies target URLs.
diff --git a/docs/resources/argus_credential.md b/docs/resources/argus_credential.md
deleted file mode 100644
index 2bd96bc9..00000000
--- a/docs/resources/argus_credential.md
+++ /dev/null
@@ -1,77 +0,0 @@
----
-# generated by https://github.com/hashicorp/terraform-plugin-docs
-page_title: "stackit_argus_credential Resource - stackit"
-subcategory: ""
-description: |-
- Argus credential resource schema. Must have a region specified in the provider configuration.
- !> The stackit_argus_credential resource has been deprecated and will be removed after February 26th 2025. Please use stackit_observability_credential instead, which offers the exact same functionality.
- Example move
- Example to move the deprecated stackit_argus_credential resource to the new stackit_observability_credential resource:
- Add a new stackit_observability_credential resource with the same values like your previous stackit_argus_credential resource.Add a moved block which reference the stackit_argus_credential and stackit_observability_credential resource.Remove your old stackit_argus_credential resource and run $ terraform apply.
-
- resource "stackit_argus_credential" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- }
-
- moved {
- from = stackit_argus_credential.example
- to = stackit_observability_credential.example
- }
-
- resource "stackit_observability_credential" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- }
----
-
-# stackit_argus_credential (Resource)
-
-Argus credential resource schema. Must have a `region` specified in the provider configuration.
-
-!> The `stackit_argus_credential` resource has been deprecated and will be removed after February 26th 2025. Please use `stackit_observability_credential` instead, which offers the exact same functionality.
-
-## Example move
-Example to move the deprecated `stackit_argus_credential` resource to the new `stackit_observability_credential` resource:
-1. Add a new `stackit_observability_credential` resource with the same values like your previous `stackit_argus_credential` resource.
-1. Add a moved block which reference the `stackit_argus_credential` and `stackit_observability_credential` resource.
-1. Remove your old `stackit_argus_credential` resource and run `$ terraform apply`.
-```terraform
-resource "stackit_argus_credential" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
-
-moved {
- from = stackit_argus_credential.example
- to = stackit_observability_credential.example
-}
-
-resource "stackit_observability_credential" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
-```
-
-## Example Usage
-
-```terraform
-resource "stackit_argus_credential" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
-```
-
-
-## Schema
-
-### Required
-
-- `instance_id` (String) The Argus Instance ID the credential belongs to.
-- `project_id` (String) STACKIT project ID to which the credential is associated.
-
-### Read-Only
-
-- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`,`username`".
-- `password` (String, Sensitive) Credential password
-- `username` (String) Credential username
diff --git a/docs/resources/argus_instance.md b/docs/resources/argus_instance.md
deleted file mode 100644
index 1e71ed44..00000000
--- a/docs/resources/argus_instance.md
+++ /dev/null
@@ -1,235 +0,0 @@
----
-# generated by https://github.com/hashicorp/terraform-plugin-docs
-page_title: "stackit_argus_instance Resource - stackit"
-subcategory: ""
-description: |-
- Argus instance resource schema. Must have a region specified in the provider configuration.
- !> The stackit_argus_instance resource has been deprecated and will be removed after February 26th 2025. Please use stackit_observability_instance instead, which offers the exact same functionality.
- Example move
- Example to move the deprecated stackit_argus_instance resource to the newstackit_observability_instance resource:
- Add a new stackit_observability_instance resource with the same values like your previous stackit_argus_instance resource.Add a moved block which reference the stackit_argus_instance and stackit_observability_instance resource.Remove your old stackit_argus_instance resource and run $ terraform apply.
-
- resource "stackit_argus_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-instance"
- plan_name = "Monitoring-Medium-EU01"
- acl = ["1.1.1.1/32", "2.2.2.2/32"]
- metrics_retention_days = 7
- metrics_retention_days_5m_downsampling = 30
- metrics_retention_days_1h_downsampling = 365
- }
-
- moved {
- from = stackit_argus_instance.example
- to = stackit_observability_instance.example
- }
-
- resource "stackit_observability_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-instance"
- plan_name = "Monitoring-Medium-EU01"
- acl = ["1.1.1.1/32", "2.2.2.2/32"]
- metrics_retention_days = 7
- metrics_retention_days_5m_downsampling = 30
- metrics_retention_days_1h_downsampling = 365
- }
----
-
-# stackit_argus_instance (Resource)
-
-Argus instance resource schema. Must have a `region` specified in the provider configuration.
-
-!> The `stackit_argus_instance` resource has been deprecated and will be removed after February 26th 2025. Please use `stackit_observability_instance` instead, which offers the exact same functionality.
-
-## Example move
-Example to move the deprecated `stackit_argus_instance` resource to the new`stackit_observability_instance` resource:
-1. Add a new `stackit_observability_instance` resource with the same values like your previous `stackit_argus_instance` resource.
-1. Add a moved block which reference the `stackit_argus_instance` and `stackit_observability_instance` resource.
-1. Remove your old `stackit_argus_instance` resource and run `$ terraform apply`.
-```terraform
-resource "stackit_argus_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-instance"
- plan_name = "Monitoring-Medium-EU01"
- acl = ["1.1.1.1/32", "2.2.2.2/32"]
- metrics_retention_days = 7
- metrics_retention_days_5m_downsampling = 30
- metrics_retention_days_1h_downsampling = 365
-}
-
-moved {
- from = stackit_argus_instance.example
- to = stackit_observability_instance.example
-}
-
-resource "stackit_observability_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-instance"
- plan_name = "Monitoring-Medium-EU01"
- acl = ["1.1.1.1/32", "2.2.2.2/32"]
- metrics_retention_days = 7
- metrics_retention_days_5m_downsampling = 30
- metrics_retention_days_1h_downsampling = 365
-}
-```
-
-## Example Usage
-
-```terraform
-resource "stackit_argus_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-instance"
- plan_name = "Monitoring-Medium-EU01"
- acl = ["1.1.1.1/32", "2.2.2.2/32"]
- metrics_retention_days = 7
- metrics_retention_days_5m_downsampling = 30
- metrics_retention_days_1h_downsampling = 365
-}
-```
-
-
-## Schema
-
-### Required
-
-- `name` (String) The name of the Argus instance.
-- `plan_name` (String) Specifies the Argus plan. E.g. `Monitoring-Medium-EU01`.
-- `project_id` (String) STACKIT project ID to which the instance is associated.
-
-### Optional
-
-- `acl` (Set of String) The access control list for this instance. Each entry is an IP address range that is permitted to access, in CIDR notation.
-- `alert_config` (Attributes) Alert configuration for the instance. (see [below for nested schema](#nestedatt--alert_config))
-- `metrics_retention_days` (Number) Specifies for how many days the raw metrics are kept.
-- `metrics_retention_days_1h_downsampling` (Number) Specifies for how many days the 1h downsampled metrics are kept. must be less than the value of the 5m downsampling retention. Default is set to `0` (disabled).
-- `metrics_retention_days_5m_downsampling` (Number) Specifies for how many days the 5m downsampled metrics are kept. must be less than the value of the general retention. Default is set to `0` (disabled).
-- `parameters` (Map of String) Additional parameters.
-
-### Read-Only
-
-- `alerting_url` (String) Specifies Alerting URL.
-- `dashboard_url` (String) Specifies Argus instance dashboard URL.
-- `grafana_initial_admin_password` (String, Sensitive) Specifies an initial Grafana admin password.
-- `grafana_initial_admin_user` (String) Specifies an initial Grafana admin username.
-- `grafana_public_read_access` (Boolean) If true, anyone can access Grafana dashboards without logging in.
-- `grafana_url` (String) Specifies Grafana URL.
-- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`".
-- `instance_id` (String) The Argus instance ID.
-- `is_updatable` (Boolean) Specifies if the instance can be updated.
-- `jaeger_traces_url` (String)
-- `jaeger_ui_url` (String)
-- `logs_push_url` (String) Specifies URL for pushing logs.
-- `logs_url` (String) Specifies Logs URL.
-- `metrics_push_url` (String) Specifies URL for pushing metrics.
-- `metrics_url` (String) Specifies metrics URL.
-- `otlp_traces_url` (String)
-- `plan_id` (String) The Argus plan ID.
-- `targets_url` (String) Specifies Targets URL.
-- `zipkin_spans_url` (String)
-
-
-### Nested Schema for `alert_config`
-
-Required:
-
-- `receivers` (Attributes List) List of alert receivers. (see [below for nested schema](#nestedatt--alert_config--receivers))
-- `route` (Attributes) Route configuration for the alerts. (see [below for nested schema](#nestedatt--alert_config--route))
-
-Optional:
-
-- `global` (Attributes) Global configuration for the alerts. (see [below for nested schema](#nestedatt--alert_config--global))
-
-
-### Nested Schema for `alert_config.receivers`
-
-Required:
-
-- `name` (String) Name of the receiver.
-
-Optional:
-
-- `email_configs` (Attributes List) List of email configurations. (see [below for nested schema](#nestedatt--alert_config--receivers--email_configs))
-- `opsgenie_configs` (Attributes List) List of OpsGenie configurations. (see [below for nested schema](#nestedatt--alert_config--receivers--opsgenie_configs))
-- `webhooks_configs` (Attributes List) List of Webhooks configurations. (see [below for nested schema](#nestedatt--alert_config--receivers--webhooks_configs))
-
-
-### Nested Schema for `alert_config.receivers.email_configs`
-
-Optional:
-
-- `auth_identity` (String) SMTP authentication information. Must be a valid email address
-- `auth_password` (String) SMTP authentication password.
-- `auth_username` (String) SMTP authentication username.
-- `from` (String) The sender email address. Must be a valid email address
-- `smart_host` (String) The SMTP host through which emails are sent.
-- `to` (String) The email address to send notifications to. Must be a valid email address
-
-
-
-### Nested Schema for `alert_config.receivers.opsgenie_configs`
-
-Optional:
-
-- `api_key` (String) The API key for OpsGenie.
-- `api_url` (String) The host to send OpsGenie API requests to. Must be a valid URL
-- `tags` (String) Comma separated list of tags attached to the notifications.
-
-
-
-### Nested Schema for `alert_config.receivers.webhooks_configs`
-
-Optional:
-
-- `ms_teams` (Boolean) Microsoft Teams webhooks require special handling, set this to true if the webhook is for Microsoft Teams.
-- `url` (String) The endpoint to send HTTP POST requests to. Must be a valid URL
-
-
-
-
-### Nested Schema for `alert_config.route`
-
-Required:
-
-- `receiver` (String) The name of the receiver to route the alerts to.
-
-Optional:
-
-- `group_by` (List of String) The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.
-- `group_interval` (String) How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)
-- `group_wait` (String) How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.)
-- `match` (Map of String) A set of equality matchers an alert has to fulfill to match the node.
-- `match_regex` (Map of String) A set of regex-matchers an alert has to fulfill to match the node.
-- `repeat_interval` (String) How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more).
-- `routes` (Attributes List) List of child routes. (see [below for nested schema](#nestedatt--alert_config--route--routes))
-
-
-### Nested Schema for `alert_config.route.routes`
-
-Required:
-
-- `receiver` (String) The name of the receiver to route the alerts to.
-
-Optional:
-
-- `group_by` (List of String) The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.
-- `group_interval` (String) How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)
-- `group_wait` (String) How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.)
-- `match` (Map of String) A set of equality matchers an alert has to fulfill to match the node.
-- `match_regex` (Map of String) A set of regex-matchers an alert has to fulfill to match the node.
-- `repeat_interval` (String) How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more).
-
-
-
-
-### Nested Schema for `alert_config.global`
-
-Optional:
-
-- `opsgenie_api_key` (String, Sensitive) The API key for OpsGenie.
-- `opsgenie_api_url` (String) The host to send OpsGenie API requests to. Must be a valid URL
-- `resolve_timeout` (String) The default value used by alertmanager if the alert does not include EndsAt. After this time passes, it can declare the alert as resolved if it has not been updated. This has no impact on alerts from Prometheus, as they always include EndsAt.
-- `smtp_auth_identity` (String) SMTP authentication information. Must be a valid email address
-- `smtp_auth_password` (String, Sensitive) SMTP Auth using LOGIN and PLAIN.
-- `smtp_auth_username` (String) SMTP Auth using CRAM-MD5, LOGIN and PLAIN. If empty, Alertmanager doesn't authenticate to the SMTP server.
-- `smtp_from` (String) The default SMTP From header field. Must be a valid email address
-- `smtp_smart_host` (String) The default SMTP smarthost used for sending emails, including port number in format `host:port` (eg. `smtp.example.com:587`). Port number usually is 25, or 587 for SMTP over TLS (sometimes referred to as STARTTLS).
diff --git a/docs/resources/argus_scrapeconfig.md b/docs/resources/argus_scrapeconfig.md
deleted file mode 100644
index df96442a..00000000
--- a/docs/resources/argus_scrapeconfig.md
+++ /dev/null
@@ -1,180 +0,0 @@
----
-# generated by https://github.com/hashicorp/terraform-plugin-docs
-page_title: "stackit_argus_scrapeconfig Resource - stackit"
-subcategory: ""
-description: |-
- Argus scrape config resource schema. Must have a region specified in the provider configuration.
- !> The stackit_argus_scrapeconfig resource has been deprecated and will be removed after February 26th 2025. Please use stackit_observability_scrapeconfig instead, which offers the exact same functionality.
- Example move
- Example to move the deprecated stackit_argus_scrapeconfig resource to the new stackit_observability_scrapeconfig resource:
- Add a new stackit_observability_scrapeconfig resource with the same values like your previous stackit_argus_scrapeconfig resource.Add a moved block which reference the stackit_argus_scrapeconfig and stackit_observability_scrapeconfig resource.Remove your old stackit_argus_scrapeconfig resource and run $ terraform apply.
-
- resource "stackit_argus_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-job"
- metrics_path = "/my-metrics"
- saml2 = {
- enable_url_parameters = true
- }
- targets = [
- {
- urls = ["url1", "urls2"]
- labels = {
- "url1" = "dev"
- }
- }
- ]
- }
-
- moved {
- from = stackit_argus_scrapeconfig.example
- to = stackit_observability_scrapeconfig.example
- }
-
- resource "stackit_observability_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-job"
- metrics_path = "/my-metrics"
- saml2 = {
- enable_url_parameters = true
- }
- targets = [
- {
- urls = ["url1", "urls2"]
- labels = {
- "url1" = "dev"
- }
- }
- ]
- }
----
-
-# stackit_argus_scrapeconfig (Resource)
-
-Argus scrape config resource schema. Must have a `region` specified in the provider configuration.
-
-!> The `stackit_argus_scrapeconfig` resource has been deprecated and will be removed after February 26th 2025. Please use `stackit_observability_scrapeconfig` instead, which offers the exact same functionality.
-
-## Example move
-Example to move the deprecated `stackit_argus_scrapeconfig` resource to the new `stackit_observability_scrapeconfig` resource:
-1. Add a new `stackit_observability_scrapeconfig` resource with the same values like your previous `stackit_argus_scrapeconfig` resource.
-1. Add a moved block which reference the `stackit_argus_scrapeconfig` and `stackit_observability_scrapeconfig` resource.
-1. Remove your old `stackit_argus_scrapeconfig` resource and run `$ terraform apply`.
-```terraform
-resource "stackit_argus_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-job"
- metrics_path = "/my-metrics"
- saml2 = {
- enable_url_parameters = true
- }
- targets = [
- {
- urls = ["url1", "urls2"]
- labels = {
- "url1" = "dev"
- }
- }
- ]
-}
-
-moved {
- from = stackit_argus_scrapeconfig.example
- to = stackit_observability_scrapeconfig.example
-}
-
-resource "stackit_observability_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-job"
- metrics_path = "/my-metrics"
- saml2 = {
- enable_url_parameters = true
- }
- targets = [
- {
- urls = ["url1", "urls2"]
- labels = {
- "url1" = "dev"
- }
- }
- ]
-}
-```
-
-## Example Usage
-
-```terraform
-resource "stackit_argus_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-job"
- metrics_path = "/my-metrics"
- saml2 = {
- enable_url_parameters = true
- }
- targets = [
- {
- urls = ["url1", "urls2"]
- labels = {
- "url1" = "dev"
- }
- }
- ]
-}
-```
-
-
-## Schema
-
-### Required
-
-- `instance_id` (String) Argus instance ID to which the scraping job is associated.
-- `metrics_path` (String) Specifies the job scraping url path. E.g. `/metrics`.
-- `name` (String) Specifies the name of the scraping job.
-- `project_id` (String) STACKIT project ID to which the scraping job is associated.
-- `targets` (Attributes List) The targets list (specified by the static config). (see [below for nested schema](#nestedatt--targets))
-
-### Optional
-
-- `basic_auth` (Attributes) A basic authentication block. (see [below for nested schema](#nestedatt--basic_auth))
-- `saml2` (Attributes) A SAML2 configuration block. (see [below for nested schema](#nestedatt--saml2))
-- `sample_limit` (Number) Specifies the scrape sample limit. Upper limit depends on the service plan. Defaults to `5000`.
-- `scheme` (String) Specifies the http scheme. Defaults to `https`.
-- `scrape_interval` (String) Specifies the scrape interval as duration string. Defaults to `5m`.
-- `scrape_timeout` (String) Specifies the scrape timeout as duration string. Defaults to `2m`.
-
-### Read-Only
-
-- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`,`name`".
-
-
-### Nested Schema for `targets`
-
-Required:
-
-- `urls` (List of String) Specifies target URLs.
-
-Optional:
-
-- `labels` (Map of String) Specifies labels.
-
-
-
-### Nested Schema for `basic_auth`
-
-Required:
-
-- `password` (String, Sensitive) Specifies basic auth password.
-- `username` (String) Specifies basic auth username.
-
-
-
-### Nested Schema for `saml2`
-
-Optional:
-
-- `enable_url_parameters` (Boolean) Specifies if URL parameters are enabled. Defaults to `true`
diff --git a/examples/data-sources/stackit_argus_instance/data-source.tf b/examples/data-sources/stackit_argus_instance/data-source.tf
deleted file mode 100644
index 462e0736..00000000
--- a/examples/data-sources/stackit_argus_instance/data-source.tf
+++ /dev/null
@@ -1,4 +0,0 @@
-data "stackit_argus_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
diff --git a/examples/data-sources/stackit_argus_scrapeconfig/data-source.tf b/examples/data-sources/stackit_argus_scrapeconfig/data-source.tf
deleted file mode 100644
index e78d7ae5..00000000
--- a/examples/data-sources/stackit_argus_scrapeconfig/data-source.tf
+++ /dev/null
@@ -1,5 +0,0 @@
-data "stackit_argus_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- job_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
diff --git a/examples/data-sources/stackit_observability_scrapeconfig/data-source.tf b/examples/data-sources/stackit_observability_scrapeconfig/data-source.tf
index c7c7d387..2efccf14 100644
--- a/examples/data-sources/stackit_observability_scrapeconfig/data-source.tf
+++ b/examples/data-sources/stackit_observability_scrapeconfig/data-source.tf
@@ -1,5 +1,5 @@
data "stackit_observability_scrapeconfig" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- job_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ name = "example"
}
diff --git a/examples/resources/stackit_argus_credential/resource.tf b/examples/resources/stackit_argus_credential/resource.tf
deleted file mode 100644
index 72e94510..00000000
--- a/examples/resources/stackit_argus_credential/resource.tf
+++ /dev/null
@@ -1,4 +0,0 @@
-resource "stackit_argus_credential" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
diff --git a/examples/resources/stackit_argus_instance/resource.tf b/examples/resources/stackit_argus_instance/resource.tf
deleted file mode 100644
index 71516262..00000000
--- a/examples/resources/stackit_argus_instance/resource.tf
+++ /dev/null
@@ -1,9 +0,0 @@
-resource "stackit_argus_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-instance"
- plan_name = "Monitoring-Medium-EU01"
- acl = ["1.1.1.1/32", "2.2.2.2/32"]
- metrics_retention_days = 7
- metrics_retention_days_5m_downsampling = 30
- metrics_retention_days_1h_downsampling = 365
-}
diff --git a/examples/resources/stackit_argus_scrapeconfig/resource.tf b/examples/resources/stackit_argus_scrapeconfig/resource.tf
deleted file mode 100644
index 4be234ee..00000000
--- a/examples/resources/stackit_argus_scrapeconfig/resource.tf
+++ /dev/null
@@ -1,17 +0,0 @@
-resource "stackit_argus_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-job"
- metrics_path = "/my-metrics"
- saml2 = {
- enable_url_parameters = true
- }
- targets = [
- {
- urls = ["url1", "urls2"]
- labels = {
- "url1" = "dev"
- }
- }
- ]
-}
diff --git a/stackit/internal/services/argus/argus_acc_test.go b/stackit/internal/services/argus/argus_acc_test.go
deleted file mode 100644
index 2d1d2ded..00000000
--- a/stackit/internal/services/argus/argus_acc_test.go
+++ /dev/null
@@ -1,896 +0,0 @@
-package argus_test
-
-import (
- "context"
- "fmt"
- "strings"
- "testing"
-
- "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
- "github.com/hashicorp/terraform-plugin-testing/helper/resource"
- "github.com/hashicorp/terraform-plugin-testing/terraform"
- "github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/utils"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
- "github.com/stackitcloud/stackit-sdk-go/services/argus/wait"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
-)
-
-var instanceResource = map[string]string{
- "project_id": testutil.ProjectId,
- "name": testutil.ResourceNameWithDateTime("argus"),
- "plan_name": "Observability-Monitoring-Basic-EU01",
- "new_plan_name": "Observability-Monitoring-Medium-EU01",
- "acl-0": "1.2.3.4/32",
- "acl-1": "111.222.111.222/32",
- "acl-1-updated": "111.222.111.125/32",
- "metrics_retention_days": "60",
- "metrics_retention_days_5m_downsampling": "30",
- "metrics_retention_days_1h_downsampling": "15",
-}
-
-var scrapeConfigResource = map[string]string{
- "project_id": testutil.ProjectId,
- "name": fmt.Sprintf("scrapeconfig-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum)),
- "urls": fmt.Sprintf(`{urls = ["www.%s.de","%s.de"]}`, acctest.RandStringFromCharSet(15, acctest.CharSetAlphaNum), acctest.RandStringFromCharSet(15, acctest.CharSetAlphaNum)),
- "metrics_path": "/metrics",
- "scheme": "https",
- "scrape_interval": "4m", // non-default
- "sample_limit": "7", // non-default
- "saml2_enable_url_parameters": "false",
-}
-
-var credentialResource = map[string]string{
- "project_id": testutil.ProjectId,
-}
-
-func buildAlertConfigReceivers(hasOpsGenie, hasEmail, hasWebhook bool) string {
- if !hasOpsGenie && !hasEmail && !hasWebhook {
- return ""
- }
-
- receivers := "["
-
- if hasOpsGenie {
- receivers += `
- {
- name = "OpsGenieReceiverInfo"
- opsgenie_configs = [
- {
- tags = "iam,argus-alert"
- api_key = "example-api-key"
- }
- ]
- },
-`
- }
-
- if hasEmail {
- receivers += `
- {
- name = "EmailReceiverInfo"
- email_configs = [
- {
- to = "me@example.com"
- },
- ]
- },
-`
- }
-
- if hasWebhook {
- receivers += `
- {
- name = "WebhookReceiverInfo"
- webhooks_configs = [
- {
- url = "https://example.com"
- ms_teams = true
- },
- ]
- },
-`
- }
-
- return receivers + "]"
-}
-
-func buildAlertConfigRoute(childRoutes bool) string {
- route := `{
- receiver = "OpsGenieReceiverInfo"
- group_by = ["alertname"]
- group_interval = "10m"
- group_wait = "1m"
- repeat_interval = "1h"`
-
- if childRoutes {
- route += `
- routes = [
- {
- match = {
- severity = "critical"
- }
- receiver = "OpsGenieReceiverInfo"
- },
- {
- match = {
- severity = "warning"
- }
- receiver = "WebhookReceiverInfo"
- }
- ]`
- }
-
- return route + "\n}"
-}
-
-func buildAlertConfigGlobal(includeEmailOptions bool) string {
- defaultOptions := `{
- resolve_timeout = "5m"
- opsgenie_api_key = "example-api-key"
- opsgenie_api_url = "https://api.eu.opsgenie.com"`
-
- if !includeEmailOptions {
- return defaultOptions + "\n}"
- }
- return defaultOptions + `
- smtp_smart_host = "smtp.example.com:587"
- smtp_from = "me@example.com"
-}`
-}
-
-func buildAlertConfig(receivers, route, global string) *string {
- if receivers == "" && route == "" && global == "" {
- return nil
- }
- returnStr := fmt.Sprintf(`
- alert_config = {
- receivers = %s,
- route = %s,
- global = %s
- }
- `, receivers, route, global)
- return &returnStr
-}
-
-func instanceResourceConfig(acl, metricsRetentionDays, metricsRetentionDays1hDownsampling, metricsRetentionDays5mDownsampling, alertConfig *string, instanceName, planName string) string {
- var aclStr string
- var metricsRetentionDaysStr string
- var metricsRetentionDays1hDownsamplingStr string
- var metricsRetentionDays5mDownsamplingStr string
- var alertConfigStr string
-
- if acl != nil {
- aclStr = fmt.Sprintf("acl = %s", *acl)
- }
-
- if metricsRetentionDays != nil {
- metricsRetentionDaysStr = fmt.Sprintf("metrics_retention_days = %s", *metricsRetentionDays)
- }
-
- if metricsRetentionDays1hDownsampling != nil {
- metricsRetentionDays1hDownsamplingStr = fmt.Sprintf("metrics_retention_days_1h_downsampling = %s", *metricsRetentionDays1hDownsampling)
- }
-
- if metricsRetentionDays5mDownsampling != nil {
- metricsRetentionDays5mDownsamplingStr = fmt.Sprintf("metrics_retention_days_5m_downsampling = %s", *metricsRetentionDays5mDownsampling)
- }
-
- if alertConfig != nil {
- alertConfigStr = *alertConfig
- }
-
- optionalsStr := strings.Join([]string{aclStr, metricsRetentionDaysStr, metricsRetentionDays1hDownsamplingStr, metricsRetentionDays5mDownsamplingStr, alertConfigStr}, "\n")
-
- return fmt.Sprintf(`
- resource "stackit_argus_instance" "instance" {
- project_id = "%s"
- name = "%s"
- plan_name = "%s"
- %s
- }
- `,
- instanceResource["project_id"],
- instanceName,
- planName,
- optionalsStr,
- )
-}
-
-func scrapeConfigResourceConfig(target, saml2EnableUrlParameters string) string {
- return fmt.Sprintf(
- `resource "stackit_argus_scrapeconfig" "scrapeconfig" {
- project_id = stackit_argus_instance.instance.project_id
- instance_id = stackit_argus_instance.instance.instance_id
- name = "%s"
- metrics_path = "%s"
- targets = [%s]
- scrape_interval = "%s"
- sample_limit = %s
- saml2 = {
- enable_url_parameters = %s
- }
- }`,
- scrapeConfigResource["name"],
- scrapeConfigResource["metrics_path"],
- target,
- scrapeConfigResource["scrape_interval"],
- scrapeConfigResource["sample_limit"],
- saml2EnableUrlParameters,
- )
-}
-
-func credentialResourceConfig() string {
- return `resource "stackit_argus_credential" "credential" {
- project_id = stackit_argus_instance.instance.project_id
- instance_id = stackit_argus_instance.instance.instance_id
- }`
-}
-
-func resourceConfig(acl, metricsRetentionDays, metricsRetentionDays1hDownsampling, metricsRetentionDays5mDownsampling, alertConfig *string, instanceName, planName, target, saml2EnableUrlParameters string) string {
- return fmt.Sprintf("%s\n\n%s\n\n%s\n\n%s",
- testutil.ArgusProviderConfig(),
- instanceResourceConfig(acl,
- metricsRetentionDays,
- metricsRetentionDays1hDownsampling,
- metricsRetentionDays5mDownsampling,
- alertConfig,
- instanceName,
- planName),
- scrapeConfigResourceConfig(target, saml2EnableUrlParameters),
- credentialResourceConfig(),
- )
-}
-
-func TestAccResource(t *testing.T) {
- resource.Test(t, resource.TestCase{
- ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
- CheckDestroy: testAccCheckArgusDestroy,
- Steps: []resource.TestStep{
- // Creation
- {
- Config: resourceConfig(
- utils.Ptr(fmt.Sprintf(
- "[%q, %q, %q]",
- instanceResource["acl-0"],
- instanceResource["acl-1"],
- instanceResource["acl-1"],
- )),
- utils.Ptr(instanceResource["metrics_retention_days"]),
- utils.Ptr(instanceResource["metrics_retention_days_1h_downsampling"]),
- utils.Ptr(instanceResource["metrics_retention_days_5m_downsampling"]),
- buildAlertConfig(buildAlertConfigReceivers(true, false, true), buildAlertConfigRoute(false), buildAlertConfigGlobal(false)),
- instanceResource["name"],
- instanceResource["plan_name"],
- scrapeConfigResource["urls"],
- scrapeConfigResource["saml2_enable_url_parameters"],
- ),
- Check: resource.ComposeAggregateTestCheckFunc(
- // Instance data
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "project_id", instanceResource["project_id"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "instance_id"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "name", instanceResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "plan_name", instanceResource["plan_name"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "dashboard_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "is_updatable"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_public_read_access"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_user"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_password"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "metrics_retention_days", instanceResource["metrics_retention_days"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "metrics_retention_days_5m_downsampling", instanceResource["metrics_retention_days_5m_downsampling"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "metrics_retention_days_1h_downsampling", instanceResource["metrics_retention_days_1h_downsampling"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_push_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "targets_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "alerting_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_push_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_traces_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_ui_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "otlp_traces_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "zipkin_spans_url"),
-
- // Alert Config
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.0", "alertname"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_interval", "10m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_wait", "1m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.repeat_interval", "1h"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.resolve_timeout", "5m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.opsgenie_api_key", "example-api-key"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.opsgenie_api_url", "https://api.eu.opsgenie.com"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.receiver", "OpsGenieReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.0", "alertname"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_interval", "10m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_wait", "1m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.repeat_interval", "1h"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.#", "0"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.name", "OpsGenieReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.tags", "iam,argus-alert"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.api_key",
- "example-api-key"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.name", "WebhookReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.webhooks_configs.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.webhooks_configs.0.url", "https://example.com"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.webhooks_configs.0.ms_teams", "true"),
-
- // ACL
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.0", instanceResource["acl-0"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.1", instanceResource["acl-1"]),
-
- // scrape config data
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "project_id",
- "stackit_argus_scrapeconfig.scrapeconfig", "project_id",
- ),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "stackit_argus_scrapeconfig.scrapeconfig", "instance_id",
- ),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
-
- // credentials
- resource.TestCheckResourceAttr("stackit_argus_credential.credential", "project_id", credentialResource["project_id"]),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "stackit_argus_credential.credential", "instance_id",
- ),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "username"),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "password"),
- ),
- },
- // Update Alert Config with complete Receiver (email, webhook and opsgenie configs), global options and Route with child routes
- {
- Config: resourceConfig(
- utils.Ptr(fmt.Sprintf(
- "[%q, %q, %q]",
- instanceResource["acl-0"],
- instanceResource["acl-1"],
- instanceResource["acl-1"],
- )),
- utils.Ptr(instanceResource["metrics_retention_days"]),
- utils.Ptr(instanceResource["metrics_retention_days_1h_downsampling"]),
- utils.Ptr(instanceResource["metrics_retention_days_5m_downsampling"]),
- buildAlertConfig(buildAlertConfigReceivers(true, true, true), buildAlertConfigRoute(true), buildAlertConfigGlobal(true)),
- instanceResource["name"],
- instanceResource["plan_name"],
- scrapeConfigResource["urls"],
- scrapeConfigResource["saml2_enable_url_parameters"],
- ),
- Check: resource.ComposeAggregateTestCheckFunc(
- // Instance data
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "project_id", instanceResource["project_id"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "instance_id"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "name", instanceResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "plan_name", instanceResource["plan_name"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "dashboard_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "is_updatable"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_public_read_access"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_user"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_password"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "metrics_retention_days", instanceResource["metrics_retention_days"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "metrics_retention_days_5m_downsampling", instanceResource["metrics_retention_days_5m_downsampling"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "metrics_retention_days_1h_downsampling", instanceResource["metrics_retention_days_1h_downsampling"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_push_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "targets_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "alerting_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_push_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_traces_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_ui_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "otlp_traces_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "zipkin_spans_url"),
-
- // Alert Config
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.#", "3"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.0", "alertname"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_interval", "10m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_wait", "1m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.repeat_interval", "1h"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.resolve_timeout", "5m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.opsgenie_api_key", "example-api-key"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.opsgenie_api_url", "https://api.eu.opsgenie.com"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.smtp_smart_host", "smtp.example.com:587"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.smtp_from", "me@example.com"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.receiver", "OpsGenieReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.0", "alertname"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_interval", "10m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_wait", "1m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.repeat_interval", "1h"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.0.match.severity", "critical"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.0.receiver", "OpsGenieReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.1.match.severity", "warning"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.1.receiver", "WebhookReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.name", "OpsGenieReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.tags", "iam,argus-alert"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.api_key",
- "example-api-key"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.name", "EmailReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.email_configs.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.email_configs.0.to", "me@example.com"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.2.name", "WebhookReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.2.webhooks_configs.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.2.webhooks_configs.0.url", "https://example.com"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.2.webhooks_configs.0.ms_teams", "true"),
-
- // ACL
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.0", instanceResource["acl-0"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.1", instanceResource["acl-1"]),
-
- // scrape config data
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "project_id",
- "stackit_argus_scrapeconfig.scrapeconfig", "project_id",
- ),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "stackit_argus_scrapeconfig.scrapeconfig", "instance_id",
- ),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
-
- // credentials
- resource.TestCheckResourceAttr("stackit_argus_credential.credential", "project_id", credentialResource["project_id"]),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "stackit_argus_credential.credential", "instance_id",
- ),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "username"),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "password"),
- ),
- },
- // Update without ACL, partial metrics retention days and NO alert configs
- {
- Config: resourceConfig(
- nil,
- nil,
- utils.Ptr(instanceResource["metrics_retention_days_1h_downsampling"]),
- nil,
- nil,
- instanceResource["name"],
- instanceResource["plan_name"],
- scrapeConfigResource["urls"],
- scrapeConfigResource["saml2_enable_url_parameters"],
- ),
- Check: resource.ComposeAggregateTestCheckFunc(
- // Instance data
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "project_id", instanceResource["project_id"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "instance_id"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "name", instanceResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "plan_name", instanceResource["plan_name"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "dashboard_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "is_updatable"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_public_read_access"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_user"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_password"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_retention_days"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_retention_days_5m_downsampling"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "metrics_retention_days_1h_downsampling", instanceResource["metrics_retention_days_1h_downsampling"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_push_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "targets_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "alerting_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_push_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_traces_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_ui_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "otlp_traces_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "zipkin_spans_url"),
-
- // ACL
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.#", "0"),
-
- // scrape config data
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "project_id",
- "stackit_argus_scrapeconfig.scrapeconfig", "project_id",
- ),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "stackit_argus_scrapeconfig.scrapeconfig", "instance_id",
- ),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
-
- // credentials
- resource.TestCheckResourceAttr("stackit_argus_credential.credential", "project_id", credentialResource["project_id"]),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "stackit_argus_credential.credential", "instance_id",
- ),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "username"),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "password"),
- ),
- },
- // Update with empty ACL, NO metrics retention days and NO alert configs
- {
- Config: resourceConfig(
- utils.Ptr("[]"),
- nil,
- nil,
- nil,
- nil,
- instanceResource["name"],
- instanceResource["plan_name"],
- scrapeConfigResource["urls"],
- scrapeConfigResource["saml2_enable_url_parameters"],
- ),
- Check: resource.ComposeAggregateTestCheckFunc(
- // Instance data
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "project_id", instanceResource["project_id"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "instance_id"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "name", instanceResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "plan_name", instanceResource["plan_name"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "dashboard_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "is_updatable"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_public_read_access"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_user"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "grafana_initial_admin_password"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_retention_days"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_retention_days_5m_downsampling"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_retention_days_1h_downsampling"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "metrics_push_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "targets_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "alerting_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "logs_push_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_traces_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "jaeger_ui_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "otlp_traces_url"),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "zipkin_spans_url"),
-
- // ACL
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.#", "0"),
-
- // scrape config data
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "project_id",
- "stackit_argus_scrapeconfig.scrapeconfig", "project_id",
- ),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "stackit_argus_scrapeconfig.scrapeconfig", "instance_id",
- ),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
-
- // credentials
- resource.TestCheckResourceAttr("stackit_argus_credential.credential", "project_id", credentialResource["project_id"]),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "stackit_argus_credential.credential", "instance_id",
- ),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "username"),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "password"),
- ),
- },
- // Data source
- {
- Config: fmt.Sprintf(`
- %s
-
- data "stackit_argus_instance" "instance" {
- project_id = stackit_argus_instance.instance.project_id
- instance_id = stackit_argus_instance.instance.instance_id
- }
-
- data "stackit_argus_scrapeconfig" "scrapeconfig" {
- project_id = stackit_argus_scrapeconfig.scrapeconfig.project_id
- instance_id = stackit_argus_scrapeconfig.scrapeconfig.instance_id
- name = stackit_argus_scrapeconfig.scrapeconfig.name
- }
- `,
- resourceConfig(
- utils.Ptr(fmt.Sprintf(
- "[%q, %q]",
- instanceResource["acl-0"],
- instanceResource["acl-1"],
- )),
- utils.Ptr(instanceResource["metrics_retention_days"]),
- utils.Ptr(instanceResource["metrics_retention_days_1h_downsampling"]),
- utils.Ptr(instanceResource["metrics_retention_days_5m_downsampling"]),
- buildAlertConfig(buildAlertConfigReceivers(true, false, true), buildAlertConfigRoute(true), buildAlertConfigGlobal(false)),
- instanceResource["name"],
- instanceResource["plan_name"],
- scrapeConfigResource["urls"],
- scrapeConfigResource["saml2_enable_url_parameters"],
- ),
- ),
- Check: resource.ComposeAggregateTestCheckFunc(
- // Instance data
- resource.TestCheckResourceAttr("data.stackit_argus_instance.instance", "project_id", instanceResource["project_id"]),
- resource.TestCheckResourceAttrSet("data.stackit_argus_instance.instance", "instance_id"),
- resource.TestCheckResourceAttr("data.stackit_argus_instance.instance", "name", instanceResource["name"]),
- resource.TestCheckResourceAttr("data.stackit_argus_instance.instance", "plan_name", instanceResource["plan_name"]),
- resource.TestCheckResourceAttr("data.stackit_argus_instance.instance", "acl.#", "2"),
- resource.TestCheckResourceAttr("data.stackit_argus_instance.instance", "acl.0", instanceResource["acl-0"]),
- resource.TestCheckResourceAttr("data.stackit_argus_instance.instance", "acl.1", instanceResource["acl-1"]),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "project_id",
- "data.stackit_argus_instance.instance", "project_id",
- ),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_instance.instance", "instance_id",
- "data.stackit_argus_instance.instance", "instance_id",
- ),
- // scrape config data
- resource.TestCheckResourceAttrPair(
- "stackit_argus_scrapeconfig.scrapeconfig", "project_id",
- "data.stackit_argus_scrapeconfig.scrapeconfig", "project_id",
- ),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_scrapeconfig.scrapeconfig", "instance_id",
- "data.stackit_argus_scrapeconfig.scrapeconfig", "instance_id",
- ),
- resource.TestCheckResourceAttrPair(
- "stackit_argus_scrapeconfig.scrapeconfig", "name",
- "data.stackit_argus_scrapeconfig.scrapeconfig", "name",
- ),
- resource.TestCheckResourceAttr("data.stackit_argus_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
- resource.TestCheckResourceAttr("data.stackit_argus_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
- resource.TestCheckResourceAttr("data.stackit_argus_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
- resource.TestCheckResourceAttr("data.stackit_argus_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
- resource.TestCheckResourceAttr("data.stackit_argus_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
- resource.TestCheckResourceAttr("data.stackit_argus_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
- ),
- },
- // Import 1
- {
- ResourceName: "stackit_argus_instance.instance",
- ImportStateIdFunc: func(s *terraform.State) (string, error) {
- r, ok := s.RootModule().Resources["stackit_argus_instance.instance"]
- if !ok {
- return "", fmt.Errorf("couldn't find resource stackit_argus_instance.instance")
- }
- instanceId, ok := r.Primary.Attributes["instance_id"]
- if !ok {
- return "", fmt.Errorf("couldn't find attribute instance_id")
- }
-
- return fmt.Sprintf("%s,%s", testutil.ProjectId, instanceId), nil
- },
- ImportState: true,
- ImportStateVerify: true,
- },
- // Import 2
- {
- ResourceName: "stackit_argus_scrapeconfig.scrapeconfig",
- ImportStateIdFunc: func(s *terraform.State) (string, error) {
- r, ok := s.RootModule().Resources["stackit_argus_scrapeconfig.scrapeconfig"]
- if !ok {
- return "", fmt.Errorf("couldn't find resource stackit_argus_scrapeconfig.scrapeconfig")
- }
- instanceId, ok := r.Primary.Attributes["instance_id"]
- if !ok {
- return "", fmt.Errorf("couldn't find attribute instance_id")
- }
- name, ok := r.Primary.Attributes["name"]
- if !ok {
- return "", fmt.Errorf("couldn't find attribute name")
- }
- return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, instanceId, name), nil
- },
- ImportState: true,
- ImportStateVerify: true,
- },
- // Update
- {
- Config: resourceConfig(
- utils.Ptr(fmt.Sprintf(
- "[%q, %q]",
- instanceResource["acl-0"],
- instanceResource["acl-1-updated"],
- )),
- utils.Ptr(instanceResource["metrics_retention_days"]),
- utils.Ptr(instanceResource["metrics_retention_days_1h_downsampling"]),
- utils.Ptr(instanceResource["metrics_retention_days_5m_downsampling"]),
- buildAlertConfig(buildAlertConfigReceivers(true, false, true), buildAlertConfigRoute(true), buildAlertConfigGlobal(false)),
- fmt.Sprintf("%s-new", instanceResource["name"]),
- instanceResource["new_plan_name"],
- "",
- "true",
- ),
- Check: resource.ComposeAggregateTestCheckFunc(
- // Instance
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "project_id", instanceResource["project_id"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "instance_id"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "name", instanceResource["name"]+"-new"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "plan_name", instanceResource["new_plan_name"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.0", instanceResource["acl-0"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.1", instanceResource["acl-1-updated"]),
-
- // Alert Config
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.0", "alertname"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_interval", "10m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_wait", "1m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.repeat_interval", "1h"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.resolve_timeout", "5m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.opsgenie_api_key", "example-api-key"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.global.opsgenie_api_url", "https://api.eu.opsgenie.com"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.receiver", "OpsGenieReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_by.0", "alertname"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_interval", "10m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.group_wait", "1m"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.repeat_interval", "1h"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.0.match.severity", "critical"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.0.receiver", "OpsGenieReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.1.match.severity", "warning"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.route.routes.1.receiver", "WebhookReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.#", "2"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.name", "OpsGenieReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.tags", "iam,argus-alert"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.api_key",
- "example-api-key"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.name", "WebhookReceiverInfo"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.webhooks_configs.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.webhooks_configs.0.url", "https://example.com"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "alert_config.receivers.1.webhooks_configs.0.ms_teams", "true"),
-
- // Scrape Config
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "targets.#", "0"),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.%", "1"),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", "true"),
-
- // Credentials
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "username"),
- resource.TestCheckResourceAttrSet("stackit_argus_credential.credential", "password"),
- ),
- },
- // Update and remove saml2 attribute
- {
- Config: fmt.Sprintf(`
- %s
-
- resource "stackit_argus_instance" "instance" {
- project_id = "%s"
- name = "%s"
- plan_name = "%s"
- }
-
- resource "stackit_argus_scrapeconfig" "scrapeconfig" {
- project_id = stackit_argus_instance.instance.project_id
- instance_id = stackit_argus_instance.instance.instance_id
- name = "%s"
- targets = [%s]
- scrape_interval = "%s"
- sample_limit = %s
- metrics_path = "%s"
- saml2 = {
- enable_url_parameters = false
- }
- }
- `,
- testutil.ArgusProviderConfig(),
- instanceResource["project_id"],
- instanceResource["name"],
- instanceResource["new_plan_name"],
- scrapeConfigResource["name"],
- scrapeConfigResource["urls"],
- scrapeConfigResource["scrape_interval"],
- scrapeConfigResource["sample_limit"],
- scrapeConfigResource["metrics_path"],
- ),
- Check: resource.ComposeAggregateTestCheckFunc(
- // Instance
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "project_id", instanceResource["project_id"]),
- resource.TestCheckResourceAttrSet("stackit_argus_instance.instance", "instance_id"),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "name", instanceResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "plan_name", instanceResource["new_plan_name"]),
-
- // ACL
- resource.TestCheckResourceAttr("stackit_argus_instance.instance", "acl.#", "0"),
-
- // Scrape Config
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "targets.#", "1"),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.%", "1"),
- resource.TestCheckResourceAttr("stackit_argus_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", "false"),
- ),
- },
-
- // Deletion is done by the framework implicitly
- },
- })
-}
-
-func testAccCheckArgusDestroy(s *terraform.State) error {
- ctx := context.Background()
- var client *argus.APIClient
- var err error
- if testutil.ArgusCustomEndpoint == "" {
- client, err = argus.NewAPIClient(
- config.WithRegion("eu01"),
- )
- } else {
- client, err = argus.NewAPIClient(
- config.WithEndpoint(testutil.ArgusCustomEndpoint),
- )
- }
- if err != nil {
- return fmt.Errorf("creating client: %w", err)
- }
-
- instancesToDestroy := []string{}
- for _, rs := range s.RootModule().Resources {
- if rs.Type != "stackit_argus_instance" {
- continue
- }
- // instance terraform ID: = "[project_id],[instance_id],[name]"
- instanceId := strings.Split(rs.Primary.ID, core.Separator)[1]
- instancesToDestroy = append(instancesToDestroy, instanceId)
- }
-
- instancesResp, err := client.ListInstances(ctx, testutil.ProjectId).Execute()
- if err != nil {
- return fmt.Errorf("getting instancesResp: %w", err)
- }
-
- instances := *instancesResp.Instances
- for i := range instances {
- if utils.Contains(instancesToDestroy, *instances[i].Id) {
- if *instances[i].Status != wait.DeleteSuccess {
- _, err := client.DeleteInstanceExecute(ctx, testutil.ProjectId, *instances[i].Id)
- if err != nil {
- return fmt.Errorf("destroying instance %s during CheckDestroy: %w", *instances[i].Id, err)
- }
- _, err = wait.DeleteInstanceWaitHandler(ctx, client, testutil.ProjectId, *instances[i].Id).WaitWithContext(ctx)
- if err != nil {
- return fmt.Errorf("destroying instance %s during CheckDestroy: waiting for deletion %w", *instances[i].Id, err)
- }
- }
- }
- }
- return nil
-}
diff --git a/stackit/internal/services/argus/credential/const.go b/stackit/internal/services/argus/credential/const.go
deleted file mode 100644
index 292e7801..00000000
--- a/stackit/internal/services/argus/credential/const.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package argus
-
-const exampleMoveToObservability = "## Example move\n" +
- "Example to move the deprecated `stackit_argus_credential` resource to the new `stackit_observability_credential` resource:" + "\n" +
- "1. Add a new `stackit_observability_credential` resource with the same values like your previous `stackit_argus_credential` resource." + "\n" +
- "1. Add a moved block which reference the `stackit_argus_credential` and `stackit_observability_credential` resource." + "\n" +
- "1. Remove your old `stackit_argus_credential` resource and run `$ terraform apply`." + "\n" +
- "```terraform" +
- `
-resource "stackit_argus_credential" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
-
-moved {
- from = stackit_argus_credential.example
- to = stackit_observability_credential.example
-}
-
-resource "stackit_observability_credential" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-}
-` + "```" + "\n"
diff --git a/stackit/internal/services/argus/credential/resource.go b/stackit/internal/services/argus/credential/resource.go
deleted file mode 100644
index ff1376df..00000000
--- a/stackit/internal/services/argus/credential/resource.go
+++ /dev/null
@@ -1,264 +0,0 @@
-package argus
-
-import (
- "context"
- "fmt"
- "net/http"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
- "github.com/hashicorp/terraform-plugin-framework/resource"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
- "github.com/hashicorp/terraform-plugin-framework/schema/validator"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/hashicorp/terraform-plugin-log/tflog"
- "github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
-)
-
-// Ensure the implementation satisfies the expected interfaces.
-var (
- _ resource.Resource = &credentialResource{}
- _ resource.ResourceWithConfigure = &credentialResource{}
-)
-
-type Model struct {
- Id types.String `tfsdk:"id"`
- ProjectId types.String `tfsdk:"project_id"`
- InstanceId types.String `tfsdk:"instance_id"`
- Username types.String `tfsdk:"username"`
- Password types.String `tfsdk:"password"`
-}
-
-// NewCredentialResource is a helper function to simplify the provider implementation.
-func NewCredentialResource() resource.Resource {
- return &credentialResource{}
-}
-
-// credentialResource is the resource implementation.
-type credentialResource struct {
- client *argus.APIClient
-}
-
-// Metadata returns the resource type name.
-func (r *credentialResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
- resp.TypeName = req.ProviderTypeName + "_argus_credential"
-}
-
-// Configure adds the provider configured client to the resource.
-func (r *credentialResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
- // Prevent panic if the provider has not been configured.
- if req.ProviderData == nil {
- return
- }
-
- providerData, ok := req.ProviderData.(core.ProviderData)
- if !ok {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
- return
- }
-
- var apiClient *argus.APIClient
- var err error
- if providerData.ArgusCustomEndpoint != "" {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithEndpoint(providerData.ArgusCustomEndpoint),
- )
- } else {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithRegion(providerData.GetRegion()),
- )
- }
-
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
- return
- }
-
- r.client = apiClient
- tflog.Info(ctx, "Argus credential client configured")
-}
-
-var (
- descriptions = map[string]string{
- "main": "Argus credential resource schema. Must have a `region` specified in the provider configuration.",
- "deprecation_message": "The `stackit_argus_credential` resource has been deprecated and will be removed after February 26th 2025. " +
- "Please use `stackit_observability_credential` instead, which offers the exact same functionality.",
- }
- Schema = schema.Schema{
- Description: fmt.Sprintf("%s\n%s", descriptions["main"], descriptions["deprecation_message"]),
- MarkdownDescription: fmt.Sprintf("%s\n\n!> %s\n\n%s", descriptions["main"], descriptions["deprecation_message"], exampleMoveToObservability),
-
- DeprecationMessage: descriptions["deprecation_message"],
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`,`username`\".",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "project_id": schema.StringAttribute{
- Description: "STACKIT project ID to which the credential is associated.",
- Required: true,
- Validators: []validator.String{
- validate.UUID(),
- },
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
- },
- },
- "instance_id": schema.StringAttribute{
- Description: "The Argus Instance ID the credential belongs to.",
- Required: true,
- Validators: []validator.String{
- validate.UUID(),
- },
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
- },
- },
- "username": schema.StringAttribute{
- Description: "Credential username",
- Computed: true,
- Validators: []validator.String{
- stringvalidator.LengthAtLeast(1),
- },
- },
- "password": schema.StringAttribute{
- Description: "Credential password",
- Computed: true,
- Sensitive: true,
- Validators: []validator.String{
- stringvalidator.LengthAtLeast(1),
- },
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- },
- }
-)
-
-func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
- resp.Schema = Schema
-}
-
-// Create creates the resource and sets the initial Terraform state.
-func (r *credentialResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
- var model Model
- diags := req.Plan.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
-
- got, err := r.client.CreateCredentials(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", fmt.Sprintf("Calling API: %v", err))
- return
- }
- err = mapFields(got.Credentials, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", fmt.Sprintf("Processing API payload: %v", err))
- return
- }
- diags = resp.State.Set(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- tflog.Info(ctx, "Argus credential created")
-}
-
-func mapFields(r *argus.Credentials, model *Model) error {
- if r == nil {
- return fmt.Errorf("response input is nil")
- }
- if model == nil {
- return fmt.Errorf("model input is nil")
- }
- var userName string
- if model.Username.ValueString() != "" {
- userName = model.Username.ValueString()
- } else if r.Username != nil {
- userName = *r.Username
- } else {
- return fmt.Errorf("username id not present")
- }
- idParts := []string{
- model.ProjectId.ValueString(),
- model.InstanceId.ValueString(),
- userName,
- }
- model.Id = types.StringValue(
- strings.Join(idParts, core.Separator),
- )
- model.Username = types.StringPointerValue(r.Username)
- model.Password = types.StringPointerValue(r.Password)
- return nil
-}
-
-// Read refreshes the Terraform state with the latest data.
-func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
- var model Model
- diags := req.State.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- userName := model.Username.ValueString()
- _, err := r.client.GetCredentials(ctx, instanceId, projectId, userName).Execute()
- if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
- return
- }
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- tflog.Info(ctx, "Argus credential read")
-}
-
-func (r *credentialResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
- // Update shouldn't be called
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating credential", "Credential can't be updated")
-}
-
-// Delete deletes the resource and removes the Terraform state on success.
-func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
- // Retrieve values from state
- var model Model
- diags := req.State.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- userName := model.Username.ValueString()
- _, err := r.client.DeleteCredentials(ctx, instanceId, projectId, userName).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err))
- return
- }
- tflog.Info(ctx, "Argus credential deleted")
-}
diff --git a/stackit/internal/services/argus/credential/resource_test.go b/stackit/internal/services/argus/credential/resource_test.go
deleted file mode 100644
index 8c38ab29..00000000
--- a/stackit/internal/services/argus/credential/resource_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package argus
-
-import (
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/stackitcloud/stackit-sdk-go/core/utils"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
-)
-
-func TestMapFields(t *testing.T) {
- tests := []struct {
- description string
- input *argus.Credentials
- expected Model
- isValid bool
- }{
- {
- "ok",
- &argus.Credentials{
- Username: utils.Ptr("username"),
- Password: utils.Ptr("password"),
- },
- Model{
- Id: types.StringValue("pid,iid,username"),
- ProjectId: types.StringValue("pid"),
- InstanceId: types.StringValue("iid"),
- Username: types.StringValue("username"),
- Password: types.StringValue("password"),
- },
- true,
- },
- {
- "response_nil_fail",
- nil,
- Model{},
- false,
- },
- {
- "response_fields_nil_fail",
- &argus.Credentials{
- Password: nil,
- Username: nil,
- },
- Model{},
- false,
- },
- {
- "no_resource_id",
- &argus.Credentials{},
- Model{},
- false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- state := &Model{
- ProjectId: tt.expected.ProjectId,
- InstanceId: tt.expected.InstanceId,
- }
- err := mapFields(tt.input, state)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
- if tt.isValid {
- diff := cmp.Diff(state, &tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
diff --git a/stackit/internal/services/argus/instance/const.go b/stackit/internal/services/argus/instance/const.go
deleted file mode 100644
index 806aaba0..00000000
--- a/stackit/internal/services/argus/instance/const.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package argus
-
-const exampleMoveToObservability = "## Example move\n" +
- "Example to move the deprecated `stackit_argus_instance` resource to the new`stackit_observability_instance` resource:" + "\n" +
- "1. Add a new `stackit_observability_instance` resource with the same values like your previous `stackit_argus_instance` resource." + "\n" +
- "1. Add a moved block which reference the `stackit_argus_instance` and `stackit_observability_instance` resource." + "\n" +
- "1. Remove your old `stackit_argus_instance` resource and run `$ terraform apply`." + "\n" +
- "```terraform" +
- `
-resource "stackit_argus_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-instance"
- plan_name = "Monitoring-Medium-EU01"
- acl = ["1.1.1.1/32", "2.2.2.2/32"]
- metrics_retention_days = 7
- metrics_retention_days_5m_downsampling = 30
- metrics_retention_days_1h_downsampling = 365
-}
-
-moved {
- from = stackit_argus_instance.example
- to = stackit_observability_instance.example
-}
-
-resource "stackit_observability_instance" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-instance"
- plan_name = "Monitoring-Medium-EU01"
- acl = ["1.1.1.1/32", "2.2.2.2/32"]
- metrics_retention_days = 7
- metrics_retention_days_5m_downsampling = 30
- metrics_retention_days_1h_downsampling = 365
-}
-` + "```" + "\n"
diff --git a/stackit/internal/services/argus/instance/datasource.go b/stackit/internal/services/argus/instance/datasource.go
deleted file mode 100644
index 57b2aa7a..00000000
--- a/stackit/internal/services/argus/instance/datasource.go
+++ /dev/null
@@ -1,442 +0,0 @@
-package argus
-
-import (
- "context"
- "fmt"
- "net/http"
-
- "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
- "github.com/hashicorp/terraform-plugin-framework/datasource"
- "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
- "github.com/hashicorp/terraform-plugin-framework/schema/validator"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/hashicorp/terraform-plugin-log/tflog"
- "github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
- "github.com/stackitcloud/stackit-sdk-go/services/argus/wait"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
-)
-
-// Ensure the implementation satisfies the expected interfaces.
-var (
- _ datasource.DataSource = &instanceDataSource{}
-)
-
-// NewInstanceDataSource is a helper function to simplify the provider implementation.
-func NewInstanceDataSource() datasource.DataSource {
- return &instanceDataSource{}
-}
-
-// instanceDataSource is the data source implementation.
-type instanceDataSource struct {
- client *argus.APIClient
-}
-
-// Metadata returns the data source type name.
-func (d *instanceDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
- resp.TypeName = req.ProviderTypeName + "_argus_instance"
-}
-
-func (d *instanceDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
- // Prevent panic if the provider has not been configured.
- if req.ProviderData == nil {
- return
- }
-
- var apiClient *argus.APIClient
- var err error
-
- providerData, ok := req.ProviderData.(core.ProviderData)
- if !ok {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
- return
- }
-
- if providerData.ArgusCustomEndpoint != "" {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithEndpoint(providerData.ArgusCustomEndpoint),
- )
- } else {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithRegion(providerData.GetRegion()),
- )
- }
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the data source configuration", err))
- return
- }
- d.client = apiClient
- tflog.Info(ctx, "Argus instance client configured")
-}
-
-// Schema defines the schema for the data source.
-func (d *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
- descriptions := map[string]string{
- "main": "Argus instance data source schema. Must have a `region` specified in the provider configuration.",
- "deprecation_message": "The `stackit_argus_instance` data source has been deprecated and will be removed after February 26th 2025. " +
- "Please use `stackit_observability_instance` instead, which offers the exact same functionality.",
- }
- resp.Schema = schema.Schema{
- Description: fmt.Sprintf("%s\n%s", descriptions["main"], descriptions["deprecation_message"]),
- MarkdownDescription: fmt.Sprintf("%s\n\n!> %s", descriptions["main"], descriptions["deprecation_message"]),
- DeprecationMessage: descriptions["deprecation_message"],
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Terraform's internal data source ID. It is structured as \"`project_id`,`instance_id`\".",
- Computed: true,
- },
- "project_id": schema.StringAttribute{
- Description: "STACKIT project ID to which the instance is associated.",
- Required: true,
- Validators: []validator.String{
- validate.UUID(),
- validate.NoSeparator(),
- },
- },
- "instance_id": schema.StringAttribute{
- Description: "The Argus instance ID.",
- Required: true,
- Validators: []validator.String{
- validate.UUID(),
- validate.NoSeparator(),
- },
- },
- "name": schema.StringAttribute{
- Description: "The name of the Argus instance.",
- Computed: true,
- Validators: []validator.String{
- stringvalidator.LengthAtLeast(1),
- stringvalidator.LengthAtMost(300),
- },
- },
- "plan_name": schema.StringAttribute{
- Description: "Specifies the Argus plan. E.g. `Monitoring-Medium-EU01`.",
- Computed: true,
- Validators: []validator.String{
- stringvalidator.LengthAtLeast(1),
- stringvalidator.LengthAtMost(200),
- },
- },
- "plan_id": schema.StringAttribute{
- Description: "The Argus plan ID.",
- Computed: true,
- Validators: []validator.String{
- validate.UUID(),
- },
- },
- "parameters": schema.MapAttribute{
- Description: "Additional parameters.",
- Computed: true,
- ElementType: types.StringType,
- },
- "dashboard_url": schema.StringAttribute{
- Description: "Specifies Argus instance dashboard URL.",
- Computed: true,
- },
- "is_updatable": schema.BoolAttribute{
- Description: "Specifies if the instance can be updated.",
- Computed: true,
- },
- "grafana_public_read_access": schema.BoolAttribute{
- Description: "If true, anyone can access Grafana dashboards without logging in.",
- Computed: true,
- },
- "grafana_url": schema.StringAttribute{
- Description: "Specifies Grafana URL.",
- Computed: true,
- },
- "grafana_initial_admin_user": schema.StringAttribute{
- Description: "Specifies an initial Grafana admin username.",
- Computed: true,
- },
- "grafana_initial_admin_password": schema.StringAttribute{
- Description: "Specifies an initial Grafana admin password.",
- Computed: true,
- Sensitive: true,
- },
- "metrics_retention_days": schema.Int64Attribute{
- Description: "Specifies for how many days the raw metrics are kept.",
- Computed: true,
- },
- "metrics_retention_days_5m_downsampling": schema.Int64Attribute{
- Description: "Specifies for how many days the 5m downsampled metrics are kept. must be less than the value of the general retention. Default is set to `0` (disabled).",
- Computed: true,
- },
- "metrics_retention_days_1h_downsampling": schema.Int64Attribute{
- Description: "Specifies for how many days the 1h downsampled metrics are kept. must be less than the value of the 5m downsampling retention. Default is set to `0` (disabled).",
- Computed: true,
- },
- "metrics_url": schema.StringAttribute{
- Description: "Specifies metrics URL.",
- Computed: true,
- },
- "metrics_push_url": schema.StringAttribute{
- Description: "Specifies URL for pushing metrics.",
- Computed: true,
- },
- "targets_url": schema.StringAttribute{
- Description: "Specifies Targets URL.",
- Computed: true,
- },
- "alerting_url": schema.StringAttribute{
- Description: "Specifies Alerting URL.",
- Computed: true,
- },
- "logs_url": schema.StringAttribute{
- Description: "Specifies Logs URL.",
- Computed: true,
- },
- "logs_push_url": schema.StringAttribute{
- Description: "Specifies URL for pushing logs.",
- Computed: true,
- },
- "jaeger_traces_url": schema.StringAttribute{
- Computed: true,
- },
- "jaeger_ui_url": schema.StringAttribute{
- Computed: true,
- },
- "otlp_traces_url": schema.StringAttribute{
- Computed: true,
- },
- "zipkin_spans_url": schema.StringAttribute{
- Computed: true,
- },
- "acl": schema.SetAttribute{
- Description: "The access control list for this instance. Each entry is an IP address range that is permitted to access, in CIDR notation.",
- ElementType: types.StringType,
- Computed: true,
- },
- "alert_config": schema.SingleNestedAttribute{
- Description: "Alert configuration for the instance.",
- Computed: true,
- Attributes: map[string]schema.Attribute{
- "receivers": schema.ListNestedAttribute{
- Description: "List of alert receivers.",
- Computed: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "name": schema.StringAttribute{
- Description: "Name of the receiver.",
- Computed: true,
- },
- "email_configs": schema.ListNestedAttribute{
- Description: "List of email configurations.",
- Computed: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "auth_identity": schema.StringAttribute{
- Description: "SMTP authentication information. Must be a valid email address",
- Computed: true,
- },
- "auth_password": schema.StringAttribute{
- Description: "SMTP authentication password.",
- Computed: true,
- },
- "auth_username": schema.StringAttribute{
- Description: "SMTP authentication username.",
- Computed: true,
- },
- "from": schema.StringAttribute{
- Description: "The sender email address. Must be a valid email address",
- Computed: true,
- },
- "smart_host": schema.StringAttribute{
- Description: "The SMTP host through which emails are sent.",
- Computed: true,
- },
- "to": schema.StringAttribute{
- Description: "The email address to send notifications to. Must be a valid email address",
- Computed: true,
- },
- },
- },
- },
- "opsgenie_configs": schema.ListNestedAttribute{
- Description: "List of OpsGenie configurations.",
- Computed: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "api_key": schema.StringAttribute{
- Description: "The API key for OpsGenie.",
- Computed: true,
- },
- "api_url": schema.StringAttribute{
- Description: "The host to send OpsGenie API requests to. Must be a valid URL",
- Computed: true,
- },
- "tags": schema.StringAttribute{
- Description: "Comma separated list of tags attached to the notifications.",
- Computed: true,
- },
- },
- },
- },
- "webhooks_configs": schema.ListNestedAttribute{
- Description: "List of Webhooks configurations.",
- Computed: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "url": schema.StringAttribute{
- Description: "The endpoint to send HTTP POST requests to. Must be a valid URL",
- Computed: true,
- },
- "ms_teams": schema.BoolAttribute{
- Description: "Microsoft Teams webhooks require special handling, set this to true if the webhook is for Microsoft Teams.",
- Computed: true,
- },
- },
- },
- },
- },
- },
- },
- "route": schema.SingleNestedAttribute{
- Description: "The route for the alert.",
- Computed: true,
- Attributes: map[string]schema.Attribute{
- "group_by": schema.ListAttribute{
- Description: "The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.",
- Computed: true,
- ElementType: types.StringType,
- },
- "group_interval": schema.StringAttribute{
- Description: "How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)",
- Computed: true,
- },
- "group_wait": schema.StringAttribute{
- Description: "How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.) .",
- Computed: true,
- },
- "match": schema.MapAttribute{
- Description: "A set of equality matchers an alert has to fulfill to match the node.",
- Computed: true,
- ElementType: types.StringType,
- },
- "match_regex": schema.MapAttribute{
- Description: "A set of regex-matchers an alert has to fulfill to match the node.",
- Computed: true,
- ElementType: types.StringType,
- },
- "receiver": schema.StringAttribute{
- Description: "The name of the receiver to route the alerts to.",
- Computed: true,
- },
- "repeat_interval": schema.StringAttribute{
- Description: "How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more).",
- Computed: true,
- },
- "routes": getDatasourceRouteNestedObject(),
- },
- },
- "global": schema.SingleNestedAttribute{
- Description: "Global configuration for the alerts.",
- Computed: true,
- Attributes: map[string]schema.Attribute{
- "opsgenie_api_key": schema.StringAttribute{
- Description: "The API key for OpsGenie.",
- Computed: true,
- Sensitive: true,
- },
- "opsgenie_api_url": schema.StringAttribute{
- Description: "The host to send OpsGenie API requests to. Must be a valid URL",
- Computed: true,
- },
- "resolve_timeout": schema.StringAttribute{
- Description: "The default value used by alertmanager if the alert does not include EndsAt. After this time passes, it can declare the alert as resolved if it has not been updated. This has no impact on alerts from Prometheus, as they always include EndsAt.",
- Computed: true,
- },
- "smtp_auth_identity": schema.StringAttribute{
- Description: "SMTP authentication information. Must be a valid email address",
- Computed: true,
- },
- "smtp_auth_password": schema.StringAttribute{
- Description: "SMTP Auth using LOGIN and PLAIN.",
- Computed: true,
- Sensitive: true,
- },
- "smtp_auth_username": schema.StringAttribute{
- Description: "SMTP Auth using CRAM-MD5, LOGIN and PLAIN. If empty, Alertmanager doesn't authenticate to the SMTP server.",
- Computed: true,
- },
- "smtp_from": schema.StringAttribute{
- Description: "The default SMTP From header field. Must be a valid email address",
- Computed: true,
- },
- "smtp_smart_host": schema.StringAttribute{
- Description: "The default SMTP smarthost used for sending emails, including port number. Port number usually is 25, or 587 for SMTP over TLS (sometimes referred to as STARTTLS).",
- Computed: true,
- },
- },
- },
- },
- },
- },
- }
-}
-
-// Read refreshes the Terraform state with the latest data.
-func (d *instanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
- var model Model
- diags := req.Config.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- instanceResp, err := d.client.GetInstance(ctx, instanceId, projectId).Execute()
- if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
- return
- }
- if instanceResp != nil && instanceResp.Status != nil && *instanceResp.Status == wait.DeleteSuccess {
- resp.State.RemoveResource(ctx)
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", "Instance was deleted successfully")
- return
- }
-
- aclListResp, err := d.client.ListACL(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to list ACL data: %v", err))
- return
- }
-
- alertConfigResp, err := d.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get alert config: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapFields(ctx, instanceResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
- return
- }
- err = mapACLField(aclListResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API response for the ACL: %v", err))
- return
- }
- err = mapAlertConfigField(ctx, alertConfigResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
- return
- }
-
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- tflog.Info(ctx, "Argus instance read")
-}
diff --git a/stackit/internal/services/argus/instance/resource.go b/stackit/internal/services/argus/instance/resource.go
deleted file mode 100644
index ca43be35..00000000
--- a/stackit/internal/services/argus/instance/resource.go
+++ /dev/null
@@ -1,2221 +0,0 @@
-package argus
-
-import (
- "context"
- "fmt"
- "net/http"
- "strconv"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
- "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
- "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
- "github.com/hashicorp/terraform-plugin-framework/attr"
- "github.com/hashicorp/terraform-plugin-framework/diag"
- "github.com/hashicorp/terraform-plugin-framework/path"
- "github.com/hashicorp/terraform-plugin-framework/resource"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
- "github.com/hashicorp/terraform-plugin-framework/schema/validator"
- "github.com/hashicorp/terraform-plugin-framework/tfsdk"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
- "github.com/hashicorp/terraform-plugin-log/tflog"
- "github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
- "github.com/stackitcloud/stackit-sdk-go/core/utils"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
- "github.com/stackitcloud/stackit-sdk-go/services/argus/wait"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
-)
-
-// Currently, due to incorrect types in the API, the maximum recursion level for child routes is set to 1.
-// Once this is fixed, the value should be set to 10.
-const childRouteMaxRecursionLevel = 1
-
-// Ensure the implementation satisfies the expected interfaces.
-var (
- _ resource.Resource = &instanceResource{}
- _ resource.ResourceWithConfigure = &instanceResource{}
- _ resource.ResourceWithImportState = &instanceResource{}
-)
-
-type Model struct {
- Id types.String `tfsdk:"id"` // needed by TF
- ProjectId types.String `tfsdk:"project_id"`
- InstanceId types.String `tfsdk:"instance_id"`
- Name types.String `tfsdk:"name"`
- PlanName types.String `tfsdk:"plan_name"`
- PlanId types.String `tfsdk:"plan_id"`
- Parameters types.Map `tfsdk:"parameters"`
- DashboardURL types.String `tfsdk:"dashboard_url"`
- IsUpdatable types.Bool `tfsdk:"is_updatable"`
- GrafanaURL types.String `tfsdk:"grafana_url"`
- GrafanaPublicReadAccess types.Bool `tfsdk:"grafana_public_read_access"`
- GrafanaInitialAdminPassword types.String `tfsdk:"grafana_initial_admin_password"`
- GrafanaInitialAdminUser types.String `tfsdk:"grafana_initial_admin_user"`
- MetricsRetentionDays types.Int64 `tfsdk:"metrics_retention_days"`
- MetricsRetentionDays5mDownsampling types.Int64 `tfsdk:"metrics_retention_days_5m_downsampling"`
- MetricsRetentionDays1hDownsampling types.Int64 `tfsdk:"metrics_retention_days_1h_downsampling"`
- MetricsURL types.String `tfsdk:"metrics_url"`
- MetricsPushURL types.String `tfsdk:"metrics_push_url"`
- TargetsURL types.String `tfsdk:"targets_url"`
- AlertingURL types.String `tfsdk:"alerting_url"`
- LogsURL types.String `tfsdk:"logs_url"`
- LogsPushURL types.String `tfsdk:"logs_push_url"`
- JaegerTracesURL types.String `tfsdk:"jaeger_traces_url"`
- JaegerUIURL types.String `tfsdk:"jaeger_ui_url"`
- OtlpTracesURL types.String `tfsdk:"otlp_traces_url"`
- ZipkinSpansURL types.String `tfsdk:"zipkin_spans_url"`
- ACL types.Set `tfsdk:"acl"`
- AlertConfig types.Object `tfsdk:"alert_config"`
-}
-
-// Struct corresponding to Model.AlertConfig
-type alertConfigModel struct {
- GlobalConfiguration types.Object `tfsdk:"global"`
- Receivers types.List `tfsdk:"receivers"`
- Route types.Object `tfsdk:"route"`
-}
-
-var alertConfigTypes = map[string]attr.Type{
- "receivers": types.ListType{ElemType: types.ObjectType{AttrTypes: receiversTypes}},
- "route": types.ObjectType{AttrTypes: routeTypes},
- "global": types.ObjectType{AttrTypes: globalConfigurationTypes},
-}
-
-// Struct corresponding to Model.AlertConfig.global
-type globalConfigurationModel struct {
- OpsgenieApiKey types.String `tfsdk:"opsgenie_api_key"`
- OpsgenieApiUrl types.String `tfsdk:"opsgenie_api_url"`
- ResolveTimeout types.String `tfsdk:"resolve_timeout"`
- SmtpAuthIdentity types.String `tfsdk:"smtp_auth_identity"`
- SmtpAuthPassword types.String `tfsdk:"smtp_auth_password"`
- SmtpAuthUsername types.String `tfsdk:"smtp_auth_username"`
- SmtpFrom types.String `tfsdk:"smtp_from"`
- SmtpSmartHost types.String `tfsdk:"smtp_smart_host"`
-}
-
-var globalConfigurationTypes = map[string]attr.Type{
- "opsgenie_api_key": types.StringType,
- "opsgenie_api_url": types.StringType,
- "resolve_timeout": types.StringType,
- "smtp_auth_identity": types.StringType,
- "smtp_auth_password": types.StringType,
- "smtp_auth_username": types.StringType,
- "smtp_from": types.StringType,
- "smtp_smart_host": types.StringType,
-}
-
-// Struct corresponding to Model.AlertConfig.route
-type routeModel struct {
- GroupBy types.List `tfsdk:"group_by"`
- GroupInterval types.String `tfsdk:"group_interval"`
- GroupWait types.String `tfsdk:"group_wait"`
- Match types.Map `tfsdk:"match"`
- MatchRegex types.Map `tfsdk:"match_regex"`
- Receiver types.String `tfsdk:"receiver"`
- RepeatInterval types.String `tfsdk:"repeat_interval"`
- Routes types.List `tfsdk:"routes"`
-}
-
-// Struct corresponding to Model.AlertConfig.route but without the recursive routes field
-// This is used to map the last level of recursion of the routes field
-type routeModelNoRoutes struct {
- GroupBy types.List `tfsdk:"group_by"`
- GroupInterval types.String `tfsdk:"group_interval"`
- GroupWait types.String `tfsdk:"group_wait"`
- Match types.Map `tfsdk:"match"`
- MatchRegex types.Map `tfsdk:"match_regex"`
- Receiver types.String `tfsdk:"receiver"`
- RepeatInterval types.String `tfsdk:"repeat_interval"`
-}
-
-var routeTypes = map[string]attr.Type{
- "group_by": types.ListType{ElemType: types.StringType},
- "group_interval": types.StringType,
- "group_wait": types.StringType,
- "match": types.MapType{ElemType: types.StringType},
- "match_regex": types.MapType{ElemType: types.StringType},
- "receiver": types.StringType,
- "repeat_interval": types.StringType,
- "routes": types.ListType{ElemType: getRouteListType()},
-}
-
-// Struct corresponding to Model.AlertConfig.receivers
-type receiversModel struct {
- Name types.String `tfsdk:"name"`
- EmailConfigs types.List `tfsdk:"email_configs"`
- OpsGenieConfigs types.List `tfsdk:"opsgenie_configs"`
- WebHooksConfigs types.List `tfsdk:"webhooks_configs"`
-}
-
-var receiversTypes = map[string]attr.Type{
- "name": types.StringType,
- "email_configs": types.ListType{ElemType: types.ObjectType{AttrTypes: emailConfigsTypes}},
- "opsgenie_configs": types.ListType{ElemType: types.ObjectType{AttrTypes: opsgenieConfigsTypes}},
- "webhooks_configs": types.ListType{ElemType: types.ObjectType{AttrTypes: webHooksConfigsTypes}},
-}
-
-// Struct corresponding to Model.AlertConfig.receivers.emailConfigs
-type emailConfigsModel struct {
- AuthIdentity types.String `tfsdk:"auth_identity"`
- AuthPassword types.String `tfsdk:"auth_password"`
- AuthUsername types.String `tfsdk:"auth_username"`
- From types.String `tfsdk:"from"`
- Smarthost types.String `tfsdk:"smart_host"`
- To types.String `tfsdk:"to"`
-}
-
-var emailConfigsTypes = map[string]attr.Type{
- "auth_identity": types.StringType,
- "auth_password": types.StringType,
- "auth_username": types.StringType,
- "from": types.StringType,
- "smart_host": types.StringType,
- "to": types.StringType,
-}
-
-// Struct corresponding to Model.AlertConfig.receivers.opsGenieConfigs
-type opsgenieConfigsModel struct {
- ApiKey types.String `tfsdk:"api_key"`
- ApiUrl types.String `tfsdk:"api_url"`
- Tags types.String `tfsdk:"tags"`
-}
-
-var opsgenieConfigsTypes = map[string]attr.Type{
- "api_key": types.StringType,
- "api_url": types.StringType,
- "tags": types.StringType,
-}
-
-// Struct corresponding to Model.AlertConfig.receivers.webHooksConfigs
-type webHooksConfigsModel struct {
- Url types.String `tfsdk:"url"`
- MsTeams types.Bool `tfsdk:"ms_teams"`
-}
-
-var webHooksConfigsTypes = map[string]attr.Type{
- "url": types.StringType,
- "ms_teams": types.BoolType,
-}
-
-var routeDescriptions = map[string]string{
- "group_by": "The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.",
- "group_interval": "How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)",
- "group_wait": "How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.)",
- "match": "A set of equality matchers an alert has to fulfill to match the node.",
- "match_regex": "A set of regex-matchers an alert has to fulfill to match the node.",
- "receiver": "The name of the receiver to route the alerts to.",
- "repeat_interval": "How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more).",
- "routes": "List of child routes.",
-}
-
-// getRouteListType is a helper function to return the route list attribute type.
-func getRouteListType() types.ObjectType {
- return getRouteListTypeAux(1, childRouteMaxRecursionLevel)
-}
-
-// getRouteListTypeAux returns the type of the route list attribute with the given level of child routes recursion.
-// The level is used to determine the current depth of the nested object.
-// The limit is used to determine the maximum depth of the nested object.
-// The level should be lower or equal to the limit, if higher, the function will produce a stack overflow.
-func getRouteListTypeAux(level, limit int) types.ObjectType {
- attributeTypes := map[string]attr.Type{
- "group_by": types.ListType{ElemType: types.StringType},
- "group_interval": types.StringType,
- "group_wait": types.StringType,
- "match": types.MapType{ElemType: types.StringType},
- "match_regex": types.MapType{ElemType: types.StringType},
- "receiver": types.StringType,
- "repeat_interval": types.StringType,
- }
-
- if level != limit {
- attributeTypes["routes"] = types.ListType{ElemType: getRouteListTypeAux(level+1, limit)}
- }
-
- return types.ObjectType{AttrTypes: attributeTypes}
-}
-
-func getRouteNestedObject() schema.ListNestedAttribute {
- return getRouteNestedObjectAux(false, 1, childRouteMaxRecursionLevel)
-}
-
-func getDatasourceRouteNestedObject() schema.ListNestedAttribute {
- return getRouteNestedObjectAux(true, 1, childRouteMaxRecursionLevel)
-}
-
-// getRouteNestedObjectAux returns the nested object for the route attribute with the given level of child routes recursion.
-// The isDatasource is used to determine if the route is used in a datasource schema or not. If it is a datasource, all fields are computed.
-// The level is used to determine the current depth of the nested object.
-// The limit is used to determine the maximum depth of the nested object.
-// The level should be lower or equal to the limit, if higher, the function will produce a stack overflow.
-func getRouteNestedObjectAux(isDatasource bool, level, limit int) schema.ListNestedAttribute {
- attributesMap := map[string]schema.Attribute{
- "group_by": schema.ListAttribute{
- Description: routeDescriptions["group_by"],
- Optional: !isDatasource,
- Computed: isDatasource,
- ElementType: types.StringType,
- },
- "group_interval": schema.StringAttribute{
- Description: routeDescriptions["group_interval"],
- Optional: !isDatasource,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "group_wait": schema.StringAttribute{
- Description: routeDescriptions["group_wait"],
- Optional: !isDatasource,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "match": schema.MapAttribute{
- Description: routeDescriptions["match"],
- Optional: !isDatasource,
- Computed: isDatasource,
- ElementType: types.StringType,
- },
- "match_regex": schema.MapAttribute{
- Description: routeDescriptions["match_regex"],
- Optional: !isDatasource,
- Computed: isDatasource,
- ElementType: types.StringType,
- },
- "receiver": schema.StringAttribute{
- Description: routeDescriptions["receiver"],
- Required: !isDatasource,
- Computed: isDatasource,
- },
- "repeat_interval": schema.StringAttribute{
- Description: routeDescriptions["repeat_interval"],
- Optional: !isDatasource,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- }
-
- if level != limit {
- attributesMap["routes"] = getRouteNestedObjectAux(isDatasource, level+1, limit)
- }
-
- return schema.ListNestedAttribute{
- Description: routeDescriptions["routes"],
- Optional: !isDatasource,
- Computed: isDatasource,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: attributesMap,
- },
- }
-}
-
-// NewInstanceResource is a helper function to simplify the provider implementation.
-func NewInstanceResource() resource.Resource {
- return &instanceResource{}
-}
-
-// instanceResource is the resource implementation.
-type instanceResource struct {
- client *argus.APIClient
-}
-
-// Metadata returns the resource type name.
-func (r *instanceResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
- resp.TypeName = req.ProviderTypeName + "_argus_instance"
-}
-
-// Configure adds the provider configured client to the resource.
-func (r *instanceResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
- // Prevent panic if the provider has not been configured.
- if req.ProviderData == nil {
- return
- }
-
- providerData, ok := req.ProviderData.(core.ProviderData)
- if !ok {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
- return
- }
-
- var apiClient *argus.APIClient
- var err error
- if providerData.ArgusCustomEndpoint != "" {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithEndpoint(providerData.ArgusCustomEndpoint),
- )
- } else {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithRegion(providerData.GetRegion()),
- )
- }
-
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
- return
- }
-
- r.client = apiClient
- tflog.Info(ctx, "Argus instance client configured")
-}
-
-var (
- descriptions = map[string]string{
- "main": "Argus instance resource schema. Must have a `region` specified in the provider configuration.",
- "deprecation_message": "The `stackit_argus_instance` resource has been deprecated and will be removed after February 26th 2025. " +
- "Please use `stackit_observability_instance` instead, which offers the exact same functionality.",
- }
- Schema = schema.Schema{
- Description: fmt.Sprintf("%s\n%s", descriptions["main"], descriptions["deprecation_message"]),
- MarkdownDescription: fmt.Sprintf("%s\n\n!> %s\n\n%s", descriptions["main"], descriptions["deprecation_message"], exampleMoveToObservability),
- DeprecationMessage: descriptions["deprecation_message"],
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`\".",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "project_id": schema.StringAttribute{
- Description: "STACKIT project ID to which the instance is associated.",
- Required: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
- },
- Validators: []validator.String{
- validate.UUID(),
- validate.NoSeparator(),
- },
- },
- "instance_id": schema.StringAttribute{
- Description: "The Argus instance ID.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- Validators: []validator.String{
- validate.UUID(),
- validate.NoSeparator(),
- },
- },
- "name": schema.StringAttribute{
- Description: "The name of the Argus instance.",
- Required: true,
- Validators: []validator.String{
- stringvalidator.LengthAtLeast(1),
- stringvalidator.LengthAtMost(200),
- },
- },
- "plan_name": schema.StringAttribute{
- Description: "Specifies the Argus plan. E.g. `Monitoring-Medium-EU01`.",
- Required: true,
- Validators: []validator.String{
- stringvalidator.LengthAtLeast(1),
- stringvalidator.LengthAtMost(200),
- },
- },
- "plan_id": schema.StringAttribute{
- Description: "The Argus plan ID.",
- Computed: true,
- Validators: []validator.String{
- validate.UUID(),
- },
- },
- "parameters": schema.MapAttribute{
- Description: "Additional parameters.",
- Optional: true,
- Computed: true,
- ElementType: types.StringType,
- PlanModifiers: []planmodifier.Map{
- mapplanmodifier.UseStateForUnknown(),
- },
- },
- "dashboard_url": schema.StringAttribute{
- Description: "Specifies Argus instance dashboard URL.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "is_updatable": schema.BoolAttribute{
- Description: "Specifies if the instance can be updated.",
- Computed: true,
- PlanModifiers: []planmodifier.Bool{
- boolplanmodifier.UseStateForUnknown(),
- },
- },
- "grafana_public_read_access": schema.BoolAttribute{
- Description: "If true, anyone can access Grafana dashboards without logging in.",
- Computed: true,
- PlanModifiers: []planmodifier.Bool{
- boolplanmodifier.UseStateForUnknown(),
- },
- },
- "grafana_url": schema.StringAttribute{
- Description: "Specifies Grafana URL.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "grafana_initial_admin_user": schema.StringAttribute{
- Description: "Specifies an initial Grafana admin username.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "grafana_initial_admin_password": schema.StringAttribute{
- Description: "Specifies an initial Grafana admin password.",
- Computed: true,
- Sensitive: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "metrics_retention_days": schema.Int64Attribute{
- Description: "Specifies for how many days the raw metrics are kept.",
- Optional: true,
- Computed: true,
- },
- "metrics_retention_days_5m_downsampling": schema.Int64Attribute{
- Description: "Specifies for how many days the 5m downsampled metrics are kept. must be less than the value of the general retention. Default is set to `0` (disabled).",
- Optional: true,
- Computed: true,
- },
- "metrics_retention_days_1h_downsampling": schema.Int64Attribute{
- Description: "Specifies for how many days the 1h downsampled metrics are kept. must be less than the value of the 5m downsampling retention. Default is set to `0` (disabled).",
- Optional: true,
- Computed: true,
- },
- "metrics_url": schema.StringAttribute{
- Description: "Specifies metrics URL.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "metrics_push_url": schema.StringAttribute{
- Description: "Specifies URL for pushing metrics.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "targets_url": schema.StringAttribute{
- Description: "Specifies Targets URL.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "alerting_url": schema.StringAttribute{
- Description: "Specifies Alerting URL.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "logs_url": schema.StringAttribute{
- Description: "Specifies Logs URL.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "logs_push_url": schema.StringAttribute{
- Description: "Specifies URL for pushing logs.",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "jaeger_traces_url": schema.StringAttribute{
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "jaeger_ui_url": schema.StringAttribute{
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "otlp_traces_url": schema.StringAttribute{
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "zipkin_spans_url": schema.StringAttribute{
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "acl": schema.SetAttribute{
- Description: "The access control list for this instance. Each entry is an IP address range that is permitted to access, in CIDR notation.",
- ElementType: types.StringType,
- Optional: true,
- Validators: []validator.Set{
- setvalidator.ValueStringsAre(
- validate.CIDR(),
- ),
- },
- },
- "alert_config": schema.SingleNestedAttribute{
- Description: "Alert configuration for the instance.",
- Optional: true,
- Attributes: map[string]schema.Attribute{
- "receivers": schema.ListNestedAttribute{
- Description: "List of alert receivers.",
- Required: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "name": schema.StringAttribute{
- Description: "Name of the receiver.",
- Required: true,
- },
- "email_configs": schema.ListNestedAttribute{
- Description: "List of email configurations.",
- Optional: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "auth_identity": schema.StringAttribute{
- Description: "SMTP authentication information. Must be a valid email address",
- Optional: true,
- },
- "auth_password": schema.StringAttribute{
- Description: "SMTP authentication password.",
- Optional: true,
- },
- "auth_username": schema.StringAttribute{
- Description: "SMTP authentication username.",
- Optional: true,
- },
- "from": schema.StringAttribute{
- Description: "The sender email address. Must be a valid email address",
- Optional: true,
- },
- "smart_host": schema.StringAttribute{
- Description: "The SMTP host through which emails are sent.",
- Optional: true,
- },
- "to": schema.StringAttribute{
- Description: "The email address to send notifications to. Must be a valid email address",
- Optional: true,
- },
- },
- },
- },
- "opsgenie_configs": schema.ListNestedAttribute{
- Description: "List of OpsGenie configurations.",
- Optional: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "api_key": schema.StringAttribute{
- Description: "The API key for OpsGenie.",
- Optional: true,
- },
- "api_url": schema.StringAttribute{
- Description: "The host to send OpsGenie API requests to. Must be a valid URL",
- Optional: true,
- },
- "tags": schema.StringAttribute{
- Description: "Comma separated list of tags attached to the notifications.",
- Optional: true,
- },
- },
- },
- },
- "webhooks_configs": schema.ListNestedAttribute{
- Description: "List of Webhooks configurations.",
- Optional: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "url": schema.StringAttribute{
- Description: "The endpoint to send HTTP POST requests to. Must be a valid URL",
- Optional: true,
- },
- "ms_teams": schema.BoolAttribute{
- Description: "Microsoft Teams webhooks require special handling, set this to true if the webhook is for Microsoft Teams.",
- Optional: true,
- },
- },
- },
- },
- },
- },
- },
- "route": schema.SingleNestedAttribute{
- Description: "Route configuration for the alerts.",
- Required: true,
- Attributes: map[string]schema.Attribute{
- "group_by": schema.ListAttribute{
- Description: routeDescriptions["group_by"],
- Optional: true,
- ElementType: types.StringType,
- },
- "group_interval": schema.StringAttribute{
- Description: routeDescriptions["group_interval"],
- Optional: true,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "group_wait": schema.StringAttribute{
- Description: routeDescriptions["group_wait"],
- Optional: true,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "match": schema.MapAttribute{
- Description: routeDescriptions["match"],
- Optional: true,
- ElementType: types.StringType,
- },
- "match_regex": schema.MapAttribute{
- Description: routeDescriptions["match_regex"],
- Optional: true,
- ElementType: types.StringType,
- },
- "receiver": schema.StringAttribute{
- Description: routeDescriptions["receiver"],
- Required: true,
- },
- "repeat_interval": schema.StringAttribute{
- Description: routeDescriptions["repeat_interval"],
- Optional: true,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "routes": getRouteNestedObject(),
- },
- },
- "global": schema.SingleNestedAttribute{
- Description: "Global configuration for the alerts.",
- Optional: true,
- Computed: true,
- Attributes: map[string]schema.Attribute{
- "opsgenie_api_key": schema.StringAttribute{
- Description: "The API key for OpsGenie.",
- Optional: true,
- Sensitive: true,
- },
- "opsgenie_api_url": schema.StringAttribute{
- Description: "The host to send OpsGenie API requests to. Must be a valid URL",
- Optional: true,
- },
- "resolve_timeout": schema.StringAttribute{
- Description: "The default value used by alertmanager if the alert does not include EndsAt. After this time passes, it can declare the alert as resolved if it has not been updated. This has no impact on alerts from Prometheus, as they always include EndsAt.",
- Optional: true,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "smtp_auth_identity": schema.StringAttribute{
- Description: "SMTP authentication information. Must be a valid email address",
- Optional: true,
- },
- "smtp_auth_password": schema.StringAttribute{
- Description: "SMTP Auth using LOGIN and PLAIN.",
- Optional: true,
- Sensitive: true,
- },
- "smtp_auth_username": schema.StringAttribute{
- Description: "SMTP Auth using CRAM-MD5, LOGIN and PLAIN. If empty, Alertmanager doesn't authenticate to the SMTP server.",
- Optional: true,
- },
- "smtp_from": schema.StringAttribute{
- Description: "The default SMTP From header field. Must be a valid email address",
- Optional: true,
- Computed: true,
- },
- "smtp_smart_host": schema.StringAttribute{
- Description: "The default SMTP smarthost used for sending emails, including port number in format `host:port` (eg. `smtp.example.com:587`). Port number usually is 25, or 587 for SMTP over TLS (sometimes referred to as STARTTLS).",
- Optional: true,
- },
- },
- },
- },
- },
- },
- }
-)
-
-// Schema defines the schema for the resource.
-func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
- resp.Schema = Schema
-}
-
-// Create creates the resource and sets the initial Terraform state.
-func (r *instanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
- // Retrieve values from plan
- var model Model
- diags := req.Plan.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- acl := []string{}
- if !(model.ACL.IsNull() || model.ACL.IsUnknown()) {
- diags = model.ACL.ElementsAs(ctx, &acl, false)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- metricsRetentionDays := conversion.Int64ValueToPointer(model.MetricsRetentionDays)
- metricsRetentionDays5mDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays5mDownsampling)
- metricsRetentionDays1hDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays1hDownsampling)
-
- alertConfig := alertConfigModel{}
- if !(model.AlertConfig.IsNull() || model.AlertConfig.IsUnknown()) {
- diags = model.AlertConfig.As(ctx, &alertConfig, basetypes.ObjectAsOptions{})
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- projectId := model.ProjectId.ValueString()
- ctx = tflog.SetField(ctx, "project_id", projectId)
-
- err := r.loadPlanId(ctx, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Loading service plan: %v", err))
- return
- }
- // Generate API request body from model
- createPayload, err := toCreatePayload(&model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating API payload: %v", err))
- return
- }
- createResp, err := r.client.CreateInstance(ctx, projectId).CreateInstancePayload(*createPayload).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Calling API: %v", err))
- return
- }
- instanceId := createResp.InstanceId
- ctx = tflog.SetField(ctx, "instance_id", instanceId)
- waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, *instanceId, projectId).WaitWithContext(ctx)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Instance creation waiting: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapFields(ctx, waitResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err))
- return
- }
-
- // Set state to instance populated data
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // Create ACL
- err = updateACL(ctx, projectId, *instanceId, acl, r.client)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating ACL: %v", err))
- return
- }
- aclList, err := r.client.ListACL(ctx, *instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Calling API to list ACL data: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapACLField(aclList, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the ACL: %v", err))
- return
- }
-
- // Set state to fully populated data
- diags = setACL(ctx, &resp.State, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // If any of the metrics retention days are set, set the metrics retention policy
- if metricsRetentionDays != nil || metricsRetentionDays5mDownsampling != nil || metricsRetentionDays1hDownsampling != nil {
- // Need to get the metrics retention policy because update endpoint is a PUT and we need to send all fields
- metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, *instanceId, projectId)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Getting metrics retention policy: %v", err))
- return
- }
-
- metricsRetentionPayload, err := toUpdateMetricsStorageRetentionPayload(metricsRetentionDays, metricsRetentionDays5mDownsampling, metricsRetentionDays1hDownsampling, metricsResp)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Building metrics retention policy payload: %v", err))
- return
- }
-
- _, err = r.client.UpdateMetricsStorageRetention(ctx, *instanceId, projectId).UpdateMetricsStorageRetentionPayload(*metricsRetentionPayload).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Setting metrics retention policy: %v", err))
- return
- }
- }
-
- // Get metrics retention policy after update
- metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, *instanceId, projectId)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Getting metrics retention policy: %v", err))
- return
- }
- // Map response body to schema
- err = mapMetricsRetentionField(metricsResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the metrics retention: %v", err))
- return
- }
-
- // Set state to fully populated data
- diags = setMetricsRetentions(ctx, &resp.State, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // Alert Config
- if model.AlertConfig.IsUnknown() || model.AlertConfig.IsNull() {
- alertConfig, err = getMockAlertConfig(ctx)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Getting mock alert config: %v", err))
- return
- }
- }
-
- alertConfigPayload, err := toUpdateAlertConfigPayload(ctx, &alertConfig)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Building alert config payload: %v", err))
- return
- }
-
- if alertConfigPayload != nil {
- _, err = r.client.UpdateAlertConfigs(ctx, *instanceId, projectId).UpdateAlertConfigsPayload(*alertConfigPayload).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Setting alert config: %v", err))
- return
- }
- }
-
- // Get alert config after update
- alertConfigResp, err := r.client.GetAlertConfigs(ctx, *instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Getting alert config: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapAlertConfigField(ctx, alertConfigResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
- return
- }
-
- // Set state to fully populated data
- diags = setAlertConfig(ctx, &resp.State, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- tflog.Info(ctx, "Argus instance created")
-}
-
-// Read refreshes the Terraform state with the latest data.
-func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
- var model Model
- diags := req.State.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- ctx = tflog.SetField(ctx, "project_id", projectId)
- ctx = tflog.SetField(ctx, "instance_id", instanceId)
-
- instanceResp, err := r.client.GetInstance(ctx, instanceId, projectId).Execute()
- if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
- return
- }
- if instanceResp != nil && instanceResp.Status != nil && *instanceResp.Status == wait.DeleteSuccess {
- resp.State.RemoveResource(ctx)
- return
- }
-
- aclListResp, err := r.client.ListACL(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API for ACL data: %v", err))
- return
- }
-
- metricsRetentionResp, err := r.client.GetMetricsStorageRetention(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get metrics retention: %v", err))
- return
- }
-
- alertConfigResp, err := r.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get alert config: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapFields(ctx, instanceResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
- return
- }
-
- // Set state to fully populated data
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // Map response body to schema
- err = mapACLField(aclListResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API response for the ACL: %v", err))
- return
- }
-
- // Set state to fully populated data
- diags = setACL(ctx, &resp.State, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // Map response body to schema
- err = mapMetricsRetentionField(metricsRetentionResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API response for the metrics retention: %v", err))
- return
- }
-
- // Set state to fully populated data
- diags = setMetricsRetentions(ctx, &resp.State, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // Map response body to schema
- err = mapAlertConfigField(ctx, alertConfigResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
- return
- }
-
- // Set state to fully populated data
- diags = setAlertConfig(ctx, &resp.State, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- tflog.Info(ctx, "Argus instance read")
-}
-
-// Update updates the resource and sets the updated Terraform state on success.
-func (r *instanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
- // Retrieve values from plan
- var model Model
- diags := req.Plan.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
-
- acl := []string{}
- if !(model.ACL.IsNull() || model.ACL.IsUnknown()) {
- diags = model.ACL.ElementsAs(ctx, &acl, false)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- metricsRetentionDays := conversion.Int64ValueToPointer(model.MetricsRetentionDays)
- metricsRetentionDays5mDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays5mDownsampling)
- metricsRetentionDays1hDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays1hDownsampling)
-
- alertConfig := alertConfigModel{}
- if !(model.AlertConfig.IsNull() || model.AlertConfig.IsUnknown()) {
- diags = model.AlertConfig.As(ctx, &alertConfig, basetypes.ObjectAsOptions{})
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- err := r.loadPlanId(ctx, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Loading service plan: %v", err))
- return
- }
-
- // Generate API request body from model
- payload, err := toUpdatePayload(&model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Creating API payload: %v", err))
- return
- }
- // Update existing instance
- _, err = r.client.UpdateInstance(ctx, instanceId, projectId).UpdateInstancePayload(*payload).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Calling API: %v", err))
- return
- }
- waitResp, err := wait.UpdateInstanceWaitHandler(ctx, r.client, instanceId, projectId).WaitWithContext(ctx)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Instance update waiting: %v", err))
- return
- }
-
- err = mapFields(ctx, waitResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
- return
- }
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // Update ACL
- err = updateACL(ctx, projectId, instanceId, acl, r.client)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Updating ACL: %v", err))
- return
- }
- aclList, err := r.client.ListACL(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Calling API to list ACL data: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapACLField(aclList, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API response for the ACL: %v", err))
- return
- }
-
- // Set state to ACL populated data
- resp.Diagnostics.Append(setACL(ctx, &resp.State, &model)...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // If any of the metrics retention days are set, set the metrics retention policy
- if metricsRetentionDays != nil || metricsRetentionDays5mDownsampling != nil || metricsRetentionDays1hDownsampling != nil {
- // Need to get the metrics retention policy because update endpoint is a PUT and we need to send all fields
- metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, instanceId, projectId)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Getting metrics retention policy: %v", err))
- return
- }
-
- metricsRetentionPayload, err := toUpdateMetricsStorageRetentionPayload(metricsRetentionDays, metricsRetentionDays5mDownsampling, metricsRetentionDays1hDownsampling, metricsResp)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Building metrics retention policy payload: %v", err))
- return
- }
- _, err = r.client.UpdateMetricsStorageRetention(ctx, instanceId, projectId).UpdateMetricsStorageRetentionPayload(*metricsRetentionPayload).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Setting metrics retention policy: %v", err))
- return
- }
- }
-
- // Get metrics retention policy after update
- metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, instanceId, projectId)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Getting metrics retention policy: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapMetricsRetentionField(metricsResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API response for the metrics retention %v", err))
- return
- }
- // Set state to fully populated data
- diags = setMetricsRetentions(ctx, &resp.State, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- // Alert Config
- if model.AlertConfig.IsUnknown() || model.AlertConfig.IsNull() {
- alertConfig, err = getMockAlertConfig(ctx)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Getting mock alert config: %v", err))
- return
- }
- }
-
- alertConfigPayload, err := toUpdateAlertConfigPayload(ctx, &alertConfig)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Building alert config payload: %v", err))
- return
- }
-
- if alertConfigPayload != nil {
- _, err = r.client.UpdateAlertConfigs(ctx, instanceId, projectId).UpdateAlertConfigsPayload(*alertConfigPayload).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Setting alert config: %v", err))
- return
- }
- }
-
- // Get updated alert config
- alertConfigResp, err := r.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Calling API to get alert config: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapAlertConfigField(ctx, alertConfigResp, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
- return
- }
-
- // Set state to fully populated data
- diags = setAlertConfig(ctx, &resp.State, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- tflog.Info(ctx, "Argus instance updated")
-}
-
-// Delete deletes the resource and removes the Terraform state on success.
-func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
- // Retrieve values from state
- var model Model
- diags := req.State.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
-
- // Delete existing instance
- _, err := r.client.DeleteInstance(ctx, instanceId, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err))
- return
- }
- _, err = wait.DeleteInstanceWaitHandler(ctx, r.client, instanceId, projectId).WaitWithContext(ctx)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Instance deletion waiting: %v", err))
- return
- }
-
- tflog.Info(ctx, "Argus instance deleted")
-}
-
-// ImportState imports a resource into the Terraform state on success.
-// The expected format of the resource import identifier is: project_id,instance_id
-func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
- idParts := strings.Split(req.ID, core.Separator)
-
- if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
- core.LogAndAddError(ctx, &resp.Diagnostics,
- "Error importing instance",
- fmt.Sprintf("Expected import identifier with format: [project_id],[instance_id] Got: %q", req.ID),
- )
- return
- }
-
- resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
- resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
- tflog.Info(ctx, "Argus instance state imported")
-}
-
-func mapFields(ctx context.Context, r *argus.GetInstanceResponse, model *Model) error {
- if r == nil {
- return fmt.Errorf("response input is nil")
- }
- if model == nil {
- return fmt.Errorf("model input is nil")
- }
- var instanceId string
- if model.InstanceId.ValueString() != "" {
- instanceId = model.InstanceId.ValueString()
- } else if r.Id != nil {
- instanceId = *r.Id
- } else {
- return fmt.Errorf("instance id not present")
- }
-
- idParts := []string{
- model.ProjectId.ValueString(),
- instanceId,
- }
- model.Id = types.StringValue(
- strings.Join(idParts, core.Separator),
- )
- model.InstanceId = types.StringValue(instanceId)
- model.PlanName = types.StringPointerValue(r.PlanName)
- model.PlanId = types.StringPointerValue(r.PlanId)
- model.Name = types.StringPointerValue(r.Name)
-
- ps := r.Parameters
- if ps == nil {
- model.Parameters = types.MapNull(types.StringType)
- } else {
- params := make(map[string]attr.Value, len(*ps))
- for k, v := range *ps {
- params[k] = types.StringValue(v)
- }
- res, diags := types.MapValueFrom(ctx, types.StringType, params)
- if diags.HasError() {
- return fmt.Errorf("parameter mapping %s", diags.Errors())
- }
- model.Parameters = res
- }
-
- model.IsUpdatable = types.BoolPointerValue(r.IsUpdatable)
- model.DashboardURL = types.StringPointerValue(r.DashboardUrl)
- if r.Instance != nil {
- i := *r.Instance
- model.GrafanaURL = types.StringPointerValue(i.GrafanaUrl)
- model.GrafanaPublicReadAccess = types.BoolPointerValue(i.GrafanaPublicReadAccess)
- model.GrafanaInitialAdminPassword = types.StringPointerValue(i.GrafanaAdminPassword)
- model.GrafanaInitialAdminUser = types.StringPointerValue(i.GrafanaAdminUser)
- model.MetricsURL = types.StringPointerValue(i.MetricsUrl)
- model.MetricsPushURL = types.StringPointerValue(i.PushMetricsUrl)
- model.TargetsURL = types.StringPointerValue(i.TargetsUrl)
- model.AlertingURL = types.StringPointerValue(i.AlertingUrl)
- model.LogsURL = types.StringPointerValue(i.LogsUrl)
- model.LogsPushURL = types.StringPointerValue(i.LogsPushUrl)
- model.JaegerTracesURL = types.StringPointerValue(i.JaegerTracesUrl)
- model.JaegerUIURL = types.StringPointerValue(i.JaegerUiUrl)
- model.OtlpTracesURL = types.StringPointerValue(i.OtlpTracesUrl)
- model.ZipkinSpansURL = types.StringPointerValue(i.ZipkinSpansUrl)
- }
-
- return nil
-}
-
-func mapACLField(aclList *argus.ListACLResponse, model *Model) error {
- if aclList == nil {
- return fmt.Errorf("mapping ACL: nil API response")
- }
-
- if aclList.Acl == nil || len(*aclList.Acl) == 0 {
- if !(model.ACL.IsNull() || model.ACL.IsUnknown() || model.ACL.Equal(types.SetValueMust(types.StringType, []attr.Value{}))) {
- model.ACL = types.SetNull(types.StringType)
- }
- return nil
- }
-
- acl := []attr.Value{}
- for _, cidr := range *aclList.Acl {
- acl = append(acl, types.StringValue(cidr))
- }
- aclTF, diags := types.SetValue(types.StringType, acl)
- if diags.HasError() {
- return fmt.Errorf("mapping ACL: %w", core.DiagsToError(diags))
- }
- model.ACL = aclTF
- return nil
-}
-
-func mapMetricsRetentionField(r *argus.GetMetricsStorageRetentionResponse, model *Model) error {
- if r == nil {
- return fmt.Errorf("response input is nil")
- }
- if model == nil {
- return fmt.Errorf("model input is nil")
- }
-
- if r.MetricsRetentionTimeRaw == nil || r.MetricsRetentionTime5m == nil || r.MetricsRetentionTime1h == nil {
- return fmt.Errorf("metrics retention time is nil")
- }
-
- stripedMetricsRetentionDays := strings.TrimSuffix(*r.MetricsRetentionTimeRaw, "d")
- metricsRetentionDays, err := strconv.ParseInt(stripedMetricsRetentionDays, 10, 64)
- if err != nil {
- return fmt.Errorf("parsing metrics retention days: %w", err)
- }
- model.MetricsRetentionDays = types.Int64Value(metricsRetentionDays)
-
- stripedMetricsRetentionDays5m := strings.TrimSuffix(*r.MetricsRetentionTime5m, "d")
- metricsRetentionDays5m, err := strconv.ParseInt(stripedMetricsRetentionDays5m, 10, 64)
- if err != nil {
- return fmt.Errorf("parsing metrics retention days 5m: %w", err)
- }
- model.MetricsRetentionDays5mDownsampling = types.Int64Value(metricsRetentionDays5m)
-
- stripedMetricsRetentionDays1h := strings.TrimSuffix(*r.MetricsRetentionTime1h, "d")
- metricsRetentionDays1h, err := strconv.ParseInt(stripedMetricsRetentionDays1h, 10, 64)
- if err != nil {
- return fmt.Errorf("parsing metrics retention days 1h: %w", err)
- }
- model.MetricsRetentionDays1hDownsampling = types.Int64Value(metricsRetentionDays1h)
-
- return nil
-}
-
-func mapAlertConfigField(ctx context.Context, resp *argus.GetAlertConfigsResponse, model *Model) error {
- if resp == nil || resp.Data == nil {
- model.AlertConfig = types.ObjectNull(alertConfigTypes)
- return nil
- }
-
- if model == nil {
- return fmt.Errorf("nil model")
- }
-
- var alertConfigTF *alertConfigModel
- if !(model.AlertConfig.IsNull() || model.AlertConfig.IsUnknown()) {
- alertConfigTF = &alertConfigModel{}
- diags := model.AlertConfig.As(ctx, &alertConfigTF, basetypes.ObjectAsOptions{})
- if diags.HasError() {
- return fmt.Errorf("mapping alert config: %w", core.DiagsToError(diags))
- }
- }
-
- respReceivers := resp.Data.Receivers
- respRoute := resp.Data.Route
- respGlobalConfigs := resp.Data.Global
-
- receiversList, err := mapReceiversToAttributes(ctx, respReceivers)
- if err != nil {
- return fmt.Errorf("mapping alert config receivers: %w", err)
- }
-
- route, err := mapRouteToAttributes(ctx, respRoute)
- if err != nil {
- return fmt.Errorf("mapping alert config route: %w", err)
- }
-
- var globalConfigModel *globalConfigurationModel
- if alertConfigTF != nil && !alertConfigTF.GlobalConfiguration.IsNull() && !alertConfigTF.GlobalConfiguration.IsUnknown() {
- globalConfigModel = &globalConfigurationModel{}
- diags := alertConfigTF.GlobalConfiguration.As(ctx, globalConfigModel, basetypes.ObjectAsOptions{})
- if diags.HasError() {
- return fmt.Errorf("mapping alert config: %w", core.DiagsToError(diags))
- }
- }
-
- globalConfig, err := mapGlobalConfigToAttributes(respGlobalConfigs, globalConfigModel)
- if err != nil {
- return fmt.Errorf("mapping alert config global config: %w", err)
- }
-
- alertConfig, diags := types.ObjectValue(alertConfigTypes, map[string]attr.Value{
- "receivers": receiversList,
- "route": route,
- "global": globalConfig,
- })
- if diags.HasError() {
- return fmt.Errorf("converting alert config to TF type: %w", core.DiagsToError(diags))
- }
-
- // Check if the alert config is equal to the mock alert config
- // This is done because the Alert Config cannot be removed from the instance, but can be unset by the user in the Terraform configuration
- // If the alert config is equal to the mock alert config, we will map the Alert Config to an empty object in the Terraform state
- // This is done to avoid inconsistent applies or non-empty plans after applying
- mockAlertConfig, err := getMockAlertConfig(ctx)
- if err != nil {
- return fmt.Errorf("getting mock alert config: %w", err)
- }
- modelMockAlertConfig, diags := types.ObjectValueFrom(ctx, alertConfigTypes, mockAlertConfig)
- if diags.HasError() {
- return fmt.Errorf("converting mock alert config to TF type: %w", core.DiagsToError(diags))
- }
- if alertConfig.Equal(modelMockAlertConfig) {
- alertConfig = types.ObjectNull(alertConfigTypes)
- }
-
- model.AlertConfig = alertConfig
- return nil
-}
-
-// getMockAlertConfig returns a default alert config to be set in the instance if the alert config is unset in the Terraform configuration
-//
-// This is done because the Alert Config cannot be removed from the instance, but can be unset by the user in the Terraform configuration.
-// So, we set the Alert Config in the instance to our mock configuration and
-// map the Alert Config to an empty object in the Terraform state if it matches the mock alert config
-func getMockAlertConfig(ctx context.Context) (alertConfigModel, error) {
- mockEmailConfig, diags := types.ObjectValue(emailConfigsTypes, map[string]attr.Value{
- "to": types.StringValue("123@gmail.com"),
- "smart_host": types.StringValue("smtp.gmail.com:587"),
- "from": types.StringValue("xxxx@gmail.com"),
- "auth_username": types.StringValue("xxxx@gmail.com"),
- "auth_password": types.StringValue("xxxxxxxxx"),
- "auth_identity": types.StringValue("xxxx@gmail.com"),
- })
- if diags.HasError() {
- return alertConfigModel{}, fmt.Errorf("mapping email config: %w", core.DiagsToError(diags))
- }
-
- mockEmailConfigs, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: emailConfigsTypes}, []attr.Value{
- mockEmailConfig,
- })
- if diags.HasError() {
- return alertConfigModel{}, fmt.Errorf("mapping email configs: %w", core.DiagsToError(diags))
- }
-
- mockReceiver, diags := types.ObjectValue(receiversTypes, map[string]attr.Value{
- "name": types.StringValue("email-me"),
- "email_configs": mockEmailConfigs,
- "opsgenie_configs": types.ListNull(types.ObjectType{AttrTypes: opsgenieConfigsTypes}),
- "webhooks_configs": types.ListNull(types.ObjectType{AttrTypes: webHooksConfigsTypes}),
- })
- if diags.HasError() {
- return alertConfigModel{}, fmt.Errorf("mapping receiver: %w", core.DiagsToError(diags))
- }
-
- mockReceivers, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- mockReceiver,
- })
- if diags.HasError() {
- return alertConfigModel{}, fmt.Errorf("mapping receivers: %w", core.DiagsToError(diags))
- }
-
- mockGroupByList, diags := types.ListValueFrom(ctx, types.StringType, []attr.Value{
- types.StringValue("job"),
- })
- if diags.HasError() {
- return alertConfigModel{}, fmt.Errorf("mapping group by list: %w", core.DiagsToError(diags))
- }
-
- mockRoute, diags := types.ObjectValue(routeTypes, map[string]attr.Value{
- "receiver": types.StringValue("email-me"),
- "group_by": mockGroupByList,
- "group_wait": types.StringValue("30s"),
- "group_interval": types.StringValue("5m"),
- "repeat_interval": types.StringValue("4h"),
- "match": types.MapNull(types.StringType),
- "match_regex": types.MapNull(types.StringType),
- "routes": types.ListNull(getRouteListType()),
- })
- if diags.HasError() {
- return alertConfigModel{}, fmt.Errorf("mapping route: %w", core.DiagsToError(diags))
- }
-
- mockGlobalConfig, diags := types.ObjectValue(globalConfigurationTypes, map[string]attr.Value{
- "opsgenie_api_key": types.StringNull(),
- "opsgenie_api_url": types.StringNull(),
- "resolve_timeout": types.StringValue("5m"),
- "smtp_auth_identity": types.StringNull(),
- "smtp_auth_password": types.StringNull(),
- "smtp_auth_username": types.StringNull(),
- "smtp_from": types.StringValue("argus@argus.stackit.cloud"),
- "smtp_smart_host": types.StringNull(),
- })
- if diags.HasError() {
- return alertConfigModel{}, fmt.Errorf("mapping global config: %w", core.DiagsToError(diags))
- }
-
- return alertConfigModel{
- Receivers: mockReceivers,
- Route: mockRoute,
- GlobalConfiguration: mockGlobalConfig,
- }, nil
-}
-
-func mapGlobalConfigToAttributes(respGlobalConfigs *argus.Global, globalConfigsTF *globalConfigurationModel) (basetypes.ObjectValue, error) {
- if respGlobalConfigs == nil {
- return types.ObjectNull(globalConfigurationTypes), nil
- }
-
- // This bypass is needed because these values are not returned in the API GET response
- smtpSmartHost := respGlobalConfigs.SmtpSmarthost
- smtpAuthIdentity := respGlobalConfigs.SmtpAuthIdentity
- smtpAuthPassword := respGlobalConfigs.SmtpAuthPassword
- smtpAuthUsername := respGlobalConfigs.SmtpAuthUsername
- if globalConfigsTF != nil {
- if respGlobalConfigs.SmtpSmarthost == nil &&
- !globalConfigsTF.SmtpSmartHost.IsNull() && !globalConfigsTF.SmtpSmartHost.IsUnknown() {
- smtpSmartHost = utils.Ptr(globalConfigsTF.SmtpSmartHost.ValueString())
- }
- if respGlobalConfigs.SmtpAuthIdentity == nil &&
- !globalConfigsTF.SmtpAuthIdentity.IsNull() && !globalConfigsTF.SmtpAuthIdentity.IsUnknown() {
- smtpAuthIdentity = utils.Ptr(globalConfigsTF.SmtpAuthIdentity.ValueString())
- }
- if respGlobalConfigs.SmtpAuthPassword == nil &&
- !globalConfigsTF.SmtpAuthPassword.IsNull() && !globalConfigsTF.SmtpAuthPassword.IsUnknown() {
- smtpAuthPassword = utils.Ptr(globalConfigsTF.SmtpAuthPassword.ValueString())
- }
- if respGlobalConfigs.SmtpAuthUsername == nil &&
- !globalConfigsTF.SmtpAuthUsername.IsNull() && !globalConfigsTF.SmtpAuthUsername.IsUnknown() {
- smtpAuthUsername = utils.Ptr(globalConfigsTF.SmtpAuthUsername.ValueString())
- }
- }
-
- globalConfigObject, diags := types.ObjectValue(globalConfigurationTypes, map[string]attr.Value{
- "opsgenie_api_key": types.StringPointerValue(respGlobalConfigs.OpsgenieApiKey),
- "opsgenie_api_url": types.StringPointerValue(respGlobalConfigs.OpsgenieApiUrl),
- "resolve_timeout": types.StringPointerValue(respGlobalConfigs.ResolveTimeout),
- "smtp_from": types.StringPointerValue(respGlobalConfigs.SmtpFrom),
- "smtp_auth_identity": types.StringPointerValue(smtpAuthIdentity),
- "smtp_auth_password": types.StringPointerValue(smtpAuthPassword),
- "smtp_auth_username": types.StringPointerValue(smtpAuthUsername),
- "smtp_smart_host": types.StringPointerValue(smtpSmartHost),
- })
- if diags.HasError() {
- return types.ObjectNull(globalConfigurationTypes), fmt.Errorf("mapping global config: %w", core.DiagsToError(diags))
- }
-
- return globalConfigObject, nil
-}
-
-func mapReceiversToAttributes(ctx context.Context, respReceivers *[]argus.Receivers) (basetypes.ListValue, error) {
- if respReceivers == nil {
- return types.ListNull(types.ObjectType{AttrTypes: receiversTypes}), nil
- }
- receiversList := []attr.Value{}
- emptyList, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{})
- if diags.HasError() {
- // Should not happen
- return emptyList, fmt.Errorf("mapping empty list: %w", core.DiagsToError(diags))
- }
-
- if len(*respReceivers) == 0 {
- return emptyList, nil
- }
-
- for i := range *respReceivers {
- receiver := (*respReceivers)[i]
-
- emailConfigList := []attr.Value{}
- if receiver.EmailConfigs != nil {
- for _, emailConfig := range *receiver.EmailConfigs {
- emailConfigMap := map[string]attr.Value{
- "auth_identity": types.StringPointerValue(emailConfig.AuthIdentity),
- "auth_password": types.StringPointerValue(emailConfig.AuthPassword),
- "auth_username": types.StringPointerValue(emailConfig.AuthUsername),
- "from": types.StringPointerValue(emailConfig.From),
- "smart_host": types.StringPointerValue(emailConfig.Smarthost),
- "to": types.StringPointerValue(emailConfig.To),
- }
- emailConfigModel, diags := types.ObjectValue(emailConfigsTypes, emailConfigMap)
- if diags.HasError() {
- return emptyList, fmt.Errorf("mapping email config: %w", core.DiagsToError(diags))
- }
- emailConfigList = append(emailConfigList, emailConfigModel)
- }
- }
-
- opsgenieConfigList := []attr.Value{}
- if receiver.OpsgenieConfigs != nil {
- for _, opsgenieConfig := range *receiver.OpsgenieConfigs {
- opsGenieConfigMap := map[string]attr.Value{
- "api_key": types.StringPointerValue(opsgenieConfig.ApiKey),
- "api_url": types.StringPointerValue(opsgenieConfig.ApiUrl),
- "tags": types.StringPointerValue(opsgenieConfig.Tags),
- }
- opsGenieConfigModel, diags := types.ObjectValue(opsgenieConfigsTypes, opsGenieConfigMap)
- if diags.HasError() {
- return emptyList, fmt.Errorf("mapping opsgenie config: %w", core.DiagsToError(diags))
- }
- opsgenieConfigList = append(opsgenieConfigList, opsGenieConfigModel)
- }
- }
-
- webhooksConfigList := []attr.Value{}
- if receiver.WebHookConfigs != nil {
- for _, webhookConfig := range *receiver.WebHookConfigs {
- webHookConfigsMap := map[string]attr.Value{
- "url": types.StringPointerValue(webhookConfig.Url),
- "ms_teams": types.BoolPointerValue(webhookConfig.MsTeams),
- }
- webHookConfigsModel, diags := types.ObjectValue(webHooksConfigsTypes, webHookConfigsMap)
- if diags.HasError() {
- return emptyList, fmt.Errorf("mapping webhooks config: %w", core.DiagsToError(diags))
- }
- webhooksConfigList = append(webhooksConfigList, webHookConfigsModel)
- }
- }
-
- if receiver.Name == nil {
- return emptyList, fmt.Errorf("receiver name is nil")
- }
-
- var emailConfigs basetypes.ListValue
- if len(emailConfigList) == 0 {
- emailConfigs = types.ListNull(types.ObjectType{AttrTypes: emailConfigsTypes})
- } else {
- emailConfigs, diags = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: emailConfigsTypes}, emailConfigList)
- if diags.HasError() {
- return emptyList, fmt.Errorf("mapping email configs: %w", core.DiagsToError(diags))
- }
- }
-
- var opsGenieConfigs basetypes.ListValue
- if len(opsgenieConfigList) == 0 {
- opsGenieConfigs = types.ListNull(types.ObjectType{AttrTypes: opsgenieConfigsTypes})
- } else {
- opsGenieConfigs, diags = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: opsgenieConfigsTypes}, opsgenieConfigList)
- if diags.HasError() {
- return emptyList, fmt.Errorf("mapping opsgenie configs: %w", core.DiagsToError(diags))
- }
- }
-
- var webHooksConfigs basetypes.ListValue
- if len(webhooksConfigList) == 0 {
- webHooksConfigs = types.ListNull(types.ObjectType{AttrTypes: webHooksConfigsTypes})
- } else {
- webHooksConfigs, diags = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: webHooksConfigsTypes}, webhooksConfigList)
- if diags.HasError() {
- return emptyList, fmt.Errorf("mapping webhooks configs: %w", core.DiagsToError(diags))
- }
- }
-
- receiverMap := map[string]attr.Value{
- "name": types.StringPointerValue(receiver.Name),
- "email_configs": emailConfigs,
- "opsgenie_configs": opsGenieConfigs,
- "webhooks_configs": webHooksConfigs,
- }
-
- receiversModel, diags := types.ObjectValue(receiversTypes, receiverMap)
- if diags.HasError() {
- return emptyList, fmt.Errorf("mapping receiver: %w", core.DiagsToError(diags))
- }
-
- receiversList = append(receiversList, receiversModel)
- }
-
- returnReceiversList, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: receiversTypes}, receiversList)
- if diags.HasError() {
- return emptyList, fmt.Errorf("mapping receivers list: %w", core.DiagsToError(diags))
- }
- return returnReceiversList, nil
-}
-
-func mapRouteToAttributes(ctx context.Context, route *argus.Route) (attr.Value, error) {
- if route == nil {
- return types.ObjectNull(routeTypes), nil
- }
-
- groupByModel, diags := types.ListValueFrom(ctx, types.StringType, route.GroupBy)
- if diags.HasError() {
- return types.ObjectNull(routeTypes), fmt.Errorf("mapping group by: %w", core.DiagsToError(diags))
- }
-
- matchModel, diags := types.MapValueFrom(ctx, types.StringType, route.Match)
- if diags.HasError() {
- return types.ObjectNull(routeTypes), fmt.Errorf("mapping match: %w", core.DiagsToError(diags))
- }
-
- matchRegexModel, diags := types.MapValueFrom(ctx, types.StringType, route.MatchRe)
- if diags.HasError() {
- return types.ObjectNull(routeTypes), fmt.Errorf("mapping match regex: %w", core.DiagsToError(diags))
- }
-
- childRoutes, err := mapChildRoutesToAttributes(ctx, route.Routes)
- if err != nil {
- return types.ObjectNull(routeTypes), fmt.Errorf("mapping child routes: %w", err)
- }
-
- routeMap := map[string]attr.Value{
- "group_by": groupByModel,
- "group_interval": types.StringPointerValue(route.GroupInterval),
- "group_wait": types.StringPointerValue(route.GroupWait),
- "match": matchModel,
- "match_regex": matchRegexModel,
- "receiver": types.StringPointerValue(route.Receiver),
- "repeat_interval": types.StringPointerValue(route.RepeatInterval),
- "routes": childRoutes,
- }
-
- routeModel, diags := types.ObjectValue(routeTypes, routeMap)
- if diags.HasError() {
- return types.ObjectNull(routeTypes), fmt.Errorf("converting route to TF types: %w", core.DiagsToError(diags))
- }
-
- return routeModel, nil
-}
-
-// mapChildRoutesToAttributes maps the child routes to the Terraform attributes
-// This should be a recursive function to handle nested child routes
-// However, the API does not currently have the correct type for the child routes
-// In the future, the current implementation should be the final case of the recursive function
-func mapChildRoutesToAttributes(ctx context.Context, routes *[]argus.RouteSerializer) (basetypes.ListValue, error) {
- nullList := types.ListNull(getRouteListType())
- if routes == nil {
- return nullList, nil
- }
-
- routesList := []attr.Value{}
- for _, route := range *routes {
- groupByModel, diags := types.ListValueFrom(ctx, types.StringType, route.GroupBy)
- if diags.HasError() {
- return nullList, fmt.Errorf("mapping group by: %w", core.DiagsToError(diags))
- }
-
- matchModel, diags := types.MapValueFrom(ctx, types.StringType, route.Match)
- if diags.HasError() {
- return nullList, fmt.Errorf("mapping match: %w", core.DiagsToError(diags))
- }
-
- matchRegexModel, diags := types.MapValueFrom(ctx, types.StringType, route.MatchRe)
- if diags.HasError() {
- return nullList, fmt.Errorf("mapping match regex: %w", core.DiagsToError(diags))
- }
-
- routeMap := map[string]attr.Value{
- "group_by": groupByModel,
- "group_interval": types.StringPointerValue(route.GroupInterval),
- "group_wait": types.StringPointerValue(route.GroupWait),
- "match": matchModel,
- "match_regex": matchRegexModel,
- "receiver": types.StringPointerValue(route.Receiver),
- "repeat_interval": types.StringPointerValue(route.RepeatInterval),
- }
-
- routeModel, diags := types.ObjectValue(getRouteListType().AttrTypes, routeMap)
- if diags.HasError() {
- return types.ListNull(getRouteListType()), fmt.Errorf("converting child route to TF types: %w", core.DiagsToError(diags))
- }
-
- routesList = append(routesList, routeModel)
- }
-
- returnRoutesList, diags := types.ListValueFrom(ctx, getRouteListType(), routesList)
- if diags.HasError() {
- return nullList, fmt.Errorf("mapping child routes list: %w", core.DiagsToError(diags))
- }
- return returnRoutesList, nil
-}
-
-func toCreatePayload(model *Model) (*argus.CreateInstancePayload, error) {
- if model == nil {
- return nil, fmt.Errorf("nil model")
- }
- elements := model.Parameters.Elements()
- pa := make(map[string]interface{}, len(elements))
- for k := range elements {
- pa[k] = elements[k].String()
- }
- return &argus.CreateInstancePayload{
- Name: conversion.StringValueToPointer(model.Name),
- PlanId: conversion.StringValueToPointer(model.PlanId),
- Parameter: &pa,
- }, nil
-}
-
-func toUpdateMetricsStorageRetentionPayload(retentionDaysRaw, retentionDays5m, retentionDays1h *int64, resp *argus.GetMetricsStorageRetentionResponse) (*argus.UpdateMetricsStorageRetentionPayload, error) {
- var retentionTimeRaw string
- var retentionTime5m string
- var retentionTime1h string
-
- if resp == nil || resp.MetricsRetentionTimeRaw == nil || resp.MetricsRetentionTime5m == nil || resp.MetricsRetentionTime1h == nil {
- return nil, fmt.Errorf("nil response")
- }
-
- if retentionDaysRaw == nil {
- retentionTimeRaw = *resp.MetricsRetentionTimeRaw
- } else {
- retentionTimeRaw = fmt.Sprintf("%dd", *retentionDaysRaw)
- }
-
- if retentionDays5m == nil {
- retentionTime5m = *resp.MetricsRetentionTime5m
- } else {
- retentionTime5m = fmt.Sprintf("%dd", *retentionDays5m)
- }
-
- if retentionDays1h == nil {
- retentionTime1h = *resp.MetricsRetentionTime1h
- } else {
- retentionTime1h = fmt.Sprintf("%dd", *retentionDays1h)
- }
-
- return &argus.UpdateMetricsStorageRetentionPayload{
- MetricsRetentionTimeRaw: &retentionTimeRaw,
- MetricsRetentionTime5m: &retentionTime5m,
- MetricsRetentionTime1h: &retentionTime1h,
- }, nil
-}
-
-func updateACL(ctx context.Context, projectId, instanceId string, acl []string, client *argus.APIClient) error {
- payload := argus.UpdateACLPayload{
- Acl: utils.Ptr(acl),
- }
-
- _, err := client.UpdateACL(ctx, instanceId, projectId).UpdateACLPayload(payload).Execute()
- if err != nil {
- return fmt.Errorf("updating ACL: %w", err)
- }
-
- return nil
-}
-
-func toUpdatePayload(model *Model) (*argus.UpdateInstancePayload, error) {
- if model == nil {
- return nil, fmt.Errorf("nil model")
- }
- elements := model.Parameters.Elements()
- pa := make(map[string]interface{}, len(elements))
- for k, v := range elements {
- pa[k] = v.String()
- }
- return &argus.UpdateInstancePayload{
- Name: conversion.StringValueToPointer(model.Name),
- PlanId: conversion.StringValueToPointer(model.PlanId),
- Parameter: &pa,
- }, nil
-}
-
-func toUpdateAlertConfigPayload(ctx context.Context, model *alertConfigModel) (*argus.UpdateAlertConfigsPayload, error) {
- if model == nil {
- return nil, fmt.Errorf("nil model")
- }
- if model.Receivers.IsNull() || model.Receivers.IsUnknown() {
- return nil, fmt.Errorf("receivers in the model are null or unknown")
- }
-
- if model.Route.IsNull() || model.Route.IsUnknown() {
- return nil, fmt.Errorf("route in the model is null or unknown")
- }
-
- var err error
-
- payload := argus.UpdateAlertConfigsPayload{}
-
- payload.Receivers, err = toReceiverPayload(ctx, model)
- if err != nil {
- return nil, fmt.Errorf("mapping receivers: %w", err)
- }
-
- routeTF := routeModel{}
- diags := model.Route.As(ctx, &routeTF, basetypes.ObjectAsOptions{})
- if diags.HasError() {
- return nil, fmt.Errorf("mapping route: %w", core.DiagsToError(diags))
- }
-
- payload.Route, err = toRoutePayload(ctx, &routeTF)
- if err != nil {
- return nil, fmt.Errorf("mapping route: %w", err)
- }
-
- if !model.GlobalConfiguration.IsNull() && !model.GlobalConfiguration.IsUnknown() {
- payload.Global, err = toGlobalConfigPayload(ctx, model)
- if err != nil {
- return nil, fmt.Errorf("mapping global: %w", err)
- }
- }
-
- return &payload, nil
-}
-
-func toReceiverPayload(ctx context.Context, model *alertConfigModel) (*[]argus.UpdateAlertConfigsPayloadReceiversInner, error) {
- if model == nil {
- return nil, fmt.Errorf("nil model")
- }
- receiversModel := []receiversModel{}
- diags := model.Receivers.ElementsAs(ctx, &receiversModel, false)
- if diags.HasError() {
- return nil, fmt.Errorf("mapping receivers: %w", core.DiagsToError(diags))
- }
-
- receivers := []argus.UpdateAlertConfigsPayloadReceiversInner{}
-
- for i := range receiversModel {
- receiver := receiversModel[i]
- receiverPayload := argus.UpdateAlertConfigsPayloadReceiversInner{
- Name: conversion.StringValueToPointer(receiver.Name),
- }
-
- if !receiver.EmailConfigs.IsNull() && !receiver.EmailConfigs.IsUnknown() {
- emailConfigs := []emailConfigsModel{}
- diags := receiver.EmailConfigs.ElementsAs(ctx, &emailConfigs, false)
- if diags.HasError() {
- return nil, fmt.Errorf("mapping email configs: %w", core.DiagsToError(diags))
- }
- payloadEmailConfigs := []argus.CreateAlertConfigReceiverPayloadEmailConfigsInner{}
- for i := range emailConfigs {
- emailConfig := emailConfigs[i]
- payloadEmailConfigs = append(payloadEmailConfigs, argus.CreateAlertConfigReceiverPayloadEmailConfigsInner{
- AuthIdentity: conversion.StringValueToPointer(emailConfig.AuthIdentity),
- AuthPassword: conversion.StringValueToPointer(emailConfig.AuthPassword),
- AuthUsername: conversion.StringValueToPointer(emailConfig.AuthUsername),
- From: conversion.StringValueToPointer(emailConfig.From),
- Smarthost: conversion.StringValueToPointer(emailConfig.Smarthost),
- To: conversion.StringValueToPointer(emailConfig.To),
- })
- }
- receiverPayload.EmailConfigs = &payloadEmailConfigs
- }
-
- if !receiver.OpsGenieConfigs.IsNull() && !receiver.OpsGenieConfigs.IsUnknown() {
- opsgenieConfigs := []opsgenieConfigsModel{}
- diags := receiver.OpsGenieConfigs.ElementsAs(ctx, &opsgenieConfigs, false)
- if diags.HasError() {
- return nil, fmt.Errorf("mapping opsgenie configs: %w", core.DiagsToError(diags))
- }
- payloadOpsGenieConfigs := []argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner{}
- for i := range opsgenieConfigs {
- opsgenieConfig := opsgenieConfigs[i]
- payloadOpsGenieConfigs = append(payloadOpsGenieConfigs, argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner{
- ApiKey: conversion.StringValueToPointer(opsgenieConfig.ApiKey),
- ApiUrl: conversion.StringValueToPointer(opsgenieConfig.ApiUrl),
- Tags: conversion.StringValueToPointer(opsgenieConfig.Tags),
- })
- }
- receiverPayload.OpsgenieConfigs = &payloadOpsGenieConfigs
- }
-
- if !receiver.WebHooksConfigs.IsNull() && !receiver.WebHooksConfigs.IsUnknown() {
- receiverWebHooksConfigs := []webHooksConfigsModel{}
- diags := receiver.WebHooksConfigs.ElementsAs(ctx, &receiverWebHooksConfigs, false)
- if diags.HasError() {
- return nil, fmt.Errorf("mapping webhooks configs: %w", core.DiagsToError(diags))
- }
- payloadWebHooksConfigs := []argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner{}
- for i := range receiverWebHooksConfigs {
- webHooksConfig := receiverWebHooksConfigs[i]
- payloadWebHooksConfigs = append(payloadWebHooksConfigs, argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner{
- Url: conversion.StringValueToPointer(webHooksConfig.Url),
- MsTeams: conversion.BoolValueToPointer(webHooksConfig.MsTeams),
- })
- }
- receiverPayload.WebHookConfigs = &payloadWebHooksConfigs
- }
-
- receivers = append(receivers, receiverPayload)
- }
- return &receivers, nil
-}
-
-func toRoutePayload(ctx context.Context, routeTF *routeModel) (*argus.UpdateAlertConfigsPayloadRoute, error) {
- if routeTF == nil {
- return nil, fmt.Errorf("nil route model")
- }
-
- var groupByPayload *[]string
- var matchPayload *map[string]interface{}
- var matchRegexPayload *map[string]interface{}
- var childRoutesPayload *[]argus.CreateAlertConfigRoutePayloadRoutesInner
-
- if !routeTF.GroupBy.IsNull() && !routeTF.GroupBy.IsUnknown() {
- groupByPayload = &[]string{}
- diags := routeTF.GroupBy.ElementsAs(ctx, groupByPayload, false)
- if diags.HasError() {
- return nil, fmt.Errorf("mapping group by: %w", core.DiagsToError(diags))
- }
- }
-
- if !routeTF.Match.IsNull() && !routeTF.Match.IsUnknown() {
- matchMap, err := conversion.ToStringInterfaceMap(ctx, routeTF.Match)
- if err != nil {
- return nil, fmt.Errorf("mapping match: %w", err)
- }
- matchPayload = &matchMap
- }
-
- if !routeTF.MatchRegex.IsNull() && !routeTF.MatchRegex.IsUnknown() {
- matchRegexMap, err := conversion.ToStringInterfaceMap(ctx, routeTF.MatchRegex)
- if err != nil {
- return nil, fmt.Errorf("mapping match regex: %w", err)
- }
- matchRegexPayload = &matchRegexMap
- }
-
- if !routeTF.Routes.IsNull() && !routeTF.Routes.IsUnknown() {
- childRoutes := []routeModel{}
- diags := routeTF.Routes.ElementsAs(ctx, &childRoutes, false)
- if diags.HasError() {
- // If there is an error, we will try to map the child routes as if they are the last child routes
- // This is done because the last child routes in the recursion have a different structure (don't have the `routes` fields)
- // and need to be unpacked to a different struct (routeModelNoRoutes)
- lastChildRoutes := []routeModelNoRoutes{}
- diags = routeTF.Routes.ElementsAs(ctx, &lastChildRoutes, true)
- if diags.HasError() {
- return nil, fmt.Errorf("mapping child routes: %w", core.DiagsToError(diags))
- }
- for i := range lastChildRoutes {
- childRoute := routeModel{
- GroupBy: lastChildRoutes[i].GroupBy,
- GroupInterval: lastChildRoutes[i].GroupInterval,
- GroupWait: lastChildRoutes[i].GroupWait,
- Match: lastChildRoutes[i].Match,
- MatchRegex: lastChildRoutes[i].MatchRegex,
- Receiver: lastChildRoutes[i].Receiver,
- RepeatInterval: lastChildRoutes[i].RepeatInterval,
- Routes: types.ListNull(getRouteListType()),
- }
- childRoutes = append(childRoutes, childRoute)
- }
- }
-
- childRoutesList := []argus.CreateAlertConfigRoutePayloadRoutesInner{}
- for i := range childRoutes {
- childRoute := childRoutes[i]
- childRoutePayload, err := toRoutePayload(ctx, &childRoute)
- if err != nil {
- return nil, fmt.Errorf("mapping child route: %w", err)
- }
- childRoutesList = append(childRoutesList, *toChildRoutePayload(childRoutePayload))
- }
-
- childRoutesPayload = &childRoutesList
- }
-
- return &argus.UpdateAlertConfigsPayloadRoute{
- GroupBy: groupByPayload,
- GroupInterval: conversion.StringValueToPointer(routeTF.GroupInterval),
- GroupWait: conversion.StringValueToPointer(routeTF.GroupWait),
- Match: matchPayload,
- MatchRe: matchRegexPayload,
- Receiver: conversion.StringValueToPointer(routeTF.Receiver),
- RepeatInterval: conversion.StringValueToPointer(routeTF.RepeatInterval),
- Routes: childRoutesPayload,
- }, nil
-}
-
-func toChildRoutePayload(in *argus.UpdateAlertConfigsPayloadRoute) *argus.CreateAlertConfigRoutePayloadRoutesInner {
- if in == nil {
- return nil
- }
- return &argus.CreateAlertConfigRoutePayloadRoutesInner{
- GroupBy: in.GroupBy,
- GroupInterval: in.GroupInterval,
- GroupWait: in.GroupWait,
- Match: in.Match,
- MatchRe: in.MatchRe,
- Receiver: in.Receiver,
- RepeatInterval: in.RepeatInterval,
- // Routes not currently supported
- }
-}
-
-func toGlobalConfigPayload(ctx context.Context, model *alertConfigModel) (*argus.UpdateAlertConfigsPayloadGlobal, error) {
- globalConfigModel := globalConfigurationModel{}
- diags := model.GlobalConfiguration.As(ctx, &globalConfigModel, basetypes.ObjectAsOptions{})
- if diags.HasError() {
- return nil, fmt.Errorf("mapping global configuration: %w", core.DiagsToError(diags))
- }
-
- return &argus.UpdateAlertConfigsPayloadGlobal{
- OpsgenieApiKey: conversion.StringValueToPointer(globalConfigModel.OpsgenieApiKey),
- OpsgenieApiUrl: conversion.StringValueToPointer(globalConfigModel.OpsgenieApiUrl),
- ResolveTimeout: conversion.StringValueToPointer(globalConfigModel.ResolveTimeout),
- SmtpAuthIdentity: conversion.StringValueToPointer(globalConfigModel.SmtpAuthIdentity),
- SmtpAuthPassword: conversion.StringValueToPointer(globalConfigModel.SmtpAuthPassword),
- SmtpAuthUsername: conversion.StringValueToPointer(globalConfigModel.SmtpAuthUsername),
- SmtpFrom: conversion.StringValueToPointer(globalConfigModel.SmtpFrom),
- SmtpSmarthost: conversion.StringValueToPointer(globalConfigModel.SmtpSmartHost),
- }, nil
-}
-
-func (r *instanceResource) loadPlanId(ctx context.Context, model *Model) error {
- projectId := model.ProjectId.ValueString()
- res, err := r.client.ListPlans(ctx, projectId).Execute()
- if err != nil {
- return err
- }
-
- planName := model.PlanName.ValueString()
- avl := ""
- plans := *res.Plans
- for i := range plans {
- p := plans[i]
- if p.Name == nil {
- continue
- }
- if strings.EqualFold(*p.Name, planName) && p.PlanId != nil {
- model.PlanId = types.StringPointerValue(p.PlanId)
- break
- }
- avl = fmt.Sprintf("%s\n- %s", avl, *p.Name)
- }
- if model.PlanId.ValueString() == "" {
- return fmt.Errorf("couldn't find plan_name '%s', available names are: %s", planName, avl)
- }
- return nil
-}
-
-func setACL(ctx context.Context, state *tfsdk.State, model *Model) diag.Diagnostics {
- return state.SetAttribute(ctx, path.Root("acl"), model.ACL)
-}
-
-func setMetricsRetentions(ctx context.Context, state *tfsdk.State, model *Model) (diags diag.Diagnostics) {
- diags = append(diags, state.SetAttribute(ctx, path.Root("metrics_retention_days"), model.MetricsRetentionDays)...)
- diags = append(diags, state.SetAttribute(ctx, path.Root("metrics_retention_days_5m_downsampling"), model.MetricsRetentionDays5mDownsampling)...)
- diags = append(diags, state.SetAttribute(ctx, path.Root("metrics_retention_days_1h_downsampling"), model.MetricsRetentionDays1hDownsampling)...)
- return diags
-}
-
-func setAlertConfig(ctx context.Context, state *tfsdk.State, model *Model) diag.Diagnostics {
- return state.SetAttribute(ctx, path.Root("alert_config"), model.AlertConfig)
-}
diff --git a/stackit/internal/services/argus/instance/resource_test.go b/stackit/internal/services/argus/instance/resource_test.go
deleted file mode 100644
index 504a9c70..00000000
--- a/stackit/internal/services/argus/instance/resource_test.go
+++ /dev/null
@@ -1,1562 +0,0 @@
-package argus
-
-import (
- "context"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
- "github.com/hashicorp/terraform-plugin-framework/attr"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
- "github.com/hashicorp/terraform-plugin-framework/schema/validator"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
- "github.com/stackitcloud/stackit-sdk-go/core/utils"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
-)
-
-func fixtureEmailConfigsModel() basetypes.ListValue {
- return types.ListValueMust(types.ObjectType{AttrTypes: emailConfigsTypes}, []attr.Value{
- types.ObjectValueMust(emailConfigsTypes, map[string]attr.Value{
- "auth_identity": types.StringValue("identity"),
- "auth_password": types.StringValue("password"),
- "auth_username": types.StringValue("username"),
- "from": types.StringValue("notification@example.com"),
- "smart_host": types.StringValue("smtp.example.com"),
- "to": types.StringValue("me@example.com"),
- }),
- })
-}
-
-func fixtureOpsGenieConfigsModel() basetypes.ListValue {
- return types.ListValueMust(types.ObjectType{AttrTypes: opsgenieConfigsTypes}, []attr.Value{
- types.ObjectValueMust(opsgenieConfigsTypes, map[string]attr.Value{
- "api_key": types.StringValue("key"),
- "tags": types.StringValue("tag"),
- "api_url": types.StringValue("ops.example.com"),
- }),
- })
-}
-
-func fixtureWebHooksConfigsModel() basetypes.ListValue {
- return types.ListValueMust(types.ObjectType{AttrTypes: webHooksConfigsTypes}, []attr.Value{
- types.ObjectValueMust(webHooksConfigsTypes, map[string]attr.Value{
- "url": types.StringValue("http://example.com"),
- "ms_teams": types.BoolValue(true),
- }),
- })
-}
-
-func fixtureReceiverModel(emailConfigs, opsGenieConfigs, webHooksConfigs basetypes.ListValue) basetypes.ObjectValue {
- return types.ObjectValueMust(receiversTypes, map[string]attr.Value{
- "name": types.StringValue("name"),
- "email_configs": emailConfigs,
- "opsgenie_configs": opsGenieConfigs,
- "webhooks_configs": webHooksConfigs,
- })
-}
-
-func fixtureRouteModel() basetypes.ObjectValue {
- return types.ObjectValueMust(routeTypes, map[string]attr.Value{
- "group_by": types.ListValueMust(types.StringType, []attr.Value{
- types.StringValue("label1"),
- types.StringValue("label2"),
- }),
- "group_interval": types.StringValue("1m"),
- "group_wait": types.StringValue("1m"),
- "match": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}),
- "match_regex": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}),
- "receiver": types.StringValue("name"),
- "repeat_interval": types.StringValue("1m"),
- // "routes": types.ListNull(getRouteListType()),
- "routes": types.ListValueMust(getRouteListType(), []attr.Value{
- types.ObjectValueMust(getRouteListType().AttrTypes, map[string]attr.Value{
- "group_by": types.ListValueMust(types.StringType, []attr.Value{
- types.StringValue("label1"),
- types.StringValue("label2"),
- }),
- "group_interval": types.StringValue("1m"),
- "group_wait": types.StringValue("1m"),
- "match": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}),
- "match_regex": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}),
- "receiver": types.StringValue("name"),
- "repeat_interval": types.StringValue("1m"),
- }),
- }),
- })
-}
-
-func fixtureNullRouteModel() basetypes.ObjectValue {
- return types.ObjectValueMust(routeTypes, map[string]attr.Value{
- "group_by": types.ListNull(types.StringType),
- "group_interval": types.StringNull(),
- "group_wait": types.StringNull(),
- "match": types.MapNull(types.StringType),
- "match_regex": types.MapNull(types.StringType),
- "receiver": types.StringNull(),
- "repeat_interval": types.StringNull(),
- "routes": types.ListNull(getRouteListType()),
- })
-}
-
-func fixtureGlobalConfigModel() basetypes.ObjectValue {
- return types.ObjectValueMust(globalConfigurationTypes, map[string]attr.Value{
- "opsgenie_api_key": types.StringValue("key"),
- "opsgenie_api_url": types.StringValue("ops.example.com"),
- "resolve_timeout": types.StringValue("1m"),
- "smtp_auth_identity": types.StringValue("identity"),
- "smtp_auth_username": types.StringValue("username"),
- "smtp_auth_password": types.StringValue("password"),
- "smtp_from": types.StringValue("me@example.com"),
- "smtp_smart_host": types.StringValue("smtp.example.com:25"),
- })
-}
-
-func fixtureNullGlobalConfigModel() basetypes.ObjectValue {
- return types.ObjectValueMust(globalConfigurationTypes, map[string]attr.Value{
- "opsgenie_api_key": types.StringNull(),
- "opsgenie_api_url": types.StringNull(),
- "resolve_timeout": types.StringNull(),
- "smtp_auth_identity": types.StringNull(),
- "smtp_auth_username": types.StringNull(),
- "smtp_auth_password": types.StringNull(),
- "smtp_from": types.StringNull(),
- "smtp_smart_host": types.StringNull(),
- })
-}
-
-func fixtureEmailConfigsPayload() argus.CreateAlertConfigReceiverPayloadEmailConfigsInner {
- return argus.CreateAlertConfigReceiverPayloadEmailConfigsInner{
- AuthIdentity: utils.Ptr("identity"),
- AuthPassword: utils.Ptr("password"),
- AuthUsername: utils.Ptr("username"),
- From: utils.Ptr("notification@example.com"),
- Smarthost: utils.Ptr("smtp.example.com"),
- To: utils.Ptr("me@example.com"),
- }
-}
-
-func fixtureOpsGenieConfigsPayload() argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner {
- return argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner{
- ApiKey: utils.Ptr("key"),
- Tags: utils.Ptr("tag"),
- ApiUrl: utils.Ptr("ops.example.com"),
- }
-}
-
-func fixtureWebHooksConfigsPayload() argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner {
- return argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner{
- Url: utils.Ptr("http://example.com"),
- MsTeams: utils.Ptr(true),
- }
-}
-
-func fixtureReceiverPayload(emailConfigs *[]argus.CreateAlertConfigReceiverPayloadEmailConfigsInner, opsGenieConfigs *[]argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner, webHooksConfigs *[]argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner) argus.UpdateAlertConfigsPayloadReceiversInner {
- return argus.UpdateAlertConfigsPayloadReceiversInner{
- EmailConfigs: emailConfigs,
- Name: utils.Ptr("name"),
- OpsgenieConfigs: opsGenieConfigs,
- WebHookConfigs: webHooksConfigs,
- }
-}
-
-func fixtureRoutePayload() *argus.UpdateAlertConfigsPayloadRoute {
- return &argus.UpdateAlertConfigsPayloadRoute{
- GroupBy: utils.Ptr([]string{"label1", "label2"}),
- GroupInterval: utils.Ptr("1m"),
- GroupWait: utils.Ptr("1m"),
- Match: &map[string]interface{}{"key": "value"},
- MatchRe: &map[string]interface{}{"key": "value"},
- Receiver: utils.Ptr("name"),
- RepeatInterval: utils.Ptr("1m"),
- Routes: &[]argus.CreateAlertConfigRoutePayloadRoutesInner{
- {
- GroupBy: utils.Ptr([]string{"label1", "label2"}),
- GroupInterval: utils.Ptr("1m"),
- GroupWait: utils.Ptr("1m"),
- Match: &map[string]interface{}{"key": "value"},
- MatchRe: &map[string]interface{}{"key": "value"},
- Receiver: utils.Ptr("name"),
- RepeatInterval: utils.Ptr("1m"),
- },
- },
- }
-}
-
-func fixtureGlobalConfigPayload() *argus.UpdateAlertConfigsPayloadGlobal {
- return &argus.UpdateAlertConfigsPayloadGlobal{
- OpsgenieApiKey: utils.Ptr("key"),
- OpsgenieApiUrl: utils.Ptr("ops.example.com"),
- ResolveTimeout: utils.Ptr("1m"),
- SmtpAuthIdentity: utils.Ptr("identity"),
- SmtpAuthUsername: utils.Ptr("username"),
- SmtpAuthPassword: utils.Ptr("password"),
- SmtpFrom: utils.Ptr("me@example.com"),
- SmtpSmarthost: utils.Ptr("smtp.example.com:25"),
- }
-}
-
-func fixtureReceiverResponse(emailConfigs *[]argus.EmailConfig, opsGenieConfigs *[]argus.OpsgenieConfig, webhookConfigs *[]argus.WebHook) argus.Receivers {
- return argus.Receivers{
- Name: utils.Ptr("name"),
- EmailConfigs: emailConfigs,
- OpsgenieConfigs: opsGenieConfigs,
- WebHookConfigs: webhookConfigs,
- }
-}
-
-func fixtureEmailConfigsResponse() argus.EmailConfig {
- return argus.EmailConfig{
- AuthIdentity: utils.Ptr("identity"),
- AuthPassword: utils.Ptr("password"),
- AuthUsername: utils.Ptr("username"),
- From: utils.Ptr("notification@example.com"),
- Smarthost: utils.Ptr("smtp.example.com"),
- To: utils.Ptr("me@example.com"),
- }
-}
-
-func fixtureOpsGenieConfigsResponse() argus.OpsgenieConfig {
- return argus.OpsgenieConfig{
- ApiKey: utils.Ptr("key"),
- Tags: utils.Ptr("tag"),
- ApiUrl: utils.Ptr("ops.example.com"),
- }
-}
-
-func fixtureWebHooksConfigsResponse() argus.WebHook {
- return argus.WebHook{
- Url: utils.Ptr("http://example.com"),
- MsTeams: utils.Ptr(true),
- }
-}
-
-func fixtureRouteResponse() *argus.Route {
- return &argus.Route{
- GroupBy: utils.Ptr([]string{"label1", "label2"}),
- GroupInterval: utils.Ptr("1m"),
- GroupWait: utils.Ptr("1m"),
- Match: &map[string]string{"key": "value"},
- MatchRe: &map[string]string{"key": "value"},
- Receiver: utils.Ptr("name"),
- RepeatInterval: utils.Ptr("1m"),
- Routes: &[]argus.RouteSerializer{
- {
- GroupBy: utils.Ptr([]string{"label1", "label2"}),
- GroupInterval: utils.Ptr("1m"),
- GroupWait: utils.Ptr("1m"),
- Match: &map[string]string{"key": "value"},
- MatchRe: &map[string]string{"key": "value"},
- Receiver: utils.Ptr("name"),
- RepeatInterval: utils.Ptr("1m"),
- },
- },
- }
-}
-
-func fixtureGlobalConfigResponse() *argus.Global {
- return &argus.Global{
- OpsgenieApiKey: utils.Ptr("key"),
- OpsgenieApiUrl: utils.Ptr("ops.example.com"),
- ResolveTimeout: utils.Ptr("1m"),
- SmtpAuthIdentity: utils.Ptr("identity"),
- SmtpAuthUsername: utils.Ptr("username"),
- SmtpAuthPassword: utils.Ptr("password"),
- SmtpFrom: utils.Ptr("me@example.com"),
- SmtpSmarthost: utils.Ptr("smtp.example.com:25"),
- }
-}
-
-func fixtureRouteAttributeSchema(route *schema.ListNestedAttribute, isDatasource bool) map[string]schema.Attribute {
- attributeMap := map[string]schema.Attribute{
- "group_by": schema.ListAttribute{
- Description: routeDescriptions["group_by"],
- Optional: !isDatasource,
- Computed: isDatasource,
- ElementType: types.StringType,
- },
- "group_interval": schema.StringAttribute{
- Description: routeDescriptions["group_interval"],
- Optional: !isDatasource,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "group_wait": schema.StringAttribute{
- Description: routeDescriptions["group_wait"],
- Optional: !isDatasource,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "match": schema.MapAttribute{
- Description: routeDescriptions["match"],
- Optional: !isDatasource,
- Computed: isDatasource,
- ElementType: types.StringType,
- },
- "match_regex": schema.MapAttribute{
- Description: routeDescriptions["match_regex"],
- Optional: !isDatasource,
- Computed: isDatasource,
- ElementType: types.StringType,
- },
- "receiver": schema.StringAttribute{
- Description: routeDescriptions["receiver"],
- Required: !isDatasource,
- Computed: isDatasource,
- },
- "repeat_interval": schema.StringAttribute{
- Description: routeDescriptions["repeat_interval"],
- Optional: !isDatasource,
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- }
- if route != nil {
- attributeMap["routes"] = *route
- }
- return attributeMap
-}
-
-func TestMapFields(t *testing.T) {
- tests := []struct {
- description string
- instanceResp *argus.GetInstanceResponse
- listACLResp *argus.ListACLResponse
- getMetricsRetentionResp *argus.GetMetricsStorageRetentionResponse
- expected Model
- isValid bool
- }{
- {
- "default_ok",
- &argus.GetInstanceResponse{
- Id: utils.Ptr("iid"),
- },
- &argus.ListACLResponse{},
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- Model{
- Id: types.StringValue("pid,iid"),
- ProjectId: types.StringValue("pid"),
- InstanceId: types.StringValue("iid"),
- PlanId: types.StringNull(),
- PlanName: types.StringNull(),
- Name: types.StringNull(),
- Parameters: types.MapNull(types.StringType),
- ACL: types.SetNull(types.StringType),
- MetricsRetentionDays: types.Int64Value(60),
- MetricsRetentionDays1hDownsampling: types.Int64Value(30),
- MetricsRetentionDays5mDownsampling: types.Int64Value(7),
- },
- true,
- },
- {
- "values_ok",
- &argus.GetInstanceResponse{
- Id: utils.Ptr("iid"),
- Name: utils.Ptr("name"),
- PlanName: utils.Ptr("plan1"),
- PlanId: utils.Ptr("planId"),
- Parameters: &map[string]string{"key": "value"},
- Instance: &argus.InstanceSensitiveData{
- MetricsRetentionTimeRaw: utils.Ptr(int64(60)),
- MetricsRetentionTime1h: utils.Ptr(int64(30)),
- MetricsRetentionTime5m: utils.Ptr(int64(7)),
- },
- },
- &argus.ListACLResponse{
- Acl: &[]string{
- "1.1.1.1/32",
- },
- Message: utils.Ptr("message"),
- },
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- Model{
- Id: types.StringValue("pid,iid"),
- ProjectId: types.StringValue("pid"),
- Name: types.StringValue("name"),
- InstanceId: types.StringValue("iid"),
- PlanId: types.StringValue("planId"),
- PlanName: types.StringValue("plan1"),
- Parameters: toTerraformStringMapMust(context.Background(), map[string]string{"key": "value"}),
- ACL: types.SetValueMust(types.StringType, []attr.Value{
- types.StringValue("1.1.1.1/32"),
- }),
- MetricsRetentionDays: types.Int64Value(60),
- MetricsRetentionDays1hDownsampling: types.Int64Value(30),
- MetricsRetentionDays5mDownsampling: types.Int64Value(7),
- },
- true,
- },
- {
- "values_ok_multiple_acls",
- &argus.GetInstanceResponse{
- Id: utils.Ptr("iid"),
- Name: utils.Ptr("name"),
- PlanName: utils.Ptr("plan1"),
- PlanId: utils.Ptr("planId"),
- Parameters: &map[string]string{"key": "value"},
- },
- &argus.ListACLResponse{
- Acl: &[]string{
- "1.1.1.1/32",
- "8.8.8.8/32",
- },
- Message: utils.Ptr("message"),
- },
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- Model{
- Id: types.StringValue("pid,iid"),
- ProjectId: types.StringValue("pid"),
- Name: types.StringValue("name"),
- InstanceId: types.StringValue("iid"),
- PlanId: types.StringValue("planId"),
- PlanName: types.StringValue("plan1"),
- Parameters: toTerraformStringMapMust(context.Background(), map[string]string{"key": "value"}),
- ACL: types.SetValueMust(types.StringType, []attr.Value{
- types.StringValue("1.1.1.1/32"),
- types.StringValue("8.8.8.8/32"),
- }),
- MetricsRetentionDays: types.Int64Value(60),
- MetricsRetentionDays1hDownsampling: types.Int64Value(30),
- MetricsRetentionDays5mDownsampling: types.Int64Value(7),
- },
- true,
- },
- {
- "nullable_fields_ok",
- &argus.GetInstanceResponse{
- Id: utils.Ptr("iid"),
- Name: nil,
- },
- &argus.ListACLResponse{
- Acl: &[]string{},
- Message: nil,
- },
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- Model{
- Id: types.StringValue("pid,iid"),
- ProjectId: types.StringValue("pid"),
- InstanceId: types.StringValue("iid"),
- PlanId: types.StringNull(),
- PlanName: types.StringNull(),
- Name: types.StringNull(),
- Parameters: types.MapNull(types.StringType),
- ACL: types.SetNull(types.StringType),
- MetricsRetentionDays: types.Int64Value(60),
- MetricsRetentionDays1hDownsampling: types.Int64Value(30),
- MetricsRetentionDays5mDownsampling: types.Int64Value(7),
- },
- true,
- },
- {
- "response_nil_fail",
- nil,
- nil,
- nil,
- Model{},
- false,
- },
- {
- "no_resource_id",
- &argus.GetInstanceResponse{},
- nil,
- nil,
- Model{},
- false,
- },
- {
- "empty metrics retention",
- &argus.GetInstanceResponse{
- Id: utils.Ptr("iid"),
- Name: nil,
- },
- &argus.ListACLResponse{
- Acl: &[]string{},
- Message: nil,
- },
- &argus.GetMetricsStorageRetentionResponse{},
- Model{},
- false,
- },
- {
- "nil metrics retention",
- &argus.GetInstanceResponse{
- Id: utils.Ptr("iid"),
- Name: nil,
- },
- &argus.ListACLResponse{
- Acl: &[]string{},
- Message: nil,
- },
- nil,
- Model{},
- false,
- },
- {
- "update metrics retention",
- &argus.GetInstanceResponse{
- Id: utils.Ptr("iid"),
- Name: utils.Ptr("name"),
- PlanName: utils.Ptr("plan1"),
- PlanId: utils.Ptr("planId"),
- Parameters: &map[string]string{"key": "value"},
- Instance: &argus.InstanceSensitiveData{
- MetricsRetentionTimeRaw: utils.Ptr(int64(30)),
- MetricsRetentionTime1h: utils.Ptr(int64(15)),
- MetricsRetentionTime5m: utils.Ptr(int64(10)),
- },
- },
- &argus.ListACLResponse{
- Acl: &[]string{
- "1.1.1.1/32",
- },
- Message: utils.Ptr("message"),
- },
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- Model{
- Id: types.StringValue("pid,iid"),
- ProjectId: types.StringValue("pid"),
- Name: types.StringValue("name"),
- InstanceId: types.StringValue("iid"),
- PlanId: types.StringValue("planId"),
- PlanName: types.StringValue("plan1"),
- Parameters: toTerraformStringMapMust(context.Background(), map[string]string{"key": "value"}),
- ACL: types.SetValueMust(types.StringType, []attr.Value{
- types.StringValue("1.1.1.1/32"),
- }),
- MetricsRetentionDays: types.Int64Value(60),
- MetricsRetentionDays1hDownsampling: types.Int64Value(30),
- MetricsRetentionDays5mDownsampling: types.Int64Value(7),
- },
- true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- state := &Model{
- ProjectId: tt.expected.ProjectId,
- ACL: types.SetNull(types.StringType),
- }
- err := mapFields(context.Background(), tt.instanceResp, state)
- aclErr := mapACLField(tt.listACLResp, state)
- metricsErr := mapMetricsRetentionField(tt.getMetricsRetentionResp, state)
- if !tt.isValid && err == nil && aclErr == nil && metricsErr == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && (err != nil || aclErr != nil || metricsErr != nil) {
- t.Fatalf("Should not have failed: %v", err)
- }
-
- if tt.isValid {
- diff := cmp.Diff(state, &tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
-
-func TestMapAlertConfigField(t *testing.T) {
- tests := []struct {
- description string
- alertConfigResp *argus.GetAlertConfigsResponse
- expected Model
- isValid bool
- }{
- {
- description: "basic_ok",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{
- fixtureReceiverResponse(
- &[]argus.EmailConfig{
- fixtureEmailConfigsResponse(),
- },
- &[]argus.OpsgenieConfig{
- fixtureOpsGenieConfigsResponse(),
- },
- &[]argus.WebHook{
- fixtureWebHooksConfigsResponse(),
- },
- ),
- },
- Route: fixtureRouteResponse(),
- Global: fixtureGlobalConfigResponse(),
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- fixtureOpsGenieConfigsModel(),
- fixtureWebHooksConfigsModel(),
- ),
- }),
- "route": fixtureRouteModel(),
- "global": fixtureGlobalConfigModel(),
- }),
- },
- isValid: true,
- },
- {
- description: "receivers only emailconfigs",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{
- fixtureReceiverResponse(
- &[]argus.EmailConfig{
- fixtureEmailConfigsResponse(),
- },
- nil,
- nil,
- ),
- },
- Route: fixtureRouteResponse(),
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- types.ListNull(types.ObjectType{AttrTypes: opsgenieConfigsTypes}),
- types.ListNull(types.ObjectType{AttrTypes: webHooksConfigsTypes}),
- ),
- }),
- "route": fixtureRouteModel(),
- "global": types.ObjectNull(globalConfigurationTypes),
- }),
- },
- isValid: true,
- },
- {
- description: "receivers only opsgenieconfigs",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{
- fixtureReceiverResponse(
- nil,
- &[]argus.OpsgenieConfig{
- fixtureOpsGenieConfigsResponse(),
- },
- nil,
- ),
- },
- Route: fixtureRouteResponse(),
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- types.ListNull(types.ObjectType{AttrTypes: emailConfigsTypes}),
- fixtureOpsGenieConfigsModel(),
- types.ListNull(types.ObjectType{AttrTypes: webHooksConfigsTypes}),
- ),
- }),
- "route": fixtureRouteModel(),
- "global": types.ObjectNull(globalConfigurationTypes),
- }),
- },
- isValid: true,
- },
- {
- description: "receivers only webhooksconfigs",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{
- fixtureReceiverResponse(
- nil,
- nil,
- &[]argus.WebHook{
- fixtureWebHooksConfigsResponse(),
- },
- ),
- },
- Route: fixtureRouteResponse(),
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- types.ListNull(types.ObjectType{AttrTypes: emailConfigsTypes}),
- types.ListNull(types.ObjectType{AttrTypes: opsgenieConfigsTypes}),
- fixtureWebHooksConfigsModel(),
- ),
- }),
- "route": fixtureRouteModel(),
- "global": types.ObjectNull(globalConfigurationTypes),
- }),
- },
- isValid: true,
- },
- {
- description: "no receivers, no routes",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{},
- Route: &argus.Route{},
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{}),
- "route": fixtureNullRouteModel(),
- "global": types.ObjectNull(globalConfigurationTypes),
- }),
- },
- isValid: true,
- },
- {
- description: "no receivers, default routes",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{},
- Route: fixtureRouteResponse(),
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{}),
- "route": fixtureRouteModel(),
- "global": types.ObjectNull(globalConfigurationTypes),
- }),
- },
- isValid: true,
- },
- {
- description: "default receivers, no routes",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{
- fixtureReceiverResponse(
- &[]argus.EmailConfig{
- fixtureEmailConfigsResponse(),
- },
- &[]argus.OpsgenieConfig{
- fixtureOpsGenieConfigsResponse(),
- },
- &[]argus.WebHook{
- fixtureWebHooksConfigsResponse(),
- },
- ),
- },
- Route: &argus.Route{},
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- fixtureOpsGenieConfigsModel(),
- fixtureWebHooksConfigsModel(),
- ),
- }),
- "route": fixtureNullRouteModel(),
- "global": types.ObjectNull(globalConfigurationTypes),
- }),
- },
- isValid: true,
- },
- {
- description: "nil receivers",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: nil,
- Route: fixtureRouteResponse(),
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListNull(types.ObjectType{AttrTypes: receiversTypes}),
- "route": fixtureRouteModel(),
- "global": types.ObjectNull(globalConfigurationTypes),
- }),
- },
- isValid: true,
- },
- {
- description: "nil route",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{
- fixtureReceiverResponse(
- &[]argus.EmailConfig{
- fixtureEmailConfigsResponse(),
- },
- &[]argus.OpsgenieConfig{
- fixtureOpsGenieConfigsResponse(),
- },
- &[]argus.WebHook{
- fixtureWebHooksConfigsResponse(),
- },
- ),
- },
- Route: nil,
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- fixtureOpsGenieConfigsModel(),
- fixtureWebHooksConfigsModel(),
- ),
- }),
- "route": types.ObjectNull(routeTypes),
- "global": types.ObjectNull(globalConfigurationTypes),
- }),
- },
- isValid: true,
- },
- {
- description: "empty global options",
- alertConfigResp: &argus.GetAlertConfigsResponse{
- Data: &argus.Alert{
- Receivers: &[]argus.Receivers{
- fixtureReceiverResponse(
- &[]argus.EmailConfig{
- fixtureEmailConfigsResponse(),
- },
- &[]argus.OpsgenieConfig{
- fixtureOpsGenieConfigsResponse(),
- },
- &[]argus.WebHook{
- fixtureWebHooksConfigsResponse(),
- },
- ),
- },
- Route: fixtureRouteResponse(),
- Global: &argus.Global{},
- },
- },
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectValueMust(alertConfigTypes, map[string]attr.Value{
- "receivers": types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- fixtureOpsGenieConfigsModel(),
- fixtureWebHooksConfigsModel(),
- ),
- }),
- "route": fixtureRouteModel(),
- "global": fixtureNullGlobalConfigModel(),
- }),
- },
- isValid: true,
- },
- {
- description: "nil resp",
- alertConfigResp: nil,
- expected: Model{
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- AlertConfig: types.ObjectNull(receiversTypes),
- },
- isValid: true,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- state := &Model{
- ProjectId: tt.expected.ProjectId,
- ACL: types.SetNull(types.StringType),
- Parameters: types.MapNull(types.StringType),
- }
- err := mapAlertConfigField(context.Background(), tt.alertConfigResp, state)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
-
- if tt.isValid {
- diff := cmp.Diff(state.AlertConfig, tt.expected.AlertConfig)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
-
-func TestToCreatePayload(t *testing.T) {
- tests := []struct {
- description string
- input *Model
- expected *argus.CreateInstancePayload
- isValid bool
- }{
- {
- "basic_ok",
- &Model{
- PlanId: types.StringValue("planId"),
- },
- &argus.CreateInstancePayload{
- Name: nil,
- PlanId: utils.Ptr("planId"),
- Parameter: &map[string]interface{}{},
- },
- true,
- },
- {
- "ok",
- &Model{
- Name: types.StringValue("Name"),
- PlanId: types.StringValue("planId"),
- Parameters: makeTestMap(t),
- },
- &argus.CreateInstancePayload{
- Name: utils.Ptr("Name"),
- PlanId: utils.Ptr("planId"),
- Parameter: &map[string]interface{}{"key": `"value"`},
- },
- true,
- },
- {
- "nil_model",
- nil,
- nil,
- false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- output, err := toCreatePayload(tt.input)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
- if tt.isValid {
- diff := cmp.Diff(output, tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
-
-func TestToPayloadUpdate(t *testing.T) {
- tests := []struct {
- description string
- input *Model
- expected *argus.UpdateInstancePayload
- isValid bool
- }{
- {
- "basic_ok",
- &Model{
- PlanId: types.StringValue("planId"),
- },
- &argus.UpdateInstancePayload{
- Name: nil,
- PlanId: utils.Ptr("planId"),
- Parameter: &map[string]any{},
- },
- true,
- },
- {
- "ok",
- &Model{
- Name: types.StringValue("Name"),
- PlanId: types.StringValue("planId"),
- Parameters: makeTestMap(t),
- },
- &argus.UpdateInstancePayload{
- Name: utils.Ptr("Name"),
- PlanId: utils.Ptr("planId"),
- Parameter: &map[string]any{"key": `"value"`},
- },
- true,
- },
- {
- "nil_model",
- nil,
- nil,
- false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- output, err := toUpdatePayload(tt.input)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
- if tt.isValid {
- diff := cmp.Diff(output, tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
-
-func TestToUpdateMetricsStorageRetentionPayload(t *testing.T) {
- tests := []struct {
- description string
- retentionDaysRaw *int64
- retentionDays1h *int64
- retentionDays5m *int64
- getMetricsResp *argus.GetMetricsStorageRetentionResponse
- expected *argus.UpdateMetricsStorageRetentionPayload
- isValid bool
- }{
- {
- "basic_ok",
- utils.Ptr(int64(120)),
- utils.Ptr(int64(60)),
- utils.Ptr(int64(14)),
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- &argus.UpdateMetricsStorageRetentionPayload{
- MetricsRetentionTimeRaw: utils.Ptr("120d"),
- MetricsRetentionTime1h: utils.Ptr("60d"),
- MetricsRetentionTime5m: utils.Ptr("14d"),
- },
- true,
- },
- {
- "only_raw_given",
- utils.Ptr(int64(120)),
- nil,
- nil,
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- &argus.UpdateMetricsStorageRetentionPayload{
- MetricsRetentionTimeRaw: utils.Ptr("120d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- true,
- },
- {
- "only_1h_given",
- nil,
- utils.Ptr(int64(60)),
- nil,
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- &argus.UpdateMetricsStorageRetentionPayload{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("60d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- true,
- },
- {
- "only_5m_given",
- nil,
- nil,
- utils.Ptr(int64(14)),
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- &argus.UpdateMetricsStorageRetentionPayload{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("14d"),
- },
- true,
- },
- {
- "none_given",
- nil,
- nil,
- nil,
- &argus.GetMetricsStorageRetentionResponse{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- &argus.UpdateMetricsStorageRetentionPayload{
- MetricsRetentionTimeRaw: utils.Ptr("60d"),
- MetricsRetentionTime1h: utils.Ptr("30d"),
- MetricsRetentionTime5m: utils.Ptr("7d"),
- },
- true,
- },
- {
- "nil_response",
- nil,
- nil,
- nil,
- nil,
- nil,
- false,
- },
- {
- "empty_response",
- nil,
- nil,
- nil,
- &argus.GetMetricsStorageRetentionResponse{},
- nil,
- false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- output, err := toUpdateMetricsStorageRetentionPayload(tt.retentionDaysRaw, tt.retentionDays5m, tt.retentionDays1h, tt.getMetricsResp)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
- if tt.isValid {
- diff := cmp.Diff(output, tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
-
-func TestToUpdateAlertConfigPayload(t *testing.T) {
- tests := []struct {
- description string
- input alertConfigModel
- expected *argus.UpdateAlertConfigsPayload
- isValid bool
- }{
- {
- description: "base",
- input: alertConfigModel{
- Receivers: types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- fixtureOpsGenieConfigsModel(),
- fixtureWebHooksConfigsModel(),
- ),
- }),
- Route: fixtureRouteModel(),
- GlobalConfiguration: fixtureGlobalConfigModel(),
- },
- expected: &argus.UpdateAlertConfigsPayload{
- Receivers: &[]argus.UpdateAlertConfigsPayloadReceiversInner{
- fixtureReceiverPayload(
- &[]argus.CreateAlertConfigReceiverPayloadEmailConfigsInner{fixtureEmailConfigsPayload()},
- &[]argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner{fixtureOpsGenieConfigsPayload()},
- &[]argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner{fixtureWebHooksConfigsPayload()},
- ),
- },
- Route: fixtureRoutePayload(),
- Global: fixtureGlobalConfigPayload(),
- },
- isValid: true,
- },
- {
- description: "receivers only emailconfigs",
- input: alertConfigModel{
- Receivers: types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- types.ListNull(types.ObjectType{AttrTypes: opsgenieConfigsTypes}),
- types.ListNull(types.ObjectType{AttrTypes: webHooksConfigsTypes}),
- ),
- }),
- Route: fixtureRouteModel(),
- },
- expected: &argus.UpdateAlertConfigsPayload{
- Receivers: &[]argus.UpdateAlertConfigsPayloadReceiversInner{
- fixtureReceiverPayload(
- &[]argus.CreateAlertConfigReceiverPayloadEmailConfigsInner{fixtureEmailConfigsPayload()},
- nil,
- nil,
- ),
- },
- Route: fixtureRoutePayload(),
- },
- isValid: true,
- },
- {
- description: "receivers only opsgenieconfigs",
- input: alertConfigModel{
- Receivers: types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- types.ListNull(types.ObjectType{AttrTypes: emailConfigsTypes}),
- fixtureOpsGenieConfigsModel(),
- types.ListNull(types.ObjectType{AttrTypes: webHooksConfigsTypes}),
- ),
- }),
- Route: fixtureRouteModel(),
- },
- expected: &argus.UpdateAlertConfigsPayload{
- Receivers: &[]argus.UpdateAlertConfigsPayloadReceiversInner{
- fixtureReceiverPayload(
- nil,
- &[]argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner{fixtureOpsGenieConfigsPayload()},
- nil,
- ),
- },
- Route: fixtureRoutePayload(),
- },
- isValid: true,
- },
- {
- description: "multiple receivers",
- input: alertConfigModel{
- Receivers: types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- fixtureOpsGenieConfigsModel(),
- fixtureWebHooksConfigsModel(),
- ),
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- fixtureOpsGenieConfigsModel(),
- fixtureWebHooksConfigsModel(),
- ),
- }),
- Route: fixtureRouteModel(),
- },
- expected: &argus.UpdateAlertConfigsPayload{
- Receivers: &[]argus.UpdateAlertConfigsPayloadReceiversInner{
- fixtureReceiverPayload(
- &[]argus.CreateAlertConfigReceiverPayloadEmailConfigsInner{fixtureEmailConfigsPayload()},
- &[]argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner{fixtureOpsGenieConfigsPayload()},
- &[]argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner{fixtureWebHooksConfigsPayload()},
- ),
- fixtureReceiverPayload(
- &[]argus.CreateAlertConfigReceiverPayloadEmailConfigsInner{fixtureEmailConfigsPayload()},
- &[]argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner{fixtureOpsGenieConfigsPayload()},
- &[]argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner{fixtureWebHooksConfigsPayload()},
- ),
- },
- Route: fixtureRoutePayload(),
- },
- isValid: true,
- },
- {
- description: "empty global options",
- input: alertConfigModel{
- Receivers: types.ListValueMust(types.ObjectType{AttrTypes: receiversTypes}, []attr.Value{
- fixtureReceiverModel(
- fixtureEmailConfigsModel(),
- fixtureOpsGenieConfigsModel(),
- fixtureWebHooksConfigsModel(),
- ),
- }),
- Route: fixtureRouteModel(),
- GlobalConfiguration: fixtureNullGlobalConfigModel(),
- },
- expected: &argus.UpdateAlertConfigsPayload{
- Receivers: &[]argus.UpdateAlertConfigsPayloadReceiversInner{
- fixtureReceiverPayload(
- &[]argus.CreateAlertConfigReceiverPayloadEmailConfigsInner{fixtureEmailConfigsPayload()},
- &[]argus.CreateAlertConfigReceiverPayloadOpsgenieConfigsInner{fixtureOpsGenieConfigsPayload()},
- &[]argus.CreateAlertConfigReceiverPayloadWebHookConfigsInner{fixtureWebHooksConfigsPayload()},
- ),
- },
- Route: fixtureRoutePayload(),
- Global: &argus.UpdateAlertConfigsPayloadGlobal{},
- },
- isValid: true,
- },
- {
- description: "empty alert config",
- input: alertConfigModel{},
- isValid: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- output, err := toUpdateAlertConfigPayload(context.Background(), &tt.input)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
- if tt.isValid {
- diff := cmp.Diff(output, tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
-
-func TestGetRouteNestedObjectAux(t *testing.T) {
- tests := []struct {
- description string
- startingLevel int
- recursionLimit int
- isDatasource bool
- expected schema.ListNestedAttribute
- }{
- {
- "no recursion, resource",
- 1,
- 1,
- false,
- schema.ListNestedAttribute{
- Description: routeDescriptions["routes"],
- Optional: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: fixtureRouteAttributeSchema(nil, false),
- },
- },
- },
- {
- "recursion 1, resource",
- 1,
- 2,
- false,
- schema.ListNestedAttribute{
- Description: routeDescriptions["routes"],
- Optional: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: fixtureRouteAttributeSchema(
- &schema.ListNestedAttribute{
- Description: routeDescriptions["routes"],
- Optional: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: fixtureRouteAttributeSchema(nil, false),
- },
- },
- false,
- ),
- },
- },
- },
- {
- "no recursion,datasource",
- 1,
- 1,
- true,
- schema.ListNestedAttribute{
- Description: routeDescriptions["routes"],
- Computed: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: fixtureRouteAttributeSchema(nil, true),
- },
- },
- },
- {
- "recursion 1, datasource",
- 1,
- 2,
- true,
- schema.ListNestedAttribute{
- Description: routeDescriptions["routes"],
- Computed: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: fixtureRouteAttributeSchema(
- &schema.ListNestedAttribute{
- Description: routeDescriptions["routes"],
- Computed: true,
- Validators: []validator.List{
- listvalidator.SizeAtLeast(1),
- },
- NestedObject: schema.NestedAttributeObject{
- Attributes: fixtureRouteAttributeSchema(nil, true),
- },
- },
- true,
- ),
- },
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- output := getRouteNestedObjectAux(tt.isDatasource, tt.startingLevel, tt.recursionLimit)
- diff := cmp.Diff(output, tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- })
- }
-}
-
-func TestGetRouteListTypeAux(t *testing.T) {
- tests := []struct {
- description string
- startingLevel int
- recursionLimit int
- expected types.ObjectType
- }{
- {
- "no recursion",
- 1,
- 1,
- types.ObjectType{
- AttrTypes: map[string]attr.Type{
- "group_by": types.ListType{ElemType: types.StringType},
- "group_interval": types.StringType,
- "group_wait": types.StringType,
- "match": types.MapType{ElemType: types.StringType},
- "match_regex": types.MapType{ElemType: types.StringType},
- "receiver": types.StringType,
- "repeat_interval": types.StringType,
- },
- },
- },
- {
- "recursion 1",
- 1,
- 2,
- types.ObjectType{
- AttrTypes: map[string]attr.Type{
- "group_by": types.ListType{ElemType: types.StringType},
- "group_interval": types.StringType,
- "group_wait": types.StringType,
- "match": types.MapType{ElemType: types.StringType},
- "match_regex": types.MapType{ElemType: types.StringType},
- "receiver": types.StringType,
- "repeat_interval": types.StringType,
- "routes": types.ListType{ElemType: types.ObjectType{AttrTypes: map[string]attr.Type{
- "group_by": types.ListType{ElemType: types.StringType},
- "group_interval": types.StringType,
- "group_wait": types.StringType,
- "match": types.MapType{ElemType: types.StringType},
- "match_regex": types.MapType{ElemType: types.StringType},
- "receiver": types.StringType,
- "repeat_interval": types.StringType,
- }}},
- },
- },
- },
- {
- "recursion 2",
- 2,
- 2,
- types.ObjectType{
- AttrTypes: map[string]attr.Type{
- "group_by": types.ListType{ElemType: types.StringType},
- "group_interval": types.StringType,
- "group_wait": types.StringType,
- "match": types.MapType{ElemType: types.StringType},
- "match_regex": types.MapType{ElemType: types.StringType},
- "receiver": types.StringType,
- "repeat_interval": types.StringType,
- },
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- output := getRouteListTypeAux(tt.startingLevel, tt.recursionLimit)
- diff := cmp.Diff(output, tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- })
- }
-}
-
-func makeTestMap(t *testing.T) basetypes.MapValue {
- p := make(map[string]attr.Value, 1)
- p["key"] = types.StringValue("value")
- params, diag := types.MapValueFrom(context.Background(), types.StringType, p)
- if diag.HasError() {
- t.Fail()
- }
- return params
-}
-
-// ToTerraformStringMapMust Silently ignores the error
-func toTerraformStringMapMust(ctx context.Context, m map[string]string) basetypes.MapValue {
- labels := make(map[string]attr.Value, len(m))
- for l, v := range m {
- stringValue := types.StringValue(v)
- labels[l] = stringValue
- }
- res, diags := types.MapValueFrom(ctx, types.StringType, m)
- if diags.HasError() {
- return types.MapNull(types.StringType)
- }
- return res
-}
diff --git a/stackit/internal/services/argus/scrapeconfig/const.go b/stackit/internal/services/argus/scrapeconfig/const.go
deleted file mode 100644
index 1e98f6e8..00000000
--- a/stackit/internal/services/argus/scrapeconfig/const.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package argus
-
-const exampleMoveToObservability = "## Example move\n" +
- "Example to move the deprecated `stackit_argus_scrapeconfig` resource to the new `stackit_observability_scrapeconfig` resource:" + "\n" +
- "1. Add a new `stackit_observability_scrapeconfig` resource with the same values like your previous `stackit_argus_scrapeconfig` resource." + "\n" +
- "1. Add a moved block which reference the `stackit_argus_scrapeconfig` and `stackit_observability_scrapeconfig` resource." + "\n" +
- "1. Remove your old `stackit_argus_scrapeconfig` resource and run `$ terraform apply`." + "\n" +
- "```terraform" +
- `
-resource "stackit_argus_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-job"
- metrics_path = "/my-metrics"
- saml2 = {
- enable_url_parameters = true
- }
- targets = [
- {
- urls = ["url1", "urls2"]
- labels = {
- "url1" = "dev"
- }
- }
- ]
-}
-
-moved {
- from = stackit_argus_scrapeconfig.example
- to = stackit_observability_scrapeconfig.example
-}
-
-resource "stackit_observability_scrapeconfig" "example" {
- project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- name = "example-job"
- metrics_path = "/my-metrics"
- saml2 = {
- enable_url_parameters = true
- }
- targets = [
- {
- urls = ["url1", "urls2"]
- labels = {
- "url1" = "dev"
- }
- }
- ]
-}
-` + "```" + "\n"
diff --git a/stackit/internal/services/argus/scrapeconfig/datasource.go b/stackit/internal/services/argus/scrapeconfig/datasource.go
deleted file mode 100644
index 21020559..00000000
--- a/stackit/internal/services/argus/scrapeconfig/datasource.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package argus
-
-import (
- "context"
- "fmt"
- "net/http"
-
- "github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
- "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
- "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
- "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
- "github.com/hashicorp/terraform-plugin-framework/datasource"
- "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
- "github.com/hashicorp/terraform-plugin-framework/schema/validator"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/hashicorp/terraform-plugin-log/tflog"
- "github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
-)
-
-// Ensure the implementation satisfies the expected interfaces.
-var (
- _ datasource.DataSource = &scrapeConfigDataSource{}
-)
-
-// NewScrapeConfigDataSource is a helper function to simplify the provider implementation.
-func NewScrapeConfigDataSource() datasource.DataSource {
- return &scrapeConfigDataSource{}
-}
-
-// scrapeConfigDataSource is the data source implementation.
-type scrapeConfigDataSource struct {
- client *argus.APIClient
-}
-
-// Metadata returns the data source type name.
-func (d *scrapeConfigDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
- resp.TypeName = req.ProviderTypeName + "_argus_scrapeconfig"
-}
-
-func (d *scrapeConfigDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
- // Prevent panic if the provider has not been configured.
- if req.ProviderData == nil {
- return
- }
-
- var apiClient *argus.APIClient
- var err error
-
- providerData, ok := req.ProviderData.(core.ProviderData)
- if !ok {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
- return
- }
-
- if providerData.ArgusCustomEndpoint != "" {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithEndpoint(providerData.ArgusCustomEndpoint),
- )
- } else {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithRegion(providerData.GetRegion()),
- )
- }
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the data source configuration", err))
- return
- }
- d.client = apiClient
-}
-
-// Schema defines the schema for the data source.
-func (d *scrapeConfigDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
- descriptions := map[string]string{
- "main": "Argus scrape config data source schema. Must have a `region` specified in the provider configuration.",
- "deprecation_message": "The `stackit_argus_scrapeconfig` data source has been deprecated and will be removed after February 26th 2025. " +
- "Please use `stackit_observability_scrapeconfig` instead, which offers the exact same functionality.",
- }
- resp.Schema = schema.Schema{
- Description: fmt.Sprintf("%s\n%s", descriptions["main"], descriptions["deprecation_message"]),
- MarkdownDescription: fmt.Sprintf("%s\n\n!> %s", descriptions["main"], descriptions["deprecation_message"]),
- DeprecationMessage: descriptions["deprecation_message"],
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Terraform's internal data source. ID. It is structured as \"`project_id`,`instance_id`,`name`\".",
- Computed: true,
- },
- "project_id": schema.StringAttribute{
- Description: "STACKIT project ID to which the scraping job is associated.",
- Required: true,
- Validators: []validator.String{
- validate.UUID(),
- validate.NoSeparator(),
- },
- },
- "instance_id": schema.StringAttribute{
- Description: "Argus instance ID to which the scraping job is associated.",
- Required: true,
- Validators: []validator.String{
- validate.UUID(),
- validate.NoSeparator(),
- },
- },
- "name": schema.StringAttribute{
- Description: "Specifies the name of the scraping job",
- Required: true,
- Validators: []validator.String{
- validate.NoSeparator(),
- stringvalidator.LengthBetween(1, 200),
- },
- },
- "metrics_path": schema.StringAttribute{
- Description: "Specifies the job scraping url path.",
- Computed: true,
- },
-
- "scheme": schema.StringAttribute{
- Description: "Specifies the http scheme.",
- Computed: true,
- },
-
- "scrape_interval": schema.StringAttribute{
- Description: "Specifies the scrape interval as duration string.",
- Validators: []validator.String{
- stringvalidator.LengthBetween(2, 8),
- },
- Computed: true,
- },
-
- "sample_limit": schema.Int64Attribute{
- Description: "Specifies the scrape sample limit.",
- Computed: true,
- Validators: []validator.Int64{
- int64validator.Between(1, 3000000),
- },
- },
-
- "scrape_timeout": schema.StringAttribute{
- Description: "Specifies the scrape timeout as duration string.",
- Computed: true,
- },
- "saml2": schema.SingleNestedAttribute{
- Description: "A SAML2 configuration block.",
- Computed: true,
- Attributes: map[string]schema.Attribute{
- "enable_url_parameters": schema.BoolAttribute{
- Description: "Specifies if URL parameters are enabled",
- Computed: true,
- },
- },
- },
- "basic_auth": schema.SingleNestedAttribute{
- Description: "A basic authentication block.",
- Computed: true,
- Attributes: map[string]schema.Attribute{
- "username": schema.StringAttribute{
- Description: "Specifies basic auth username.",
- Computed: true,
- Validators: []validator.String{
- stringvalidator.LengthBetween(1, 200),
- },
- },
- "password": schema.StringAttribute{
- Description: "Specifies basic auth password.",
- Computed: true,
- Sensitive: true,
- Validators: []validator.String{
- stringvalidator.LengthBetween(1, 200),
- },
- },
- },
- },
- "targets": schema.ListNestedAttribute{
- Description: "The targets list (specified by the static config).",
- Computed: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "urls": schema.ListAttribute{
- Description: "Specifies target URLs.",
- Computed: true,
- ElementType: types.StringType,
- Validators: []validator.List{
- listvalidator.ValueStringsAre(
- stringvalidator.LengthBetween(1, 500),
- ),
- },
- },
- "labels": schema.MapAttribute{
- Description: "Specifies labels.",
- Computed: true,
- ElementType: types.StringType,
- Validators: []validator.Map{
- mapvalidator.SizeAtMost(10),
- mapvalidator.ValueStringsAre(stringvalidator.LengthBetween(0, 200)),
- mapvalidator.KeysAre(stringvalidator.LengthBetween(0, 200)),
- },
- },
- },
- },
- },
- },
- }
-}
-
-// Read refreshes the Terraform state with the latest data.
-func (d *scrapeConfigDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
- var model Model
- diags := req.Config.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- scName := model.Name.ValueString()
-
- scResp, err := d.client.GetScrapeConfig(ctx, instanceId, scName, projectId).Execute()
- if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Unable to read scrape config", err.Error())
- return
- }
-
- err = mapFields(ctx, scResp.Data, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Mapping fields", err.Error())
- return
- }
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- tflog.Info(ctx, "Argus scrape config read")
-}
diff --git a/stackit/internal/services/argus/scrapeconfig/resource.go b/stackit/internal/services/argus/scrapeconfig/resource.go
deleted file mode 100644
index 4a73b30f..00000000
--- a/stackit/internal/services/argus/scrapeconfig/resource.go
+++ /dev/null
@@ -1,880 +0,0 @@
-package argus
-
-import (
- "context"
- "fmt"
- "net/http"
- "strings"
- "time"
-
- "github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
- "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
- "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
- "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
- "github.com/hashicorp/terraform-plugin-framework/attr"
- "github.com/hashicorp/terraform-plugin-framework/path"
- "github.com/hashicorp/terraform-plugin-framework/resource"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
- "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
- "github.com/hashicorp/terraform-plugin-framework/schema/validator"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
- "github.com/hashicorp/terraform-plugin-log/tflog"
- "github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
- "github.com/stackitcloud/stackit-sdk-go/core/utils"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
- "github.com/stackitcloud/stackit-sdk-go/services/argus/wait"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
-)
-
-const (
- DefaultScheme = "https" // API default is "http"
- DefaultScrapeInterval = "5m"
- DefaultScrapeTimeout = "2m"
- DefaultSampleLimit = int64(5000)
- DefaultSAML2EnableURLParameters = true
-)
-
-// Ensure the implementation satisfies the expected interfaces.
-var (
- _ resource.Resource = &scrapeConfigResource{}
- _ resource.ResourceWithConfigure = &scrapeConfigResource{}
- _ resource.ResourceWithImportState = &scrapeConfigResource{}
-)
-
-type Model struct {
- Id types.String `tfsdk:"id"` // needed by TF
- ProjectId types.String `tfsdk:"project_id"`
- InstanceId types.String `tfsdk:"instance_id"`
- Name types.String `tfsdk:"name"`
- MetricsPath types.String `tfsdk:"metrics_path"`
- Scheme types.String `tfsdk:"scheme"`
- ScrapeInterval types.String `tfsdk:"scrape_interval"`
- ScrapeTimeout types.String `tfsdk:"scrape_timeout"`
- SampleLimit types.Int64 `tfsdk:"sample_limit"`
- SAML2 types.Object `tfsdk:"saml2"`
- BasicAuth types.Object `tfsdk:"basic_auth"`
- Targets types.List `tfsdk:"targets"`
-}
-
-// Struct corresponding to Model.SAML2
-type saml2Model struct {
- EnableURLParameters types.Bool `tfsdk:"enable_url_parameters"`
-}
-
-// Types corresponding to saml2Model
-var saml2Types = map[string]attr.Type{
- "enable_url_parameters": types.BoolType,
-}
-
-// Struct corresponding to Model.BasicAuth
-type basicAuthModel struct {
- Username types.String `tfsdk:"username"`
- Password types.String `tfsdk:"password"`
-}
-
-// Types corresponding to basicAuthModel
-var basicAuthTypes = map[string]attr.Type{
- "username": types.StringType,
- "password": types.StringType,
-}
-
-// Struct corresponding to Model.Targets[i]
-type targetModel struct {
- URLs types.List `tfsdk:"urls"`
- Labels types.Map `tfsdk:"labels"`
-}
-
-// Types corresponding to targetModel
-var targetTypes = map[string]attr.Type{
- "urls": types.ListType{ElemType: types.StringType},
- "labels": types.MapType{ElemType: types.StringType},
-}
-
-// NewScrapeConfigResource is a helper function to simplify the provider implementation.
-func NewScrapeConfigResource() resource.Resource {
- return &scrapeConfigResource{}
-}
-
-// scrapeConfigResource is the resource implementation.
-type scrapeConfigResource struct {
- client *argus.APIClient
-}
-
-// Metadata returns the resource type name.
-func (r *scrapeConfigResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
- resp.TypeName = req.ProviderTypeName + "_argus_scrapeconfig"
-}
-
-// Configure adds the provider configured client to the resource.
-func (r *scrapeConfigResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
- // Prevent panic if the provider has not been configured.
- if req.ProviderData == nil {
- return
- }
-
- providerData, ok := req.ProviderData.(core.ProviderData)
- if !ok {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
- return
- }
-
- var apiClient *argus.APIClient
- var err error
- if providerData.ArgusCustomEndpoint != "" {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithEndpoint(providerData.ArgusCustomEndpoint),
- )
- } else {
- apiClient, err = argus.NewAPIClient(
- config.WithCustomAuth(providerData.RoundTripper),
- config.WithRegion(providerData.GetRegion()),
- )
- }
-
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
- return
- }
- r.client = apiClient
- tflog.Info(ctx, "Argus scrape config client configured")
-}
-
-var (
- descriptions = map[string]string{
- "main": "Argus scrape config resource schema. Must have a `region` specified in the provider configuration.",
- "deprecation_message": "The `stackit_argus_scrapeconfig` resource has been deprecated and will be removed after February 26th 2025. " +
- "Please use `stackit_observability_scrapeconfig` instead, which offers the exact same functionality.",
- }
- Schema = schema.Schema{
- Description: fmt.Sprintf("%s\n%s", descriptions["main"], descriptions["deprecation_message"]),
- MarkdownDescription: fmt.Sprintf("%s\n\n!> %s\n\n%s", descriptions["main"], descriptions["deprecation_message"], exampleMoveToObservability),
- DeprecationMessage: descriptions["deprecation_message"],
- Attributes: map[string]schema.Attribute{
- "id": schema.StringAttribute{
- Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`,`name`\".",
- Computed: true,
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
- "project_id": schema.StringAttribute{
- Description: "STACKIT project ID to which the scraping job is associated.",
- Required: true,
- Validators: []validator.String{
- validate.UUID(),
- validate.NoSeparator(),
- },
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
- },
- },
- "instance_id": schema.StringAttribute{
- Description: "Argus instance ID to which the scraping job is associated.",
- Required: true,
- Validators: []validator.String{
- validate.UUID(),
- validate.NoSeparator(),
- },
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
- },
- },
- "name": schema.StringAttribute{
- Description: "Specifies the name of the scraping job.",
- Required: true,
- Validators: []validator.String{
- validate.NoSeparator(),
- stringvalidator.LengthBetween(1, 200),
- },
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.RequiresReplace(),
- },
- },
- "metrics_path": schema.StringAttribute{
- Description: "Specifies the job scraping url path. E.g. `/metrics`.",
- Required: true,
- Validators: []validator.String{
- stringvalidator.LengthBetween(1, 200),
- },
- },
-
- "scheme": schema.StringAttribute{
- Description: "Specifies the http scheme. Defaults to `https`.",
- Optional: true,
- Computed: true,
- Default: stringdefault.StaticString(DefaultScheme),
- },
- "scrape_interval": schema.StringAttribute{
- Description: "Specifies the scrape interval as duration string. Defaults to `5m`.",
- Optional: true,
- Computed: true,
- Validators: []validator.String{
- stringvalidator.LengthBetween(2, 8),
- },
- Default: stringdefault.StaticString(DefaultScrapeInterval),
- },
- "scrape_timeout": schema.StringAttribute{
- Description: "Specifies the scrape timeout as duration string. Defaults to `2m`.",
- Optional: true,
- Computed: true,
- Validators: []validator.String{
- stringvalidator.LengthBetween(2, 8),
- },
- Default: stringdefault.StaticString(DefaultScrapeTimeout),
- },
- "sample_limit": schema.Int64Attribute{
- Description: "Specifies the scrape sample limit. Upper limit depends on the service plan. Defaults to `5000`.",
- Optional: true,
- Computed: true,
- Validators: []validator.Int64{
- int64validator.Between(1, 3000000),
- },
- Default: int64default.StaticInt64(DefaultSampleLimit),
- },
- "saml2": schema.SingleNestedAttribute{
- Description: "A SAML2 configuration block.",
- Optional: true,
- Computed: true,
- Default: objectdefault.StaticValue(
- types.ObjectValueMust(
- map[string]attr.Type{
- "enable_url_parameters": types.BoolType,
- },
- map[string]attr.Value{
- "enable_url_parameters": types.BoolValue(DefaultSAML2EnableURLParameters),
- },
- ),
- ),
- Attributes: map[string]schema.Attribute{
- "enable_url_parameters": schema.BoolAttribute{
- Description: "Specifies if URL parameters are enabled. Defaults to `true`",
- Optional: true,
- Computed: true,
- Default: booldefault.StaticBool(DefaultSAML2EnableURLParameters),
- },
- },
- },
- "basic_auth": schema.SingleNestedAttribute{
- Description: "A basic authentication block.",
- Optional: true,
- Computed: true,
- Attributes: map[string]schema.Attribute{
- "username": schema.StringAttribute{
- Description: "Specifies basic auth username.",
- Required: true,
- Validators: []validator.String{
- stringvalidator.LengthBetween(1, 200),
- },
- },
- "password": schema.StringAttribute{
- Description: "Specifies basic auth password.",
- Required: true,
- Sensitive: true,
- Validators: []validator.String{
- stringvalidator.LengthBetween(1, 200),
- },
- },
- },
- },
- "targets": schema.ListNestedAttribute{
- Description: "The targets list (specified by the static config).",
- Required: true,
- NestedObject: schema.NestedAttributeObject{
- Attributes: map[string]schema.Attribute{
- "urls": schema.ListAttribute{
- Description: "Specifies target URLs.",
- Required: true,
- ElementType: types.StringType,
- Validators: []validator.List{
- listvalidator.ValueStringsAre(
- stringvalidator.LengthBetween(1, 500),
- ),
- },
- },
- "labels": schema.MapAttribute{
- Description: "Specifies labels.",
- Optional: true,
- ElementType: types.StringType,
- Validators: []validator.Map{
- mapvalidator.SizeAtMost(10),
- mapvalidator.ValueStringsAre(stringvalidator.LengthBetween(0, 200)),
- mapvalidator.KeysAre(stringvalidator.LengthBetween(0, 200)),
- },
- },
- },
- },
- },
- },
- }
-)
-
-// Schema defines the schema for the resource.
-func (r *scrapeConfigResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
- resp.Schema = Schema
-}
-
-// Create creates the resource and sets the initial Terraform state.
-func (r *scrapeConfigResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
- // Retrieve values from plan
- var model Model
- diags := req.Plan.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- scName := model.Name.ValueString()
-
- saml2Model := saml2Model{}
- if !model.SAML2.IsNull() && !model.SAML2.IsUnknown() {
- diags = model.SAML2.As(ctx, &saml2Model, basetypes.ObjectAsOptions{})
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- basicAuthModel := basicAuthModel{}
- if !model.BasicAuth.IsNull() && !model.BasicAuth.IsUnknown() {
- diags = model.BasicAuth.As(ctx, &basicAuthModel, basetypes.ObjectAsOptions{})
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- targetsModel := []targetModel{}
- if !model.Targets.IsNull() && !model.Targets.IsUnknown() {
- diags = model.Targets.ElementsAs(ctx, &targetsModel, false)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- // Generate API request body from model
- payload, err := toCreatePayload(ctx, &model, &saml2Model, &basicAuthModel, targetsModel)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating scrape config", fmt.Sprintf("Creating API payload: %v", err))
- return
- }
- _, err = r.client.CreateScrapeConfig(ctx, instanceId, projectId).CreateScrapeConfigPayload(*payload).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating scrape config", fmt.Sprintf("Calling API: %v", err))
- return
- }
- _, err = wait.CreateScrapeConfigWaitHandler(ctx, r.client, instanceId, scName, projectId).WaitWithContext(ctx)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating scrape config", fmt.Sprintf("Scrape config creation waiting: %v", err))
- return
- }
- got, err := r.client.GetScrapeConfig(ctx, instanceId, scName, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating scrape config", fmt.Sprintf("Calling API for updated data: %v", err))
- return
- }
- err = mapFields(ctx, got.Data, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating scrape config", fmt.Sprintf("Processing API payload: %v", err))
- return
- }
- // Set state to fully populated data
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- tflog.Info(ctx, "Argus scrape config created")
-}
-
-// Read refreshes the Terraform state with the latest data.
-func (r *scrapeConfigResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
- var model Model
- diags := req.State.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- scName := model.Name.ValueString()
-
- scResp, err := r.client.GetScrapeConfig(ctx, instanceId, scName, projectId).Execute()
- if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading scrape config", fmt.Sprintf("Calling API: %v", err))
- return
- }
-
- // Map response body to schema
- err = mapFields(ctx, scResp.Data, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading scrape config", fmt.Sprintf("Processing API payload: %v", err))
- return
- }
- // Set refreshed model
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- tflog.Info(ctx, "Argus scrape config read")
-}
-
-// Update updates the resource and sets the updated Terraform state on success.
-func (r *scrapeConfigResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
- // Retrieve values from plan
- var model Model
- diags := req.Plan.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- scName := model.Name.ValueString()
-
- saml2Model := saml2Model{}
- if !model.SAML2.IsNull() && !model.SAML2.IsUnknown() {
- diags = model.SAML2.As(ctx, &saml2Model, basetypes.ObjectAsOptions{})
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- basicAuthModel := basicAuthModel{}
- if !model.BasicAuth.IsNull() && !model.BasicAuth.IsUnknown() {
- diags = model.BasicAuth.As(ctx, &basicAuthModel, basetypes.ObjectAsOptions{})
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- targetsModel := []targetModel{}
- if !model.Targets.IsNull() && !model.Targets.IsUnknown() {
- diags = model.Targets.ElementsAs(ctx, &targetsModel, false)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- }
-
- // Generate API request body from model
- payload, err := toUpdatePayload(ctx, &model, &saml2Model, &basicAuthModel, targetsModel)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating scrape config", fmt.Sprintf("Creating API payload: %v", err))
- return
- }
- _, err = r.client.UpdateScrapeConfig(ctx, instanceId, scName, projectId).UpdateScrapeConfigPayload(*payload).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating scrape config", fmt.Sprintf("Calling API: %v", err))
- return
- }
- // We do not have an update status provided by the argus scrape config api, so we cannot use a waiter here, hence a simple sleep is used.
- time.Sleep(15 * time.Second)
-
- // Fetch updated ScrapeConfig
- scResp, err := r.client.GetScrapeConfig(ctx, instanceId, scName, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating scrape config", fmt.Sprintf("Calling API for updated data: %v", err))
- return
- }
- err = mapFields(ctx, scResp.Data, &model)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating scrape config", fmt.Sprintf("Processing API payload: %v", err))
- return
- }
- diags = resp.State.Set(ctx, model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
- tflog.Info(ctx, "Argus scrape config updated")
-}
-
-// Delete deletes the resource and removes the Terraform state on success.
-func (r *scrapeConfigResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
- // Retrieve values from state
- var model Model
- diags := req.State.Get(ctx, &model)
- resp.Diagnostics.Append(diags...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- projectId := model.ProjectId.ValueString()
- instanceId := model.InstanceId.ValueString()
- scName := model.Name.ValueString()
-
- // Delete existing ScrapeConfig
- _, err := r.client.DeleteScrapeConfig(ctx, instanceId, scName, projectId).Execute()
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting scrape config", fmt.Sprintf("Calling API: %v", err))
- return
- }
- _, err = wait.DeleteScrapeConfigWaitHandler(ctx, r.client, instanceId, scName, projectId).WaitWithContext(ctx)
- if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting scrape config", fmt.Sprintf("Scrape config deletion waiting: %v", err))
- return
- }
-
- tflog.Info(ctx, "Argus scrape config deleted")
-}
-
-// ImportState imports a resource into the Terraform state on success.
-// The expected format of the resource import identifier is: project_id,instance_id,name
-func (r *scrapeConfigResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
- idParts := strings.Split(req.ID, core.Separator)
-
- if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
- core.LogAndAddError(ctx, &resp.Diagnostics,
- "Error importing scrape config",
- fmt.Sprintf("Expected import identifier with format: [project_id],[instance_id],[name] Got: %q", req.ID),
- )
- return
- }
-
- resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
- resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
- resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), idParts[2])...)
- tflog.Info(ctx, "Argus scrape config state imported")
-}
-
-func mapFields(ctx context.Context, sc *argus.Job, model *Model) error {
- if sc == nil {
- return fmt.Errorf("response input is nil")
- }
- if model == nil {
- return fmt.Errorf("model input is nil")
- }
-
- var scName string
- if model.Name.ValueString() != "" {
- scName = model.Name.ValueString()
- } else if sc.JobName != nil {
- scName = *sc.JobName
- } else {
- return fmt.Errorf("scrape config name not present")
- }
-
- idParts := []string{
- model.ProjectId.ValueString(),
- model.InstanceId.ValueString(),
- scName,
- }
- model.Id = types.StringValue(
- strings.Join(idParts, core.Separator),
- )
- model.Name = types.StringValue(scName)
-
- model.MetricsPath = types.StringPointerValue(sc.MetricsPath)
- model.Scheme = types.StringPointerValue(sc.Scheme)
- model.ScrapeInterval = types.StringPointerValue(sc.ScrapeInterval)
- model.ScrapeTimeout = types.StringPointerValue(sc.ScrapeTimeout)
- model.SampleLimit = types.Int64PointerValue(sc.SampleLimit)
- err := mapSAML2(sc, model)
- if err != nil {
- return fmt.Errorf("map saml2: %w", err)
- }
- err = mapBasicAuth(sc, model)
- if err != nil {
- return fmt.Errorf("map basic auth: %w", err)
- }
- err = mapTargets(ctx, sc, model)
- if err != nil {
- return fmt.Errorf("map targets: %w", err)
- }
- return nil
-}
-
-func mapBasicAuth(sc *argus.Job, model *Model) error {
- if sc.BasicAuth == nil {
- model.BasicAuth = types.ObjectNull(basicAuthTypes)
- return nil
- }
- basicAuthMap := map[string]attr.Value{
- "username": types.StringValue(*sc.BasicAuth.Username),
- "password": types.StringValue(*sc.BasicAuth.Password),
- }
- basicAuthTF, diags := types.ObjectValue(basicAuthTypes, basicAuthMap)
- if diags.HasError() {
- return core.DiagsToError(diags)
- }
- model.BasicAuth = basicAuthTF
- return nil
-}
-
-func mapSAML2(sc *argus.Job, model *Model) error {
- if (sc.Params == nil || *sc.Params == nil) && model.SAML2.IsNull() {
- return nil
- }
-
- if model.SAML2.IsNull() || model.SAML2.IsUnknown() {
- model.SAML2 = types.ObjectNull(saml2Types)
- }
-
- flag := true
- if sc.Params == nil || *sc.Params == nil {
- return nil
- }
- p := *sc.Params
- if v, ok := p["saml2"]; ok {
- if len(v) == 1 && v[0] == "disabled" {
- flag = false
- }
- }
-
- saml2Map := map[string]attr.Value{
- "enable_url_parameters": types.BoolValue(flag),
- }
- saml2TF, diags := types.ObjectValue(saml2Types, saml2Map)
- if diags.HasError() {
- return core.DiagsToError(diags)
- }
- model.SAML2 = saml2TF
- return nil
-}
-
-func mapTargets(ctx context.Context, sc *argus.Job, model *Model) error {
- if sc == nil || sc.StaticConfigs == nil {
- model.Targets = types.ListNull(types.ObjectType{AttrTypes: targetTypes})
- return nil
- }
-
- targetsModel := []targetModel{}
- if !model.Targets.IsNull() && !model.Targets.IsUnknown() {
- diags := model.Targets.ElementsAs(ctx, &targetsModel, false)
- if diags.HasError() {
- return core.DiagsToError(diags)
- }
- }
-
- newTargets := []attr.Value{}
- for i, sc := range *sc.StaticConfigs {
- nt := targetModel{}
-
- // Map URLs
- urls := []attr.Value{}
- if sc.Targets != nil {
- for _, v := range *sc.Targets {
- urls = append(urls, types.StringValue(v))
- }
- }
- nt.URLs = types.ListValueMust(types.StringType, urls)
-
- // Map Labels
- if len(model.Targets.Elements()) > i && targetsModel[i].Labels.IsNull() || sc.Labels == nil {
- nt.Labels = types.MapNull(types.StringType)
- } else {
- newl := map[string]attr.Value{}
- for k, v := range *sc.Labels {
- newl[k] = types.StringValue(v)
- }
- nt.Labels = types.MapValueMust(types.StringType, newl)
- }
-
- // Build target
- targetMap := map[string]attr.Value{
- "urls": nt.URLs,
- "labels": nt.Labels,
- }
- targetTF, diags := types.ObjectValue(targetTypes, targetMap)
- if diags.HasError() {
- return core.DiagsToError(diags)
- }
-
- newTargets = append(newTargets, targetTF)
- }
-
- targetsTF, diags := types.ListValue(types.ObjectType{AttrTypes: targetTypes}, newTargets)
- if diags.HasError() {
- return core.DiagsToError(diags)
- }
-
- model.Targets = targetsTF
- return nil
-}
-
-func toCreatePayload(ctx context.Context, model *Model, saml2Model *saml2Model, basicAuthModel *basicAuthModel, targetsModel []targetModel) (*argus.CreateScrapeConfigPayload, error) {
- if model == nil {
- return nil, fmt.Errorf("nil model")
- }
-
- sc := argus.CreateScrapeConfigPayload{
- JobName: conversion.StringValueToPointer(model.Name),
- MetricsPath: conversion.StringValueToPointer(model.MetricsPath),
- ScrapeInterval: conversion.StringValueToPointer(model.ScrapeInterval),
- ScrapeTimeout: conversion.StringValueToPointer(model.ScrapeTimeout),
- // potentially lossy conversion, depending on the allowed range for sample_limit
- SampleLimit: utils.Ptr(float64(model.SampleLimit.ValueInt64())),
- Scheme: conversion.StringValueToPointer(model.Scheme),
- }
- setDefaultsCreateScrapeConfig(&sc, model, saml2Model)
-
- if !saml2Model.EnableURLParameters.IsNull() && !saml2Model.EnableURLParameters.IsUnknown() {
- m := make(map[string]interface{})
- if sc.Params != nil {
- m = *sc.Params
- }
- if saml2Model.EnableURLParameters.ValueBool() {
- m["saml2"] = []string{"enabled"}
- } else {
- m["saml2"] = []string{"disabled"}
- }
- sc.Params = &m
- }
-
- if sc.BasicAuth == nil && !basicAuthModel.Username.IsNull() && !basicAuthModel.Password.IsNull() {
- sc.BasicAuth = &argus.CreateScrapeConfigPayloadBasicAuth{
- Username: conversion.StringValueToPointer(basicAuthModel.Username),
- Password: conversion.StringValueToPointer(basicAuthModel.Password),
- }
- }
-
- t := make([]argus.CreateScrapeConfigPayloadStaticConfigsInner, len(targetsModel))
- for i, target := range targetsModel {
- ti := argus.CreateScrapeConfigPayloadStaticConfigsInner{}
-
- urls := []string{}
- diags := target.URLs.ElementsAs(ctx, &urls, false)
- if diags.HasError() {
- return nil, core.DiagsToError(diags)
- }
- ti.Targets = &urls
-
- labels := map[string]interface{}{}
- for k, v := range target.Labels.Elements() {
- labels[k], _ = conversion.ToString(ctx, v)
- }
- ti.Labels = &labels
- t[i] = ti
- }
- sc.StaticConfigs = &t
-
- return &sc, nil
-}
-
-func setDefaultsCreateScrapeConfig(sc *argus.CreateScrapeConfigPayload, model *Model, saml2Model *saml2Model) {
- if sc == nil {
- return
- }
- if model.Scheme.IsNull() || model.Scheme.IsUnknown() {
- sc.Scheme = utils.Ptr(DefaultScheme)
- }
- if model.ScrapeInterval.IsNull() || model.ScrapeInterval.IsUnknown() {
- sc.ScrapeInterval = utils.Ptr(DefaultScrapeInterval)
- }
- if model.ScrapeTimeout.IsNull() || model.ScrapeTimeout.IsUnknown() {
- sc.ScrapeTimeout = utils.Ptr(DefaultScrapeTimeout)
- }
- if model.SampleLimit.IsNull() || model.SampleLimit.IsUnknown() {
- sc.SampleLimit = utils.Ptr(float64(DefaultSampleLimit))
- }
- // Make the API default more explicit by setting the field.
- if saml2Model.EnableURLParameters.IsNull() || saml2Model.EnableURLParameters.IsUnknown() {
- m := map[string]interface{}{}
- if sc.Params != nil {
- m = *sc.Params
- }
- if DefaultSAML2EnableURLParameters {
- m["saml2"] = []string{"enabled"}
- } else {
- m["saml2"] = []string{"disabled"}
- }
- sc.Params = &m
- }
-}
-
-func toUpdatePayload(ctx context.Context, model *Model, saml2Model *saml2Model, basicAuthModel *basicAuthModel, targetsModel []targetModel) (*argus.UpdateScrapeConfigPayload, error) {
- if model == nil {
- return nil, fmt.Errorf("nil model")
- }
-
- sc := argus.UpdateScrapeConfigPayload{
- MetricsPath: conversion.StringValueToPointer(model.MetricsPath),
- ScrapeInterval: conversion.StringValueToPointer(model.ScrapeInterval),
- ScrapeTimeout: conversion.StringValueToPointer(model.ScrapeTimeout),
- // potentially lossy conversion, depending on the allowed range for sample_limit
- SampleLimit: utils.Ptr(float64(model.SampleLimit.ValueInt64())),
- Scheme: conversion.StringValueToPointer(model.Scheme),
- }
- setDefaultsUpdateScrapeConfig(&sc, model)
-
- if !saml2Model.EnableURLParameters.IsNull() && !saml2Model.EnableURLParameters.IsUnknown() {
- m := make(map[string]interface{})
- if sc.Params != nil {
- m = *sc.Params
- }
- if saml2Model.EnableURLParameters.ValueBool() {
- m["saml2"] = []string{"enabled"}
- } else {
- m["saml2"] = []string{"disabled"}
- }
- sc.Params = &m
- }
-
- if sc.BasicAuth == nil && !basicAuthModel.Username.IsNull() && !basicAuthModel.Password.IsNull() {
- sc.BasicAuth = &argus.CreateScrapeConfigPayloadBasicAuth{
- Username: conversion.StringValueToPointer(basicAuthModel.Username),
- Password: conversion.StringValueToPointer(basicAuthModel.Password),
- }
- }
-
- t := make([]argus.UpdateScrapeConfigPayloadStaticConfigsInner, len(targetsModel))
- for i, target := range targetsModel {
- ti := argus.UpdateScrapeConfigPayloadStaticConfigsInner{}
-
- urls := []string{}
- diags := target.URLs.ElementsAs(ctx, &urls, false)
- if diags.HasError() {
- return nil, core.DiagsToError(diags)
- }
- ti.Targets = &urls
-
- ls := map[string]interface{}{}
- for k, v := range target.Labels.Elements() {
- ls[k], _ = conversion.ToString(ctx, v)
- }
- ti.Labels = &ls
- t[i] = ti
- }
- sc.StaticConfigs = &t
-
- return &sc, nil
-}
-
-func setDefaultsUpdateScrapeConfig(sc *argus.UpdateScrapeConfigPayload, model *Model) {
- if sc == nil {
- return
- }
- if model.Scheme.IsNull() || model.Scheme.IsUnknown() {
- sc.Scheme = utils.Ptr(DefaultScheme)
- }
- if model.ScrapeInterval.IsNull() || model.ScrapeInterval.IsUnknown() {
- sc.ScrapeInterval = utils.Ptr(DefaultScrapeInterval)
- }
- if model.ScrapeTimeout.IsNull() || model.ScrapeTimeout.IsUnknown() {
- sc.ScrapeTimeout = utils.Ptr(DefaultScrapeTimeout)
- }
- if model.SampleLimit.IsNull() || model.SampleLimit.IsUnknown() {
- sc.SampleLimit = utils.Ptr(float64(DefaultSampleLimit))
- }
-}
diff --git a/stackit/internal/services/argus/scrapeconfig/resource_test.go b/stackit/internal/services/argus/scrapeconfig/resource_test.go
deleted file mode 100644
index cee63679..00000000
--- a/stackit/internal/services/argus/scrapeconfig/resource_test.go
+++ /dev/null
@@ -1,504 +0,0 @@
-package argus
-
-import (
- "context"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/hashicorp/terraform-plugin-framework/attr"
- "github.com/hashicorp/terraform-plugin-framework/types"
- "github.com/stackitcloud/stackit-sdk-go/core/utils"
- "github.com/stackitcloud/stackit-sdk-go/services/argus"
-)
-
-func TestMapFields(t *testing.T) {
- tests := []struct {
- description string
- input *argus.Job
- expected Model
- isValid bool
- }{
- {
- "default_ok",
- &argus.Job{
- JobName: utils.Ptr("name"),
- },
- Model{
- Id: types.StringValue("pid,iid,name"),
- ProjectId: types.StringValue("pid"),
- InstanceId: types.StringValue("iid"),
- Name: types.StringValue("name"),
- MetricsPath: types.StringNull(),
- Scheme: types.StringNull(),
- ScrapeInterval: types.StringNull(),
- ScrapeTimeout: types.StringNull(),
- SAML2: types.ObjectNull(saml2Types),
- BasicAuth: types.ObjectNull(basicAuthTypes),
- Targets: types.ListNull(types.ObjectType{AttrTypes: targetTypes}),
- },
- true,
- },
- {
- description: "values_ok",
- input: &argus.Job{
- JobName: utils.Ptr("name"),
- MetricsPath: utils.Ptr("/m"),
- BasicAuth: &argus.BasicAuth{
- Password: utils.Ptr("p"),
- Username: utils.Ptr("u"),
- },
- Params: &map[string][]string{"saml2": {"disabled"}, "x": {"y", "z"}},
- Scheme: utils.Ptr("scheme"),
- ScrapeInterval: utils.Ptr("1"),
- ScrapeTimeout: utils.Ptr("2"),
- SampleLimit: utils.Ptr(int64(17)),
- StaticConfigs: &[]argus.StaticConfigs{
- {
- Labels: &map[string]string{"k1": "v1"},
- Targets: &[]string{"url1"},
- },
- {
- Labels: &map[string]string{"k2": "v2", "k3": "v3"},
- Targets: &[]string{"url1", "url3"},
- },
- {
- Labels: nil,
- Targets: &[]string{},
- },
- },
- },
- expected: Model{
- Id: types.StringValue("pid,iid,name"),
- ProjectId: types.StringValue("pid"),
- InstanceId: types.StringValue("iid"),
- Name: types.StringValue("name"),
- MetricsPath: types.StringValue("/m"),
- Scheme: types.StringValue("scheme"),
- ScrapeInterval: types.StringValue("1"),
- ScrapeTimeout: types.StringValue("2"),
- SampleLimit: types.Int64Value(17),
- SAML2: types.ObjectValueMust(saml2Types, map[string]attr.Value{
- "enable_url_parameters": types.BoolValue(false),
- }),
- BasicAuth: types.ObjectValueMust(basicAuthTypes, map[string]attr.Value{
- "username": types.StringValue("u"),
- "password": types.StringValue("p"),
- }),
- Targets: types.ListValueMust(types.ObjectType{AttrTypes: targetTypes}, []attr.Value{
- types.ObjectValueMust(targetTypes, map[string]attr.Value{
- "urls": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("url1")}),
- "labels": types.MapValueMust(types.StringType, map[string]attr.Value{
- "k1": types.StringValue("v1"),
- }),
- }),
- types.ObjectValueMust(targetTypes, map[string]attr.Value{
- "urls": types.ListValueMust(types.StringType, []attr.Value{types.StringValue("url1"), types.StringValue("url3")}),
- "labels": types.MapValueMust(types.StringType, map[string]attr.Value{
- "k2": types.StringValue("v2"),
- "k3": types.StringValue("v3"),
- }),
- }),
- types.ObjectValueMust(targetTypes, map[string]attr.Value{
- "urls": types.ListValueMust(types.StringType, []attr.Value{}),
- "labels": types.MapNull(types.StringType),
- }),
- }),
- },
- isValid: true,
- },
- {
- "response_nil_fail",
- nil,
- Model{},
- false,
- },
- {
- "no_resource_id",
- &argus.Job{},
- Model{},
- false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- state := &Model{
- ProjectId: tt.expected.ProjectId,
- InstanceId: tt.expected.InstanceId,
- }
- err := mapFields(context.Background(), tt.input, state)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
- if tt.isValid {
- diff := cmp.Diff(state, &tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
-
-func TestToCreatePayload(t *testing.T) {
- tests := []struct {
- description string
- input *Model
- inputSAML2 *saml2Model
- inputBasicAuth *basicAuthModel
- inputTargets []targetModel
- expected *argus.CreateScrapeConfigPayload
- isValid bool
- }{
- {
- "basic_ok",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- },
- &saml2Model{},
- &basicAuthModel{},
- []targetModel{},
- &argus.CreateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- // Defaults
- Scheme: utils.Ptr("https"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- StaticConfigs: &[]argus.CreateScrapeConfigPayloadStaticConfigsInner{},
- Params: &map[string]any{"saml2": []string{"enabled"}},
- },
- true,
- },
- {
- "ok - false enable_url_parameters",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- Name: types.StringValue("Name"),
- },
- &saml2Model{
- EnableURLParameters: types.BoolValue(false),
- },
- &basicAuthModel{},
- []targetModel{},
- &argus.CreateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- JobName: utils.Ptr("Name"),
- Params: &map[string]any{"saml2": []string{"disabled"}},
- // Defaults
- Scheme: utils.Ptr("https"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- StaticConfigs: &[]argus.CreateScrapeConfigPayloadStaticConfigsInner{},
- },
- true,
- },
- {
- "ok - true enable_url_parameters",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- Name: types.StringValue("Name"),
- },
- &saml2Model{
- EnableURLParameters: types.BoolValue(true),
- },
- &basicAuthModel{},
- []targetModel{},
- &argus.CreateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- JobName: utils.Ptr("Name"),
- Params: &map[string]any{"saml2": []string{"enabled"}},
- // Defaults
- Scheme: utils.Ptr("https"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- StaticConfigs: &[]argus.CreateScrapeConfigPayloadStaticConfigsInner{},
- },
- true,
- },
- {
- "ok - with basic auth",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- Name: types.StringValue("Name"),
- },
- &saml2Model{},
- &basicAuthModel{
- Username: types.StringValue("u"),
- Password: types.StringValue("p"),
- },
- []targetModel{},
- &argus.CreateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- JobName: utils.Ptr("Name"),
- BasicAuth: &argus.CreateScrapeConfigPayloadBasicAuth{
- Username: utils.Ptr("u"),
- Password: utils.Ptr("p"),
- },
- // Defaults
- Scheme: utils.Ptr("https"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- StaticConfigs: &[]argus.CreateScrapeConfigPayloadStaticConfigsInner{},
- Params: &map[string]any{"saml2": []string{"enabled"}},
- },
- true,
- },
- {
- "ok - with targets",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- Name: types.StringValue("Name"),
- },
- &saml2Model{},
- &basicAuthModel{},
- []targetModel{
- {
- URLs: types.ListValueMust(types.StringType, []attr.Value{types.StringValue("url1")}),
- Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"k1": types.StringValue("v1")}),
- },
- {
- URLs: types.ListValueMust(types.StringType, []attr.Value{types.StringValue("url1"), types.StringValue("url3")}),
- Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"k2": types.StringValue("v2"), "k3": types.StringValue("v3")}),
- },
- {
- URLs: types.ListValueMust(types.StringType, []attr.Value{}),
- Labels: types.MapNull(types.StringType),
- },
- },
- &argus.CreateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- JobName: utils.Ptr("Name"),
- StaticConfigs: &[]argus.CreateScrapeConfigPayloadStaticConfigsInner{
- {
- Targets: &[]string{"url1"},
- Labels: &map[string]interface{}{"k1": "v1"},
- },
- {
- Targets: &[]string{"url1", "url3"},
- Labels: &map[string]interface{}{"k2": "v2", "k3": "v3"},
- },
- {
- Targets: &[]string{},
- Labels: &map[string]interface{}{},
- },
- },
- // Defaults
- Scheme: utils.Ptr("https"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- Params: &map[string]any{"saml2": []string{"enabled"}},
- },
- true,
- },
- {
- "nil_model",
- nil,
- nil,
- nil,
- nil,
- nil,
- false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- output, err := toCreatePayload(context.Background(), tt.input, tt.inputSAML2, tt.inputBasicAuth, tt.inputTargets)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
- if tt.isValid {
- diff := cmp.Diff(output, tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
-
-func TestToUpdatePayload(t *testing.T) {
- tests := []struct {
- description string
- input *Model
- inputSAML2 *saml2Model
- basicAuthModel *basicAuthModel
- inputTargets []targetModel
- expected *argus.UpdateScrapeConfigPayload
- isValid bool
- }{
- {
- "basic_ok",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- },
- &saml2Model{},
- &basicAuthModel{},
- []targetModel{},
- &argus.UpdateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- // Defaults
- Scheme: utils.Ptr("https"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- StaticConfigs: &[]argus.UpdateScrapeConfigPayloadStaticConfigsInner{},
- },
- true,
- },
- {
- "ok - true enable_url_parameters",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- Scheme: types.StringValue("http"),
- },
- &saml2Model{
- EnableURLParameters: types.BoolValue(true),
- },
- &basicAuthModel{},
- []targetModel{},
- &argus.UpdateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- // Defaults
- Scheme: utils.Ptr("http"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- StaticConfigs: &[]argus.UpdateScrapeConfigPayloadStaticConfigsInner{},
- Params: &map[string]any{"saml2": []string{"enabled"}},
- },
- true,
- },
- {
- "ok - false enable_url_parameters",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- Scheme: types.StringValue("http"),
- },
- &saml2Model{
- EnableURLParameters: types.BoolValue(false),
- },
- &basicAuthModel{},
- []targetModel{},
- &argus.UpdateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- // Defaults
- Scheme: utils.Ptr("http"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- StaticConfigs: &[]argus.UpdateScrapeConfigPayloadStaticConfigsInner{},
- Params: &map[string]any{"saml2": []string{"disabled"}},
- },
- true,
- },
- {
- "ok - with basic auth",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- Name: types.StringValue("Name"),
- },
- &saml2Model{},
- &basicAuthModel{
- Username: types.StringValue("u"),
- Password: types.StringValue("p"),
- },
- []targetModel{},
- &argus.UpdateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- BasicAuth: &argus.CreateScrapeConfigPayloadBasicAuth{
- Username: utils.Ptr("u"),
- Password: utils.Ptr("p"),
- },
- // Defaults
- Scheme: utils.Ptr("https"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- StaticConfigs: &[]argus.UpdateScrapeConfigPayloadStaticConfigsInner{},
- },
- true,
- },
- {
- "ok - with targets",
- &Model{
- MetricsPath: types.StringValue("/metrics"),
- Name: types.StringValue("Name"),
- },
- &saml2Model{},
- &basicAuthModel{},
- []targetModel{
- {
- URLs: types.ListValueMust(types.StringType, []attr.Value{types.StringValue("url1")}),
- Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"k1": types.StringValue("v1")}),
- },
- {
- URLs: types.ListValueMust(types.StringType, []attr.Value{types.StringValue("url1"), types.StringValue("url3")}),
- Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"k2": types.StringValue("v2"), "k3": types.StringValue("v3")}),
- },
- {
- URLs: types.ListValueMust(types.StringType, []attr.Value{}),
- Labels: types.MapNull(types.StringType),
- },
- },
- &argus.UpdateScrapeConfigPayload{
- MetricsPath: utils.Ptr("/metrics"),
- StaticConfigs: &[]argus.UpdateScrapeConfigPayloadStaticConfigsInner{
- {
- Targets: &[]string{"url1"},
- Labels: &map[string]interface{}{"k1": "v1"},
- },
- {
- Targets: &[]string{"url1", "url3"},
- Labels: &map[string]interface{}{"k2": "v2", "k3": "v3"},
- },
- {
- Targets: &[]string{},
- Labels: &map[string]interface{}{},
- },
- },
- // Defaults
- Scheme: utils.Ptr("https"),
- ScrapeInterval: utils.Ptr("5m"),
- ScrapeTimeout: utils.Ptr("2m"),
- SampleLimit: utils.Ptr(float64(5000)),
- },
- true,
- },
- {
- "nil_model",
- nil,
- nil,
- nil,
- nil,
- nil,
- false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.description, func(t *testing.T) {
- output, err := toUpdatePayload(context.Background(), tt.input, tt.inputSAML2, tt.basicAuthModel, tt.inputTargets)
- if !tt.isValid && err == nil {
- t.Fatalf("Should have failed")
- }
- if tt.isValid && err != nil {
- t.Fatalf("Should not have failed: %v", err)
- }
- if tt.isValid {
- diff := cmp.Diff(output, tt.expected)
- if diff != "" {
- t.Fatalf("Data does not match: %s", diff)
- }
- }
- })
- }
-}
diff --git a/stackit/internal/services/dns/recordset/datasource.go b/stackit/internal/services/dns/recordset/datasource.go
index ca2ffbe5..d0041668 100644
--- a/stackit/internal/services/dns/recordset/datasource.go
+++ b/stackit/internal/services/dns/recordset/datasource.go
@@ -3,6 +3,7 @@ package dns
import (
"context"
"fmt"
+ "net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
@@ -13,6 +14,7 @@ import (
"github.com/stackitcloud/stackit-sdk-go/services/dns"
"github.com/stackitcloud/stackit-sdk-go/services/dns/wait"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -161,7 +163,17 @@ func (d *recordSetDataSource) Read(ctx context.Context, req datasource.ReadReque
ctx = tflog.SetField(ctx, "record_set_id", recordSetId)
recordSetResp, err := d.client.GetRecordSet(ctx, projectId, zoneId, recordSetId).Execute()
if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading record set", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading record set",
+ fmt.Sprintf("The record set %q or zone %q does not exist in project %q.", recordSetId, zoneId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
if recordSetResp != nil && recordSetResp.Rrset.State != nil && *recordSetResp.Rrset.State == wait.DeleteSuccess {
diff --git a/stackit/internal/services/dns/zone/datasource.go b/stackit/internal/services/dns/zone/datasource.go
index 99e23714..0d545f75 100644
--- a/stackit/internal/services/dns/zone/datasource.go
+++ b/stackit/internal/services/dns/zone/datasource.go
@@ -3,6 +3,7 @@ package dns
import (
"context"
"fmt"
+ "net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
@@ -13,6 +14,7 @@ import (
"github.com/stackitcloud/stackit-sdk-go/services/dns"
"github.com/stackitcloud/stackit-sdk-go/services/dns/wait"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -191,7 +193,17 @@ func (d *zoneDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
zoneResp, err := d.client.GetZone(ctx, projectId, zoneId).Execute()
if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading zone", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading zone",
+ fmt.Sprintf("Zone with ID %q does not exist in project %q.", zoneId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
if zoneResp != nil && zoneResp.Zone.State != nil && *zoneResp.Zone.State == wait.DeleteSuccess {
diff --git a/stackit/internal/services/iaas/affinitygroup/datasource.go b/stackit/internal/services/iaas/affinitygroup/datasource.go
index cbbec093..656f1ea2 100644
--- a/stackit/internal/services/iaas/affinitygroup/datasource.go
+++ b/stackit/internal/services/iaas/affinitygroup/datasource.go
@@ -7,6 +7,7 @@ import (
"regexp"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
@@ -17,7 +18,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
)
@@ -143,12 +143,17 @@ func (d *affinityGroupDatasource) Read(ctx context.Context, req datasource.ReadR
affinityGroupResp, err := d.client.GetAffinityGroupExecute(ctx, projectId, affinityGroupId)
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading affinity group", fmt.Sprintf("Call API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading affinity group",
+ fmt.Sprintf("Affinity group with ID %q does not exist in project %q.", affinityGroupId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/image/datasource.go b/stackit/internal/services/iaas/image/datasource.go
index 04e676e6..d9a231e2 100644
--- a/stackit/internal/services/iaas/image/datasource.go
+++ b/stackit/internal/services/iaas/image/datasource.go
@@ -15,9 +15,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -238,12 +238,17 @@ func (r *imageDataSource) Read(ctx context.Context, req datasource.ReadRequest,
imageResp, err := r.client.GetImage(ctx, projectId, imageId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading image", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading image",
+ fmt.Sprintf("Image with ID %q does not exist in project %q.", imageId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/keypair/datasource.go b/stackit/internal/services/iaas/keypair/datasource.go
index d6cbd1b3..123dad04 100644
--- a/stackit/internal/services/iaas/keypair/datasource.go
+++ b/stackit/internal/services/iaas/keypair/datasource.go
@@ -2,18 +2,16 @@ package keypair
import (
"context"
- "errors"
"fmt"
- "net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
)
// Ensure the implementation satisfies the expected interfaces.
@@ -117,18 +115,15 @@ func (r *keyPairDataSource) Read(ctx context.Context, req datasource.ReadRequest
keypairResp, err := r.client.GetKeyPair(ctx, name).Execute()
if err != nil {
- var oapiErr *oapierror.GenericOpenAPIError
- ok := errors.As(err, &oapiErr)
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- summary := fmt.Sprintf("Key Pair with name %q does not exists", name)
- description := fmt.Sprintf("Key Pair with name %q cannot be found. A key pair can be added with the resource \"stackit_key_pair\"", name)
- diags.AddError(summary, description)
- resp.Diagnostics.Append(diags...)
-
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading key pair", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading key pair",
+ fmt.Sprintf("Key pair with name %q does not exist.", name),
+ nil,
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/network/datasource.go b/stackit/internal/services/iaas/network/datasource.go
index 9a0c80b5..bda0f85f 100644
--- a/stackit/internal/services/iaas/network/datasource.go
+++ b/stackit/internal/services/iaas/network/datasource.go
@@ -15,7 +15,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
@@ -222,12 +221,17 @@ func (d *networkDataSource) Read(ctx context.Context, req datasource.ReadRequest
networkResp, err := d.client.GetNetwork(ctx, projectId, networkId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading network",
+ fmt.Sprintf("Network with ID %q does not exist in project %q.", networkId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/networkarea/datasource.go b/stackit/internal/services/iaas/networkarea/datasource.go
index 2f413308..3614f0b0 100644
--- a/stackit/internal/services/iaas/networkarea/datasource.go
+++ b/stackit/internal/services/iaas/networkarea/datasource.go
@@ -14,9 +14,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -196,12 +196,17 @@ func (d *networkAreaDataSource) Read(ctx context.Context, req datasource.ReadReq
networkAreaResp, err := d.client.GetNetworkArea(ctx, organizationId, networkAreaId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network area", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading network area",
+ fmt.Sprintf("Network area with ID %q does not exist in organization %q.", networkAreaId, organizationId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Organization with ID %q not found or forbidden access", organizationId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/networkarearoute/datasource.go b/stackit/internal/services/iaas/networkarearoute/datasource.go
index 4f8ffa8b..b0884407 100644
--- a/stackit/internal/services/iaas/networkarearoute/datasource.go
+++ b/stackit/internal/services/iaas/networkarearoute/datasource.go
@@ -11,9 +11,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -141,12 +141,17 @@ func (d *networkAreaRouteDataSource) Read(ctx context.Context, req datasource.Re
networkAreaRouteResp, err := d.client.GetNetworkAreaRoute(ctx, organizationId, networkAreaId, networkAreaRouteId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network area route", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading network area route",
+ fmt.Sprintf("Network area route with ID %q or network area with ID %q does not exist in organization %q.", networkAreaRouteId, networkAreaId, organizationId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Organization with ID %q not found or forbidden access", organizationId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/networkinterface/datasource.go b/stackit/internal/services/iaas/networkinterface/datasource.go
index 444057ee..6a54e66d 100644
--- a/stackit/internal/services/iaas/networkinterface/datasource.go
+++ b/stackit/internal/services/iaas/networkinterface/datasource.go
@@ -11,7 +11,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
@@ -170,12 +169,17 @@ func (d *networkInterfaceDataSource) Read(ctx context.Context, req datasource.Re
networkInterfaceResp, err := d.client.GetNic(ctx, projectId, networkId, networkInterfaceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network interface", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading network interface",
+ fmt.Sprintf("Network interface with ID %q or network with ID %q does not exist in project %q.", networkInterfaceId, networkId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/publicip/datasource.go b/stackit/internal/services/iaas/publicip/datasource.go
index 0123bdfb..05506808 100644
--- a/stackit/internal/services/iaas/publicip/datasource.go
+++ b/stackit/internal/services/iaas/publicip/datasource.go
@@ -11,9 +11,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -135,12 +135,17 @@ func (d *publicIpDataSource) Read(ctx context.Context, req datasource.ReadReques
publicIpResp, err := d.client.GetPublicIP(ctx, projectId, publicIpId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading public IP", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading public ip",
+ fmt.Sprintf("Public ip with ID %q does not exist in project %q.", publicIpId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/publicipranges/datasource.go b/stackit/internal/services/iaas/publicipranges/datasource.go
index 5e213ff1..2630c1f8 100644
--- a/stackit/internal/services/iaas/publicipranges/datasource.go
+++ b/stackit/internal/services/iaas/publicipranges/datasource.go
@@ -9,6 +9,7 @@ import (
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
@@ -19,7 +20,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
)
// Ensure the implementation satisfies the expected interfaces.
@@ -130,12 +130,17 @@ func (d *publicIpRangesDataSource) Read(ctx context.Context, req datasource.Read
}
publicIpRangeResp, err := d.client.ListPublicIPRangesExecute(ctx)
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading public IP ranges", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading public ip ranges",
+ "Public ip ranges cannot be found",
+ map[int]string{
+ http.StatusForbidden: "Forbidden access",
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/securitygroup/datasource.go b/stackit/internal/services/iaas/securitygroup/datasource.go
index 8ea4ad99..707dbc11 100644
--- a/stackit/internal/services/iaas/securitygroup/datasource.go
+++ b/stackit/internal/services/iaas/securitygroup/datasource.go
@@ -11,9 +11,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -135,12 +135,17 @@ func (d *securityGroupDataSource) Read(ctx context.Context, req datasource.ReadR
securityGroupResp, err := d.client.GetSecurityGroup(ctx, projectId, securityGroupId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading security group", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading security group",
+ fmt.Sprintf("Security group with ID %q does not exist in project %q.", securityGroupId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/securitygrouprule/datasource.go b/stackit/internal/services/iaas/securitygrouprule/datasource.go
index 7568e243..052ab0a4 100644
--- a/stackit/internal/services/iaas/securitygrouprule/datasource.go
+++ b/stackit/internal/services/iaas/securitygrouprule/datasource.go
@@ -10,7 +10,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
@@ -192,12 +191,17 @@ func (d *securityGroupRuleDataSource) Read(ctx context.Context, req datasource.R
securityGroupRuleResp, err := d.client.GetSecurityGroupRule(ctx, projectId, securityGroupId, securityGroupRuleId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading security group rule", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading security group rule",
+ fmt.Sprintf("Security group rule with ID %q or security group with ID %q does not exist in project %q.", securityGroupRuleId, securityGroupId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/server/datasource.go b/stackit/internal/services/iaas/server/datasource.go
index 3e806028..1b8d1aa7 100644
--- a/stackit/internal/services/iaas/server/datasource.go
+++ b/stackit/internal/services/iaas/server/datasource.go
@@ -16,9 +16,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -213,12 +213,17 @@ func (r *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
serverReq = serverReq.Details(true)
serverResp, err := serverReq.Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading server", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading server",
+ fmt.Sprintf("Server with ID %q does not exist in project %q.", serverId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/iaas/volume/datasource.go b/stackit/internal/services/iaas/volume/datasource.go
index db3a0e1b..4d01b8d1 100644
--- a/stackit/internal/services/iaas/volume/datasource.go
+++ b/stackit/internal/services/iaas/volume/datasource.go
@@ -11,7 +11,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
@@ -166,12 +165,17 @@ func (d *volumeDataSource) Read(ctx context.Context, req datasource.ReadRequest,
volumeResp, err := d.client.GetVolume(ctx, projectId, volumeId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading volume", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading volume",
+ fmt.Sprintf("Volume with ID %q does not exist in project %q.", volumeId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/loadbalancer/loadbalancer/datasource.go b/stackit/internal/services/loadbalancer/loadbalancer/datasource.go
index d8c4921c..33514471 100644
--- a/stackit/internal/services/loadbalancer/loadbalancer/datasource.go
+++ b/stackit/internal/services/loadbalancer/loadbalancer/datasource.go
@@ -18,7 +18,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer"
)
@@ -336,11 +335,17 @@ func (r *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRe
lbResp, err := r.client.GetLoadBalancer(ctx, projectId, region, name).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading load balancer", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading load balancer",
+ fmt.Sprintf("Load balancer with name %q does not exist in project %q.", name, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/logme/credential/datasource.go b/stackit/internal/services/logme/credential/datasource.go
index 4c7c3b5e..8c0218c8 100644
--- a/stackit/internal/services/logme/credential/datasource.go
+++ b/stackit/internal/services/logme/credential/datasource.go
@@ -9,11 +9,11 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/logme"
)
@@ -152,11 +152,17 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
recordSetResp, err := r.client.GetCredentials(ctx, projectId, instanceId, credentialId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading credential",
+ fmt.Sprintf("Credential with ID %q or instance with ID %q does not exist in project %q.", credentialId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/logme/instance/datasource.go b/stackit/internal/services/logme/instance/datasource.go
index e3adf50a..46686ac7 100644
--- a/stackit/internal/services/logme/instance/datasource.go
+++ b/stackit/internal/services/logme/instance/datasource.go
@@ -10,11 +10,11 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/logme"
)
@@ -272,11 +272,18 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ http.StatusGone: fmt.Sprintf("Instance %q is gone.", instanceId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/mariadb/credential/datasource.go b/stackit/internal/services/mariadb/credential/datasource.go
index 62cc0c36..174e9eff 100644
--- a/stackit/internal/services/mariadb/credential/datasource.go
+++ b/stackit/internal/services/mariadb/credential/datasource.go
@@ -9,12 +9,12 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/mariadb"
)
@@ -160,11 +160,17 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
recordSetResp, err := r.client.GetCredentials(ctx, projectId, instanceId, credentialId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading credential",
+ fmt.Sprintf("Credential with ID %q or instance with ID %q does not exist in project %q.", credentialId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/mariadb/instance/datasource.go b/stackit/internal/services/mariadb/instance/datasource.go
index 92839256..2134d6f0 100644
--- a/stackit/internal/services/mariadb/instance/datasource.go
+++ b/stackit/internal/services/mariadb/instance/datasource.go
@@ -10,11 +10,11 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/mariadb"
)
@@ -208,11 +208,18 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ http.StatusGone: fmt.Sprintf("Instance %q is gone.", instanceId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/mongodbflex/instance/datasource.go b/stackit/internal/services/mongodbflex/instance/datasource.go
index 981d23ff..f2d01453 100644
--- a/stackit/internal/services/mongodbflex/instance/datasource.go
+++ b/stackit/internal/services/mongodbflex/instance/datasource.go
@@ -10,12 +10,12 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/mongodbflex"
)
@@ -213,11 +213,17 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
ctx = tflog.SetField(ctx, "instance_id", instanceId)
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/mongodbflex/user/datasource.go b/stackit/internal/services/mongodbflex/user/datasource.go
index ebebd2a3..2b40a051 100644
--- a/stackit/internal/services/mongodbflex/user/datasource.go
+++ b/stackit/internal/services/mongodbflex/user/datasource.go
@@ -11,12 +11,12 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/mongodbflex"
)
@@ -165,11 +165,17 @@ func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
recordSetResp, err := r.client.GetUser(ctx, projectId, instanceId, userId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading user",
+ fmt.Sprintf("User with ID %q or instance with ID %q does not exist in project %q.", userId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/objectstorage/bucket/datasource.go b/stackit/internal/services/objectstorage/bucket/datasource.go
index 090e8efa..562e989c 100644
--- a/stackit/internal/services/objectstorage/bucket/datasource.go
+++ b/stackit/internal/services/objectstorage/bucket/datasource.go
@@ -14,7 +14,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
)
@@ -147,11 +146,17 @@ func (r *bucketDataSource) Read(ctx context.Context, req datasource.ReadRequest,
bucketResp, err := r.client.GetBucket(ctx, projectId, region, bucketName).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading bucket", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading bucket",
+ fmt.Sprintf("Bucket with name %q does not exist in project %q.", bucketName, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/objectstorage/credential/datasource.go b/stackit/internal/services/objectstorage/credential/datasource.go
index 307176c9..82b23e2f 100644
--- a/stackit/internal/services/objectstorage/credential/datasource.go
+++ b/stackit/internal/services/objectstorage/credential/datasource.go
@@ -15,7 +15,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
)
@@ -157,22 +156,27 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
credentialsGroupResp, err := r.client.ListAccessKeys(ctx, projectId, region).CredentialsGroup(credentialsGroupId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credentials", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading credential",
+ fmt.Sprintf("Credential group with ID %q does not exist in project %q.", credentialsGroupId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
if credentialsGroupResp == nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credentials", fmt.Sprintf("Response is nil: %v", err))
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Reading credentials", fmt.Sprintf("Response is nil: %v", err))
return
}
credential := findCredential(*credentialsGroupResp, credentialId)
if credential == nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", "Credential not found")
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Reading credential", fmt.Sprintf("Credential with ID %q not found in credentials group %q", credentialId, credentialsGroupId))
return
}
diff --git a/stackit/internal/services/objectstorage/credentialsgroup/datasource.go b/stackit/internal/services/objectstorage/credentialsgroup/datasource.go
index 56dd8b95..e1850111 100644
--- a/stackit/internal/services/objectstorage/credentialsgroup/datasource.go
+++ b/stackit/internal/services/objectstorage/credentialsgroup/datasource.go
@@ -152,7 +152,7 @@ func (r *credentialsGroupDataSource) Read(ctx context.Context, req datasource.Re
}
if !found {
resp.State.RemoveResource(ctx)
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credentials group", "Credentials group not found")
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credentials group", fmt.Sprintf("Credentials group with ID %q does not exists in project %q", credentialsGroupId, projectId))
return
}
diff --git a/stackit/internal/services/observability/credential/resource.go b/stackit/internal/services/observability/credential/resource.go
index c940b268..ba3a55db 100644
--- a/stackit/internal/services/observability/credential/resource.go
+++ b/stackit/internal/services/observability/credential/resource.go
@@ -15,10 +15,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/observability"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- argusCredentialResource "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/argus/credential"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -26,7 +25,6 @@ import (
var (
_ resource.Resource = &credentialResource{}
_ resource.ResourceWithConfigure = &credentialResource{}
- _ resource.ResourceWithMoveState = &credentialResource{}
)
type Model struct {
@@ -88,40 +86,6 @@ func (r *credentialResource) Configure(ctx context.Context, req resource.Configu
tflog.Info(ctx, "Observability credential client configured")
}
-func (r *credentialResource) MoveState(_ context.Context) []resource.StateMover {
- return []resource.StateMover{
- {
- SourceSchema: &argusCredentialResource.Schema,
- StateMover: func(ctx context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) {
- if req.SourceTypeName != "stackit_argus_credential" {
- return
- }
-
- // Checks source provider
- if !strings.HasSuffix(req.SourceProviderAddress, "stackitcloud/stackit") {
- return
- }
-
- var sourceStateData argusCredentialResource.Model
- resp.Diagnostics.Append(req.SourceState.Get(ctx, &sourceStateData)...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- targetStateData := Model{
- Id: sourceStateData.Id,
- ProjectId: sourceStateData.ProjectId,
- InstanceId: sourceStateData.InstanceId,
- Username: sourceStateData.Username,
- Password: sourceStateData.Password,
- }
-
- resp.Diagnostics.Append(resp.TargetState.Set(ctx, targetStateData)...)
- },
- },
- }
-}
-
func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Observability credential resource schema. Must have a `region` specified in the provider configuration.",
@@ -246,12 +210,17 @@ func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest,
userName := model.Username.ValueString()
_, err := r.client.GetCredentials(ctx, instanceId, projectId, userName).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading credential",
+ fmt.Sprintf("Credential with username %q or instance with ID %q does not exist in project %q.", userName, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diags = resp.State.Set(ctx, model)
diff --git a/stackit/internal/services/observability/instance/datasource.go b/stackit/internal/services/observability/instance/datasource.go
index 15ea5471..271b89ab 100644
--- a/stackit/internal/services/observability/instance/datasource.go
+++ b/stackit/internal/services/observability/instance/datasource.go
@@ -12,10 +12,10 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/observability"
"github.com/stackitcloud/stackit-sdk-go/services/observability/wait"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -384,11 +384,17 @@ func (d *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
instanceId := model.InstanceId.ValueString()
instanceResp, err := d.client.GetInstance(ctx, instanceId, projectId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
if instanceResp != nil && instanceResp.Status != nil && *instanceResp.Status == wait.DeleteSuccess {
diff --git a/stackit/internal/services/observability/instance/resource.go b/stackit/internal/services/observability/instance/resource.go
index f1976413..315f793e 100644
--- a/stackit/internal/services/observability/instance/resource.go
+++ b/stackit/internal/services/observability/instance/resource.go
@@ -31,7 +31,6 @@ import (
"github.com/stackitcloud/stackit-sdk-go/services/observability/wait"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- argusInstanceResource "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/argus/instance"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -44,7 +43,6 @@ var (
_ resource.Resource = &instanceResource{}
_ resource.ResourceWithConfigure = &instanceResource{}
_ resource.ResourceWithImportState = &instanceResource{}
- _ resource.ResourceWithMoveState = &instanceResource{}
)
type Model struct {
@@ -377,64 +375,6 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure
tflog.Info(ctx, "Observability instance client configured")
}
-// MoveState moves the state of a `stackit_argus_instance` resource to a `stackit_observability_instance` resource.
-func (r *instanceResource) MoveState(_ context.Context) []resource.StateMover {
- return []resource.StateMover{
- {
- SourceSchema: &argusInstanceResource.Schema,
- StateMover: func(ctx context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) {
- if req.SourceTypeName != "stackit_argus_instance" {
- return
- }
-
- // Checks source provider
- if !strings.HasSuffix(req.SourceProviderAddress, "stackitcloud/stackit") {
- return
- }
-
- var sourceStateData argusInstanceResource.Model
- resp.Diagnostics.Append(req.SourceState.Get(ctx, &sourceStateData)...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- targetStateData := Model{
- Id: sourceStateData.Id,
- ProjectId: sourceStateData.ProjectId,
- InstanceId: sourceStateData.InstanceId,
- Name: sourceStateData.Name,
- PlanName: sourceStateData.PlanName,
- PlanId: sourceStateData.PlanId,
- Parameters: sourceStateData.Parameters,
- DashboardURL: sourceStateData.DashboardURL,
- IsUpdatable: sourceStateData.IsUpdatable,
- GrafanaURL: sourceStateData.GrafanaURL,
- GrafanaPublicReadAccess: sourceStateData.GrafanaPublicReadAccess,
- GrafanaInitialAdminPassword: sourceStateData.GrafanaInitialAdminPassword,
- GrafanaInitialAdminUser: sourceStateData.GrafanaInitialAdminUser,
- MetricsRetentionDays: sourceStateData.MetricsRetentionDays,
- MetricsRetentionDays5mDownsampling: sourceStateData.MetricsRetentionDays5mDownsampling,
- MetricsRetentionDays1hDownsampling: sourceStateData.MetricsRetentionDays1hDownsampling,
- MetricsURL: sourceStateData.MetricsURL,
- MetricsPushURL: sourceStateData.MetricsPushURL,
- TargetsURL: sourceStateData.TargetsURL,
- AlertingURL: sourceStateData.AlertingURL,
- LogsURL: sourceStateData.LogsURL,
- LogsPushURL: sourceStateData.LogsPushURL,
- JaegerTracesURL: sourceStateData.JaegerTracesURL,
- JaegerUIURL: sourceStateData.JaegerUIURL,
- OtlpTracesURL: sourceStateData.OtlpTracesURL,
- ZipkinSpansURL: sourceStateData.ZipkinSpansURL,
- ACL: sourceStateData.ACL,
- AlertConfig: sourceStateData.AlertConfig,
- }
-
- resp.Diagnostics.Append(resp.TargetState.Set(ctx, targetStateData)...)
- },
- },
- }
-}
-
// Schema defines the schema for the resource.
func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
diff --git a/stackit/internal/services/observability/scrapeconfig/datasource.go b/stackit/internal/services/observability/scrapeconfig/datasource.go
index 3af59aa7..b8ba9963 100644
--- a/stackit/internal/services/observability/scrapeconfig/datasource.go
+++ b/stackit/internal/services/observability/scrapeconfig/datasource.go
@@ -15,9 +15,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/observability"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -214,11 +214,17 @@ func (d *scrapeConfigDataSource) Read(ctx context.Context, req datasource.ReadRe
scResp, err := d.client.GetScrapeConfig(ctx, instanceId, scName, projectId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Unable to read scrape config", err.Error())
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading scrape config",
+ fmt.Sprintf("Scrape config with name %q or instance with ID %q does not exist in project %q.", scName, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/observability/scrapeconfig/resource.go b/stackit/internal/services/observability/scrapeconfig/resource.go
index 0bf680e9..25c5fb53 100644
--- a/stackit/internal/services/observability/scrapeconfig/resource.go
+++ b/stackit/internal/services/observability/scrapeconfig/resource.go
@@ -32,7 +32,6 @@ import (
"github.com/stackitcloud/stackit-sdk-go/services/observability/wait"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
- argusScrapeConfigResource "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/argus/scrapeconfig"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -49,7 +48,6 @@ var (
_ resource.Resource = &scrapeConfigResource{}
_ resource.ResourceWithConfigure = &scrapeConfigResource{}
_ resource.ResourceWithImportState = &scrapeConfigResource{}
- _ resource.ResourceWithMoveState = &scrapeConfigResource{}
)
type Model struct {
@@ -151,47 +149,6 @@ func (r *scrapeConfigResource) Configure(ctx context.Context, req resource.Confi
tflog.Info(ctx, "Observability scrape config client configured")
}
-func (r *scrapeConfigResource) MoveState(_ context.Context) []resource.StateMover {
- return []resource.StateMover{
- {
- SourceSchema: &argusScrapeConfigResource.Schema,
- StateMover: func(ctx context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) {
- if req.SourceTypeName != "stackit_argus_scrapeconfig" {
- return
- }
-
- // Checks source provider
- if !strings.HasSuffix(req.SourceProviderAddress, "stackitcloud/stackit") {
- return
- }
-
- var sourceStateData argusScrapeConfigResource.Model
- resp.Diagnostics.Append(req.SourceState.Get(ctx, &sourceStateData)...)
- if resp.Diagnostics.HasError() {
- return
- }
-
- targetStateData := Model{
- Id: sourceStateData.Id,
- ProjectId: sourceStateData.ProjectId,
- InstanceId: sourceStateData.InstanceId,
- Name: sourceStateData.Name,
- MetricsPath: sourceStateData.MetricsPath,
- Scheme: sourceStateData.Scheme,
- ScrapeInterval: sourceStateData.ScrapeInterval,
- ScrapeTimeout: sourceStateData.ScrapeTimeout,
- SampleLimit: sourceStateData.SampleLimit,
- SAML2: sourceStateData.SAML2,
- BasicAuth: sourceStateData.BasicAuth,
- Targets: sourceStateData.Targets,
- }
-
- resp.Diagnostics.Append(resp.TargetState.Set(ctx, targetStateData)...)
- },
- },
- }
-}
-
// Schema defines the schema for the resource.
func (r *scrapeConfigResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
diff --git a/stackit/internal/services/opensearch/credential/datasource.go b/stackit/internal/services/opensearch/credential/datasource.go
index fb54d921..a66bc0a5 100644
--- a/stackit/internal/services/opensearch/credential/datasource.go
+++ b/stackit/internal/services/opensearch/credential/datasource.go
@@ -9,12 +9,12 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/opensearch"
)
@@ -160,11 +160,17 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
recordSetResp, err := r.client.GetCredentials(ctx, projectId, instanceId, credentialId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading credential",
+ fmt.Sprintf("Credential with ID %q or instance with ID %q does not exist in project %q.", credentialId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/opensearch/instance/datasource.go b/stackit/internal/services/opensearch/instance/datasource.go
index befda6bf..fb445a7d 100644
--- a/stackit/internal/services/opensearch/instance/datasource.go
+++ b/stackit/internal/services/opensearch/instance/datasource.go
@@ -10,11 +10,11 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/opensearch"
)
@@ -241,11 +241,18 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ http.StatusGone: fmt.Sprintf("Instance %q is gone.", instanceId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/postgresflex/database/datasource.go b/stackit/internal/services/postgresflex/database/datasource.go
index 07f3fde9..e3e1046a 100644
--- a/stackit/internal/services/postgresflex/database/datasource.go
+++ b/stackit/internal/services/postgresflex/database/datasource.go
@@ -14,7 +14,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex"
)
@@ -160,11 +159,17 @@ func (r *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
databaseResp, err := getDatabase(ctx, r.client, projectId, region, instanceId, databaseId)
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading database", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading database",
+ fmt.Sprintf("Database with ID %q or instance with ID %q does not exist in project %q.", databaseId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/postgresflex/instance/datasource.go b/stackit/internal/services/postgresflex/instance/datasource.go
index 7ae3233c..0e4259a1 100644
--- a/stackit/internal/services/postgresflex/instance/datasource.go
+++ b/stackit/internal/services/postgresflex/instance/datasource.go
@@ -16,7 +16,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex"
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex/wait"
)
@@ -191,11 +190,17 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
ctx = tflog.SetField(ctx, "region", region)
instanceResp, err := r.client.GetInstance(ctx, projectId, region, instanceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
if instanceResp != nil && instanceResp.Item != nil && instanceResp.Item.Status != nil && *instanceResp.Item.Status == wait.InstanceStateDeleted {
diff --git a/stackit/internal/services/postgresflex/user/datasource.go b/stackit/internal/services/postgresflex/user/datasource.go
index 19edd9b4..f26bc948 100644
--- a/stackit/internal/services/postgresflex/user/datasource.go
+++ b/stackit/internal/services/postgresflex/user/datasource.go
@@ -17,7 +17,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex"
)
@@ -178,11 +177,17 @@ func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
recordSetResp, err := r.client.GetUser(ctx, projectId, region, instanceId, userId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading user",
+ fmt.Sprintf("User with ID %q or instance with ID %q does not exist in project %q.", userId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/rabbitmq/credential/datasource.go b/stackit/internal/services/rabbitmq/credential/datasource.go
index 2efd416b..7c9b8a17 100644
--- a/stackit/internal/services/rabbitmq/credential/datasource.go
+++ b/stackit/internal/services/rabbitmq/credential/datasource.go
@@ -9,12 +9,12 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/rabbitmq"
)
@@ -171,11 +171,17 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
recordSetResp, err := r.client.GetCredentials(ctx, projectId, instanceId, credentialId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading credential",
+ fmt.Sprintf("Credential with ID %q or instance with ID %q does not exist in project %q.", credentialId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/rabbitmq/instance/datasource.go b/stackit/internal/services/rabbitmq/instance/datasource.go
index 5a2ea563..a35cd48f 100644
--- a/stackit/internal/services/rabbitmq/instance/datasource.go
+++ b/stackit/internal/services/rabbitmq/instance/datasource.go
@@ -10,11 +10,11 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/rabbitmq"
)
@@ -237,11 +237,17 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/redis/credential/datasource.go b/stackit/internal/services/redis/credential/datasource.go
index 14794777..5df204ce 100644
--- a/stackit/internal/services/redis/credential/datasource.go
+++ b/stackit/internal/services/redis/credential/datasource.go
@@ -9,12 +9,12 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/redis"
)
@@ -162,11 +162,17 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
recordSetResp, err := r.client.GetCredentials(ctx, projectId, instanceId, credentialId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading credential",
+ fmt.Sprintf("Credential with ID %q or instance with ID %q does not exist in project %q.", credentialId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/redis/instance/datasource.go b/stackit/internal/services/redis/instance/datasource.go
index 9fc03e59..abe72ed1 100644
--- a/stackit/internal/services/redis/instance/datasource.go
+++ b/stackit/internal/services/redis/instance/datasource.go
@@ -10,11 +10,11 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/redis"
)
@@ -285,11 +285,18 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ http.StatusGone: fmt.Sprintf("Instance %q is gone.", instanceId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/resourcemanager/project/datasource.go b/stackit/internal/services/resourcemanager/project/datasource.go
index 8171a7c6..f67961f6 100644
--- a/stackit/internal/services/resourcemanager/project/datasource.go
+++ b/stackit/internal/services/resourcemanager/project/datasource.go
@@ -11,13 +11,13 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
)
@@ -165,17 +165,25 @@ func (d *projectDataSource) Read(ctx context.Context, req datasource.ReadRequest
// set project identifier. If projectId is provided, it takes precedence over containerId
var identifier = containerId
+ identifierType := "Container"
if projectId != "" {
identifier = projectId
+ identifierType = "Project"
}
projectResp, err := d.client.GetProject(ctx, identifier).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusForbidden {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading project", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading project",
+ fmt.Sprintf("%s with ID %q does not exist.", identifierType, identifier),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("%s with ID %q not found or forbidden access", identifierType, identifier),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/secretsmanager/instance/datasource.go b/stackit/internal/services/secretsmanager/instance/datasource.go
index d0a6e758..9551da15 100644
--- a/stackit/internal/services/secretsmanager/instance/datasource.go
+++ b/stackit/internal/services/secretsmanager/instance/datasource.go
@@ -10,11 +10,11 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/secretsmanager"
)
@@ -136,11 +136,17 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
aclList, err := r.client.ListACLs(ctx, projectId, instanceId).Execute()
diff --git a/stackit/internal/services/secretsmanager/user/datasource.go b/stackit/internal/services/secretsmanager/user/datasource.go
index 81a7bd4b..8d5496a2 100644
--- a/stackit/internal/services/secretsmanager/user/datasource.go
+++ b/stackit/internal/services/secretsmanager/user/datasource.go
@@ -10,12 +10,12 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/secretsmanager"
)
@@ -162,11 +162,17 @@ func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
userResp, err := r.client.GetUser(ctx, projectId, instanceId, userId).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading user",
+ fmt.Sprintf("User with ID %q or instance with ID %q does not exist in project %q.", userId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/serverbackup/schedule/schedule_datasource.go b/stackit/internal/services/serverbackup/schedule/schedule_datasource.go
index b9f9da57..777ebf4d 100644
--- a/stackit/internal/services/serverbackup/schedule/schedule_datasource.go
+++ b/stackit/internal/services/serverbackup/schedule/schedule_datasource.go
@@ -18,7 +18,6 @@ import (
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/serverbackup"
)
@@ -185,11 +184,17 @@ func (r *scheduleDataSource) Read(ctx context.Context, req datasource.ReadReques
scheduleResp, err := r.client.GetBackupSchedule(ctx, projectId, serverId, region, strconv.FormatInt(backupScheduleId, 10)).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading server backup schedule", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading server backup schedule",
+ fmt.Sprintf("Backup schedule with ID %q or server with ID %q does not exist in project %q.", strconv.FormatInt(backupScheduleId, 10), serverId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/serverbackup/schedule/schedules_datasource.go b/stackit/internal/services/serverbackup/schedule/schedules_datasource.go
index e81aacab..b99c618c 100644
--- a/stackit/internal/services/serverbackup/schedule/schedules_datasource.go
+++ b/stackit/internal/services/serverbackup/schedule/schedules_datasource.go
@@ -17,7 +17,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/serverbackup"
)
@@ -205,11 +204,17 @@ func (r *schedulesDataSource) Read(ctx context.Context, req datasource.ReadReque
schedules, err := r.client.ListBackupSchedules(ctx, projectId, serverId, region).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading server backup schedules", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading server backup schedules",
+ fmt.Sprintf("Server with ID %q does not exist in project %q.", serverId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/serverupdate/schedule/schedule_datasource.go b/stackit/internal/services/serverupdate/schedule/schedule_datasource.go
index 99e0b41a..18d54b90 100644
--- a/stackit/internal/services/serverupdate/schedule/schedule_datasource.go
+++ b/stackit/internal/services/serverupdate/schedule/schedule_datasource.go
@@ -17,7 +17,6 @@ import (
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/serverupdate"
)
@@ -171,11 +170,17 @@ func (r *scheduleDataSource) Read(ctx context.Context, req datasource.ReadReques
scheduleResp, err := r.client.GetUpdateSchedule(ctx, projectId, serverId, strconv.FormatInt(updateScheduleId, 10), region).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading server update schedule", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading server update schedule",
+ fmt.Sprintf("Update schedule with ID %q or server with ID %q does not exist in project %q.", strconv.FormatInt(updateScheduleId, 10), serverId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/serverupdate/schedule/schedules_datasource.go b/stackit/internal/services/serverupdate/schedule/schedules_datasource.go
index ee3ef78b..e9d6b474 100644
--- a/stackit/internal/services/serverupdate/schedule/schedules_datasource.go
+++ b/stackit/internal/services/serverupdate/schedule/schedules_datasource.go
@@ -17,7 +17,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/serverupdate"
)
@@ -192,11 +191,17 @@ func (r *schedulesDataSource) Read(ctx context.Context, req datasource.ReadReque
schedules, err := r.client.ListUpdateSchedules(ctx, projectId, serverId, region).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading server update schedules", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading server update schedules",
+ fmt.Sprintf("Server with ID %q does not exist in project %q.", serverId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/serviceaccount/account/datasource.go b/stackit/internal/services/serviceaccount/account/datasource.go
index 4cde8481..9d00fdfb 100644
--- a/stackit/internal/services/serviceaccount/account/datasource.go
+++ b/stackit/internal/services/serviceaccount/account/datasource.go
@@ -13,6 +13,7 @@ import (
"github.com/stackitcloud/stackit-sdk-go/services/serviceaccount"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
@@ -139,7 +140,15 @@ func (r *serviceAccountDataSource) Read(ctx context.Context, req datasource.Read
// Call the API to list service accounts in the specified project
listSaResp, err := r.client.ListServiceAccounts(ctx, projectId).Execute()
if err != nil {
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading service account", fmt.Sprintf("Error calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading service account",
+ fmt.Sprintf("Forbidden access for service account in project %q.", projectId),
+ map[int]string{},
+ )
+ resp.State.RemoveResource(ctx)
return
}
@@ -171,6 +180,6 @@ func (r *serviceAccountDataSource) Read(ctx context.Context, req datasource.Read
}
// If no matching service account is found, remove the resource from the state
- core.LogAndAddError(ctx, &resp.Diagnostics, "Service account not found", "")
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Reading service account", "Service account not found")
resp.State.RemoveResource(ctx)
}
diff --git a/stackit/internal/services/ske/cluster/datasource.go b/stackit/internal/services/ske/cluster/datasource.go
index fba6fc2a..d5e0690f 100644
--- a/stackit/internal/services/ske/cluster/datasource.go
+++ b/stackit/internal/services/ske/cluster/datasource.go
@@ -11,7 +11,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
@@ -353,11 +352,17 @@ func (r *clusterDataSource) Read(ctx context.Context, req datasource.ReadRequest
ctx = tflog.SetField(ctx, "region", region)
clusterResp, err := r.client.GetCluster(ctx, projectId, name).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading cluster", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading cluster",
+ fmt.Sprintf("Cluster with name %q does not exist in project %q.", name, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/ske/kubeconfig/resource.go b/stackit/internal/services/ske/kubeconfig/resource.go
index 00789ec3..52cb85fe 100644
--- a/stackit/internal/services/ske/kubeconfig/resource.go
+++ b/stackit/internal/services/ske/kubeconfig/resource.go
@@ -13,6 +13,7 @@ import (
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/resource"
@@ -24,8 +25,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
- "github.com/stackitcloud/stackit-sdk-go/core/utils"
+ sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
)
@@ -261,12 +261,17 @@ func (r *kubeconfigResource) Read(ctx context.Context, req resource.ReadRequest,
cluster, err := r.client.GetClusterExecute(ctx, projectId, clusterName)
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- return
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading kubeconfig", fmt.Sprintf("Could not get cluster(%s): %v", clusterName, err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading kubeconfig",
+ fmt.Sprintf("Kubeconfig with ID %q or cluster with name %q does not exist in project %q.", kubeconfigUUID, clusterName, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
@@ -391,7 +396,7 @@ func toCreatePayload(model *Model) (*ske.CreateKubeconfigPayload, error) {
expiration := conversion.Int64ValueToPointer(model.Expiration)
var expirationStringPtr *string
if expiration != nil {
- expirationStringPtr = utils.Ptr(strconv.FormatInt(*expiration, 10))
+ expirationStringPtr = sdkUtils.Ptr(strconv.FormatInt(*expiration, 10))
}
return &ske.CreateKubeconfigPayload{
diff --git a/stackit/internal/services/sqlserverflex/instance/datasource.go b/stackit/internal/services/sqlserverflex/instance/datasource.go
index 817dd67d..ea1570ac 100644
--- a/stackit/internal/services/sqlserverflex/instance/datasource.go
+++ b/stackit/internal/services/sqlserverflex/instance/datasource.go
@@ -16,7 +16,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
)
@@ -205,11 +204,17 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
ctx = tflog.SetField(ctx, "region", region)
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId, region).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading instance",
+ fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/services/sqlserverflex/user/datasource.go b/stackit/internal/services/sqlserverflex/user/datasource.go
index 6c1e284a..8cd066c1 100644
--- a/stackit/internal/services/sqlserverflex/user/datasource.go
+++ b/stackit/internal/services/sqlserverflex/user/datasource.go
@@ -17,7 +17,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
- "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
)
@@ -183,11 +182,17 @@ func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
recordSetResp, err := r.client.GetUser(ctx, projectId, instanceId, userId, region).Execute()
if err != nil {
- oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
- if ok && oapiErr.StatusCode == http.StatusNotFound {
- resp.State.RemoveResource(ctx)
- }
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Calling API: %v", err))
+ utils.LogError(
+ ctx,
+ &resp.Diagnostics,
+ err,
+ "Reading user",
+ fmt.Sprintf("User with ID %q or instance with ID %q does not exist in project %q.", userId, instanceId, projectId),
+ map[int]string{
+ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
+ },
+ )
+ resp.State.RemoveResource(ctx)
return
}
diff --git a/stackit/internal/utils/utils.go b/stackit/internal/utils/utils.go
index dc938bb2..330abfa2 100644
--- a/stackit/internal/utils/utils.go
+++ b/stackit/internal/utils/utils.go
@@ -1,13 +1,19 @@
package utils
import (
+ "context"
+ "errors"
"fmt"
"regexp"
"strings"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
)
const (
@@ -114,3 +120,27 @@ type value interface {
func IsUndefined(val value) bool {
return val.IsUnknown() || val.IsNull()
}
+
+// LogError logs errors. In descriptions different messages for http status codes can be passed. When no one matches the defaultDescription will be used
+func LogError(ctx context.Context, inputDiags *diag.Diagnostics, err error, summary, defaultDescription string, descriptions map[int]string) {
+ if err == nil {
+ return
+ }
+ tflog.Error(ctx, fmt.Sprintf("%s. Err: %v", summary, err))
+
+ var oapiErr *oapierror.GenericOpenAPIError
+ ok := errors.As(err, &oapiErr)
+ if !ok {
+ core.LogAndAddError(ctx, inputDiags, summary, fmt.Sprintf("Calling API: %v", err))
+ return
+ }
+
+ var description string
+ if len(descriptions) != 0 {
+ description, ok = descriptions[oapiErr.StatusCode]
+ }
+ if !ok || description == "" {
+ description = defaultDescription
+ }
+ core.LogAndAddError(ctx, inputDiags, summary, description)
+}
diff --git a/stackit/provider.go b/stackit/provider.go
index 820925cc..f33b961c 100644
--- a/stackit/provider.go
+++ b/stackit/provider.go
@@ -13,9 +13,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
- argusCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/argus/credential"
- argusInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/argus/instance"
- argusScrapeConfig "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/argus/scrapeconfig"
roleassignments "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/authorization/roleassignments"
dnsRecordSet "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/recordset"
dnsZone "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/zone"
@@ -465,8 +462,6 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
// DataSources defines the data sources implemented in the provider.
func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
- argusInstance.NewInstanceDataSource,
- argusScrapeConfig.NewScrapeConfigDataSource,
dnsZone.NewZoneDataSource,
dnsRecordSet.NewRecordSetDataSource,
iaasAffinityGroup.NewAffinityGroupDatasource,
@@ -520,9 +515,6 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
// Resources defines the resources implemented in the provider.
func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
resources := []func() resource.Resource{
- argusCredential.NewCredentialResource,
- argusInstance.NewInstanceResource,
- argusScrapeConfig.NewScrapeConfigResource,
dnsZone.NewZoneResource,
dnsRecordSet.NewRecordSetResource,
iaasAffinityGroup.NewAffinityGroupResource,