Skip to content

Commit 3c82f2f

Browse files
yfodilremyleone
andauthored
feat(cockpit): add datasources (#2543)
* feat(cockpit): add datasources * add type possible values * rename to source --------- Co-authored-by: Rémy Léone <[email protected]>
1 parent 0fe8dc3 commit 3c82f2f

File tree

7 files changed

+887
-16
lines changed

7 files changed

+887
-16
lines changed

docs/resources/cockpit_source.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
subcategory: "Cockpit"
3+
page_title: "Scaleway: scaleway_cockpit_source"
4+
---
5+
6+
# Resource: scaleway_cockpit_source
7+
8+
Creates and manages Scaleway Cockpit Data Sources.
9+
10+
For more information consult the [documentation](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#data-sources).
11+
12+
## Example Usage
13+
14+
```terraform
15+
resource "scaleway_account_project" "project" {
16+
name = "test project data source"
17+
}
18+
19+
resource "scaleway_cockpit_source" "main" {
20+
project_id = scaleway_account_project.project.id
21+
name = "my-data-source"
22+
type = "metrics"
23+
}
24+
```
25+
26+
## Argument Reference
27+
28+
- `name` - (Required) The name of the cockpit data source.
29+
- `type` - (Required) The type of the cockpit data source. Possible values are: `metrics`, `logs` or `traces`.
30+
- `region` - (Defaults to [provider](../index.md#region) `region`) The [region](../guides/regions_and_zones.md#regions) of the cockpit datasource.
31+
- `project_id` - (Defaults to [provider](../index.md#project_id) `project_id`) The ID of the project the cockpit data source is associated with.
32+
33+
## Attributes Reference
34+
35+
In addition to all arguments above, the following attributes are exported:
36+
37+
- `id` - The ID of the cockpit data source.
38+
39+
~> **Important:** cockpit data sources' IDs are [regional](../guides/regions_and_zones.md#resource-ids), which means they are of the form `{region}/{id}`, e.g. `fr-par/11111111-1111-1111-1111-111111111111
40+
41+
- `url` - The URL of the cockpit data source.
42+
- `origin` - The origin of the cockpit data source.
43+
- `synchronized_with_grafana` - Indicates whether the data source is synchronized with Grafana.
44+
- `created_at` - Date and time of the cockpit data source's creation (RFC 3339 format).
45+
- `updated_at` - Date and time of the cockpit datas ource's last update (RFC 3339 format).
46+
47+
## Import
48+
49+
Cockpits Data Sources can be imported using the `{region}/{id}`, e.g.
50+
51+
```bash
52+
$ terraform import scaleway_cockpit_source.main fr-par/11111111-1111-1111-1111-111111111111
53+
```

internal/provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ func Provider(config *Config) plugin.ProviderFunc {
123123
"scaleway_block_snapshot": block.ResourceSnapshot(),
124124
"scaleway_block_volume": block.ResourceVolume(),
125125
"scaleway_cockpit": cockpit.ResourceCockpit(),
126+
"scaleway_cockpit_source": cockpit.ResourceCockpitSource(),
126127
"scaleway_cockpit_grafana_user": cockpit.ResourceCockpitGrafanaUser(),
127128
"scaleway_cockpit_token": cockpit.ResourceToken(),
128129
"scaleway_container": container.ResourceContainer(),

internal/services/cockpit/helpers_cockpit.go

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import (
77
"strings"
88
"time"
99

10-
cockpit "github.com/scaleway/scaleway-sdk-go/api/cockpit/v1beta1"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/scaleway/scaleway-sdk-go/api/cockpit/v1"
12+
cockpitv1beta1 "github.com/scaleway/scaleway-sdk-go/api/cockpit/v1beta1"
1113
"github.com/scaleway/scaleway-sdk-go/scw"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
1215
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
1316
"github.com/scaleway/terraform-provider-scaleway/v2/internal/transport"
1417
)
@@ -21,14 +24,34 @@ const (
2124
)
2225

2326
// NewAPI returns a new cockpit API.
24-
func NewAPI(m interface{}) (*cockpit.API, error) {
25-
api := cockpit.NewAPI(meta.ExtractScwClient(m))
27+
func NewAPI(m interface{}) (*cockpitv1beta1.API, error) {
28+
api := cockpitv1beta1.NewAPI(meta.ExtractScwClient(m))
2629

2730
return api, nil
2831
}
2932

33+
func cockpitAPIWithRegion(d *schema.ResourceData, m interface{}) (*cockpit.RegionalAPI, scw.Region, error) {
34+
api := cockpit.NewRegionalAPI(meta.ExtractScwClient(m))
35+
36+
region, err := meta.ExtractRegion(d, m)
37+
if err != nil {
38+
return nil, "", err
39+
}
40+
return api, region, err
41+
}
42+
43+
func NewAPIWithRegionAndID(m interface{}, id string) (*cockpit.RegionalAPI, scw.Region, string, error) {
44+
api := cockpit.NewRegionalAPI(meta.ExtractScwClient(m))
45+
46+
region, id, err := regional.ParseID(id)
47+
if err != nil {
48+
return nil, "", "", err
49+
}
50+
return api, region, id, nil
51+
}
52+
3053
// NewAPIGrafanaUserID returns a new cockpit API with the Grafana user ID and the project ID.
31-
func NewAPIGrafanaUserID(m interface{}, id string) (*cockpit.API, string, uint32, error) {
54+
func NewAPIGrafanaUserID(m interface{}, id string) (*cockpitv1beta1.API, string, uint32, error) {
3255
projectID, resourceIDString, err := parseCockpitID(id)
3356
if err != nil {
3457
return nil, "", 0, err
@@ -61,13 +84,13 @@ func parseCockpitID(id string) (projectID string, cockpitID string, err error) {
6184
return parts[0], parts[1], nil
6285
}
6386

64-
func waitForCockpit(ctx context.Context, api *cockpit.API, projectID string, timeout time.Duration) (*cockpit.Cockpit, error) {
87+
func waitForCockpit(ctx context.Context, api *cockpitv1beta1.API, projectID string, timeout time.Duration) (*cockpitv1beta1.Cockpit, error) {
6588
retryInterval := defaultCockpitRetryInterval
6689
if transport.DefaultWaitRetryInterval != nil {
6790
retryInterval = *transport.DefaultWaitRetryInterval
6891
}
6992

70-
return api.WaitForCockpit(&cockpit.WaitForCockpitRequest{
93+
return api.WaitForCockpit(&cockpitv1beta1.WaitForCockpitRequest{
7194
ProjectID: projectID,
7295
Timeout: scw.TimeDurationPtr(timeout),
7396
RetryInterval: &retryInterval,

internal/services/cockpit/source.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package cockpit
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
9+
"github.com/scaleway/scaleway-sdk-go/api/cockpit/v1"
10+
"github.com/scaleway/scaleway-sdk-go/scw"
11+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
15+
)
16+
17+
func ResourceCockpitSource() *schema.Resource {
18+
return &schema.Resource{
19+
CreateContext: ResourceCockpitSourceCreate,
20+
ReadContext: ResourceCockpitSourceRead,
21+
DeleteContext: ResourceCockpitSourceDelete,
22+
Timeouts: &schema.ResourceTimeout{
23+
Create: schema.DefaultTimeout(DefaultCockpitTimeout),
24+
Read: schema.DefaultTimeout(DefaultCockpitTimeout),
25+
Delete: schema.DefaultTimeout(DefaultCockpitTimeout),
26+
Default: schema.DefaultTimeout(DefaultCockpitTimeout),
27+
},
28+
Importer: &schema.ResourceImporter{
29+
StateContext: schema.ImportStatePassthroughContext,
30+
},
31+
Schema: map[string]*schema.Schema{
32+
"name": {
33+
Type: schema.TypeString,
34+
Optional: true,
35+
ForceNew: true,
36+
Description: "Name of the datasource",
37+
},
38+
"type": {
39+
Type: schema.TypeString,
40+
Optional: true,
41+
ForceNew: true,
42+
Description: "The type of the datasource",
43+
ValidateFunc: validation.StringInSlice([]string{
44+
cockpit.DataSourceTypeMetrics.String(),
45+
cockpit.DataSourceTypeLogs.String(),
46+
cockpit.DataSourceTypeTraces.String(),
47+
}, false),
48+
},
49+
// computed
50+
"url": {
51+
Type: schema.TypeString,
52+
Computed: true,
53+
Description: "The URL of the datasource",
54+
},
55+
"origin": {
56+
Type: schema.TypeString,
57+
Computed: true,
58+
Description: "The origin of the datasource",
59+
},
60+
"synchronized_with_grafana": {
61+
Type: schema.TypeBool,
62+
Computed: true,
63+
Description: "Indicates whether the data source is synchronized with Grafana",
64+
},
65+
"created_at": {
66+
Type: schema.TypeString,
67+
Computed: true,
68+
Description: "The date and time of the creation of the cockpit datasource",
69+
},
70+
"updated_at": {
71+
Type: schema.TypeString,
72+
Computed: true,
73+
Description: "The date and time of the last update of the cockpit datasource",
74+
},
75+
"project_id": account.ProjectIDSchema(),
76+
"region": regional.Schema(),
77+
},
78+
}
79+
}
80+
81+
func ResourceCockpitSourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
82+
api, region, err := cockpitAPIWithRegion(d, meta)
83+
if err != nil {
84+
return diag.FromErr(err)
85+
}
86+
87+
res, err := api.CreateDataSource(&cockpit.RegionalAPICreateDataSourceRequest{
88+
Region: region,
89+
ProjectID: d.Get("project_id").(string),
90+
Name: d.Get("name").(string),
91+
Type: cockpit.DataSourceType(d.Get("type").(string)),
92+
}, scw.WithContext(ctx))
93+
if err != nil {
94+
return diag.FromErr(err)
95+
}
96+
97+
d.SetId(regional.NewIDString(region, res.ID))
98+
return ResourceCockpitSourceRead(ctx, d, meta)
99+
}
100+
101+
func ResourceCockpitSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
102+
api, region, id, err := NewAPIWithRegionAndID(meta, d.Id())
103+
if err != nil {
104+
return diag.FromErr(err)
105+
}
106+
107+
res, err := api.GetDataSource(&cockpit.RegionalAPIGetDataSourceRequest{
108+
Region: region,
109+
DataSourceID: id,
110+
}, scw.WithContext(ctx))
111+
if err != nil {
112+
if httperrors.Is404(err) {
113+
d.SetId("")
114+
return nil
115+
}
116+
return diag.FromErr(err)
117+
}
118+
119+
_ = d.Set("name", res.Name)
120+
_ = d.Set("type", res.Type.String())
121+
_ = d.Set("url", res.URL)
122+
_ = d.Set("origin", res.Origin)
123+
_ = d.Set("synchronized_with_grafana", res.SynchronizedWithGrafana)
124+
_ = d.Set("region", res.Region)
125+
_ = d.Set("created_at", types.FlattenTime(res.CreatedAt))
126+
_ = d.Set("updated_at", types.FlattenTime(res.UpdatedAt))
127+
_ = d.Set("project_id", res.ProjectID)
128+
129+
return nil
130+
}
131+
132+
func ResourceCockpitSourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
133+
api, region, id, err := NewAPIWithRegionAndID(meta, d.Id())
134+
if err != nil {
135+
return diag.FromErr(err)
136+
}
137+
138+
err = api.DeleteDataSource(&cockpit.RegionalAPIDeleteDataSourceRequest{
139+
DataSourceID: id,
140+
Region: region,
141+
}, scw.WithContext(ctx))
142+
if err != nil && !httperrors.Is404(err) {
143+
return diag.FromErr(err)
144+
}
145+
146+
return nil
147+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package cockpit_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
9+
cockpitSDK "github.com/scaleway/scaleway-sdk-go/api/cockpit/v1"
10+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
11+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/cockpit"
13+
)
14+
15+
func TestAccCockpitSource_Basic(t *testing.T) {
16+
tt := acctest.NewTestTools(t)
17+
defer tt.Cleanup()
18+
19+
resource.ParallelTest(t, resource.TestCase{
20+
PreCheck: func() { acctest.PreCheck(t) },
21+
ProviderFactories: tt.ProviderFactories,
22+
CheckDestroy: isSourceDestroyed(tt),
23+
Steps: []resource.TestStep{
24+
{
25+
Config: `
26+
resource "scaleway_account_project" "project" {
27+
name = "tf_tests_cockpit_datasource_basic"
28+
}
29+
30+
resource "scaleway_cockpit_source" "main" {
31+
project_id = scaleway_account_project.project.id
32+
name = "my-source"
33+
type = "metrics"
34+
}
35+
`,
36+
Check: resource.ComposeTestCheckFunc(
37+
isSourcePresent(tt, "scaleway_cockpit_source.main"),
38+
resource.TestCheckResourceAttr("scaleway_cockpit_source.main", "name", "my-source"),
39+
resource.TestCheckResourceAttr("scaleway_cockpit_source.main", "type", "metrics"),
40+
resource.TestCheckResourceAttr("scaleway_cockpit_source.main", "region", "fr-par"),
41+
resource.TestCheckResourceAttrSet("scaleway_cockpit_source.main", "url"),
42+
resource.TestCheckResourceAttrSet("scaleway_cockpit_source.main", "origin"),
43+
resource.TestCheckResourceAttrSet("scaleway_cockpit_source.main", "created_at"),
44+
resource.TestCheckResourceAttrSet("scaleway_cockpit_source.main", "updated_at"),
45+
resource.TestCheckResourceAttrSet("scaleway_cockpit_source.main", "synchronized_with_grafana"),
46+
resource.TestCheckResourceAttrPair("scaleway_cockpit_source.main", "project_id", "scaleway_account_project.project", "id"),
47+
),
48+
},
49+
},
50+
})
51+
}
52+
53+
func isSourcePresent(tt *acctest.TestTools, n string) resource.TestCheckFunc {
54+
return func(state *terraform.State) error {
55+
rs, ok := state.RootModule().Resources[n]
56+
if !ok {
57+
return fmt.Errorf("resource cockpit source not found: %s", n)
58+
}
59+
60+
api, region, ID, err := cockpit.NewAPIWithRegionAndID(tt.Meta, rs.Primary.ID)
61+
if err != nil {
62+
return err
63+
}
64+
65+
_, err = api.GetDataSource(&cockpitSDK.RegionalAPIGetDataSourceRequest{
66+
Region: region,
67+
DataSourceID: ID,
68+
})
69+
if err != nil {
70+
return err
71+
}
72+
73+
return nil
74+
}
75+
}
76+
77+
func isSourceDestroyed(tt *acctest.TestTools) resource.TestCheckFunc {
78+
return func(state *terraform.State) error {
79+
for _, rs := range state.RootModule().Resources {
80+
if rs.Type != "scaleway_cockpit_source" {
81+
continue
82+
}
83+
84+
api, region, ID, err := cockpit.NewAPIWithRegionAndID(tt.Meta, rs.Primary.ID)
85+
if err != nil {
86+
return err
87+
}
88+
89+
_, err = api.GetDataSource(&cockpitSDK.RegionalAPIGetDataSourceRequest{
90+
Region: region,
91+
DataSourceID: ID,
92+
})
93+
94+
if err == nil {
95+
return fmt.Errorf("cockpit source (%s) still exists", rs.Primary.ID)
96+
}
97+
98+
if !httperrors.Is404(err) {
99+
return err
100+
}
101+
}
102+
103+
return nil
104+
}
105+
}

0 commit comments

Comments
 (0)