Skip to content

Commit

Permalink
Framework provider stub and mux functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
t0mk committed Oct 10, 2023
1 parent e0a9ac4 commit 47c48a3
Show file tree
Hide file tree
Showing 10 changed files with 588 additions and 9 deletions.
92 changes: 92 additions & 0 deletions equinix/framework_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package equinix

import (
"context"
"fmt"

"github.com/equinix/terraform-provider-equinix/equinix/helper"
"github.com/equinix/terraform-provider-equinix/equinix/metal_ssh_key"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

type FrameworkProvider struct {
ProviderVersion string
Meta *helper.FrameworkProviderMeta
}

func CreateFrameworkProvider(version string) provider.Provider {
return &FrameworkProvider{

Check failure on line 21 in equinix/framework_provider.go

View workflow job for this annotation

GitHub Actions / Test

cannot use &FrameworkProvider{…} (value of type *FrameworkProvider) as provider.Provider value in return statement: *FrameworkProvider does not implement provider.Provider (missing method Configure)

Check failure on line 21 in equinix/framework_provider.go

View workflow job for this annotation

GitHub Actions / Build

cannot use &FrameworkProvider{…} (value of type *FrameworkProvider) as provider.Provider value in return statement: *FrameworkProvider does not implement provider.Provider (missing method Configure)
ProviderVersion: version,
}
}

func (p *FrameworkProvider) Metadata(
ctx context.Context,
req provider.MetadataRequest,
resp *provider.MetadataResponse,
) {
resp.TypeName = "equinixcloud"
}

func (p *FrameworkProvider) Schema(
ctx context.Context,
req provider.SchemaRequest,
resp *provider.SchemaResponse,
) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"endpoint": schema.StringAttribute{
Optional: true,
Description: "The Equinix API base URL to point out desired environment. Defaults to " + DefaultBaseURL,
},
"client_id": schema.StringAttribute{
Optional: true,
Description: "API Consumer Key available under My Apps section in developer portal",
},
"client_secret": schema.StringAttribute{
Optional: true,
Description: "API Consumer secret available under My Apps section in developer portal",
},
"token": schema.StringAttribute{
Optional: true,
Description: "API token from the developer sandbox",
},
"auth_token": schema.StringAttribute{
Optional: true,
Description: "The Equinix Metal API auth key for API operations",
},
"request_timeout": schema.Int64Attribute{
Optional: true,
Description: "The duration of time, in seconds, that the Equinix Platform API Client should wait before canceling an API request. Defaults to " + fmt.Sprint(DefaultTimeout),
},
"response_max_page_size": schema.Int64Attribute{
Optional: true,
Description: "The maximum number of records in a single response for REST queries that produce paginated responses",
},
"max_retries": schema.Int64Attribute{
Optional: true,
Description: "Maximum number of retries.",
},
"max_retry_wait_seconds": schema.Int64Attribute{
Optional: true,
Description: "Maximum number of seconds to wait before retrying a request.",
},
},
}
}

func (p *FrameworkProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
metal_ssh_key.NewResource,
}
}

func (p *FrameworkProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
// return nil
return []func() datasource.DataSource{
metal_ssh_key.NewDataSource,
}
}
73 changes: 73 additions & 0 deletions equinix/helper/framework_datasource_base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package helper

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)

// NewBaseDataSource returns a new instance of the BaseDataSource
// struct for cleaner initialization.
func NewBaseDataSource(cfg BaseDataSourceConfig) BaseDataSource {
return BaseDataSource{
Config: cfg,
}
}

// BaseDataSourceConfig contains all configurable base resource fields.
type BaseDataSourceConfig struct {
Name string

// Optional
Schema *schema.Schema
IsEarlyAccess bool
}

// BaseDataSource contains various re-usable fields and methods
// intended for use in data source implementations by composition.
type BaseDataSource struct {
Config BaseDataSourceConfig
Meta *FrameworkProviderMeta
}

func (r *BaseDataSource) Configure(
ctx context.Context,
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

r.Meta = GetDataSourceMeta(req, resp)
if resp.Diagnostics.HasError() {
return
}
}

func (r *BaseDataSource) Metadata(
ctx context.Context,
req datasource.MetadataRequest,
resp *datasource.MetadataResponse,
) {
resp.TypeName = r.Config.Name
}

func (r *BaseDataSource) Schema(
ctx context.Context,
req datasource.SchemaRequest,
resp *datasource.SchemaResponse,
) {
if r.Config.Schema == nil {
resp.Diagnostics.AddError(
"Missing Schema",
"Base data source was not provided a schema. "+
"Please provide a Schema config attribute or implement, the Schema(...) function.",
)
return
}

resp.Schema = *r.Config.Schema
}
23 changes: 23 additions & 0 deletions equinix/helper/framework_provider_model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package helper

