This reverts commit ddcbc77685.
This commit is contained in:
parent
ddcbc77685
commit
9c89e02e4c
45 changed files with 7718 additions and 71 deletions
1
.github/workflows/tf-acc-test.yaml
vendored
1
.github/workflows/tf-acc-test.yaml
vendored
|
|
@ -22,6 +22,7 @@ jobs:
|
|||
TF_ACC_DNS_CUSTOM_ENDPOINT: ${{ secrets.TF_ACC_DNS_CUSTOM_ENDPOINT }}
|
||||
TF_ACC_LOGME_CUSTOM_ENDPOINT: ${{ secrets.TF_ACC_LOGME_CUSTOM_ENDPOINT }}
|
||||
TF_ACC_MARIADB_CUSTOM_ENDPOINT: ${{ secrets.TF_ACC_MARIADB_CUSTOM_ENDPOINT }}
|
||||
TF_ACC_OBSERVABILITY_CUSTOM_ENDPOINT: ${{ secrets.TF_ACC_OBSERVABILITY_CUSTOM_ENDPOINT }}
|
||||
TF_ACC_OPENSEARCH_CUSTOM_ENDPOINT: ${{ secrets.TF_ACC_OPENSEARCH_CUSTOM_ENDPOINT }}
|
||||
TF_ACC_POSTGRESFLEX_CUSTOM_ENDPOINT: ${{ secrets.TF_ACC_POSTGRESFLEX_CUSTOM_ENDPOINT }}
|
||||
TF_ACC_POSTGRESQL_CUSTOM_ENDPOINT: ${{ secrets.TF_ACC_POSTGRESQL_CUSTOM_ENDPOINT }}
|
||||
|
|
|
|||
69
MIGRATION.md
69
MIGRATION.md
|
|
@ -128,36 +128,39 @@ resource "stackit_logme_credential" "example-credential" {
|
|||
|
||||
## Available resources
|
||||
|
||||
| Community provider | Official provider | Import available? | `id` format | Notes|
|
||||
|-|-|-|-|-|
|
||||
| stackit_argus_credential | stackit_argus_credential | :x: | | |
|
||||
| stackit_argus_instance | stackit_argus_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_argus_job | stackit_argus_scrapeconfig | :white_check_mark: | [project_id],[instance_id],[name] | |
|
||||
| stackit_elasticsearch_credential | | | | Service deprecated |
|
||||
| stackit_elasticsearch_instance | | | | Service deprecated |
|
||||
| stackit_kubernetes_cluster | stackit_ske_cluster | :white_check_mark: | [project_id],[name] | |
|
||||
| stackit_kubernetes_project | stackit_ske_project | :white_check_mark: | [project_id] | |
|
||||
| stackit_load_balancer | stackit_loadbalancer | :white_check_mark: | [project_id],[name] | |
|
||||
| stackit_logme_credential | stackit_logme_credential | :white_check_mark: | [project_id],[instance_id],[credential_id] | |
|
||||
| stackit_logme_instance | stackit_logme_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_mariadb_credential | stackit_mariadb_credential | :white_check_mark: | [project_id],[instance_id],[credential_id] | |
|
||||
| stackit_mariadb_instance | stackit_mariadb_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_mongodb_flex_instance | stackit_mongodbflex_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_mongodb_flex_user | stackit_mongodbflex_user | :warning: | [project_id],[instance_id],[user_id] | `password` field will be empty |
|
||||
| stackit_object_storage_bucket | stackit_objectstorage_bucket | :white_check_mark: | [project_id],[name] | |
|
||||
| stackit_object_storage_credential | stackit_objectstorage_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_object_storage_credentials_group | stackit_objectstorage_credentials_group | :white_check_mark: | [project_id],[credentials_group_id] | |
|
||||
| stackit_object_storage_project | | | | Resource deprecated |
|
||||
| stackit_opensearch_credential | stackit_opensearch_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_opensearch_instance | stackit_opensearch_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_postgres_credential | stackit_postgresql_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_postgres_flex_instance | stackit_postgresflex_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_postgres_flex_user | stackit_postgresflex_user | :warning: | [project_id],[instance_id],[user_id] | `password` field will be empty |
|
||||
| stackit_postgres_instance | stackit_postgresql_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_project | stackit_resourcemanager_project | :white_check_mark: | [container_id] | |
|
||||
| stackit_rabbitmq_credential | stackit_rabbitmq_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_rabbitmq_instance | stackit_rabbitmq_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_redis_credential | stackit_redis_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_redis_instance | stackit_redis_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_secrets_manager_instance | stackit_secretsmanager_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_secrets_manager_user | stackit_secretsmanager_user | :warning: | [project_id],[instance_id],[user_id] | `password` field will be empty |
|
||||
| Community provider | Official provider | Import available? | `id` format | Notes |
|
||||
|------------------------------------------|-----------------------------------------|-|-|------------------------------------------------------------------|
|
||||
| stackit_argus_credential | stackit_observability_credential | :x: | | Service deprecated, use stackit_observability_credential instead |
|
||||
| stackit_argus_instance | stackit_observability_instance | :white_check_mark: | [project_id],[instance_id] | Service deprecated, use stackit_observability_instance instead |
|
||||
| stackit_argus_job | stackit_observability_scrapeconfig | :white_check_mark: | [project_id],[instance_id],[name] | Service deprecated, use stackit_observability_scrapeconfig instead |
|
||||
| stackit_elasticsearch_credential | | | | Service deprecated |
|
||||
| stackit_elasticsearch_instance | | | | Service deprecated |
|
||||
| stackit_kubernetes_cluster | stackit_ske_cluster | :white_check_mark: | [project_id],[name] | |
|
||||
| stackit_kubernetes_project | stackit_ske_project | :white_check_mark: | [project_id] | |
|
||||
| stackit_load_balancer | stackit_loadbalancer | :white_check_mark: | [project_id],[name] | |
|
||||
| stackit_logme_credential | stackit_logme_credential | :white_check_mark: | [project_id],[instance_id],[credential_id] | |
|
||||
| stackit_logme_instance | stackit_logme_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_mariadb_credential | stackit_mariadb_credential | :white_check_mark: | [project_id],[instance_id],[credential_id] | |
|
||||
| stackit_mariadb_instance | stackit_mariadb_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_mongodb_flex_instance | stackit_mongodbflex_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_mongodb_flex_user | stackit_mongodbflex_user | :warning: | [project_id],[instance_id],[user_id] | `password` field will be empty |
|
||||
| stackit_object_storage_bucket | stackit_objectstorage_bucket | :white_check_mark: | [project_id],[name] | |
|
||||
| stackit_object_storage_credential | stackit_objectstorage_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_object_storage_credentials_group | stackit_objectstorage_credentials_group | :white_check_mark: | [project_id],[credentials_group_id] | |
|
||||
| stackit_object_storage_project | | | | Resource deprecated |
|
||||
| stackit_observability_credential | stackit_observability_credential | :x: | | |
|
||||
| stackit_observability_instance | stackit_observability_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_observability_job | stackit_observability_scrapeconfig | :white_check_mark: | [project_id],[instance_id],[name] | |
|
||||
| stackit_opensearch_credential | stackit_opensearch_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_opensearch_instance | stackit_opensearch_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_postgres_credential | stackit_postgresql_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_postgres_flex_instance | stackit_postgresflex_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_postgres_flex_user | stackit_postgresflex_user | :warning: | [project_id],[instance_id],[user_id] | `password` field will be empty |
|
||||
| stackit_postgres_instance | stackit_postgresql_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_project | stackit_resourcemanager_project | :white_check_mark: | [container_id] | |
|
||||
| stackit_rabbitmq_credential | stackit_rabbitmq_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_rabbitmq_instance | stackit_rabbitmq_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_redis_credential | stackit_redis_credential | :white_check_mark: | [project_id],[credentials_group_id],[credential_id] | |
|
||||
| stackit_redis_instance | stackit_redis_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_secrets_manager_instance | stackit_secretsmanager_instance | :white_check_mark: | [project_id],[instance_id] | |
|
||||
| stackit_secrets_manager_user | stackit_secretsmanager_user | :warning: | [project_id],[instance_id],[user_id] | `password` field will be empty |
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ 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
|
||||
|
|
@ -37,7 +40,7 @@ data "stackit_argus_instance" "example" {
|
|||
- `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`".
|
||||
- `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)
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ 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
|
||||
|
|
|
|||
151
docs/data-sources/observability_instance.md
Normal file
151
docs/data-sources/observability_instance.md
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_observability_instance Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Observability instance data source schema. Must have a region specified in the provider configuration.
|
||||
---
|
||||
|
||||
# stackit_observability_instance (Data Source)
|
||||
|
||||
Observability instance data source schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "stackit_observability_instance" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `instance_id` (String) The Observability 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 Observability 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 Observability instance.
|
||||
- `otlp_traces_url` (String)
|
||||
- `parameters` (Map of String) Additional parameters.
|
||||
- `plan_id` (String) The Observability plan ID.
|
||||
- `plan_name` (String) Specifies the Observability plan. E.g. `Monitoring-Medium-EU01`.
|
||||
- `targets_url` (String) Specifies Targets URL.
|
||||
- `zipkin_spans_url` (String)
|
||||
|
||||
<a id="nestedatt--alert_config"></a>
|
||||
### 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))
|
||||
|
||||
<a id="nestedatt--alert_config--global"></a>
|
||||
### 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).
|
||||
|
||||
|
||||
<a id="nestedatt--alert_config--receivers"></a>
|
||||
### 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))
|
||||
|
||||
<a id="nestedatt--alert_config--receivers--email_configs"></a>
|
||||
### 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
|
||||
|
||||
|
||||
<a id="nestedatt--alert_config--receivers--opsgenie_configs"></a>
|
||||
### 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.
|
||||
|
||||
|
||||
<a id="nestedatt--alert_config--receivers--webhooks_configs"></a>
|
||||
### 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
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--alert_config--route"></a>
|
||||
### 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))
|
||||
|
||||
<a id="nestedatt--alert_config--route--routes"></a>
|
||||
### 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).
|
||||
67
docs/data-sources/observability_scrapeconfig.md
Normal file
67
docs/data-sources/observability_scrapeconfig.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_observability_scrapeconfig Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Observability scrape config data source schema. Must have a region specified in the provider configuration.
|
||||
---
|
||||
|
||||
# stackit_observability_scrapeconfig (Data Source)
|
||||
|
||||
Observability scrape config data source schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
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"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `instance_id` (String) Observability 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))
|
||||
|
||||
<a id="nestedatt--basic_auth"></a>
|
||||
### Nested Schema for `basic_auth`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `password` (String, Sensitive) Specifies basic auth password.
|
||||
- `username` (String) Specifies basic auth username.
|
||||
|
||||
|
||||
<a id="nestedatt--saml2"></a>
|
||||
### Nested Schema for `saml2`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `enable_url_parameters` (Boolean) Specifies if URL parameters are enabled
|
||||
|
||||
|
||||
<a id="nestedatt--targets"></a>
|
||||
### Nested Schema for `targets`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `labels` (Map of String) Specifies labels.
|
||||
- `urls` (List of String) Specifies target URLs.
|
||||
|
|
@ -4,7 +4,7 @@ page_title: "stackit_postgresql_credential Data Source - stackit"
|
|||
subcategory: ""
|
||||
description: |-
|
||||
PostgreSQL credential data source schema. Must have a region specified in the provider configuration.
|
||||
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_user instead. For more details, check
|
||||
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackitpostgresflexuser instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||
---
|
||||
|
||||
# stackit_postgresql_credential (Data Source)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ page_title: "stackit_postgresql_instance Data Source - stackit"
|
|||
subcategory: ""
|
||||
description: |-
|
||||
PostgreSQL instance data source schema. Must have a region specified in the provider configuration.
|
||||
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. For more details, check
|
||||
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackitpostgresflexinstance instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||
---
|
||||
|
||||
# stackit_postgresql_instance (Data Source)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
page_title: "stackit_resourcemanager_project Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Resource Manager project data source schema. To identify the project, you need to provider either project_id or container_id. If you provide both, project_id will be used.
|
||||
Resource Manager project data source schema. To identify the project, you need to provider either projectid or containerid. If you provide both, project_id will be used.
|
||||
---
|
||||
|
||||
# stackit_resourcemanager_project (Data Source)
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@ This guide outlines the process of utilizing the HashiCorp Vault provider alongs
|
|||
}
|
||||
```
|
||||
|
||||
5. **Define Terraform Resource (Example: Argus Monitoring Instance)**
|
||||
5. **Define Terraform Resource (Example: Observability Monitoring Instance)**
|
||||
|
||||
```hcl
|
||||
resource "stackit_argus_instance" "example" {
|
||||
resource "stackit_observability_instance" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-instance"
|
||||
plan_name = "Monitoring-Medium-EU01"
|
||||
|
|
@ -71,7 +71,7 @@ This guide outlines the process of utilizing the HashiCorp Vault provider alongs
|
|||
delete_all_versions = true
|
||||
data_json = jsonencode(
|
||||
{
|
||||
grafana_password = stackit_argus_instance.example.grafana_initial_admin_password,
|
||||
grafana_password = stackit_observability_instance.example.grafana_initial_admin_password,
|
||||
other_secret = ...,
|
||||
}
|
||||
)
|
||||
|
|
@ -80,4 +80,4 @@ This guide outlines the process of utilizing the HashiCorp Vault provider alongs
|
|||
|
||||
## Note
|
||||
|
||||
This example can be adapted for various resources within the provider as well as any other Secret the user wants to set in the Secrets Manager instance. Adapting this examples means replacing the Argus Monitoring Grafana password with the appropriate value.
|
||||
This example can be adapted for various resources within the provider as well as any other Secret the user wants to set in the Secrets Manager instance. Adapting this examples means replacing the Observability Monitoring Grafana password with the appropriate value.
|
||||
|
|
@ -140,7 +140,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
|
|||
|
||||
### Optional
|
||||
|
||||
- `argus_custom_endpoint` (String) Custom endpoint for the Argus service
|
||||
- `argus_custom_endpoint` (String, Deprecated) Custom endpoint for the Argus service
|
||||
- `authorization_custom_endpoint` (String) Custom endpoint for the Membership service
|
||||
- `credentials_path` (String) Path of JSON from where the credentials are read. Takes precedence over the env var `STACKIT_CREDENTIALS_PATH`. Default value is `~/.stackit/credentials.json`.
|
||||
- `dns_custom_endpoint` (String) Custom endpoint for the DNS service
|
||||
|
|
@ -152,6 +152,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
|
|||
- `mariadb_custom_endpoint` (String) Custom endpoint for the MariaDB service
|
||||
- `mongodbflex_custom_endpoint` (String) Custom endpoint for the MongoDB Flex service
|
||||
- `objectstorage_custom_endpoint` (String) Custom endpoint for the Object Storage service
|
||||
- `observability_custom_endpoint` (String) Custom endpoint for the Observability service
|
||||
- `opensearch_custom_endpoint` (String) Custom endpoint for the OpenSearch service
|
||||
- `postgresflex_custom_endpoint` (String) Custom endpoint for the PostgresFlex service
|
||||
- `postgresql_custom_endpoint` (String) Custom endpoint for the PostgreSQL service
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ 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.
|
||||
---
|
||||
|
||||
# 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 Usage
|
||||
|
||||
```terraform
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ 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.
|
||||
---
|
||||
|
||||
# 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 Usage
|
||||
|
||||
```terraform
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ 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.
|
||||
---
|
||||
|
||||
# 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 Usage
|
||||
|
||||
```terraform
|
||||
|
|
|
|||
|
|
@ -8,25 +8,23 @@ description: |-
|
|||
To automate the creation of load balancers, OpenStack can be used to setup the supporting infrastructure.
|
||||
To set up the OpenStack provider, you can create a token through the STACKIT Portal, in your project's Infrastructure API page.
|
||||
There, the OpenStack user domain name, username, and password are generated and can be obtained. The provider can then be configured as follows:
|
||||
|
||||
```terraform
|
||||
terraform {
|
||||
required_providers {
|
||||
(...)
|
||||
openstack = {
|
||||
source = "terraform-provider-openstack/openstack"
|
||||
}
|
||||
}
|
||||
required_providers {
|
||||
(...)
|
||||
openstack = {
|
||||
source = "terraform-provider-openstack/openstack"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "openstack" {
|
||||
user_domain_name = "{OpenStack user domain name}"
|
||||
user_name = "{OpenStack username}"
|
||||
password = "{OpenStack password}"
|
||||
region = "RegionOne"
|
||||
auth_url = "https://keystone.api.iaas.eu01.stackit.cloud/v3"
|
||||
userdomainname = "{OpenStack user domain name}"
|
||||
username = "{OpenStack username}"
|
||||
password = "{OpenStack password}"
|
||||
region = "RegionOne"
|
||||
authurl = "https://keystone.api.iaas.eu01.stackit.cloud/v3"
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
Configuring the supporting infrastructure
|
||||
The example below uses OpenStack to create the network, router, a public IP address and a compute instance.
|
||||
---
|
||||
|
|
|
|||
34
docs/resources/observability_credential.md
Normal file
34
docs/resources/observability_credential.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_observability_credential Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Observability credential resource schema. Must have a region specified in the provider configuration.
|
||||
---
|
||||
|
||||
# stackit_observability_credential (Resource)
|
||||
|
||||
Observability credential resource schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "stackit_observability_credential" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `instance_id` (String) The Observability 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
|
||||
172
docs/resources/observability_instance.md
Normal file
172
docs/resources/observability_instance.md
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_observability_instance Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Observability instance resource schema. Must have a region specified in the provider configuration.
|
||||
---
|
||||
|
||||
# stackit_observability_instance (Resource)
|
||||
|
||||
Observability instance resource schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The name of the Observability instance.
|
||||
- `plan_name` (String) Specifies the Observability 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 Observability 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 Observability 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 Observability plan ID.
|
||||
- `targets_url` (String) Specifies Targets URL.
|
||||
- `zipkin_spans_url` (String)
|
||||
|
||||
<a id="nestedatt--alert_config"></a>
|
||||
### 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))
|
||||
|
||||
<a id="nestedatt--alert_config--receivers"></a>
|
||||
### 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))
|
||||
|
||||
<a id="nestedatt--alert_config--receivers--email_configs"></a>
|
||||
### 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
|
||||
|
||||
|
||||
<a id="nestedatt--alert_config--receivers--opsgenie_configs"></a>
|
||||
### 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.
|
||||
|
||||
|
||||
<a id="nestedatt--alert_config--receivers--webhooks_configs"></a>
|
||||
### 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
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--alert_config--route"></a>
|
||||
### 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))
|
||||
|
||||
<a id="nestedatt--alert_config--route--routes"></a>
|
||||
### 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).
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--alert_config--global"></a>
|
||||
### 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).
|
||||
85
docs/resources/observability_scrapeconfig.md
Normal file
85
docs/resources/observability_scrapeconfig.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_observability_scrapeconfig Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Observability scrape config resource schema. Must have a region specified in the provider configuration.
|
||||
---
|
||||
|
||||
# stackit_observability_scrapeconfig (Resource)
|
||||
|
||||
Observability scrape config resource schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `instance_id` (String) Observability 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`".
|
||||
|
||||
<a id="nestedatt--targets"></a>
|
||||
### Nested Schema for `targets`
|
||||
|
||||
Required:
|
||||
|
||||
- `urls` (List of String) Specifies target URLs.
|
||||
|
||||
Optional:
|
||||
|
||||
- `labels` (Map of String) Specifies labels.
|
||||
|
||||
|
||||
<a id="nestedatt--basic_auth"></a>
|
||||
### Nested Schema for `basic_auth`
|
||||
|
||||
Required:
|
||||
|
||||
- `password` (String, Sensitive) Specifies basic auth password.
|
||||
- `username` (String) Specifies basic auth username.
|
||||
|
||||
|
||||
<a id="nestedatt--saml2"></a>
|
||||
### Nested Schema for `saml2`
|
||||
|
||||
Optional:
|
||||
|
||||
- `enable_url_parameters` (Boolean) Specifies if URL parameters are enabled. Defaults to `true`
|
||||
|
|
@ -4,7 +4,7 @@ page_title: "stackit_postgresql_credential Resource - stackit"
|
|||
subcategory: ""
|
||||
description: |-
|
||||
PostgreSQL credential resource schema. Must have a region specified in the provider configuration.
|
||||
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_user instead. For more details, check
|
||||
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackitpostgresflexuser instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||
---
|
||||
|
||||
# stackit_postgresql_credential (Resource)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ page_title: "stackit_postgresql_instance Resource - stackit"
|
|||
subcategory: ""
|
||||
description: |-
|
||||
PostgreSQL instance resource schema. Must have a region specified in the provider configuration.
|
||||
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an "import" block ()
|
||||
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackitpostgresflexinstance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an "import" block (https://developer.hashicorp.com/terraform/language/import)
|
||||
---
|
||||
|
||||
# stackit_postgresql_instance (Resource)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
data "stackit_observability_instance" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
|
|
@ -0,0 +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"
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
resource "stackit_observability_credential" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1
go.mod
1
go.mod
|
|
@ -20,6 +20,7 @@ require (
|
|||
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.17.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.14.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v0.10.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/observability v0.1.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.16.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.15.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/postgresql v0.12.1
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -166,6 +166,8 @@ github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.14.0 h1:FaJYVfha+
|
|||
github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.14.0/go.mod h1:iFerEzGmkg6R13ldFUyHUWHm0ac9cS4ftTDLhP0k/dU=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v0.10.0 h1:tn1MD1nu+gYEbT3lslRI6BrapKwuvHv5Wi2Zw9uVPPc=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v0.10.0/go.mod h1:dkVMJI88eJ3Xs0ZV15r4tUpgitUGJXcvrX3RL4Zq2bQ=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/observability v0.1.0 h1:VdxYMJqGKUvk7/l2b83Z/bB0FUYuFbELK/ipTicJ5QY=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/observability v0.1.0/go.mod h1:cSnBZGdtx4jnn9HEefkQHDrm8+PuS0NCWvukVfuwP/8=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.16.0 h1:EEjhfIFiC4TsaFKB4mkxz6NFz4InfVs5STmWc+oEjgQ=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.16.0/go.mod h1:ZecMIf9oYj2DGZqWh93l97WdVaRdLl+tW5Fq3YKGwBM=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.15.0 h1:05wQYhO37Z4y8xAD+4OTYz6rYu6eJEmwMfCG4tjETEc=
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ type ProviderData struct {
|
|||
MariaDBCustomEndpoint string
|
||||
MongoDBFlexCustomEndpoint string
|
||||
ObjectStorageCustomEndpoint string
|
||||
ObservabilityCustomEndpoint string
|
||||
OpenSearchCustomEndpoint string
|
||||
PostgresFlexCustomEndpoint string
|
||||
PostgreSQLCustomEndpoint string
|
||||
|
|
|
|||
|
|
@ -87,8 +87,15 @@ func (r *credentialResource) Configure(ctx context.Context, req resource.Configu
|
|||
}
|
||||
|
||||
func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
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.",
|
||||
}
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Argus credential resource schema. Must have a `region` specified in the provider configuration.",
|
||||
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 resource ID. It is structured as \"`project_id`,`instance_id`,`username`\".",
|
||||
|
|
|
|||
|
|
@ -75,11 +75,18 @@ func (d *instanceDataSource) Configure(ctx context.Context, req datasource.Confi
|
|||
|
||||
// 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: "Argus instance data source schema. Must have a `region` specified in the provider configuration.",
|
||||
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`\".",
|
||||
Description: "Terraform's internal data source ID. It is structured as \"`project_id`,`instance_id`\".",
|
||||
Computed: true,
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
|
|
|
|||
|
|
@ -375,8 +375,15 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure
|
|||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
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.",
|
||||
}
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Argus instance resource schema. Must have a `region` specified in the provider configuration.",
|
||||
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 resource ID. It is structured as \"`project_id`,`instance_id`\".",
|
||||
|
|
|
|||
|
|
@ -76,8 +76,15 @@ func (d *scrapeConfigDataSource) Configure(ctx context.Context, req datasource.C
|
|||
|
||||
// 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: "Argus scrape config data source schema. Must have a `region` specified in the provider configuration.",
|
||||
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`\".",
|
||||
|
|
|
|||
|
|
@ -151,8 +151,15 @@ func (r *scrapeConfigResource) Configure(ctx context.Context, req resource.Confi
|
|||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *scrapeConfigResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
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.",
|
||||
}
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Argus scrape config resource schema. Must have a `region` specified in the provider configuration.",
|
||||
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 resource ID. It is structured as \"`project_id`,`instance_id`,`name`\".",
|
||||
|
|
|
|||
252
stackit/internal/services/observability/credential/resource.go
Normal file
252
stackit/internal/services/observability/credential/resource.go
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
package observability
|
||||
|
||||
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/observability"
|
||||
"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 *observability.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the resource type name.
|
||||
func (r *credentialResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_observability_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 *observability.APIClient
|
||||
var err error
|
||||
if providerData.ObservabilityCustomEndpoint != "" {
|
||||
apiClient, err = observability.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.ObservabilityCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = observability.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithRegion(providerData.Region),
|
||||
)
|
||||
}
|
||||
|
||||
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, "Observability credential client configured")
|
||||
}
|
||||
|
||||
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.",
|
||||
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 Observability 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(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 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, "Observability credential created")
|
||||
}
|
||||
|
||||
func mapFields(r *observability.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, "Observability 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, "Observability credential deleted")
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package observability
|
||||
|
||||
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/observability"
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *observability.Credentials
|
||||
expected Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"ok",
|
||||
&observability.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",
|
||||
&observability.Credentials{
|
||||
Password: nil,
|
||||
Username: nil,
|
||||
},
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
&observability.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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
435
stackit/internal/services/observability/instance/datasource.go
Normal file
435
stackit/internal/services/observability/instance/datasource.go
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
package observability
|
||||
|
||||
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/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/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 *observability.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (d *instanceDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_observability_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 *observability.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.ObservabilityCustomEndpoint != "" {
|
||||
apiClient, err = observability.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.ObservabilityCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = observability.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithRegion(providerData.Region),
|
||||
)
|
||||
}
|
||||
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, "Observability instance client configured")
|
||||
}
|
||||
|
||||
// Schema defines the schema for the data source.
|
||||
func (d *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Observability instance data source schema. Must have a `region` specified in the provider configuration.",
|
||||
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 Observability instance ID.",
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: "The name of the Observability instance.",
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
stringvalidator.LengthAtMost(300),
|
||||
},
|
||||
},
|
||||
"plan_name": schema.StringAttribute{
|
||||
Description: "Specifies the Observability plan. E.g. `Monitoring-Medium-EU01`.",
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
stringvalidator.LengthAtMost(200),
|
||||
},
|
||||
},
|
||||
"plan_id": schema.StringAttribute{
|
||||
Description: "The Observability 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 Observability 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, "Observability instance read")
|
||||
}
|
||||
2176
stackit/internal/services/observability/instance/resource.go
Normal file
2176
stackit/internal/services/observability/instance/resource.go
Normal file
File diff suppressed because it is too large
Load diff
1562
stackit/internal/services/observability/instance/resource_test.go
Normal file
1562
stackit/internal/services/observability/instance/resource_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,896 @@
|
|||
package observability_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/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/testutil"
|
||||
)
|
||||
|
||||
var instanceResource = map[string]string{
|
||||
"project_id": testutil.ProjectId,
|
||||
"name": testutil.ResourceNameWithDateTime("observability"),
|
||||
"plan_name": "Monitoring-Basic-EU01",
|
||||
"new_plan_name": "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,observability-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_observability_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_observability_scrapeconfig" "scrapeconfig" {
|
||||
project_id = stackit_observability_instance.instance.project_id
|
||||
instance_id = stackit_observability_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_observability_credential" "credential" {
|
||||
project_id = stackit_observability_instance.instance.project_id
|
||||
instance_id = stackit_observability_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.ObservabilityProviderConfig(),
|
||||
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: testAccCheckObservabilityDestroy,
|
||||
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_observability_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "plan_name", instanceResource["plan_name"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "dashboard_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "is_updatable"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_public_read_access"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_initial_admin_user"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_initial_admin_password"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "metrics_retention_days", instanceResource["metrics_retention_days"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "metrics_retention_days_5m_downsampling", instanceResource["metrics_retention_days_5m_downsampling"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "metrics_retention_days_1h_downsampling", instanceResource["metrics_retention_days_1h_downsampling"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_push_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "targets_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "alerting_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "logs_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "logs_push_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "jaeger_traces_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "jaeger_ui_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "otlp_traces_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "zipkin_spans_url"),
|
||||
|
||||
// Alert Config
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.0", "alertname"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_interval", "10m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_wait", "1m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.repeat_interval", "1h"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.resolve_timeout", "5m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.opsgenie_api_key", "example-api-key"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.opsgenie_api_url", "https://api.eu.opsgenie.com"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.receiver", "OpsGenieReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.0", "alertname"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_interval", "10m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_wait", "1m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.repeat_interval", "1h"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.#", "0"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.name", "OpsGenieReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.tags", "iam,observability-alert"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.api_key",
|
||||
"example-api-key"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.name", "WebhookReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.webhooks_configs.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.webhooks_configs.0.url", "https://example.com"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.webhooks_configs.0.ms_teams", "true"),
|
||||
|
||||
// ACL
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.0", instanceResource["acl-0"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.1", instanceResource["acl-1"]),
|
||||
|
||||
// scrape config data
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "project_id",
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "project_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
|
||||
|
||||
// credentials
|
||||
resource.TestCheckResourceAttr("stackit_observability_credential.credential", "project_id", credentialResource["project_id"]),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"stackit_observability_credential.credential", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_credential.credential", "username"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_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_observability_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "plan_name", instanceResource["plan_name"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "dashboard_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "is_updatable"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_public_read_access"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_initial_admin_user"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_initial_admin_password"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "metrics_retention_days", instanceResource["metrics_retention_days"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "metrics_retention_days_5m_downsampling", instanceResource["metrics_retention_days_5m_downsampling"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "metrics_retention_days_1h_downsampling", instanceResource["metrics_retention_days_1h_downsampling"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_push_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "targets_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "alerting_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "logs_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "logs_push_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "jaeger_traces_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "jaeger_ui_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "otlp_traces_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "zipkin_spans_url"),
|
||||
|
||||
// Alert Config
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.#", "3"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.0", "alertname"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_interval", "10m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_wait", "1m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.repeat_interval", "1h"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.resolve_timeout", "5m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.opsgenie_api_key", "example-api-key"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.opsgenie_api_url", "https://api.eu.opsgenie.com"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.smtp_smart_host", "smtp.example.com:587"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.smtp_from", "me@example.com"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.receiver", "OpsGenieReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.0", "alertname"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_interval", "10m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_wait", "1m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.repeat_interval", "1h"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.match.severity", "critical"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.receiver", "OpsGenieReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.1.match.severity", "warning"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.1.receiver", "WebhookReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.name", "OpsGenieReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.tags", "iam,observability-alert"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.api_key",
|
||||
"example-api-key"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.name", "EmailReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.email_configs.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.email_configs.0.to", "me@example.com"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.2.name", "WebhookReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.2.webhooks_configs.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.2.webhooks_configs.0.url", "https://example.com"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.2.webhooks_configs.0.ms_teams", "true"),
|
||||
|
||||
// ACL
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.0", instanceResource["acl-0"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.1", instanceResource["acl-1"]),
|
||||
|
||||
// scrape config data
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "project_id",
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "project_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
|
||||
|
||||
// credentials
|
||||
resource.TestCheckResourceAttr("stackit_observability_credential.credential", "project_id", credentialResource["project_id"]),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"stackit_observability_credential.credential", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_credential.credential", "username"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_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_observability_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "plan_name", instanceResource["plan_name"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "dashboard_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "is_updatable"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_public_read_access"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_initial_admin_user"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_initial_admin_password"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_retention_days"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_retention_days_5m_downsampling"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "metrics_retention_days_1h_downsampling", instanceResource["metrics_retention_days_1h_downsampling"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_push_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "targets_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "alerting_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "logs_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "logs_push_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "jaeger_traces_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "jaeger_ui_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "otlp_traces_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "zipkin_spans_url"),
|
||||
|
||||
// ACL
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.#", "0"),
|
||||
|
||||
// scrape config data
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "project_id",
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "project_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
|
||||
|
||||
// credentials
|
||||
resource.TestCheckResourceAttr("stackit_observability_credential.credential", "project_id", credentialResource["project_id"]),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"stackit_observability_credential.credential", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_credential.credential", "username"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_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_observability_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "plan_name", instanceResource["plan_name"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "dashboard_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "is_updatable"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_public_read_access"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_initial_admin_user"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "grafana_initial_admin_password"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_retention_days"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_retention_days_5m_downsampling"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_retention_days_1h_downsampling"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "metrics_push_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "targets_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "alerting_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "logs_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "logs_push_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "jaeger_traces_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "jaeger_ui_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "otlp_traces_url"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "zipkin_spans_url"),
|
||||
|
||||
// ACL
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.#", "0"),
|
||||
|
||||
// scrape config data
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "project_id",
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "project_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
|
||||
|
||||
// credentials
|
||||
resource.TestCheckResourceAttr("stackit_observability_credential.credential", "project_id", credentialResource["project_id"]),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"stackit_observability_credential.credential", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_credential.credential", "username"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_credential.credential", "password"),
|
||||
),
|
||||
},
|
||||
// Data source
|
||||
{
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "stackit_observability_instance" "instance" {
|
||||
project_id = stackit_observability_instance.instance.project_id
|
||||
instance_id = stackit_observability_instance.instance.instance_id
|
||||
}
|
||||
|
||||
data "stackit_observability_scrapeconfig" "scrapeconfig" {
|
||||
project_id = stackit_observability_scrapeconfig.scrapeconfig.project_id
|
||||
instance_id = stackit_observability_scrapeconfig.scrapeconfig.instance_id
|
||||
name = stackit_observability_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_observability_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_observability_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "plan_name", instanceResource["plan_name"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "acl.#", "2"),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "acl.0", instanceResource["acl-0"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "acl.1", instanceResource["acl-1"]),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "project_id",
|
||||
"data.stackit_observability_instance.instance", "project_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_instance.instance", "instance_id",
|
||||
"data.stackit_observability_instance.instance", "instance_id",
|
||||
),
|
||||
// scrape config data
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "project_id",
|
||||
"data.stackit_observability_scrapeconfig.scrapeconfig", "project_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "instance_id",
|
||||
"data.stackit_observability_scrapeconfig.scrapeconfig", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_observability_scrapeconfig.scrapeconfig", "name",
|
||||
"data.stackit_observability_scrapeconfig.scrapeconfig", "name",
|
||||
),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_scrapeconfig.scrapeconfig", "targets.0.urls.#", "2"),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", scrapeConfigResource["saml2_enable_url_parameters"]),
|
||||
),
|
||||
},
|
||||
// Import 1
|
||||
{
|
||||
ResourceName: "stackit_observability_instance.instance",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_observability_instance.instance"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_observability_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_observability_scrapeconfig.scrapeconfig",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_observability_scrapeconfig.scrapeconfig"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_observability_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_observability_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "name", instanceResource["name"]+"-new"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "plan_name", instanceResource["new_plan_name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.0", instanceResource["acl-0"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.1", instanceResource["acl-1-updated"]),
|
||||
|
||||
// Alert Config
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.0", "alertname"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_interval", "10m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_wait", "1m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.repeat_interval", "1h"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.resolve_timeout", "5m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.opsgenie_api_key", "example-api-key"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.global.opsgenie_api_url", "https://api.eu.opsgenie.com"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.receiver", "OpsGenieReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.0", "alertname"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_interval", "10m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_wait", "1m"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.repeat_interval", "1h"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.match.severity", "critical"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.receiver", "OpsGenieReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.1.match.severity", "warning"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.1.receiver", "WebhookReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.name", "OpsGenieReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.tags", "iam,observability-alert"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.api_key",
|
||||
"example-api-key"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.name", "WebhookReceiverInfo"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.webhooks_configs.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.webhooks_configs.0.url", "https://example.com"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.1.webhooks_configs.0.ms_teams", "true"),
|
||||
|
||||
// Scrape Config
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "targets.#", "0"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "saml2.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", "true"),
|
||||
|
||||
// Credentials
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_credential.credential", "username"),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_credential.credential", "password"),
|
||||
),
|
||||
},
|
||||
// Update and remove saml2 attribute
|
||||
{
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
resource "stackit_observability_instance" "instance" {
|
||||
project_id = "%s"
|
||||
name = "%s"
|
||||
plan_name = "%s"
|
||||
}
|
||||
|
||||
resource "stackit_observability_scrapeconfig" "scrapeconfig" {
|
||||
project_id = stackit_observability_instance.instance.project_id
|
||||
instance_id = stackit_observability_instance.instance.instance_id
|
||||
name = "%s"
|
||||
targets = [%s]
|
||||
scrape_interval = "%s"
|
||||
sample_limit = %s
|
||||
metrics_path = "%s"
|
||||
saml2 = {
|
||||
enable_url_parameters = false
|
||||
}
|
||||
}
|
||||
`,
|
||||
testutil.ObservabilityProviderConfig(),
|
||||
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_observability_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_observability_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "plan_name", instanceResource["new_plan_name"]),
|
||||
|
||||
// ACL
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "acl.#", "0"),
|
||||
|
||||
// Scrape Config
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "name", scrapeConfigResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "targets.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "metrics_path", scrapeConfigResource["metrics_path"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scheme", scrapeConfigResource["scheme"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "scrape_interval", scrapeConfigResource["scrape_interval"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "sample_limit", scrapeConfigResource["sample_limit"]),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "saml2.%", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_observability_scrapeconfig.scrapeconfig", "saml2.enable_url_parameters", "false"),
|
||||
),
|
||||
},
|
||||
|
||||
// Deletion is done by the framework implicitly
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckObservabilityDestroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *observability.APIClient
|
||||
var err error
|
||||
if testutil.ObservabilityCustomEndpoint == "" {
|
||||
client, err = observability.NewAPIClient(
|
||||
config.WithRegion("eu01"),
|
||||
)
|
||||
} else {
|
||||
client, err = observability.NewAPIClient(
|
||||
config.WithEndpoint(testutil.ObservabilityCustomEndpoint),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating client: %w", err)
|
||||
}
|
||||
|
||||
instancesToDestroy := []string{}
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "stackit_observability_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
|
||||
}
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
package observability
|
||||
|
||||
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/observability"
|
||||
"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 *observability.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (d *scrapeConfigDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_observability_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 *observability.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.ObservabilityCustomEndpoint != "" {
|
||||
apiClient, err = observability.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.ObservabilityCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = observability.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithRegion(providerData.Region),
|
||||
)
|
||||
}
|
||||
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) {
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Observability scrape config data source schema. Must have a `region` specified in the provider configuration.",
|
||||
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: "Observability 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, "Observability scrape config read")
|
||||
}
|
||||
869
stackit/internal/services/observability/scrapeconfig/resource.go
Normal file
869
stackit/internal/services/observability/scrapeconfig/resource.go
Normal file
|
|
@ -0,0 +1,869 @@
|
|||
package observability
|
||||
|
||||
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/observability"
|
||||
"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"
|
||||
"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 *observability.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the resource type name.
|
||||
func (r *scrapeConfigResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_observability_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 *observability.APIClient
|
||||
var err error
|
||||
if providerData.ObservabilityCustomEndpoint != "" {
|
||||
apiClient, err = observability.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.ObservabilityCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = observability.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithRegion(providerData.Region),
|
||||
)
|
||||
}
|
||||
|
||||
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, "Observability scrape config client configured")
|
||||
}
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *scrapeConfigResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
Description: "Observability scrape config resource schema. Must have a `region` specified in the provider configuration.",
|
||||
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: "Observability 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)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 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, "Observability 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, "Observability 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 observability 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, "Observability 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, "Observability 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, "Observability scrape config state imported")
|
||||
}
|
||||
|
||||
func mapFields(ctx context.Context, sc *observability.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 *observability.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 *observability.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 *observability.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) (*observability.CreateScrapeConfigPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
sc := observability.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 = &observability.CreateScrapeConfigPayloadBasicAuth{
|
||||
Username: conversion.StringValueToPointer(basicAuthModel.Username),
|
||||
Password: conversion.StringValueToPointer(basicAuthModel.Password),
|
||||
}
|
||||
}
|
||||
|
||||
t := make([]observability.CreateScrapeConfigPayloadStaticConfigsInner, len(targetsModel))
|
||||
for i, target := range targetsModel {
|
||||
ti := observability.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 *observability.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) (*observability.UpdateScrapeConfigPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
sc := observability.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 = &observability.CreateScrapeConfigPayloadBasicAuth{
|
||||
Username: conversion.StringValueToPointer(basicAuthModel.Username),
|
||||
Password: conversion.StringValueToPointer(basicAuthModel.Password),
|
||||
}
|
||||
}
|
||||
|
||||
t := make([]observability.UpdateScrapeConfigPayloadStaticConfigsInner, len(targetsModel))
|
||||
for i, target := range targetsModel {
|
||||
ti := observability.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 *observability.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))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,504 @@
|
|||
package observability
|
||||
|
||||
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/observability"
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *observability.Job
|
||||
expected Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
&observability.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: &observability.Job{
|
||||
JobName: utils.Ptr("name"),
|
||||
MetricsPath: utils.Ptr("/m"),
|
||||
BasicAuth: &observability.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: &[]observability.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",
|
||||
&observability.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 *observability.CreateScrapeConfigPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"basic_ok",
|
||||
&Model{
|
||||
MetricsPath: types.StringValue("/metrics"),
|
||||
},
|
||||
&saml2Model{},
|
||||
&basicAuthModel{},
|
||||
[]targetModel{},
|
||||
&observability.CreateScrapeConfigPayload{
|
||||
MetricsPath: utils.Ptr("/metrics"),
|
||||
// Defaults
|
||||
Scheme: utils.Ptr("https"),
|
||||
ScrapeInterval: utils.Ptr("5m"),
|
||||
ScrapeTimeout: utils.Ptr("2m"),
|
||||
SampleLimit: utils.Ptr(float64(5000)),
|
||||
StaticConfigs: &[]observability.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{},
|
||||
&observability.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: &[]observability.CreateScrapeConfigPayloadStaticConfigsInner{},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ok - true enable_url_parameters",
|
||||
&Model{
|
||||
MetricsPath: types.StringValue("/metrics"),
|
||||
Name: types.StringValue("Name"),
|
||||
},
|
||||
&saml2Model{
|
||||
EnableURLParameters: types.BoolValue(true),
|
||||
},
|
||||
&basicAuthModel{},
|
||||
[]targetModel{},
|
||||
&observability.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: &[]observability.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{},
|
||||
&observability.CreateScrapeConfigPayload{
|
||||
MetricsPath: utils.Ptr("/metrics"),
|
||||
JobName: utils.Ptr("Name"),
|
||||
BasicAuth: &observability.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: &[]observability.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),
|
||||
},
|
||||
},
|
||||
&observability.CreateScrapeConfigPayload{
|
||||
MetricsPath: utils.Ptr("/metrics"),
|
||||
JobName: utils.Ptr("Name"),
|
||||
StaticConfigs: &[]observability.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 *observability.UpdateScrapeConfigPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"basic_ok",
|
||||
&Model{
|
||||
MetricsPath: types.StringValue("/metrics"),
|
||||
},
|
||||
&saml2Model{},
|
||||
&basicAuthModel{},
|
||||
[]targetModel{},
|
||||
&observability.UpdateScrapeConfigPayload{
|
||||
MetricsPath: utils.Ptr("/metrics"),
|
||||
// Defaults
|
||||
Scheme: utils.Ptr("https"),
|
||||
ScrapeInterval: utils.Ptr("5m"),
|
||||
ScrapeTimeout: utils.Ptr("2m"),
|
||||
SampleLimit: utils.Ptr(float64(5000)),
|
||||
StaticConfigs: &[]observability.UpdateScrapeConfigPayloadStaticConfigsInner{},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ok - true enable_url_parameters",
|
||||
&Model{
|
||||
MetricsPath: types.StringValue("/metrics"),
|
||||
Scheme: types.StringValue("http"),
|
||||
},
|
||||
&saml2Model{
|
||||
EnableURLParameters: types.BoolValue(true),
|
||||
},
|
||||
&basicAuthModel{},
|
||||
[]targetModel{},
|
||||
&observability.UpdateScrapeConfigPayload{
|
||||
MetricsPath: utils.Ptr("/metrics"),
|
||||
// Defaults
|
||||
Scheme: utils.Ptr("http"),
|
||||
ScrapeInterval: utils.Ptr("5m"),
|
||||
ScrapeTimeout: utils.Ptr("2m"),
|
||||
SampleLimit: utils.Ptr(float64(5000)),
|
||||
StaticConfigs: &[]observability.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{},
|
||||
&observability.UpdateScrapeConfigPayload{
|
||||
MetricsPath: utils.Ptr("/metrics"),
|
||||
// Defaults
|
||||
Scheme: utils.Ptr("http"),
|
||||
ScrapeInterval: utils.Ptr("5m"),
|
||||
ScrapeTimeout: utils.Ptr("2m"),
|
||||
SampleLimit: utils.Ptr(float64(5000)),
|
||||
StaticConfigs: &[]observability.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{},
|
||||
&observability.UpdateScrapeConfigPayload{
|
||||
MetricsPath: utils.Ptr("/metrics"),
|
||||
BasicAuth: &observability.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: &[]observability.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),
|
||||
},
|
||||
},
|
||||
&observability.UpdateScrapeConfigPayload{
|
||||
MetricsPath: utils.Ptr("/metrics"),
|
||||
StaticConfigs: &[]observability.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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ var (
|
|||
AuthorizationCustomEndpoint = os.Getenv("TF_ACC_authorization_custom_endpoint")
|
||||
MongoDBFlexCustomEndpoint = os.Getenv("TF_ACC_MONGODBFLEX_CUSTOM_ENDPOINT")
|
||||
OpenSearchCustomEndpoint = os.Getenv("TF_ACC_OPENSEARCH_CUSTOM_ENDPOINT")
|
||||
ObservabilityCustomEndpoint = os.Getenv("TF_ACC_OBSERVABILITY_CUSTOM_ENDPOINT")
|
||||
ObjectStorageCustomEndpoint = os.Getenv("TF_ACC_OBJECTSTORAGE_CUSTOM_ENDPOINT")
|
||||
PostgreSQLCustomEndpoint = os.Getenv("TF_ACC_POSTGRESQL_CUSTOM_ENDPOINT")
|
||||
PostgresFlexCustomEndpoint = os.Getenv("TF_ACC_POSTGRESFLEX_CUSTOM_ENDPOINT")
|
||||
|
|
@ -87,6 +88,22 @@ func ArgusProviderConfig() string {
|
|||
)
|
||||
}
|
||||
|
||||
// Provider config helper functions
|
||||
|
||||
func ObservabilityProviderConfig() string {
|
||||
if ObservabilityCustomEndpoint == "" {
|
||||
return `provider "stackit" {
|
||||
region = "eu01"
|
||||
}`
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
provider "stackit" {
|
||||
observability_custom_endpoint = "%s"
|
||||
}`,
|
||||
ObservabilityCustomEndpoint,
|
||||
)
|
||||
}
|
||||
|
||||
func DnsProviderConfig() string {
|
||||
if DnsCustomEndpoint == "" {
|
||||
return `provider "stackit" {}`
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ import (
|
|||
objectStorageBucket "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/objectstorage/bucket"
|
||||
objecStorageCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/objectstorage/credential"
|
||||
objecStorageCredentialsGroup "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/objectstorage/credentialsgroup"
|
||||
observabilityCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/credential"
|
||||
observabilityInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/instance"
|
||||
observabilityScrapeConfig "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/observability/scrapeconfig"
|
||||
openSearchCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/opensearch/credential"
|
||||
openSearchInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/opensearch/instance"
|
||||
postgresFlexDatabase "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/postgresflex/database"
|
||||
|
|
@ -101,6 +104,7 @@ type providerModel struct {
|
|||
MariaDBCustomEndpoint types.String `tfsdk:"mariadb_custom_endpoint"`
|
||||
AuthorizationCustomEndpoint types.String `tfsdk:"authorization_custom_endpoint"`
|
||||
ObjectStorageCustomEndpoint types.String `tfsdk:"objectstorage_custom_endpoint"`
|
||||
ObservabilityCustomEndpoint types.String `tfsdk:"observability_custom_endpoint"`
|
||||
OpenSearchCustomEndpoint types.String `tfsdk:"opensearch_custom_endpoint"`
|
||||
RedisCustomEndpoint types.String `tfsdk:"redis_custom_endpoint"`
|
||||
SecretsManagerCustomEndpoint types.String `tfsdk:"secretsmanager_custom_endpoint"`
|
||||
|
|
@ -135,6 +139,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
|||
"mariadb_custom_endpoint": "Custom endpoint for the MariaDB service",
|
||||
"authorization_custom_endpoint": "Custom endpoint for the Membership service",
|
||||
"objectstorage_custom_endpoint": "Custom endpoint for the Object Storage service",
|
||||
"observability_custom_endpoint": "Custom endpoint for the Observability service",
|
||||
"opensearch_custom_endpoint": "Custom endpoint for the OpenSearch service",
|
||||
"postgresql_custom_endpoint": "Custom endpoint for the PostgreSQL service",
|
||||
"postgresflex_custom_endpoint": "Custom endpoint for the PostgresFlex service",
|
||||
|
|
@ -185,8 +190,9 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
|||
Description: descriptions["region"],
|
||||
},
|
||||
"argus_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["argus_custom_endpoint"],
|
||||
Optional: true,
|
||||
Description: descriptions["argus_custom_endpoint"],
|
||||
DeprecationMessage: "Argus service has been deprecated and integration will be removed after February 26th 2025. Please use `observability_custom_endpoint` and `observability` resources instead, which offer the exact same functionality.",
|
||||
},
|
||||
"dns_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
|
|
@ -232,6 +238,10 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
|||
Optional: true,
|
||||
Description: descriptions["objectstorage_custom_endpoint"],
|
||||
},
|
||||
"observability_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["observability_custom_endpoint"],
|
||||
},
|
||||
"opensearch_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["opensearch_custom_endpoint"],
|
||||
|
|
@ -355,6 +365,9 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
|
|||
if !(providerConfig.ObjectStorageCustomEndpoint.IsUnknown() || providerConfig.ObjectStorageCustomEndpoint.IsNull()) {
|
||||
providerData.ObjectStorageCustomEndpoint = providerConfig.ObjectStorageCustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.ObservabilityCustomEndpoint.IsUnknown() || providerConfig.ObservabilityCustomEndpoint.IsNull()) {
|
||||
providerData.ObservabilityCustomEndpoint = providerConfig.ObservabilityCustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.OpenSearchCustomEndpoint.IsUnknown() || providerConfig.OpenSearchCustomEndpoint.IsNull()) {
|
||||
providerData.OpenSearchCustomEndpoint = providerConfig.OpenSearchCustomEndpoint.ValueString()
|
||||
}
|
||||
|
|
@ -415,6 +428,8 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
|||
objectStorageBucket.NewBucketDataSource,
|
||||
objecStorageCredentialsGroup.NewCredentialsGroupDataSource,
|
||||
objecStorageCredential.NewCredentialDataSource,
|
||||
observabilityInstance.NewInstanceDataSource,
|
||||
observabilityScrapeConfig.NewScrapeConfigDataSource,
|
||||
openSearchInstance.NewInstanceDataSource,
|
||||
openSearchCredential.NewCredentialDataSource,
|
||||
postgresFlexDatabase.NewDatabaseDataSource,
|
||||
|
|
@ -461,6 +476,9 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
|
|||
objectStorageBucket.NewBucketResource,
|
||||
objecStorageCredentialsGroup.NewCredentialsGroupResource,
|
||||
objecStorageCredential.NewCredentialResource,
|
||||
observabilityCredential.NewCredentialResource,
|
||||
observabilityInstance.NewInstanceResource,
|
||||
observabilityScrapeConfig.NewScrapeConfigResource,
|
||||
openSearchInstance.NewInstanceResource,
|
||||
openSearchCredential.NewCredentialResource,
|
||||
postgresFlexDatabase.NewDatabaseResource,
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@ This guide outlines the process of utilizing the HashiCorp Vault provider alongs
|
|||
}
|
||||
```
|
||||
|
||||
5. **Define Terraform Resource (Example: Argus Monitoring Instance)**
|
||||
5. **Define Terraform Resource (Example: Observability Monitoring Instance)**
|
||||
|
||||
```hcl
|
||||
resource "stackit_argus_instance" "example" {
|
||||
resource "stackit_observability_instance" "example" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-instance"
|
||||
plan_name = "Monitoring-Medium-EU01"
|
||||
|
|
@ -71,7 +71,7 @@ This guide outlines the process of utilizing the HashiCorp Vault provider alongs
|
|||
delete_all_versions = true
|
||||
data_json = jsonencode(
|
||||
{
|
||||
grafana_password = stackit_argus_instance.example.grafana_initial_admin_password,
|
||||
grafana_password = stackit_observability_instance.example.grafana_initial_admin_password,
|
||||
other_secret = ...,
|
||||
}
|
||||
)
|
||||
|
|
@ -80,4 +80,4 @@ This guide outlines the process of utilizing the HashiCorp Vault provider alongs
|
|||
|
||||
## Note
|
||||
|
||||
This example can be adapted for various resources within the provider as well as any other Secret the user wants to set in the Secrets Manager instance. Adapting this examples means replacing the Argus Monitoring Grafana password with the appropriate value.
|
||||
This example can be adapted for various resources within the provider as well as any other Secret the user wants to set in the Secrets Manager instance. Adapting this examples means replacing the Observability Monitoring Grafana password with the appropriate value.
|
||||
Loading…
Add table
Add a link
Reference in a new issue