Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the TF Provider to use the experimental Go SDK CloudOpsAPIClient Issue #120 #121

Merged
merged 6 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Interact with [Temporal Cloud](https://temporal.io/cloud) resources.

- [Terraform](https://developer.hashicorp.com/terraform/downloads) >= 1.0
- [Go](https://golang.org/doc/install) >= 1.19
- [Temporal SDK](https://github.com/temporalio/sdk-go) >= 1.26.0

### Building

Expand Down
44 changes: 29 additions & 15 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/temporalio/terraform-provider-temporalcloud
go 1.21

require (
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/terraform-plugin-docs v0.18.0
github.com/hashicorp/terraform-plugin-framework v1.3.5
Expand All @@ -13,9 +12,9 @@ require (
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.4.0
github.com/jpillora/maplock v0.0.0-20160420012925-5c725ac6e22a
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe
google.golang.org/grpc v1.61.1
google.golang.org/protobuf v1.32.0
go.temporal.io/api v1.38.0
go.temporal.io/sdk v1.29.1
google.golang.org/grpc v1.65.0
)

require (
Expand All @@ -30,10 +29,16 @@ require (
github.com/armon/go-radix v1.0.0 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/hashicorp/cli v1.1.6 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
Expand Down Expand Up @@ -62,26 +67,35 @@ require (
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/nexus-rpc/sdk-go v0.0.10 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/robfig/cron v1.2.0 // indirect
github.com/russross/blackfriday v1.6.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.9.0 // indirect
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/yuin/goldmark v1.6.0 // indirect
github.com/yuin/goldmark-meta v1.1.0 // indirect
github.com/zclconf/go-cty v1.14.1 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
180 changes: 145 additions & 35 deletions go.sum

Large diffs are not rendered by default.

47 changes: 0 additions & 47 deletions internal/client/apikey.go

This file was deleted.

95 changes: 19 additions & 76 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,16 @@ package client

import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/url"
"strings"

"time"

grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/retry"
"github.com/hashicorp/terraform-plugin-log/tflog"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"

cloudservicev1 "github.com/temporalio/terraform-provider-temporalcloud/proto/go/temporal/api/cloud/cloudservice/v1"
operationv1 "github.com/temporalio/terraform-provider-temporalcloud/proto/go/temporal/api/cloud/operation/v1"
cloudservicev1 "go.temporal.io/api/cloud/cloudservice/v1"
operationv1 "go.temporal.io/api/cloud/operation/v1"
"go.temporal.io/sdk/client"
)

const TemporalCloudAPIVersionHeader = "temporal-cloud-api-version"
Expand All @@ -48,54 +42,31 @@ var TemporalCloudAPIVersion = "2023-10-01-00"

// Client is a client for the Temporal Cloud API.
type Client struct {
cloudservicev1.CloudServiceClient
client.CloudOperationsClient
}

var (
_ cloudservicev1.CloudServiceClient = &Client{}
_ client.CloudOperationsClient = &Client{}
)

func NewConnectionWithAPIKey(addrStr string, allowInsecure bool, apiKey string, opts ...grpc.DialOption) (*Client, error) {
defaultOpts := []grpc.DialOption{
grpc.WithPerRPCCredentials(NewAPIKeyRPCCredential(apiKey, allowInsecure)),
grpc.WithChainUnaryInterceptor(
grpcretry.UnaryClientInterceptor(
grpcretry.WithBackoff(
grpcretry.BackoffExponentialWithJitter(250*time.Millisecond, 0.1),
),
grpcretry.WithMax(5),
),
),
}

opts = append(defaultOpts, opts...)

return newConnection(
addrStr,
allowInsecure,
opts...,
)
}
func NewConnectionWithAPIKey(addrStr string, allowInsecure bool, apiKey string) (*Client, error) {

func newConnection(addrStr string, allowInsecure bool, opts ...grpc.DialOption) (*Client, error) {
addr, err := url.Parse(addrStr)
if err != nil {
return nil, fmt.Errorf("unable to parse server address: %s", err)
}
defaultOpts := defaultDialOptions(addr, allowInsecure)
conn, err := grpc.Dial(
addr.String(),
append(defaultOpts, opts...)...,
)
var cClient client.CloudOperationsClient
var err error
cClient, err = client.DialCloudOperationsClient(context.Background(), client.CloudOperationsClientOptions{
Version: TemporalCloudAPIVersion,
Credentials: client.NewAPIKeyStaticCredentials(apiKey),
DisableTLS: allowInsecure,
HostPort: addrStr,
})
if err != nil {
return nil, fmt.Errorf("failed to dial `%s`: %v", addr.String(), err)
return nil, fmt.Errorf("failed to connect `%s`: %v", client.DefaultHostPort, err)
}

cloudClient := cloudservicev1.NewCloudServiceClient(conn)
return &Client{CloudServiceClient: cloudClient}, nil
return &Client{cClient}, nil
}

func AwaitAsyncOperation(ctx context.Context, client cloudservicev1.CloudServiceClient, op *operationv1.AsyncOperation) error {
func AwaitAsyncOperation(ctx context.Context, client client.CloudOperationsClient, op *operationv1.AsyncOperation) error {
if op == nil {
return fmt.Errorf("failed to await response: nil operation")
}
Expand All @@ -106,7 +77,7 @@ func AwaitAsyncOperation(ctx context.Context, client cloudservicev1.CloudService
for {
select {
case <-ticker.C:
status, err := client.GetAsyncOperation(ctx, &cloudservicev1.GetAsyncOperationRequest{
status, err := client.CloudService().GetAsyncOperation(ctx, &cloudservicev1.GetAsyncOperationRequest{
AsyncOperationId: op.Id,
})
if err != nil {
Expand Down Expand Up @@ -145,31 +116,3 @@ func AwaitAsyncOperation(ctx context.Context, client cloudservicev1.CloudService
}
}
}

func defaultDialOptions(addr *url.URL, allowInsecure bool) []grpc.DialOption {
var opts []grpc.DialOption

transport := credentials.NewTLS(&tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: addr.Hostname(),
})
if allowInsecure {
transport = insecure.NewCredentials()
}

opts = append(opts, grpc.WithTransportCredentials(transport))
opts = append(opts, grpc.WithUnaryInterceptor(setAPIVersionInterceptor))
return opts
}

func setAPIVersionInterceptor(
ctx context.Context,
method string,
req, reply interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
ctx = metadata.AppendToOutgoingContext(ctx, TemporalCloudAPIVersionHeader, strings.TrimSpace(TemporalCloudAPIVersion))
return invoker(ctx, method, req, reply, cc, opts...)
}
27 changes: 14 additions & 13 deletions internal/provider/namespace_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ import (

"github.com/temporalio/terraform-provider-temporalcloud/internal/client"
internaltypes "github.com/temporalio/terraform-provider-temporalcloud/internal/types"
cloudservicev1 "github.com/temporalio/terraform-provider-temporalcloud/proto/go/temporal/api/cloud/cloudservice/v1"
namespacev1 "github.com/temporalio/terraform-provider-temporalcloud/proto/go/temporal/api/cloud/namespace/v1"
cloudservicev1 "go.temporal.io/api/cloud/cloudservice/v1"
namespacev1 "go.temporal.io/api/cloud/namespace/v1"
)

const (
Expand All @@ -53,7 +53,8 @@ const (

type (
namespaceResource struct {
client cloudservicev1.CloudServiceClient
// client cloudservicev1.CloudServiceClient
client *client.Client
}

namespaceResourceModel struct {
Expand Down Expand Up @@ -121,11 +122,11 @@ func (r *namespaceResource) Configure(_ context.Context, req resource.ConfigureR
return
}

client, ok := req.ProviderData.(cloudservicev1.CloudServiceClient)
client, ok := req.ProviderData.(*client.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected cloudservicev1.CloudServiceClient, got: %T. Please report this issue to the provider developers.", req.ProviderData),
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

return
Expand Down Expand Up @@ -277,7 +278,7 @@ func (r *namespaceResource) Create(ctx context.Context, req resource.CreateReque
return
}
}
svcResp, err := r.client.CreateNamespace(ctx, &cloudservicev1.CreateNamespaceRequest{
svcResp, err := r.client.CloudService().CreateNamespace(ctx, &cloudservicev1.CreateNamespaceRequest{
Spec: &namespacev1.NamespaceSpec{
Name: plan.Name.ValueString(),
Regions: regions,
Expand All @@ -299,7 +300,7 @@ func (r *namespaceResource) Create(ctx context.Context, req resource.CreateReque
return
}

ns, err := r.client.GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
ns, err := r.client.CloudService().GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
Namespace: svcResp.Namespace,
})
if err != nil {
Expand All @@ -319,7 +320,7 @@ func (r *namespaceResource) Read(ctx context.Context, req resource.ReadRequest,
return
}

model, err := r.client.GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
model, err := r.client.CloudService().GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
Namespace: state.ID.ValueString(),
})
if err != nil {
Expand Down Expand Up @@ -348,7 +349,7 @@ func (r *namespaceResource) Update(ctx context.Context, req resource.UpdateReque
return
}

currentNs, err := r.client.GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
currentNs, err := r.client.CloudService().GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
Namespace: plan.ID.ValueString(),
})
if err != nil {
Expand All @@ -359,7 +360,7 @@ func (r *namespaceResource) Update(ctx context.Context, req resource.UpdateReque
if resp.Diagnostics.HasError() {
return
}
svcResp, err := r.client.UpdateNamespace(ctx, &cloudservicev1.UpdateNamespaceRequest{
svcResp, err := r.client.CloudService().UpdateNamespace(ctx, &cloudservicev1.UpdateNamespaceRequest{
Namespace: plan.ID.ValueString(),
Spec: &namespacev1.NamespaceSpec{
Name: plan.Name.ValueString(),
Expand All @@ -384,7 +385,7 @@ func (r *namespaceResource) Update(ctx context.Context, req resource.UpdateReque
return
}

ns, err := r.client.GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
ns, err := r.client.CloudService().GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
Namespace: plan.ID.ValueString(),
})
if err != nil {
Expand All @@ -410,7 +411,7 @@ func (r *namespaceResource) Delete(ctx context.Context, req resource.DeleteReque
return
}

currentNs, err := r.client.GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
currentNs, err := r.client.CloudService().GetNamespace(ctx, &cloudservicev1.GetNamespaceRequest{
Namespace: state.ID.ValueString(),
})
if err != nil {
Expand All @@ -419,7 +420,7 @@ func (r *namespaceResource) Delete(ctx context.Context, req resource.DeleteReque
}
ctx, cancel := context.WithTimeout(ctx, deleteTimeout)
defer cancel()
svcResp, err := r.client.DeleteNamespace(ctx, &cloudservicev1.DeleteNamespaceRequest{
svcResp, err := r.client.CloudService().DeleteNamespace(ctx, &cloudservicev1.DeleteNamespaceRequest{
Namespace: state.ID.ValueString(),
ResourceVersion: currentNs.GetNamespace().GetResourceVersion(),
})
Expand Down
Loading
Loading