Skip to content

Commit

Permalink
so close...
Browse files Browse the repository at this point in the history
  • Loading branch information
aslilac committed Nov 26, 2024
1 parent 3bf1734 commit a185262
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 48 deletions.
159 changes: 119 additions & 40 deletions internal/provider/organization_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (
"github.com/coder/coder/v2/codersdk"
"github.com/coder/terraform-provider-coderd/internal/codersdkvalidator"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
Expand All @@ -17,6 +20,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

Expand All @@ -41,6 +45,18 @@ type OrganizationResourceModel struct {
RoleSync types.Object `tfsdk:"role_sync"`
}

type GroupSyncModel struct {
Field types.String `tfsdk:"field"`
RegexFilter types.String `tfsdk:"regex_filter"`
AutoCreateMissing types.Bool `tfsdk:"auto_create_missing"`
Mapping types.Map `tfsdk:"mapping"`
}

type RoleSyncModel struct {
Field types.String `tfsdk:"field"`
Mapping types.Map `tfsdk:"mapping"`
}

func NewOrganizationResource() resource.Resource {
return &OrganizationResource{}
}
Expand Down Expand Up @@ -101,7 +117,7 @@ func (r *OrganizationResource) Schema(ctx context.Context, req resource.SchemaRe
stringvalidator.LengthAtLeast(1),
},
},
"regex": schema.StringAttribute{
"regex_filter": schema.StringAttribute{
Optional: true,
MarkdownDescription: "A regular expression that will be used to " +
"filter the groups returned by the OIDC provider. Any group " +
Expand All @@ -116,9 +132,12 @@ func (r *OrganizationResource) Schema(ctx context.Context, req resource.SchemaRe
"they are missing.",
},
"mapping": schema.MapAttribute{
ElementType: UUIDType,
ElementType: types.ListType{ElemType: UUIDType},
Optional: true,
MarkdownDescription: "A map from OIDC group name to Coder group ID.",
Validators: []validator.Map{
mapvalidator.ValueListsAre(listvalidator.ValueStringsAre(stringvalidator.Any())),
},
},
},
},
Expand All @@ -133,10 +152,13 @@ func (r *OrganizationResource) Schema(ctx context.Context, req resource.SchemaRe
},
},
"mapping": schema.MapAttribute{
ElementType: UUIDType,
ElementType: types.ListType{ElemType: UUIDType},
Optional: true,
MarkdownDescription: "A map from OIDC group name to Coder " +
"organization role.",
Validators: []validator.Map{
mapvalidator.ValueListsAre(listvalidator.ValueStringsAre(stringvalidator.Any())),
},
},
},
},
Expand Down Expand Up @@ -191,6 +213,26 @@ func (r *OrganizationResource) Read(ctx context.Context, req resource.ReadReques
}
}

if !data.GroupSync.IsNull() {
_, err := r.Client.GroupIDPSyncSettings(ctx, data.ID.ValueUUID().String())
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("unable to get organization group sync settings, got error: %s", err))
return
}

// data.GroupSync = ???
}

if !data.RoleSync.IsNull() {
_, err := r.Client.RoleIDPSyncSettings(ctx, data.ID.ValueUUID().String())
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("unable to get organization role sync settings, got error: %s", err))
return
}

// data.RoleSync = ???
}

// We've fetched the organization ID from state, and the latest values for
// everything else from the backend. Ensure that any mutable data is synced
// with the backend.
Expand Down Expand Up @@ -241,6 +283,21 @@ func (r *OrganizationResource) Create(ctx context.Context, req resource.CreateRe
// default it.
data.DisplayName = types.StringValue(org.DisplayName)

// Now apply group and role sync settings, if specified
orgID := data.ID.ValueUUID()
tflog.Trace(ctx, "updating group sync", map[string]any{
"orgID": orgID,
})
if !data.GroupSync.IsNull() {
r.patchGroupSync(ctx, orgID, data.GroupSync)
}
tflog.Trace(ctx, "updating role sync", map[string]any{
"orgID": orgID,
})
if !data.RoleSync.IsNull() {
r.patchRoleSync(ctx, orgID, data.RoleSync)
}

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Expand Down Expand Up @@ -282,12 +339,17 @@ func (r *OrganizationResource) Update(ctx context.Context, req resource.UpdateRe
"icon": org.Icon,
})

