Skip to content

Commit

Permalink
feat: add coderd_group data source
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanndickson committed Jul 19, 2024
1 parent b3458c7 commit 62ed2be
Show file tree
Hide file tree
Showing 4 changed files with 470 additions and 0 deletions.
44 changes: 44 additions & 0 deletions docs/data-sources/group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "coderd_group Data Source - coderd"
subcategory: ""
description: |-
An existing group on the coder deployment.
---

# coderd_group (Data Source)

An existing group on the coder deployment.



<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- `id` (String) The ID of the group to retrieve. This field will be populated if a name and organization ID is supplied.
- `name` (String) The name of the group to retrieve. This field will be populated if an ID is supplied.
- `organization_id` (String) The organization ID that the group belongs to. This field will be populated if an ID is supplied.

### Read-Only

- `avatar_url` (String)
- `display_name` (String)
- `members` (Attributes Set) Members of the group. (see [below for nested schema](#nestedatt--members))
- `quota_allowance` (Number) The number of quota credits to allocate to each user in the group.
- `source` (String) The source of the group. Either 'oidc' or 'user'.

<a id="nestedatt--members"></a>
### Nested Schema for `members`

Read-Only:

- `created_at` (Number) Unix timestamp of when the member was created.
- `email` (String)
- `id` (String)
- `last_seen_at` (Number) Unix timestamp of when the member was last seen.
- `login_type` (String) The login type of the member. Can be 'oidc', 'token', 'password', 'github' or 'none'.
- `status` (String) The status of the member. Can be 'active', 'dormant' or 'suspended'.
- `theme_preference` (String)
- `username` (String)
225 changes: 225 additions & 0 deletions internal/provider/group_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
package provider

import (
"context"
"fmt"

"github.com/coder/coder/v2/codersdk"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// Ensure provider defined types fully satisfy framework interfaces.
var _ datasource.DataSource = &GroupDataSource{}

func NewGroupDataSource() datasource.DataSource {
return &GroupDataSource{}
}

// GroupDataSource defines the data source implementation.
type GroupDataSource struct {
data *CoderdProviderData
}

// GroupDataSourceModel describes the data source data model.
type GroupDataSourceModel struct {
// ID or name and organization ID must be set
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
OrganizationID types.String `tfsdk:"organization_id"`

DisplayName types.String `tfsdk:"display_name"`
AvatarURL types.String `tfsdk:"avatar_url"`
QuotaAllowance types.Int32 `tfsdk:"quota_allowance"`
Source types.String `tfsdk:"source"`
Members []Member `tfsdk:"members"`
}

type Member struct {
ID types.String `tfsdk:"id"`
Username types.String `tfsdk:"username"`
Email types.String `tfsdk:"email"`
CreatedAt types.Int64 `tfsdk:"created_at"`
LastSeenAt types.Int64 `tfsdk:"last_seen_at"`
Status types.String `tfsdk:"status"`
LoginType types.String `tfsdk:"login_type"`
ThemePreference types.String `tfsdk:"theme_preference"`
}

func (d *GroupDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_group"
}

func (d *GroupDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "An existing group on the coder deployment.",

Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "The ID of the group to retrieve. This field will be populated if a name and organization ID is supplied.",
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.AtLeastOneOf(path.Expressions{
path.MatchRoot("name"),
}...),
},
},
"name": schema.StringAttribute{
MarkdownDescription: "The name of the group to retrieve. This field will be populated if an ID is supplied.",
Optional: true,
Computed: true,
Validators: []validator.String{},
},
"organization_id": schema.StringAttribute{
MarkdownDescription: "The organization ID that the group belongs to. This field will be populated if an ID is supplied. Defaults to the provider default organization ID.",
Optional: true,
Computed: true,
},
"display_name": schema.StringAttribute{
Computed: true,
},
"avatar_url": schema.StringAttribute{
Computed: true,
},
"quota_allowance": schema.Int32Attribute{
MarkdownDescription: "The number of quota credits to allocate to each user in the group.",
Computed: true,
},
"source": schema.StringAttribute{
MarkdownDescription: "The source of the group. Either 'oidc' or 'user'.",
Computed: true,
},
"members": schema.SetNestedAttribute{
MarkdownDescription: "Members of the group.",
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"username": schema.StringAttribute{
Computed: true,
},
"email": schema.StringAttribute{
Computed: true,
},
"created_at": schema.Int64Attribute{
MarkdownDescription: "Unix timestamp of when the member was created.",
Computed: true,
},
"last_seen_at": schema.Int64Attribute{
MarkdownDescription: "Unix timestamp of when the member was last seen.",
Computed: true,
},
"status": schema.StringAttribute{
MarkdownDescription: "The status of the member. Can be 'active', 'dormant' or 'suspended'.",
Computed: true,
},
"login_type": schema.StringAttribute{
MarkdownDescription: "The login type of the member. Can be 'oidc', 'token', 'password', 'github' or 'none'.",
Computed: true,
},
"theme_preference": schema.StringAttribute{
Computed: true,
},
// TODO: Upgrade requested user type if required
},
},
},
},
}
}

func (d *GroupDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

data, ok := req.ProviderData.(*CoderdProviderData)

if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *CoderdProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

return
}

d.data = data
}

func (d *GroupDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data GroupDataSourceModel

// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

client := d.data.Client

if data.OrganizationID.IsNull() {
data.OrganizationID = types.StringValue(d.data.DefaultOrganizationID)
}

var group codersdk.Group
if !data.ID.IsNull() {
groupID, err := uuid.Parse(data.ID.ValueString())
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse supplied group ID as UUID, got error: %s", err))
return
}

group, err = client.Group(ctx, groupID)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get group by ID, got error: %s", err))
return
}
data.Name = types.StringValue(group.Name)
data.OrganizationID = types.StringValue(group.OrganizationID.String())
} else {
orgID, err := uuid.Parse(data.OrganizationID.ValueString())
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse supplied organization ID as UUID, got error: %s", err))
return
}
group, err = client.GroupByOrgAndName(ctx, orgID, data.Name.ValueString())
if err != nil {
resp.Diagnostics.AddError("Failed to get group by name and org ID", err.Error())
return
}
data.ID = types.StringValue(group.ID.String())
}

data.DisplayName = types.StringValue(group.DisplayName)
data.AvatarURL = types.StringValue(group.AvatarURL)
data.QuotaAllowance = types.Int32Value(int32(group.QuotaAllowance))
members := make([]Member, 0, len(group.Members))
for _, member := range group.Members {
members = append(members, Member{
ID: types.StringValue(member.ID.String()),
Username: types.StringValue(member.Username),
Email: types.StringValue(member.Email),
CreatedAt: types.Int64Value(member.CreatedAt.Unix()),
LastSeenAt: types.Int64Value(member.LastSeenAt.Unix()),
Status: types.StringValue(string(member.Status)),
LoginType: types.StringValue(string(member.LoginType)),
ThemePreference: types.StringValue(member.ThemePreference),
})
}
data.Members = members
data.Source = types.StringValue(string(group.Source))

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Loading

0 comments on commit 62ed2be

Please sign in to comment.