From 860baf99576b760e96f4ddba50dc77ad30ab470a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Wysoki=C5=84ski?= Date: Mon, 28 Aug 2023 08:38:02 +0200 Subject: [PATCH] feat: add secret data source (#4) --- docs/data-sources/secret.md | 33 +++++++ .../woodpecker_secret/data-source.tf | 3 + internal/data_source_secret.go | 96 +++++++++++++++++++ internal/data_source_secret_test.go | 41 ++++++++ internal/models.go | 29 +++++- internal/provider.go | 1 + internal/resource_secret.go | 8 +- 7 files changed, 204 insertions(+), 7 deletions(-) create mode 100644 docs/data-sources/secret.md create mode 100644 examples/data-sources/woodpecker_secret/data-source.tf create mode 100644 internal/data_source_secret.go create mode 100644 internal/data_source_secret_test.go diff --git a/docs/data-sources/secret.md b/docs/data-sources/secret.md new file mode 100644 index 0000000..a65cda5 --- /dev/null +++ b/docs/data-sources/secret.md @@ -0,0 +1,33 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "woodpecker_secret Data Source - terraform-provider-woodpecker" +subcategory: "" +description: |- + Use this data source to retrieve information about a global secret. +--- + +# woodpecker_secret (Data Source) + +Use this data source to retrieve information about a global secret. + +## Example Usage + +```terraform +data "woodpecker_secret" "test_secret" { + name = "test" +} +``` + + +## Schema + +### Required + +- `name` (String) the name of the secret + +### Read-Only + +- `events` (Set of String) events for which the secret is available (push, tag, pull_request, deployment, cron, manual) +- `id` (Number) the secret's id +- `images` (Set of String) list of Docker images for which this secret is available +- `plugins_only` (Boolean) whether secret is only available for [plugins](https://woodpecker-ci.org/docs/usage/plugins/plugins) diff --git a/examples/data-sources/woodpecker_secret/data-source.tf b/examples/data-sources/woodpecker_secret/data-source.tf new file mode 100644 index 0000000..1dfae5e --- /dev/null +++ b/examples/data-sources/woodpecker_secret/data-source.tf @@ -0,0 +1,3 @@ +data "woodpecker_secret" "test_secret" { + name = "test" +} diff --git a/internal/data_source_secret.go b/internal/data_source_secret.go new file mode 100644 index 0000000..2774217 --- /dev/null +++ b/internal/data_source_secret.go @@ -0,0 +1,96 @@ +package internal + +import ( + "context" + "fmt" + + "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" +) + +type secretDataSource struct { + client woodpecker.Client +} + +var _ datasource.DataSource = (*secretDataSource)(nil) +var _ datasource.DataSourceWithConfigure = (*secretDataSource)(nil) + +func newSecretDataSource() datasource.DataSource { + return &secretDataSource{} +} + +func (d *secretDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_secret" +} + +func (d *secretDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Use this data source to retrieve information about a global secret.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Computed: true, + Description: "the secret's id", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "the name of the secret", + }, + "events": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + Description: "events for which the secret is available (push, tag, pull_request, deployment, cron, manual)", + }, + "plugins_only": schema.BoolAttribute{ + Computed: true, + MarkdownDescription: "whether secret is only available for [plugins](https://woodpecker-ci.org/docs/usage/plugins/plugins)", + }, + "images": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + Description: "list of Docker images for which this secret is available", + }, + }, + } +} + +func (d *secretDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.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 + } + + d.client = client +} + +func (d *secretDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data secretDataSourceModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + secret, err := d.client.GlobalSecret(data.Name.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Couldn't read secret data", err.Error()) + return + } + + resp.Diagnostics.Append(data.setValues(ctx, secret)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/data_source_secret_test.go b/internal/data_source_secret_test.go new file mode 100644 index 0000000..d07a948 --- /dev/null +++ b/internal/data_source_secret_test.go @@ -0,0 +1,41 @@ +package internal_test + +import ( + "fmt" + "testing" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestDataSourceSecret(t *testing.T) { + t.Parallel() + + name := uuid.NewString() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` +resource "woodpecker_secret" "test_secret" { + name = "%s" + value = "test123" + events = ["push"] +} + +data "woodpecker_secret" "test_secret" { + name = woodpecker_secret.test_secret.name +} +`, name), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.woodpecker_secret.test_secret", "id"), + resource.TestCheckResourceAttr("data.woodpecker_secret.test_secret", "name", name), + resource.TestCheckTypeSetElemAttr("data.woodpecker_secret.test_secret", "events.*", "push"), + resource.TestCheckResourceAttr("data.woodpecker_secret.test_secret", "plugins_only", "false"), + ), + }, + }, + }) +} diff --git a/internal/models.go b/internal/models.go index 40bfd7e..493ca7c 100644 --- a/internal/models.go +++ b/internal/models.go @@ -38,7 +38,7 @@ func (m *userModel) toWoodpeckerModel(_ context.Context) (*woodpecker.User, diag }, nil } -type secretModel struct { +type secretResourceModel struct { ID types.Int64 `tfsdk:"id"` Name types.String `tfsdk:"name"` Value types.String `tfsdk:"value"` @@ -47,7 +47,7 @@ type secretModel struct { Events types.Set `tfsdk:"events"` } -func (m *secretModel) setValues(ctx context.Context, secret *woodpecker.Secret) diag.Diagnostics { +func (m *secretResourceModel) setValues(ctx context.Context, secret *woodpecker.Secret) diag.Diagnostics { var diagsRes diag.Diagnostics var diags diag.Diagnostics @@ -62,7 +62,7 @@ func (m *secretModel) setValues(ctx context.Context, secret *woodpecker.Secret) return diagsRes } -func (m *secretModel) toWoodpeckerModel(ctx context.Context) (*woodpecker.Secret, diag.Diagnostics) { +func (m *secretResourceModel) toWoodpeckerModel(ctx context.Context) (*woodpecker.Secret, diag.Diagnostics) { var diags diag.Diagnostics secret := &woodpecker.Secret{ @@ -76,3 +76,26 @@ func (m *secretModel) toWoodpeckerModel(ctx context.Context) (*woodpecker.Secret return secret, diags } + +type secretDataSourceModel struct { + ID types.Int64 `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Images types.Set `tfsdk:"images"` + PluginsOnly types.Bool `tfsdk:"plugins_only"` + Events types.Set `tfsdk:"events"` +} + +func (m *secretDataSourceModel) setValues(ctx context.Context, secret *woodpecker.Secret) diag.Diagnostics { + var diagsRes diag.Diagnostics + var diags diag.Diagnostics + + m.ID = types.Int64Value(secret.ID) + m.Name = types.StringValue(secret.Name) + m.Images, diags = types.SetValueFrom(ctx, types.StringType, secret.Images) + diagsRes.Append(diags...) + m.PluginsOnly = types.BoolValue(secret.PluginsOnly) + m.Events, diags = types.SetValueFrom(ctx, types.StringType, secret.Events) + diagsRes.Append(diags...) + + return diagsRes +} diff --git a/internal/provider.go b/internal/provider.go index 3205571..5f799e0 100644 --- a/internal/provider.go +++ b/internal/provider.go @@ -55,6 +55,7 @@ func (p *woodpeckerProvider) Schema(_ context.Context, _ provider.SchemaRequest, func (p *woodpeckerProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ newUserDataSource, + newSecretDataSource, } } diff --git a/internal/resource_secret.go b/internal/resource_secret.go index 41648fa..8f65fb5 100644 --- a/internal/resource_secret.go +++ b/internal/resource_secret.go @@ -98,7 +98,7 @@ func (r *secretResource) Configure(_ context.Context, req resource.ConfigureRequ } func (r *secretResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data secretModel + var data secretResourceModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) if resp.Diagnostics.HasError() { @@ -126,7 +126,7 @@ func (r *secretResource) Create(ctx context.Context, req resource.CreateRequest, } func (r *secretResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var data secretModel + var data secretResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) if resp.Diagnostics.HasError() { @@ -147,7 +147,7 @@ func (r *secretResource) Read(ctx context.Context, req resource.ReadRequest, res } func (r *secretResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var data secretModel + var data secretResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) if resp.Diagnostics.HasError() { @@ -175,7 +175,7 @@ func (r *secretResource) Update(ctx context.Context, req resource.UpdateRequest, } func (r *secretResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var data secretModel + var data secretResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) if resp.Diagnostics.HasError() {