Skip to content

Commit 3bfde74

Browse files
Lichard TormanClément Decoodt
Lichard Torman
and
Clément Decoodt
authored
feat(iot): Add IoT Network resource (#772)
Co-authored-by: Clément Decoodt <[email protected]>
1 parent e6c6a35 commit 3bfde74

6 files changed

+1377
-3
lines changed

docs/resources/iot_network.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
layout: "scaleway"
3+
page_title: "Scaleway: scaleway_iot_network"
4+
description: |-
5+
Manages Scaleway IoT Networks.
6+
---
7+
8+
# scaleway_iot_network
9+
10+
-> **Note:** This terraform resource is currently in beta and might include breaking change in future releases.
11+
12+
Creates and manages Scaleway IoT Networks. For more information, see the following:
13+
14+
- [API documentation](https://developers.scaleway.com/en/products/iot/api).
15+
- [Product documentation](https://www.scaleway.com/en/docs/scaleway-iothub-networks/)
16+
17+
For more step-by-step instructions on how to setup the networks on the external providers backends, you can follow these guides:
18+
19+
- [Configuring the Sigfox backend](https://www.scaleway.com/en/docs/scaleway-iothub-networks/#-Configuring-the-Sigfox-backend)
20+
- [Using the Rest Network](https://www.scaleway.com/en/docs/scaleway-iothub-networks/#-Using-the-Rest-Network)
21+
22+
## Examples
23+
24+
```hcl
25+
resource "scaleway_iot_network" "main" {
26+
name = "main"
27+
hub_id = scaleway_iot_hub.main.id
28+
type = "sigfox"
29+
}
30+
resource "scaleway_iot_hub" "main" {
31+
name = "main"
32+
product_plan = "plan_shared"
33+
}
34+
```
35+
36+
## Arguments Reference
37+
38+
~> **Important:** Updates to any value will recreate the IoT Route.
39+
40+
The following arguments are supported:
41+
42+
- `name` - (Required) The name of the IoT Network you want to create (e.g. `my-net`).
43+
44+
- `hub_id` - (Required) The hub ID to which the Network will be attached to.
45+
46+
- `type` - (Required) The network type to create (e.g. `sigfox`).
47+
48+
- `topic_prefix` - (Optional) The prefix that will be prepended to all topics for this Network.
49+
50+
## Attributes Reference
51+
52+
In addition to all arguments above, the following attributes are exported:
53+
54+
- `id` - The ID of the Network.
55+
- `region` - (Defaults to [provider](../index.md#region) `region`) The [region](../guides/regions_and_zones.md#regions) in which the Network is attached to.
56+
- `created_at` - The date and time the Network was created.
57+
- `endpoint` - The endpoint to use when interacting with the network.
58+
- `secret` - The endpoint key to keep secret.
59+
60+
## Import
61+
62+
IoT Networks can be imported using the `{region}/{id}`, e.g.
63+
64+
```bash
65+
$ terraform import scaleway_iot_network.net01 fr-par/11111111-1111-1111-1111-111111111111
66+
```
67+

scaleway/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
7878
"scaleway_iot_hub": resourceScalewayIotHub(),
7979
"scaleway_iot_device": resourceScalewayIotDevice(),
8080
"scaleway_iot_route": resourceScalewayIotRoute(),
81+
"scaleway_iot_network": resourceScalewayIotNetwork(),
8182
"scaleway_k8s_cluster": resourceScalewayK8SCluster(),
8283
"scaleway_k8s_pool": resourceScalewayK8SPool(),
8384
"scaleway_lb": resourceScalewayLb(),

scaleway/resource_iot_device.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ func resourceScalewayIotDevice() *schema.Resource {
2323
SchemaVersion: 0,
2424
Schema: map[string]*schema.Schema{
2525
"hub_id": {
26-
Type: schema.TypeString,
27-
Required: true,
28-
Description: "The ID of the hub on which this device will be created",
26+
Type: schema.TypeString,
27+
Required: true,
28+
Description: "The ID of the hub on which this device will be created",
29+
DiffSuppressFunc: diffSuppressFuncLocality,
2930
},
3031
"name": {
3132
Type: schema.TypeString,

scaleway/resource_iot_network.go

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package scaleway
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+
iot "github.com/scaleway/scaleway-sdk-go/api/iot/v1"
10+
"github.com/scaleway/scaleway-sdk-go/scw"
11+
)
12+
13+
func resourceScalewayIotNetwork() *schema.Resource {
14+
return &schema.Resource{
15+
CreateContext: resourceScalewayIotNetworkCreate,
16+
ReadContext: resourceScalewayIotNetworkRead,
17+
DeleteContext: resourceScalewayIotNetworkDelete,
18+
Importer: &schema.ResourceImporter{
19+
StateContext: schema.ImportStatePassthroughContext,
20+
},
21+
SchemaVersion: 0,
22+
Schema: map[string]*schema.Schema{
23+
"hub_id": {
24+
Type: schema.TypeString,
25+
Required: true,
26+
ForceNew: true,
27+
Description: "The ID of the hub on which this network will be created",
28+
DiffSuppressFunc: diffSuppressFuncLocality,
29+
},
30+
"name": {
31+
Type: schema.TypeString,
32+
Required: true,
33+
ForceNew: true,
34+
Description: "The name of the network",
35+
},
36+
"type": {
37+
Type: schema.TypeString,
38+
Required: true,
39+
ForceNew: true,
40+
Description: "The type of the network",
41+
ValidateFunc: validation.StringInSlice([]string{
42+
iot.NetworkNetworkTypeSigfox.String(),
43+
iot.NetworkNetworkTypeRest.String(),
44+
}, false),
45+
},
46+
"topic_prefix": {
47+
Type: schema.TypeString,
48+
Optional: true,
49+
ForceNew: true,
50+
Description: "The prefix that will be prepended to all topics for this Network",
51+
},
52+
// Computed elements
53+
"created_at": {
54+
Type: schema.TypeString,
55+
Computed: true,
56+
Description: "The date and time of the creation of the network",
57+
},
58+
"endpoint": {
59+
Type: schema.TypeString,
60+
Computed: true,
61+
Description: "The endpoint to use when interacting with the network",
62+
},
63+
"secret": {
64+
Type: schema.TypeString,
65+
Computed: true,
66+
Description: "The endpoint key to keep secret",
67+
Sensitive: true,
68+
},
69+
},
70+
}
71+
}
72+
73+
func resourceScalewayIotNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
74+
iotAPI, region, err := iotAPIWithRegion(d, meta)
75+
if err != nil {
76+
return diag.FromErr(err)
77+
}
78+
79+
////
80+
// Create network
81+
////
82+
83+
req := &iot.CreateNetworkRequest{
84+
Region: region,
85+
Name: expandOrGenerateString(d.Get("name"), "network"),
86+
Type: iot.NetworkNetworkType(d.Get("type").(string)),
87+
HubID: expandID(d.Get("hub_id")),
88+
}
89+
90+
if topicPrefix, ok := d.GetOk("topic_prefix"); ok {
91+
req.TopicPrefix = topicPrefix.(string)
92+
}
93+
94+
res, err := iotAPI.CreateNetwork(req, scw.WithContext(ctx))
95+
if err != nil {
96+
return diag.FromErr(err)
97+
}
98+
99+
d.SetId(newRegionalIDString(region, res.Network.ID))
100+
101+
// Secret key cannot be retreived later
102+
_ = d.Set("secret", res.Secret)
103+
104+
return resourceScalewayIotNetworkRead(ctx, d, meta)
105+
}
106+
107+
func resourceScalewayIotNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
108+
iotAPI, region, networkID, err := iotAPIWithRegionAndID(m, d.Id())
109+
if err != nil {
110+
return diag.FromErr(err)
111+
}
112+
113+
////
114+
// Read Network
115+
////
116+
network, err := iotAPI.GetNetwork(&iot.GetNetworkRequest{
117+
Region: region,
118+
NetworkID: networkID,
119+
}, scw.WithContext(ctx))
120+
if err != nil {
121+
if is404Error(err) {
122+
d.SetId("")
123+
return nil
124+
}
125+
return diag.FromErr(err)
126+
}
127+
128+
_ = d.Set("name", network.Name)
129+
_ = d.Set("type", network.Type.String())
130+
_ = d.Set("endpoint", network.Endpoint)
131+
_ = d.Set("hub_id", newRegionalID(region, network.HubID).String())
132+
_ = d.Set("created_at", network.CreatedAt.String())
133+
_ = d.Set("topic_prefix", network.TopicPrefix)
134+
135+
return nil
136+
}
137+
138+
func resourceScalewayIotNetworkDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
139+
iotAPI, region, networkID, err := iotAPIWithRegionAndID(m, d.Id())
140+
if err != nil {
141+
return diag.FromErr(err)
142+
}
143+
144+
////
145+
// Delete Network
146+
////
147+
err = iotAPI.DeleteNetwork(&iot.DeleteNetworkRequest{
148+
Region: region,
149+
NetworkID: networkID,
150+
}, scw.WithContext(ctx))
151+
if err != nil {
152+
if !is404Error(err) {
153+
return diag.FromErr(err)
154+
}
155+
}
156+
157+
return nil
158+
}

scaleway/resource_iot_network_test.go

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package scaleway
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+
iot "github.com/scaleway/scaleway-sdk-go/api/iot/v1"
10+
)
11+
12+
func TestAccScalewayIotNetwork_Minimal(t *testing.T) {
13+
tt := NewTestTools(t)
14+
defer tt.Cleanup()
15+
resource.ParallelTest(t, resource.TestCase{
16+
PreCheck: func() { testAccPreCheck(t) },
17+
ProviderFactories: tt.ProviderFactories,
18+
// Destruction is done via the hub destruction.
19+
CheckDestroy: testAccCheckScalewayIotHubDestroy(tt),
20+
Steps: []resource.TestStep{
21+
{
22+
Config: `
23+
resource "scaleway_iot_network" "default" {
24+
name = "default"
25+
hub_id = scaleway_iot_hub.minimal.id
26+
type = "sigfox"
27+
}
28+
resource "scaleway_iot_hub" "minimal" {
29+
name = "minimal"
30+
product_plan = "plan_shared"
31+
}
32+
`,
33+
Check: resource.ComposeTestCheckFunc(
34+
testAccCheckScalewayIotHubExists(tt, "scaleway_iot_hub.minimal"),
35+
testAccCheckScalewayIotNetworkExists(tt, "scaleway_iot_network.default"),
36+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "id"),
37+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "hub_id"),
38+
resource.TestCheckResourceAttr("scaleway_iot_network.default", "name", "default"),
39+
resource.TestCheckResourceAttr("scaleway_iot_network.default", "type", "sigfox"),
40+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "endpoint"),
41+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "secret"),
42+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "created_at"),
43+
),
44+
},
45+
{
46+
Config: `
47+
resource "scaleway_iot_network" "default" {
48+
name = "default"
49+
hub_id = scaleway_iot_hub.minimal.id
50+
type = "rest"
51+
}
52+
resource "scaleway_iot_hub" "minimal" {
53+
name = "minimal"
54+
product_plan = "plan_shared"
55+
}
56+
`,
57+
Check: resource.ComposeTestCheckFunc(
58+
testAccCheckScalewayIotHubExists(tt, "scaleway_iot_hub.minimal"),
59+
testAccCheckScalewayIotNetworkExists(tt, "scaleway_iot_network.default"),
60+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "id"),
61+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "hub_id"),
62+
resource.TestCheckResourceAttr("scaleway_iot_network.default", "name", "default"),
63+
resource.TestCheckResourceAttr("scaleway_iot_network.default", "type", "rest"),
64+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "endpoint"),
65+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "secret"),
66+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "created_at"),
67+
),
68+
},
69+
{
70+
Config: `
71+
resource "scaleway_iot_network" "default" {
72+
name = "default"
73+
hub_id = scaleway_iot_hub.minimal.id
74+
type = "rest"
75+
topic_prefix = "foo/bar"
76+
}
77+
resource "scaleway_iot_hub" "minimal" {
78+
name = "minimal"
79+
product_plan = "plan_shared"
80+
}
81+
`,
82+
Check: resource.ComposeTestCheckFunc(
83+
testAccCheckScalewayIotHubExists(tt, "scaleway_iot_hub.minimal"),
84+
testAccCheckScalewayIotNetworkExists(tt, "scaleway_iot_network.default"),
85+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "id"),
86+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "hub_id"),
87+
resource.TestCheckResourceAttr("scaleway_iot_network.default", "name", "default"),
88+
resource.TestCheckResourceAttr("scaleway_iot_network.default", "type", "rest"),
89+
resource.TestCheckResourceAttr("scaleway_iot_network.default", "topic_prefix", "foo/bar"),
90+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "endpoint"),
91+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "secret"),
92+
resource.TestCheckResourceAttrSet("scaleway_iot_network.default", "created_at"),
93+
),
94+
},
95+
},
96+
})
97+
}
98+
99+
func testAccCheckScalewayIotNetworkExists(tt *TestTools, n string) resource.TestCheckFunc {
100+
return func(s *terraform.State) error {
101+
rs, ok := s.RootModule().Resources[n]
102+
if !ok {
103+
return fmt.Errorf("resource not found: %s", n)
104+
}
105+
106+
iotAPI, region, networkID, err := iotAPIWithRegionAndID(tt.Meta, rs.Primary.ID)
107+
if err != nil {
108+
return err
109+
}
110+
111+
_, err = iotAPI.GetNetwork(&iot.GetNetworkRequest{
112+
Region: region,
113+
NetworkID: networkID,
114+
})
115+
if err != nil {
116+
return err
117+
}
118+
119+
return nil
120+
}
121+
}

0 commit comments

Comments
 (0)