-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(addon): generic addons support (#50)
- Loading branch information
Showing
14 changed files
with
436 additions
and
33 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,38 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "clevercloud_addon Resource - terraform-provider-clevercloud" | ||
subcategory: "" | ||
description: |- | ||
Manage any addon through the addon-provider API https://developers.clever-cloud.com/doc/extend/add-ons-api/#add-on-provider-api | ||
List of available providers: | ||
Mailpace https://mailpace.com/ | ||
--- | ||
|
||
# clevercloud_addon (Resource) | ||
|
||
Manage any addon through the [addon-provider API](https://developers.clever-cloud.com/doc/extend/add-ons-api/#add-on-provider-api) | ||
|
||
|
||
List of available providers: | ||
|
||
* [Mailpace](https://mailpace.com/) | ||
|
||
|
||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `name` (String) Name of the addon | ||
- `plan` (String) billing plan | ||
- `region` (String) Geographical region where the addon will be deployed (when relevant) | ||
- `third_party_provider` (String) Provider ID | ||
|
||
### Read-Only | ||
|
||
- `configurations` (Map of String, Sensitive) Any configuration exposed by the addon | ||
- `creation_date` (Number) Date of database creation | ||
- `id` (String) Generated unique identifier | ||
|
||
|
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,31 @@ | ||
package pkg | ||
|
||
import "go.clever-cloud.com/terraform-provider/pkg/tmp" | ||
|
||
func AddonProvidersAsList(providers []tmp.AddonProvider) []string { | ||
return Map(providers, func(provider tmp.AddonProvider) string { | ||
return provider.ID | ||
}) | ||
} | ||
|
||
func LookupAddonProvider(providers []tmp.AddonProvider, providerId string) *tmp.AddonProvider { | ||
return First(providers, func(provider tmp.AddonProvider) bool { | ||
return provider.ID == providerId | ||
}) | ||
} | ||
|
||
func LookupProviderPlan(provider *tmp.AddonProvider, planId string) *tmp.AddonPlan { | ||
if provider == nil { | ||
return nil | ||
} | ||
|
||
return First(provider.Plans, func(plan tmp.AddonPlan) bool { | ||
return plan.Slug == planId | ||
}) | ||
} | ||
|
||
func ProviderPlansAsList(provider *tmp.AddonProvider) []string { | ||
return Map(provider.Plans, func(plan tmp.AddonPlan) string { | ||
return plan.Slug | ||
}) | ||
} |
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 |
---|---|---|
@@ -1 +1 @@ | ||
CleverCloud provider allow you to interract with CleverCloud platform. | ||
CleverCloud provider allow you to interact with CleverCloud platform. |
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
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 @@ | ||
provider "clevercloud" { | ||
organisation = "%s" | ||
} |
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,21 @@ | ||
package addon | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"go.clever-cloud.dev/client" | ||
) | ||
|
||
type ResourceAddon struct { | ||
cc *client.Client | ||
org string | ||
} | ||
|
||
func NewResourceAddon() resource.Resource { | ||
return &ResourceAddon{} | ||
} | ||
|
||
func (r *ResourceAddon) Metadata(ctx context.Context, req resource.MetadataRequest, res *resource.MetadataResponse) { | ||
res.TypeName = req.ProviderTypeName + "_addon" | ||
} |
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,6 @@ | ||
Manage any addon through the [addon-provider API](https://developers.clever-cloud.com/doc/extend/add-ons-api/#add-on-provider-api) | ||
|
||
|
||
List of available providers: | ||
|
||
* [Mailpace](https://mailpace.com/) |
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,188 @@ | ||
package addon | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/attr" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/hashicorp/terraform-plugin-log/tflog" | ||
"go.clever-cloud.com/terraform-provider/pkg" | ||
"go.clever-cloud.com/terraform-provider/pkg/provider" | ||
"go.clever-cloud.com/terraform-provider/pkg/tmp" | ||
) | ||
|
||
// Weird behaviour, but TF can ask for a Resource without having configured a Provider (maybe for Meta and Schema) | ||
// So we need to handle the case there is no ProviderData | ||
func (r *ResourceAddon) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { | ||
tflog.Info(ctx, "ResourceAddon.Configure()") | ||
|
||
// Prevent panic if the provider has not been configured. | ||
if req.ProviderData == nil { | ||
return | ||
} | ||
|
||
provider, ok := req.ProviderData.(provider.Provider) | ||
if ok { | ||
r.cc = provider.Client() | ||
r.org = provider.Organization() | ||
} | ||
} | ||
|
||
// Create a new resource | ||
func (r *ResourceAddon) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { | ||
ad := Addon{} | ||
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &ad)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
addonsProvidersRes := tmp.GetAddonsProviders(ctx, r.cc) | ||
if addonsProvidersRes.HasError() { | ||
resp.Diagnostics.AddError("failed to get addon providers", addonsProvidersRes.Error().Error()) | ||
return | ||
} | ||
|
||
addonsProviders := addonsProvidersRes.Payload() | ||
|
||
provider := pkg.LookupAddonProvider(*addonsProviders, ad.ThirdPartyProvider.ValueString()) | ||
if provider == nil { | ||
resp.Diagnostics.AddError("This provider does not exists", fmt.Sprintf("available providers are: %s", strings.Join(pkg.AddonProvidersAsList(*addonsProviders), ", "))) | ||
return | ||
} | ||
|
||
plan := pkg.LookupProviderPlan(provider, ad.Plan.ValueString()) | ||
if plan == nil { | ||
resp.Diagnostics.AddError("This plan does not exists", "available plans are: "+strings.Join(pkg.ProviderPlansAsList(provider), ", ")) | ||
return | ||
} | ||
|
||
addonReq := tmp.AddonRequest{ | ||
Name: ad.Name.ValueString(), | ||
Plan: plan.ID, | ||
ProviderID: provider.ID, | ||
Region: ad.Region.ValueString(), | ||
} | ||
|
||
res := tmp.CreateAddon(ctx, r.cc, r.org, addonReq) | ||
if res.HasError() { | ||
resp.Diagnostics.AddError("failed to create addon", res.Error().Error()) | ||
return | ||
} | ||
|
||
ad.ID = pkg.FromStr(res.Payload().ID) | ||
ad.CreationDate = pkg.FromI(res.Payload().CreationDate) | ||
|
||
envRes := tmp.GetAddonEnv(ctx, r.cc, r.org, res.Payload().ID) | ||
if res.HasError() { | ||
resp.Diagnostics.AddError("failed to get addon env", res.Error().Error()) | ||
return | ||
} | ||
|
||
envAsMap := pkg.Reduce(*envRes.Payload(), map[string]attr.Value{}, func(acc map[string]attr.Value, v tmp.EnvVar) map[string]attr.Value { | ||
acc[v.Name] = pkg.FromStr(v.Value) | ||
return acc | ||
}) | ||
ad.Configurations = types.MapValueMust(types.StringType, envAsMap) | ||
|
||
resp.Diagnostics.Append(resp.State.Set(ctx, ad)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
} | ||
|
||
// Read resource information | ||
func (r *ResourceAddon) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { | ||
tflog.Info(ctx, "Addon READ", map[string]interface{}{"request": req}) | ||
|
||
var ad Addon | ||
diags := req.State.Get(ctx, &ad) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
addonRes := tmp.GetAddon(ctx, r.cc, r.org, ad.ID.ValueString()) | ||
if addonRes.HasError() { | ||
resp.Diagnostics.AddError("failed to get addon", addonRes.Error().Error()) | ||
return | ||
} | ||
|
||
addonEnvRes := tmp.GetAddonEnv(ctx, r.cc, r.org, ad.ID.ValueString()) | ||
if addonEnvRes.HasError() { | ||
resp.Diagnostics.AddError("failed to get addon env", addonEnvRes.Error().Error()) | ||
return | ||
} | ||
|
||
envAsMap := pkg.Reduce(*addonEnvRes.Payload(), map[string]attr.Value{}, func(acc map[string]attr.Value, v tmp.EnvVar) map[string]attr.Value { | ||
acc[v.Name] = pkg.FromStr(v.Value) | ||
return acc | ||
}) | ||
|
||
a := addonRes.Payload() | ||
ad.Name = pkg.FromStr(a.Name) | ||
ad.Plan = pkg.FromStr(a.Plan.Slug) | ||
ad.Region = pkg.FromStr(a.Region) | ||
ad.ThirdPartyProvider = pkg.FromStr(a.Provider.ID) | ||
ad.CreationDate = pkg.FromI(a.CreationDate) | ||
ad.Configurations = types.MapValueMust(types.StringType, envAsMap) | ||
|
||
/*addonPGRes := tmp.GetPostgreSQL(ctx, r.cc, pg.ID.ValueString()) | ||
if addonPGRes.IsNotFoundError() { | ||
diags = resp.State.SetAttribute(ctx, path.Root("id"), types.StringUnknown()) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
} | ||
if addonPGRes.HasError() { | ||
resp.Diagnostics.AddError("failed to get Postgres resource", addonPGRes.Error().Error()) | ||
}*/ | ||
|
||
diags = resp.State.Set(ctx, ad) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
} | ||
|
||
// Update resource | ||
func (r *ResourceAddon) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
// TODO | ||
} | ||
|
||
// Delete resource | ||
func (r *ResourceAddon) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
var ad Addon | ||
|
||
diags := req.State.Get(ctx, &ad) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
tflog.Info(ctx, "Addon DELETE", map[string]interface{}{"addon": ad}) | ||
|
||
res := tmp.DeleteAddon(ctx, r.cc, r.org, ad.ID.ValueString()) | ||
if res.IsNotFoundError() { | ||
resp.State.RemoveResource(ctx) | ||
return | ||
} | ||
if res.HasError() { | ||
resp.Diagnostics.AddError("failed to delete addon", res.Error().Error()) | ||
return | ||
} | ||
|
||
resp.State.RemoveResource(ctx) | ||
} | ||
|
||
// Import resource | ||
func (r *ResourceAddon) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { | ||
// Save the import identifier in the id attribute | ||
// and call Read() to fill fields | ||
attr := path.Root("id") | ||
resource.ImportStatePassthroughID(ctx, attr, req, resp) | ||
} |
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,52 @@ | ||
package addon | ||
|
||
import ( | ||
"context" | ||
_ "embed" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
type Addon struct { | ||
ID types.String `tfsdk:"id"` | ||
ThirdPartyProvider types.String `tfsdk:"third_party_provider"` | ||
Name types.String `tfsdk:"name"` | ||
CreationDate types.Int64 `tfsdk:"creation_date"` | ||
Plan types.String `tfsdk:"plan"` | ||
Region types.String `tfsdk:"region"` | ||
Configurations types.Map `tfsdk:"configurations"` | ||
} | ||
|
||
//go:embed resource_addon.md | ||
var resourcePostgresqlDoc string | ||
|
||
func (r ResourceAddon) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { | ||
resp.Schema = schema.Schema{ | ||
Version: 0, | ||
MarkdownDescription: resourcePostgresqlDoc, | ||
Attributes: map[string]schema.Attribute{ | ||
// customer provided | ||
"name": schema.StringAttribute{Required: true, MarkdownDescription: "Name of the addon"}, | ||
"plan": schema.StringAttribute{Required: true, MarkdownDescription: "billing plan"}, | ||
"region": schema.StringAttribute{Required: true, MarkdownDescription: "Geographical region where the addon will be deployed (when relevant)"}, | ||
"third_party_provider": schema.StringAttribute{Required: true, MarkdownDescription: "Provider ID"}, | ||
|
||
// provider | ||
"id": schema.StringAttribute{Computed: true, MarkdownDescription: "Generated unique identifier"}, | ||
"creation_date": schema.Int64Attribute{Computed: true, MarkdownDescription: "Date of database creation"}, | ||
"configurations": schema.MapAttribute{ | ||
Computed: true, | ||
Sensitive: true, | ||
MarkdownDescription: "Any configuration exposed by the addon", | ||
ElementType: types.StringType, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
// https://developer.hashicorp.com/terraform/plugin/framework/resources/state-upgrade#implementing-state-upgrade-support | ||
func (r ResourceAddon) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { | ||
return map[int64]resource.StateUpgrader{} | ||
} |
Oops, something went wrong.