Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

controllers: support disposeDevDB flag #91

Merged
merged 3 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,25 @@ jobs:

# Expect the new column to be present.
kubectl exec $POD_MYSQL -- mysql -uroot -h 127.0.0.1 -ppass -e "SHOW COLUMNS FROM myapp.users LIKE 'phone';"

# Expect the devdb deployment is scaled to 0.
kubectl get deployment atlasschema-mysql-atlas-dev-db -o=jsonpath='{.spec.replicas}' | grep -q '0'

# SET PREWARM_DEVDB to true
kubectl set env -n atlas-operator-system deployment/atlas-operator-controller-manager PREWARM_DEVDB=true

# Reset database resources
kubectl delete pods -l app=mysql
kubectl wait --for condition=ready pods -l app=mysql --timeout=60s

# Apply the desired schema and wait for it to be ready.
kubectl delete -f config/integration/schema
kubectl apply -f config/integration/schema
kubectl wait --for=condition=ready --timeout=120s atlasschemas --all

# Expect the devdb deployment is scaled to 1.
kubectl get deployment atlasschema-mysql-atlas-dev-db -o=jsonpath='{.spec.replicas}' | grep -q '1'

- name: Reset database resources
run: |
kubectl delete pods -l app=mysql
Expand Down
5 changes: 5 additions & 0 deletions charts/atlas-operator/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ spec:
{{- toYaml .Values.resources | nindent 12 }}
securityContext:
{{- toYaml .Values.containerSecurityContext | nindent 12 }}
env:
{{- if .Values.prewarmDevDB }}
- name: PREWARM_DEVDB
value: "true"
{{- end }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
Expand Down
4 changes: 4 additions & 0 deletions charts/atlas-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ nodeSelector: {}
tolerations: []

affinity: {}

# By default, the operator will recreate devdb pods after migration
# Set this to true to keep the devdb pods around.
prewarmDevDB: true
4 changes: 3 additions & 1 deletion controllers/atlasschema_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type (
configMapWatcher *watch.ResourceWatcher
secretWatcher *watch.ResourceWatcher
recorder record.EventRecorder
prewarmDevDB bool
}
// managedData contains information about the managed database and its desired state.
managedData struct {
Expand All @@ -73,14 +74,15 @@ type (
}
)

func NewAtlasSchemaReconciler(mgr Manager, execPath string) *AtlasSchemaReconciler {
func NewAtlasSchemaReconciler(mgr Manager, execPath string, prewarmDevDB bool) *AtlasSchemaReconciler {
return &AtlasSchemaReconciler{
Client: mgr.GetClient(),
scheme: mgr.GetScheme(),
execPath: execPath,
configMapWatcher: watch.New(),
secretWatcher: watch.New(),
recorder: mgr.GetEventRecorderFor("atlasschema-controller"),
prewarmDevDB: prewarmDevDB,
}
}

Expand Down
5 changes: 4 additions & 1 deletion controllers/atlasschema_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

const (
Expand Down Expand Up @@ -109,7 +110,9 @@ func TestReconcile_Reconcile(t *testing.T) {
ObjectMeta: meta,
Spec: dbv1alpha1.AtlasSchemaSpec{},
}
h, reconcile := newRunner(NewAtlasSchemaReconciler, func(cb *fake.ClientBuilder) {
h, reconcile := newRunner(func(mgr Manager, name string) reconcile.Reconciler {
return NewAtlasSchemaReconciler(mgr, name, true)
}, func(cb *fake.ClientBuilder) {
cb.WithObjects(obj)
})
assert := func(except ctrl.Result, ready bool, reason, msg string) {
Expand Down
51 changes: 39 additions & 12 deletions controllers/devdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,33 @@ const (

// cleanUp clean up any resources created by the controller
func (r *AtlasSchemaReconciler) cleanUp(ctx context.Context, sc *dbv1alpha1.AtlasSchema) {
pods := &corev1.PodList{}
err := r.List(ctx, pods, client.MatchingLabels(map[string]string{
labelInstance: nameDevDB(sc.ObjectMeta).Name,
}))
if err != nil {
r.recorder.Eventf(sc, corev1.EventTypeWarning, "CleanUpDevDB", "Error listing devDB pods: %v", err)
}
for _, p := range pods.Items {
if err := r.Delete(ctx, &p); err != nil {
r.recorder.Eventf(sc, corev1.EventTypeWarning, "CleanUpDevDB", "Error deleting devDB pod %s: %v", p.Name, err)
// If prewarmDevDB is true, delete pods to clean up
// Otherwise, scale down the deployment to 0
if r.prewarmDevDB {
pods := &corev1.PodList{}
err := r.List(ctx, pods, client.MatchingLabels(map[string]string{
labelInstance: nameDevDB(sc.ObjectMeta).Name,
}))
if err != nil {
r.recorder.Eventf(sc, corev1.EventTypeWarning, "CleanUpDevDB", "Error listing devDB pods: %v", err)
}

for _, p := range pods.Items {
if err := r.Delete(ctx, &p); err != nil {
r.recorder.Eventf(sc, corev1.EventTypeWarning, "CleanUpDevDB", "Error deleting devDB pod %s: %v", p.Name, err)
}
}
} else {
giautm marked this conversation as resolved.
Show resolved Hide resolved
deploy := &appsv1.Deployment{}
key := nameDevDB(sc.ObjectMeta)
err := r.Get(ctx, key, deploy)
if err != nil {
r.recorder.Eventf(sc, corev1.EventTypeWarning, "CleanUpDevDB", "Error getting devDB deployment: %v", err)
return
}
deploy.Spec.Replicas = new(int32)
if err := r.Update(ctx, deploy); err != nil {
r.recorder.Eventf(sc, corev1.EventTypeWarning, "CleanUpDevDB", "Error scaling down devDB deployment: %v", err)
}
}
}
Expand All @@ -69,10 +86,20 @@ func (r *AtlasSchemaReconciler) devURL(ctx context.Context, sc *dbv1alpha1.Atlas
}
// make sure we have a dev db running
key := nameDevDB(sc.ObjectMeta)
switch err := r.Get(ctx, key, &appsv1.Deployment{}); {
deploy := &appsv1.Deployment{}
switch err := r.Get(ctx, key, deploy); {
case err == nil:
// The dev database already exists,
// return the connection string.
// If it is scaled down, scale it up.
if deploy.Spec.Replicas == nil || *deploy.Spec.Replicas == 0 {
*deploy.Spec.Replicas = int32(1)
if err := r.Update(ctx, deploy); err != nil {
return "", transient(err)
}
r.recorder.Eventf(sc, corev1.EventTypeNormal, "ScaledUpDevDB", "Scaled up dev database deployment: %s", deploy.Name)
return "", transientAfter(errors.New("waiting for dev database to be ready"), 15*time.Second)
}

case apierrors.IsNotFound(err):
// The dev database does not exist, create it.
deploy, err := deploymentDevDB(key, drv, isSchemaBound(drv, &targetURL))
Expand Down
20 changes: 19 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bytes"
"flag"
"os"
"strconv"
"time"

"golang.org/x/mod/semver"
Expand Down Expand Up @@ -53,6 +54,8 @@ const (
envNoUpdate = "SKIP_VERCHECK"
vercheckURL = "https://vercheck.ariga.io"
execPath = "/atlas"
// prewarmDevDB when disabled it deletes the devDB pods after the schema is created
prewarmDevDB = "PREWARM_DEVDB"
)

func init() {
Expand Down Expand Up @@ -91,7 +94,8 @@ func main() {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
if err = controllers.NewAtlasSchemaReconciler(mgr, execPath).
prewarmDevDB := getPrewarmDevDBEnv()
if err = controllers.NewAtlasSchemaReconciler(mgr, execPath, prewarmDevDB).
SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "AtlasSchema")
os.Exit(1)
Expand Down Expand Up @@ -154,3 +158,17 @@ func checkForUpdate() {
<-time.After(24 * time.Hour)
}
}

// getPrewarmDevDBEnv returns the value of the env var PREWARM_DEVDB.
func getPrewarmDevDBEnv() bool {
env := os.Getenv(prewarmDevDB)
if env == "" {
return false
}
prewarmDevDB, err := strconv.ParseBool(env)
if err != nil {
setupLog.Error(err, "invalid value for env var PREWARM_DEVDB, expected true or false")
os.Exit(1)
}
return prewarmDevDB
}