Skip to content

Commit

Permalink
feat: keycloak
Browse files Browse the repository at this point in the history
Closes #86
  • Loading branch information
miton18 committed Nov 6, 2024
1 parent 0049392 commit 0277412
Show file tree
Hide file tree
Showing 14 changed files with 396 additions and 44 deletions.
18 changes: 9 additions & 9 deletions docs/resources/java_war.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
page_title: "clevercloud_java_war Resource - terraform-provider-clevercloud"
subcategory: ""
description: |-
Manage Java https://www.java.com/en/ applications.
Manage Java applications.
See Java product https://www.clever-cloud.com/doc/getting-started/by-language/java/ specification.
Example usage
Basic
terraform
resource "clevercloud_java_war" "myapp" {
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
}
Advanced
terraform
resource "clevercloud_java_war" "myapp" {
name = "tf-myapp"
region = "par"
Expand Down
30 changes: 30 additions & 0 deletions docs/resources/keycloak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "clevercloud_keycloak Resource - terraform-provider-clevercloud"
subcategory: ""
description: |-
Manage Keycloak
---

# clevercloud_keycloak (Resource)

Manage Keycloak



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) Name of the service

### Optional

- `region` (String) Geographical region where the data will be stored

### Read-Only

- `creation_date` (Number) Date of database creation
- `host` (String) URL to access Keycloak
- `id` (String) Generated unique identifier
16 changes: 8 additions & 8 deletions docs/resources/nodejs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ description: |-
See NodeJS product https://www.clever-cloud.com/nodejs-hosting/ specification.
Example usage
Basic
terraform
resource "clevercloud_nodejs" "myapp" {
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
}
Advanced
terraform
resource "clevercloud_nodejs" "myapp" {
name = "tf-myapp"
region = "par"
Expand Down
16 changes: 8 additions & 8 deletions docs/resources/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ description: |-
See Python product https://www.clever-cloud.com/python-hosting/ specification.
Example usage
Basic
terraform
resource "clevercloud_python" "myapp" {
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
}
Advanced
terraform
resource "clevercloud_python" "myapp" {
name = "tf-myapp"
region = "par"
Expand Down
18 changes: 9 additions & 9 deletions docs/resources/scala.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
page_title: "clevercloud_scala Resource - terraform-provider-clevercloud"
subcategory: ""
description: |-
Manage Scala https://www.scala-lang.org/ applications.
Manage Scala applications.
See Scala product https://www.clever-cloud.com/scala-hosting/ specification.
Example usage
Basic
terraform
resource "clevercloud_scala" "myapp" {
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
}
Advanced
terraform
resource "clevercloud_scala" "myapp" {
name = "tf-myapp"
region = "par"
Expand Down
16 changes: 8 additions & 8 deletions docs/resources/static.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ description: |-
See Static product https://www.clever-cloud.com/doc/deploy/application/static/static/ specification.
Example usage
Basic
terraform
resource "clevercloud_static" "myapp" {
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
name = "tf-myapp"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
}
Advanced
terraform
resource "clevercloud_static" "myapp" {
name = "tf-myapp"
region = "par"
Expand Down
8 changes: 6 additions & 2 deletions pkg/provider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package pkg

import "go.clever-cloud.com/terraform-provider/pkg/tmp"
import (
"strings"

"go.clever-cloud.com/terraform-provider/pkg/tmp"
)

func AddonProvidersAsList(providers []tmp.AddonProvider) []string {
return Map(providers, func(provider tmp.AddonProvider) string {
Expand All @@ -20,7 +24,7 @@ func LookupProviderPlan(provider *tmp.AddonProvider, planId string) *tmp.AddonPl
}

return First(provider.Plans, func(plan tmp.AddonPlan) bool {
return plan.Slug == planId
return strings.EqualFold(plan.Slug, planId)
})
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"go.clever-cloud.com/terraform-provider/pkg/resources/cellar/bucket"
"go.clever-cloud.com/terraform-provider/pkg/resources/docker"
"go.clever-cloud.com/terraform-provider/pkg/resources/java"
"go.clever-cloud.com/terraform-provider/pkg/resources/keycloak"
"go.clever-cloud.com/terraform-provider/pkg/resources/materiakv"
"go.clever-cloud.com/terraform-provider/pkg/resources/mongodb"
"go.clever-cloud.com/terraform-provider/pkg/resources/nodejs"
Expand All @@ -34,4 +35,5 @@ var Resources = []func() resource.Resource{
scala.NewResourceScala(),
static.NewResourceStatic(),
docker.NewResourceDocker,
keycloak.NewResourceKeycloak,
}
162 changes: 162 additions & 0 deletions pkg/resources/keycloak/crud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package keycloak

import (
"context"
"fmt"
"strings"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"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 *ResourceKeycloak) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
tflog.Debug(ctx, "ResourceKeycloak.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()
}

tflog.Warn(ctx, "Keycloak product is still in beta, use it with care")
}

// Create a new resource
func (r *ResourceKeycloak) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
kc := Keycloak{}

resp.Diagnostics.Append(req.Plan.Get(ctx, &kc)...)
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, "keycloak")

plan := pkg.LookupProviderPlan(provider, "beta")
if plan == nil {
resp.Diagnostics.AddError("This plan does not exists", "available plans are: "+strings.Join(pkg.ProviderPlansAsList(provider), ", "))
return
}

addonReq := tmp.AddonRequest{
Name: kc.Name.ValueString(),
Plan: plan.ID,
ProviderID: "keycloak",
Region: kc.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
}

kc.ID = pkg.FromStr(res.Payload().RealID)
kc.CreationDate = pkg.FromI(res.Payload().CreationDate)

resp.Diagnostics.Append(resp.State.Set(ctx, kc)...)
if resp.Diagnostics.HasError() {
return
}

kcEnvRes := tmp.GetAddonEnv(ctx, r.cc, r.org, kc.ID.ValueString())
if kcEnvRes.HasError() {
resp.Diagnostics.AddError("failed to get Keycloak connection infos", kcEnvRes.Error().Error())
return
}

kcEnv := *kcEnvRes.Payload()
tflog.Debug(ctx, "API response", map[string]interface{}{
"payload": fmt.Sprintf("%+v", kcEnv),
})

hostEnvVar := pkg.First(kcEnv, func(v tmp.EnvVar) bool {
return v.Name == "CC_KEYCLOAK_URL"
})
if hostEnvVar == nil {
resp.Diagnostics.AddError("cannot get Keycloak infos", "missing CC_KEYCLOAK_URL env var on created addon")
return
}

kc.Host = pkg.FromStr(hostEnvVar.Value)

resp.Diagnostics.Append(resp.State.Set(ctx, kc)...)
if resp.Diagnostics.HasError() {
return
}
}

// Read resource information
func (r *ResourceKeycloak) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
tflog.Debug(ctx, "Keycloak READ", map[string]interface{}{"request": req})

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

// TODO

diags = resp.State.Set(ctx, kc)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}

// Update resource
func (r *ResourceKeycloak) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
// TODO
}

// Delete resource
func (r *ResourceKeycloak) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
kc := Keycloak{}

diags := req.State.Get(ctx, &kc)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Debug(ctx, "Keycloak DELETE", map[string]interface{}{"keycloak": kc})

res := tmp.DeleteAddon(ctx, r.cc, r.org, kc.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 *ResourceKeycloak) 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)
}
1 change: 1 addition & 0 deletions pkg/resources/keycloak/doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Manage Keycloak
21 changes: 21 additions & 0 deletions pkg/resources/keycloak/keycloak.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package keycloak

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/resource"
"go.clever-cloud.dev/client"
)

type ResourceKeycloak struct {
cc *client.Client
org string
}

func NewResourceKeycloak() resource.Resource {
return &ResourceKeycloak{}
}

func (r *ResourceKeycloak) Metadata(ctx context.Context, req resource.MetadataRequest, res *resource.MetadataResponse) {
res.TypeName = req.ProviderTypeName + "_keycloak"
}
Loading

0 comments on commit 0277412

Please sign in to comment.