Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/resources/function.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ The following arguments are supported:

- `project_id` - (Defaults to [provider](../index.md#project_id) `project_id`) The ID of the project the functions namespace is associated with.

- `private_network_id` (Optional) The ID of the Private Network the function is connected to.

~> **Important** This feature is currently in beta and requires a namespace with VPC integration activated by setting the `activate_vpc_integration` attribute to `true`.

## Attributes Reference

Expand Down
4 changes: 4 additions & 0 deletions docs/resources/function_namespace.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ The following arguments are supported:

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

- `activate_vpc_integration` - (Optional) Activates VPC integration for the namespace. Functions of a namespace with VPC integration activated will be able to connect to a Private Network.

~> **Important** Updates to `activate_vpc_integration` will recreate the namespace.

## Attributes Reference

The `scaleway_function_namespace` resource exports certain attributes once the Functions namespace has been created. These attributes can be referenced in other parts of your Terraform configuration.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ require (
github.com/nats-io/jwt/v2 v2.7.4
github.com/nats-io/nats.go v1.38.0
github.com/robfig/cron/v3 v3.0.1
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250604134054-a06406d42247
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250613133518-6987cd48643b
github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.38.0
gopkg.in/dnaeon/go-vcr.v3 v3.2.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250604134054-a06406d42247 h1:wlIvcSpGl3mGDpQmwrZHnYMIlB7Mwx3bhg151LG22Ws=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250604134054-a06406d42247/go.mod h1:qiGzapFyNPFwBBLJ+hTFykKSnU95n1zL64+o1ubmwf0=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250613133518-6987cd48643b h1:7V7T9XzUl+B/6zHeNxyX0DV3OVjCSedA0stHo0BNKZw=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33.0.20250613133518-6987cd48643b/go.mod h1:zFWiHphneiey3s8HOtAEnGrRlWivNaxW5T6d5Xfco7g=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
Expand Down
1 change: 1 addition & 0 deletions internal/acctest/validate_cassettes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func exceptionsCassettesCases() map[string]struct{} {
"../services/secret/testdata/secret-version-type.cassette.yaml": {},
"../services/file/testdata/file-system-invalid-size-granularity-fails.cassette.yaml": {},
"../services/file/testdata/file-system-size-too-small-fails.cassette.yaml": {},
"../services/function/testdata/function-namespace-vpc-integration.cassette.yaml": {},
}
}

Expand Down
21 changes: 21 additions & 0 deletions internal/services/function/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/scaleway/terraform-provider-scaleway/v2/internal/cdf"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/dsf"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
Expand Down Expand Up @@ -170,6 +171,11 @@ func ResourceFunction() *schema.Resource {
Computed: true,
Description: "The native function domain name.",
},
"private_network_id": {
Type: schema.TypeString,
Optional: true,
Description: "ID of the Private Network the container is connected to",
},
"region": regional.Schema(),
"organization_id": account.OrganizationIDSchema(),
"project_id": account.ProjectIDSchema(),
Expand Down Expand Up @@ -214,6 +220,10 @@ func ResourceFunctionCreate(ctx context.Context, d *schema.ResourceData, m any)
req.Timeout = &scw.Duration{Seconds: int64(timeout.(int))}
}

if pnID, ok := d.GetOk("private_network_id"); ok {
req.PrivateNetworkID = types.ExpandStringPtr(locality.ExpandID(pnID.(string)))
}

f, err := api.CreateFunction(req, scw.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
Expand Down Expand Up @@ -324,6 +334,12 @@ func ResourceFunctionRead(ctx context.Context, d *schema.ResourceData, m any) di
_ = d.Set("secret_environment_variables", flattenFunctionSecrets(f.SecretEnvironmentVariables))
_ = d.Set("tags", types.FlattenSliceString(f.Tags))

if f.PrivateNetworkID != nil {
_ = d.Set("private_network_id", regional.NewID(region, types.FlattenStringPtr(f.PrivateNetworkID).(string)).String())
} else {
_ = d.Set("private_network_id", nil)
}

return diags
}

Expand Down Expand Up @@ -415,6 +431,11 @@ func ResourceFunctionUpdate(ctx context.Context, d *schema.ResourceData, m any)
updated = true
}

