Skip to content

Commit 5067452

Browse files
Mia-Crossremyleone
andauthored
feat(container): vpc integration (#3158)
* feat(container): add support for vpc integration * Fixes --------- Co-authored-by: Rémy Léone <[email protected]>
1 parent 3a38fcc commit 5067452

File tree

10 files changed

+7331
-0
lines changed

10 files changed

+7331
-0
lines changed

docs/resources/container.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ The following arguments are supported:
111111

112112
- `args` - (Optional) Arguments passed to the command specified in the "command" field. These override the default arguments from the container image, and behave like command-line parameters.
113113

114+
- `private_network_id` (Optional) The ID of the Private Network the container is connected to.
115+
116+
~> **Important** This feature is currently in beta and requires a namespace with VPC integration activated by setting the `activate_vpc_integration` attribute to `true`.
117+
114118
Note that if you want to use your own configuration, you must consult our configuration [restrictions](https://www.scaleway.com/en/docs/serverless-containers/reference-content/containers-limitations/#configuration-restrictions) section.
115119

116120
## Attributes Reference

docs/resources/container_namespace.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ The following arguments are supported:
4040

4141
- `secret_environment_variables` - (Optional) The secret environment variables of the namespace.
4242

43+
- `activate_vpc_integration` - (Optional) Activates VPC integration for the namespace. Containers of a namespace with VPC integration activated will be able to connect to a Private Network.
44+
45+
~> **Important** Updates to `activate_vpc_integration` will recreate the namespace.
46+
4347
## Attributes Reference
4448

4549
The `scaleway_container_namespace` resource exports certain attributes once the Containers namespace has been created. These attributes can be referenced in other parts of your Terraform configuration.

internal/acctest/validate_cassettes_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func exceptionsCassettesCases() map[string]struct{} {
2929
"../services/secret/testdata/secret-version-type.cassette.yaml": {},
3030
"../services/file/testdata/file-system-invalid-size-granularity-fails.cassette.yaml": {},
3131
"../services/file/testdata/file-system-size-too-small-fails.cassette.yaml": {},
32+
"../services/container/testdata/namespace-vpc-integration.cassette.yaml": {},
3233
"../services/function/testdata/function-namespace-vpc-integration.cassette.yaml": {},
3334
}
3435
}

internal/services/container/container.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func ResourceContainer() *schema.Resource {
5353
"namespace_id": {
5454
Type: schema.TypeString,
5555
Required: true,
56+
ForceNew: true,
5657
Description: "The container namespace associated",
5758
},
5859
"tags": {
@@ -263,6 +264,11 @@ func ResourceContainer() *schema.Resource {
263264
Optional: true,
264265
Description: "Arguments passed to the command from the command \"field\". Overrides the arguments from the container image.",
265266
},
267+
"private_network_id": {
268+
Type: schema.TypeString,
269+
Optional: true,
270+
Description: "ID of the Private Network the container is connected to",
271+
},
266272
// computed
267273
"status": {
268274
Type: schema.TypeString,
@@ -385,6 +391,12 @@ func ResourceContainerRead(ctx context.Context, d *schema.ResourceData, m any) d
385391
_ = d.Set("command", types.FlattenSliceString(co.Command))
386392
_ = d.Set("args", types.FlattenSliceString(co.Args))
387393

394+
if co.PrivateNetworkID != nil {
395+
_ = d.Set("private_network_id", regional.NewID(region, types.FlattenStringPtr(co.PrivateNetworkID).(string)).String())
396+
} else {
397+
_ = d.Set("private_network_id", nil)
398+
}
399+
388400
return nil
389401
}
390402

internal/services/container/container_test.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
1313
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
1414
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/container"
15+
vpcchecks "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/vpc/testfuncs"
1516
)
1617

1718
func TestAccContainer_Basic(t *testing.T) {
@@ -625,6 +626,137 @@ func TestAccContainer_CommandAndArgs(t *testing.T) {
625626
})
626627
}
627628

629+
func TestAccContainer_PrivateNetwork(t *testing.T) {
630+
tt := acctest.NewTestTools(t)
631+
defer tt.Cleanup()
632+
resource.ParallelTest(t, resource.TestCase{
633+
PreCheck: func() { acctest.PreCheck(t) },
634+
ProviderFactories: tt.ProviderFactories,
635+
CheckDestroy: resource.ComposeTestCheckFunc(
636+
isNamespaceDestroyed(tt),
637+
isContainerDestroyed(tt),
638+
vpcchecks.CheckPrivateNetworkDestroy(tt),
639+
),
640+
Steps: []resource.TestStep{
641+
{
642+
Config: `
643+
resource scaleway_vpc_private_network pn00 {
644+
name = "test-acc-container-pn-pn00"
645+
}
646+
resource scaleway_vpc_private_network pn01 {
647+
name = "test-acc-container-pn-pn01"
648+
}
649+
650+
resource scaleway_container_namespace main {
651+
activate_vpc_integration = true
652+
}
653+
654+
resource scaleway_container c00 {
655+
name = "test-acc-container-pn-c00"
656+
namespace_id = scaleway_container_namespace.main.id
657+
private_network_id = scaleway_vpc_private_network.pn00.id
658+
sandbox = "v1"
659+
}
660+
`,
661+
Check: resource.ComposeTestCheckFunc(
662+
isContainerPresent(tt, "scaleway_container.c00"),
663+
resource.TestCheckResourceAttr("scaleway_container_namespace.main", "activate_vpc_integration", "true"),
664+
resource.TestCheckResourceAttr("scaleway_container.c00", "sandbox", "v1"),
665+
resource.TestCheckResourceAttrPair("scaleway_container.c00", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
666+
),
667+
},
668+
{
669+
Config: `
670+
resource scaleway_vpc_private_network pn00 {
671+
name = "test-acc-container-pn-pn00"
672+
}
673+
resource scaleway_vpc_private_network pn01 {
674+
name = "test-acc-container-pn-pn01"
675+
}
676+
677+
resource scaleway_container_namespace main {
678+
activate_vpc_integration = true
679+
}
680+
681+
resource scaleway_container c00 {
682+
name = "test-acc-container-pn-c00"
683+
namespace_id = scaleway_container_namespace.main.id
684+
private_network_id = scaleway_vpc_private_network.pn00.id
685+
sandbox = "v1"
686+
}
687+
688+
resource scaleway_container c01 {
689+
name = "test-acc-container-pn-c01"
690+
namespace_id = scaleway_container_namespace.main.id
691+
private_network_id = scaleway_vpc_private_network.pn00.id
692+
sandbox = "v1"
693+
}
694+
695+
resource scaleway_container c02 {
696+
name = "test-acc-container-pn-c02"
697+
namespace_id = scaleway_container_namespace.main.id
698+
private_network_id = scaleway_vpc_private_network.pn00.id
699+
sandbox = "v1"
700+
}
701+
`,
702+
Check: resource.ComposeTestCheckFunc(
703+
isContainerPresent(tt, "scaleway_container.c00"),
704+
isContainerPresent(tt, "scaleway_container.c01"),
705+
isContainerPresent(tt, "scaleway_container.c02"),
706+
resource.TestCheckResourceAttr("scaleway_container.c00", "sandbox", "v1"),
707+
resource.TestCheckResourceAttr("scaleway_container.c01", "sandbox", "v1"),
708+
resource.TestCheckResourceAttr("scaleway_container.c02", "sandbox", "v1"),
709+
resource.TestCheckResourceAttrPair("scaleway_container.c00", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
710+
resource.TestCheckResourceAttrPair("scaleway_container.c01", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
711+
resource.TestCheckResourceAttrPair("scaleway_container.c02", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
712+
),
713+
},
714+
{
715+
Config: `
716+
resource scaleway_vpc_private_network pn00 {
717+
name = "test-acc-container-pn-pn00"
718+
}
719+
resource scaleway_vpc_private_network pn01 {
720+
name = "test-acc-container-pn-pn01"
721+
}
722+
723+
resource scaleway_container_namespace main {
724+
activate_vpc_integration = true
725+
}
726+
727+
resource scaleway_container c00 {
728+
name = "test-acc-container-pn-c00"
729+
namespace_id = scaleway_container_namespace.main.id
730+
sandbox = "v1"
731+
}
732+
733+
resource scaleway_container c01 {
734+
name = "test-acc-container-pn-c01"
735+
namespace_id = scaleway_container_namespace.main.id
736+
private_network_id = scaleway_vpc_private_network.pn01.id
737+
sandbox = "v1"
738+
}
739+
740+
resource scaleway_container c02 {
741+
name = "test-acc-container-pn-c02"
742+
namespace_id = scaleway_container_namespace.main.id
743+
private_network_id = scaleway_vpc_private_network.pn00.id
744+
sandbox = "v1"
745+
}
746+
`,
747+
Check: resource.ComposeTestCheckFunc(
748+
isContainerPresent(tt, "scaleway_container.c00"),
749+
isContainerPresent(tt, "scaleway_container.c01"),
750+
isContainerPresent(tt, "scaleway_container.c02"),
751+
resource.TestCheckResourceAttr("scaleway_container.c00", "private_network_id", ""),
752+
resource.TestCheckResourceAttrPair("scaleway_container.c01", "private_network_id", "scaleway_vpc_private_network.pn01", "id"),
753+
resource.TestCheckResourceAttrPair("scaleway_container.c02", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
754+
),
755+
},
756+
},
757+
})
758+
}
759+
628760
func isContainerPresent(tt *acctest.TestTools, n string) resource.TestCheckFunc {
629761
return func(state *terraform.State) error {
630762
rs, ok := state.RootModule().Resources[n]

internal/services/container/helpers_container.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ func setCreateContainerRequest(d *schema.ResourceData, region scw.Region) (*cont
152152
req.Args = types.ExpandStrings(args)
153153
}
154154

155+
if pnID, ok := d.GetOk("private_network_id"); ok {
156+
req.PrivateNetworkID = types.ExpandStringPtr(locality.ExpandID(pnID.(string)))
157+
}
158+
155159
return req, nil
156160
}
157161

@@ -270,6 +274,10 @@ func setUpdateContainerRequest(d *schema.ResourceData, region scw.Region, contai
270274
req.Args = types.ExpandUpdatedStringsPtr(d.Get("args"))
271275
}
272276

277+
if d.HasChanges("private_network_id") {
278+
req.PrivateNetworkID = types.ExpandUpdatedStringPtr(locality.ExpandID(d.Get("private_network_id")))
279+
}
280+
273281
return req, nil
274282
}
275283

internal/services/container/namespace.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ func ResourceNamespace() *schema.Resource {
9595
Description: "Destroy registry on deletion",
9696
Deprecated: "Registry namespace is automatically destroyed with namespace",
9797
},
98+
"activate_vpc_integration": {
99+
Type: schema.TypeBool,
100+
ForceNew: true,
101+
Optional: true,
102+
Default: false,
103+
Description: "Activate VPC integration for the namespace",
104+
},
98105
"region": regional.Schema(),
99106
"organization_id": account.OrganizationIDSchema(),
100107
"project_id": account.ProjectIDSchema(),
@@ -122,6 +129,10 @@ func ResourceContainerNamespaceCreate(ctx context.Context, d *schema.ResourceDat
122129
createReq.Tags = types.ExpandStrings(rawTag)
123130
}
124131

132+
if activateVPC, ok := d.GetOk("activate_vpc_integration"); ok {
133+
createReq.ActivateVpcIntegration = activateVPC.(bool)
134+
}
135+
125136
ns, err := api.CreateNamespace(createReq, scw.WithContext(ctx))
126137
if err != nil {
127138
return diag.FromErr(err)
@@ -164,6 +175,7 @@ func ResourceContainerNamespaceRead(ctx context.Context, d *schema.ResourceData,
164175
_ = d.Set("registry_endpoint", ns.RegistryEndpoint)
165176
_ = d.Set("registry_namespace_id", ns.RegistryNamespaceID)
166177
_ = d.Set("secret_environment_variables", flattenContainerSecrets(ns.SecretEnvironmentVariables))
178+
_ = d.Set("activate_vpc_integration", types.FlattenBoolPtr(ns.VpcIntegrationActivated)) //nolint:staticcheck
167179

168180
return nil
169181
}

internal/services/container/namespace_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package container_test
22

33
import (
44
"fmt"
5+
"regexp"
56
"testing"
67

78
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
@@ -12,6 +13,7 @@ import (
1213
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
1314
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/container"
1415
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/registry"
16+
vpcchecks "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/vpc/testfuncs"
1517
)
1618

1719
const containerNamespaceResource = "scaleway_container_namespace"
@@ -265,6 +267,78 @@ func TestAccNamespace_DestroyRegistry(t *testing.T) {
265267
})
266268
}
267269

270+
func TestAccNamespace_VPCIntegration(t *testing.T) {
271+
tt := acctest.NewTestTools(t)
272+
defer tt.Cleanup()
273+
274+
namespaceID := ""
275+
276+
resource.ParallelTest(t, resource.TestCase{
277+
PreCheck: func() { acctest.PreCheck(t) },
278+
ProviderFactories: tt.ProviderFactories,
279+
CheckDestroy: resource.ComposeTestCheckFunc(
280+
isNamespaceDestroyed(tt),
281+
isContainerDestroyed(tt),
282+
vpcchecks.CheckPrivateNetworkDestroy(tt),
283+
),
284+
Steps: []resource.TestStep{
285+
{
286+
Config: `
287+
resource scaleway_vpc_private_network main {}
288+
289+
resource scaleway_container_namespace main {}
290+
291+
resource scaleway_container main {
292+
namespace_id = scaleway_container_namespace.main.id
293+
sandbox = "v1"
294+
}
295+
`,
296+
Check: resource.ComposeTestCheckFunc(
297+
isNamespacePresent(tt, "scaleway_container_namespace.main"),
298+
resource.TestCheckResourceAttr("scaleway_container_namespace.main", "activate_vpc_integration", "false"),
299+
acctest.CheckResourceIDPersisted("scaleway_container_namespace.main", &namespaceID),
300+
),
301+
},
302+
{
303+
Config: `
304+
resource scaleway_vpc_private_network main {}
305+
306+
resource scaleway_container_namespace main {}
307+
308+
resource scaleway_container main {
309+
namespace_id = scaleway_container_namespace.main.id
310+
private_network_id = scaleway_vpc_private_network.main.id
311+
sandbox = "v1"
312+
}
313+
`,
314+
ExpectError: regexp.MustCompile("Application can't be attached to private network, vpc integration must be activated on its parent namespace"),
315+
},
316+
{
317+
Config: `
318+
resource scaleway_vpc_private_network main {}
319+
320+
resource scaleway_container_namespace main {
321+
activate_vpc_integration = true
322+
}
323+
324+
resource scaleway_container main {
325+
namespace_id = scaleway_container_namespace.main.id
326+
private_network_id = scaleway_vpc_private_network.main.id
327+
sandbox = "v1"
328+
}
329+
`,
330+
Check: resource.ComposeTestCheckFunc(
331+
isNamespacePresent(tt, "scaleway_container_namespace.main"),
332+
isContainerPresent(tt, "scaleway_container.main"),
333+
resource.TestCheckResourceAttr("scaleway_container_namespace.main", "activate_vpc_integration", "true"),
334+
resource.TestCheckResourceAttrPair("scaleway_container.main", "private_network_id", "scaleway_vpc_private_network.main", "id"),
335+
acctest.CheckResourceIDChanged("scaleway_container_namespace.main", &namespaceID),
336+
),
337+
},
338+
},
339+
})
340+
}
341+
268342
func isNamespacePresent(tt *acctest.TestTools, n string) resource.TestCheckFunc {
269343
return func(state *terraform.State) error {
270344
rs, ok := state.RootModule().Resources[n]

0 commit comments

Comments
 (0)