Skip to content

Commit

Permalink
feat: add gcp support
Browse files Browse the repository at this point in the history
  • Loading branch information
goncalo-rodrigues committed Jun 15, 2022
1 parent 0c073bc commit 758b8d4
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 9 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ env:
ARM_CLIENT_SECRET: ${{ secrets.AZURE_AD_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }}
GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}

jobs:
build:
Expand Down Expand Up @@ -56,7 +57,7 @@ jobs:
uses: actions/checkout@v3

- name: Test
run: "parallel --tagstring '{%}' TF_ACC=1 TF_VAR_cloud={} USER_SECRET_PREFIX={}-${{github.run_id}} go test ./multy/... -v -timeout 60m ::: aws azure"
run: "parallel --tagstring '{%}' TF_ACC=1 TF_VAR_cloud={} USER_SECRET_PREFIX={}-${{github.run_id}} go test ./multy/... -v -timeout 60m ::: aws azure gcp"
env:
TF_ACC: 1

Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ require (
github.com/hashicorp/terraform-plugin-go v0.8.0
github.com/hashicorp/terraform-plugin-log v0.3.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1
github.com/multycloud/multy v0.1.45
github.com/mitchellh/go-homedir v1.1.0
github.com/multycloud/multy v0.1.47
golang.org/x/exp v0.0.0-20220218215828-6cf2b201936e
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.28.0
)

//replace github.com/multycloud/multy v0.1.43 => ../multy
//replace github.com/multycloud/multy v0.1.45 => ../multy

require (
github.com/Azure/azure-sdk-for-go v59.2.0+incompatible // indirect
Expand Down Expand Up @@ -80,7 +81,6 @@ require (
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/cli v1.1.2 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.1.2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/multycloud/multy v0.1.45 h1:aRU0mn+0j/gwFkZPPEPYAtYDwwrBaEYBxx5kTj3EI9c=
github.com/multycloud/multy v0.1.45/go.mod h1:ZRk8zUP2oOf5Jv72aLbwxqyDchzAVWvlWWXzcUn90jw=
github.com/multycloud/multy v0.1.47 h1:kzcDYiqSHPQL8E1NmSwYiLtGZInZ8vtOhWATwb2d18E=
github.com/multycloud/multy v0.1.47/go.mod h1:ZRk8zUP2oOf5Jv72aLbwxqyDchzAVWvlWWXzcUn90jw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
Expand Down
13 changes: 13 additions & 0 deletions multy/common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type ProviderConfig struct {
ApiKey string
Aws *AwsConfig
Azure *AzureConfig
Gcp *GcpConfig
}

func (c *ProviderConfig) AddHeaders(ctx context.Context) (context.Context, error) {
Expand All @@ -34,6 +35,13 @@ func (c *ProviderConfig) AddHeaders(ctx context.Context) (context.Context, error
}
}

if c.Gcp != nil {
cloudCreds.GcpCreds = &credspb.GCPCredentials{
Credentials: c.Gcp.Credentials,
Project: c.Gcp.Project,
}
}

b, err := proto.Marshal(&cloudCreds)
if err != nil {
return nil, err
Expand Down Expand Up @@ -64,3 +72,8 @@ type AzureConfig struct {
ClientSecret string
TenantId string
}

type GcpConfig struct {
Project string
Credentials string
}
112 changes: 110 additions & 2 deletions multy/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"crypto/x509"
"fmt"
"github.com/mitchellh/go-homedir"
"io/ioutil"
"os"
"strings"
"terraform-provider-multy/multy/common"
Expand Down Expand Up @@ -84,6 +86,23 @@ var azureSchema = tfsdk.Attribute{
},
}),
}
var gcpSchema = tfsdk.Attribute{
Optional: true,
Description: "Credentials for Google Cloud. See how to authenticate through Service Principals in the [Google docs](https://cloud.google.com/compute/docs/authentication)",
Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
"credentials": {
Optional: true,
Description: "Either the path to or the contents of a service account key file in JSON format. " + common.HelperValueViaEnvVar("GOOGLE_APPLICATION_CREDENTIALS"),
Type: types.StringType,
Sensitive: true,
},
"project": {
Optional: true,
Description: "The project to manage resources in. " + common.HelperValueViaEnvVar("GOOGLE_CREDENTIALS"),
Type: types.StringType,
},
}),
}

