Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
80 changes: 80 additions & 0 deletions linode/obj/bucket_accessor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package obj

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/linode/linodego"
"github.com/linode/terraform-provider-linode/v3/linode/helper"
)

// BucketAccessor provides the minimal information needed to resolve Object Storage
// access keys for an operation.
//
// Implementations typically come from either a resource plan/state model or a
// data model that can supply:
// - explicit access keys on the resource, or
// - a bucket label + region/cluster so temporary keys can be created.
type BucketAccessor interface {
ObjectStorageKeys() ObjectKeys
BucketLabel() string
RegionOrCluster(context.Context, *diag.Diagnostics) string
}

// GetObjectStorageKeys resolves Object Storage access keys used by OBJ resources.
//
// Resolution order:
// 1. Keys specified on the resource itself.
// 2. Keys specified in provider configuration (obj_access_key/obj_secret_key).
// 3. If enabled, temporary keys created via the Linode API (obj_use_temp_keys).
//
// When temporary keys are created, a non-nil teardown function is returned to
// delete them after the operation.
func GetObjectStorageKeys(
ctx context.Context,
data BucketAccessor,
client *linodego.Client,
config *helper.FrameworkProviderModel,
permissions string,
endpointType *linodego.ObjectStorageEndpointType,
diags *diag.Diagnostics,
) (*ObjectKeys, func()) {
result := data.ObjectStorageKeys()
if result.Ok() {
return &result, nil
}

result.AccessKey = config.ObjAccessKey.ValueString()
result.SecretKey = config.ObjSecretKey.ValueString()
if result.Ok() {
return &result, nil
}

if config.ObjUseTempKeys.ValueBool() {
clusterOrRegion := data.RegionOrCluster(ctx, diags)
if diags.HasError() {
return nil, nil
}

objKey := fwCreateTempKeys(ctx, client, data.BucketLabel(), clusterOrRegion, permissions, endpointType, diags)
if diags.HasError() {
return nil, nil
}

result.AccessKey = objKey.AccessKey
result.SecretKey = objKey.SecretKey

teardownTempKeysCleanUp := func() {
cleanUpTempKeys(ctx, client, objKey.ID)
}

return &result, teardownTempKeysCleanUp
}

diags.AddError(
"Keys Not Found",
"`access_key` and `secret_key` are Required but not Configured",
)

return nil, nil
}
68 changes: 13 additions & 55 deletions linode/obj/framework_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,66 +75,13 @@ func (data ResourceModel) getObjectBody(diags *diag.Diagnostics) (body *s3manage
return s3manager.ReadSeekCloser(bytes.NewReader(contentBytes))
}

func (data ResourceModel) GetObjectStorageKeys(
ctx context.Context,
client *linodego.Client,
config *helper.FrameworkProviderModel,
permissions string,
endpointType *linodego.ObjectStorageEndpointType,
diags *diag.Diagnostics,
) (*ObjectKeys, func()) {
result := &ObjectKeys{}

result.AccessKey = data.AccessKey.ValueString()
result.SecretKey = data.SecretKey.ValueString()

if result.Ok() {
return result, nil
}

result.AccessKey = config.ObjAccessKey.ValueString()
result.SecretKey = config.ObjSecretKey.ValueString()

if result.Ok() {
return result, nil
}

if config.ObjUseTempKeys.ValueBool() {
clusterOrRegion := data.GetRegionOrCluster(ctx, diags)
if diags.HasError() {
return nil, nil
}

objKey := fwCreateTempKeys(ctx, client, data.Bucket.ValueString(), clusterOrRegion, permissions, nil, diags)
if diags.HasError() {
return nil, nil
}

result.AccessKey = objKey.AccessKey
result.SecretKey = objKey.SecretKey

teardownTempKeysCleanUp := func() {
cleanUpTempKeys(ctx, client, objKey.ID)
}

return result, teardownTempKeysCleanUp
}

diags.AddError(
"Keys Not Found",
"`access_key` and `secret_key` are Required but not Configured",
)

return nil, nil
}

