Skip to content

Commit

Permalink
feat: setup initial plugin framework skaffolding (#749)
Browse files Browse the repository at this point in the history
Related to #752

Co-authored-by: jo <[email protected]>
  • Loading branch information
apricote and jooola authored Sep 26, 2023
1 parent 91a7b1a commit 5c284e2
Show file tree
Hide file tree
Showing 59 changed files with 853 additions and 426 deletions.
32 changes: 18 additions & 14 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@ go 1.21.1

require (
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
github.com/hashicorp/go-hclog v1.5.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/terraform-plugin-framework v1.4.0
github.com/hashicorp/terraform-plugin-go v0.19.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.28.0
github.com/hashicorp/terraform-plugin-mux v0.12.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0
github.com/hashicorp/terraform-plugin-testing v1.5.1
github.com/hetznercloud/hcloud-go v1.49.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.13.0
golang.org/x/net v0.15.0
)

require (
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
Expand All @@ -27,17 +32,15 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-plugin v1.4.10 // indirect
github.com/hashicorp/go-plugin v1.5.1 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hc-install v0.5.2 // indirect
github.com/hashicorp/hcl/v2 v2.17.0 // indirect
github.com/hashicorp/hc-install v0.6.0 // indirect
github.com/hashicorp/hcl/v2 v2.18.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.18.1 // indirect
github.com/hashicorp/terraform-exec v0.19.0 // indirect
github.com/hashicorp/terraform-json v0.17.1 // indirect
github.com/hashicorp/terraform-plugin-go v0.18.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.1 // indirect
github.com/hashicorp/terraform-registry-address v0.2.2 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
Expand All @@ -59,13 +62,14 @@ require (
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/zclconf/go-cty v1.13.2 // indirect
golang.org/x/mod v0.10.0 // indirect
github.com/zclconf/go-cty v1.14.0 // indirect
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
google.golang.org/grpc v1.57.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
136 changes: 85 additions & 51 deletions go.sum

Large diffs are not rendered by default.

162 changes: 162 additions & 0 deletions hcloud/plugin_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package hcloud

import (
"context"
"fmt"
"os"
"time"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/terraform-provider-hcloud/internal/util/tflogutil"
)

type PluginProvider struct{}

var _ provider.Provider = &PluginProvider{}

func NewPluginProvider() provider.Provider {
return &PluginProvider{}
}

// Metadata should return the metadata for the provider, such as
// a type name and version data.
//
// Implementing the MetadataResponse.TypeName will populate the
// datasource.MetadataRequest.ProviderTypeName and
// resource.MetadataRequest.ProviderTypeName fields automatically.
func (p *PluginProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "hcloud"
resp.Version = Version
}

// Schema should return the schema for this provider.
func (p *PluginProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"token": schema.StringAttribute{
Description: "The Hetzner Cloud API token, can also be specified with the HCLOUD_TOKEN environment variable.",
Optional: true,
Sensitive: true,
},
"endpoint": schema.StringAttribute{
Description: "The Hetzner Cloud API endpoint, can be used to override the default API Endpoint https://api.hetzner.cloud/v1.",
Optional: true,
},
"poll_interval": schema.StringAttribute{
Description: "The interval at which actions are polled by the client. Default `500ms`. Increase this interval if you run into rate limiting errors.",
Optional: true,
},
},
// TODO: Uncomment once we get rid of the SDK v2 Provider
// MarkdownDescription: `The Hetzner Cloud (hcloud) provider is used to interact with the resources supported by
// [Hetzner Cloud](https://www.hetzner.com/cloud). The provider needs to be configured with the proper credentials
// before it can be used.`,
}
}

// PluginProviderModel describes the provider data model.
type PluginProviderModel struct {
Token types.String `tfsdk:"token"`
Endpoint types.String `tfsdk:"endpoint"`
PollInterval types.String `tfsdk:"poll_interval"`
}

// Configure is called at the beginning of the provider lifecycle, when
// Terraform sends to the provider the values the user specified in the
// provider configuration block. These are supplied in the
// ConfigureProviderRequest argument.
// Values from provider configuration are often used to initialize an
// API client, which should be stored on the struct to initialize an
// Provider interface.
func (p *PluginProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var data PluginProviderModel

resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

opts := []hcloud.ClientOption{
hcloud.WithApplication("hcloud-terraform", Version),
}

endpoint := os.Getenv("HCLOUD_ENDPOINT")
if data.Endpoint.ValueString() != "" {
endpoint = data.Endpoint.ValueString()
}
if endpoint != "" {
opts = append(opts, hcloud.WithEndpoint(endpoint))
}

token := os.Getenv("HCLOUD_TOKEN")
if data.Token.ValueString() != "" {
token = data.Token.ValueString()
}
if token != "" {
opts = append(opts, hcloud.WithToken(token))
} else {
resp.Diagnostics.AddAttributeError(
path.Root("token"),
"Missing Hetzner Cloud API token",
"While configuring the provider, the Hetzner Cloud API token was not found in the HCLOUD_TOKEN environment variable or provider configuration block token attribute.",
)
}

if data.PollInterval.ValueString() != "" {
pollInterval, err := time.ParseDuration(data.PollInterval.ValueString())
if err != nil {
resp.Diagnostics.AddAttributeError(
path.Root("poll_interval"),
"Unparsable poll interval value",
fmt.Sprintf("An unexpected error was encountered trying to parse the value.\n\n%s", err.Error()),
)
}
opts = append(opts, hcloud.WithPollBackoffFunc(hcloud.ExponentialBackoff(2, pollInterval)))
}

if resp.Diagnostics.HasError() {
return
}

// Debug writer
opts = append(opts,
hcloud.WithDebugWriter(
tflogutil.NewWriter(
tflog.NewSubsystem(ctx, "hcloud-go", tflog.WithLevel(hclog.Debug)),
),
),
)

client := hcloud.NewClient(opts...)
resp.DataSourceData = client
resp.ResourceData = client

tflog.Info(ctx, "terraform-provider-hcloud info", map[string]any{"version": Version, "commit": Commit})
tflog.Info(ctx, "hcloud-go info", map[string]any{"version": hcloud.Version})
}

// DataSources returns a slice of functions to instantiate each DataSource
// implementation.
//
// The data source type name is determined by the DataSource implementing
// the Metadata method. All data sources must have unique names.
func (p *PluginProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{}
}

// Resources returns a slice of functions to instantiate each Resource
// implementation.
//
// The resource type name is determined by the Resource implementing
// the Metadata method. All resources must have unique names.
func (p *PluginProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{}
}
10 changes: 6 additions & 4 deletions hcloud/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func Provider() *schema.Provider {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("HCLOUD_TOKEN", nil),
Description: "The API token to access the Hetzner cloud.",
Description: "The Hetzner Cloud API token, can also be specified with the HCLOUD_TOKEN environment variable.",
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
token := val.(string)
if len(token) != 64 {
Expand All @@ -61,11 +61,13 @@ func Provider() *schema.Provider {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("HCLOUD_ENDPOINT", nil),
Description: "The Hetzner Cloud API endpoint, can be used to override the default API Endpoint https://api.hetzner.cloud/v1.",
},
"poll_interval": {
Type: schema.TypeString,
Optional: true,
Default: "500ms",
Type: schema.TypeString,
Optional: true,
Default: "500ms",
Description: "The interval at which actions are polled by the client. Default `500ms`. Increase this interval if you run into rate limiting errors.",
},
},
ResourcesMap: map[string]*schema.Resource{
Expand Down
2 changes: 1 addition & 1 deletion internal/certificate/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/hetznercloud/terraform-provider-hcloud/internal/testsupport"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testtemplate"
)
Expand Down
14 changes: 7 additions & 7 deletions internal/e2etests/certificate/data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/hetznercloud/terraform-provider-hcloud/internal/certificate"
"github.com/hetznercloud/terraform-provider-hcloud/internal/e2etests"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testsupport"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testtemplate"
)
Expand All @@ -30,9 +30,9 @@ func TestAccHcloudDataSourceCertificateTest(t *testing.T) {
certificateBySel.SetRName("certificate_by_sel")

resource.ParallelTest(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.ResourceType, certificate.ByID(t, nil)),
PreCheck: e2etests.PreCheck(t),
ProtoV6ProviderFactories: e2etests.ProtoV6ProviderFactories(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.ResourceType, certificate.ByID(t, nil)),
Steps: []resource.TestStep{
{
Config: tmplMan.Render(t,
Expand Down Expand Up @@ -78,9 +78,9 @@ func TestAccHcloudDataSourceCertificateListTest(t *testing.T) {

tmplMan := testtemplate.Manager{}
resource.ParallelTest(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.ResourceType, certificate.ByID(t, nil)),
PreCheck: e2etests.PreCheck(t),
ProtoV6ProviderFactories: e2etests.ProtoV6ProviderFactories(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.ResourceType, certificate.ByID(t, nil)),
Steps: []resource.TestStep{
{
Config: tmplMan.Render(t,
Expand Down
22 changes: 11 additions & 11 deletions internal/e2etests/certificate/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"github.com/hetznercloud/terraform-provider-hcloud/internal/certificate"
"github.com/hetznercloud/terraform-provider-hcloud/internal/e2etests"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testsupport"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testtemplate"
Expand All @@ -30,9 +30,9 @@ func TestCertificateResource_Uploaded_Basic(t *testing.T) {
tmplMan := testtemplate.Manager{}
// Not parallel because number of certificates per domain is limited
resource.Test(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.UploadedResourceType, certificate.ByID(t, &cert)),
PreCheck: e2etests.PreCheck(t),
ProtoV6ProviderFactories: e2etests.ProtoV6ProviderFactories(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.UploadedResourceType, certificate.ByID(t, &cert)),
Steps: []resource.TestStep{
{
// Create a new Certificate using the required values
Expand Down Expand Up @@ -77,9 +77,9 @@ func TestCertificateResource_Uploaded_ChangeCertRequiresNewResource(t *testing.T
tmplMan := testtemplate.Manager{}
// Not parallel because number of certificates per domain is limited
resource.Test(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.UploadedResourceType, certificate.ByID(t, &cert)),
PreCheck: e2etests.PreCheck(t),
ProtoV6ProviderFactories: e2etests.ProtoV6ProviderFactories(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.UploadedResourceType, certificate.ByID(t, &cert)),
Steps: []resource.TestStep{
{
// Create a new Certificate using the required values
Expand Down Expand Up @@ -132,9 +132,9 @@ func TestCertificateResource_Managed_Basic(t *testing.T) {
tmplMan := testtemplate.Manager{}
// Not parallel because number of certificates per domain is limited
resource.Test(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.ManagedResourceType, certificate.ByID(t, &cert)),
PreCheck: e2etests.PreCheck(t),
ProtoV6ProviderFactories: e2etests.ProtoV6ProviderFactories(),
CheckDestroy: testsupport.CheckResourcesDestroyed(certificate.ManagedResourceType, certificate.ByID(t, &cert)),
Steps: []resource.TestStep{
{
// Create a new Certificate using the required values
Expand Down
10 changes: 5 additions & 5 deletions internal/e2etests/datacenter/data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/hetznercloud/terraform-provider-hcloud/internal/datacenter"
"github.com/hetznercloud/terraform-provider-hcloud/internal/e2etests"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testtemplate"
)

Expand All @@ -22,8 +22,8 @@ func TestAccHcloudDataSourceDatacenterTest(t *testing.T) {
}
dcByID.SetRName("dc_by_id")
resource.ParallelTest(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
PreCheck: e2etests.PreCheck(t),
ProtoV6ProviderFactories: e2etests.ProtoV6ProviderFactories(),
Steps: []resource.TestStep{
{
Config: tmplMan.Render(t,
Expand All @@ -49,8 +49,8 @@ func TestAccHcloudDataSourceDatacentersTest(t *testing.T) {
datacentersD := &datacenter.DDataList{}
datacentersD.SetRName("ds")
resource.ParallelTest(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
PreCheck: e2etests.PreCheck(t),
ProtoV6ProviderFactories: e2etests.ProtoV6ProviderFactories(),
Steps: []resource.TestStep{
{
Config: tmplMan.Render(t,
Expand Down
Loading

0 comments on commit 5c284e2

Please sign in to comment.