Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/go_modules/google.golang.org/prot…
Browse files Browse the repository at this point in the history
…obuf-1.35.2
  • Loading branch information
briankassouf authored Nov 15, 2024
2 parents 773acf9 + 9845961 commit 8964705
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 44 deletions.
3 changes: 2 additions & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
* @temporalio/infra
* @temporalio/saas
* @jlacefie
2 changes: 1 addition & 1 deletion docs/resources/namespace.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ resource "temporalcloud_namespace" "terraform3" {

- `accepted_client_ca` (String) The Base64-encoded CA cert in PEM format that clients use when authenticating with Temporal Cloud. This is a required field when a Namespace uses mTLS authentication.
- `api_key_auth` (Boolean) If true, Temporal Cloud will use API key authentication for this namespace. If false, mutual TLS (mTLS) authentication will be used.
- `certificate_filters` (Attributes List) A list of filters to apply to client certificates when initiating a connection Temporal Cloud. If present, connections will only be allowed from client certificates whose distinguished name properties match at least one of the filters. (see [below for nested schema](#nestedatt--certificate_filters))
- `certificate_filters` (Attributes List) A list of filters to apply to client certificates when initiating a connection Temporal Cloud. If present, connections will only be allowed from client certificates whose distinguished name properties match at least one of the filters. Empty lists are not allowed, omit the attribute instead. (see [below for nested schema](#nestedatt--certificate_filters))
- `codec_server` (Attributes) A codec server is used by the Temporal Cloud UI to decode payloads for all users interacting with this namespace, even if the workflow history itself is encrypted. (see [below for nested schema](#nestedatt--codec_server))
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))

Expand Down
4 changes: 2 additions & 2 deletions docs/resources/service_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ resource "temporalcloud_service_account" "namespace_admin" {

### Required

- `account_access` (String) The role on the account. Must be one of [admin, developer, read] (case-insensitive)
- `account_access` (String) The role on the account. Must be one of [admin, developer, read] (case-insensitive).
- `name` (String) The name associated with the service account.

### Optional

- `namespace_accesses` (Attributes List) The list of namespace accesses. (see [below for nested schema](#nestedatt--namespace_accesses))
- `namespace_accesses` (Attributes List) The list of namespace accesses. Empty lists are not allowed, omit the attribute instead. Service Accounts with an account_access role of admin cannot be assigned explicit permissions to namespaces. admins implicitly receive access to all Namespaces. (see [below for nested schema](#nestedatt--namespace_accesses))
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))

### Read-Only
Expand Down
4 changes: 2 additions & 2 deletions docs/resources/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ resource "temporalcloud_user" "namespace_admin" {

### Required

- `account_access` (String) The role on the account. Must be one of [owner, admin, developer, read] (case-insensitive). owner is only valid for import.
- `account_access` (String) The role on the account. Must be one of [owner, admin, developer, read] (case-insensitive). owner is only valid for import and cannot be created, updated or deleted without Temporal support.
- `email` (String) The email address for the user.

### Optional

- `namespace_accesses` (Attributes List) The list of namespace accesses. (see [below for nested schema](#nestedatt--namespace_accesses))
- `namespace_accesses` (Attributes List) The list of namespace accesses. Empty lists are not allowed, omit the attribute instead. Users with account_access roles of owner or admin cannot be assigned explicit permissions to namespaces. They implicitly receive access to all Namespaces. (see [below for nested schema](#nestedatt--namespace_accesses))
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))

### Read-Only
Expand Down
98 changes: 70 additions & 28 deletions internal/provider/namespace_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import (
"fmt"
"time"

"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"

"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
Expand Down Expand Up @@ -181,7 +184,7 @@ func (r *namespaceResource) Schema(ctx context.Context, _ resource.SchemaRequest
Required: true,
},
"certificate_filters": schema.ListNestedAttribute{
Description: "A list of filters to apply to client certificates when initiating a connection Temporal Cloud. If present, connections will only be allowed from client certificates whose distinguished name properties match at least one of the filters.",
Description: "A list of filters to apply to client certificates when initiating a connection Temporal Cloud. If present, connections will only be allowed from client certificates whose distinguished name properties match at least one of the filters. Empty lists are not allowed, omit the attribute instead.",
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
Expand All @@ -203,6 +206,9 @@ func (r *namespaceResource) Schema(ctx context.Context, _ resource.SchemaRequest
},
},
},
Validators: []validator.List{
listvalidator.SizeAtLeast(1),
},
},
"api_key_auth": schema.BoolAttribute{
Description: "If true, Temporal Cloud will use API key authentication for this namespace. If false, mutual TLS (mTLS) authentication will be used.",
Expand Down Expand Up @@ -276,17 +282,21 @@ func (r *namespaceResource) Create(ctx context.Context, req resource.CreateReque
ctx, cancel := context.WithTimeout(ctx, createTimeout)
defer cancel()

regions := getRegionsFromModel(ctx, resp.Diagnostics, &plan)
regions, d := getRegionsFromModel(ctx, &plan)
resp.Diagnostics.Append(d...)
if resp.Diagnostics.HasError() {
return
}
certFilters := getCertFiltersFromModel(ctx, resp.Diagnostics, &plan)
certFilters, d := getCertFiltersFromModel(ctx, &plan)
resp.Diagnostics.Append(d...)
if resp.Diagnostics.HasError() {
return
}
var codecServer *namespacev1.CodecServerSpec
if !plan.CodecServer.IsNull() {
codecServer = getCodecServerFromModel(ctx, resp.Diagnostics, &plan)
var d diag.Diagnostics
codecServer, d = getCodecServerFromModel(ctx, &plan)
resp.Diagnostics.Append(d...)
if resp.Diagnostics.HasError() {
return
}
Expand Down Expand Up @@ -348,7 +358,11 @@ func (r *namespaceResource) Create(ctx context.Context, req resource.CreateReque
return
}

updateModelFromSpec(ctx, resp.Diagnostics, &plan, ns.Namespace)
resp.Diagnostics.Append(updateModelFromSpec(ctx, &plan, ns.Namespace)...)
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
}

Expand All @@ -368,7 +382,11 @@ func (r *namespaceResource) Read(ctx context.Context, req resource.ReadRequest,
return
}

updateModelFromSpec(ctx, resp.Diagnostics, &state, model.Namespace)
resp.Diagnostics.Append(updateModelFromSpec(ctx, &state, model.Namespace)...)
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, state)...)
}

Expand All @@ -380,11 +398,13 @@ func (r *namespaceResource) Update(ctx context.Context, req resource.UpdateReque
return
}

regions := getRegionsFromModel(ctx, resp.Diagnostics, &plan)
regions, d := getRegionsFromModel(ctx, &plan)
resp.Diagnostics.Append(d...)
if resp.Diagnostics.HasError() {
return
}
certFilters := getCertFiltersFromModel(ctx, resp.Diagnostics, &plan)
certFilters, d := getCertFiltersFromModel(ctx, &plan)
resp.Diagnostics.Append(d...)
if resp.Diagnostics.HasError() {
return
}
Expand All @@ -396,9 +416,15 @@ func (r *namespaceResource) Update(ctx context.Context, req resource.UpdateReque
resp.Diagnostics.AddError("Failed to get current namespace status", err.Error())
return
}
codecServer := getCodecServerFromModel(ctx, resp.Diagnostics, &plan)
if resp.Diagnostics.HasError() {
return

var codecServer *namespacev1.CodecServerSpec
if !plan.CodecServer.IsNull() {
var d diag.Diagnostics
codecServer, d = getCodecServerFromModel(ctx, &plan)
resp.Diagnostics.Append(d...)
if resp.Diagnostics.HasError() {
return
}
}

var spec = &namespacev1.NamespaceSpec{
Expand Down Expand Up @@ -460,7 +486,11 @@ func (r *namespaceResource) Update(ctx context.Context, req resource.UpdateReque
return
}

updateModelFromSpec(ctx, resp.Diagnostics, &plan, ns.Namespace)
resp.Diagnostics.Append(updateModelFromSpec(ctx, &plan, ns.Namespace)...)
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
}

Expand Down Expand Up @@ -505,28 +535,31 @@ func (r *namespaceResource) ImportState(ctx context.Context, req resource.Import
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

func getRegionsFromModel(ctx context.Context, diags diag.Diagnostics, plan *namespaceResourceModel) []string {
func getRegionsFromModel(ctx context.Context, plan *namespaceResourceModel) ([]string, diag.Diagnostics) {
var diags diag.Diagnostics
regions := make([]types.String, 0, len(plan.Regions.Elements()))
diags.Append(plan.Regions.ElementsAs(ctx, &regions, false)...)
if diags.HasError() {
return nil
return nil, diags
}

requestRegions := make([]string, len(regions))
for i, region := range regions {
requestRegions[i] = region.ValueString()
}

return requestRegions
return requestRegions, diags
}

func updateModelFromSpec(ctx context.Context, diags diag.Diagnostics, state *namespaceResourceModel, ns *namespacev1.Namespace) {
func updateModelFromSpec(ctx context.Context, state *namespaceResourceModel, ns *namespacev1.Namespace) diag.Diagnostics {
var diags diag.Diagnostics

state.ID = types.StringValue(ns.GetNamespace())
state.Name = types.StringValue(ns.GetSpec().GetName())
planRegions, listDiags := types.ListValueFrom(ctx, types.StringType, ns.GetSpec().GetRegions())
diags.Append(listDiags...)
if diags.HasError() {
return
return diags
}

certificateFilter := types.ListNull(types.ObjectType{AttrTypes: namespaceCertificateFilterAttrs})
Expand All @@ -542,15 +575,15 @@ func updateModelFromSpec(ctx context.Context, diags diag.Diagnostics, state *nam
obj, diag := types.ObjectValueFrom(ctx, namespaceCertificateFilterAttrs, model)
diags.Append(diag...)
if diags.HasError() {
return
return diags
}
certificateFilterObjects[i] = obj
}

filters, diag := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: namespaceCertificateFilterAttrs}, certificateFilterObjects)
diags.Append(diag...)
if diags.HasError() {
return
return diags
}

certificateFilter = filters
Expand Down Expand Up @@ -579,6 +612,10 @@ func updateModelFromSpec(ctx context.Context, diags diag.Diagnostics, state *nam

state, objectDiags := types.ObjectValueFrom(ctx, codecServerAttrs, codecServer)
diags.Append(objectDiags...)
if diags.HasError() {
return diags
}

codecServerState = state
} else {
codecServerState = types.ObjectNull(codecServerAttrs)
Expand All @@ -593,32 +630,36 @@ func updateModelFromSpec(ctx context.Context, diags diag.Diagnostics, state *nam
endpointsState, objectDiags := types.ObjectValueFrom(ctx, endpointsAttrs, endpoints)
diags.Append(objectDiags...)
if diags.HasError() {
return
return diags
}

state.Endpoints = endpointsState
state.Regions = planRegions
state.CertificateFilters = certificateFilter
state.RetentionDays = types.Int64Value(int64(ns.GetSpec().GetRetentionDays()))

return diags
}

func getCertFiltersFromModel(ctx context.Context, diags diag.Diagnostics, model *namespaceResourceModel) []*namespacev1.CertificateFilterSpec {
func getCertFiltersFromModel(ctx context.Context, model *namespaceResourceModel) ([]*namespacev1.CertificateFilterSpec, diag.Diagnostics) {
var diags diag.Diagnostics

elements := make([]types.Object, 0, len(model.CertificateFilters.Elements()))
diags.Append(model.CertificateFilters.ElementsAs(ctx, &elements, false)...)
if diags.HasError() {
return nil
return nil, diags
}

if len(elements) == 0 {
return nil
return nil, diags
}

certificateFilters := make([]*namespacev1.CertificateFilterSpec, len(elements))
for i, filter := range elements {
var model namespaceCertificateFilterModel
diags.Append(filter.As(ctx, &model, basetypes.ObjectAsOptions{})...)
if diags.HasError() {
return nil
return nil, diags
}

certificateFilters[i] = &namespacev1.CertificateFilterSpec{
Expand All @@ -629,20 +670,21 @@ func getCertFiltersFromModel(ctx context.Context, diags diag.Diagnostics, model
}
}

return certificateFilters
return certificateFilters, diags
}

func getCodecServerFromModel(ctx context.Context, diags diag.Diagnostics, model *namespaceResourceModel) *namespacev1.CodecServerSpec {
func getCodecServerFromModel(ctx context.Context, model *namespaceResourceModel) (*namespacev1.CodecServerSpec, diag.Diagnostics) {
var diags diag.Diagnostics
var codecServer codecServerModel
diags.Append(model.CodecServer.As(ctx, &codecServer, basetypes.ObjectAsOptions{})...)
if diags.HasError() {
return nil
return nil, diags
}
return &namespacev1.CodecServerSpec{
Endpoint: codecServer.Endpoint.ValueString(),
PassAccessToken: codecServer.PassAccessToken.ValueBool(),
IncludeCrossOriginCredentials: codecServer.IncludeCrossOriginCredentials.ValueBool(),
}
}, diags
}

func stringOrNull(s string) types.String {
Expand Down
23 changes: 19 additions & 4 deletions internal/provider/namespace_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"math/rand"
"os"
"regexp"
"testing"
"text/template"
"time"
Expand Down Expand Up @@ -164,10 +165,11 @@ func TestAccNamespaceWithCodecServer(t *testing.T) {
}

configArgs struct {
Name string
RetentionDays int
CodecServer *codecServer
ApiKeyAuth bool
Name string
RetentionDays int
CodecServer *codecServer
ApiKeyAuth bool
CertFiltersEmptyList bool
}
)

Expand Down Expand Up @@ -205,6 +207,10 @@ PEM
retention_days = {{ .RetentionDays }}
{{ if .CertFiltersEmptyList }}
certificate_filters = []
{{ end }}
{{ with .CodecServer }}
codec_server = {
endpoint = "{{ .Endpoint }}"
Expand Down Expand Up @@ -236,6 +242,15 @@ PEM
RetentionDays: 7,
}),
},
{
// Error on empty cert filers
Config: config(configArgs{
Name: name,
RetentionDays: 7,
CertFiltersEmptyList: true,
}),
ExpectError: regexp.MustCompile("certificate_filters list must contain at least 1 elements"),
},
{
Config: config(configArgs{
Name: name,
Expand Down
10 changes: 7 additions & 3 deletions internal/provider/service_account_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"

"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
Expand Down Expand Up @@ -109,14 +111,14 @@ func (r *serviceAccountResource) Schema(ctx context.Context, _ resource.SchemaRe
},
"account_access": schema.StringAttribute{
CustomType: internaltypes.CaseInsensitiveStringType{},
Description: "The role on the account. Must be one of [admin, developer, read] (case-insensitive)",
Description: "The role on the account. Must be one of [admin, developer, read] (case-insensitive).",
Required: true,
Validators: []validator.String{
stringvalidator.OneOfCaseInsensitive("admin", "developer", "read"),
},
},
"namespace_accesses": schema.ListNestedAttribute{
Description: "The list of namespace accesses.",
Description: "The list of namespace accesses. Empty lists are not allowed, omit the attribute instead. Service Accounts with an account_access role of admin cannot be assigned explicit permissions to namespaces. admins implicitly receive access to all Namespaces.",
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
Expand All @@ -134,6 +136,9 @@ func (r *serviceAccountResource) Schema(ctx context.Context, _ resource.SchemaRe
},
},
},
Validators: []validator.List{
listvalidator.SizeAtLeast(1),
},
},
},
Blocks: map[string]schema.Block{
Expand Down Expand Up @@ -182,7 +187,6 @@ func (r *serviceAccountResource) Create(ctx context.Context, req resource.Create
},
},
})

if err != nil {
resp.Diagnostics.AddError("Failed to create Service Account", err.Error())
return
Expand Down
Loading

0 comments on commit 8964705

Please sign in to comment.