Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Commit

Permalink
Add initial implementation for common controller
Browse files Browse the repository at this point in the history
Signed-off-by: Hasan Turken <turkenh@gmail.com>
  • Loading branch information
turkenh committed Aug 10, 2021
1 parent 65ef979 commit 17c1f04
Show file tree
Hide file tree
Showing 8 changed files with 1,091 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ linters-settings:
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: github.com/crossplane/crossplane
local-prefixes: github.com/crossplane-contrib/terrajet

gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
Expand Down
10 changes: 0 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,6 @@ cobertura:
@cat $(GO_TEST_OUTPUT)/coverage.txt | \
$(GOCOVER_COBERTURA) > $(GO_TEST_OUTPUT)/cobertura-coverage.xml

# Ensure a PR is ready for review.
reviewable: generate lint
@go mod tidy

# Ensure branch is clean.
check-diff: reviewable
@$(INFO) checking that branch is clean
@git diff --quiet || $(FAIL)
@$(OK) branch is clean

# Update the submodules, such as the common build scripts.
submodules:
@git submodule sync
Expand Down
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module github.com/crossplane/terrajet
module github.com/crossplane-contrib/terrajet

go 1.16

require (
github.com/crossplane/crossplane-runtime v0.14.0
github.com/pkg/errors v0.9.1
)
819 changes: 819 additions & 0 deletions go.sum

Large diffs are not rendered by default.

135 changes: 135 additions & 0 deletions pkg/reconciler/external.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package reconciler

import (
"context"

"github.com/pkg/errors"

xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"

"github.com/crossplane-contrib/terrajet/pkg/resource"
"github.com/crossplane-contrib/terrajet/pkg/scheduler"
)

const (
errUnexpectedObject = "The managed resource is not an Terraformed resource"
)

// NewTerraformExternal returns a terraform external client
func NewTerraformExternal(e scheduler.Scheduler) *TerraformExternal {
return &TerraformExternal{
executor: e,
}
}

// TerraformExternal manages lifecycle of a Terraform managed resource by implementing
// managed.ExternalClient interface.
type TerraformExternal struct {
executor scheduler.Scheduler
}

// Observe does an observation for the Terraform managed resource.
func (e *TerraformExternal) Observe(ctx context.Context, mg xpresource.Managed) (managed.ExternalObservation, error) {
tr, ok := mg.(resource.Terraformed)
if !ok {
return managed.ExternalObservation{}, errors.New(errUnexpectedObject)
}

exists, err := e.executor.Exists(ctx, tr)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "failed to check if exists")
}
if !exists {
return managed.ExternalObservation{
ResourceExists: false,
}, nil
}

lateInitialized, err := e.executor.LateInitialize(ctx, tr)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "failed to late init")
}

upToDate, err := e.executor.IsUpToDate(ctx, tr)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "failed to check if is up to date")
}

isReady, err := e.executor.IsReady(ctx, tr)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "failed to check if ready")
}
if isReady {
mg.SetConditions(xpv1.Available())
} else {
mg.SetConditions(xpv1.Creating())
}

if err = e.executor.UpdateStatus(ctx, tr); err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "failed to update status")
}

conn, err := e.executor.GetConnectionDetails(ctx, tr)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "failed to get connection details")
}
return managed.ExternalObservation{
ResourceExists: true,
ResourceUpToDate: upToDate,
ResourceLateInitialized: lateInitialized,
ConnectionDetails: conn,
}, nil
}

// Create creates the Terraform managed resource.
func (e *TerraformExternal) Create(ctx context.Context, mg xpresource.Managed) (managed.ExternalCreation, error) {
tr, ok := mg.(resource.Terraformed)
if !ok {
return managed.ExternalCreation{}, errors.New(errUnexpectedObject)
}

ar, err := e.executor.Apply(ctx, tr)
if err != nil {
return managed.ExternalCreation{}, errors.Wrap(err, "failed to apply")
}
return managed.ExternalCreation{
ExternalNameAssigned: meta.GetExternalName(tr) != "",
ConnectionDetails: ar.ConnectionDetails,
}, nil
}

// Update updates the Terraform managed resource.
func (e *TerraformExternal) Update(ctx context.Context, mg xpresource.Managed) (managed.ExternalUpdate, error) {
tr, ok := mg.(resource.Terraformed)
if !ok {
return managed.ExternalUpdate{}, errors.New(errUnexpectedObject)
}

ar, err := e.executor.Apply(ctx, tr)
if err != nil {
return managed.ExternalUpdate{}, errors.Wrap(err, "failed to apply")
}
return managed.ExternalUpdate{
ConnectionDetails: ar.ConnectionDetails,
}, nil
}

