Skip to content

Commit

Permalink
WIP: GCP Cloud Provider WIF support
Browse files Browse the repository at this point in the history
  • Loading branch information
aidy committed Feb 21, 2025
1 parent 84d5445 commit 92d6fbf
Show file tree
Hide file tree
Showing 11 changed files with 7,030 additions and 2 deletions.
1 change: 1 addition & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ lint:

generate:
cd tools; go generate ./...
cd internal; go generate ./...

fmt:
gofmt -s -w -e .
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module terraform-provider-tlspc
go 1.22.7

require (
github.com/Khan/genqlient v0.8.0
github.com/google/uuid v1.6.0
github.com/hashicorp/terraform-plugin-framework v1.14.1
github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0
)
Expand All @@ -22,6 +24,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/vektah/gqlparser/v2 v2.5.19 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/net v0.34.0 // indirect
Expand Down
12 changes: 10 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
github.com/Khan/genqlient v0.8.0 h1:Hd1a+E1CQHYbMEKakIkvBH3zW0PWEeiX6Hp1i2kP2WE=
github.com/Khan/genqlient v0.8.0/go.mod h1:hn70SpYjWteRGvxTwo0kfaqg4wxvndECGkfa1fdDdYI=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -53,10 +57,14 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vektah/gqlparser/v2 v2.5.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egplt7iSg=
github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
Expand Down
230 changes: 230 additions & 0 deletions internal/provider/cloudprovider_gcp_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// Copyright (c) Venafi, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"fmt"

"terraform-provider-tlspc/internal/tlspc"

"github.com/hashicorp/terraform-plugin-framework/path"
"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"
"github.com/hashicorp/terraform-plugin-framework/types"
)

var (
_ resource.Resource = &cloudProviderGCPResource{}
_ resource.ResourceWithConfigure = &cloudProviderGCPResource{}
_ resource.ResourceWithImportState = &cloudProviderGCPResource{}
)

type cloudProviderGCPResource struct {
client *tlspc.Client
}

func NewCloudProviderGCPResource() resource.Resource {
return &cloudProviderGCPResource{}
}

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

func (r *cloudProviderGCPResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"issuer_url": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"name": schema.StringAttribute{
Required: true,
},
"team": schema.StringAttribute{
Required: true,
},
"service_account_email": schema.StringAttribute{
Required: true,
},
"project_number": schema.Int64Attribute{
Required: true,
},
"workload_identity_pool_id": schema.StringAttribute{
Required: true,
},
"workload_identity_pool_provider_id": schema.StringAttribute{
Required: true,
},
},
}
}

func (r *cloudProviderGCPResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*tlspc.Client)

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

return
}

r.client = client
}

type cloudProviderGCPResourceModel struct {
ID types.String `tfsdk:"id"`
IssuerUrl types.String `tfsdk:"issuer_url"`
Name types.String `tfsdk:"name"`
Team types.String `tfsdk:"team"`
ServiceAccountEmail types.String `tfsdk:"service_account_email"`
ProjectNumber types.Int64 `tfsdk:"project_number"`
WorkloadIdentityPoolId types.String `tfsdk:"workload_identity_pool_id"`
WorkloadIdentityPoolProviderId types.String `tfsdk:"workload_identity_pool_provider_id"`
}

func (r *cloudProviderGCPResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan cloudProviderGCPResourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

p := tlspc.CloudProviderGCP{
Name: plan.Name.ValueString(),
Team: plan.Team.ValueString(),
ServiceAccountEmail: plan.ServiceAccountEmail.ValueString(),
ProjectNumber: plan.ProjectNumber.ValueInt64(),
WorkloadIdentityPoolId: plan.WorkloadIdentityPoolId.ValueString(),
WorkloadIdentityPoolProviderId: plan.WorkloadIdentityPoolProviderId.ValueString(),
}

created, err := r.client.CreateCloudProviderGCP(ctx, p)

if err != nil {
resp.Diagnostics.AddError(
"Error creating GCP Cloud Provider",
"Could not create GCP Cloud Provider: "+err.Error(),
)
return
}

plan.ID = types.StringValue(created.ID)
plan.IssuerUrl = types.StringValue(created.IssuerUrl)

diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
}

func (r *cloudProviderGCPResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state cloudProviderGCPResourceModel

diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

cp, err := r.client.GetCloudProviderGCP(ctx, state.ID.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Error retrieving GCP Cloud Provider",
"Could not find GCP Cloud Provider: "+err.Error(),
)
return
}

state.IssuerUrl = types.StringValue(cp.IssuerUrl)
state.Name = types.StringValue(cp.Name)
state.Team = types.StringValue(cp.Team)
state.ServiceAccountEmail = types.StringValue(cp.ServiceAccountEmail)
state.ProjectNumber = types.Int64Value(cp.ProjectNumber)
state.WorkloadIdentityPoolId = types.StringValue(cp.WorkloadIdentityPoolId)
state.WorkloadIdentityPoolProviderId = types.StringValue(cp.WorkloadIdentityPoolProviderId)

diags = resp.State.Set(ctx, state)
resp.Diagnostics.Append(diags...)
}

func (r *cloudProviderGCPResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var state, plan cloudProviderGCPResourceModel

diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
diags = req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

cp := tlspc.CloudProviderGCP{
ID: state.ID.ValueString(),
Name: plan.Name.ValueString(),
Team: plan.Team.ValueString(),
ServiceAccountEmail: plan.ServiceAccountEmail.ValueString(),
ProjectNumber: plan.ProjectNumber.ValueInt64(),
WorkloadIdentityPoolId: plan.WorkloadIdentityPoolId.ValueString(),
WorkloadIdentityPoolProviderId: plan.WorkloadIdentityPoolProviderId.ValueString(),
}

updated, err := r.client.UpdateCloudProviderGCP(ctx, cp)

if err != nil {
resp.Diagnostics.AddError(
"Error updating GCP Cloud Provider",
"Could not update GCP Cloud Provider, unexpected error: "+err.Error(),
)
return
}
plan.IssuerUrl = types.StringValue(updated.IssuerUrl)

diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
}

func (r *cloudProviderGCPResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state cloudProviderGCPResourceModel

diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

err := r.client.DeleteCloudProviderGCP(ctx, state.ID.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Error updating GCP Cloud Provider",
"Could not updating GCP Cloud Provider: "+err.Error(),
)
return
}
}

func (r *cloudProviderGCPResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Retrieve import ID and save to id attribute
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (p *tlspcProvider) Resources(ctx context.Context) []func() resource.Resourc
NewPluginResource,
NewCertificateTemplateResource,
NewApplicationResource,
NewCloudProviderGCPResource,
}
}

Expand Down
Loading

0 comments on commit 92d6fbf

Please sign in to comment.