Skip to content

Commit

Permalink
CDPCP-9883 - GCP credential create/delete/read (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
gregito authored Jul 26, 2023
1 parent 38d7bf5 commit d71019b
Show file tree
Hide file tree
Showing 9 changed files with 384 additions and 0 deletions.
32 changes: 32 additions & 0 deletions examples/resources/cdp_environments_gcp_credential/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Copyright 2023 Cloudera. All Rights Reserved.
#
# This file is licensed under the Apache License Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
#
# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
# OF ANY KIND, either express or implied. Refer to the License for the specific
# permissions and limitations governing your use of the file.

terraform {
required_providers {
cdp = {
source = "registry.terraform.io/cloudera/cdp"
}
}
}

resource "cdp_environments_gcp_credential" "example" {
credential_name = "cdp-gcp-credential"
credential_key = "<BASE64 content>"
description = "Example GCP Credentials"
}

output "credential_name" {
value = cdp_environments_gcp_credential.example.credential_name
}

output "credential_key" {
value = cdp_environments_gcp_credential.example.credential_key
sensitive = true
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ require (
github.com/hashicorp/terraform-registry-address v0.1.0 // indirect
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jessevdk/go-flags v1.5.0 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKL
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM=
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
Expand Down Expand Up @@ -630,6 +632,7 @@ golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
1 change: 1 addition & 0 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ func (p *CdpProvider) Resources(_ context.Context) []func() resource.Resource {
environments.NewAzureCredentialResource,
environments.NewAzureEnvironmentResource,
environments.NewGcpEnvironmentResource,
environments.NewGcpCredentialResource,
datalake.NewAwsDatalakeResource,
datalake.NewAzureDatalakeResource,
iam.NewGroupResource,
Expand Down
21 changes: 21 additions & 0 deletions resources/environments/model_gcp_credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2023 Cloudera. All Rights Reserved.
//
// This file is licensed under the Apache License Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
//
// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied. Refer to the License for the specific
// permissions and limitations governing your use of the file.

package environments

import "github.com/hashicorp/terraform-plugin-framework/types"

type gcpCredentialResourceModel struct {
ID types.String `tfsdk:"id"`
CredentialName types.String `tfsdk:"credential_name"`
CredentialKey types.String `tfsdk:"credential_key"`
Crn types.String `tfsdk:"crn"`
Description types.String `tfsdk:"description"`
}
152 changes: 152 additions & 0 deletions resources/environments/resource_gcp_credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2023 Cloudera. All Rights Reserved.
//
// This file is licensed under the Apache License Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
//
// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied. Refer to the License for the specific
// permissions and limitations governing your use of the file.

package environments

import (
"context"
"encoding/base64"
"github.com/hashicorp/terraform-plugin-log/tflog"

"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/cdp"
"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/environments/client/operations"
environmentsmodels "github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/environments/models"
"github.com/cloudera/terraform-provider-cdp/utils"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &gcpCredentialResource{}
)

type gcpCredentialResource struct {
client *cdp.Client
}

func NewGcpCredentialResource() resource.Resource {
return &gcpCredentialResource{}
}

func (r *gcpCredentialResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_environments_gcp_credential"
}

func (r *gcpCredentialResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
r.client = utils.GetCdpClientForResource(req, resp)
}

func (r *gcpCredentialResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
// Retrieve values from data
var data gcpCredentialResourceModel
diags := req.Plan.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

dec, err := base64.StdEncoding.DecodeString(data.CredentialKey.ValueString())

if err != nil {
diags.AddError("Unable to decode GCP credentials, please double check it.",
"Unable to decode GCP credential due to: "+err.Error())
return
}

credentialKey := string(dec)

params := operations.NewCreateGCPCredentialParamsWithContext(ctx)
params.WithInput(&environmentsmodels.CreateGCPCredentialRequest{
CredentialName: data.CredentialName.ValueStringPointer(),
Description: data.Description.ValueString(),
CredentialKey: &credentialKey,
})

responseOk, err := r.client.Environments.Operations.CreateGCPCredential(params)
if err != nil {
utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "creating GCP Credential")
return
}

data.Crn = types.StringPointerValue(responseOk.Payload.Credential.Crn)
data.ID = data.Crn

// Save data into Terraform state
diags = resp.State.Set(ctx, data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}

func (r *gcpCredentialResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
// Get current state
var state gcpCredentialResourceModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

// Get refreshed value from CDP
credentialName := state.CredentialName.ValueString()
params := operations.NewListCredentialsParamsWithContext(ctx)
params.WithInput(&environmentsmodels.ListCredentialsRequest{CredentialName: credentialName})
listCredentialsResp, err := r.client.Environments.Operations.ListCredentials(params)
if err != nil {
utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "reading GCP Credential")
return
}

// Overwrite items with refreshed state
credentials := listCredentialsResp.GetPayload().Credentials
if len(credentials) == 0 || *credentials[0].CredentialName != credentialName {
resp.State.RemoveResource(ctx) // deleted
return
}
c := credentials[0]

state.ID = types.StringPointerValue(c.Crn)
state.CredentialName = types.StringPointerValue(c.CredentialName)
state.Crn = types.StringPointerValue(c.Crn)
state.Description = types.StringValue(c.Description)

// Set refreshed state
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}

func (r *gcpCredentialResource) Update(ctx context.Context, _ resource.UpdateRequest, _ *resource.UpdateResponse) {
tflog.Warn(ctx, "Update operation is not implemented yet.")
}

func (r *gcpCredentialResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
// Retrieve values from state
var state gcpCredentialResourceModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

credentialName := state.CredentialName.ValueString()
params := operations.NewDeleteCredentialParamsWithContext(ctx)
params.WithInput(&environmentsmodels.DeleteCredentialRequest{CredentialName: &credentialName})
_, err := r.client.Environments.Operations.DeleteCredential(params)
if err != nil {
utils.AddEnvironmentDiagnosticsError(err, &resp.Diagnostics, "deleting GCP Credential")
return
}
}
55 changes: 55 additions & 0 deletions resources/environments/schema_gcp_credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2023 Cloudera. All Rights Reserved.
//
// This file is licensed under the Apache License Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
//
// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied. Refer to the License for the specific
// permissions and limitations governing your use of the file.

package environments

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
)

func (r *gcpCredentialResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "The GCP credential is used for authorization to provision resources such as compute instances within your cloud provider account.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"credential_name": schema.StringAttribute{
MarkdownDescription: "The name of the CDP credential.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"credential_key": schema.StringAttribute{
MarkdownDescription: "The GCP credential JSON content encoded in Base64",
Required: true,
Sensitive: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"description": schema.StringAttribute{
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"crn": schema.StringAttribute{
Computed: true,
},
},
}
}
30 changes: 30 additions & 0 deletions utils/file_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2023 Cloudera. All Rights Reserved.
//
// This file is licensed under the Apache License Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
//
// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied. Refer to the License for the specific
// permissions and limitations governing your use of the file.

package utils

import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
"os"
)

func ReadFileContent(ctx context.Context, path string) (*string, error) {
tflog.Info(ctx, fmt.Sprintf("About to read file on path: %s", path))
data, err := os.ReadFile(path)
if err != nil {
tflog.Warn(ctx, fmt.Sprintf("Error occurred during file read: %s", err.Error()))
return nil, err
}
tflog.Info(ctx, "Reading file was successful.")
content := string(data)
return &content, nil
}
Loading

0 comments on commit d71019b

Please sign in to comment.