Skip to content

Commit

Permalink
Merge pull request #1185 from tmatilai/r-registry-provider
Browse files Browse the repository at this point in the history
Add `tfe_registry_provider` resource and data source
  • Loading branch information
brandonc authored Jan 2, 2024
2 parents a2cc0e7 + 5668851 commit e8838d2
Show file tree
Hide file tree
Showing 11 changed files with 1,249 additions and 0 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## UNRELEASED
<!-- Add CHANGELOG entry to this section for any PR awaiting the next release -->
<!-- Please also include if this is a Bug Fix, Enhancement, or Feature -->

FEATURES:
* **New Resource**: `r/tfe_registry_provider` is a new resource for managing public and private providers in the private registry, by @tmatilai [1185](https://github.com/hashicorp/terraform-provider-tfe/pull/1185)
* **New Data Source**: `d/tfe_registry_provider` is a new data source to retrieve information about a public or private provider in the private registry, by @tmatilai [1185](https://github.com/hashicorp/terraform-provider-tfe/pull/1185)
* **New Data Source**: `d/tfe_registry_providers` is a new data source to retrieve information about public and private providers in the private registry, by @tmatilai [1185](https://github.com/hashicorp/terraform-provider-tfe/pull/1185)

## v0.51.1

BUG FIXES:
Expand Down
182 changes: 182 additions & 0 deletions internal/provider/data_source_registry_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"fmt"

"github.com/hashicorp/go-tfe"
"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-log/tflog"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &dataSourceTFERegistryProvider{}
_ datasource.DataSourceWithConfigure = &dataSourceTFERegistryProvider{}
_ datasource.DataSourceWithValidateConfig = &dataSourceTFERegistryProvider{}
)

// NewRegistryProviderDataSource is a helper function to simplify the provider implementation.
func NewRegistryProviderDataSource() datasource.DataSource {
return &dataSourceTFERegistryProvider{}
}

// dataSourceTFERegistryProvider is the data source implementation.
type dataSourceTFERegistryProvider struct {
config ConfiguredClient
}

// Metadata returns the data source type name.
func (d *dataSourceTFERegistryProvider) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_registry_provider"
}

// Schema defines the schema for the data source.
func (d *dataSourceTFERegistryProvider) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "This data source can be used to retrieve a public or private provider from the private registry.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "ID of the provider.",
Computed: true,
},
"organization": schema.StringAttribute{
Description: "Name of the organization. If omitted, organization must be defined in the provider config.",
Optional: true,
Computed: true,
},
"registry_name": schema.StringAttribute{
Description: "Whether this is a publicly maintained provider or private. Must be either `public` or `private`.",
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.OneOf(
string(tfe.PrivateRegistry),
string(tfe.PublicRegistry),
),
},
},
"namespace": schema.StringAttribute{
Description: "The namespace of the provider. For private providers this is the same as the oraganization.",
Optional: true,
Computed: true,
},
"name": schema.StringAttribute{
Description: "Name of the provider.",
Required: true,
},
"created_at": schema.StringAttribute{
Description: "The time when the provider was created.",
Computed: true,
},
"updated_at": schema.StringAttribute{
Description: "The time when the provider was last updated.",
Computed: true,
},
},
}
}

func (d *dataSourceTFERegistryProvider) ValidateConfig(ctx context.Context, req datasource.ValidateConfigRequest, resp *datasource.ValidateConfigResponse) {
var config modelTFERegistryProvider

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

if resp.Diagnostics.HasError() {
return
}

if config.RegistryName.ValueString() == "public" && config.Namespace.IsNull() {
resp.Diagnostics.AddAttributeError(
path.Root("namespace"),
"Missing Attribute Configuration",
"Expected namespace to be configured when registry_name is \"public\".",
)
} else if (config.RegistryName.IsNull() || config.RegistryName.ValueString() == "private") && !config.Namespace.IsNull() && !config.Namespace.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("namespace"),
"Invalid Attribute Combination",
"The namespace attribute cannot be configured when registry_name is \"private\".",
)
}
}

// Configure adds the provider configured client to the data source.
func (d *dataSourceTFERegistryProvider) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(ConfiguredClient)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected tfe.ConfiguredClient, got %T. This is a bug in the tfe provider, so please report it on GitHub.", req.ProviderData),
)

