Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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