import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/packethost/packngo"
)

type FrameworkProviderModel struct {
Endpoint types.String `tfsdk:"endpoint,omitempty"`
ClientID types.String `tfsdk:"client_id,omitempty"`
ClientSecret types.String `tfsdk:"client_secret,omitempty"`
Token types.String `tfsdk:"token,omitempty"`
AuthToken types.String `tfsdk:"auth_token,omitempty"`
RequestTimeout types.Int64 `tfsdk:"request_timeout,omitempty"`
ResponseMaxPageSize types.Int64 `tfsdk:"response_max_page_size,omitempty"`
MaxRetries types.Int64 `tfsdk:"max_retries,omitempty"`
MaxRetryWaitSeconds types.Int64 `tfsdk:"max_retry_wait_seconds,omitempty"`
}

type FrameworkProviderMeta struct {
Client *packngo.Client
Config *FrameworkProviderModel
}
132 changes: 132 additions & 0 deletions equinix/helper/framework_resource_base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package helper

import (
"context"
"fmt"
"strconv"

"github.com/hashicorp/terraform-plugin-framework/attr"
"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/types"
)

// NewBaseResource returns a new instance of the BaseResource
// struct for cleaner initialization.
func NewBaseResource(cfg BaseResourceConfig) BaseResource {
return BaseResource{
Config: cfg,
}
}

// BaseResourceConfig contains all configurable base resource fields.
type BaseResourceConfig struct {
Name string
IDAttr string
IDType attr.Type

// Optional
Schema *schema.Schema
IsEarlyAccess bool
}

// BaseResource contains various re-usable fields and methods
// intended for use in resource implementations by composition.
type BaseResource struct {
Config BaseResourceConfig
Meta *FrameworkProviderMeta
}

func (r *BaseResource) Configure(
ctx context.Context,
req resource.ConfigureRequest,
resp *resource.ConfigureResponse,
) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

r.Meta = GetResourceMeta(req, resp)
if resp.Diagnostics.HasError() {
return
}
}

func (r *BaseResource) Metadata(
ctx context.Context,
req resource.MetadataRequest,
resp *resource.MetadataResponse,
) {
resp.TypeName = r.Config.Name
}

func (r *BaseResource) Schema(
ctx context.Context,
req resource.SchemaRequest,
resp *resource.SchemaResponse,
) {
if r.Config.Schema == nil {
resp.Diagnostics.AddError(
"Missing Schema",
"Base resource was not provided a schema. "+
"Please provide a Schema config attribute or implement, the Schema(...) function.",
)
return
}

resp.Schema = *r.Config.Schema
}

// ImportState should be overridden for resources with
// complex read logic (e.g. parent ID).
func (r *BaseResource) ImportState(
ctx context.Context,
req resource.ImportStateRequest,
resp *resource.ImportStateResponse,
) {
// Enforce defaults
idAttr := r.Config.IDAttr
if idAttr == "" {
idAttr = "id"
}

idType := r.Config.IDType
if idType == nil {
idType = types.Int64Type
}

attrPath := path.Root(idAttr)

if attrPath.Equal(path.Empty()) {
resp.Diagnostics.AddError(
"Resource Import Passthrough Missing Attribute Path",
"This is always an error in the provider. Please report the following to the provider developer:\n\n"+
"Resource ImportState path must be set to a valid attribute path.",
)
return
}

// Handle type conversion
var err error
var idValue any

switch idType {
case types.Int64Type:
idValue, err = strconv.ParseInt(req.ID, 10, 64)
case types.StringType:
idValue = req.ID
default:
err = fmt.Errorf("unsupported id attribute type: %v", idType)
}
if err != nil {
resp.Diagnostics.AddError(
"Failed to convert ID attribute",
err.Error(),
)
return
}

resp.Diagnostics.Append(resp.State.SetAttribute(ctx, attrPath, idValue)...)
}
48 changes: 48 additions & 0 deletions equinix/helper/resource_datasource_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package helper

import (
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

func GetResourceMeta(
req resource.ConfigureRequest,
resp *resource.ConfigureResponse,
) *FrameworkProviderMeta {
meta, ok := req.ProviderData.(*FrameworkProviderMeta)

if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf(
"Expected *http.Client, got: %T. Please report this issue to the provider developers.",
req.ProviderData,
),
)
return nil
}

return meta
}

func GetDataSourceMeta(
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) *FrameworkProviderMeta {
meta, ok := req.ProviderData.(*FrameworkProviderMeta)

if !ok {
resp.Diagnostics.AddError(
"Unexpected DataSource Configure Type",
fmt.Sprintf(
"Expected *http.Client, got: %T. Please report this issue to the provider developers.",
req.ProviderData,
),
)
return nil
}

return meta
}
Loading

0 comments on commit 47c48a3

Please sign in to comment.