mirror of
https://github.com/Kichiyaki/terraform-provider-woodpecker.git
synced 2024-06-18 18:47:38 +00:00
feat: add user resource (#2)
This commit is contained in:
parent
b881a7ac23
commit
22cc289730
5
Makefile
5
Makefile
|
@ -6,6 +6,7 @@ GOBIN := $(shell go env GOPATH)/bin
|
|||
endif
|
||||
OSARCH=$(shell uname -m)
|
||||
GOLANGCI_LINT_PATH=$(GOBIN)/golangci-lint
|
||||
TFPLUGINDOCS_PATH=$(GOBIN)/tfplugindocs
|
||||
|
||||
.PHONY: install-git-hooks
|
||||
install-git-hooks:
|
||||
|
@ -15,8 +16,8 @@ install-git-hooks:
|
|||
|
||||
.PHONY: install-tfplugindocs
|
||||
install-tfplugindocs:
|
||||
@echo "Installing github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs..."
|
||||
@go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.16.0
|
||||
@echo "Installing github.com/hashicorp/terraform-plugin-docs..."
|
||||
@(test -f $(TFPLUGINDOCS_PATH) && echo "github.com/hashicorp/terraform-plugin-docs is already installed. Skipping...") || (wget -q -O $(TFPLUGINDOCS_PATH).zip https://github.com/hashicorp/terraform-plugin-docs/releases/download/v0.16.0/tfplugindocs_0.16.0_$(GOOS)_$(GOARCH).zip && unzip $(TFPLUGINDOCS_PATH).zip tfplugindocs -d $(GOBIN) && rm $(TFPLUGINDOCS_PATH).zip)
|
||||
|
||||
.PHONY: install-golangci-lint
|
||||
install-golangci-lint:
|
||||
|
|
|
@ -33,8 +33,8 @@ data "woodpecker_user" "user" {
|
|||
|
||||
### Read-Only
|
||||
|
||||
- `active` (Boolean) Whether user is active in the system
|
||||
- `admin` (Boolean) Whether user is an admin
|
||||
- `active` (Boolean) whether user is active in the system
|
||||
- `admin` (Boolean) whether user is an admin
|
||||
- `avatar` (String) the user's avatar URL
|
||||
- `email` (String) the user's email
|
||||
- `id` (Number) the user's id
|
||||
|
|
43
docs/resources/user.md
Normal file
43
docs/resources/user.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "woodpecker_user Resource - terraform-provider-woodpecker"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Provides a user resource.
|
||||
This resource allows you to add/remove users. When applied, a new user will be created. When destroyed, that user will be removed.
|
||||
---
|
||||
|
||||
# woodpecker_user (Resource)
|
||||
|
||||
Provides a user resource.
|
||||
|
||||
|
||||
This resource allows you to add/remove users. When applied, a new user will be created. When destroyed, that user will be removed.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
# Create a user
|
||||
resource "woodpecker_user" "test" {
|
||||
login = "test"
|
||||
email = "test@localhost"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `login` (String) the name of the user
|
||||
|
||||
### Optional
|
||||
|
||||
- `admin` (Boolean) whether user is an admin
|
||||
- `avatar` (String) the user's avatar URL
|
||||
- `email` (String) the email of the user
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `active` (Boolean) whether user is active in the system
|
||||
- `id` (Number) the user's id
|
5
examples/resources/woodpecker_user/resource.tf
Normal file
5
examples/resources/woodpecker_user/resource.tf
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Create a user
|
||||
resource "woodpecker_user" "test" {
|
||||
login = "test"
|
||||
email = "test@localhost"
|
||||
}
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/woodpecker-ci/woodpecker/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
|
@ -15,36 +14,18 @@ type userDataSource struct {
|
|||
}
|
||||
|
||||
var _ datasource.DataSource = (*userDataSource)(nil)
|
||||
var _ datasource.DataSourceWithConfigure = (*userDataSource)(nil)
|
||||
|
||||
func newUserDataSource() datasource.DataSource {
|
||||
return &userDataSource{}
|
||||
}
|
||||
|
||||
type userDataSourceModel struct {
|
||||
ID types.Int64 `tfsdk:"id"`
|
||||
Login types.String `tfsdk:"login"`
|
||||
Email types.String `tfsdk:"email"`
|
||||
Avatar types.String `tfsdk:"avatar"`
|
||||
Active types.Bool `tfsdk:"active"`
|
||||
Admin types.Bool `tfsdk:"admin"`
|
||||
}
|
||||
|
||||
func (m *userDataSourceModel) setValues(user *woodpecker.User) {
|
||||
m.ID = types.Int64Value(user.ID)
|
||||
m.Login = types.StringValue(user.Login)
|
||||
m.Email = types.StringValue(user.Email)
|
||||
m.Avatar = types.StringValue(user.Avatar)
|
||||
m.Active = types.BoolValue(user.Active)
|
||||
m.Admin = types.BoolValue(user.Admin)
|
||||
}
|
||||
|
||||
func (d *userDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_user"
|
||||
}
|
||||
|
||||
func (d *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
// This description is used by the documentation generator and the language server.
|
||||
MarkdownDescription: "Use this data source to retrieve information about a user.",
|
||||
|
||||
Attributes: map[string]schema.Attribute{
|
||||
|
@ -66,17 +47,17 @@ func (d *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r
|
|||
},
|
||||
"active": schema.BoolAttribute{
|
||||
Computed: true,
|
||||
Description: "Whether user is active in the system",
|
||||
Description: "whether user is active in the system",
|
||||
},
|
||||
"admin": schema.BoolAttribute{
|
||||
Computed: true,
|
||||
Description: "Whether user is an admin",
|
||||
Description: "whether user is an admin",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *userDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
func (d *userDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
|
@ -95,9 +76,8 @@ func (d *userDataSource) Configure(ctx context.Context, req datasource.Configure
|
|||
}
|
||||
|
||||
func (d *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data userDataSourceModel
|
||||
var data userModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
@ -117,6 +97,5 @@ func (d *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
|
|||
|
||||
data.setValues(user)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
|
35
internal/models.go
Normal file
35
internal/models.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/woodpecker-ci/woodpecker/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
type userModel struct {
|
||||
ID types.Int64 `tfsdk:"id"`
|
||||
Login types.String `tfsdk:"login"`
|
||||
Email types.String `tfsdk:"email"`
|
||||
Avatar types.String `tfsdk:"avatar"`
|
||||
Active types.Bool `tfsdk:"active"`
|
||||
Admin types.Bool `tfsdk:"admin"`
|
||||
}
|
||||
|
||||
func (m *userModel) setValues(user *woodpecker.User) {
|
||||
m.ID = types.Int64Value(user.ID)
|
||||
m.Login = types.StringValue(user.Login)
|
||||
m.Email = types.StringValue(user.Email)
|
||||
m.Avatar = types.StringValue(user.Avatar)
|
||||
m.Active = types.BoolValue(user.Active)
|
||||
m.Admin = types.BoolValue(user.Admin)
|
||||
}
|
||||
|
||||
func (m *userModel) toWoodpeckerModel() *woodpecker.User {
|
||||
return &woodpecker.User{
|
||||
ID: m.ID.ValueInt64(),
|
||||
Login: m.Login.ValueString(),
|
||||
Email: m.Email.ValueString(),
|
||||
Avatar: m.Avatar.ValueString(),
|
||||
Active: m.Active.ValueBool(),
|
||||
Admin: m.Admin.ValueBool(),
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@ import (
|
|||
|
||||
type woodpeckerProvider struct {
|
||||
version string
|
||||
client woodpecker.Client
|
||||
}
|
||||
|
||||
var _ provider.Provider = (*woodpeckerProvider)(nil)
|
||||
|
@ -60,7 +59,9 @@ func (p *woodpeckerProvider) DataSources(_ context.Context) []func() datasource.
|
|||
}
|
||||
|
||||
func (p *woodpeckerProvider) Resources(_ context.Context) []func() resource.Resource {
|
||||
return []func() resource.Resource{}
|
||||
return []func() resource.Resource{
|
||||
newUserResource,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *woodpeckerProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
|
||||
|
@ -69,10 +70,10 @@ func (p *woodpeckerProvider) Configure(ctx context.Context, req provider.Configu
|
|||
return
|
||||
}
|
||||
|
||||
p.client = newClient(ctx, cfg, resp)
|
||||
client := newClient(ctx, cfg, resp)
|
||||
|
||||
resp.DataSourceData = p.client
|
||||
resp.ResourceData = p.client
|
||||
resp.DataSourceData = client
|
||||
resp.ResourceData = client
|
||||
}
|
||||
|
||||
type providerConfig struct {
|
||||
|
|
163
internal/resource_user.go
Normal file
163
internal/resource_user.go
Normal file
|
@ -0,0 +1,163 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/woodpecker-ci/woodpecker/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
type userResource struct {
|
||||
client woodpecker.Client
|
||||
}
|
||||
|
||||
var _ resource.Resource = (*userResource)(nil)
|
||||
var _ resource.ResourceWithConfigure = (*userResource)(nil)
|
||||
var _ resource.ResourceWithImportState = (*userResource)(nil)
|
||||
|
||||
func newUserResource() resource.Resource {
|
||||
return &userResource{}
|
||||
}
|
||||
|
||||
func (r *userResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_user"
|
||||
}
|
||||
|
||||
func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: `Provides a user resource.
|
||||
|
||||
|
||||
This resource allows you to add/remove users. When applied, a new user will be created. When destroyed, that user will be removed.`,
|
||||
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "the user's id",
|
||||
},
|
||||
"login": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "the name of the user",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"email": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "the email of the user",
|
||||
},
|
||||
"avatar": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "the user's avatar URL",
|
||||
},
|
||||
"active": schema.BoolAttribute{
|
||||
Computed: true,
|
||||
Description: "whether user is active in the system",
|
||||
},
|
||||
"admin": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "whether user is an admin",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *userResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
client, ok := req.ProviderData.(woodpecker.Client)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Data Source Configure Type",
|
||||
fmt.Sprintf("Expected woodpecker.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
r.client = client
|
||||
}
|
||||
|
||||
func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data userModel
|
||||
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := r.client.UserPost(data.toWoodpeckerModel())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Couldn't create user", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
data.setValues(user)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data userModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := r.client.User(data.Login.ValueString())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Couldn't get user", err.Error())
|
||||
}
|
||||
|
||||
data.setValues(user)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data userModel
|
||||
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := r.client.UserPatch(data.toWoodpeckerModel())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Couldn't update user", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
data.setValues(user)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data userModel
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.client.UserDel(data.Login.ValueString()); err != nil {
|
||||
resp.Diagnostics.AddError("Couldn't delete user", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *userResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("login"), req.ID)...)
|
||||
}
|
93
internal/resource_user_test.go
Normal file
93
internal/resource_user_test.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package internal_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/plancheck"
|
||||
)
|
||||
|
||||
func TestResourceUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
login := uuid.NewString()
|
||||
newLogin := uuid.NewString()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(`
|
||||
resource "woodpecker_user" "test_user" {
|
||||
login = "%s"
|
||||
}
|
||||
`, login),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "login", login),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "email", ""),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "avatar", ""),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "active", "false"),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "admin", "false"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: fmt.Sprintf(`
|
||||
resource "woodpecker_user" "test_user" {
|
||||
login = "%s"
|
||||
email = "%s@localhost"
|
||||
avatar = "http://localhost/%s"
|
||||
admin = true
|
||||
}
|
||||
`, login, login, login),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "login", login),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "email", login+"@localhost"),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "avatar", "http://localhost/"+login),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "active", "false"),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "admin", "true"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: fmt.Sprintf(`
|
||||
resource "woodpecker_user" "test_user" {
|
||||
login = "%s"
|
||||
}
|
||||
`, login),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "login", login),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "email", login+"@localhost"),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "avatar", "http://localhost/"+login),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "active", "false"),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "admin", "true"),
|
||||
),
|
||||
},
|
||||
{
|
||||
ResourceName: "woodpecker_user.test_user",
|
||||
ImportState: true,
|
||||
ImportStateId: login,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
{
|
||||
Config: fmt.Sprintf(`
|
||||
resource "woodpecker_user" "test_user" {
|
||||
login = "%s"
|
||||
}
|
||||
`, newLogin),
|
||||
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||
PreApply: []plancheck.PlanCheck{
|
||||
plancheck.ExpectResourceAction("woodpecker_user.test_user", plancheck.ResourceActionReplace),
|
||||
},
|
||||
},
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "login", newLogin),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "email", ""),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "avatar", ""),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "active", "false"),
|
||||
resource.TestCheckResourceAttr("woodpecker_user.test_user", "admin", "false"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user