This repository has been archived by the owner on Dec 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial implementation for common controller
Signed-off-by: Hasan Turken <turkenh@gmail.com>
- Loading branch information
Showing
8 changed files
with
1,091 additions
and
12 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
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 |
---|---|---|
@@ -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 | ||
) |
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,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") | ||
} |
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 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 | ||
} |
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,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 | ||
} |
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,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) | ||
} |