101 lines
3.0 KiB
Go
101 lines
3.0 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|||
|
|
// SPDX-License-Identifier: MPL-2.0
|
||
|
|
|
||
|
|
package host
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
compute "cloud.google.com/go/compute/apiv1"
|
||
|
|
computepb "cloud.google.com/go/compute/apiv1/computepb"
|
||
|
|
"github.com/googleapis/gax-go/v2"
|
||
|
|
"github.com/hashicorp/boundary-plugin-gcp/internal/credential"
|
||
|
|
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||
|
|
"google.golang.org/api/option"
|
||
|
|
"google.golang.org/protobuf/types/known/structpb"
|
||
|
|
)
|
||
|
|
|
||
|
|
type InstancesAPI interface {
|
||
|
|
List(ctx context.Context, req *computepb.ListInstancesRequest, opts ...gax.CallOption) *compute.InstanceIterator
|
||
|
|
}
|
||
|
|
|
||
|
|
type gcpCatalogPersistedState struct {
|
||
|
|
// CredentialsConfig is the configuration for the GCP credentials
|
||
|
|
*credential.PersistedState
|
||
|
|
// testInstancesAPIFunc provides a way to provide a factory for a mock Instances client
|
||
|
|
testInstancesAPIFunc instancesAPIFunc
|
||
|
|
}
|
||
|
|
|
||
|
|
type instancesAPIFunc func(...*credential.Config) (InstancesAPI, error)
|
||
|
|
|
||
|
|
type gcpCatalogPersistedStateOption func(s *gcpCatalogPersistedState) error
|
||
|
|
|
||
|
|
func withCredentials(x *credential.PersistedState) gcpCatalogPersistedStateOption {
|
||
|
|
return func(s *gcpCatalogPersistedState) error {
|
||
|
|
if s.PersistedState != nil {
|
||
|
|
return errors.New("gcp credentials already set")
|
||
|
|
}
|
||
|
|
|
||
|
|
s.PersistedState = x
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func withTestInstancesAPIFunc(f instancesAPIFunc) gcpCatalogPersistedStateOption {
|
||
|
|
return func(s *gcpCatalogPersistedState) error {
|
||
|
|
if s.testInstancesAPIFunc != nil {
|
||
|
|
return errors.New("test API function already set")
|
||
|
|
}
|
||
|
|
|
||
|
|
s.testInstancesAPIFunc = f
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func newGCPCatalogPersistedState(opts ...gcpCatalogPersistedStateOption) (*gcpCatalogPersistedState, error) {
|
||
|
|
s := new(gcpCatalogPersistedState)
|
||
|
|
for _, opt := range opts {
|
||
|
|
if err := opt(s); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return s, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// ToProto converts the state to HostCatalogPersisted proto message.
|
||
|
|
func (s *gcpCatalogPersistedState) toProto() (*pb.HostCatalogPersisted, error) {
|
||
|
|
data, err := structpb.NewStruct(s.ToMap())
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("error converting state to structpb.Struct: %w", err)
|
||
|
|
}
|
||
|
|
return &pb.HostCatalogPersisted{Secrets: data}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// InstancesClient returns a configured Instances client based on the session
|
||
|
|
// information stored in the state.
|
||
|
|
func (s *gcpCatalogPersistedState) InstancesClient(
|
||
|
|
ctx context.Context,
|
||
|
|
opts ...option.ClientOption,
|
||
|
|
) (InstancesAPI, error) {
|
||
|
|
if s.testInstancesAPIFunc != nil {
|
||
|
|
return s.testInstancesAPIFunc(s.CredentialsConfig)
|
||
|
|
}
|
||
|
|
creds, err := s.CredentialsConfig.GenerateCredentials(ctx)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("error generation GCP credentials: %w", err)
|
||
|
|
}
|
||
|
|
if creds == nil {
|
||
|
|
return nil, fmt.Errorf("nil gcp credentials")
|
||
|
|
}
|
||
|
|
clientOptions := []option.ClientOption{option.WithTokenSource(creds.TokenSource)}
|
||
|
|
clientOptions = append(clientOptions, opts...)
|
||
|
|
|
||
|
|
client, err := compute.NewInstancesRESTClient(ctx, clientOptions...)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("error creating instances client: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
return client, nil
|
||
|
|
}
|