3582 lines
93 KiB
Go
3582 lines
93 KiB
Go
package controllers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"k8s.io/utils/pointer"
|
|
|
|
"github.com/agiledragon/gomonkey/v2"
|
|
"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
|
|
"github.com/pkg/errors"
|
|
"github.com/stretchr/testify/assert"
|
|
batchv1 "k8s.io/api/batch/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
apierrors "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"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
k8stypes "k8s.io/apimachinery/pkg/types"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
|
|
"github.com/oam-dev/terraform-controller/api/types"
|
|
crossplane "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/configuration/backend"
|
|
"github.com/oam-dev/terraform-controller/controllers/provider"
|
|
)
|
|
|
|
func TestInitTFConfigurationMeta(t *testing.T) {
|
|
req := ctrl.Request{}
|
|
req.Namespace = "default"
|
|
req.Name = "abc"
|
|
|
|
completeConfiguration := v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
Path: "alibaba/rds",
|
|
Backend: &v1beta2.Backend{
|
|
SecretSuffix: "s1",
|
|
},
|
|
},
|
|
}
|
|
completeConfiguration.Spec.ProviderReference = &crossplane.Reference{
|
|
Name: "xxx",
|
|
Namespace: "default",
|
|
}
|
|
completeConfiguration.Spec.InlineCredentials = false
|
|
|
|
testcases := []struct {
|
|
name string
|
|
configuration v1beta2.Configuration
|
|
want *TFConfigurationMeta
|
|
}{
|
|
{
|
|
name: "empty configuration",
|
|
configuration: v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
},
|
|
want: &TFConfigurationMeta{
|
|
Namespace: "default",
|
|
ControllerNamespace: "default",
|
|
Name: "abc",
|
|
ConfigurationCMName: "tf-abc",
|
|
VariableSecretName: "variable-abc",
|
|
ApplyJobName: "abc-apply",
|
|
DestroyJobName: "abc-destroy",
|
|
|
|
RemoteGitPath: ".",
|
|
ProviderReference: &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "complete configuration",
|
|
configuration: completeConfiguration,
|
|
want: &TFConfigurationMeta{
|
|
Namespace: "default",
|
|
ControllerNamespace: "default",
|
|
Name: "abc",
|
|
ConfigurationCMName: "tf-abc",
|
|
VariableSecretName: "variable-abc",
|
|
ApplyJobName: "abc-apply",
|
|
DestroyJobName: "abc-destroy",
|
|
|
|
RemoteGitPath: "alibaba/rds",
|
|
ProviderReference: &crossplane.Reference{
|
|
Name: "xxx",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
meta := initTFConfigurationMeta(req, tc.configuration, nil)
|
|
if !reflect.DeepEqual(meta.Name, tc.want.Name) {
|
|
t.Errorf("initTFConfigurationMeta = %v, want %v", meta, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInitTFConfigurationMetaWithDeleteResource(t *testing.T) {
|
|
req := ctrl.Request{}
|
|
req.Namespace = "default"
|
|
req.Name = "abc"
|
|
testcases := []struct {
|
|
name string
|
|
configuration v1beta2.Configuration
|
|
meta *TFConfigurationMeta
|
|
}{
|
|
{
|
|
name: "DeleteResource is true",
|
|
configuration: v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
DeleteResource: pointer.Bool(true),
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
DeleteResource: true,
|
|
},
|
|
},
|
|
{
|
|
name: "DeleteResource is false",
|
|
configuration: v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
DeleteResource: pointer.Bool(false),
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
DeleteResource: false,
|
|
},
|
|
},
|
|
{
|
|
name: "DeleteResource is nil",
|
|
configuration: v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
DeleteResource: nil,
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
DeleteResource: true,
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
meta := initTFConfigurationMeta(req, tc.configuration, nil)
|
|
if !reflect.DeepEqual(meta.DeleteResource, tc.meta.DeleteResource) {
|
|
t.Errorf("initTFConfigurationMeta = %v, want %v", meta, tc.meta)
|
|
}
|
|
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInitTFConfigurationMetaWithJobNodeSelector(t *testing.T) {
|
|
req := ctrl.Request{}
|
|
req.Namespace = "default"
|
|
req.Name = "abc"
|
|
err := os.Setenv("JOB_NODE_SELECTOR", "{\"ssd\": \"true\"}")
|
|
assert.Nil(t, err)
|
|
configuration := v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{},
|
|
}
|
|
meta := initTFConfigurationMeta(req, configuration, nil)
|
|
assert.Equal(t, meta.JobNodeSelector, map[string]string{"ssd": "true"})
|
|
}
|
|
|
|
func TestPrepareTFVariables(t *testing.T) {
|
|
prjID := "PrjID"
|
|
prjIDValue := "test123"
|
|
testKey := "Test"
|
|
testValue := "abc123"
|
|
credentialKey := "key"
|
|
credentialValue := "testkey"
|
|
variable, _ := json.Marshal(map[string]interface{}{
|
|
testKey: testValue,
|
|
})
|
|
configuration := v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
ProviderReference: &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
HCL: "test",
|
|
Variable: &runtime.RawExtension{
|
|
Raw: variable,
|
|
},
|
|
},
|
|
}
|
|
meta := &TFConfigurationMeta{
|
|
JobEnv: map[string]interface{}{
|
|
prjID: prjIDValue,
|
|
},
|
|
ProviderReference: &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Credentials: map[string]string{
|
|
credentialKey: credentialValue,
|
|
},
|
|
}
|
|
err := meta.prepareTFVariables(&configuration)
|
|
assert.Nil(t, err)
|
|
rTestKey := fmt.Sprintf("TF_VAR_%s", testKey)
|
|
wantVarSecretData := map[string]string{prjID: prjIDValue, rTestKey: testValue, credentialKey: credentialValue}
|
|
for k, v := range wantVarSecretData {
|
|
actualV, ok := meta.VariableSecretData[k]
|
|
assert.Equal(t, ok, true)
|
|
assert.Equal(t, actualV, []byte(v))
|
|
}
|
|
existMap := map[string]bool{}
|
|
for _, e := range meta.Envs {
|
|
switch e.Name {
|
|
case prjID:
|
|
existMap[prjID] = true
|
|
case rTestKey:
|
|
existMap[rTestKey] = true
|
|
case credentialKey:
|
|
existMap[credentialKey] = true
|
|
default:
|
|
t.Fatalf("unexpected %s", e.Name)
|
|
}
|
|
}
|
|
assert.Equal(t, len(existMap), 3)
|
|
for _, v := range existMap {
|
|
assert.Equal(t, v, true)
|
|
}
|
|
}
|
|
|
|
func TestInitTFConfigurationMetaWithJobEnv(t *testing.T) {
|
|
req := ctrl.Request{}
|
|
r := &ConfigurationReconciler{}
|
|
s := runtime.NewScheme()
|
|
v1beta1.AddToScheme(s)
|
|
v1beta2.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
req.Namespace = "default"
|
|
req.Name = "abc"
|
|
prjID := "PrjID"
|
|
prjIDValue := "abc"
|
|
data, _ := json.Marshal(map[string]interface{}{
|
|
prjID: prjIDValue,
|
|
})
|
|
ak := provider.AlibabaCloudCredentials{
|
|
AccessKeyID: "aaaa",
|
|
AccessKeySecret: "bbbbb",
|
|
}
|
|
credentials, err := json.Marshal(&ak)
|
|
secret := corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"credentials": credentials,
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
provider := v1beta1.Provider{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "terraform.core.oam.dev/v1beta2",
|
|
Kind: "Provider",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Spec: v1beta1.ProviderSpec{
|
|
Provider: "ucloud",
|
|
Credentials: v1beta1.ProviderCredentials{
|
|
Source: crossplane.CredentialsSourceSecret,
|
|
SecretRef: &crossplane.SecretKeySelector{
|
|
SecretReference: crossplane.SecretReference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Key: "credentials",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
configuration := v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
ProviderReference: &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
JobEnv: &runtime.RawExtension{
|
|
Raw: data,
|
|
},
|
|
HCL: "test",
|
|
},
|
|
}
|
|
r.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(&configuration, &provider, &secret).Build()
|
|
meta := initTFConfigurationMeta(req, configuration, r.Client)
|
|
err = r.preCheck(context.Background(), &configuration, meta)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, meta.JobEnv, map[string]interface{}{
|
|
prjID: prjIDValue,
|
|
})
|
|
assert.Equal(t, meta.VariableSecretData[prjID], []byte(prjIDValue))
|
|
envExist := false
|
|
for _, e := range meta.Envs {
|
|
if e.Name == prjID {
|
|
envExist = true
|
|
}
|
|
}
|
|
assert.Equal(t, envExist, true)
|
|
}
|
|
|
|
func TestCheckProvider(t *testing.T) {
|
|
ctx := context.Background()
|
|
scheme := runtime.NewScheme()
|
|
v1beta2.AddToScheme(scheme)
|
|
|
|
k8sClient1 := fake.NewClientBuilder().WithScheme(scheme).Build()
|
|
|
|
meta := &TFConfigurationMeta{
|
|
ProviderReference: &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
}
|
|
|
|
type args struct {
|
|
k8sClient client.Client
|
|
provider *v1beta1.Provider
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want string
|
|
}{
|
|
{
|
|
name: "provider exists, and is not ready",
|
|
args: args{
|
|
k8sClient: k8sClient1,
|
|
provider: &v1beta1.Provider{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Status: v1beta1.ProviderStatus{
|
|
State: types.ProviderIsNotReady,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "provider doesn't not exist",
|
|
args: args{
|
|
k8sClient: fake.NewClientBuilder().WithScheme(scheme).Build(),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if err := meta.getCredentials(ctx, tc.args.k8sClient, tc.args.provider); tc.want != "" &&
|
|
!strings.Contains(err.Error(), tc.want) {
|
|
t.Errorf("getCredentials = %v, want %v", err.Error(), tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigurationReconcile(t *testing.T) {
|
|
req := ctrl.Request{}
|
|
req.NamespacedName = k8stypes.NamespacedName{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
}
|
|
|
|
r1 := &ConfigurationReconciler{}
|
|
ctx := context.Background()
|
|
s := runtime.NewScheme()
|
|
v1beta1.AddToScheme(s)
|
|
v1beta2.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
batchv1.AddToScheme(s)
|
|
r1.Client = fake.NewClientBuilder().WithScheme(s).Build()
|
|
|
|
ak := provider.AlibabaCloudCredentials{
|
|
AccessKeyID: "aaaa",
|
|
AccessKeySecret: "bbbbb",
|
|
}
|
|
credentials, err := json.Marshal(&ak)
|
|
assert.Nil(t, err)
|
|
secret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"credentials": credentials,
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
|
|
provider := &v1beta1.Provider{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "terraform.core.oam.dev/v1beta2",
|
|
Kind: "Provider",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Spec: v1beta1.ProviderSpec{
|
|
Provider: "alibaba",
|
|
Credentials: v1beta1.ProviderCredentials{
|
|
Source: "Secret",
|
|
SecretRef: &crossplane.SecretKeySelector{
|
|
SecretReference: crossplane.SecretReference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Key: "credentials",
|
|
},
|
|
},
|
|
Region: "xxx",
|
|
},
|
|
}
|
|
|
|
data, _ := json.Marshal(map[string]interface{}{
|
|
"name": "abc",
|
|
})
|
|
variables := &runtime.RawExtension{Raw: data}
|
|
configuration2 := &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "c",
|
|
Variable: variables,
|
|
},
|
|
Status: v1beta2.ConfigurationStatus{
|
|
Apply: v1beta2.ConfigurationApplyStatus{
|
|
State: types.Available,
|
|
},
|
|
},
|
|
}
|
|
configuration2.Spec.ProviderReference = &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
}
|
|
configuration2.Spec.WriteConnectionSecretToReference = &crossplane.SecretReference{
|
|
Name: "db-conn",
|
|
Namespace: "default",
|
|
}
|
|
|
|
patches := gomonkey.ApplyMethod(reflect.TypeOf(&sts.Client{}), "GetCallerIdentity", func(_ *sts.Client, request *sts.GetCallerIdentityRequest) (response *sts.GetCallerIdentityResponse, err error) {
|
|
response = nil
|
|
err = nil
|
|
return
|
|
})
|
|
defer patches.Reset()
|
|
|
|
applyingJob2 := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: req.Name + "-" + string(TerraformApply),
|
|
Namespace: req.Namespace,
|
|
},
|
|
Status: batchv1.JobStatus{
|
|
Succeeded: int32(1),
|
|
},
|
|
}
|
|
|
|
stateData, _ := base64.StdEncoding.DecodeString("H4sIAAAAAAAA/4SQzarbMBCF934KoXUdPKNf+1VKCWNp5AocO8hyaSl592KlcBd3cZfnHPHpY/52QshfXI68b3IS+tuVK5dCaS+P+8ci4TbcULb94JJplZPAFte8MS18PQrKBO8Q+xk59SHa1AMA9M4YmoN3FGJ8M/azPs96yElcCkLIsG+V8sblnqOc3uXlRuvZ0GxSSuiCRUYbw2gGHRFGPxitEgJYQDQ0a68I2ChNo1cAZJ2bR20UtW8bsv55NuJRS94W2erXe5X5QQs3A/FZ4fhJaOwUgZTVMRjto1HGpSGSQuuD955hdDDPcR6NY1ZpQJ/YwagTRAvBpsi8LXn7Pa1U+ahfWHX/zWThYz9L4Otg3390r+5fAAAA//8hmcuNuQEAAA==")
|
|
|
|
backendSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-a",
|
|
Namespace: "vela-system",
|
|
},
|
|
Data: map[string][]byte{
|
|
"tfstate": stateData,
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
|
|
variableSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: fmt.Sprintf(TFVariableSecret, req.Name),
|
|
Namespace: req.Namespace,
|
|
},
|
|
Data: map[string][]byte{
|
|
"name": []byte("def"),
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
|
|
r2 := &ConfigurationReconciler{}
|
|
r2.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(secret, provider, applyingJob2, backendSecret,
|
|
variableSecret, configuration2).Build()
|
|
|
|
time := v1.NewTime(time.Now())
|
|
configuration3 := &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
DeletionTimestamp: &time,
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "c",
|
|
},
|
|
}
|
|
configuration3.Spec.ProviderReference = &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
}
|
|
|
|
destroyJob3 := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a-destroy",
|
|
Namespace: req.Namespace,
|
|
},
|
|
Status: batchv1.JobStatus{
|
|
Succeeded: int32(1),
|
|
},
|
|
}
|
|
|
|
r3 := &ConfigurationReconciler{}
|
|
r3.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(secret, provider, configuration3, destroyJob3).Build()
|
|
|
|
configuration4 := &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
DeletionTimestamp: &time,
|
|
Finalizers: []string{configurationFinalizer},
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "c",
|
|
Backend: &v1beta2.Backend{
|
|
BackendType: "kubernetes",
|
|
Kubernetes: &v1beta2.KubernetesBackendConf{
|
|
SecretSuffix: "a",
|
|
Namespace: &backendSecret.Namespace,
|
|
},
|
|
},
|
|
},
|
|
Status: v1beta2.ConfigurationStatus{
|
|
Apply: v1beta2.ConfigurationApplyStatus{
|
|
State: types.ConfigurationProvisioningAndChecking,
|
|
},
|
|
},
|
|
}
|
|
configuration4.Spec.ProviderReference = &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
}
|
|
|
|
destroyJob4 := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a-destroy",
|
|
Namespace: req.Namespace,
|
|
},
|
|
Status: batchv1.JobStatus{
|
|
Succeeded: int32(1),
|
|
},
|
|
}
|
|
|
|
r4 := &ConfigurationReconciler{}
|
|
r4.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(secret, provider, configuration4, destroyJob4, backendSecret).Build()
|
|
|
|
configuration5 := &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
DeletionTimestamp: &time,
|
|
Finalizers: []string{configurationFinalizer},
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "c",
|
|
},
|
|
}
|
|
configuration5.Spec.ProviderReference = &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
}
|
|
|
|
destroyJob5 := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a-destroy",
|
|
Namespace: req.Namespace,
|
|
},
|
|
Status: batchv1.JobStatus{
|
|
Succeeded: int32(1),
|
|
},
|
|
}
|
|
|
|
r5 := &ConfigurationReconciler{}
|
|
r5.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(secret, provider, configuration5, destroyJob5).Build()
|
|
|
|
// @step: create the setup for the job namespace tests
|
|
configuration6 := &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
Finalizers: []string{configurationFinalizer},
|
|
UID: "12345",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "c",
|
|
},
|
|
}
|
|
|
|
namespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "builds"}}
|
|
r6 := &ConfigurationReconciler{ControllerNamespace: "builds"}
|
|
r6.Client = fake.NewClientBuilder().
|
|
WithScheme(s).
|
|
WithObjects(namespace, secret, provider, configuration6).
|
|
Build()
|
|
|
|
// for case "Configuration changed, and reconcile"
|
|
appliedConfigurationCM := &corev1.ConfigMap{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "tf-a",
|
|
Namespace: req.Namespace,
|
|
},
|
|
Data: map[string]string{types.TerraformHCLConfigurationName: `Here is the original hcl
|
|
|
|
terraform {
|
|
backend "kubernetes" {
|
|
secret_suffix = "a"
|
|
in_cluster_config = true
|
|
namespace = "b"
|
|
}
|
|
}
|
|
`,
|
|
},
|
|
}
|
|
varMap := map[string]string{"name": "abc"}
|
|
appliedEnvVariable := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: fmt.Sprintf(TFVariableSecret, req.Name),
|
|
Namespace: req.Namespace,
|
|
},
|
|
Data: map[string][]byte{
|
|
"TF_VAR_name": []byte(varMap["name"]),
|
|
"ALICLOUD_ACCESS_KEY": []byte(ak.AccessKeyID),
|
|
"ALICLOUD_SECRET_KEY": []byte(ak.AccessKeySecret),
|
|
"ALICLOUD_REGION": []byte(provider.Spec.Region),
|
|
"ALICLOUD_SECURITY_TOKEN": []byte(""),
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
appliedJobName := req.Name + "-" + string(TerraformApply)
|
|
appliedJob := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: appliedJobName,
|
|
Namespace: req.Namespace,
|
|
UID: "111",
|
|
},
|
|
Status: batchv1.JobStatus{
|
|
Succeeded: int32(1),
|
|
},
|
|
}
|
|
varData, _ := json.Marshal(varMap)
|
|
configuration7 := &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "Here is the changed hcl",
|
|
Variable: &runtime.RawExtension{Raw: varData},
|
|
ProviderReference: &crossplane.Reference{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
WriteConnectionSecretToReference: &crossplane.SecretReference{
|
|
Name: "db-conn",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
Status: v1beta2.ConfigurationStatus{
|
|
Apply: v1beta2.ConfigurationApplyStatus{
|
|
State: types.Available,
|
|
},
|
|
},
|
|
}
|
|
r7 := &ConfigurationReconciler{}
|
|
r7.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(secret, provider, backendSecret,
|
|
appliedJob, appliedEnvVariable, appliedConfigurationCM, configuration7).Build()
|
|
|
|
type args struct {
|
|
req reconcile.Request
|
|
r *ConfigurationReconciler
|
|
}
|
|
|
|
type want struct {
|
|
errMsg string
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
check func(t *testing.T, cc client.Client)
|
|
}{
|
|
{
|
|
name: "Configuration is not found",
|
|
args: args{
|
|
req: req,
|
|
r: r1,
|
|
},
|
|
},
|
|
{
|
|
name: "Configuration exists, and it's available",
|
|
args: args{
|
|
req: req,
|
|
r: r2,
|
|
},
|
|
},
|
|
{
|
|
name: "Configuration is deleting",
|
|
args: args{
|
|
req: req,
|
|
r: r3,
|
|
},
|
|
},
|
|
{
|
|
name: "Configuration is deleting, but failed to delete",
|
|
args: args{
|
|
req: req,
|
|
r: r4,
|
|
},
|
|
want: want{
|
|
errMsg: "Destroy could not complete and needs to wait for Provision to complete first: Cloud resources are being provisioned and provisioning status is checking...",
|
|
},
|
|
},
|
|
{
|
|
name: "Configuration is deleting, and succeeded to delete",
|
|
args: args{
|
|
req: req,
|
|
r: r5,
|
|
},
|
|
},
|
|
{
|
|
name: "Builds should be run in the controller namespace when defined",
|
|
args: args{
|
|
req: req,
|
|
r: r6,
|
|
},
|
|
check: func(t *testing.T, cc client.Client) {
|
|
job := &batchv1.Job{}
|
|
err := cc.Get(context.TODO(), k8stypes.NamespacedName{Name: "12345-apply", Namespace: "builds"}, job)
|
|
if err != nil {
|
|
t.Error("Failed to retrieve jobs from builds namespace")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "Configuration changed, and reconcile",
|
|
args: args{
|
|
req: req,
|
|
r: r7,
|
|
},
|
|
check: func(t *testing.T, cc client.Client) {
|
|
job := &batchv1.Job{}
|
|
if err = cc.Get(context.TODO(), k8stypes.NamespacedName{Name: appliedJobName, Namespace: req.Namespace}, job); err != nil {
|
|
t.Error("Failed to retrieve the new job")
|
|
}
|
|
assert.Equal(t, job.Name, appliedJob.Name, "Not expected job name")
|
|
assert.Equal(t, job.Namespace, appliedJob.Namespace, "Not expected job namespace")
|
|
assert.NotEqual(t, job.UID, appliedJob.UID, "No new job created")
|
|
assert.NotEqual(t, job.Status.Succeeded, appliedJob.Status.Succeeded, "Not expected job status")
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
for i := 0; i < 5; i++ {
|
|
if _, err := tc.args.r.Reconcile(ctx, tc.args.req); tc.want.errMsg != "" &&
|
|
!strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("Reconcile() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
}
|
|
}
|
|
if tc.check != nil {
|
|
tc.check(t, tc.args.r.Client)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPreCheckResourcesSetting(t *testing.T) {
|
|
r := &ConfigurationReconciler{}
|
|
s := runtime.NewScheme()
|
|
v1beta1.AddToScheme(s)
|
|
v1beta2.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
provider := &v1beta1.Provider{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Status: v1beta1.ProviderStatus{
|
|
State: types.ProviderIsNotReady,
|
|
},
|
|
}
|
|
r.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(provider).Build()
|
|
|
|
type args struct {
|
|
r *ConfigurationReconciler
|
|
configuration *v1beta2.Configuration
|
|
meta *TFConfigurationMeta
|
|
}
|
|
|
|
type want struct {
|
|
errMsg string
|
|
}
|
|
|
|
type prepare func(*testing.T)
|
|
|
|
testcases := []struct {
|
|
name string
|
|
prepare
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "wrong value in environment variable RESOURCES_LIMITS_CPU",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_LIMITS_CPU", "abcde")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "failed to parse env variable RESOURCES_LIMITS_CPU into resource.Quantity",
|
|
},
|
|
},
|
|
{
|
|
name: "wrong value in environment variable RESOURCES_LIMITS_MEMORY",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_LIMITS_MEMORY", "xxxx5Gi")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "failed to parse env variable RESOURCES_LIMITS_MEMORY into resource.Quantity",
|
|
},
|
|
},
|
|
{
|
|
name: "wrong value in environment variable RESOURCES_REQUESTS_CPU",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_REQUESTS_CPU", "ekiadasdflksas")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "failed to parse env variable RESOURCES_REQUESTS_CPU into resource.Quantity",
|
|
},
|
|
},
|
|
{
|
|
name: "wrong value in environment variable RESOURCES_REQUESTS_MEMORY",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_REQUESTS_MEMORY", "123x456")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "failed to parse env variable RESOURCES_REQUESTS_MEMORY into resource.Quantity",
|
|
},
|
|
},
|
|
{
|
|
name: "correct value of resources setting in environment variable",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_LIMITS_CPU", "10m")
|
|
t.Setenv("RESOURCES_LIMITS_MEMORY", "10Mi")
|
|
t.Setenv("RESOURCES_REQUESTS_CPU", "100")
|
|
t.Setenv("RESOURCES_REQUESTS_MEMORY", "5Gi")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if tc.prepare != nil {
|
|
tc.prepare(t)
|
|
}
|
|
if err := tc.args.r.preCheckResourcesSetting(tc.args.meta); (tc.want.errMsg != "") &&
|
|
!strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("preCheckResourcesSetting() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPreCheck(t *testing.T) {
|
|
r := &ConfigurationReconciler{}
|
|
ctx := context.Background()
|
|
s := runtime.NewScheme()
|
|
v1beta1.AddToScheme(s)
|
|
v1beta2.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
provider := &v1beta1.Provider{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Status: v1beta1.ProviderStatus{
|
|
State: types.ProviderIsNotReady,
|
|
},
|
|
}
|
|
r.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(provider).Build()
|
|
|
|
type args struct {
|
|
r *ConfigurationReconciler
|
|
configuration *v1beta2.Configuration
|
|
meta *TFConfigurationMeta
|
|
}
|
|
|
|
type want struct {
|
|
errMsg string
|
|
}
|
|
|
|
type prepare func(*testing.T)
|
|
|
|
testcases := []struct {
|
|
name string
|
|
prepare
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "configuration is invalid",
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
Remote: "aaa",
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{},
|
|
},
|
|
want: want{
|
|
errMsg: "spec.HCL and spec.Remote cloud not be set at the same time",
|
|
},
|
|
},
|
|
{
|
|
name: "HCL configuration is valid",
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{},
|
|
},
|
|
{
|
|
name: "Remote configuration is valid",
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
Remote: "https://github.com/a/b",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{},
|
|
},
|
|
{
|
|
name: "could not find provider",
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "d",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "provider not found",
|
|
},
|
|
},
|
|
{
|
|
name: "wrong value in environment variable RESOURCES_LIMITS_CPU",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_LIMITS_CPU", "abcde")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "failed to parse env variable RESOURCES_LIMITS_CPU into resource.Quantity",
|
|
},
|
|
},
|
|
{
|
|
name: "wrong value in environment variable RESOURCES_LIMITS_MEMORY",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_LIMITS_MEMORY", "xxxx5Gi")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "failed to parse env variable RESOURCES_LIMITS_MEMORY into resource.Quantity",
|
|
},
|
|
},
|
|
{
|
|
name: "wrong value in environment variable RESOURCES_REQUESTS_CPU",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_REQUESTS_CPU", "ekiadasdflksas")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "failed to parse env variable RESOURCES_REQUESTS_CPU into resource.Quantity",
|
|
},
|
|
},
|
|
{
|
|
name: "wrong value in environment variable RESOURCES_REQUESTS_MEMORY",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_REQUESTS_MEMORY", "123x456")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "failed to parse env variable RESOURCES_REQUESTS_MEMORY into resource.Quantity",
|
|
},
|
|
},
|
|
{
|
|
name: "correct value of resources setting in environment variable",
|
|
prepare: func(t *testing.T) {
|
|
t.Setenv("RESOURCES_LIMITS_CPU", "10m")
|
|
t.Setenv("RESOURCES_LIMITS_MEMORY", "10Mi")
|
|
t.Setenv("RESOURCES_REQUESTS_CPU", "100")
|
|
t.Setenv("RESOURCES_REQUESTS_MEMORY", "5Gi")
|
|
},
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
},
|
|
},
|
|
want: want{},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if tc.prepare != nil {
|
|
tc.prepare(t)
|
|
}
|
|
|
|
err := tc.args.r.preCheck(ctx, tc.args.configuration, tc.args.meta)
|
|
if tc.want.errMsg != "" || err != nil {
|
|
if !strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("preCheck() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPreCheckWhenConfigurationIsChanged(t *testing.T) {
|
|
r := &ConfigurationReconciler{}
|
|
ctx := context.Background()
|
|
s := runtime.NewScheme()
|
|
v1beta1.AddToScheme(s)
|
|
v1beta2.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
provider := &v1beta1.Provider{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Status: v1beta1.ProviderStatus{
|
|
State: types.ProviderIsNotReady,
|
|
},
|
|
}
|
|
r.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(provider).Build()
|
|
|
|
provider3 := &v1beta1.Provider{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
Status: v1beta1.ProviderStatus{
|
|
State: types.ProviderIsNotReady,
|
|
},
|
|
}
|
|
configurationCM3 := &corev1.ConfigMap{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
Namespace: "default",
|
|
},
|
|
}
|
|
r.Client = fake.NewClientBuilder().WithScheme(s).WithObjects(provider3, configurationCM3).Build()
|
|
meta3 := &TFConfigurationMeta{
|
|
ConfigurationCMName: "abc",
|
|
ProviderReference: &crossplane.Reference{
|
|
Namespace: "default",
|
|
Name: "default",
|
|
},
|
|
CompleteConfiguration: "d",
|
|
Namespace: "default",
|
|
}
|
|
|
|
patches := gomonkey.ApplyFunc(reflect.DeepEqual, func(x, y interface{}) bool {
|
|
return true
|
|
})
|
|
defer patches.Reset()
|
|
|
|
type args struct {
|
|
r *ConfigurationReconciler
|
|
configuration *v1beta2.Configuration
|
|
meta *TFConfigurationMeta
|
|
}
|
|
|
|
type want struct {
|
|
errMsg string
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "configuration is changed",
|
|
args: args{
|
|
r: r,
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "abc",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "bbb",
|
|
},
|
|
},
|
|
meta: meta3,
|
|
},
|
|
want: want{},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if err := tc.args.r.preCheck(ctx, tc.args.configuration, tc.args.meta); (tc.want.errMsg != "") &&
|
|
!strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("preCheck() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTerraformDestroy(t *testing.T) {
|
|
const (
|
|
// secrets/configmaps are dispatched to this ns
|
|
controllerNamespace = "tf-controller-namespace"
|
|
legacyNamespace = "legacy-namespace"
|
|
configurationCMName = "tf-config-cm"
|
|
secretSuffix = "secret-suffix"
|
|
secretNS = "default"
|
|
destroyJobName = "destroy-job"
|
|
applyJobName = "apply-job"
|
|
|
|
connectionSecretName = "conn"
|
|
connectionSecretNS = "conn-ns"
|
|
)
|
|
ctx := context.Background()
|
|
s := runtime.NewScheme()
|
|
v1beta1.AddToScheme(s)
|
|
v1beta2.AddToScheme(s)
|
|
corev1.AddToScheme(s)
|
|
batchv1.AddToScheme(s)
|
|
rbacv1.AddToScheme(s)
|
|
// this is default provider if not specified in configuration.spec.providerRef
|
|
baseProvider := &v1beta1.Provider{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "default",
|
|
Namespace: "default",
|
|
},
|
|
}
|
|
readyProvider := baseProvider.DeepCopy()
|
|
readyProvider.Status.State = types.ProviderIsReady
|
|
notReadyProvider := baseProvider.DeepCopy()
|
|
notReadyProvider.Status.State = types.ProviderIsNotReady
|
|
|
|
baseApplyJob := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: applyJobName,
|
|
Namespace: controllerNamespace,
|
|
},
|
|
}
|
|
applyJobInLegacyNS := baseApplyJob.DeepCopy()
|
|
applyJobInLegacyNS.Namespace = legacyNamespace
|
|
|
|
baseDestroyJob := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: destroyJobName,
|
|
Namespace: controllerNamespace,
|
|
},
|
|
}
|
|
completeDestroyJob := baseDestroyJob.DeepCopy()
|
|
completeDestroyJob.Status.Succeeded = int32(1)
|
|
|
|
// Resources to be GC
|
|
baseConfigurationCM := &corev1.ConfigMap{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ConfigMap",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: configurationCMName,
|
|
Namespace: controllerNamespace,
|
|
},
|
|
}
|
|
baseVariableSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: fmt.Sprintf(TFVariableSecret, secretSuffix),
|
|
Namespace: controllerNamespace,
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
connectionSecret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: connectionSecretName,
|
|
Namespace: connectionSecretNS,
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
ConfigurationCMInLegacyNS := baseConfigurationCM.DeepCopy()
|
|
ConfigurationCMInLegacyNS.Namespace = legacyNamespace
|
|
variableSecretInLegacyNS := baseVariableSecret.DeepCopy()
|
|
variableSecretInLegacyNS.Namespace = legacyNamespace
|
|
|
|
baseMeta := TFConfigurationMeta{
|
|
DestroyJobName: destroyJobName,
|
|
ControllerNamespace: controllerNamespace,
|
|
ProviderReference: &crossplane.Reference{
|
|
Name: baseProvider.Name,
|
|
Namespace: baseProvider.Namespace,
|
|
},
|
|
VariableSecretName: fmt.Sprintf(TFVariableSecret, secretSuffix),
|
|
ConfigurationCMName: configurationCMName,
|
|
// True is default value if user ignores configuration.Spec.DeleteResource
|
|
DeleteResource: true,
|
|
}
|
|
metaWithLegacyResource := baseMeta
|
|
metaWithLegacyResource.LegacySubResources = LegacySubResources{
|
|
Namespace: legacyNamespace,
|
|
ApplyJobName: applyJobName,
|
|
DestroyJobName: destroyJobName,
|
|
ConfigurationCMName: configurationCMName,
|
|
VariableSecretName: fmt.Sprintf(TFVariableSecret, secretSuffix),
|
|
}
|
|
|
|
baseConfiguration := &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "base-conf",
|
|
Namespace: "default",
|
|
},
|
|
}
|
|
configurationWithConnSecret := baseConfiguration.DeepCopy()
|
|
configurationWithConnSecret.Spec.WriteConnectionSecretToReference = &crossplane.SecretReference{
|
|
Name: connectionSecretName,
|
|
Namespace: connectionSecretNS,
|
|
}
|
|
configurationPrdNotFound := baseConfiguration.DeepCopy()
|
|
configurationPrdNotFound.Spec.ProviderReference = &crossplane.Reference{
|
|
Name: "not-exist",
|
|
Namespace: "default",
|
|
}
|
|
forceDeleteConfiguration := baseConfiguration.DeepCopy()
|
|
forceDeleteConfiguration.Spec.ForceDelete = pointer.Bool(true)
|
|
|
|
type args struct {
|
|
namespace string
|
|
configuration *v1beta2.Configuration
|
|
meta *TFConfigurationMeta
|
|
}
|
|
type want struct {
|
|
errMsg string
|
|
}
|
|
var testcases = []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
objects []client.Object
|
|
deletedResources []client.Object
|
|
keptResources []client.Object
|
|
}{
|
|
{
|
|
name: "dispatch destroy job, subresource not cleanup, job not completed",
|
|
args: args{
|
|
configuration: baseConfiguration,
|
|
meta: &baseMeta,
|
|
},
|
|
want: want{
|
|
errMsg: types.MessageDestroyJobNotCompleted,
|
|
},
|
|
objects: []client.Object{readyProvider, baseConfiguration, baseConfigurationCM},
|
|
keptResources: []client.Object{baseConfigurationCM},
|
|
},
|
|
{
|
|
name: "provider is not ready, cloud resource couldn't be created, delete directly",
|
|
args: args{
|
|
configuration: baseConfiguration,
|
|
meta: &baseMeta,
|
|
},
|
|
objects: []client.Object{notReadyProvider, baseConfiguration, baseConfigurationCM},
|
|
deletedResources: []client.Object{baseConfigurationCM},
|
|
want: want{},
|
|
},
|
|
{
|
|
name: "referenced provider not exist, allow to delete directly",
|
|
args: args{
|
|
configuration: configurationPrdNotFound,
|
|
meta: &baseMeta,
|
|
},
|
|
objects: []client.Object{readyProvider, baseConfiguration, baseConfigurationCM, baseVariableSecret},
|
|
want: want{},
|
|
deletedResources: []client.Object{baseConfigurationCM, baseVariableSecret},
|
|
},
|
|
{
|
|
name: "destroy job has completes, cleanup resources",
|
|
args: args{
|
|
configuration: configurationWithConnSecret,
|
|
meta: &baseMeta,
|
|
},
|
|
want: want{},
|
|
objects: []client.Object{readyProvider, configurationWithConnSecret, baseConfigurationCM, completeDestroyJob, baseVariableSecret, connectionSecret},
|
|
deletedResources: []client.Object{baseConfigurationCM, completeDestroyJob, baseVariableSecret, connectionSecret},
|
|
},
|
|
{
|
|
name: "force delete configuration",
|
|
args: args{
|
|
configuration: forceDeleteConfiguration,
|
|
meta: &baseMeta,
|
|
},
|
|
objects: []client.Object{readyProvider, forceDeleteConfiguration, baseConfigurationCM, completeDestroyJob, baseVariableSecret},
|
|
deletedResources: []client.Object{baseConfigurationCM, completeDestroyJob, baseVariableSecret},
|
|
},
|
|
{
|
|
name: "compatible to clean up resources when upgrade controller with --controller-namespace",
|
|
args: args{
|
|
configuration: baseConfiguration,
|
|
meta: &metaWithLegacyResource,
|
|
},
|
|
objects: []client.Object{baseConfiguration, variableSecretInLegacyNS, ConfigurationCMInLegacyNS, applyJobInLegacyNS},
|
|
deletedResources: []client.Object{variableSecretInLegacyNS, ConfigurationCMInLegacyNS, applyJobInLegacyNS},
|
|
want: want{},
|
|
},
|
|
}
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Prepare for test. 1. Build reconciler with fake client
|
|
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(tc.objects...).Build()
|
|
reconciler := &ConfigurationReconciler{
|
|
Client: k8sClient,
|
|
}
|
|
// 2. Set meta backend
|
|
tc.args.meta.Backend = &backend.K8SBackend{
|
|
Client: k8sClient,
|
|
SecretSuffix: secretSuffix,
|
|
SecretNS: secretNS,
|
|
}
|
|
|
|
err := reconciler.terraformDestroy(ctx, *tc.args.configuration, tc.args.meta)
|
|
if tc.want.errMsg != "" {
|
|
if err == nil {
|
|
t.Errorf("expected error: %s, got nil", tc.want.errMsg)
|
|
} else if !strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("expected error containing %q, got %q", tc.want.errMsg, err.Error())
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
for _, rsc := range tc.keptResources {
|
|
err = k8sClient.Get(ctx, client.ObjectKey{Namespace: rsc.GetNamespace(), Name: rsc.GetName()}, rsc)
|
|
assert.NoError(t, err, "resource should be kept %s/%s", rsc.GetNamespace(), rsc.GetName())
|
|
}
|
|
for _, rsc := range tc.deletedResources {
|
|
err = k8sClient.Get(ctx, client.ObjectKey{Namespace: rsc.GetNamespace(), Name: rsc.GetName()}, rsc)
|
|
assert.True(t, apierrors.IsNotFound(err), "resource %s/%s should be deleted", rsc.GetNamespace(), rsc.GetName())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAssembleTerraformJob(t *testing.T) {
|
|
meta := &TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
}
|
|
job := meta.assembleTerraformJob(TerraformApply)
|
|
containers := job.Spec.Template.Spec.InitContainers
|
|
assert.Equal(t, containers[0].Image, "c")
|
|
assert.Equal(t, containers[1].Image, "d")
|
|
}
|
|
|
|
func TestAssembleTerraformJobWithNodeSelectorSetting(t *testing.T) {
|
|
meta := &TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
JobNodeSelector: map[string]string{"ssd": "true"},
|
|
}
|
|
|
|
job := meta.assembleTerraformJob(TerraformApply)
|
|
spec := job.Spec.Template.Spec
|
|
assert.Equal(t, spec.NodeSelector, map[string]string{"ssd": "true"})
|
|
}
|
|
|
|
func TestAssembleTerraformJobWithResourcesSetting(t *testing.T) {
|
|
quantityLimitsCPU, _ := resource.ParseQuantity("10m")
|
|
quantityLimitsMemory, _ := resource.ParseQuantity("10Mi")
|
|
quantityRequestsCPU, _ := resource.ParseQuantity("100m")
|
|
quantityRequestsMemory, _ := resource.ParseQuantity("5Gi")
|
|
meta := &TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
|
|
ResourceQuota: ResourceQuota{
|
|
ResourcesLimitsCPU: "10m",
|
|
ResourcesLimitsCPUQuantity: quantityLimitsCPU,
|
|
ResourcesLimitsMemory: "10Mi",
|
|
ResourcesLimitsMemoryQuantity: quantityLimitsMemory,
|
|
ResourcesRequestsCPU: "100m",
|
|
ResourcesRequestsCPUQuantity: quantityRequestsCPU,
|
|
ResourcesRequestsMemory: "5Gi",
|
|
ResourcesRequestsMemoryQuantity: quantityRequestsMemory,
|
|
},
|
|
}
|
|
|
|
job := meta.assembleTerraformJob(TerraformApply)
|
|
initContainers := job.Spec.Template.Spec.InitContainers
|
|
assert.Equal(t, initContainers[0].Image, "c")
|
|
assert.Equal(t, initContainers[1].Image, "d")
|
|
|
|
container := job.Spec.Template.Spec.Containers[0]
|
|
limitsCPU := container.Resources.Limits["cpu"]
|
|
limitsMemory := container.Resources.Limits["memory"]
|
|
requestsCPU := container.Resources.Requests["cpu"]
|
|
requestsMemory := container.Resources.Requests["memory"]
|
|
assert.Equal(t, "10m", limitsCPU.String())
|
|
assert.Equal(t, "10Mi", limitsMemory.String())
|
|
assert.Equal(t, "100m", requestsCPU.String())
|
|
assert.Equal(t, "5Gi", requestsMemory.String())
|
|
}
|
|
|
|
func TestAssembleTerraformJobWithGitCredentialsSecretRef(t *testing.T) {
|
|
meta := &TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-ssh",
|
|
},
|
|
}
|
|
|
|
job := meta.assembleTerraformJob(TerraformApply)
|
|
spec := job.Spec.Template.Spec
|
|
|
|
var gitSecretDefaultMode int32 = 0400
|
|
gitAuthSecretVolume := corev1.Volume{Name: GitAuthConfigVolumeName}
|
|
gitAuthSecretVolume.Secret = &corev1.SecretVolumeSource{
|
|
SecretName: "git-ssh",
|
|
DefaultMode: &gitSecretDefaultMode,
|
|
}
|
|
|
|
gitSecretVolumeMount := corev1.VolumeMount{
|
|
Name: GitAuthConfigVolumeName,
|
|
MountPath: GitAuthConfigVolumeMountPath,
|
|
}
|
|
assert.Contains(t, spec.InitContainers[1].VolumeMounts, gitSecretVolumeMount)
|
|
assert.Contains(t, spec.Volumes, gitAuthSecretVolume)
|
|
}
|
|
|
|
func TestAssembleTerraformJobWithTerraformRCAndCredentials(t *testing.T) {
|
|
meta := &TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config",
|
|
},
|
|
TerraformCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials",
|
|
},
|
|
TerraformCredentialsHelperConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
}
|
|
|
|
job := meta.assembleTerraformJob(TerraformApply)
|
|
spec := job.Spec.Template.Spec
|
|
|
|
var terraformSecretDefaultMode int32 = 0400
|
|
|
|
terraformRegistryConfigMapVolume := corev1.Volume{Name: TerraformRCConfigVolumeName}
|
|
terraformRegistryConfigMapVolume.ConfigMap = &corev1.ConfigMapVolumeSource{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "terraform-registry-config",
|
|
},
|
|
DefaultMode: &terraformSecretDefaultMode,
|
|
}
|
|
|
|
terraformRegistryConfigVolumeMount := corev1.VolumeMount{
|
|
Name: TerraformRCConfigVolumeName,
|
|
MountPath: TerraformRCConfigVolumeMountPath,
|
|
}
|
|
terraformCredentialsSecretVolume := corev1.Volume{Name: TerraformCredentialsConfigVolumeName}
|
|
terraformCredentialsSecretVolume.Secret = &corev1.SecretVolumeSource{
|
|
SecretName: "terraform-credentials",
|
|
DefaultMode: &terraformSecretDefaultMode,
|
|
}
|
|
|
|
terraformCredentialsSecretVolumeMount := corev1.VolumeMount{
|
|
Name: TerraformCredentialsConfigVolumeName,
|
|
MountPath: TerraformCredentialsConfigVolumeMountPath,
|
|
}
|
|
|
|
terraformCredentialsHelperConfigVolume := corev1.Volume{Name: TerraformCredentialsHelperConfigVolumeName}
|
|
terraformCredentialsHelperConfigVolume.ConfigMap = &corev1.ConfigMapVolumeSource{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
DefaultMode: &terraformSecretDefaultMode,
|
|
}
|
|
|
|
terraformCredentialsHelperConfigVolumeMount := corev1.VolumeMount{
|
|
Name: TerraformCredentialsHelperConfigVolumeName,
|
|
MountPath: TerraformCredentialsHelperConfigVolumeMountPath,
|
|
}
|
|
|
|
assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformCredentialsHelperConfigVolumeMount)
|
|
assert.Contains(t, spec.Volumes, terraformCredentialsHelperConfigVolume)
|
|
|
|
assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformRegistryConfigVolumeMount)
|
|
assert.Contains(t, spec.Volumes, terraformRegistryConfigMapVolume)
|
|
|
|
assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformCredentialsSecretVolumeMount)
|
|
assert.Contains(t, spec.Volumes, terraformCredentialsSecretVolume)
|
|
|
|
assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformRegistryConfigVolumeMount)
|
|
assert.Contains(t, spec.Volumes, terraformRegistryConfigMapVolume)
|
|
}
|
|
|
|
func TestAssembleTerraformJobWithTerraformRCAndCredentialsHelper(t *testing.T) {
|
|
meta := &TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config",
|
|
},
|
|
TerraformCredentialsHelperConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
}
|
|
|
|
job := meta.assembleTerraformJob(TerraformApply)
|
|
spec := job.Spec.Template.Spec
|
|
|
|
var terraformSecretDefaultMode int32 = 0400
|
|
|
|
terraformRegistryConfigMapVolume := corev1.Volume{Name: TerraformRCConfigVolumeName}
|
|
terraformRegistryConfigMapVolume.ConfigMap = &corev1.ConfigMapVolumeSource{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "terraform-registry-config",
|
|
},
|
|
DefaultMode: &terraformSecretDefaultMode,
|
|
}
|
|
|
|
terraformRegistryConfigVolumeMount := corev1.VolumeMount{
|
|
Name: TerraformRCConfigVolumeName,
|
|
MountPath: TerraformRCConfigVolumeMountPath,
|
|
}
|
|
terraformCredentialsHelperConfigVolume := corev1.Volume{Name: TerraformCredentialsHelperConfigVolumeName}
|
|
terraformCredentialsHelperConfigVolume.ConfigMap = &corev1.ConfigMapVolumeSource{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
DefaultMode: &terraformSecretDefaultMode,
|
|
}
|
|
|
|
terraformCredentialsHelperConfigVolumeMount := corev1.VolumeMount{
|
|
Name: TerraformCredentialsHelperConfigVolumeName,
|
|
MountPath: TerraformCredentialsHelperConfigVolumeMountPath,
|
|
}
|
|
|
|
assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformRegistryConfigVolumeMount)
|
|
assert.Contains(t, spec.Volumes, terraformRegistryConfigMapVolume)
|
|
|
|
assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformCredentialsHelperConfigVolumeMount)
|
|
assert.Contains(t, spec.Volumes, terraformCredentialsHelperConfigVolume)
|
|
|
|
}
|
|
|
|
func TestTfStatePropertyToToProperty(t *testing.T) {
|
|
testcases := []TfStateProperty{
|
|
{
|
|
Value: 123,
|
|
Type: "integer",
|
|
},
|
|
{
|
|
Value: 123.1,
|
|
Type: "float64",
|
|
},
|
|
{
|
|
Value: "123",
|
|
Type: "string",
|
|
},
|
|
{
|
|
Value: true,
|
|
Type: "bool",
|
|
},
|
|
{
|
|
Value: []interface{}{"21", "aaa", 12, true},
|
|
Type: "slice",
|
|
},
|
|
{
|
|
Value: map[string]interface{}{
|
|
"test1": "abc",
|
|
"test2": 123,
|
|
},
|
|
Type: "map",
|
|
},
|
|
}
|
|
for _, testcase := range testcases {
|
|
property, err := testcase.ToProperty()
|
|
assert.Equal(t, err, nil)
|
|
if testcase.Type == "integer" {
|
|
assert.Equal(t, property.Value, "123")
|
|
}
|
|
if testcase.Type == "float64" {
|
|
assert.Equal(t, property.Value, "123.1")
|
|
}
|
|
if testcase.Type == "bool" {
|
|
assert.Equal(t, property.Value, "true")
|
|
}
|
|
if testcase.Type == "slice" {
|
|
assert.Equal(t, property.Value, `["21","aaa",12,true]`)
|
|
}
|
|
if testcase.Type == "map" {
|
|
assert.Equal(t, property.Value, `{"test1":"abc","test2":123}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsTFStateGenerated(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
k8sClient client.Client
|
|
configuration v1beta2.Configuration
|
|
meta *TFConfigurationMeta
|
|
}
|
|
type want struct {
|
|
generated bool
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
k8sClient1 := fake.NewClientBuilder().Build()
|
|
meta1 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient1,
|
|
SecretSuffix: "a",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
secret2 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-a",
|
|
Namespace: "default",
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
k8sClient2 := fake.NewClientBuilder().WithObjects(secret2).Build()
|
|
meta2 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient2,
|
|
SecretSuffix: "a",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
tfStateData3, _ := base64.StdEncoding.DecodeString("H4sIAAAAAAAA/0SMwa7CIBBF9/0KMutH80ArDb9ijKHDYEhqMQO4afrvBly4POfc3H0QAt7EOaYNrDj/NS7E7ELi5/1XQI3/o4beM3F0K1ihO65xI/egNsLThLPRWi6agkR/CVIppaSZJrfgbBx6//1ItbxqyWDFfnTBlFNlpKaut+EYPgEAAP//xUXpvZsAAAA=")
|
|
secret3 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-a",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"tfstate": tfStateData3,
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
k8sClient3 := fake.NewClientBuilder().WithObjects(secret3).Build()
|
|
meta3 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient3,
|
|
SecretSuffix: "a",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
tfStateData4, _ := base64.StdEncoding.DecodeString("H4sIAAAAAAAA/4SQzarbMBCF934KoXUdPKNf+1VKCWNp5AocO8hyaSl592KlcBd3cZfnHPHpY/52QshfXI68b3IS+tuVK5dCaS+P+8ci4TbcULb94JJplZPAFte8MS18PQrKBO8Q+xk59SHa1AMA9M4YmoN3FGJ8M/azPs96yElcCkLIsG+V8sblnqOc3uXlRuvZ0GxSSuiCRUYbw2gGHRFGPxitEgJYQDQ0a68I2ChNo1cAZJ2bR20UtW8bsv55NuJRS94W2erXe5X5QQs3A/FZ4fhJaOwUgZTVMRjto1HGpSGSQuuD955hdDDPcR6NY1ZpQJ/YwagTRAvBpsi8LXn7Pa1U+ahfWHX/zWThYz9L4Otg3390r+5fAAAA//8hmcuNuQEAAA==")
|
|
secret4 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-a",
|
|
Namespace: "default",
|
|
},
|
|
Data: map[string][]byte{
|
|
"tfstate": tfStateData4,
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
k8sClient4 := fake.NewClientBuilder().WithObjects(secret4).Build()
|
|
meta4 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient4,
|
|
SecretSuffix: "a",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
testcases := map[string]struct {
|
|
args args
|
|
want want
|
|
}{
|
|
"could not find backend secret": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient1,
|
|
meta: meta1,
|
|
},
|
|
want: want{
|
|
generated: false,
|
|
},
|
|
},
|
|
"no data in a backend secret": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient2,
|
|
meta: meta2,
|
|
},
|
|
want: want{
|
|
generated: false,
|
|
},
|
|
},
|
|
"outputs in the backend secret are empty.": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient3,
|
|
meta: meta3,
|
|
},
|
|
want: want{
|
|
generated: false,
|
|
},
|
|
},
|
|
"outputs in the backend secret are not empty": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient4,
|
|
meta: meta4,
|
|
},
|
|
want: want{
|
|
generated: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, tc := range testcases {
|
|
t.Run(name, func(t *testing.T) {
|
|
generated := tc.args.meta.isTFStateGenerated(tc.args.ctx)
|
|
assert.Equal(t, tc.want.generated, generated)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetTFOutputs(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
k8sClient client.Client
|
|
configuration v1beta2.Configuration
|
|
meta *TFConfigurationMeta
|
|
}
|
|
type want struct {
|
|
property map[string]v1beta2.Property
|
|
errMsg string
|
|
}
|
|
|
|
ctx := context.Background()
|
|
k8sClient1 := fake.NewClientBuilder().Build()
|
|
meta1 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient1,
|
|
SecretSuffix: "a",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
secret2 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-a",
|
|
Namespace: "default",
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
}
|
|
k8sClient2 := fake.NewClientBuilder().WithObjects(secret2).Build()
|
|
meta2 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient2,
|
|
SecretSuffix: "a",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
tfStateData, _ := base64.StdEncoding.DecodeString("H4sIAAAAAAAA/4SQzarbMBCF934KoXUdPKNf+1VKCWNp5AocO8hyaSl592KlcBd3cZfnHPHpY/52QshfXI68b3IS+tuVK5dCaS+P+8ci4TbcULb94JJplZPAFte8MS18PQrKBO8Q+xk59SHa1AMA9M4YmoN3FGJ8M/azPs96yElcCkLIsG+V8sblnqOc3uXlRuvZ0GxSSuiCRUYbw2gGHRFGPxitEgJYQDQ0a68I2ChNo1cAZJ2bR20UtW8bsv55NuJRS94W2erXe5X5QQs3A/FZ4fhJaOwUgZTVMRjto1HGpSGSQuuD955hdDDPcR6NY1ZpQJ/YwagTRAvBpsi8LXn7Pa1U+ahfWHX/zWThYz9L4Otg3390r+5fAAAA//8hmcuNuQEAAA==")
|
|
tfStateOutputs := map[string]v1beta2.Property{
|
|
"container_id": {
|
|
Value: "e5fff27c62e26dc9504d21980543f21161225ab483a1e534a98311a677b9453a",
|
|
},
|
|
"image_id": {
|
|
Value: "sha256:d1a364dc548d5357f0da3268c888e1971bbdb957ee3f028fe7194f1d61c6fdeenginx:latest",
|
|
},
|
|
}
|
|
|
|
secret3 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-b",
|
|
Namespace: "default",
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
Data: map[string][]byte{
|
|
"tfstate": tfStateData,
|
|
},
|
|
}
|
|
k8sClient3 := fake.NewClientBuilder().WithObjects(secret3).Build()
|
|
meta3 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient3,
|
|
SecretSuffix: "b",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
secret4 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-c",
|
|
Namespace: "default",
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
Data: map[string][]byte{
|
|
"tfstate": tfStateData,
|
|
},
|
|
}
|
|
k8sClient4 := fake.NewClientBuilder().WithObjects(secret4).Build()
|
|
configuration4 := v1beta2.Configuration{
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
WriteConnectionSecretToReference: &crossplane.SecretReference{
|
|
Name: "connection-secret-c",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
meta4 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient4,
|
|
SecretSuffix: "c",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
secret5 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-d",
|
|
Namespace: "default",
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
Data: map[string][]byte{
|
|
"tfstate": tfStateData,
|
|
},
|
|
}
|
|
oldConnectionSecret5 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "connection-secret-d",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
"terraform.core.oam.dev/created-by": "terraform-controller",
|
|
"terraform.core.oam.dev/owned-by": "configuration5",
|
|
},
|
|
},
|
|
TypeMeta: metav1.TypeMeta{Kind: "Secret"},
|
|
Data: map[string][]byte{
|
|
"container_id": []byte("something"),
|
|
},
|
|
}
|
|
k8sClient5 := fake.NewClientBuilder().WithObjects(secret5, oldConnectionSecret5).Build()
|
|
configuration5 := v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "configuration5",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
WriteConnectionSecretToReference: &crossplane.SecretReference{
|
|
Name: "connection-secret-d",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
meta5 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient5,
|
|
SecretSuffix: "d",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
secret6 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-e",
|
|
Namespace: "default",
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
Data: map[string][]byte{
|
|
"tfstate": tfStateData,
|
|
},
|
|
}
|
|
oldConnectionSecret6 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "connection-secret-e",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
"terraform.core.oam.dev/created-by": "terraform-controller",
|
|
"terraform.core.oam.dev/owned-by": "configuration5",
|
|
"terraform.core.oam.dev/owned-namespace": "default",
|
|
},
|
|
},
|
|
TypeMeta: metav1.TypeMeta{Kind: "Secret"},
|
|
Data: map[string][]byte{
|
|
"container_id": []byte("something"),
|
|
},
|
|
}
|
|
k8sClient6 := fake.NewClientBuilder().WithObjects(secret6, oldConnectionSecret6).Build()
|
|
configuration6 := v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "configuration6",
|
|
Namespace: "default",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
WriteConnectionSecretToReference: &crossplane.SecretReference{
|
|
Name: "connection-secret-e",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
meta6 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient6,
|
|
SecretSuffix: "e",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
namespaceA := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "a"}}
|
|
namespaceB := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "b"}}
|
|
secret7 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-f",
|
|
Namespace: "a",
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
Data: map[string][]byte{
|
|
"tfstate": tfStateData,
|
|
},
|
|
}
|
|
oldConnectionSecret7 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "connection-secret-e",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
"terraform.core.oam.dev/created-by": "terraform-controller",
|
|
"terraform.core.oam.dev/owned-by": "configuration6",
|
|
"terraform.core.oam.dev/owned-namespace": "a",
|
|
},
|
|
},
|
|
TypeMeta: metav1.TypeMeta{Kind: "Secret"},
|
|
Data: map[string][]byte{
|
|
"container_id": []byte("something"),
|
|
},
|
|
}
|
|
k8sClient7 := fake.NewClientBuilder().WithObjects(namespaceA, namespaceB, secret7, oldConnectionSecret7).Build()
|
|
configuration7 := v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "configuration6",
|
|
Namespace: "b",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
WriteConnectionSecretToReference: &crossplane.SecretReference{
|
|
Name: "connection-secret-e",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
meta7 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient7,
|
|
SecretSuffix: "f",
|
|
SecretNS: "a",
|
|
},
|
|
}
|
|
|
|
secret8 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "tfstate-default-d",
|
|
Namespace: "default",
|
|
},
|
|
Type: corev1.SecretTypeOpaque,
|
|
Data: map[string][]byte{
|
|
"tfstate": tfStateData,
|
|
},
|
|
}
|
|
oldConnectionSecret8 := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "connection-secret-d",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
"terraform.core.oam.dev/created-by": "terraform-controller",
|
|
},
|
|
},
|
|
TypeMeta: metav1.TypeMeta{Kind: "Secret"},
|
|
Data: map[string][]byte{
|
|
"container_id": []byte("something"),
|
|
},
|
|
}
|
|
k8sClient8 := fake.NewClientBuilder().WithObjects(secret8, oldConnectionSecret8).Build()
|
|
configuration8 := v1beta2.Configuration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: "configuration5",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
WriteConnectionSecretToReference: &crossplane.SecretReference{
|
|
Name: "connection-secret-d",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
}
|
|
meta8 := &TFConfigurationMeta{
|
|
Backend: &backend.K8SBackend{
|
|
Client: k8sClient8,
|
|
SecretSuffix: "d",
|
|
SecretNS: "default",
|
|
},
|
|
}
|
|
|
|
testcases := map[string]struct {
|
|
args args
|
|
want want
|
|
}{
|
|
"could not find backend secret": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient1,
|
|
meta: meta1,
|
|
},
|
|
want: want{
|
|
property: nil,
|
|
errMsg: "terraform state file backend secret is not generated",
|
|
},
|
|
},
|
|
"no data in a backend secret": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient2,
|
|
meta: meta2,
|
|
},
|
|
want: want{
|
|
property: nil,
|
|
errMsg: "failed to get tfstate from Terraform State secret",
|
|
},
|
|
},
|
|
"some data in a backend secret": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient3,
|
|
meta: meta3,
|
|
},
|
|
want: want{
|
|
property: tfStateOutputs,
|
|
errMsg: "",
|
|
},
|
|
},
|
|
"some data in a backend secret and creates a connectionSecret": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient4,
|
|
configuration: configuration4,
|
|
meta: meta4,
|
|
},
|
|
want: want{
|
|
property: tfStateOutputs,
|
|
errMsg: "",
|
|
},
|
|
},
|
|
"some data in a backend secret and update a connectionSecret belong to the same configuration": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient5,
|
|
configuration: configuration5,
|
|
meta: meta5,
|
|
},
|
|
want: want{
|
|
property: tfStateOutputs,
|
|
errMsg: "",
|
|
},
|
|
},
|
|
"some data in a backend secret and update a connectionSecret belong to another configuration": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient6,
|
|
configuration: configuration6,
|
|
meta: meta6,
|
|
},
|
|
want: want{
|
|
property: nil,
|
|
errMsg: "configuration(namespace: default ; name: configuration6) cannot update secret(namespace: default ; name: connection-secret-e) whose owner is configuration(namespace: default ; name: configuration5)",
|
|
},
|
|
},
|
|
"update a connectionSecret belong to another configuration(same name but different namespace": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient7,
|
|
configuration: configuration7,
|
|
meta: meta7,
|
|
},
|
|
want: want{
|
|
property: nil,
|
|
errMsg: "configuration(namespace: b ; name: configuration6) cannot update secret(namespace: default ; name: connection-secret-e) whose owner is configuration(namespace: a ; name: configuration6)",
|
|
},
|
|
},
|
|
"update a connectionSecret without owner labels": {
|
|
args: args{
|
|
ctx: ctx,
|
|
k8sClient: k8sClient8,
|
|
configuration: configuration8,
|
|
meta: meta8,
|
|
},
|
|
want: want{
|
|
property: tfStateOutputs,
|
|
errMsg: "",
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, tc := range testcases {
|
|
t.Run(name, func(t *testing.T) {
|
|
property, err := tc.args.meta.getTFOutputs(tc.args.ctx, tc.args.k8sClient, tc.args.configuration)
|
|
if tc.want.errMsg != "" {
|
|
if !strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("getTFOutputs() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
}
|
|
}
|
|
assert.Equal(t, tc.want.property, property)
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestUpdateApplyStatus(t *testing.T) {
|
|
type args struct {
|
|
k8sClient client.Client
|
|
state types.ConfigurationState
|
|
message string
|
|
meta *TFConfigurationMeta
|
|
}
|
|
type want struct {
|
|
errMsg string
|
|
}
|
|
ctx := context.Background()
|
|
s := runtime.NewScheme()
|
|
v1beta2.AddToScheme(s)
|
|
|
|
configuration := &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
Generation: int64(1),
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
HCL: "c",
|
|
},
|
|
Status: v1beta2.ConfigurationStatus{
|
|
Apply: v1beta2.ConfigurationApplyStatus{
|
|
State: types.Available,
|
|
},
|
|
},
|
|
}
|
|
k8sClient = fake.NewClientBuilder().WithScheme(s).WithObjects(configuration).Build()
|
|
|
|
testcases := map[string]struct {
|
|
args args
|
|
want want
|
|
}{
|
|
"configuration is available": {
|
|
args: args{
|
|
meta: &TFConfigurationMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
},
|
|
state: types.Available,
|
|
message: "xxx",
|
|
},
|
|
},
|
|
"configuration cloud not be found": {
|
|
args: args{
|
|
meta: &TFConfigurationMeta{
|
|
Name: "z",
|
|
Namespace: "b",
|
|
},
|
|
state: types.Available,
|
|
message: "xxx",
|
|
},
|
|
},
|
|
}
|
|
for name, tc := range testcases {
|
|
t.Run(name, func(t *testing.T) {
|
|
err := tc.args.meta.updateApplyStatus(ctx, k8sClient, tc.args.state, tc.args.message)
|
|
if tc.want.errMsg != "" || err != nil {
|
|
if !strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("updateApplyStatus() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAssembleAndTriggerJob(t *testing.T) {
|
|
type prepare func(t *testing.T)
|
|
type args struct {
|
|
k8sClient client.Client
|
|
executionType TerraformExecutionType
|
|
prepare
|
|
}
|
|
type want struct {
|
|
errMsg string
|
|
}
|
|
ctx := context.Background()
|
|
k8sClient = fake.NewClientBuilder().Build()
|
|
meta := &TFConfigurationMeta{
|
|
Namespace: "b",
|
|
}
|
|
|
|
patches := gomonkey.ApplyFunc(apiutil.GVKForObject, func(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error) {
|
|
return schema.GroupVersionKind{}, apierrors.NewNotFound(schema.GroupResource{}, "")
|
|
})
|
|
defer patches.Reset()
|
|
|
|
testcases := map[string]struct {
|
|
args args
|
|
want want
|
|
}{
|
|
"failed to create ServiceAccount": {
|
|
args: args{
|
|
executionType: TerraformApply,
|
|
},
|
|
want: want{
|
|
errMsg: "failed to create ServiceAccount for Terraform executor",
|
|
},
|
|
},
|
|
}
|
|
for name, tc := range testcases {
|
|
t.Run(name, func(t *testing.T) {
|
|
err := meta.assembleAndTriggerJob(ctx, k8sClient, tc.args.executionType)
|
|
if tc.want.errMsg != "" || err != nil {
|
|
if !strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("assembleAndTriggerJob() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckWhetherConfigurationChanges(t *testing.T) {
|
|
type args struct {
|
|
k8sClient client.Client
|
|
configurationType types.ConfigurationType
|
|
meta *TFConfigurationMeta
|
|
}
|
|
type want struct {
|
|
errMsg string
|
|
}
|
|
ctx := context.Background()
|
|
cm := &corev1.ConfigMap{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "a",
|
|
Namespace: "b",
|
|
},
|
|
Data: map[string]string{
|
|
"c": "d",
|
|
},
|
|
}
|
|
k8sClient = fake.NewClientBuilder().WithObjects(cm).Build()
|
|
|
|
testcases := map[string]struct {
|
|
args args
|
|
want want
|
|
}{
|
|
"unknown configuration type": {
|
|
args: args{
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "a",
|
|
Namespace: "b",
|
|
ControllerNamespace: "b",
|
|
},
|
|
configurationType: "xxx",
|
|
},
|
|
want: want{
|
|
errMsg: "unsupported configuration type, only HCL or Remote is supported",
|
|
},
|
|
},
|
|
"configuration map is not found": {
|
|
args: args{
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "aaa",
|
|
Namespace: "b",
|
|
ControllerNamespace: "b",
|
|
},
|
|
configurationType: "HCL",
|
|
},
|
|
want: want{
|
|
errMsg: "",
|
|
},
|
|
},
|
|
"configuration type is remote": {
|
|
args: args{
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "aaa",
|
|
Namespace: "b",
|
|
ControllerNamespace: "b",
|
|
},
|
|
configurationType: "Remote",
|
|
},
|
|
want: want{
|
|
errMsg: "",
|
|
},
|
|
},
|
|
"create configuration for the first time": {
|
|
args: args{
|
|
meta: &TFConfigurationMeta{
|
|
ConfigurationCMName: "aa",
|
|
Namespace: "b",
|
|
ControllerNamespace: "b",
|
|
},
|
|
configurationType: "HCL",
|
|
},
|
|
want: want{
|
|
errMsg: "",
|
|
},
|
|
},
|
|
}
|
|
for name, tc := range testcases {
|
|
t.Run(name, func(t *testing.T) {
|
|
err := tc.args.meta.CheckWhetherConfigurationChanges(ctx, k8sClient, tc.args.configurationType)
|
|
if tc.want.errMsg != "" || err != nil {
|
|
if !strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("CheckWhetherConfigurationChanges() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
}
|
|
}
|
|
|
|
if tc.want.errMsg == "" {
|
|
assert.Nil(t, err)
|
|
assert.False(t, tc.args.meta.ConfigurationChanged)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetApplyJob(t *testing.T) {
|
|
const (
|
|
applyJobNameWithUID = "xxxx-xxxx-xxxx"
|
|
controllerNamespace = "ctrl-ns"
|
|
applyJobName = "configuraion-apply"
|
|
jobNamespace = "configuration-ns"
|
|
//legacyApplyJobName = "legacy-job-name"
|
|
//legacyJobNamespace = "legacy-job-ns"
|
|
)
|
|
|
|
ctx := context.Background()
|
|
s := runtime.NewScheme()
|
|
v1beta1.AddToScheme(s)
|
|
v1beta2.AddToScheme(s)
|
|
batchv1.AddToScheme(s)
|
|
baseMeta := TFConfigurationMeta{
|
|
ApplyJobName: applyJobName,
|
|
ControllerNamespace: jobNamespace,
|
|
}
|
|
baseJob := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: applyJobName,
|
|
Namespace: jobNamespace,
|
|
},
|
|
}
|
|
jobInCtrlNS := &batchv1.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: applyJobNameWithUID,
|
|
Namespace: controllerNamespace,
|
|
},
|
|
}
|
|
legacyJob := baseJob.DeepCopy()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
|
|
objects []client.Object
|
|
wantErr error
|
|
meta TFConfigurationMeta
|
|
}{
|
|
{
|
|
name: "get job successfully",
|
|
objects: []client.Object{baseJob},
|
|
meta: baseMeta,
|
|
},
|
|
{name: "get legacy job successfully",
|
|
objects: []client.Object{legacyJob},
|
|
meta: TFConfigurationMeta{LegacySubResources: LegacySubResources{
|
|
Namespace: jobNamespace,
|
|
ApplyJobName: applyJobName,
|
|
}},
|
|
},
|
|
{
|
|
name: "get job in controller namespace",
|
|
objects: []client.Object{jobInCtrlNS},
|
|
meta: TFConfigurationMeta{
|
|
ControllerNamespace: controllerNamespace,
|
|
ApplyJobName: applyJobNameWithUID,
|
|
},
|
|
},
|
|
{
|
|
name: "not get any job",
|
|
objects: []client.Object{},
|
|
meta: TFConfigurationMeta{
|
|
ControllerNamespace: controllerNamespace,
|
|
ApplyJobName: applyJobNameWithUID,
|
|
LegacySubResources: LegacySubResources{
|
|
Namespace: jobNamespace,
|
|
ApplyJobName: applyJobName,
|
|
},
|
|
},
|
|
wantErr: errors.New("not found"),
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var job batchv1.Job
|
|
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(tc.objects...).Build()
|
|
|
|
err := tc.meta.getApplyJob(ctx, k8sClient, &job)
|
|
if tc.wantErr != nil {
|
|
if err == nil {
|
|
t.Errorf("expected error: %s, got nil", tc.wantErr)
|
|
} else if !strings.Contains(err.Error(), tc.wantErr.Error()) {
|
|
t.Errorf("expected error containing %q, got %q", tc.wantErr, err.Error())
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRenderConfiguration(t *testing.T) {
|
|
type args struct {
|
|
configuration *v1beta2.Configuration
|
|
configurationType types.ConfigurationType
|
|
credentials map[string]string
|
|
controllerNSSpecified bool
|
|
}
|
|
type want struct {
|
|
cfg string
|
|
backendInterface backend.Backend
|
|
errMsg string
|
|
}
|
|
|
|
k8sClient := fake.NewClientBuilder().Build()
|
|
baseMeta := TFConfigurationMeta{
|
|
K8sClient: k8sClient,
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "backend is not nil, configuration is hcl",
|
|
args: args{
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "n1",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
Backend: &v1beta2.Backend{},
|
|
HCL: "image_id=123",
|
|
},
|
|
},
|
|
configurationType: types.ConfigurationHCL,
|
|
},
|
|
want: want{
|
|
cfg: `image_id=123
|
|
|
|
terraform {
|
|
backend "kubernetes" {
|
|
secret_suffix = ""
|
|
in_cluster_config = true
|
|
namespace = "n1"
|
|
}
|
|
}
|
|
`,
|
|
backendInterface: &backend.K8SBackend{
|
|
Client: k8sClient,
|
|
HCLCode: `
|
|
terraform {
|
|
backend "kubernetes" {
|
|
secret_suffix = ""
|
|
in_cluster_config = true
|
|
namespace = "n1"
|
|
}
|
|
}
|
|
`,
|
|
SecretSuffix: "",
|
|
SecretNS: "n1",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "backend is nil, configuration is remote",
|
|
args: args{
|
|
configuration: &v1beta2.Configuration{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "n2",
|
|
},
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
Remote: "https://github.com/a/b.git",
|
|
},
|
|
},
|
|
configurationType: types.ConfigurationRemote,
|
|
},
|
|
want: want{
|
|
cfg: `
|
|
terraform {
|
|
backend "kubernetes" {
|
|
secret_suffix = ""
|
|
in_cluster_config = true
|
|
namespace = "n2"
|
|
}
|
|
}
|
|
`,
|
|
backendInterface: &backend.K8SBackend{
|
|
Client: k8sClient,
|
|
HCLCode: `
|
|
terraform {
|
|
backend "kubernetes" {
|
|
secret_suffix = ""
|
|
in_cluster_config = true
|
|
namespace = "n2"
|
|
}
|
|
}
|
|
`,
|
|
SecretSuffix: "",
|
|
SecretNS: "n2",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "backend is nil, configuration is not supported",
|
|
args: args{
|
|
configuration: &v1beta2.Configuration{
|
|
Spec: v1beta2.ConfigurationSpec{},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "Unsupported Configuration Type",
|
|
},
|
|
},
|
|
{
|
|
name: "controller-namespace specified, backend should have legacy secret suffix",
|
|
args: args{
|
|
configuration: &v1beta2.Configuration{
|
|
Spec: v1beta2.ConfigurationSpec{
|
|
Backend: nil,
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
UID: "xxxx-xxxx",
|
|
Namespace: "n2",
|
|
Name: "name",
|
|
},
|
|
},
|
|
controllerNSSpecified: true,
|
|
configurationType: types.ConfigurationRemote,
|
|
},
|
|
want: want{
|
|
cfg: `
|
|
terraform {
|
|
backend "kubernetes" {
|
|
secret_suffix = "xxxx-xxxx"
|
|
in_cluster_config = true
|
|
namespace = "n2"
|
|
}
|
|
}
|
|
`,
|
|
backendInterface: &backend.K8SBackend{
|
|
Client: k8sClient,
|
|
HCLCode: `
|
|
terraform {
|
|
backend "kubernetes" {
|
|
secret_suffix = "xxxx-xxxx"
|
|
in_cluster_config = true
|
|
namespace = "n2"
|
|
}
|
|
}
|
|
`,
|
|
SecretSuffix: "xxxx-xxxx",
|
|
SecretNS: "n2",
|
|
LegacySecretSuffix: "name",
|
|
}},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
meta := baseMeta
|
|
meta.ControllerNSSpecified = tc.args.controllerNSSpecified
|
|
got, backendConf, err := meta.RenderConfiguration(tc.args.configuration, tc.args.configurationType)
|
|
if tc.want.errMsg != "" && !strings.Contains(err.Error(), tc.want.errMsg) {
|
|
t.Errorf("ValidConfigurationObject() error = %v, wantErr %v", err, tc.want.errMsg)
|
|
return
|
|
}
|
|
if tc.want.errMsg == "" && err != nil {
|
|
t.Errorf("ValidConfigurationObject() error = %v, wantErr nil", err)
|
|
return
|
|
}
|
|
assert.Equal(t, tc.want.cfg, got)
|
|
|
|
if !reflect.DeepEqual(tc.want.backendInterface, backendConf) {
|
|
t.Errorf("backendInterface is not equal.\n got %#v\n, want %#v", backendConf, tc.want.backendInterface)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckGitCredentialsSecretReference(t *testing.T) {
|
|
ctx := context.Background()
|
|
scheme := runtime.NewScheme()
|
|
corev1.AddToScheme(scheme)
|
|
k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build()
|
|
|
|
privateKey := []byte("aaa")
|
|
knownHosts := []byte("zzz")
|
|
secret := &corev1.Secret{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "git-ssh",
|
|
},
|
|
Data: map[string][]byte{
|
|
corev1.SSHAuthPrivateKey: privateKey,
|
|
"known_hosts": knownHosts,
|
|
},
|
|
Type: corev1.SecretTypeSSHAuth,
|
|
}
|
|
assert.Nil(t, k8sClient.Create(ctx, secret))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(secret), secret))
|
|
|
|
secretNoKnownhost := &corev1.Secret{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "git-ssh-no-known-hosts",
|
|
},
|
|
Data: map[string][]byte{
|
|
corev1.SSHAuthPrivateKey: privateKey,
|
|
},
|
|
Type: corev1.SecretTypeSSHAuth,
|
|
}
|
|
assert.Nil(t, k8sClient.Create(ctx, secretNoKnownhost))
|
|
|
|
secretNoPrivateKey := &corev1.Secret{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "git-ssh-no-private-key",
|
|
},
|
|
Data: map[string][]byte{
|
|
"known_hosts": knownHosts,
|
|
},
|
|
}
|
|
assert.Nil(t, k8sClient.Create(ctx, secretNoPrivateKey))
|
|
|
|
type args struct {
|
|
k8sClient client.Client
|
|
GitCredentialsSecretReference *corev1.SecretReference
|
|
}
|
|
|
|
type want struct {
|
|
secret *corev1.Secret
|
|
errMsg string
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "secret not found",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-shh",
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "Failed to get git credentials secret: secrets \"git-shh\" not found",
|
|
},
|
|
},
|
|
{
|
|
name: "key 'known_hosts' not in git credentials secret",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-ssh-no-known-hosts",
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: fmt.Sprintf("'%s' not in git credentials secret", GitCredsKnownHosts),
|
|
},
|
|
},
|
|
{
|
|
name: "key 'ssh-privatekey' not in git credentials secret",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-ssh-no-private-key",
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: fmt.Sprintf("'%s' not in git credentials secret", corev1.SSHAuthPrivateKey),
|
|
},
|
|
},
|
|
{
|
|
name: "secret exists",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-ssh",
|
|
},
|
|
},
|
|
want: want{
|
|
secret: secret,
|
|
},
|
|
},
|
|
}
|
|
neededKeys := []string{GitCredsKnownHosts, corev1.SSHAuthPrivateKey}
|
|
errKey := "git credentials"
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
sec, err := GetSecretOrConfigMap(ctx, tc.args.k8sClient, true, tc.args.GitCredentialsSecretReference, neededKeys, errKey)
|
|
if err != nil {
|
|
assert.EqualError(t, err, tc.want.errMsg)
|
|
}
|
|
if tc.want.secret != nil {
|
|
assert.EqualValues(t, sec, tc.want.secret)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckTerraformCredentialsSecretReference(t *testing.T) {
|
|
ctx := context.Background()
|
|
scheme := runtime.NewScheme()
|
|
corev1.AddToScheme(scheme)
|
|
k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build()
|
|
|
|
credentialstfrcjson := []byte("tfcreds")
|
|
terraformrc := []byte("tfrc")
|
|
|
|
secret := &corev1.Secret{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "terraform-creds",
|
|
},
|
|
Data: map[string][]byte{
|
|
"credentials.tfrc.json": credentialstfrcjson,
|
|
"terraformrc": terraformrc,
|
|
},
|
|
Type: corev1.SecretTypeSSHAuth,
|
|
}
|
|
assert.Nil(t, k8sClient.Create(ctx, secret))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(secret), secret))
|
|
|
|
secretNotTerraformCreds := &corev1.Secret{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "terraform-creds-no-creds",
|
|
},
|
|
Data: map[string][]byte{
|
|
"terraformrc": terraformrc,
|
|
},
|
|
Type: corev1.SecretTypeSSHAuth,
|
|
}
|
|
|
|
assert.Nil(t, k8sClient.Create(ctx, secretNotTerraformCreds))
|
|
|
|
type args struct {
|
|
k8sClient client.Client
|
|
TerraformCredentialsSecretReference *corev1.SecretReference
|
|
}
|
|
|
|
type want struct {
|
|
secret *corev1.Secret
|
|
errMsg string
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "secret not found",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
TerraformCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "secret-not-exists",
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "Failed to get terraform credentials secret: secrets \"secret-not-exists\" not found",
|
|
},
|
|
},
|
|
{
|
|
name: "key 'credentials.tfrc.json' not in terraform credentials secret",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
TerraformCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-creds-no-creds",
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: fmt.Sprintf("'%s' not in terraform credentials secret", TerraformCredentials),
|
|
},
|
|
},
|
|
{
|
|
name: "secret exists",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
TerraformCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-creds",
|
|
},
|
|
},
|
|
want: want{
|
|
secret: secret,
|
|
},
|
|
},
|
|
}
|
|
|
|
neededKeys := []string{TerraformCredentials}
|
|
errKey := "terraform credentials"
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
sec, err := GetSecretOrConfigMap(ctx, tc.args.k8sClient, true, tc.args.TerraformCredentialsSecretReference, neededKeys, errKey)
|
|
|
|
if err != nil {
|
|
assert.EqualError(t, err, tc.want.errMsg)
|
|
}
|
|
if tc.want.secret != nil {
|
|
assert.EqualValues(t, sec, tc.want.secret)
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestCheckTerraformRCConfigMapReference(t *testing.T) {
|
|
ctx := context.Background()
|
|
scheme := runtime.NewScheme()
|
|
corev1.AddToScheme(scheme)
|
|
k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build()
|
|
|
|
configMap := &corev1.ConfigMap{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config",
|
|
},
|
|
Data: map[string]string{
|
|
".terraformrc": "tfrc",
|
|
},
|
|
}
|
|
|
|
assert.Nil(t, k8sClient.Create(ctx, configMap))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(configMap), configMap))
|
|
|
|
configMapNotTerraformRc := &corev1.ConfigMap{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config-no-terraformrc",
|
|
},
|
|
Data: map[string]string{
|
|
"terraform": "tfrc",
|
|
},
|
|
}
|
|
|
|
assert.Nil(t, k8sClient.Create(ctx, configMapNotTerraformRc))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(configMapNotTerraformRc), configMapNotTerraformRc))
|
|
|
|
type args struct {
|
|
k8sClient client.Client
|
|
TerraformRCConfigMapReference *corev1.SecretReference
|
|
}
|
|
|
|
type want struct {
|
|
configMap *corev1.ConfigMap
|
|
errMsg string
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "configmap not found",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "configmap-not-exists",
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "Failed to get terraformrc configuration configmap: configmaps \"configmap-not-exists\" not found",
|
|
},
|
|
},
|
|
{
|
|
name: "key '.terraformrc' not in terraform registry config",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config-no-terraformrc",
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: fmt.Sprintf("'%s' not in terraformrc configuration configmap", TerraformRegistryConfig),
|
|
},
|
|
},
|
|
{
|
|
name: "configmap exists",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config",
|
|
},
|
|
},
|
|
want: want{
|
|
configMap: configMap,
|
|
},
|
|
},
|
|
}
|
|
|
|
neededKeys := []string{TerraformRegistryConfig}
|
|
errKey := "terraformrc configuration"
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
configMap, err := GetSecretOrConfigMap(ctx, tc.args.k8sClient, false, tc.args.TerraformRCConfigMapReference, neededKeys, errKey)
|
|
|
|
if err != nil {
|
|
assert.EqualError(t, err, tc.want.errMsg)
|
|
}
|
|
if tc.want.configMap != nil {
|
|
assert.EqualValues(t, configMap, tc.want.configMap)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTerraformCredentialsHelperConfigMap(t *testing.T) {
|
|
ctx := context.Background()
|
|
scheme := runtime.NewScheme()
|
|
corev1.AddToScheme(scheme)
|
|
k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build()
|
|
|
|
configMap := &corev1.ConfigMap{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
Data: map[string]string{
|
|
"terraform-credentials-artifactory": "tfrc",
|
|
},
|
|
}
|
|
|
|
assert.Nil(t, k8sClient.Create(ctx, configMap))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(configMap), configMap))
|
|
|
|
type args struct {
|
|
k8sClient client.Client
|
|
TerraformCredentialsHelperConfigMapReference *corev1.SecretReference
|
|
}
|
|
|
|
type want struct {
|
|
configMap *corev1.ConfigMap
|
|
errMsg string
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "configmap not found",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
TerraformCredentialsHelperConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry",
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "Failed to get terraform credentials helper configmap: configmaps \"terraform-registry\" not found",
|
|
},
|
|
},
|
|
{
|
|
name: "configmap exists",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
TerraformCredentialsHelperConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
},
|
|
want: want{
|
|
configMap: configMap,
|
|
},
|
|
},
|
|
}
|
|
|
|
neededKeys := []string{}
|
|
errKey := "terraform credentials helper"
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
configMap, err := GetSecretOrConfigMap(ctx, tc.args.k8sClient, false, tc.args.TerraformCredentialsHelperConfigMapReference, neededKeys, errKey)
|
|
|
|
if err != nil {
|
|
assert.EqualError(t, err, tc.want.errMsg)
|
|
}
|
|
if tc.want.configMap != nil {
|
|
assert.EqualValues(t, configMap, tc.want.configMap)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckValidateSecretAndConfigMap(t *testing.T) {
|
|
ctx := context.Background()
|
|
scheme := runtime.NewScheme()
|
|
corev1.AddToScheme(scheme)
|
|
k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build()
|
|
|
|
privateKey := []byte("aaa")
|
|
knownHosts := []byte("zzz")
|
|
secretGitCreds := &corev1.Secret{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "git-ssh",
|
|
},
|
|
Data: map[string][]byte{
|
|
corev1.SSHAuthPrivateKey: privateKey,
|
|
"known_hosts": knownHosts,
|
|
},
|
|
Type: corev1.SecretTypeSSHAuth,
|
|
}
|
|
assert.Nil(t, k8sClient.Create(ctx, secretGitCreds))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(secretGitCreds), secretGitCreds))
|
|
|
|
credentialstfrcjson := []byte("tfcreds")
|
|
terraformrc := []byte("tfrc")
|
|
secretTerraformCredentials := &corev1.Secret{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "terraform-creds",
|
|
},
|
|
Data: map[string][]byte{
|
|
"credentials.tfrc.json": credentialstfrcjson,
|
|
"terraformrc": terraformrc,
|
|
},
|
|
Type: corev1.SecretTypeSSHAuth,
|
|
}
|
|
assert.Nil(t, k8sClient.Create(ctx, secretTerraformCredentials))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(secretTerraformCredentials), secretTerraformCredentials))
|
|
|
|
configMapTerraformRC := &corev1.ConfigMap{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config",
|
|
},
|
|
Data: map[string]string{
|
|
".terraformrc": "tfrc",
|
|
},
|
|
}
|
|
|
|
assert.Nil(t, k8sClient.Create(ctx, configMapTerraformRC))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(configMapTerraformRC), configMapTerraformRC))
|
|
|
|
configMapCredentialsHelper := &corev1.ConfigMap{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
Data: map[string]string{
|
|
"terraform-credentials-artifactory": "tfrc",
|
|
},
|
|
}
|
|
|
|
assert.Nil(t, k8sClient.Create(ctx, configMapCredentialsHelper))
|
|
assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(configMapCredentialsHelper), configMapCredentialsHelper))
|
|
|
|
type args struct {
|
|
k8sClient client.Client
|
|
meta TFConfigurationMeta
|
|
}
|
|
|
|
type want struct {
|
|
configMap *corev1.ConfigMap
|
|
errMsg string
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "configmap not found",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
meta: TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
ControllerNamespace: "default",
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-ssh",
|
|
},
|
|
TerraformCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-creds",
|
|
},
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config",
|
|
},
|
|
TerraformCredentialsHelperConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "NoError",
|
|
},
|
|
},
|
|
{
|
|
name: "terraform credentials configmap not found",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
meta: TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
ControllerNamespace: "default",
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-ssh",
|
|
},
|
|
TerraformCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-creds",
|
|
},
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config",
|
|
},
|
|
TerraformCredentialsHelperConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "Failed to get terraform credentials helper configmap: configmaps \"terraform-registry\" not found",
|
|
},
|
|
},
|
|
{
|
|
name: "terraformrc configmap not found",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
meta: TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
ControllerNamespace: "default",
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-ssh",
|
|
},
|
|
TerraformCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-creds",
|
|
},
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry",
|
|
},
|
|
TerraformCredentialsHelperConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "Failed to get terraformrc configuration configmap: configmaps \"terraform-registry\" not found",
|
|
},
|
|
},
|
|
{
|
|
name: "git-ssh secret invalid namespace",
|
|
args: args{
|
|
k8sClient: k8sClient,
|
|
meta: TFConfigurationMeta{
|
|
Name: "a",
|
|
ConfigurationCMName: "b",
|
|
BusyboxImage: "c",
|
|
GitImage: "d",
|
|
Namespace: "e",
|
|
TerraformImage: "f",
|
|
RemoteGit: "g",
|
|
ControllerNamespace: "vela-system",
|
|
GitCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "git-ssh",
|
|
},
|
|
TerraformCredentialsSecretReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-creds",
|
|
},
|
|
TerraformRCConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-registry-config",
|
|
},
|
|
TerraformCredentialsHelperConfigMapReference: &corev1.SecretReference{
|
|
Namespace: "default",
|
|
Name: "terraform-credentials-helper",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
errMsg: "Invalid Secret 'default/git-ssh', whose namespace 'vela-system' is different from the Configuration, cannot mount the volume," +
|
|
" you can fix this issue by creating the Secret/ConfigMap in the 'vela-system' namespace.",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := tc.args.meta.validateSecretAndConfigMap(ctx, k8sClient)
|
|
if err != nil {
|
|
assert.EqualError(t, err, tc.want.errMsg)
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|