return
}
d.config = client
}

// Read refreshes the Terraform state with the latest data.
func (d *dataSourceTFERegistryProvider) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data modelTFERegistryProvider

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

if resp.Diagnostics.HasError() {
return
}

var organization string
resp.Diagnostics.Append(d.config.dataOrDefaultOrganization(ctx, req.Config, &organization)...)

if resp.Diagnostics.HasError() {
return
}

var registryName string
if data.RegistryName.IsNull() {
registryName = "private"
} else {
registryName = data.RegistryName.ValueString()
}

var namespace string
if registryName == "private" {
namespace = organization
} else {
namespace = data.Namespace.ValueString()
}

providerID := tfe.RegistryProviderID{
OrganizationName: organization,
RegistryName: tfe.RegistryName(registryName),
Namespace: namespace,
Name: data.Name.ValueString(),
}

options := tfe.RegistryProviderReadOptions{}

tflog.Debug(ctx, "Reading private registry provider")
provider, err := d.config.Client.RegistryProviders.Read(ctx, providerID, &options)
if err != nil {
resp.Diagnostics.AddError("Unable to read private registry provider", err.Error())
return
}

data = modelFromTFERegistryProvider(provider)

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
87 changes: 87 additions & 0 deletions internal/provider/data_source_registry_provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"fmt"
"math/rand"
"testing"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccTFERegistryProviderDataSource_public(t *testing.T) {
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
orgName := fmt.Sprintf("tst-terraform-%d", rInt)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV5ProviderFactories: testAccMuxedProviders,
Steps: []resource.TestStep{
{
Config: testAccTFERegistryProviderDataSourceConfig_public(orgName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("tfe_registry_provider.foobar", "id"),
resource.TestCheckResourceAttr("tfe_registry_provider.foobar", "organization", orgName),
resource.TestCheckResourceAttr("tfe_registry_provider.foobar", "registry_name", "public"),
resource.TestCheckResourceAttr("tfe_registry_provider.foobar", "namespace", "hashicorp"),
resource.TestCheckResourceAttr("tfe_registry_provider.foobar", "name", "aws"),
resource.TestCheckResourceAttrSet("tfe_registry_provider.foobar", "created_at"),
resource.TestCheckResourceAttrSet("tfe_registry_provider.foobar", "updated_at"),
),
},
},
})
}

func TestAccTFERegistryProviderDataSource_private(t *testing.T) {
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
orgName := fmt.Sprintf("tst-terraform-%d", rInt)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV5ProviderFactories: testAccMuxedProviders,
Steps: []resource.TestStep{
{
Config: testAccTFERegistryProviderDataSourceConfig_private(orgName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("tfe_registry_provider.foobar", "id"),
resource.TestCheckResourceAttr("tfe_registry_provider.foobar", "organization", orgName),
resource.TestCheckResourceAttr("tfe_registry_provider.foobar", "registry_name", "private"),
resource.TestCheckResourceAttr("tfe_registry_provider.foobar", "namespace", orgName),
resource.TestCheckResourceAttr("tfe_registry_provider.foobar", "name", "example"),
resource.TestCheckResourceAttrSet("tfe_registry_provider.foobar", "created_at"),
resource.TestCheckResourceAttrSet("tfe_registry_provider.foobar", "updated_at"),
),
},
},
})
}

func testAccTFERegistryProviderDataSourceConfig_public(orgName string) string {
return fmt.Sprintf(`
%s
data "tfe_registry_provider" "foobar" {
organization = tfe_organization.foobar.name
registry_name = "public"
namespace = tfe_registry_provider.foobar.namespace
name = tfe_registry_provider.foobar.name
}
`, testAccTFERegistryProviderResourceConfig_public(orgName))
}

func testAccTFERegistryProviderDataSourceConfig_private(orgName string) string {
return fmt.Sprintf(`
%s
data "tfe_registry_provider" "foobar" {
organization = tfe_organization.foobar.name
name = tfe_registry_provider.foobar.name
}
`, testAccTFERegistryProviderResourceConfig_private(orgName))
}
Loading

0 comments on commit e8838d2

Please sign in to comment.