Repository: schemahero/schemahero
Pull request #138 (2020-04-05 16:29:07)

Generating better names for plan phase


Patch:
diff --git a/README.md b/README.md
index 025e7942..a28a2726 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
 
 ## What is SchemaHero?
 
-SchemaHero is a Kubernetes Operator for [Declarative Schema Management](https://schemahero.io/overview/about/declarative-schema-management/) for [various databases](https://schemahero.io/databases). SchemaHero has the following goals:
+SchemaHero is a Kubernetes Operator for [Declarative Schema Management](https://schemahero.io/docs/concepts/declarative-schema-management/) for [various databases](https://schemahero.io/docs/databases//). SchemaHero has the following goals:
 
 1. Database table schemas can be expressed as [Kubernetes resources](https://schemahero.io/docs/managing-tables/creating-tables/) that can be deployed to a cluster.
 2. Database schemas can be edited and deployed to the cluster. SchemaHero will calculate the required change (`ALTER TABLE` statement) and apply it.
@@ -24,10 +24,11 @@ The recommended way to deploy SchemaHero is:
 kubectl apply -f https://git.io/schemahero
 ```
 
-[Additional installation options](https://git.io/schemahero) are available in the documentation.
+[Additional installation options](https://git.io/schemahero) are availabe in the documentation.
 
 To get started, read the [tutorial](https://schemahero.io/tutorial/) and the [full documentation](https://schemahero.io/docs/)
 
 # Community
 
 For questions about using SchemaHero, there's a [Replicated Community](https://help.replicated.com/community) forum, and a [#schemahero channel in Kubernetes Slack](https://kubernetes.slack.com/channels/schemahero).
+
diff --git a/docs/README.md b/docs/README.md
index 51079d5f..b2838e8f 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,3 +1,3 @@
 # SchemaHero Docs
 
-The SchemaHero documentation is at https://schemahero.io/docs
+The SchemaHero documentation is at https://docs.schemahero.io
diff --git a/pkg/controller/migration/migration_controller.go b/pkg/controller/migration/migration_controller.go
index ce22f2e9..25311ebd 100644
--- a/pkg/controller/migration/migration_controller.go
+++ b/pkg/controller/migration/migration_controller.go
@@ -107,7 +107,7 @@ func (r *ReconcileMigration) Reconcile(request reconcile.Request) (reconcile.Res
 	// so this function is simply an entrypoint that executes the right reconcile loop
 	instance, instanceErr := r.getInstance(request)
 	if instanceErr == nil {
-		result, err := r.reconcileInstance(context.Background(), instance)
+		result, err := r.reconcileMigration(context.Background(), instance)
 		if err != nil {
 			logger.Error(err)
 		}
@@ -117,7 +117,7 @@ func (r *ReconcileMigration) Reconcile(request reconcile.Request) (reconcile.Res
 	pod := &corev1.Pod{}
 	podErr := r.Get(context.Background(), request.NamespacedName, pod)
 	if podErr == nil {
-		result, err := r.reconcilePod(pod)
+		result, err := r.reconcilePod(context.Background(), pod)
 		if err != nil {
 			logger.Error(err)
 		}
@@ -127,6 +127,16 @@ func (r *ReconcileMigration) Reconcile(request reconcile.Request) (reconcile.Res
 	return reconcile.Result{}, errors.New("unknown error in migration reconciler")
 }
 
+func (r *ReconcileMigration) getInstance(request reconcile.Request) (*schemasv1alpha3.Migration, error) {
+	v1alpha3instance := &schemasv1alpha3.Migration{}
+	err := r.Get(context.Background(), request.NamespacedName, v1alpha3instance)
+	if err != nil {
+		return nil, err // don't wrap
+	}
+
+	return v1alpha3instance, nil
+}
+
 func (r *ReconcileMigration) readConnectionURI(database *databasesv1alpha3.Database) (string, error) {
 	var valueOrValueFrom *databasesv1alpha3.ValueOrValueFrom
 
diff --git a/pkg/controller/migration/reconcile.go b/pkg/controller/migration/reconcile_migration.go
similarity index 85%
rename from pkg/controller/migration/reconcile.go
rename to pkg/controller/migration/reconcile_migration.go
index b0f85a44..9e89b119 100644
--- a/pkg/controller/migration/reconcile.go
+++ b/pkg/controller/migration/reconcile_migration.go
@@ -14,17 +14,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
-func (r *ReconcileMigration) getInstance(request reconcile.Request) (*schemasv1alpha3.Migration, error) {
-	v1alpha3instance := &schemasv1alpha3.Migration{}
-	err := r.Get(context.Background(), request.NamespacedName, v1alpha3instance)
-	if err != nil {
-		return nil, err // don't wrap
-	}
-
-	return v1alpha3instance, nil
-}
-
-func (r *ReconcileMigration) reconcileInstance(ctx context.Context, instance *schemasv1alpha3.Migration) (reconcile.Result, error) {
+func (r *ReconcileMigration) reconcileMigration(ctx context.Context, instance *schemasv1alpha3.Migration) (reconcile.Result, error) {
 	logger.Debug("reconciling migration",
 		zap.String("kind", instance.Kind),
 		zap.String("name", instance.Name),
@@ -122,13 +112,3 @@ func (r *ReconcileMigration) reconcileInstance(ctx context.Context, instance *sc
 
 	return reconcile.Result{}, nil
 }
-
-func (r *ReconcileMigration) reconcilePod(pod *corev1.Pod) (reconcile.Result, error) {
-	// podLabels := pod.GetObjectMeta().GetLabels()
-	// role, ok := podLabels["schemahero-role"]
-	// if !ok {
-	// 	return reconcile.Result{}, nil
-	// }
-
-	return reconcile.Result{}, nil
-}
diff --git a/pkg/controller/migration/reconcile_pod.go b/pkg/controller/migration/reconcile_pod.go
new file mode 100644
index 00000000..4866c2bd
--- /dev/null
+++ b/pkg/controller/migration/reconcile_pod.go
@@ -0,0 +1,41 @@
+package migration
+
+import (
+	"context"
+
+	"github.com/pkg/errors"
+	"github.com/schemahero/schemahero/pkg/logger"
+	"go.uber.org/zap"
+	corev1 "k8s.io/api/core/v1"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+func (r *ReconcileMigration) reconcilePod(ctx context.Context, pod *corev1.Pod) (reconcile.Result, error) {
+	podLabels := pod.GetObjectMeta().GetLabels()
+	role, ok := podLabels["schemahero-role"]
+	if !ok {
+		return reconcile.Result{}, nil
+	}
+
+	if role != "" && role != "apply" {
+		// make sure we are filtering to relevant events
+		return reconcile.Result{}, nil
+	}
+
+	logger.Debug("reconciling schemahero pod",
+		zap.String("kind", pod.Kind),
+		zap.String("name", pod.Name),
+		zap.String("role", role),
+		zap.String("podPhase", string(pod.Status.Phase)))
+
+	if pod.Status.Phase != corev1.PodSucceeded {
+		return reconcile.Result{}, nil
+	}
+
+	// delete it
+	if err := r.Delete(ctx, pod); err != nil {
+		return reconcile.Result{}, errors.Wrap(err, "failed to delete apply pod")
+	}
+
+	return reconcile.Result{}, nil
+}
diff --git a/pkg/controller/table/objects.go b/pkg/controller/table/objects.go
index d492d6c5..982e8a4c 100644
--- a/pkg/controller/table/objects.go
+++ b/pkg/controller/table/objects.go
@@ -1,7 +1,7 @@
 package table
 
 import (
-	"context"
+	"crypto/sha256"
 	"fmt"
 
 	"github.com/pkg/errors"
@@ -9,13 +9,54 @@ import (
 	schemasv1alpha3 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha3"
 	"gopkg.in/yaml.v2"
 	corev1 "k8s.io/api/core/v1"
-	kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
+	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
 )
 
-func planConfigMap(namespace string, tableName string, tableSpec schemasv1alpha3.TableSpec) (*corev1.ConfigMap, error) {
-	b, err := yaml.Marshal(tableSpec)
+func configMapNameForPlan(database *databasesv1alpha3.Database, table *schemasv1alpha3.Table) string {
+	shortID, err := getShortIDForTableSpec(table)
+	if err != nil {
+		return table.Name
+	}
+	configMapName := fmt.Sprintf("%s-%s-%s-plan", database.Name, table.Name, shortID)
+	if len(apimachineryvalidation.NameIsDNSSubdomain(configMapName, false)) > 0 {
+		configMapName = fmt.Sprintf("%s-%s-plan", table.Name, shortID)
+		if len(apimachineryvalidation.NameIsDNSSubdomain(configMapName, false)) > 0 {
+			configMapName = fmt.Sprintf("%s-plan", shortID)
+		}
+	}
+
+	return configMapName
+}
+
+func podNameForPlan(database *databasesv1alpha3.Database, table *schemasv1alpha3.Table) string {
+	shortID, err := getShortIDForTableSpec(table)
+	if err != nil {
+		return table.Name
+	}
+	podName := fmt.Sprintf("%s-%s-%s-plan", database.Name, table.Name, shortID)
+	if len(apimachineryvalidation.NameIsDNSSubdomain(podName, false)) > 0 {
+		podName = fmt.Sprintf("%s-%s-plan", table.Name, shortID)
+		if len(apimachineryvalidation.NameIsDNSSubdomain(podName, false)) > 0 {
+			podName = fmt.Sprintf("%s-plan", shortID)
+		}
+	}
+
+	return podName
+}
+
+func getShortIDForTableSpec(table *schemasv1alpha3.Table) (string, error) {
+	b, err := yaml.Marshal(table.Spec)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to marshal yaml spec")
+	}
+
+	sum := sha256.Sum256(b)
+	return fmt.Sprintf("%x", sum)[:7], nil
+}
+
+func getPlanConfigMap(database *databasesv1alpha3.Database, table *schemasv1alpha3.Table) (*corev1.ConfigMap, error) {
+	b, err := yaml.Marshal(table.Spec)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to marshal yaml spec")
 	}
@@ -23,11 +64,10 @@ func planConfigMap(namespace string, tableName string, tableSpec schemasv1alpha3
 	tableData := make(map[string]string)
 	tableData["table.yaml"] = string(b)
 
-	name := tableName
 	configMap := &corev1.ConfigMap{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
+			Name:      configMapNameForPlan(database, table),
+			Namespace: table.Namespace,
 		},
 		TypeMeta: metav1.TypeMeta{
 			APIVersion: "v1",
@@ -39,7 +79,7 @@ func planConfigMap(namespace string, tableName string, tableSpec schemasv1alpha3
 	return configMap, nil
 }
 
-func (r *ReconcileTable) planPod(database *databasesv1alpha3.Database, table *schemasv1alpha3.Table) (*corev1.Pod, error) {
+func (r *ReconcileTable) getPlanPod(database *databasesv1alpha3.Database, table *schemasv1alpha3.Table) (*corev1.Pod, error) {
 	imageName := "schemahero/schemahero:alpha"
 	nodeSelector := make(map[string]string)
 	driver := ""
@@ -78,9 +118,6 @@ func (r *ReconcileTable) planPod(database *databasesv1alpha3.Database, table *sc
 	labels["schemahero-namespace"] = table.Namespace
 	labels["schemahero-role"] = "plan"
 
-	name := fmt.Sprintf("%s-plan", table.Name)
-	configMapName := table.Name
-
 	args := []string{
 		"plan",
 		"--driver",
@@ -93,7 +130,7 @@ func (r *ReconcileTable) planPod(database *databasesv1alpha3.Database, table *sc
 
 	pod := &corev1.Pod{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
+			Name:      podNameForPlan(database, table),
 			Namespace: database.Namespace,
 			Labels:    labels,
 		},
@@ -125,7 +162,7 @@ func (r *ReconcileTable) planPod(database *databasesv1alpha3.Database, table *sc
 					VolumeSource: corev1.VolumeSource{
 						ConfigMap: &corev1.ConfigMapVolumeSource{
 							LocalObjectReference: corev1.LocalObjectReference{
-								Name: configMapName,
+								Name: configMapNameForPlan(database, table),
 							},
 						},
 					},
@@ -136,37 +173,3 @@ func (r *ReconcileTable) planPod(database *databasesv1alpha3.Database, table *sc
 
 	return pod, nil
 }
-
-func (r *ReconcileTable) ensureTableConfigMap(ctx context.Context, desiredConfigMap *corev1.ConfigMap) error {
-	existingConfigMap := corev1.ConfigMap{}
-	if err := r.Get(ctx, types.NamespacedName{Name: desiredConfigMap.Name, Namespace: desiredConfigMap.Namespace}, &existingConfigMap); err != nil {
-		if kuberneteserrors.IsNotFound(err) {
-			err = r.Create(ctx, desiredConfigMap)
-			if err != nil {
-				return errors.Wrap(err, "failed to create configmap")
-			}
-		}
-
-		return errors.Wrap(err, "failed to get existing configmap")
-	}
-
-	return nil
-}
-
-func (r *ReconcileTable) ensureTablePod(ctx context.Context, desiredPod *corev1.Pod) error {
-	existingPod := corev1.Pod{}
-	if err := r.Get(ctx, types.NamespacedName{Name: desiredPod.Name, Namespace: desiredPod.Namespace}, &existingPod); err != nil {
-		if kuberneteserrors.IsNotFound(err) {
-			err = r.Create(ctx, desiredPod)
-			if err != nil {
-				return errors.Wrap(err, "failed to create table migration pod")
-			}
-
-			return nil
-		}
-
-		return errors.Wrap(err, "failed to get existing pod object")
-	}
-
-	return nil
-}
diff --git a/pkg/controller/table/plan.go b/pkg/controller/table/reconcile_pod.go
similarity index 90%
rename from pkg/controller/table/plan.go
rename to pkg/controller/table/reconcile_pod.go
index 1989103f..f0e23dd0 100644
--- a/pkg/controller/table/plan.go
+++ b/pkg/controller/table/reconcile_pod.go
@@ -31,17 +31,17 @@ func (r *ReconcileTable) reconcilePod(ctx context.Context, pod *corev1.Pod) (rec
 		return reconcile.Result{}, nil
 	}
 
+	if role != "table" && role != "plan" {
+		// we want to avoid migration pods in this reconciler
+		return reconcile.Result{}, nil
+	}
+
 	logger.Debug("reconciling schemahero pod",
 		zap.String("kind", pod.Kind),
 		zap.String("name", pod.Name),
 		zap.String("role", role),
 		zap.String("podPhase", string(pod.Status.Phase)))
 
-	if role != "table" && role != "plan" {
-		// we want to avoid migration pods in this reconciler
-		return reconcile.Result{}, nil
-	}
-
 	if pod.Status.Phase != corev1.PodSucceeded {
 		return reconcile.Result{}, nil
 	}
@@ -188,29 +188,6 @@ func (r *ReconcileTable) reconcilePod(ctx context.Context, pod *corev1.Pod) (rec
 	return reconcile.Result{}, nil
 }
 
-func (r *ReconcileTable) plan(ctx context.Context, database *databasesv1alpha3.Database, table *schemasv1alpha3.Table) error {
-	logger.Debug("deploying plan")
-
-	configMap, err := planConfigMap(database.Namespace, table.Name, table.Spec)
-	if err != nil {
-		return errors.Wrap(err, "failed to get config map object for plan")
-	}
-	pod, err := r.planPod(database, table)
-	if err != nil {
-		return errors.Wrap(err, "failed to get pod for plan")
-	}
-
-	if err := r.ensureTableConfigMap(ctx, configMap); err != nil {
-		return errors.Wrap(err, "failed to create config map for plan")
-	}
-
-	if err := r.ensureTablePod(ctx, pod); err != nil {
-		return errors.Wrap(err, "failerd to create pod for plan")
-	}
-
-	return nil
-}
-
 func (r *ReconcileTable) readConnectionURI(namespace string, valueOrValueFrom databasesv1alpha3.ValueOrValueFrom) (string, error) {
 	if valueOrValueFrom.Value != "" {
 		return valueOrValueFrom.Value, nil
diff --git a/pkg/controller/table/reconcile_test.go b/pkg/controller/table/reconcile_table_test.go
similarity index 100%
rename from pkg/controller/table/reconcile_test.go
rename to pkg/controller/table/reconcile_table_test.go
diff --git a/pkg/controller/table/reconile.go b/pkg/controller/table/reconile_table.go
similarity index 54%
rename from pkg/controller/table/reconile.go
rename to pkg/controller/table/reconile_table.go
index 5d9805cf..4dd60bce 100644
--- a/pkg/controller/table/reconile.go
+++ b/pkg/controller/table/reconile_table.go
@@ -10,23 +10,29 @@ import (
 	databasesclientv1alpha3 "github.com/schemahero/schemahero/pkg/client/schemaheroclientset/typed/databases/v1alpha3"
 	"github.com/schemahero/schemahero/pkg/logger"
 	"go.uber.org/zap"
+	corev1 "k8s.io/api/core/v1"
 	kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client/config"
+	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
-func (r *ReconcileTable) reconcileInstance(ctx context.Context, instance *schemasv1alpha3.Table) (reconcile.Result, error) {
+func (r *ReconcileTable) reconcileTable(ctx context.Context, instance *schemasv1alpha3.Table) (reconcile.Result, error) {
 	logger.Debug("reconciling table",
 		zap.String("kind", instance.Kind),
 		zap.String("name", instance.Name),
 		zap.String("database", instance.Spec.Database))
 
+	// get the full database spec from the api
 	database, err := r.getDatabaseSpec(ctx, instance.Namespace, instance.Spec.Database)
 	if err != nil {
 		return reconcile.Result{}, errors.Wrap(err, "failed to get database spec")
 	}
 
+	// the database object might not yet exist
+	// this can happen if the table was deployed at the same time or before the database object
 	if database == nil {
 		// TDOO add a status field with this state
 		logger.Debug("requeuing table reconcile request for 10 seconds because database instance was not present",
@@ -64,11 +70,7 @@ func (r *ReconcileTable) reconcileInstance(ctx context.Context, instance *schema
 	}
 
 	// Deploy a pod to calculculate the plan
-	if err := r.plan(ctx, database, instance); err != nil {
-		return reconcile.Result{}, errors.Wrap(err, "failed to schedule plan phase")
-	}
-
-	return reconcile.Result{}, nil
+	return r.deployMigrationPlanPhase(ctx, database, instance)
 }
 
 func (r *ReconcileTable) getInstance(request reconcile.Request) (*schemasv1alpha3.Table, error) {
@@ -140,3 +142,87 @@ func checkDatabaseTypeMatches(connection *databasesv1alpha3.DatabaseConnection,
 
 	return false
 }
+
+func (r *ReconcileTable) deployMigrationPlanPhase(ctx context.Context, database *databasesv1alpha3.Database, table *schemasv1alpha3.Table) (reconcile.Result, error) {
+	logger.Debug("deploying plan phase of migration",
+		zap.String("databaseName", database.Name),
+		zap.String("tableName", table.Name))
+
+	desiredConfigMap, err := getPlanConfigMap(database, table)
+	if err != nil {
+		return reconcile.Result{}, errors.Wrap(err, "failed to get config map object for plan")
+	}
+	var existingConfigMap corev1.ConfigMap
+	configMapChanged := false
+	err = r.Get(ctx, types.NamespacedName{
+		Name:      desiredConfigMap.Name,
+		Namespace: desiredConfigMap.Namespace,
+	}, &existingConfigMap)
+	if kuberneteserrors.IsNotFound(err) {
+		// create it
+		if err := controllerutil.SetControllerReference(table, desiredConfigMap, r.scheme); err != nil {
+			return reconcile.Result{}, errors.Wrap(err, "failed to set owner on configmap")
+		}
+		if err := r.Create(ctx, desiredConfigMap); err != nil {
+			return reconcile.Result{}, errors.Wrap(err, "failed to create config map")
+		}
+	} else if err == nil {
+		// update it
+		existingConfigMap.Data = map[string]string{}
+		for k, v := range desiredConfigMap.Data {
+			d, ok := existingConfigMap.Data[k]
+			if !ok || d != v {
+				existingConfigMap.Data[k] = v
+				configMapChanged = true
+			}
+		}
+		if configMapChanged {
+			if err := controllerutil.SetControllerReference(table, &existingConfigMap, r.scheme); err != nil {
+				return reconcile.Result{}, errors.Wrap(err, "failed to update owner on configmap")
+			}
+			if err = r.Update(ctx, &existingConfigMap); err != nil {
+				return reconcile.Result{}, errors.Wrap(err, "failed to update config map")
+			}
+		}
+	} else {
+		// something bad is happening here
+		return reconcile.Result{}, errors.Wrap(err, "failed to check if config map exists")
+	}
+
+	desiredPod, err := r.getPlanPod(database, table)
+	if err != nil {
+		return reconcile.Result{}, errors.Wrap(err, "failed to get pod for plan")
+	}
+	var existingPod corev1.Pod
+	err = r.Get(ctx, types.NamespacedName{
+		Name:      desiredPod.Name,
+		Namespace: desiredPod.Namespace,
+	}, &existingPod)
+	if kuberneteserrors.IsNotFound(err) {
+		// create it
+		if err := r.Create(ctx, desiredPod); err != nil {
+			return reconcile.Result{}, errors.Wrap(err, "failed to create plan pod")
+		}
+		if err := controllerutil.SetControllerReference(table, desiredPod, r.scheme); err != nil {
+			return reconcile.Result{}, errors.Wrap(err, "failed to set owner on pod")
+		}
+	} else if err == nil {
+		// maybe update it
+		if configMapChanged {
+			// restart the pod by deleting and recreating
+			logger.Debug("deleting plan pod because config map has changed",
+				zap.String("podName", existingPod.Name))
+			if err = r.Delete(ctx, &existingPod); err != nil {
+				return reconcile.Result{}, errors.Wrap(err, "failed to delete pod")
+			}
+
+			// This pod will be recreated later in another exceution of the reconcile loop
+			// we watch pods, and when that above delete completed, the reconcile will happen again
+		}
+	} else {
+		// again, something bad
+		return reconcile.Result{}, errors.Wrap(err, "failed to check if pod exists")
+	}
+
+	return reconcile.Result{}, nil
+}
diff --git a/pkg/controller/table/table_controller.go b/pkg/controller/table/table_controller.go
index c3985d35..a00e4fbd 100644
--- a/pkg/controller/table/table_controller.go
+++ b/pkg/controller/table/table_controller.go
@@ -107,7 +107,7 @@ func (r *ReconcileTable) Reconcile(request reconcile.Request) (reconcile.Result,
 	// so this function is simply an entrypoint that executes the right reconcile loop
 	instance, instanceErr := r.getInstance(request)
 	if instanceErr == nil {
-		result, err := r.reconcileInstance(context.Background(), instance)
+		result, err := r.reconcileTable(context.Background(), instance)
 		if err != nil {
 			logger.Error(err)
 		}


Test patch:
diff --git a/pkg/controller/table/objects_test.go b/pkg/controller/table/objects_test.go
index ad43ef81..5fc46eb9 100644
--- a/pkg/controller/table/objects_test.go
+++ b/pkg/controller/table/objects_test.go
@@ -3,31 +3,38 @@ package table
 import (
 	"testing"
 
+	databasesv1alpha3 "github.com/schemahero/schemahero/pkg/apis/databases/v1alpha3"
 	schemasv1alpha3 "github.com/schemahero/schemahero/pkg/apis/schemas/v1alpha3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
-	corev1 "k8s.io/api/core/v1"
 )
 
 func Test_planConfigMap(t *testing.T) {
 	tests := []struct {
-		name      string
-		namespace string
-		tableName string
-		tableSpec schemasv1alpha3.TableSpec
-		expect    corev1.ConfigMap
+		name     string
+		table    schemasv1alpha3.Table
+		database databasesv1alpha3.Database
+		expect   string
 	}{
 		{
-			name:      "basic test",
-			namespace: "foo",
-			tableName: "name",
-			tableSpec: schemasv1alpha3.TableSpec{
-				Database: "db",
-				Name:     "name",
-				Schema: &schemasv1alpha3.TableSchema{
-					Postgres: &schemasv1alpha3.SQLTableSchema{},
+			name: "basic test",
+			table: schemasv1alpha3.Table{
+				Spec: schemasv1alpha3.TableSpec{
+					Database: "db",
+					Name:     "name",
+					Schema: &schemasv1alpha3.TableSchema{
+						Postgres: &schemasv1alpha3.SQLTableSchema{},
+					},
 				},
 			},
+			database: databasesv1alpha3.Database{},
+			expect: `database: db
+name: name
+schema:
+  postgres:
+    primaryKey: []
+    columns: []
+`,
 		},
 	}
 
@@ -35,13 +42,11 @@ func Test_planConfigMap(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			req := require.New(t)
 
-			actual, err := planConfigMap(test.namespace, test.tableName, test.tableSpec)
+			actual, err := getPlanConfigMap(&test.database, &test.table)
 			req.NoError(err)
 
 			// check some of the fields on the config map
-			assert.Equal(t, test.tableName, actual.Name)
-			assert.Len(t, actual.Data, 1)
-			assert.NotNil(t, actual.Data["table.yaml"], actual.Data["table.yaml"])
+			assert.Equal(t, actual.Data["table.yaml"], test.expect)
 		})
 	}
 }