if data.GroupSync.IsNull() {
err = r.patchGroupSync(ctx, orgID, data.GroupSync)
if err != nil {
resp.Diagnostics.AddError("Group Sync Update error", err.Error())
return
}
tflog.Trace(ctx, "updating group sync", map[string]any{
"orgID": orgID,
})
if !data.GroupSync.IsNull() {
r.patchGroupSync(ctx, orgID, data.GroupSync)
}
tflog.Trace(ctx, "updating role sync", map[string]any{
"orgID": orgID,
})
if !data.RoleSync.IsNull() {
r.patchRoleSync(ctx, orgID, data.RoleSync)
}

// Save updated data into Terraform state
Expand Down Expand Up @@ -331,48 +393,65 @@ func (r *OrganizationResource) ImportState(ctx context.Context, req resource.Imp
func (r *OrganizationResource) patchGroupSync(
ctx context.Context,
orgID uuid.UUID,
groupSyncAttr types.Object,
) error {
var settings codersdk.GroupSyncSettings
groupSyncObject types.Object,
) diag.Diagnostics {

Check failure on line 397 in internal/provider/organization_resource.go

View workflow job for this annotation

GitHub Actions / Build

(*OrganizationResource).patchGroupSync - result 0 (github.com/hashicorp/terraform-plugin-framework/diag.Diagnostics) is never used (unparam)
var diags diag.Diagnostics

field, ok := groupSyncAttr.Attributes()["field"].(types.String)
if !ok {
return fmt.Errorf("oh jeez")
// Read values from Terraform
var groupSyncData GroupSyncModel
diags.Append(groupSyncObject.As(ctx, &groupSyncData, basetypes.ObjectAsOptions{})...)
if diags.HasError() {
return diags
}
settings.Field = field.ValueString()

mappingMap, ok := groupSyncAttr.Attributes()["mapping"].(types.Map)
if !ok {
return fmt.Errorf("oh jeez")
}
var mapping map[string][]uuid.UUID
diags := mappingMap.ElementsAs(ctx, mapping, false)
// Convert that into the type used to send the PATCH to the backend
var groupSync codersdk.GroupSyncSettings
groupSync.Field = groupSyncData.Field.ValueString()
groupSync.RegexFilter = regexp.MustCompile(groupSyncData.RegexFilter.ValueString())
groupSync.AutoCreateMissing = groupSyncData.AutoCreateMissing.ValueBool()
diags.Append(groupSyncData.Mapping.ElementsAs(ctx, &groupSync.Mapping, false)...)
if diags.HasError() {
return fmt.Errorf("oh jeez")
return diags
}
settings.Mapping = mapping

regexFilterStr, ok := groupSyncAttr.Attributes()["regex_filter"].(types.String)
if !ok {
return fmt.Errorf("oh jeez")
}
regexFilter, err := regexp.Compile(regexFilterStr.ValueString())
// Perform the PATCH
_, err := r.Client.PatchGroupIDPSyncSettings(ctx, orgID.String(), groupSync)
if err != nil {
return err
diags.AddError("Group Sync Update error", err.Error())
return diags
}
settings.RegexFilter = regexFilter

legacyMappingMap, ok := groupSyncAttr.Attributes()["legacy_group_name_mapping"].(types.Map)
if !ok {
return fmt.Errorf("oh jeez")
return diags
}

func (r *OrganizationResource) patchRoleSync(
ctx context.Context,
orgID uuid.UUID,
roleSyncObject types.Object,
) diag.Diagnostics {

Check failure on line 431 in internal/provider/organization_resource.go

View workflow job for this annotation

GitHub Actions / Build

(*OrganizationResource).patchRoleSync - result 0 (github.com/hashicorp/terraform-plugin-framework/diag.Diagnostics) is never used (unparam)
var diags diag.Diagnostics

// Read values from Terraform
var roleSyncData RoleSyncModel
diags.Append(roleSyncObject.As(ctx, &roleSyncData, basetypes.ObjectAsOptions{})...)
if diags.HasError() {
return diags
}
var legacyMapping map[string]string
diags = legacyMappingMap.ElementsAs(ctx, legacyMapping, false)

// Convert that into the type used to send the PATCH to the backend
var roleSync codersdk.RoleSyncSettings
roleSync.Field = roleSyncData.Field.ValueString()
diags.Append(roleSyncData.Mapping.ElementsAs(ctx, &roleSync.Mapping, false)...)
if diags.HasError() {
return fmt.Errorf("oh jeez")
return diags
}

// Perform the PATCH
_, err := r.Client.PatchRoleIDPSyncSettings(ctx, orgID.String(), roleSync)
if err != nil {
diags.AddError("Role Sync Update error", err.Error())
return diags
}
settings.LegacyNameMapping = legacyMapping

_, err = r.Client.PatchGroupIDPSyncSettings(ctx, orgID.String(), settings)
return err
return diags
}
21 changes: 13 additions & 8 deletions internal/provider/organization_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package provider

import (
"context"
"fmt"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -42,22 +41,20 @@ func TestAccOrganizationResource(t *testing.T) {
cfg2.Name = ptr.Ref("example-org-new")
cfg2.DisplayName = ptr.Ref("Example Organization New")

cfg3 := cfg1
cfg3 := cfg2
cfg3.GroupSync = ptr.Ref(codersdk.GroupSyncSettings{
Field: "wibble",
Mapping: map[string][]uuid.UUID{
"wibble": {uuid.MustParse("6e57187f-6543-46ab-a62c-a10065dd4314")},
},
})
cfg3.RoleSync = ptr.Ref(codersdk.RoleSyncSettings{
Field: "wibble",
Field: "wobble",
Mapping: map[string][]string{
"wibble": {"wobble"},
"wobble": {"wobbly"},
},
})

fmt.Println(cfg3)

t.Run("CreateImportUpdateReadOk", func(t *testing.T) {
resource.Test(t, resource.TestCase{
IsUnitTest: true,
Expand Down Expand Up @@ -89,6 +86,14 @@ func TestAccOrganizationResource(t *testing.T) {
statecheck.ExpectKnownValue("coderd_organization.test", tfjsonpath.New("display_name"), knownvalue.StringExact("Example Organization New")),
},
},
// Add group and role sync
{
Config: cfg3.String(t),
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("coderd_organization.test", tfjsonpath.New("group_sync.field"), knownvalue.StringExact("wibble")),
statecheck.ExpectKnownValue("coderd_organization.test", tfjsonpath.New("role_sync.field"), knownvalue.StringExact("wobble")),
},
},
},
})
})
Expand Down Expand Up @@ -126,7 +131,7 @@ resource "coderd_organization" "test" {
field = "{{.GroupSync.Field}}"
mapping = {
{{- range $key, $value := .GroupSync.Mapping}}
{{$key}} = "{{$value}}"
{{$key}} = {{printf "%q" $value}}
{{- end}}
}
}
Expand All @@ -137,7 +142,7 @@ resource "coderd_organization" "test" {
field = "{{.RoleSync.Field}}"
mapping = {
{{- range $key, $value := .RoleSync.Mapping}}
{{$key}} = "{{$value}}"
{{$key}} = {{printf "%q" $value}}
{{- end}}
}
}
Expand Down

0 comments on commit a185262

Please sign in to comment.