if d.HasChanges("private_network_id") {
req.PrivateNetworkID = types.ExpandUpdatedStringPtr(locality.ExpandID(d.Get("private_network_id")))
updated = true
}

if updated {
_, err = api.UpdateFunction(req, scw.WithContext(ctx))
if err != nil {
Expand Down
155 changes: 154 additions & 1 deletion internal/services/function/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/function"
vpcchecks "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/vpc/testfuncs"
)

func TestAccFunction_Basic(t *testing.T) {
Expand Down Expand Up @@ -410,6 +411,158 @@ func TestAccFunction_Sandbox(t *testing.T) {
})
}

func TestAccFunction_PrivateNetwork(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckFunctionNamespaceDestroy(tt),
testAccCheckFunctionDestroy(tt),
vpcchecks.CheckPrivateNetworkDestroy(tt),
),
Steps: []resource.TestStep{
{
Config: `
resource scaleway_vpc_private_network pn00 {
name = "test-acc-function-pn-pn00"
}
resource scaleway_vpc_private_network pn01 {
name = "test-acc-function-pn-pn01"
}

resource scaleway_function_namespace main {
activate_vpc_integration = true
}

resource scaleway_function f00 {
name = "test-acc-function-pn-00"
namespace_id = scaleway_function_namespace.main.id
privacy = "private"
runtime = "go123"
handler = "Handle"
sandbox = "v1"
private_network_id = scaleway_vpc_private_network.pn00.id
}
`,
Check: resource.ComposeTestCheckFunc(
testAccCheckFunctionExists(tt, "scaleway_function.f00"),
resource.TestCheckResourceAttr("scaleway_function_namespace.main", "activate_vpc_integration", "true"),
resource.TestCheckResourceAttr("scaleway_function.f00", "sandbox", "v1"),
resource.TestCheckResourceAttrPair("scaleway_function.f00", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
),
},
{
Config: `
resource scaleway_vpc_private_network pn00 {
name = "test-acc-function-pn-pn00"
}
resource scaleway_vpc_private_network pn01 {
name = "test-acc-function-pn-pn01"
}

resource scaleway_function_namespace main {
activate_vpc_integration = true
}

resource scaleway_function f00 {
name = "test-acc-function-pn-f00"
namespace_id = scaleway_function_namespace.main.id
privacy = "private"
runtime = "go123"
handler = "Handle"
sandbox = "v1"
private_network_id = scaleway_vpc_private_network.pn00.id
}

resource scaleway_function f01 {
name = "test-acc-function-pn-f01"
namespace_id = scaleway_function_namespace.main.id
privacy = "private"
runtime = "go123"
handler = "Handle"
sandbox = "v1"
private_network_id = scaleway_vpc_private_network.pn00.id
}

resource scaleway_function f02 {
name = "test-acc-function-pn-f02"
namespace_id = scaleway_function_namespace.main.id
privacy = "private"
runtime = "go123"
handler = "Handle"
sandbox = "v1"
private_network_id = scaleway_vpc_private_network.pn00.id
}
`,
Check: resource.ComposeTestCheckFunc(
testAccCheckFunctionExists(tt, "scaleway_function.f00"),
testAccCheckFunctionExists(tt, "scaleway_function.f01"),
testAccCheckFunctionExists(tt, "scaleway_function.f02"),
resource.TestCheckResourceAttr("scaleway_function.f00", "sandbox", "v1"),
resource.TestCheckResourceAttr("scaleway_function.f01", "sandbox", "v1"),
resource.TestCheckResourceAttr("scaleway_function.f02", "sandbox", "v1"),
resource.TestCheckResourceAttrPair("scaleway_function.f00", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
resource.TestCheckResourceAttrPair("scaleway_function.f01", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
resource.TestCheckResourceAttrPair("scaleway_function.f02", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
),
},
{
Config: `
resource scaleway_vpc_private_network pn00 {
name = "test-acc-function-pn-pn00"
}
resource scaleway_vpc_private_network pn01 {
name = "test-acc-function-pn-pn01"
}

resource scaleway_function_namespace main {
activate_vpc_integration = true
}

resource scaleway_function f00 {
name = "test-acc-function-pn-f00"
namespace_id = scaleway_function_namespace.main.id
privacy = "private"
runtime = "go123"
handler = "Handle"
sandbox = "v1"
}

resource scaleway_function f01 {
name = "test-acc-function-pn-f01"
namespace_id = scaleway_function_namespace.main.id
privacy = "private"
runtime = "go123"
handler = "Handle"
sandbox = "v1"
private_network_id = scaleway_vpc_private_network.pn01.id
}

resource scaleway_function f02 {
name = "test-acc-function-pn-02"
namespace_id = scaleway_function_namespace.main.id
privacy = "private"
runtime = "go123"
handler = "Handle"
sandbox = "v1"
private_network_id = scaleway_vpc_private_network.pn00.id
}
`,
Check: resource.ComposeTestCheckFunc(
testAccCheckFunctionExists(tt, "scaleway_function.f00"),
testAccCheckFunctionExists(tt, "scaleway_function.f01"),
testAccCheckFunctionExists(tt, "scaleway_function.f02"),
resource.TestCheckResourceAttr("scaleway_function.f00", "private_network_id", ""),
resource.TestCheckResourceAttrPair("scaleway_function.f01", "private_network_id", "scaleway_vpc_private_network.pn01", "id"),
resource.TestCheckResourceAttrPair("scaleway_function.f02", "private_network_id", "scaleway_vpc_private_network.pn00", "id"),
),
},
},
})
}

func testAccCheckFunctionExists(tt *acctest.TestTools, n string) resource.TestCheckFunc {
return func(state *terraform.State) error {
rs, ok := state.RootModule().Resources[n]
Expand Down Expand Up @@ -468,7 +621,7 @@ func passwordMatchHash(parent string, key string, password string) resource.Test
return func(state *terraform.State) error {
rs, ok := state.RootModule().Resources[parent]
if !ok {
return fmt.Errorf("resource container not found: %s", parent)
return fmt.Errorf("resource not found: %s", parent)
}

match, err := argon2id.ComparePasswordAndHash(password, rs.Primary.Attributes[key])
Expand Down
12 changes: 12 additions & 0 deletions internal/services/function/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ func ResourceNamespace() *schema.Resource {
Computed: true,
Description: "The ID of the registry namespace",
},
"activate_vpc_integration": {
Type: schema.TypeBool,
ForceNew: true,
Optional: true,
Default: false,
Description: "Activate VPC integration for the namespace",
},
"region": regional.Schema(),
"organization_id": account.OrganizationIDSchema(),
"project_id": account.ProjectIDSchema(),
Expand Down Expand Up @@ -113,6 +120,10 @@ func ResourceFunctionNamespaceCreate(ctx context.Context, d *schema.ResourceData
createReq.Tags = types.ExpandStrings(rawTag)
}

if activateVPC, ok := d.GetOk("activate_vpc_integration"); ok {
createReq.ActivateVpcIntegration = activateVPC.(bool)
}

ns, err := api.CreateNamespace(createReq, scw.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
Expand Down Expand Up @@ -155,6 +166,7 @@ func ResourceFunctionNamespaceRead(ctx context.Context, d *schema.ResourceData,
_ = d.Set("registry_endpoint", ns.RegistryEndpoint)
_ = d.Set("registry_namespace_id", ns.RegistryNamespaceID)
_ = d.Set("secret_environment_variables", flattenFunctionSecrets(ns.SecretEnvironmentVariables))
_ = d.Set("activate_vpc_integration", types.FlattenBoolPtr(ns.VpcIntegrationActivated)) //nolint:staticcheck

return nil
}
Expand Down
Loading
Loading