Skip to content

Commit dd06529

Browse files
pmdzfornfrey
andauthored
Add grafana_k6_project_allowed_load_zones resource / data source (#2271)
* k6 allowed load zones schema / notes * extend k6 project resource/data source with PLZs * update docs * linter fixes * add project_allowed_load_zones resource * fix formatting * properly handle allowed_load_zones null value * return api error content * project_allowed_load_zones - project_id as int * update examples and docs * fix allowed_load_zones example * update internal/resources/k6/catalog-resource.yaml * docs update * add tests * update provider schema * setProjectAllowedLoadZones - fetch LZs in single request * Update internal/resources/k6/resource_project_allowed_load_zones.go Co-authored-by: Oleksandr Kotov <[email protected]> * resource_project_allowed_load_zones Read() - rm checking if proj exist * revert changes made to resource_project * add k6_project_allowed_load_zones data source * handle 404 in resource_project_allowed_load_zones Delete() * setProjectAllowedLoadZones() - skip lz ids match for empty arr * update docs * add test for data_source_k6_project_allowed_load_zones * update docs * fix formatting * resource_project_allowed_load_zones - project_id as str * project_allowed_load_zones resource - project_id as str * tweak descriptions --------- Co-authored-by: Oleksandr Kotov <[email protected]>
1 parent de2f9c1 commit dd06529

File tree

16 files changed

+682
-4
lines changed

16 files changed

+682
-4
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "grafana_k6_project_allowed_load_zones Data Source - terraform-provider-grafana"
4+
subcategory: "k6"
5+
description: |-
6+
Retrieves allowed private load zones for a k6 project.
7+
---
8+
9+
# grafana_k6_project_allowed_load_zones (Data Source)
10+
11+
Retrieves allowed private load zones for a k6 project.
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "grafana_k6_project" "test_project_allowed_load_zones" {
17+
name = "Terraform Project Test Allowed Load Zones"
18+
}
19+
20+
data "grafana_k6_project_allowed_load_zones" "from_project_id" {
21+
project_id = grafana_k6_project.test_project_allowed_load_zones.id
22+
}
23+
```
24+
25+
<!-- schema generated by tfplugindocs -->
26+
## Schema
27+
28+
### Required
29+
30+
- `project_id` (String) The identifier of the project to retrieve private allowed load zones for.
31+
32+
### Read-Only
33+
34+
- `allowed_load_zones` (List of String) List of allowed private k6 load zone IDs for this project.
35+
- `id` (String) The identifier of the project. This is set to the same as the project_id.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "grafana_k6_project_allowed_load_zones Resource - terraform-provider-grafana"
4+
subcategory: "k6"
5+
description: |-
6+
Manages allowed private load zones for a k6 project.
7+
---
8+
9+
# grafana_k6_project_allowed_load_zones (Resource)
10+
11+
Manages allowed private load zones for a k6 project.
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "grafana_k6_project" "test_project_allowed_load_zones" {
17+
name = "Terraform Project Test Allowed Load Zones"
18+
}
19+
20+
resource "grafana_k6_project_allowed_load_zones" "test_allowed_zones" {
21+
project_id = grafana_k6_project.test_project_allowed_load_zones.id
22+
allowed_load_zones = ["my-load-zone-1", "other-load-zone"]
23+
}
24+
```
25+
26+
<!-- schema generated by tfplugindocs -->
27+
## Schema
28+
29+
### Required
30+
31+
- `allowed_load_zones` (List of String) List of allowed private k6 load zone IDs for this project.
32+
- `project_id` (String) The identifier of the project to manage private allowed load zones for.
33+
34+
### Read-Only
35+
36+
- `id` (String) The identifier of the project. This is set to the same as the project_id.
37+
38+
## Import
39+
40+
Import is supported using the following syntax:
41+
42+
```shell
43+
terraform import grafana_k6_project_allowed_load_zones.name "{{ project_id }}"
44+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
resource "grafana_k6_project" "test_project_allowed_load_zones" {
2+
name = "Terraform Project Test Allowed Load Zones"
3+
}
4+
5+
data "grafana_k6_project_allowed_load_zones" "from_project_id" {
6+
project_id = grafana_k6_project.test_project_allowed_load_zones.id
7+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
terraform import grafana_k6_project_allowed_load_zones.name "{{ project_id }}"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
resource "grafana_k6_project" "test_project_allowed_load_zones" {
2+
name = "Terraform Project Test Allowed Load Zones"
3+
}
4+
5+
resource "grafana_k6_project_allowed_load_zones" "test_allowed_zones" {
6+
project_id = grafana_k6_project.test_project_allowed_load_zones.id
7+
allowed_load_zones = ["my-load-zone-1", "other-load-zone"]
8+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ require (
1717
github.com/grafana/grafana/apps/dashboard v0.0.0-20250424064802-2fbb2d6f5d27
1818
github.com/grafana/grafana/apps/playlist v0.0.0-20250424064802-2fbb2d6f5d27
1919
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250424064802-2fbb2d6f5d27
20-
github.com/grafana/k6-cloud-openapi-client-go v0.0.0-20250416134020-d958828152cd
20+
github.com/grafana/k6-cloud-openapi-client-go v0.0.0-20250715154343-32edc34ec1db
2121
github.com/grafana/machine-learning-go-client v0.8.2
2222
github.com/grafana/river v0.3.0
2323
github.com/grafana/slo-openapi-client/go/slo v0.0.0-20250218172929-ab9cae090da6

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ github.com/grafana/grafana/apps/playlist v0.0.0-20250424064802-2fbb2d6f5d27 h1:a
194194
github.com/grafana/grafana/apps/playlist v0.0.0-20250424064802-2fbb2d6f5d27/go.mod h1:9U44mptAJW8bkvgPgCxsnki58/nz3wKPgDayeyeFWJs=
195195
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250424064802-2fbb2d6f5d27 h1:2Q158xAoyGgPVw7BIWF5cPYTkjjdsn9c8TpNA38gKzM=
196196
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250424064802-2fbb2d6f5d27/go.mod h1:kzjpaBODMbCSS2kvAnV43Pwxoq4lOxrgw/TGKqq8oTA=
197-
github.com/grafana/k6-cloud-openapi-client-go v0.0.0-20250416134020-d958828152cd h1:UOHbve+keUInhIBSqA4m7GtF2wEiKyW8f/kUt9S0+zM=
198-
github.com/grafana/k6-cloud-openapi-client-go v0.0.0-20250416134020-d958828152cd/go.mod h1:nBtSyJ0NTfJvEPSrRSKc1u7Vn0mB/kttEZdClHwTqi0=
197+
github.com/grafana/k6-cloud-openapi-client-go v0.0.0-20250715154343-32edc34ec1db h1:GZGkcFrQF09j8rZfxcIRql7liBf1U7pdmqSAseGintg=
198+
github.com/grafana/k6-cloud-openapi-client-go v0.0.0-20250715154343-32edc34ec1db/go.mod h1:RBPBP7qIR/K6qzQEQYESVhp/XJspiBTOyBEBCbPXrvI=
199199
github.com/grafana/machine-learning-go-client v0.8.2 h1:TvU4e+Kgg4GhwBNYTMjBUNq4tbhcxe0L8w1eo/UfV2M=
200200
github.com/grafana/machine-learning-go-client v0.8.2/go.mod h1:GQKDn10CZqG11l1Qtc6BZ5V6e54fSv5Vi8wskWn3BWs=
201201
github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8=

internal/resources/k6/catalog-resource.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,16 @@ spec:
3737
type: terraform-resource
3838
owner: group:default/k6-cloud-provisioning
3939
lifecycle: production
40+
---
41+
apiVersion: backstage.io/v1alpha1
42+
kind: Component
43+
metadata:
44+
name: resource-grafana_k6_project_allowed_load_zones
45+
title: grafana_k6_project_allowed_load_zones (resource)
46+
description: |
47+
resource `grafana_k6_project_allowed_load_zones` in Grafana Labs' Terraform Provider
48+
spec:
49+
subcomponentOf: component:default/terraform-provider-grafana
50+
type: terraform-resource
51+
owner: group:default/k6-cloud-provisioning
52+
lifecycle: production

internal/resources/k6/common.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package k6
33
import (
44
"context"
55
"fmt"
6+
"io"
7+
"net/http"
68

79
"github.com/hashicorp/terraform-plugin-framework/datasource"
810
"github.com/hashicorp/terraform-plugin-framework/resource"
@@ -80,3 +82,88 @@ func (d *basePluginFrameworkDataSource) Configure(_ context.Context, req datasou
8082
d.client = client.K6APIClient
8183
d.config = client.K6APIConfig
8284
}
85+
86+
// getProjectAllowedLoadZones retrieves the allowed load zones for a project
87+
// Returns k6_load_zone_ids directly from the API response
88+
func getProjectAllowedLoadZones(ctx context.Context, client *k6.APIClient, config *k6providerapi.K6APIConfig, projectID int32) ([]string, error) {
89+
ctx = context.WithValue(ctx, k6.ContextAccessToken, config.Token)
90+
91+
resp, _, err := client.LoadZonesAPI.ProjectsAllowedLoadZonesRetrieve(ctx, projectID).
92+
XStackId(config.StackID).
93+
Execute()
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
var k6LoadZoneIds []string
99+
for _, zone := range resp.GetValue() {
100+
k6LoadZoneIds = append(k6LoadZoneIds, zone.GetK6LoadZoneId())
101+
}
102+
103+
return k6LoadZoneIds, nil
104+
}
105+
106+
// setProjectAllowedLoadZones updates the allowed load zones for a project
107+
// loadZones parameter contains k6_load_zone_ids, which need to be resolved to actual load zone IDs
108+
func setProjectAllowedLoadZones(ctx context.Context, client *k6.APIClient, config *k6providerapi.K6APIConfig, projectID int32, k6LoadZoneIds []string) error {
109+
ctx = context.WithValue(ctx, k6.ContextAccessToken, config.Token)
110+
111+
// Initialize allowedZones as an empty slice to ensure it's serialized as [] instead of null
112+
allowedZones := make([]k6.AllowedLoadZoneToUpdateApiModel, 0)
113+
114+
// Skip fetching load zones if k6LoadZoneIds is empty
115+
if len(k6LoadZoneIds) > 0 {
116+
// Fetch all load zones
117+
allZonesResp, _, err := client.LoadZonesAPI.LoadZonesList(ctx).
118+
XStackId(config.StackID).
119+
Execute()
120+
if err != nil {
121+
return fmt.Errorf("failed to fetch load zones: %w", err)
122+
}
123+
124+
// Track invalid k6_load_zone_ids
125+
var invalidZoneIds []string
126+
127+
// Resolve each k6_load_zone_id to actual load zone ID using in-memory matching
128+
for _, k6LoadZoneID := range k6LoadZoneIds {
129+
found := false
130+
for _, zone := range allZonesResp.GetValue() {
131+
if zone.GetK6LoadZoneId() == k6LoadZoneID {
132+
// Create an AllowedLoadZoneToUpdateApiModel with the load zone ID
133+
zoneToUpdate := k6.NewAllowedLoadZoneToUpdateApiModel(zone.GetId())
134+
allowedZones = append(allowedZones, *zoneToUpdate)
135+
found = true
136+
break
137+
}
138+
}
139+
if !found {
140+
invalidZoneIds = append(invalidZoneIds, k6LoadZoneID)
141+
}
142+
}
143+
144+
// Return error if any invalid zone IDs were found
145+
if len(invalidZoneIds) > 0 {
146+
return fmt.Errorf("invalid k6_load_zone_ids: %v", invalidZoneIds)
147+
}
148+
}
149+
150+
updateData := k6.NewUpdateAllowedLoadZonesListApiModel(allowedZones)
151+
152+
_, httpResp, err := client.LoadZonesAPI.ProjectsAllowedLoadZonesUpdate(ctx, projectID).
153+
UpdateAllowedLoadZonesListApiModel(updateData).
154+
XStackId(config.StackID).
155+
Execute()
156+
157+
if err != nil && httpResp != nil && httpResp.StatusCode == http.StatusBadRequest {
158+
// Read the response body to include it in the error message
159+
if httpResp.Body != nil {
160+
bodyBytes, readErr := io.ReadAll(httpResp.Body)
161+
if readErr == nil {
162+
return fmt.Errorf("API returned 400 Bad Request: %s. Original error: %v", string(bodyBytes), err)
163+
}
164+
}
165+
return fmt.Errorf("API returned 400 Bad Request. Original error: %v", err)
166+
}
167+
168+
return err
169+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package k6
2+
3+
import (
4+
"context"
5+
"strconv"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework/datasource"
9+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
10+
"github.com/hashicorp/terraform-plugin-framework/types"
11+
12+
"github.com/grafana/terraform-provider-grafana/v4/internal/common"
13+
)
14+
15+
// Ensure the implementation satisfies the expected interfaces.
16+
var (
17+
_ datasource.DataSourceWithConfigure = (*projectAllowedLoadZonesDataSource)(nil)
18+
)
19+
20+
var (
21+
dataSourceProjectAllowedLoadZonesName = "grafana_k6_project_allowed_load_zones"
22+
)
23+
24+
func dataSourceProjectAllowedLoadZones() *common.DataSource {
25+
return common.NewDataSource(
26+
common.CategoryK6,
27+
dataSourceProjectAllowedLoadZonesName,
28+
&projectAllowedLoadZonesDataSource{},
29+
)
30+
}
31+
32+
// projectAllowedLoadZonesDataSourceModel maps the data source schema data.
33+
type projectAllowedLoadZonesDataSourceModel struct {
34+
ID types.String `tfsdk:"id"`
35+
ProjectID types.String `tfsdk:"project_id"`
36+
AllowedLoadZones types.List `tfsdk:"allowed_load_zones"`
37+
}
38+
39+
// projectAllowedLoadZonesDataSource is the data source implementation.
40+
type projectAllowedLoadZonesDataSource struct {
41+
basePluginFrameworkDataSource
42+
}
43+
44+
// Metadata returns the data source type name.
45+
func (d *projectAllowedLoadZonesDataSource) Metadata(_ context.Context, _ datasource.MetadataRequest, resp *datasource.MetadataResponse) {
46+
resp.TypeName = dataSourceProjectAllowedLoadZonesName
47+
}
48+
49+
// Schema defines the schema for the data source.
50+
func (d *projectAllowedLoadZonesDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
51+
resp.Schema = schema.Schema{
52+
Description: "Retrieves allowed private load zones for a k6 project.",
53+
Attributes: map[string]schema.Attribute{
54+
"id": schema.StringAttribute{
55+
Description: "The identifier of the project. This is set to the same as the project_id.",
56+
Computed: true,
57+
},
58+
"project_id": schema.StringAttribute{
59+
Description: "The identifier of the project to retrieve private allowed load zones for.",
60+
Required: true,
61+
},
62+
"allowed_load_zones": schema.ListAttribute{
63+
Description: "List of allowed private k6 load zone IDs for this project.",
64+
Computed: true,
65+
ElementType: types.StringType,
66+
},
67+
},
68+
}
69+
}
70+
71+
// Read refreshes the Terraform state with the latest data.
72+
func (d *projectAllowedLoadZonesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
73+
var state projectAllowedLoadZonesDataSourceModel
74+
diags := req.Config.Get(ctx, &state)
75+
resp.Diagnostics.Append(diags...)
76+
if resp.Diagnostics.HasError() {
77+
return
78+
}
79+
80+
intID, err := strconv.ParseInt(state.ProjectID.ValueString(), 10, 32)
81+
if err != nil {
82+
resp.Diagnostics.AddError(
83+
"Error parsing project ID",
84+
"Could not parse project ID '"+state.ProjectID.ValueString()+"': "+err.Error(),
85+
)
86+
return
87+
}
88+
projectID := int32(intID)
89+
90+
// Get allowed load zones
91+
allowedZones, err := getProjectAllowedLoadZones(ctx, d.client, d.config, projectID)
92+
if err != nil {
93+
resp.Diagnostics.AddError(
94+
"Error reading allowed load zones",
95+
"Could not read allowed load zones for k6 project: "+err.Error(),
96+
)
97+
return
98+
}
99+
100+
// Set ID to match project_id
101+
state.ID = state.ProjectID
102+
103+
// Convert to types.List
104+
var zoneValues []attr.Value
105+
for _, zone := range allowedZones {
106+
zoneValues = append(zoneValues, types.StringValue(zone))
107+
}
108+
state.AllowedLoadZones, diags = types.ListValue(types.StringType, zoneValues)
109+
resp.Diagnostics.Append(diags...)
110+
if resp.Diagnostics.HasError() {
111+
return
112+
}
113+
114+
diags = resp.State.Set(ctx, &state)
115+
resp.Diagnostics.Append(diags...)
116+
}

0 commit comments

Comments
 (0)