diff --git a/docs/data-sources/deployments.md b/docs/data-sources/deployments.md new file mode 100644 index 0000000..854e2db --- /dev/null +++ b/docs/data-sources/deployments.md @@ -0,0 +1,28 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "artie_deployments Data Source - artie" +subcategory: "" +description: |- + Artie Deployments Data Source +--- + +# artie_deployments (Data Source) + +Artie Deployments Data Source + + + + +## Schema + +### Read-Only + +- `deployments` (Attributes List) (see [below for nested schema](#nestedatt--deployments)) + + +### Nested Schema for `deployments` + +Read-Only: + +- `name` (String) +- `uuid` (String) diff --git a/docs/index.md b/docs/index.md index f938291..8b9a51d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,4 +23,5 @@ provider "artie" { ### Optional -- `endpoint` (String) Example provider attribute +- `api_key` (String) Artie API key +- `endpoint` (String) Artie API endpoint diff --git a/examples/deployments/main.tf b/examples/deployments/main.tf new file mode 100644 index 0000000..45be905 --- /dev/null +++ b/examples/deployments/main.tf @@ -0,0 +1,18 @@ +terraform { + required_providers { + artie = { + source = "artie.com/terraform/artie" + } + } +} + +provider "artie" { + endpoint = "http://0.0.0.0:8000" + api_key = "api-key" +} + +data "artie_deployments" "example" {} + +output "deployments" { + value = data.artie_deployments.example.deployments +} diff --git a/internal/provider/deployments_data_source.go b/internal/provider/deployments_data_source.go new file mode 100644 index 0000000..a551254 --- /dev/null +++ b/internal/provider/deployments_data_source.go @@ -0,0 +1,144 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &deploymentsDataSource{} + _ datasource.DataSourceWithConfigure = &deploymentsDataSource{} +) + +// NewDeploymentsDataSource is a helper function to simplify the provider implementation. +func NewDeploymentsDataSource() datasource.DataSource { + return &deploymentsDataSource{} +} + +type deploymentsModel struct { + UUID string `tfsdk:"uuid"` + Name string `tfsdk:"name"` +} + +type deploymentsDataSourceModel struct { + Deployments []deploymentsModel `tfsdk:"deployments"` +} + +type deploymentsResponse struct { + Deployments []deploymentsModel `json:"items"` +} + +// deploymentsDataSource is the data source implementation. +type deploymentsDataSource struct { + endpoint basetypes.StringValue + apiKey basetypes.StringValue +} + +// Metadata returns the data source type name. +func (d *deploymentsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_deployments" +} + +// Configure adds the provider configured client to the data source. +func (d *deploymentsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + // Add a nil check when handling ProviderData because Terraform + // sets that data after it calls the ConfigureProvider RPC. + if req.ProviderData == nil { + return + } + + providerData, ok := req.ProviderData.(ArtieProviderModel) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.endpoint = providerData.Endpoint + d.apiKey = providerData.APIKey +} + +func (d *deploymentsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Artie Deployments Data Source", + Attributes: map[string]schema.Attribute{ + "deployments": schema.ListNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "uuid": schema.StringAttribute{ + Computed: true, + }, + "name": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + }, + } +} + +// Read refreshes the Terraform state with the latest data. +func (d *deploymentsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + tflog.Info(ctx, fmt.Sprintf("Endpoint: %s", d.endpoint.ValueString())) + var state deploymentsDataSourceModel + + apiReq, err := http.NewRequest("GET", fmt.Sprintf("%s/deployments", d.endpoint.ValueString()), nil) + if err != nil { + resp.Diagnostics.AddError("Unable to Read Deployments", err.Error()) + return + } + + apiReq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiKey.ValueString())) + apiResp, err := http.DefaultClient.Do(apiReq) + if err != nil { + resp.Diagnostics.AddError("Unable to Read Deployments", err.Error()) + return + } + + if apiResp.StatusCode != http.StatusOK { + resp.Diagnostics.AddError("Unable to Read Deployments", fmt.Sprintf("Received status code %d", apiResp.StatusCode)) + return + } + + bodyBytes, err := io.ReadAll(apiResp.Body) + if err != nil { + resp.Diagnostics.AddError("Unable to Read Deployments", err.Error()) + return + } + + var deploymentsResp deploymentsResponse + err = json.Unmarshal(bodyBytes, &deploymentsResp) + if err != nil { + resp.Diagnostics.AddError("Unable to Read Deployments", err.Error()) + return + } + + for _, deployment := range deploymentsResp.Deployments { + deploymentState := deploymentsModel{ + UUID: deployment.UUID, + Name: deployment.Name, + } + state.Deployments = append(state.Deployments, deploymentState) + } + + // Set state + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/internal/provider/example_data_source_test.go b/internal/provider/example_data_source_test.go deleted file mode 100644 index deebb1f..0000000 --- a/internal/provider/example_data_source_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" -) - -func TestAccExampleDataSource(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - // Read testing - { - Config: testAccExampleDataSourceConfig, - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("data.artie_example.test", "id", "example-id"), - ), - }, - }, - }) -} - -const testAccExampleDataSourceConfig = ` -data "artie_example" "test" { - configurable_attribute = "example" -} -` diff --git a/internal/provider/example_resource_test.go b/internal/provider/example_resource_test.go deleted file mode 100644 index b18a087..0000000 --- a/internal/provider/example_resource_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" -) - -func TestAccExampleResource(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - // Create and Read testing - { - Config: testAccExampleResourceConfig("one"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("artie_example.test", "configurable_attribute", "one"), - resource.TestCheckResourceAttr("artie_example.test", "defaulted", "example value when not configured"), - resource.TestCheckResourceAttr("artie_example.test", "id", "example-id"), - ), - }, - // ImportState testing - { - ResourceName: "artie_example.test", - ImportState: true, - ImportStateVerify: true, - // This is not normally necessary, but is here because this - // example code does not have an actual upstream service. - // Once the Read method is able to refresh information from - // the upstream service, this can be removed. - ImportStateVerifyIgnore: []string{"configurable_attribute", "defaulted"}, - }, - // Update and Read testing - { - Config: testAccExampleResourceConfig("two"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("artie_example.test", "configurable_attribute", "two"), - ), - }, - // Delete testing automatically occurs in TestCase - }, - }) -} - -func testAccExampleResourceConfig(configurableAttribute string) string { - return fmt.Sprintf(` -resource "artie_example" "test" { - configurable_attribute = %[1]q -} -`, configurableAttribute) -} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 17aaeef..4073d4c 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -5,7 +5,6 @@ package provider import ( "context" - "net/http" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/function" @@ -30,6 +29,7 @@ type ArtieProvider struct { // ArtieProviderModel describes the provider data model. type ArtieProviderModel struct { Endpoint types.String `tfsdk:"endpoint"` + APIKey types.String `tfsdk:"api_key"` } func (p *ArtieProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { @@ -41,7 +41,11 @@ func (p *ArtieProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "endpoint": schema.StringAttribute{ - MarkdownDescription: "Example provider attribute", + MarkdownDescription: "Artie API endpoint", + Optional: true, + }, + "api_key": schema.StringAttribute{ + MarkdownDescription: "Artie API key", Optional: true, }, }, @@ -60,10 +64,8 @@ func (p *ArtieProvider) Configure(ctx context.Context, req provider.ConfigureReq // Configuration values are now available. // if data.Endpoint.IsNull() { /* ... */ } - // Example client configuration for data sources and resources - client := http.DefaultClient - resp.DataSourceData = client - resp.ResourceData = client + resp.DataSourceData = data + resp.ResourceData = data } func (p *ArtieProvider) Resources(ctx context.Context) []func() resource.Resource { @@ -75,6 +77,7 @@ func (p *ArtieProvider) Resources(ctx context.Context) []func() resource.Resourc func (p *ArtieProvider) DataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewExampleDataSource, + NewDeploymentsDataSource, } } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 2fa09c1..1d3dc75 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -4,8 +4,6 @@ package provider import ( - "testing" - "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) @@ -17,9 +15,3 @@ import ( var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ "artie": providerserver.NewProtocol6WithError(New("test")()), } - -func testAccPreCheck(t *testing.T) { - // You can add code here to run prior to any test case execution, for example assertions - // about the appropriate environment variables being set are common to see in a pre-check - // function. -}