This is an automated email from the ASF dual-hosted git repository.

vishesh pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git


The following commit(s) were added to refs/heads/main by this push:
     new e6bcc74  feat: migrate to terraform plugin framework (#113)
e6bcc74 is described below

commit e6bcc7489e84db74e30117146b6dec8f76461eeb
Author: Fábio Matavelli <fabiomatave...@gmail.com>
AuthorDate: Thu Jul 25 01:36:50 2024 -0300

    feat: migrate to terraform plugin framework (#113)
---
 cloudstack/provider.go      |   4 +-
 cloudstack/provider_test.go |  82 +++++++++++++++++------
 cloudstack/provider_v6.go   | 155 ++++++++++++++++++++++++++++++++++++++++++++
 cloudstack/resources.go     |  23 +++++++
 go.mod                      |   8 ++-
 go.sum                      |  16 +++--
 main.go                     |  32 ++++++---
 7 files changed, 280 insertions(+), 40 deletions(-)

diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index 6d7b7b5..5aad7c5 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -26,7 +26,7 @@ import (
        "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 )
 
-func New() *schema.Provider {
+func Provider() *schema.Provider {
        return &schema.Provider{
                Schema: map[string]*schema.Schema{
                        "api_url": {
@@ -41,6 +41,7 @@ func New() *schema.Provider {
                                Optional:      true,
                                DefaultFunc:   
schema.EnvDefaultFunc("CLOUDSTACK_API_KEY", nil),
                                ConflictsWith: []string{"config", "profile"},
+                               Sensitive:     true,
                        },
 
                        "secret_key": {
@@ -48,6 +49,7 @@ func New() *schema.Provider {
                                Optional:      true,
                                DefaultFunc:   
schema.EnvDefaultFunc("CLOUDSTACK_SECRET_KEY", nil),
                                ConflictsWith: []string{"config", "profile"},
+                               Sensitive:     true,
                        },
 
                        "config": {
diff --git a/cloudstack/provider_test.go b/cloudstack/provider_test.go
index 0a3acf0..fb868e4 100644
--- a/cloudstack/provider_test.go
+++ b/cloudstack/provider_test.go
@@ -22,10 +22,13 @@ package cloudstack
 import (
        "context"
        "os"
+       "regexp"
        "testing"
 
-       "github.com/hashicorp/terraform-plugin-go/tfprotov5"
-       "github.com/hashicorp/terraform-plugin-mux/tf5muxserver"
+       "github.com/hashicorp/terraform-plugin-framework/providerserver"
+       "github.com/hashicorp/terraform-plugin-go/tfprotov6"
+       "github.com/hashicorp/terraform-plugin-mux/tf5to6server"
+       "github.com/hashicorp/terraform-plugin-mux/tf6muxserver"
        "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
        "github.com/hashicorp/terraform-plugin-testing/helper/resource"
 )
@@ -33,44 +36,65 @@ import (
 var testAccProviders map[string]*schema.Provider
 var testAccProvider *schema.Provider
 
+var testAccMuxProvider map[string]func() (tfprotov6.ProviderServer, error)
+
 var cloudStackTemplateURL = os.Getenv("CLOUDSTACK_TEMPLATE_URL")
 
 func init() {
-       testAccProvider = New()
+       testAccProvider = Provider()
        testAccProviders = map[string]*schema.Provider{
                "cloudstack": testAccProvider,
        }
+
+       testAccMuxProvider = map[string]func() (tfprotov6.ProviderServer, 
error){
+               "cloudstack": func() (tfprotov6.ProviderServer, error) {
+                       ctx := context.Background()
+
+                       upgradedSdkServer, err := tf5to6server.UpgradeServer(
+                               ctx,
+                               Provider().GRPCProvider,
+                       )
+
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       providers := []func() tfprotov6.ProviderServer{
+                               providerserver.NewProtocol6(New()),
+                               func() tfprotov6.ProviderServer {
+                                       return upgradedSdkServer
+                               },
+                       }
+
+                       muxServer, err := tf6muxserver.NewMuxServer(ctx, 
providers...)
+
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       return muxServer.ProviderServer(), nil
+               },
+       }
 }
 
 func TestProvider(t *testing.T) {
-       if err := New().InternalValidate(); err != nil {
+       if err := Provider().InternalValidate(); err != nil {
                t.Fatalf("err: %s", err)
        }
 }
 
 func TestProvider_impl(t *testing.T) {
-       var _ *schema.Provider = New()
+       var _ *schema.Provider = Provider()
 }
 
 func TestMuxServer(t *testing.T) {
        resource.Test(t, resource.TestCase{
-               ProtoV5ProviderFactories: map[string]func() 
(tfprotov5.ProviderServer, error){
-                       "cloudstack": func() (tfprotov5.ProviderServer, error) {
-                               ctx := context.Background()
-                               providers := []func() tfprotov5.ProviderServer{
-                                       New().GRPCProvider,
-                               }
-
-                               muxServer, err := 
tf5muxserver.NewMuxServer(ctx, providers...)
-
-                               if err != nil {
-                                       return nil, err
-                               }
-
-                               return muxServer.ProviderServer(), nil
-                       },
-               },
+               ProtoV6ProviderFactories: testAccMuxProvider,
                Steps: []resource.TestStep{
+                       {
+                               Config:      testMuxServerConfig_conflict,
+                               ExpectError: regexp.MustCompile("Invalid 
Attribute Combination"),
+                       },
                        {
                                Config: testMuxServerConfig_basic,
                        },
@@ -94,6 +118,22 @@ resource "cloudstack_zone" "zone_resource"{
   }
   `
 
+const testMuxServerConfig_conflict = `
+provider "cloudstack" {
+       api_url = "http://localhost:8080/client/api";
+       api_key = "xxxxx"
+       secret_key = "xxxxx"
+       config = "cloudstack.ini"
+}
+
+data "cloudstack_zone" "zone_data_source"{
+    filter{
+       name    =       "name"
+       value   =       "test"
+    }
+}
+  `
+
 func testAccPreCheck(t *testing.T) {
        if v := os.Getenv("CLOUDSTACK_API_URL"); v == "" {
                t.Fatal("CLOUDSTACK_API_URL must be set for acceptance tests")
diff --git a/cloudstack/provider_v6.go b/cloudstack/provider_v6.go
new file mode 100644
index 0000000..339e4b3
--- /dev/null
+++ b/cloudstack/provider_v6.go
@@ -0,0 +1,155 @@
+package cloudstack
+
+import (
+       "context"
+       "fmt"
+       "os"
+       "strconv"
+
+       
"github.com/hashicorp/terraform-plugin-framework-validators/providervalidator"
+       "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"
+)
+
+type CloudstackProvider struct{}
+
+type CloudstackProviderModel struct {
+       ApiUrl      types.String `tfsdk:"api_url"`
+       ApiKey      types.String `tfsdk:"api_key"`
+       SecretKey   types.String `tfsdk:"secret_key"`
+       Config      types.String `tfsdk:"config"`
+       Profile     types.String `tfsdk:"profile"`
+       HttpGetOnly types.Bool   `tfsdk:"http_get_only"`
+       Timeout     types.Int64  `tfsdk:"timeout"`
+}
+
+var _ provider.Provider = (*CloudstackProvider)(nil)
+
+func New() provider.Provider {
+       return &CloudstackProvider{}
+}
+
+func (p *CloudstackProvider) Metadata(ctx context.Context, req 
provider.MetadataRequest, resp *provider.MetadataResponse) {
+       resp.TypeName = "cloudstack"
+}
+
+func (p *CloudstackProvider) Schema(ctx context.Context, req 
provider.SchemaRequest, resp *provider.SchemaResponse) {
+       resp.Schema = schema.Schema{
+               Attributes: map[string]schema.Attribute{
+                       "api_url": schema.StringAttribute{
+                               Optional: true,
+                       },
+                       "api_key": schema.StringAttribute{
+                               Optional:  true,
+                               Sensitive: true,
+                       },
+                       "secret_key": schema.StringAttribute{
+                               Optional:  true,
+                               Sensitive: true,
+                       },
+                       "config": schema.StringAttribute{
+                               Optional: true,
+                       },
+                       "profile": schema.StringAttribute{
+                               Optional: true,
+                       },
+                       "http_get_only": schema.BoolAttribute{
+                               Optional: true,
+                       },
+                       "timeout": schema.Int64Attribute{
+                               Optional: true,
+                       },
+               },
+       }
+}
+
+func (p *CloudstackProvider) Configure(ctx context.Context, req 
provider.ConfigureRequest, resp *provider.ConfigureResponse) {
+       apiUrl := os.Getenv("CLOUDSTACK_API_URL")
+       apiKey := os.Getenv("CLOUDSTACK_API_KEY")
+       secretKey := os.Getenv("CLOUDSTACK_SECRET_KEY")
+       httpGetOnly, _ := 
strconv.ParseBool(os.Getenv("CLOUDSTACK_HTTP_GET_ONLY"))
+       timeout, _ := strconv.ParseInt(os.Getenv("CLOUDSTACK_TIMEOUT"), 2, 64)
+
+       var data CloudstackProviderModel
+
+       resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+       if data.ApiUrl.ValueString() != "" {
+               apiUrl = data.ApiUrl.ValueString()
+       }
+
+       if data.ApiKey.ValueString() != "" {
+               apiKey = data.ApiKey.ValueString()
+       }
+
+       if data.SecretKey.ValueString() != "" {
+               secretKey = data.SecretKey.ValueString()
+       }
+
+       if data.HttpGetOnly.ValueBool() {
+               httpGetOnly = true
+       }
+
+       if data.Timeout.ValueInt64() != 0 {
+               timeout = data.Timeout.ValueInt64()
+       }
+
+       cfg := Config{
+               APIURL:      apiUrl,
+               APIKey:      apiKey,
+               SecretKey:   secretKey,
+               HTTPGETOnly: httpGetOnly,
+               Timeout:     timeout,
+       }
+
+       client, err := cfg.NewClient()
+
+       if err != nil {
+               resp.Diagnostics.AddError("cloudstack", fmt.Sprintf("failed to 
create client: %T", err))
+               return
+       }
+
+       resp.ResourceData = client
+       resp.DataSourceData = client
+}
+
+func (p *CloudstackProvider) ConfigValidators(ctx context.Context) 
[]provider.ConfigValidator {
+       return []provider.ConfigValidator{
+               providervalidator.Conflicting(
+                       path.MatchRoot("api_url"),
+                       path.MatchRoot("config"),
+               ),
+               providervalidator.Conflicting(
+                       path.MatchRoot("api_url"),
+                       path.MatchRoot("profile"),
+               ),
+               providervalidator.Conflicting(
+                       path.MatchRoot("api_key"),
+                       path.MatchRoot("config"),
+               ),
+               providervalidator.Conflicting(
+                       path.MatchRoot("api_key"),
+                       path.MatchRoot("profile"),
+               ),
+               providervalidator.Conflicting(
+                       path.MatchRoot("secret_key"),
+                       path.MatchRoot("config"),
+               ),
+               providervalidator.Conflicting(
+                       path.MatchRoot("secret_key"),
+                       path.MatchRoot("profile"),
+               ),
+       }
+}
+
+func (p *CloudstackProvider) Resources(ctx context.Context) []func() 
resource.Resource {
+       return []func() resource.Resource{}
+}
+
+func (p *CloudstackProvider) DataSources(ctx context.Context) []func() 
datasource.DataSource {
+       return []func() datasource.DataSource{}
+}
diff --git a/cloudstack/resources.go b/cloudstack/resources.go
index 0fedb70..22b2adc 100644
--- a/cloudstack/resources.go
+++ b/cloudstack/resources.go
@@ -20,6 +20,7 @@
 package cloudstack
 
 import (
+       "context"
        "fmt"
        "log"
        "regexp"
@@ -27,6 +28,7 @@ import (
        "time"
 
        "github.com/apache/cloudstack-go/v2/cloudstack"
+       "github.com/hashicorp/terraform-plugin-framework/resource"
        "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 )
 
@@ -170,3 +172,24 @@ func importStatePassthrough(d *schema.ResourceData, meta 
interface{}) ([]*schema
 
        return []*schema.ResourceData{d}, nil
 }
+
+type ResourceWithConfigure struct {
+       client *cloudstack.CloudStackClient
+}
+
+func (r *ResourceWithConfigure) Configure(ctx context.Context, req 
resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+       if req.ProviderData == nil {
+               return
+       }
+
+       client, ok := req.ProviderData.(*cloudstack.CloudStackClient)
+
+       if !ok {
+               resp.Diagnostics.AddError(
+                       "Unexpected Resource Configure Type",
+                       fmt.Sprintf("Expected *cloudstack.CloudStackClient, got 
%T", req.ProviderData),
+               )
+       }
+
+       r.client = client
+}
diff --git a/go.mod b/go.mod
index 7a55244..8952c35 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,9 @@ require (
        github.com/apache/cloudstack-go/v2 v2.16.1
        github.com/go-ini/ini v1.67.0
        github.com/hashicorp/go-multierror v1.1.1
-       github.com/hashicorp/terraform-plugin-go v0.22.0
+       github.com/hashicorp/terraform-plugin-framework v1.7.0
+       github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
+       github.com/hashicorp/terraform-plugin-go v0.22.1
        github.com/hashicorp/terraform-plugin-mux v0.15.0
        github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0
        github.com/hashicorp/terraform-plugin-testing v1.7.0
@@ -59,8 +61,8 @@ require (
        golang.org/x/tools v0.13.0 // indirect
        google.golang.org/appengine v1.6.8 // indirect
        google.golang.org/genproto/googleapis/rpc 
v0.0.0-20240123012728-ef4313101c80 // indirect
-       google.golang.org/grpc v1.62.0 // indirect
-       google.golang.org/protobuf v1.32.0 // indirect
+       google.golang.org/grpc v1.62.1 // indirect
+       google.golang.org/protobuf v1.33.0 // indirect
        gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 )
 
diff --git a/go.sum b/go.sum
index 010e7b8..51980c3 100644
--- a/go.sum
+++ b/go.sum
@@ -79,8 +79,12 @@ github.com/hashicorp/terraform-exec v0.20.0 
h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8J
 github.com/hashicorp/terraform-exec v0.20.0/go.mod 
h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw=
 github.com/hashicorp/terraform-json v0.21.0 
h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U=
 github.com/hashicorp/terraform-json v0.21.0/go.mod 
h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk=
-github.com/hashicorp/terraform-plugin-go v0.22.0 
h1:1OS1Jk5mO0f5hrziWJGXXIxBrMe2j/B8E+DVGw43Xmc=
-github.com/hashicorp/terraform-plugin-go v0.22.0/go.mod 
h1:mPULV91VKss7sik6KFEcEu7HuTogMLLO/EvWCuFkRVE=
+github.com/hashicorp/terraform-plugin-framework v1.7.0 
h1:wOULbVmfONnJo9iq7/q+iBOBJul5vRovaYJIu2cY/Pw=
+github.com/hashicorp/terraform-plugin-framework v1.7.0/go.mod 
h1:jY9Id+3KbZ17OMpulgnWLSfwxNVYSoYBQFTgsx044CI=
+github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 
h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc=
+github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod 
h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg=
+github.com/hashicorp/terraform-plugin-go v0.22.1 
h1:iTS7WHNVrn7uhe3cojtvWWn83cm2Z6ryIUDTRO0EV7w=
+github.com/hashicorp/terraform-plugin-go v0.22.1/go.mod 
h1:qrjnqRghvQ6KnDbB12XeZ4FluclYwptntoWCr9QaXTI=
 github.com/hashicorp/terraform-plugin-log v0.9.0 
h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
 github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod 
h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
 github.com/hashicorp/terraform-plugin-mux v0.15.0 
h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME=
@@ -224,12 +228,12 @@ google.golang.org/appengine v1.6.8 
h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs
 google.golang.org/appengine v1.6.8/go.mod 
h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 
h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
 google.golang.org/genproto/googleapis/rpc 
v0.0.0-20240123012728-ef4313101c80/go.mod 
h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
-google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
-google.golang.org/grpc v1.62.0/go.mod 
h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
+google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
+google.golang.org/grpc v1.62.1/go.mod 
h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod 
h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod 
h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.32.0 
h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
-google.golang.org/protobuf v1.32.0/go.mod 
h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.33.0 
h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod 
h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/main.go b/main.go
index 0ec5162..b018e6a 100644
--- a/main.go
+++ b/main.go
@@ -24,10 +24,12 @@ import (
        "flag"
        "log"
 
-       "github.com/hashicorp/terraform-plugin-go/tfprotov5"
-       "github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server"
-       "github.com/hashicorp/terraform-plugin-mux/tf5muxserver"
+       "github.com/hashicorp/terraform-plugin-go/tfprotov6"
+       "github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server"
+       "github.com/hashicorp/terraform-plugin-mux/tf6muxserver"
 
+       "github.com/hashicorp/terraform-plugin-framework/providerserver"
+       "github.com/hashicorp/terraform-plugin-mux/tf5to6server"
        
"github.com/terraform-providers/terraform-provider-cloudstack/cloudstack"
 )
 
@@ -39,23 +41,35 @@ func main() {
        flag.BoolVar(&debug, "debug", false, "set to true to run the provider 
with support for debuggers like delve")
        flag.Parse()
 
-       providers := []func() tfprotov5.ProviderServer{
-               cloudstack.New().GRPCProvider,
+       updatedSdkServer, err := tf5to6server.UpgradeServer(
+               ctx,
+               cloudstack.Provider().GRPCProvider,
+       )
+
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       providers := []func() tfprotov6.ProviderServer{
+               providerserver.NewProtocol6(cloudstack.New()),
+               func() tfprotov6.ProviderServer {
+                       return updatedSdkServer
+               },
        }
 
-       muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...)
+       muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)
 
        if err != nil {
                log.Fatal(err)
        }
 
-       var serveOpts []tf5server.ServeOpt
+       var serveOpts []tf6server.ServeOpt
 
        if debug {
-               serveOpts = append(serveOpts, tf5server.WithManagedDebug())
+               serveOpts = append(serveOpts, tf6server.WithManagedDebug())
        }
 
-       err = tf5server.Serve(
+       err = tf6server.Serve(
                "registry.terraform.io/cloudstack/cloudstack",
                muxServer.ProviderServer,
                serveOpts...,

Reply via email to