forked from cloudflare/terraform-provider-cloudflare
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GIN-341: add teams certificates to Gateway and to Gateway account con…
…figuration
- Loading branch information
Showing
12 changed files
with
776 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:new-resource | ||
resource/teams-certificate: add teams certificates to Gateway and Gateway account configurations | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package gateway_app_types | ||
|
||
import ( | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
type GatewayCertificateModel struct { | ||
ID types.String `tfsdk:"id"` | ||
Custom types.Bool `tfsdk:"custom"` | ||
GatewayManaged types.Bool `tfsdk:"gateway_managed"` | ||
ValidityPeriodDays types.Int64 `tfsdk:"validity_period_days"` | ||
Activate types.Bool `tfsdk:"activate"` | ||
Enabled types.Bool `tfsdk:"enabled"` | ||
BindingStatus types.String `tfsdk:"binding_status"` | ||
ExpiresOn types.String `tfsdk:"expires_on"` | ||
} |
138 changes: 138 additions & 0 deletions
138
internal/framework/service/gateway_certificates/resource.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package r2_bucket | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
cfv1 "github.com/cloudflare/cloudflare-go" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/muxclient" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
// Ensure provider defined types fully satisfy framework interfaces. | ||
var _ resource.Resource = &GatewayCertificateModel{} | ||
var _ resource.ResourceWithImportState = &GatewayCertificateModel{} | ||
|
||
func NewResource() resource.Resource { | ||
return &GatewayCertificateModel{} | ||
} | ||
|
||
// GatewayCertificateResource defines the resource implementation. | ||
type GatewayCertificateResource struct { | ||
client *muxclient.Client | ||
} | ||
|
||
func (r *GatewayCertificateResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { | ||
resp.TypeName = req.ProviderTypeName + "_zero_trust_gateway_certificate" | ||
} | ||
|
||
func (r *GatewayCertificateResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { | ||
if req.ProviderData == nil { | ||
return | ||
} | ||
|
||
client, ok := req.ProviderData.(*muxclient.Client) | ||
|
||
if !ok { | ||
resp.Diagnostics.AddError( | ||
"unexpected resource configure type", | ||
fmt.Sprintf("Expected *muxclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), | ||
) | ||
|
||
return | ||
} | ||
|
||
r.client = client | ||
} | ||
|
||
func (r *GatewayCertificateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { | ||
var data *GatewayCertificateModel | ||
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
cert, err := r.client.V1.TeamsGenerateCertificate(ctx, cfv1.AccountIdentifier(data.AccountID.ValueString()), | ||
cfv1.TeamsCertificateCreateRequest{ | ||
ValidityPeriodDays: data.ValidityPeriodDays.ValueString(), | ||
}, | ||
) | ||
if err != nil { | ||
resp.Diagnostics.AddError("failed to generate Gateway certificate", err.Error()) | ||
return | ||
} | ||
data.ID = types.StringValue(cert.ID) | ||
data.BindingStatus = types.StringValue(cert.BindingStatus) | ||
data.Activate = types.Bool(false) | ||
data.Enabled = types.Bool(cert.Enabled) | ||
data.ExpiresOn = types.StringValue(cert.ExpiresOn) | ||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
} | ||
|
||
func (r *GatewayCertificateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { | ||
var data *GatewayCertificateModel | ||
|
||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
r2Bucket, err := r.client.V1.GetR2Bucket(ctx, cfv1.AccountIdentifier(data.AccountID.ValueString()), data.ID.ValueString()) | ||
if err != nil { | ||
resp.Diagnostics.AddError("failed reading R2 bucket", err.Error()) | ||
return | ||
} | ||
data.ID = types.StringValue(r2Bucket.Name) | ||
data.Name = types.StringValue(r2Bucket.Name) | ||
data.Location = types.StringValue(r2Bucket.Location) | ||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
} | ||
|
||
func (r *GatewayCertificateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
var data *GatewayCertificateModel | ||
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
resp.Diagnostics.AddError("failed to update R2 bucket", "Not implemented") | ||
} | ||
|
||
func (r *GatewayCertificateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
var data *GatewayCertificateModel | ||
|
||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
err := r.client.V1.DeleteR2Bucket(ctx, cfv1.AccountIdentifier(data.AccountID.ValueString()), data.ID.ValueString()) | ||
|
||
if err != nil { | ||
resp.Diagnostics.AddError("failed to delete R2 bucket", err.Error()) | ||
return | ||
} | ||
} | ||
|
||
func (r *GatewayCertificateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { | ||
idparts := strings.Split(req.ID, "/") | ||
if len(idparts) != 2 { | ||
resp.Diagnostics.AddError("error importing R2 bucket", "invalid ID specified. Please specify the ID as \"account_id/name\"") | ||
return | ||
} | ||
resp.Diagnostics.Append(resp.State.SetAttribute( | ||
ctx, path.Root("account_id"), idparts[0], | ||
)...) | ||
resp.Diagnostics.Append(resp.State.SetAttribute( | ||
ctx, path.Root("id"), idparts[1], | ||
)...) | ||
} |
191 changes: 191 additions & 0 deletions
191
internal/framework/service/gateway_certificates/resource_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
package r2_bucket_test | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"regexp" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go-v2/aws" | ||
"github.com/aws/aws-sdk-go-v2/config" | ||
"github.com/aws/aws-sdk-go-v2/credentials" | ||
"github.com/aws/aws-sdk-go-v2/service/s3" | ||
cfv1 "github.com/cloudflare/cloudflare-go" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/utils" | ||
"github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
resource.TestMain(m) | ||
} | ||
|
||
func init() { | ||
resource.AddTestSweepers("cloudflare_r2_bucket", &resource.Sweeper{ | ||
Name: "cloudflare_r2_bucket", | ||
F: func(region string) error { | ||
client, err := acctest.SharedV1Client() | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
|
||
accessKeyId := os.Getenv("CLOUDFLARE_R2_ACCESS_KEY_ID") | ||
accessKeySecret := os.Getenv("CLOUDFLARE_R2_ACCESS_KEY_SECRET") | ||
|
||
if accessKeyId == "" { | ||
return errors.New("CLOUDFLARE_R2_ACCESS_KEY_ID must be set for this acceptance test") | ||
} | ||
|
||
if accessKeyId == "" { | ||
return errors.New("CLOUDFLARE_R2_ACCESS_KEY_SECRET must be set for this acceptance test") | ||
} | ||
|
||
if err != nil { | ||
return fmt.Errorf("error establishing client: %w", err) | ||
} | ||
|
||
ctx := context.Background() | ||
buckets, err := client.ListR2Buckets(ctx, cfv1.AccountIdentifier(accountID), cfv1.ListR2BucketsParams{}) | ||
if err != nil { | ||
return fmt.Errorf("failed to fetch R2 buckets: %w", err) | ||
} | ||
|
||
for _, bucket := range buckets { | ||
// hard coded bucket name for Worker script acceptance tests | ||
// until we can break out the packages without cyclic errors. | ||
if bucket.Name == "bnfywlzwpt" { | ||
continue | ||
} | ||
|
||
r2Resolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) { | ||
return aws.Endpoint{ | ||
URL: fmt.Sprintf("https://%s.r2.cloudflarestorage.com", accountID), | ||
}, nil | ||
}) | ||
|
||
cfg, err := config.LoadDefaultConfig(context.TODO(), | ||
config.WithEndpointResolverWithOptions(r2Resolver), | ||
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyId, accessKeySecret, "")), | ||
config.WithRegion("auto"), | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
s3client := s3.NewFromConfig(cfg) | ||
listObjectsOutput, err := s3client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{ | ||
Bucket: &bucket.Name, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, object := range listObjectsOutput.Contents { | ||
_, err = s3client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{ | ||
Bucket: &bucket.Name, | ||
Key: object.Key, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
err = client.DeleteR2Bucket(ctx, cfv1.AccountIdentifier(accountID), bucket.Name) | ||
if err != nil { | ||
return fmt.Errorf("failed to delete R2 bucket %q: %w", bucket.Name, err) | ||
} | ||
} | ||
|
||
return nil | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccCloudflareR2Bucket_Basic(t *testing.T) { | ||
rnd := utils.GenerateRandomResourceName() | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
resourceName := "cloudflare_r2_bucket." + rnd | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { acctest.TestAccPreCheck(t) }, | ||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCheckCloudflareR2BucketBasic(rnd, accountID), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr(resourceName, "name", rnd), | ||
resource.TestCheckResourceAttr(resourceName, "id", rnd), | ||
resource.TestCheckResourceAttr(resourceName, "location", "ENAM"), | ||
), | ||
}, | ||
{ | ||
ResourceName: resourceName, | ||
ImportStateIdPrefix: fmt.Sprintf("%s/", accountID), | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccCloudflareR2Bucket_Minimum(t *testing.T) { | ||
rnd := utils.GenerateRandomResourceName() | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
resourceName := "cloudflare_r2_bucket." + rnd | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { acctest.TestAccPreCheck(t) }, | ||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCheckCloudflareR2BucketMinimum(rnd, accountID), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr(resourceName, "name", rnd), | ||
resource.TestCheckResourceAttr(resourceName, "id", rnd), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccCloudflareR2Bucket_InvalidLocation(t *testing.T) { | ||
rnd := utils.GenerateRandomResourceName() | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { acctest.TestAccPreCheck(t) }, | ||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCheckCloudflareR2BucketInvalidLocation(rnd, accountID), | ||
ExpectError: regexp.MustCompile("Error: Invalid Attribute Value Match"), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckCloudflareR2BucketMinimum(rnd, accountID string) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_r2_bucket" "%[1]s" { | ||
account_id = "%[2]s" | ||
name = "%[1]s" | ||
}`, rnd, accountID) | ||
} | ||
|
||
func testAccCheckCloudflareR2BucketBasic(rnd, accountID string) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_r2_bucket" "%[1]s" { | ||
account_id = "%[2]s" | ||
name = "%[1]s" | ||
location = "ENAM" | ||
}`, rnd, accountID) | ||
} | ||
|
||
func testAccCheckCloudflareR2BucketInvalidLocation(rnd, accountID string) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_r2_bucket" "%[1]s" { | ||
account_id = "%[2]s" | ||
name = "%[1]s" | ||
location = "foo" | ||
}`, rnd, accountID) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package gateway_app_types | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/datasource" | ||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" | ||
) | ||
|
||
func (d *CloudflareGatewayAppTypesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { | ||
resp.Schema = schema.Schema{ | ||
Description: "Use this data source to retrieve all Gateway application types for an account.", | ||
Attributes: map[string]schema.Attribute{ | ||
"account_id": schema.StringAttribute{ | ||
Required: true, | ||
Description: "The account ID to fetch Gateway App Types from.", | ||
}, | ||
"app_types": schema.ListNestedAttribute{ | ||
Computed: true, | ||
Description: "A list of Gateway App Types.", | ||
NestedObject: schema.NestedAttributeObject{ | ||
Attributes: map[string]schema.Attribute{ | ||
"id": schema.Int64Attribute{ | ||
Computed: true, | ||
Description: "The identifier for this app type. There is only one app type per ID.", | ||
}, | ||
"application_type_id": schema.Int64Attribute{ | ||
Computed: true, | ||
Description: "The identifier for the application type of this app.", | ||
}, | ||
"name": schema.StringAttribute{ | ||
Computed: true, | ||
Description: "The name of the app type.", | ||
}, | ||
"description": schema.StringAttribute{ | ||
Computed: true, | ||
Description: "A short summary of the app type.", | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} |
Oops, something went wrong.