chore: backport release changes from main for v1.6.1 (#831)
* Add default permissions for build workflow (#826) Add workflow level read-only permission for build workflow to support new default permissions github policy. * feat(proj-alias): support project scoped alias via suffixes (#824) * feat(proj-alias): support project scoped alias via suffixes * fix: generate resouce docs using make gen * fix: address review comments * chore: make gen changes * fix(proja-alias): validate scope id before calling remove alias suffix * fix: filter unknown scopes in validation * chore: update version and changelog (#828) * chore: update version and changelog * fix: update version number to 1.6.1 * fix: update changelog description for project alias changes --------- Co-authored-by: Susmitha Girumala <susmitha@hashicorp.com>
This commit is contained in:
1 parent
3d90fa4023
commit
7c3959d981
14 files changed
+597
-8
No files matched your search
@@ -24,6 +24,9 @@ on: [workflow_dispatch, push]
|
||||
env:
|
||||
PKG_NAME: "terraform-provider-boundary"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Detects the Go version to use for builds.
|
||||
get-go-version:
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
Canonical reference for changes, improvements, and bugfixes for the Boundary Terraform provider.
|
||||
|
||||
## Next
|
||||
## 1.6.1 (Jul 24th, 2026)
|
||||
|
||||
### New and Improved
|
||||
|
||||
* Adds support to enable set/remove alias suffix and aliases at project scope
|
||||
([PR](https://github.com/hashicorp/terraform-provider-boundary/pull/824))
|
||||
|
||||
## 1.6.0 (Jul 8th, 2026)
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
page_title: "boundary_alias_target Resource - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The target alias resource allows you to configure a Boundary target alias.
|
||||
The target alias resource allows you to configure a Boundary target alias at project and global scopes.
|
||||
---
|
||||
|
||||
# boundary_alias_target (Resource)
|
||||
|
||||
The target alias resource allows you to configure a Boundary target alias.
|
||||
The target alias resource allows you to configure a Boundary target alias at project and global scopes.
|
||||
|
||||
## Example Usage
|
||||
|
||||
@@ -28,6 +28,16 @@ resource "boundary_scope" "project" {
|
||||
auto_create_admin_role = true
|
||||
}
|
||||
|
||||
resource "boundary_scope_alias_suffix" "org" {
|
||||
scope_id = boundary_scope.org.id
|
||||
alias_suffix = "org"
|
||||
}
|
||||
|
||||
resource "boundary_scope_alias_suffix" "project" {
|
||||
scope_id = boundary_scope.project.id
|
||||
alias_suffix = "projectone"
|
||||
}
|
||||
|
||||
resource "boundary_host_catalog_static" "foo" {
|
||||
name = "test"
|
||||
description = "test catalog"
|
||||
@@ -75,6 +85,19 @@ resource "boundary_alias_target" "example_alias_target" {
|
||||
destination_id = boundary_target.foo.id
|
||||
authorize_session_host_id = boundary_host_static.bar.id
|
||||
}
|
||||
|
||||
resource "boundary_alias_target" "example_alias_target_project" {
|
||||
name = "example_alias_target_project"
|
||||
description = "Example project-scoped alias to target foo"
|
||||
scope_id = boundary_scope.project.id
|
||||
value = "prodops.projectone.org"
|
||||
destination_id = boundary_target.foo.id
|
||||
authorize_session_host_id = boundary_host_static.bar.id
|
||||
depends_on = [
|
||||
boundary_scope_alias_suffix.org,
|
||||
boundary_scope_alias_suffix.project,
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
@@ -82,7 +105,7 @@ resource "boundary_alias_target" "example_alias_target" {
|
||||
|
||||
### Required
|
||||
|
||||
- `scope_id` (String) The scope ID.
|
||||
- `scope_id` (String) The scope ID. Org scopes are not supported for aliases.
|
||||
- `value` (String) The value of the alias.
|
||||
|
||||
### Optional
|
||||
@@ -105,4 +128,5 @@ The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/c
|
||||
|
||||
```shell
|
||||
terraform import boundary_alias_target.example_alias_target <my-id>
|
||||
terraform import boundary_alias_target.example_alias_target_project <my-id>
|
||||
```
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "boundary_scope_alias_suffix Resource - terraform-provider-boundary"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The scope alias suffix resource allows you to set an alias suffix for an org or project scope. Global scopes are not valid for alias suffix operations.
|
||||
---
|
||||
|
||||
# boundary_scope_alias_suffix (Resource)
|
||||
|
||||
The scope alias suffix resource allows you to set an alias suffix for an org or project scope. Global scopes are not valid for alias suffix operations.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "boundary_scope" "global" {
|
||||
global_scope = true
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = boundary_scope.global.id
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_scope" "project" {
|
||||
name = "project_one"
|
||||
description = "My first project"
|
||||
scope_id = boundary_scope.org.id
|
||||
auto_create_admin_role = true
|
||||
}
|
||||
|
||||
resource "boundary_scope_alias_suffix" "org_suffix" {
|
||||
scope_id = boundary_scope.org.id
|
||||
alias_suffix = "org"
|
||||
}
|
||||
|
||||
resource "boundary_scope_alias_suffix" "project_suffix" {
|
||||
scope_id = boundary_scope.project.id
|
||||
alias_suffix = "projectone"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `alias_suffix` (String) The alias suffix value for this scope.
|
||||
- `scope_id` (String) The scope ID. Alias suffixes are supported for org and project scopes.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (String) The ID of this resource.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
|
||||
|
||||
```shell
|
||||
terraform import boundary_scope_alias_suffix.org_suffix <scope-id>
|
||||
```
|
||||
@@ -1 +1,2 @@
|
||||
terraform import boundary_alias_target.example_alias_target <my-id>
|
||||
terraform import boundary_alias_target.example_alias_target_project <my-id>
|
||||
@@ -13,6 +13,16 @@ resource "boundary_scope" "project" {
|
||||
auto_create_admin_role = true
|
||||
}
|
||||
|
||||
resource "boundary_scope_alias_suffix" "org" {
|
||||
scope_id = boundary_scope.org.id
|
||||
alias_suffix = "org"
|
||||
}
|
||||
|
||||
resource "boundary_scope_alias_suffix" "project" {
|
||||
scope_id = boundary_scope.project.id
|
||||
alias_suffix = "projectone"
|
||||
}
|
||||
|
||||
resource "boundary_host_catalog_static" "foo" {
|
||||
name = "test"
|
||||
description = "test catalog"
|
||||
@@ -59,4 +69,17 @@ resource "boundary_alias_target" "example_alias_target" {
|
||||
value = "example.bar.foo.boundary"
|
||||
destination_id = boundary_target.foo.id
|
||||
authorize_session_host_id = boundary_host_static.bar.id
|
||||
}
|
||||
}
|
||||
|
||||
resource "boundary_alias_target" "example_alias_target_project" {
|
||||
name = "example_alias_target_project"
|
||||
description = "Example project-scoped alias to target foo"
|
||||
scope_id = boundary_scope.project.id
|
||||
value = "prodops.projectone.org"
|
||||
destination_id = boundary_target.foo.id
|
||||
authorize_session_host_id = boundary_host_static.bar.id
|
||||
depends_on = [
|
||||
boundary_scope_alias_suffix.org,
|
||||
boundary_scope_alias_suffix.project,
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import boundary_scope_alias_suffix.org_suffix <scope-id>
|
||||
@@ -0,0 +1,29 @@
|
||||
resource "boundary_scope" "global" {
|
||||
global_scope = true
|
||||
scope_id = "global"
|
||||
}
|
||||
|
||||
resource "boundary_scope" "org" {
|
||||
name = "organization_one"
|
||||
description = "My first scope!"
|
||||
scope_id = boundary_scope.global.id
|
||||
auto_create_admin_role = true
|
||||
auto_create_default_role = true
|
||||
}
|
||||
|
||||
resource "boundary_scope" "project" {
|
||||
name = "project_one"
|
||||
description = "My first project"
|
||||
scope_id = boundary_scope.org.id
|
||||
auto_create_admin_role = true
|
||||
}
|
||||
|
||||
resource "boundary_scope_alias_suffix" "org_suffix" {
|
||||
scope_id = boundary_scope.org.id
|
||||
alias_suffix = "org"
|
||||
}
|
||||
|
||||
resource "boundary_scope_alias_suffix" "project_suffix" {
|
||||
scope_id = boundary_scope.project.id
|
||||
alias_suffix = "projectone"
|
||||
}
|
||||
@@ -125,6 +125,7 @@ func New() *schema.Provider {
|
||||
"boundary_host_set_static": resourceHostSetStatic(),
|
||||
"boundary_host_set_plugin": resourceHostSetPlugin(),
|
||||
"boundary_policy_storage": resourcePolicyStorage(),
|
||||
"boundary_scope_alias_suffix": resourceScopeAliasSuffix(),
|
||||
"boundary_scope_policy_attachment": resourceScopePolicyAttachment(),
|
||||
"boundary_role": resourceRole(),
|
||||
"boundary_scope": resourceScope(),
|
||||
|
||||
@@ -5,7 +5,9 @@ package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/aliases"
|
||||
@@ -17,11 +19,15 @@ const (
|
||||
aliasTypeTarget = "target"
|
||||
|
||||
aliasTargetAuthorizeSessionHostIdKey = "authorize_session_host_id"
|
||||
|
||||
globalScopeId = "global"
|
||||
orgScopePrefix = "o_"
|
||||
projectScopePrefix = "p_"
|
||||
)
|
||||
|
||||
func resourceAliasTarget() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Description: "The target alias resource allows you to configure a Boundary target alias.",
|
||||
Description: "The target alias resource allows you to configure a Boundary target alias at project and global scopes.",
|
||||
|
||||
CreateContext: resourceTargetAliasCreate,
|
||||
ReadContext: resourceTargetAliasRead,
|
||||
@@ -48,7 +54,7 @@ func resourceAliasTarget() *schema.Resource {
|
||||
Optional: true,
|
||||
},
|
||||
ScopeIdKey: {
|
||||
Description: "The scope ID.",
|
||||
Description: "The scope ID. Org scopes are not supported for aliases.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
@@ -144,6 +150,10 @@ func resourceTargetAliasCreate(ctx context.Context, d *schema.ResourceData, meta
|
||||
return diag.Errorf("no alias value provided")
|
||||
}
|
||||
|
||||
if err := validateTargetAliasScope(scopeId); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
if nameVal, ok := d.GetOk(NameKey); ok {
|
||||
opts = append(opts, aliases.WithName(nameVal.(string)))
|
||||
}
|
||||
@@ -221,7 +231,12 @@ func resourceTargetAliasUpdate(ctx context.Context, d *schema.ResourceData, meta
|
||||
|
||||
if d.HasChange(ValueKey) {
|
||||
if valVal, ok := d.GetOk(ValueKey); ok {
|
||||
opts = append(opts, aliases.WithValue(valVal.(string)))
|
||||
aliasValue := valVal.(string)
|
||||
scopeId := d.Get(ScopeIdKey).(string)
|
||||
if err := validateTargetAliasScope(scopeId); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
opts = append(opts, aliases.WithValue(aliasValue))
|
||||
} else {
|
||||
return diag.Errorf("no value provided")
|
||||
}
|
||||
@@ -267,3 +282,16 @@ func resourceTargetAliasDelete(ctx context.Context, d *schema.ResourceData, meta
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateTargetAliasScope(scopeId string) error {
|
||||
switch {
|
||||
case scopeId == globalScopeId:
|
||||
return nil
|
||||
case strings.HasPrefix(scopeId, projectScopePrefix):
|
||||
return nil
|
||||
case strings.HasPrefix(scopeId, orgScopePrefix):
|
||||
return fmt.Errorf("target aliases are not supported for org scopes; use a project or global scope ID")
|
||||
default:
|
||||
return fmt.Errorf("target aliases are supported only for project and global scopes")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright IBM Corp. 2020, 2026
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidateTargetAliasScope(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
scopeId string
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "org scope rejected",
|
||||
scopeId: "o_1234567890",
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "global scope allowed",
|
||||
scopeId: globalScopeId,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "project scope allowed",
|
||||
scopeId: "p_1234567890",
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "unknown scope type rejected",
|
||||
scopeId: "s_1234567890",
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := validateTargetAliasScope(tt.scopeId)
|
||||
if tt.wantError && err == nil {
|
||||
t.Fatal("expected error but got nil")
|
||||
}
|
||||
if !tt.wantError && err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
// Copyright IBM Corp. 2020, 2026
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/scopes"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
aliasSuffixKey = "alias_suffix"
|
||||
)
|
||||
|
||||
func resourceScopeAliasSuffix() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Description: "The scope alias suffix resource allows you to set an alias suffix for an org or project scope. " +
|
||||
"Global scopes are not valid for alias suffix operations.",
|
||||
|
||||
CreateContext: resourceScopeAliasSuffixCreate,
|
||||
UpdateWithoutTimeout: resourceScopeAliasSuffixUpdate,
|
||||
ReadWithoutTimeout: resourceScopeAliasSuffixRead,
|
||||
DeleteWithoutTimeout: resourceScopeAliasSuffixDelete,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
StateContext: schema.ImportStatePassthroughContext,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
ScopeIdKey: {
|
||||
Description: "The scope ID. Alias suffixes are supported for org and project scopes.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
aliasSuffixKey: {
|
||||
Description: "The alias suffix value for this scope.",
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceScopeAliasSuffixCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
scp := scopes.NewClient(md.client)
|
||||
opts := []scopes.Option{scopes.WithAutomaticVersioning(true)}
|
||||
|
||||
scopeId, err := scopeIdFromAliasSuffixResourceData(d)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
if err := validateAliasSuffixScope(scopeId); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
var aliasSuffix string
|
||||
if aliasSuffixVal, ok := d.GetOk(aliasSuffixKey); ok {
|
||||
aliasSuffix = aliasSuffixVal.(string)
|
||||
} else {
|
||||
return diag.Errorf("no alias suffix provided")
|
||||
}
|
||||
|
||||
if _, err := scp.SetAliasSuffix(ctx, scopeId, 0, aliasSuffix, opts...); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
if err := setScopeAliasSuffixState(d, scopeId, aliasSuffix); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceScopeAliasSuffixRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
scp := scopes.NewClient(md.client)
|
||||
|
||||
scopeId, err := scopeIdFromAliasSuffixResourceData(d)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
s, err := scp.Read(ctx, scopeId)
|
||||
if err != nil {
|
||||
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
|
||||
// the scope is gone, destroy this resource
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return diag.Errorf("error reading scope: %v", err)
|
||||
}
|
||||
if s == nil {
|
||||
return diag.Errorf("scope nil after read")
|
||||
}
|
||||
if s.GetResponse() == nil {
|
||||
return diag.Errorf("scope response nil after read")
|
||||
}
|
||||
|
||||
serverAliasSuffix, err := aliasSuffixFromScopeResponseMap(s.GetResponse().Map)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
if serverAliasSuffix == "" {
|
||||
// no alias suffix is currently set on this scope, destroy this resource
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := setScopeAliasSuffixState(d, scopeId, serverAliasSuffix); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceScopeAliasSuffixUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
scp := scopes.NewClient(md.client)
|
||||
opts := []scopes.Option{scopes.WithAutomaticVersioning(true)}
|
||||
|
||||
scopeId, err := scopeIdFromAliasSuffixResourceData(d)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
if err := validateAliasSuffixScope(scopeId); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
var aliasSuffix string
|
||||
if aliasSuffixVal, ok := d.GetOk(aliasSuffixKey); ok {
|
||||
aliasSuffix = aliasSuffixVal.(string)
|
||||
} else {
|
||||
return diag.Errorf("no alias suffix provided")
|
||||
}
|
||||
|
||||
if _, err := scp.SetAliasSuffix(ctx, scopeId, 0, aliasSuffix, opts...); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
if err := setScopeAliasSuffixState(d, scopeId, aliasSuffix); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceScopeAliasSuffixDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
|
||||
md := meta.(*metaData)
|
||||
scp := scopes.NewClient(md.client)
|
||||
opts := []scopes.Option{scopes.WithAutomaticVersioning(true)}
|
||||
|
||||
scopeId, err := scopeIdFromAliasSuffixResourceData(d)
|
||||
if err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
if err := validateAliasSuffixScope(scopeId); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
if _, err := scp.RemoveAliasSuffix(ctx, scopeId, 0, opts...); err != nil {
|
||||
return diag.FromErr(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func aliasSuffixFromScopeResponseMap(raw map[string]interface{}) (string, error) {
|
||||
if raw == nil {
|
||||
return "", fmt.Errorf("scope response map nil after read")
|
||||
}
|
||||
|
||||
aliasSuffixRaw, ok := raw[aliasSuffixKey]
|
||||
if !ok || aliasSuffixRaw == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
aliasSuffix, ok := aliasSuffixRaw.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("alias suffix in scope response has unexpected type %T", aliasSuffixRaw)
|
||||
}
|
||||
|
||||
return aliasSuffix, nil
|
||||
}
|
||||
|
||||
func setScopeAliasSuffixState(d *schema.ResourceData, scopeId, aliasSuffix string) error {
|
||||
if err := d.Set(ScopeIdKey, scopeId); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.Set(aliasSuffixKey, aliasSuffix); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// One alias suffix may be set per scope, so scope ID is the stable TF ID.
|
||||
d.SetId(scopeId)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAliasSuffixScope(scopeId string) error {
|
||||
switch {
|
||||
case strings.HasPrefix(scopeId, orgScopePrefix):
|
||||
return nil
|
||||
case strings.HasPrefix(scopeId, projectScopePrefix):
|
||||
return nil
|
||||
case scopeId == globalScopeId:
|
||||
return fmt.Errorf("alias suffixes are not supported for the global scope; use an org or project scope ID")
|
||||
default:
|
||||
return fmt.Errorf("alias suffixes are supported only for org and project scopes")
|
||||
}
|
||||
}
|
||||
|
||||
func scopeIdFromAliasSuffixResourceData(d *schema.ResourceData) (string, error) {
|
||||
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
|
||||
scopeId := scopeIdVal.(string)
|
||||
if scopeId != "" {
|
||||
return scopeId, nil
|
||||
}
|
||||
}
|
||||
|
||||
id := d.Id()
|
||||
if id == "" {
|
||||
return "", fmt.Errorf("no scope ID provided")
|
||||
}
|
||||
|
||||
// Backward-compatibility for any prior state that used alias_suffix:scope_id.
|
||||
if index := strings.LastIndex(id, ":"); index >= 0 && index < len(id)-1 {
|
||||
return id[index+1:], nil
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright IBM Corp. 2020, 2026
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package provider
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidateAliasSuffixScope(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
scopeId string
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "global scope rejected",
|
||||
scopeId: globalScopeId,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "org scope allowed",
|
||||
scopeId: "o_1234567890",
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "project scope allowed",
|
||||
scopeId: "p_1234567890",
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "unknown scope type rejected",
|
||||
scopeId: "s_1234567890",
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := validateAliasSuffixScope(tt.scopeId)
|
||||
if tt.wantError && err == nil {
|
||||
t.Fatal("expected error but got nil")
|
||||
}
|
||||
if !tt.wantError && err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAliasSuffixFromScopeResponseMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("nil map", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := aliasSuffixFromScopeResponseMap(nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for nil map")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("missing key", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := aliasSuffixFromScopeResponseMap(map[string]interface{}{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != "" {
|
||||
t.Fatalf("expected empty alias suffix, got %q", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil value", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := aliasSuffixFromScopeResponseMap(map[string]interface{}{aliasSuffixKey: nil})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != "" {
|
||||
t.Fatalf("expected empty alias suffix, got %q", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("wrong type", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := aliasSuffixFromScopeResponseMap(map[string]interface{}{aliasSuffixKey: 123})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for non-string alias suffix")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("string value", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := aliasSuffixFromScopeResponseMap(map[string]interface{}{aliasSuffixKey: "example.boundary"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != "example.boundary" {
|
||||
t.Fatalf("unexpected alias suffix: %q", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.6.0
|
||||
1.6.1
|
||||
Reference in new issue
Block a user