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

initial ephemeral poc #11824

Draft
wants to merge 7 commits into
base: FEATURE-BRANCH-ephemeral-resource
Choose a base branch
from
Draft
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
6 changes: 6 additions & 0 deletions mmv1/provider/terraform/common~compile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@
-%>
'<%= dir -%>/functions/<%= fname.delete_suffix(".erb") -%>': 'third_party/terraform/functions/<%= fname -%>'
<% end -%>
<%
Dir["third_party/terraform/ephemeral/*.go.erb"].each do |file_path|
fname = file_path.split('/')[-1]
-%>
'<%= dir -%>/ephemeral/<%= fname.delete_suffix(".erb") -%>': 'third_party/terraform/ephemeral/<%= fname -%>'
<% end -%>
<%
Dir["third_party/terraform/scripts/**/*.erb"].each do |file_path|
fname = file_path.delete_prefix("third_party/terraform/")
Expand Down
7 changes: 7 additions & 0 deletions mmv1/provider/terraform/common~copy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@
'<%= dir -%>/functions/<%= fname -%>': 'third_party/terraform/functions/<%= fname -%>'
<% end -%>

<%
Dir["third_party/terraform/ephemeral/*.go"].each do |file_path|
fname = file_path.split('/')[-1]
-%>
'<%= dir -%>/ephemeral/<%= fname -%>': 'third_party/terraform/ephemeral/<%= fname -%>'
<% end -%>

<%
Dir["third_party/terraform/scripts/*.*"].each do |file_path|
next if file_path.end_with?('.erb')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
Comment on lines +1 to +4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package ephemeral

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-provider-google/google/fwtransport"
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
"google.golang.org/api/iamcredentials/v1"
)

var (
_ ephemeral.EphemeralResourceWithConfigure = &googleEphemeralServiceAccountAccessToken{}
_ ephemeral.EphemeralResource = &googleEphemeralServiceAccountAccessToken{}
)

func GoogleEphemeralServiceAccountAccessToken() ephemeral.EphemeralResource {
return &googleEphemeralServiceAccountAccessToken{}
}

type googleEphemeralServiceAccountAccessToken struct {
providerConfig *fwtransport.FrameworkProviderConfig
}

func (p *googleEphemeralServiceAccountAccessToken) Metadata(ctx context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_service_account_access_access_token"
}

func (p *googleEphemeralServiceAccountAccessToken) Configure(ctx context.Context, req ephemeral.ConfigureRequest, resp *ephemeral.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

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

// Required for accessing project, region, zone and tokenSource
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
// Required for accessing project, region, zone and tokenSource

p.providerConfig = data
}

type ephemeralServiceAccountAccessTokenModel struct {
TargetServiceAccount types.Int64 `tfsdk:"target_service_account"`
AccessToken types.String `tfsdk:"access_token"`
Scopes types.Set `tfsdk:"scopes"`
Delegates types.Set `tfsdk:"delegates"`
Lifetime types.String `tfsdk:"lifetime"`
}

func (p *googleEphemeralServiceAccountAccessToken) Schema(ctx context.Context, req ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"target_service_account": schema.StringAttribute{
Required: true,
// Validators: verify.ValidateRegexp("(" + strings.Join(verify.PossibleServiceAccountNames, "|") + ")"),
},
"access_token": schema.StringAttribute{
Sensitive: true,
Computed: true,
},
"lifetime": schema.StringAttribute{
Optional: true,
// Validators: verify.ValidateDuration(), // duration <=3600s; TODO: support validateDuration(min,max)
// Default: "3600s",
},
"scopes": schema.SetAttribute{
Required: true,
ElementType: types.StringType,
// ValidateFunc is not yet supported on lists or sets.
},
"delegates": schema.SetAttribute{
Optional: true,
ElementType: types.StringType,
},
},
}
}

func (p *googleEphemeralServiceAccountAccessToken) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
config := p.providerConfig
// userAgent, err := GenerateUserAgentString(config)
// if err != nil {
// resp.Diagnostics.Append(diag.NewErrorDiagnostic("Couldnt generate User Agent String", err.Error()))
// return
// }

service := config.NewIamCredentialsClient(config.UserAgent)
var data ephemeralServiceAccountAccessTokenModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)

if data.Lifetime.IsNull() {
data.Lifetime = types.StringValue("3600s")
}

name := fmt.Sprintf("projects/-/serviceAccounts/%s", data.TargetServiceAccount.String())
DelegatesSetValue, _ := data.Delegates.ToSetValue(ctx)
ScopesSetValue, _ := data.Scopes.ToSetValue(ctx)
tokenRequest := &iamcredentials.GenerateAccessTokenRequest{
Lifetime: data.Lifetime.String(),
Delegates: StringSet(DelegatesSetValue),
Scope: tpgresource.CanonicalizeServiceScopes(StringSet(ScopesSetValue)),
}

at, err := service.Projects.ServiceAccounts.GenerateAccessToken(name, tokenRequest).Do()
if err != nil {
resp.Diagnostics.Append(diag.NewErrorDiagnostic("Couldnt generate token", err.Error()))
return
}
data.AccessToken = basetypes.NewStringValue(at.AccessToken)

resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
}

func StringSet(d basetypes.SetValue) []string {

StringSlice := make([]string, 0)
for _, v := range d.Elements() {
StringSlice = append(StringSlice, v.String())
}
return StringSlice
}

