Skip to content

Commit

Permalink
Merge pull request #297 from zzxwill/inline-credentials
Browse files Browse the repository at this point in the history
Support inline credentials besides provider
  • Loading branch information
zzxwill authored May 10, 2022
2 parents 80fa089 + d40417a commit 50994f3
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 142 deletions.
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,11 @@ Bucket Number is: 0

0.030917(s) elapsed
```

## Generate CRDs

```shell
$ make manifests
go: creating new go.mod: module tmp
/Users/zhouzhengxi/go/bin/controller-gen "crd:trivialVersions=true" webhook paths="./..." output:crd:artifacts:config=chart/crds
```
9 changes: 9 additions & 0 deletions api/v1beta2/configuration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ type BaseConfigurationSpec struct {
// ProviderReference specifies the reference to Provider
ProviderReference *types.Reference `json:"providerRef,omitempty"`

// InlineCredentials specifies the credentials in spec.HCl field as below.
// provider "aws" {
// region = "us-west-2"
// access_key = "my-access-key"
// secret_key = "my-secret-key"
// }
// Or indicates a Terraform module or configuration don't need credentials at all, like provider `random`
InlineCredentials bool `json:"inlineCredentials,omitempty"`

// DeleteResource will determine whether provisioned cloud resources will be deleted when CR is deleted
// +kubebuilder:default:=true
DeleteResource bool `json:"deleteResource,omitempty"`
Expand Down
7 changes: 7 additions & 0 deletions chart/crds/terraform.core.oam.dev_configurations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ spec:
hcl:
description: HCL is the Terraform HCL type configuration
type: string
inlineCredentials:
description: "InlineCredentials specifies the credentials in spec.HCl
field as below. \tprovider \"aws\" { \t\tregion = \"us-west-2\"
\t\taccess_key = \"my-access-key\" \t\tsecret_key = \"my-secret-key\"
\t} Or indicates a Terraform module or configuration don't need
credentials at all, like provider `random`"
type: boolean
path:
description: Path is the sub-directory of remote git repository.
type: string
Expand Down
20 changes: 11 additions & 9 deletions controllers/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,17 @@ func Get(ctx context.Context, k8sClient client.Client, namespacedName apitypes.N
// IsDeletable will check whether the Configuration can be deleted immediately
// If deletable, it means no external cloud resources are provisioned
func IsDeletable(ctx context.Context, k8sClient client.Client, configuration *v1beta2.Configuration) (bool, error) {
providerRef := GetProviderNamespacedName(*configuration)
providerObj, err := provider.GetProviderFromConfiguration(ctx, k8sClient, providerRef.Namespace, providerRef.Name)
if err != nil {
return false, err
}
// allow Configuration to delete when the Provider doesn't exist or is not ready, which means external cloud resources are
// not provisioned at all
if providerObj == nil || providerObj.Status.State == types.ProviderIsNotReady || configuration.Status.Apply.State == types.TerraformInitError {
return true, nil
if !configuration.Spec.InlineCredentials {
providerRef := GetProviderNamespacedName(*configuration)
providerObj, err := provider.GetProviderFromConfiguration(ctx, k8sClient, providerRef.Namespace, providerRef.Name)
if err != nil {
return false, err
}
// allow Configuration to delete when the Provider doesn't exist or is not ready, which means external cloud resources are
// not provisioned at all
if providerObj == nil || providerObj.Status.State == types.ProviderIsNotReady || configuration.Status.Apply.State == types.TerraformInitError {
return true, nil
}
}

if configuration.Status.Apply.State == types.ConfigurationProvisioningAndChecking {
Expand Down
61 changes: 42 additions & 19 deletions controllers/configuration/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,31 @@ func TestIsDeletable(t *testing.T) {
Name: "default",
Namespace: "default",
}
configuration.Spec.InlineCredentials = false

defaultConfiguration := &v1beta2.Configuration{}
defaultConfiguration.Spec.InlineCredentials = false

provisioningConfiguration := &v1beta2.Configuration{
Status: v1beta2.ConfigurationStatus{
Apply: v1beta2.ConfigurationApplyStatus{
State: types.ConfigurationProvisioningAndChecking,
},
},
}
provisioningConfiguration.Spec.InlineCredentials = false

readyConfiguration := &v1beta2.Configuration{
Status: v1beta2.ConfigurationStatus{
Apply: v1beta2.ConfigurationApplyStatus{
State: types.Available,
},
},
}
readyConfiguration.Spec.InlineCredentials = false

inlineConfiguration := &v1beta2.Configuration{}
inlineConfiguration.Spec.InlineCredentials = true

type args struct {
configuration *v1beta2.Configuration
Expand All @@ -297,7 +322,7 @@ func TestIsDeletable(t *testing.T) {
name: "provider is not found",
args: args{
k8sClient: k8sClient1,
configuration: &v1beta2.Configuration{},
configuration: defaultConfiguration,
},
want: want{
deletable: true,
Expand All @@ -307,7 +332,7 @@ func TestIsDeletable(t *testing.T) {
name: "provider is not ready, use default providerRef",
args: args{
k8sClient: k8sClient2,
configuration: &v1beta2.Configuration{},
configuration: defaultConfiguration,
},
want: want{
deletable: true,
Expand All @@ -326,14 +351,8 @@ func TestIsDeletable(t *testing.T) {
{
name: "configuration is provisioning",
args: args{
k8sClient: k8sClient3,
configuration: &v1beta2.Configuration{
Status: v1beta2.ConfigurationStatus{
Apply: v1beta2.ConfigurationApplyStatus{
State: types.ConfigurationProvisioningAndChecking,
},
},
},
k8sClient: k8sClient3,
configuration: provisioningConfiguration,
},
want: want{
errMsg: "Destroy could not complete and needs to wait for Provision to complete first",
Expand All @@ -342,27 +361,31 @@ func TestIsDeletable(t *testing.T) {
{
name: "configuration is ready",
args: args{
k8sClient: k8sClient3,
configuration: &v1beta2.Configuration{
Status: v1beta2.ConfigurationStatus{
Apply: v1beta2.ConfigurationApplyStatus{
State: types.Available,
},
},
},
k8sClient: k8sClient3,
configuration: readyConfiguration,
},
want: want{},
},
{
name: "failed to get provider",
args: args{
k8sClient: k8sClient4,
configuration: &v1beta2.Configuration{},
configuration: defaultConfiguration,
},
want: want{
errMsg: "failed to get Provider object",
},
},
{
name: "no provider is needed",
args: args{
k8sClient: k8sClient4,
configuration: inlineConfiguration,
},
want: want{
deletable: false,
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
38 changes: 19 additions & 19 deletions controllers/configuration_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,9 @@ func initTFConfigurationMeta(req ctrl.Request, configuration v1beta2.Configurati
meta.RemoteGitPath = configuration.Spec.Path
}

meta.ProviderReference = tfcfg.GetProviderNamespacedName(configuration)
if !configuration.Spec.InlineCredentials {
meta.ProviderReference = tfcfg.GetProviderNamespacedName(configuration)
}

// Check the existence of Terraform state secret which is used to store TF state file. For detailed information,
// please refer to https://www.terraform.io/docs/language/settings/backends/kubernetes.html#configuration-variables
Expand Down Expand Up @@ -519,20 +521,22 @@ func (r *ConfigurationReconciler) preCheck(ctx context.Context, configuration *v
}

// Check provider
p, err := provider.GetProviderFromConfiguration(ctx, k8sClient, meta.ProviderReference.Namespace, meta.ProviderReference.Name)
if p == nil {
msg := types.ErrProviderNotFound
if err != nil {
msg = err.Error()
}
if updateStatusErr := meta.updateApplyStatus(ctx, k8sClient, types.Authorizing, msg); updateStatusErr != nil {
return errors.Wrap(updateStatusErr, msg)
if !configuration.Spec.InlineCredentials {
p, err := provider.GetProviderFromConfiguration(ctx, k8sClient, meta.ProviderReference.Namespace, meta.ProviderReference.Name)
if p == nil {
msg := types.ErrProviderNotFound
if err != nil {
msg = err.Error()
}
if updateStatusErr := meta.updateApplyStatus(ctx, k8sClient, types.Authorizing, msg); updateStatusErr != nil {
return errors.Wrap(updateStatusErr, msg)
}
return errors.New(msg)
}
return errors.New(msg)
}

if err := meta.getCredentials(ctx, k8sClient, p); err != nil {
return err
if err := meta.getCredentials(ctx, k8sClient, p); err != nil {
return err
}
}

// Check whether env changes
Expand Down Expand Up @@ -953,7 +957,7 @@ func (meta *TFConfigurationMeta) prepareTFVariables(configuration *v1beta2.Confi
if configuration == nil {
return errors.New("configuration is nil")
}
if meta.ProviderReference == nil {
if !configuration.Spec.InlineCredentials && meta.ProviderReference == nil {
return errors.New("The referenced provider could not be retrieved")
}

Expand All @@ -972,7 +976,7 @@ func (meta *TFConfigurationMeta) prepareTFVariables(configuration *v1beta2.Confi
envs = append(envs, v1.EnvVar{Name: k, ValueFrom: valueFrom})
}

if meta.Credentials == nil {
if !configuration.Spec.InlineCredentials && meta.Credentials == nil {
return errors.New(provider.ErrCredentialNotRetrieved)
}
for k, v := range meta.Credentials {
Expand All @@ -981,10 +985,6 @@ func (meta *TFConfigurationMeta) prepareTFVariables(configuration *v1beta2.Confi
valueFrom.SecretKeyRef.Name = meta.VariableSecretName
envs = append(envs, v1.EnvVar{Name: k, ValueFrom: valueFrom})
}
// make sure the env of the Job is set
if envs == nil {
return errors.New(provider.ErrCredentialNotRetrieved)
}
meta.Envs = envs
meta.VariableSecretData = data

Expand Down
14 changes: 6 additions & 8 deletions controllers/configuration_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ import (
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"

kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -34,7 +32,6 @@ import (

"github.com/oam-dev/terraform-controller/api/types"
crossplane "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime"
runtimetypes "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime"
"github.com/oam-dev/terraform-controller/api/v1beta1"
"github.com/oam-dev/terraform-controller/api/v1beta2"
"github.com/oam-dev/terraform-controller/controllers/provider"
Expand All @@ -60,6 +57,7 @@ func TestInitTFConfigurationMeta(t *testing.T) {
Name: "xxx",
Namespace: "default",
}
completeConfiguration.Spec.InlineCredentials = false

testcases := []struct {
name string
Expand Down Expand Up @@ -1388,7 +1386,7 @@ func TestGetTFOutputs(t *testing.T) {
configuration4 := v1beta2.Configuration{
Spec: v1beta2.ConfigurationSpec{
BaseConfigurationSpec: v1beta2.BaseConfigurationSpec{
WriteConnectionSecretToReference: &runtimetypes.SecretReference{
WriteConnectionSecretToReference: &crossplane.SecretReference{
Name: "connection-secret-c",
Namespace: "default",
},
Expand Down Expand Up @@ -1431,7 +1429,7 @@ func TestGetTFOutputs(t *testing.T) {
},
Spec: v1beta2.ConfigurationSpec{
BaseConfigurationSpec: v1beta2.BaseConfigurationSpec{
WriteConnectionSecretToReference: &runtimetypes.SecretReference{
WriteConnectionSecretToReference: &crossplane.SecretReference{
Name: "connection-secret-d",
Namespace: "default",
},
Expand Down Expand Up @@ -1476,7 +1474,7 @@ func TestGetTFOutputs(t *testing.T) {
},
Spec: v1beta2.ConfigurationSpec{
BaseConfigurationSpec: v1beta2.BaseConfigurationSpec{
WriteConnectionSecretToReference: &runtimetypes.SecretReference{
WriteConnectionSecretToReference: &crossplane.SecretReference{
Name: "connection-secret-e",
Namespace: "default",
},
Expand Down Expand Up @@ -1523,7 +1521,7 @@ func TestGetTFOutputs(t *testing.T) {
},
Spec: v1beta2.ConfigurationSpec{
BaseConfigurationSpec: v1beta2.BaseConfigurationSpec{
WriteConnectionSecretToReference: &runtimetypes.SecretReference{
WriteConnectionSecretToReference: &crossplane.SecretReference{
Name: "connection-secret-e",
Namespace: "default",
},
Expand Down Expand Up @@ -1565,7 +1563,7 @@ func TestGetTFOutputs(t *testing.T) {
},
Spec: v1beta2.ConfigurationSpec{
BaseConfigurationSpec: v1beta2.BaseConfigurationSpec{
WriteConnectionSecretToReference: &runtimetypes.SecretReference{
WriteConnectionSecretToReference: &crossplane.SecretReference{
Name: "connection-secret-d",
Namespace: "default",
},
Expand Down
Loading

0 comments on commit 50994f3

Please sign in to comment.