// Delete deletes the Terraform managed resource.
func (e *TerraformExternal) Delete(ctx context.Context, mg xpresource.Managed) error {
tr, ok := mg.(resource.Terraformed)
if !ok {
return errors.New(errUnexpectedObject)
}

dr, err := e.executor.Delete(ctx, tr)
if err != nil {
return errors.Wrap(err, "failed to delete")
}
if dr.Completed {
return nil
}
return errors.Errorf("still deleting")
}
21 changes: 21 additions & 0 deletions pkg/resource/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package resource

import (
"github.com/crossplane/crossplane-runtime/pkg/resource"
)

// JSONIterable can get or set parameters or observations of terraform managed resources
type JSONIterable interface {
GetObservation() ([]byte, error)
SetObservation(data []byte) error

GetParameters() ([]byte, error)
SetParameters(data []byte) error
}

// Terraformed is a Kubernetes object representing a concrete terraform managed resource
type Terraformed interface {
resource.Managed

JSONIterable
}
73 changes: 73 additions & 0 deletions pkg/scheduler/fake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package scheduler

import (
"context"
"fmt"

"github.com/pkg/errors"

"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"

"github.com/crossplane-contrib/terrajet/pkg/resource"
)

// Fake is a fake Scheduler implementation
type Fake struct{}

// Exists is a fake implementation for Exists function of Scheduler interface.
func (f *Fake) Exists(ctx context.Context, tr resource.Terraformed) (bool, error) {
fmt.Println("Fake executor checking if exists...")
return true, nil
}

// UpdateStatus is a fake implementation for UpdateStatus function of Scheduler interface.
func (f *Fake) UpdateStatus(ctx context.Context, tr resource.Terraformed) error {
/*if err := tr.SetObservation([]byte{}); err != nil {
return errors.Wrap(err, "failed to set observation")
}*/
fmt.Println("Fake executor updating status...")
return nil
}

// LateInitialize is a fake implementation for LateInitialize function of Scheduler interface.
func (f *Fake) LateInitialize(ctx context.Context, tr resource.Terraformed) (bool, error) {
/*if err := tr.SetParameters([]byte{}); err != nil {
return false, errors.Wrap(err, "failed to set parameters")
}*/
fmt.Println("Fake executor late initializing...")
return true, nil
}

// IsReady is a fake implementation for IsReady function of Scheduler interface.
func (f *Fake) IsReady(ctx context.Context, tr resource.Terraformed) (bool, error) {
fmt.Println("Fake executor checking if ready...")
return true, nil
}

// IsUpToDate is a fake implementation for IsUpToDate function of Scheduler interface.
func (f *Fake) IsUpToDate(ctx context.Context, tr resource.Terraformed) (bool, error) {
fmt.Println("Fake executor checking if up to date...")
return false, nil
}

// GetConnectionDetails is a fake implementation for GetConnectionDetails function of Scheduler interface.
func (f *Fake) GetConnectionDetails(ctx context.Context, tr resource.Terraformed) (managed.ConnectionDetails, error) {
fmt.Println("Fake executor returning connection details...")
return managed.ConnectionDetails{}, nil
}

// Apply is a fake implementation for Apply function of Scheduler interface.
func (f *Fake) Apply(ctx context.Context, tr resource.Terraformed) (*ApplyResult, error) {
b, err := tr.GetParameters()
if err != nil {
return nil, errors.Wrap(err, "failed to get parameters")
}
fmt.Printf("Fake executor applying with parameters %s\n", b)
return &ApplyResult{Completed: true}, nil
}

// Delete is a fake implementation for Delete function of Scheduler interface.
func (f *Fake) Delete(ctx context.Context, tr resource.Terraformed) (*DeletionResult, error) {
fmt.Println("Fake executor deleting...")
return &DeletionResult{Completed: true}, nil
}
36 changes: 36 additions & 0 deletions pkg/scheduler/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package scheduler

import (
"context"

"github.com/crossplane-contrib/terrajet/pkg/resource"

"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
)

// ApplyResult represents result of an apply operation
type ApplyResult struct {
// Tells whether the apply operation is completed.
Completed bool

// Sensitive information that is available during creation/update.
ConnectionDetails managed.ConnectionDetails
}

// DeletionResult represents result of a delete operation
type DeletionResult struct {
// Tells whether the apply operation is completed.
Completed bool
}

// A Scheduler is used to interact with terraform managed resources
type Scheduler interface {
Exists(ctx context.Context, mg resource.Terraformed) (bool, error)
UpdateStatus(ctx context.Context, mg resource.Terraformed) error
LateInitialize(ctx context.Context, mg resource.Terraformed) (bool, error)
IsReady(ctx context.Context, mg resource.Terraformed) (bool, error)
IsUpToDate(ctx context.Context, mg resource.Terraformed) (bool, error)
GetConnectionDetails(ctx context.Context, mg resource.Terraformed) (managed.ConnectionDetails, error)
Apply(ctx context.Context, mg resource.Terraformed) (*ApplyResult, error)
Delete(ctx context.Context, mg resource.Terraformed) (*DeletionResult, error)
}

0 comments on commit 17c1f04

Please sign in to comment.