func (plan *ResourceModel) ComputeEndpointIfUnknown(ctx context.Context, client *linodego.Client, diags *diag.Diagnostics) {
if !plan.Endpoint.IsUnknown() {
return
}

bucketName := plan.Bucket.ValueString()
regionOrCluster := plan.GetRegionOrCluster(ctx, diags)
regionOrCluster := plan.RegionOrCluster(ctx, diags)
if diags.HasError() {
return
}
Expand All @@ -161,7 +108,7 @@ func (data *ResourceModel) GenerateObjectStorageObjectID(apply bool, preserveKno
return id
}

func (data ResourceModel) GetRegionOrCluster(ctx context.Context, diags *diag.Diagnostics) string {
func (data ResourceModel) RegionOrCluster(ctx context.Context, diags *diag.Diagnostics) string {
if !data.Region.IsNull() && !data.Region.IsUnknown() {
return data.Region.ValueString()
} else {
Expand All @@ -174,6 +121,17 @@ func (data ResourceModel) GetRegionOrCluster(ctx context.Context, diags *diag.Di
return data.Cluster.ValueString()
}

func (data ResourceModel) ObjectStorageKeys() ObjectKeys {
return ObjectKeys{
AccessKey: data.AccessKey.ValueString(),
SecretKey: data.SecretKey.ValueString(),
}
}

func (data ResourceModel) BucketLabel() string {
return data.Bucket.ValueString()
}

func (data *ResourceModel) FlattenObject(
obj s3.HeadObjectOutput, preserveKnown bool,
) {
Expand Down
2 changes: 1 addition & 1 deletion linode/obj/framework_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ func (r *Resource) Delete(
func populateLogAttributes(ctx context.Context, model ResourceModel, diags *diag.Diagnostics) context.Context {
return helper.SetLogFieldBulk(ctx, map[string]any{
"bucket": model.Bucket.ValueString(),
"region_or_cluster": model.GetRegionOrCluster(ctx, diags),
"region_or_cluster": model.RegionOrCluster(ctx, diags),
"object_key": model.Key.ValueString(),
})
}
2 changes: 1 addition & 1 deletion linode/obj/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func getS3ClientFromModel(
endpointType *linodego.ObjectStorageEndpointType,
diags *diag.Diagnostics,
) (*s3.Client, func()) {
keys, teardownKeys := data.GetObjectStorageKeys(ctx, client, config, permission, endpointType, diags)
keys, teardownKeys := GetObjectStorageKeys(ctx, data, client, config, permission, endpointType, diags)
if diags.HasError() {
return nil, teardownKeys
}
Expand Down
26 changes: 13 additions & 13 deletions linode/objbucket/framework_datasource_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import (

var frameworkDatasourceSchema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The id of this bucket.",
Computed: true,
},
"label": schema.StringAttribute{
Description: "The name of this bucket.",
Required: true,
},
"cluster": schema.StringAttribute{
Description: "The ID of the Object Storage Cluster this bucket is in.",
DeprecationMessage: "The cluster attribute has been deprecated, please consider " +
Expand Down Expand Up @@ -41,24 +49,11 @@ var frameworkDatasourceSchema = schema.Schema{
Description: "The S3 endpoint URL of the bucket, based on the `endpoint_type` and `region`.",
Computed: true,
},
"created": schema.StringAttribute{
Description: "When this bucket was created.",
CustomType: timetypes.RFC3339Type{},
Computed: true,
},
"hostname": schema.StringAttribute{
Description: "The hostname where this bucket can be accessed." +
"This hostname can be accessed through a browser if the bucket is made public.",
Computed: true,
},
"id": schema.StringAttribute{
Description: "The id of this bucket.",
Computed: true,
},
"label": schema.StringAttribute{
Description: "The name of this bucket.",
Required: true,
},
"objects": schema.Int64Attribute{
Description: "The number of objects stored in this bucket.",
Computed: true,
Expand All @@ -67,5 +62,10 @@ var frameworkDatasourceSchema = schema.Schema{
Description: "The size of the bucket in bytes.",
Computed: true,
},
"created": schema.StringAttribute{
Description: "When this bucket was created.",
CustomType: timetypes.RFC3339Type{},
Computed: true,
},
},
}
15 changes: 15 additions & 0 deletions linode/objbucket/framework_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ type BaseModel struct {
Created timetypes.RFC3339 `tfsdk:"created"`
}

func (data BaseModel) RegionOrCluster() (regionOrCluster string) {
if data.Region.ValueString() != "" {
regionOrCluster = data.Region.ValueString()
} else {
regionOrCluster = data.Cluster.ValueString()
}
return
}

func (data BaseModel) BucketLabel() (label string) {
// Label is a required attribute, so there is no need
// to check whether it is null or unknown.
return data.Label.ValueString()
}

type DataSourceModel struct {
BaseModel
}
Expand Down