Skip to content

Commit

Permalink
feat: docker
Browse files Browse the repository at this point in the history
  • Loading branch information
miton18 committed Jul 2, 2024
1 parent 093eb22 commit c5a4c43
Show file tree
Hide file tree
Showing 10 changed files with 488 additions and 19 deletions.
2 changes: 2 additions & 0 deletions pkg/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"go.clever-cloud.com/terraform-provider/pkg/resources/addon"
"go.clever-cloud.com/terraform-provider/pkg/resources/cellar"
"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/materiakv"
"go.clever-cloud.com/terraform-provider/pkg/resources/mongodb"
Expand All @@ -32,4 +33,5 @@ var Resources = []func() resource.Resource{
python.NewResourcePython,
scala.NewResourceScala(),
static.NewResourceStatic(),
docker.NewResourceDocker(),
}
23 changes: 23 additions & 0 deletions pkg/resources/docker/resource_docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package docker

import (
"context"

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

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

func NewResourceDocker() func() resource.Resource {
return func() resource.Resource {
return &ResourceDocker{}
}
}

func (r *ResourceDocker) Metadata(ctx context.Context, req resource.MetadataRequest, res *resource.MetadataResponse) {
res.TypeName = req.ProviderTypeName + "_docker"
}
3 changes: 3 additions & 0 deletions pkg/resources/docker/resource_docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Manage [Docker](https://) applications.

See [Docker product](https://www.clever-cloud.com/doc/getting-started/by-language/docker/) specification.
198 changes: 198 additions & 0 deletions pkg/resources/docker/resource_docker_crud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package docker

import (
"context"

"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/application"
"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 *ResourceDocker) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
tflog.Debug(ctx, "ResourceDocker.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.Debug(ctx, "AFTER CONFIGURED", map[string]interface{}{"cc": r.cc == nil, "org": r.org})
}

// Create a new resource
func (r *ResourceDocker) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
plan := Docker{}

resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

instance := application.LookupInstance(ctx, r.cc, "docker", "Docker", resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

vhosts := []string{}
resp.Diagnostics.Append(plan.AdditionalVHosts.ElementsAs(ctx, &vhosts, false)...)
if resp.Diagnostics.HasError() {
return
}

environment := plan.toEnv(ctx, resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

createAppReq := application.CreateReq{
Client: r.cc,
Organization: r.org,
Application: tmp.CreateAppRequest{
Name: plan.Name.ValueString(),
Deploy: "git",
Description: plan.Description.ValueString(),
InstanceType: instance.Type,
InstanceVariant: instance.Variant.ID,
InstanceVersion: instance.Version,
BuildFlavor: plan.BuildFlavor.ValueString(),
MinFlavor: plan.SmallestFlavor.ValueString(),
MaxFlavor: plan.BiggestFlavor.ValueString(),
MinInstances: plan.MinInstanceCount.ValueInt64(),
MaxInstances: plan.MaxInstanceCount.ValueInt64(),
StickySessions: plan.StickySessions.ValueBool(),
ForceHttps: application.FromForceHTTPS(plan.RedirectHTTPS.ValueBool()),
Zone: plan.Region.ValueString(),
CancelOnPush: false,
},
Environment: environment,
VHosts: vhosts,
Deployment: plan.toDeployment(),
}

createAppRes, diags := application.CreateApp(ctx, createAppReq)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

tflog.Debug(ctx, "BUILD FLAVOR RES"+createAppRes.Application.BuildFlavor.Name, map[string]interface{}{})
plan.ID = pkg.FromStr(createAppRes.Application.ID)
plan.DeployURL = pkg.FromStr(createAppRes.Application.DeployURL)
plan.VHost = pkg.FromStr(createAppRes.Application.Vhosts[0].Fqdn)

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

// Read resource information
func (r *ResourceDocker) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state Docker

resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

appPHP, diags := application.ReadApp(ctx, r.cc, r.org, state.ID.ValueString())
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if appPHP.AppIsDeleted {
resp.State.RemoveResource(ctx)
return
}

state.Name = pkg.FromStr(appPHP.App.Name)
state.Description = pkg.FromStr(appPHP.App.Description)
state.MinInstanceCount = pkg.FromI(int64(appPHP.App.Instance.MinInstances))
state.MaxInstanceCount = pkg.FromI(int64(appPHP.App.Instance.MaxInstances))
state.SmallestFlavor = pkg.FromStr(appPHP.App.Instance.MinFlavor.Name)
state.BiggestFlavor = pkg.FromStr(appPHP.App.Instance.MaxFlavor.Name)
state.Region = pkg.FromStr(appPHP.App.Zone)
state.DeployURL = pkg.FromStr(appPHP.App.DeployURL)

if appPHP.App.SeparateBuild {
state.BuildFlavor = pkg.FromStr(appPHP.App.BuildFlavor.Name)
} else {
state.BuildFlavor = types.StringNull()
}

vhosts := pkg.Map(appPHP.App.Vhosts, func(vhost tmp.Vhost) string {
return vhost.Fqdn
})
hasDefaultVHost := pkg.HasSome(vhosts, func(vhost string) bool {
return pkg.VhostCleverAppsRegExp.MatchString(vhost)
})
if hasDefaultVHost {
cleverapps := *pkg.First(vhosts, func(vhost string) bool {
return pkg.VhostCleverAppsRegExp.MatchString(vhost)
})
state.VHost = pkg.FromStr(cleverapps)
} else {
state.VHost = types.StringNull()
}

vhostsWithoutDefault := pkg.Filter(vhosts, func(vhost string) bool {
ok := pkg.VhostCleverAppsRegExp.MatchString(vhost)
return !ok
})
if len(vhostsWithoutDefault) > 0 {
state.AdditionalVHosts = pkg.FromListString(vhostsWithoutDefault)
} else {
state.AdditionalVHosts = types.ListNull(types.StringType)
}

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

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

// Delete resource
func (r *ResourceDocker) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state Docker

resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
tflog.Debug(ctx, "DOCKER DELETE", map[string]interface{}{"state": state})

res := tmp.DeleteApp(ctx, r.cc, r.org, state.ID.ValueString())
if res.IsNotFoundError() {
resp.State.RemoveResource(ctx)
return
}
if res.HasError() {
resp.Diagnostics.AddError("failed to delete app", res.Error().Error())
return
}

resp.State.RemoveResource(ctx)
}

// Import resource
func (r *ResourceDocker) 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)
}
Loading

0 comments on commit c5a4c43

Please sign in to comment.