func (p *Provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
Expand All @@ -97,6 +116,7 @@ func (p *Provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics)
},
"aws": awsSchema,
"azure": azureSchema,
"gcp": gcpSchema,
"server_endpoint": {
Type: types.StringType,
Description: "Address of the multy server. Defaults to `api.multy.dev`. If local, it will be run without SSL",
Expand All @@ -112,6 +132,7 @@ type providerData struct {
ServerEndpoint types.String `tfsdk:"server_endpoint"`
Aws *providerAwsConfig `tfsdk:"aws"`
Azure *providerAzureConfig `tfsdk:"azure"`
Gcp *providerGcpConfig `tfsdk:"gcp"`
}

type providerAwsConfig struct {
Expand All @@ -127,6 +148,11 @@ type providerAzureConfig struct {
TenantId types.String `tfsdk:"tenant_id"`
}

type providerGcpConfig struct {
Credentials types.String `tfsdk:"credentials"`
Project types.String `tfsdk:"project"`
}

func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderRequest, resp *tfsdk.ConfigureProviderResponse) {
var config providerData
diags := req.Config.Get(ctx, &config)
Expand Down Expand Up @@ -168,7 +194,7 @@ func (p *Provider) ConfigureProvider(ctx context.Context, config providerData, r
awsConfig, err = p.validateAwsConfig(ctx, config.Aws)
if err != nil {
resp.Diagnostics.AddError(
"Unable to connect to AWS",
"Unable to retrieve AWS credentials.",
err.Error(),
)
return
Expand All @@ -180,13 +206,25 @@ func (p *Provider) ConfigureProvider(ctx context.Context, config providerData, r
azureConfig, err = p.validateAzureConfig(config.Azure)
if err != nil {
resp.Diagnostics.AddError(
"Unable to connect to Azure",
"Unable to retrieve Azure credentials.",
err.Error(),
)
return
}
}

var gcpConfig *common.GcpConfig
if config.Gcp != nil {
gcpConfig, err = p.validateGcpConfig(config.Gcp)
if err != nil {
resp.Diagnostics.AddError(
"Unable to retrieve Azure credentials.",
err.Error(),
)
return
}
}

endpoint := "api.multy.dev:443"
if !config.ServerEndpoint.Null {
endpoint = config.ServerEndpoint.Value
Expand Down Expand Up @@ -221,6 +259,7 @@ func (p *Provider) ConfigureProvider(ctx context.Context, config providerData, r
c.ApiKey = apiKey
c.Aws = awsConfig
c.Azure = azureConfig
c.Gcp = gcpConfig

ctx, err = c.AddHeaders(ctx)
if err != nil {
Expand Down Expand Up @@ -367,3 +406,72 @@ func (p *Provider) validateAzureConfig(config *providerAzureConfig) (*common.Azu
// todo check if access is valid by calling sts.GetCallerIdentity
return &azureConfig, fmt.Errorf("azure credentials not set")
}

func (p *Provider) validateGcpConfig(config *providerGcpConfig) (*common.GcpConfig, error) {
var c common.GcpConfig

if config.Credentials.Unknown {
return nil, fmt.Errorf("cannot use unknown value as credentials")
}
if config.Project.Unknown {
return nil, fmt.Errorf("cannot use unknown value as project")
}

if !config.Project.Null {
c.Project = config.Project.Value
} else if project, ok := os.LookupEnv("GOOGLE_PROJECT"); ok {
c.Project = project
} else {
return nil, fmt.Errorf("google project is not set")
}

if !config.Credentials.Null {
contents, _, err := pathOrContents(config.Credentials.Value)
if err != nil {
return nil, err
}
c.Credentials = contents
} else if creds, ok := os.LookupEnv("GOOGLE_CREDENTIALS"); ok {
c.Credentials = creds
} else if credsFile, ok := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); ok {
contents, fromFile, err := pathOrContents(credsFile)
if err != nil {
return nil, err
}
if !fromFile {
return nil, fmt.Errorf(
"GOOGLE_APPLICATION_CREDENTIALS should contain a path to the JSON file, but the content was found" +
" instead. Did you mean to use GOOGLE_CREDENTIALS?")
}
c.Credentials = contents
} else {
return nil, fmt.Errorf("google credentials not set")
}

return &c, nil
}

func pathOrContents(pathOrContent string) (string, bool, error) {
if len(pathOrContent) == 0 {
return pathOrContent, false, nil
}

path := pathOrContent
if path[0] == '~' {
var err error
path, err = homedir.Expand(path)
if err != nil {
return path, true, err
}
}

if _, err := os.Stat(path); err == nil {
contents, err := ioutil.ReadFile(path)
if err != nil {
return string(contents), true, err
}
return string(contents), true, nil
}

return pathOrContent, false, nil
}
9 changes: 9 additions & 0 deletions multy/resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"golang.org/x/exp/slices"
"io/ioutil"
"os"
"path/filepath"
Expand Down Expand Up @@ -49,8 +50,15 @@ func TestAccResources(t *testing.T) {
}
}

var gcpTests = []string{"TestAccResources/virtual_network_gcp"}

func getTestFunc(path string, testString string, testNumber int) func(t *testing.T) {
return func(t *testing.T) {
if os.Getenv("TF_VAR_cloud") == "gcp" {
if !slices.Contains(gcpTests, t.Name()) {
t.Skip("GCP not implemented yet for this resource")
}
}
isError := strings.HasSuffix(filepath.Base(filepath.Dir(path)), "_failed")
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
Expand Down Expand Up @@ -79,6 +87,7 @@ func getProviderBlock(n int) string {
provider "multy" {
aws = {}
azure = {}
gcp = { project = "multy-project" }
server_endpoint = "localhost:8000"
api_key = "%s-%d"
}
Expand Down
4 changes: 2 additions & 2 deletions tests/resources/virtual_network/main.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
variable "cloud" {
type = string
default = "aws"
default = "gcp"
}

resource "multy_virtual_network" "vn" {
name = "vn_test2"
name = "vn-test2"
cidr_block = "10.0.0.0/16"
location = "eu_west_1"
cloud = var.cloud
Expand Down
4 changes: 3 additions & 1 deletion tests/resources/virtual_network/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ terraform {
}

provider "multy" {
aws = {}
gcp = {
project = "multy-project"
}
api_key = "aws-123-1"
server_endpoint = "localhost:8000"
}

0 comments on commit 758b8d4

Please sign in to comment.