// TODO: investigation will be needed to determine how to best approach generating
// a userAgentString with module name
// func GenerateUserAgentString(currentUserAgent string) (string, error) {
// var m transport_tpg.ProviderMeta

// err := d.GetProviderMeta(&m)
// if err != nil {
// return currentUserAgent, err
// }

// if m.ModuleName != "" {
// return strings.Join([]string{currentUserAgent, m.ModuleName}, " "), nil
// }

// return currentUserAgent, nil
// }
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package ephemeral

import (
"context"

"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func GoogleEphemeralServiceAccountIdToken() ephemeral.EphemeralResource {
return &googleEphemeralServiceAccountIdToken{}
}

type googleEphemeralServiceAccountIdToken struct{}

func (p *googleEphemeralServiceAccountIdToken) Metadata(ctx context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_service_account_access_id_token"
}

type ephemeralServiceAccountIdTokenModel struct {
Length types.Int64 `tfsdk:"length"`
Result types.String `tfsdk:"result"`
}

func (p *googleEphemeralServiceAccountIdToken) Schema(ctx context.Context, req ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generates a test string",
Attributes: map[string]schema.Attribute{
"length": schema.Int64Attribute{
Description: "The amount of test in string desired. The minimum value for length is 1.",
Required: true,
Validators: []validator.Int64{
int64validator.AtLeast(1),
},
},
"result": schema.StringAttribute{
Description: "The generated test string.",
Computed: true,
Sensitive: true,
},
},
}
}

func (p *googleEphemeralServiceAccountIdToken) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var data ephemeralServiceAccountIdTokenModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
result := ""
for range data.Length.ValueInt64() {
result += "test"
}

data.Result = types.StringValue(string(result))
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package ephemeral

import (
"context"

"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func GoogleEphemeralServiceAccountJwt() ephemeral.EphemeralResource {
return &googleEphemeralServiceAccountJwt{}
}

type googleEphemeralServiceAccountJwt struct{}

func (p *googleEphemeralServiceAccountJwt) Metadata(ctx context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_service_account_access_jwt"
}

type ephemeralServiceAccountJwtModel struct {
Length types.Int64 `tfsdk:"length"`
Result types.String `tfsdk:"result"`
}

func (p *googleEphemeralServiceAccountJwt) Schema(ctx context.Context, req ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generates a test string",
Attributes: map[string]schema.Attribute{
"length": schema.Int64Attribute{
Description: "The amount of test in string desired. The minimum value for length is 1.",
Required: true,
Validators: []validator.Int64{
int64validator.AtLeast(1),
},
},
"result": schema.StringAttribute{
Description: "The generated test string.",
Computed: true,
Sensitive: true,
},
},
}
}

func (p *googleEphemeralServiceAccountJwt) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var data ephemeralServiceAccountJwtModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
result := ""
for range data.Length.ValueInt64() {
result += "test"
}

data.Result = types.StringValue(string(result))
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package ephemeral

import (
"context"

"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func GoogleEphemeralServiceAccountKey() ephemeral.EphemeralResource {
return &googleEphemeralServiceAccountKey{}
}

type googleEphemeralServiceAccountKey struct{}

func (p *googleEphemeralServiceAccountKey) Metadata(ctx context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_service_account_access_key"
}

type ephemeralServiceAccountKeyModel struct {
Length types.Int64 `tfsdk:"length"`
Result types.String `tfsdk:"result"`
}

func (p *googleEphemeralServiceAccountKey) Schema(ctx context.Context, req ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generates a test string",
Attributes: map[string]schema.Attribute{
"length": schema.Int64Attribute{
Description: "The amount of test in string desired. The minimum value for length is 1.",
Required: true,
Validators: []validator.Int64{
int64validator.AtLeast(1),
},
},
"result": schema.StringAttribute{
Description: "The generated test string.",
Computed: true,
Sensitive: true,
},
},
}
}

func (p *googleEphemeralServiceAccountKey) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var data ephemeralServiceAccountKeyModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
result := ""
for range data.Length.ValueInt64() {
result += "test"
}

data.Result = types.StringValue(string(result))
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
}
12 changes: 12 additions & 0 deletions mmv1/third_party/terraform/fwprovider/framework_provider.go.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/function"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/metaschema"
Expand All @@ -15,6 +16,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"

eph "github.com/hashicorp/terraform-provider-google/google/ephemeral"
"github.com/hashicorp/terraform-provider-google/google/functions"
"github.com/hashicorp/terraform-provider-google/google/fwmodels"
"github.com/hashicorp/terraform-provider-google/google/fwtransport"
Expand Down Expand Up @@ -296,3 +298,13 @@ func (p *FrameworkProvider) Functions(_ context.Context) []func() function.Funct
functions.NewZoneFromIdFunction,
}
}

// EphemeralResources defines the resources that are of ephemral type implemented in the provider.
func (p *FrameworkProvider) EphemeralResources(_ context.Context) []func() ephemeral.EphemeralResource {
return []func() ephemeral.EphemeralResource{
eph.GoogleEphemeralServiceAccountAccessToken,
eph.GoogleEphemeralServiceAccountKey,
eph.GoogleEphemeralServiceAccountJwt,
eph.GoogleEphemeralServiceAccountIdToken,
}
}
Loading
Loading