feat: support to datastore migration w/ the same driver

This commit is contained in:
Dario Tranchitella
2022-12-02 18:45:20 +01:00
parent a260a92495
commit 9e899379f4
9 changed files with 396 additions and 38 deletions

View File

@@ -566,7 +566,7 @@ func (d *Deployment) buildKubeAPIServerCommand(tenantControlPlane *kamajiv1alpha
}
desiredArgs["--etcd-compaction-interval"] = "0"
desiredArgs["--etcd-prefix"] = fmt.Sprintf("/%s", tenantControlPlane.GetName())
desiredArgs["--etcd-prefix"] = fmt.Sprintf("/%s_%s", tenantControlPlane.GetNamespace(), tenantControlPlane.GetName())
desiredArgs["--etcd-servers"] = strings.Join(httpsEndpoints, ",")
desiredArgs["--etcd-cafile"] = "/etc/kubernetes/pki/etcd/ca.crt"
desiredArgs["--etcd-certfile"] = "/etc/kubernetes/pki/etcd/server.crt"

View File

@@ -0,0 +1,159 @@
// Copyright 2022 Clastix Labs
// SPDX-License-Identifier: Apache-2.0
package resources
import (
"context"
"fmt"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
"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"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
"github.com/clastix/kamaji/internal/utilities"
)
type DatastoreMigrate struct {
Client client.Client
KamajiNamespace string
KamajiServiceAccount string
ShouldCleanUp bool
actualDatastore *kamajiv1alpha1.DataStore
desiredDatastore *kamajiv1alpha1.DataStore
job *batchv1.Job
inProgress bool
}
func (d *DatastoreMigrate) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
if len(tenantControlPlane.Status.Storage.DataStoreName) == 0 {
return nil
}
d.job = &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("migrate-%s-%s", tenantControlPlane.GetNamespace(), tenantControlPlane.GetName()),
Namespace: d.KamajiNamespace,
},
}
if d.ShouldCleanUp {
return nil
}
if err := d.Client.Get(ctx, types.NamespacedName{Name: d.job.GetName(), Namespace: d.job.GetNamespace()}, d.job); err != nil {
if !errors.IsNotFound(err) {
return err
}
}
d.actualDatastore = &kamajiv1alpha1.DataStore{}
if err := d.Client.Get(ctx, types.NamespacedName{Name: tenantControlPlane.Status.Storage.DataStoreName}, d.actualDatastore); err != nil {
return err
}
d.desiredDatastore = &kamajiv1alpha1.DataStore{}
if err := d.Client.Get(ctx, types.NamespacedName{Name: tenantControlPlane.Spec.DataStore}, d.desiredDatastore); err != nil {
return err
}
return nil
}
func (d *DatastoreMigrate) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
return d.ShouldCleanUp
}
func (d *DatastoreMigrate) CleanUp(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) (bool, error) {
if err := d.Client.Get(ctx, types.NamespacedName{Name: d.job.GetName(), Namespace: d.job.GetNamespace()}, d.job); err != nil && errors.IsNotFound(err) {
return false, nil
}
err := d.Client.Delete(ctx, d.job)
return err == nil, err
}
func (d *DatastoreMigrate) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
if d.desiredDatastore == nil {
return controllerutil.OperationResultNone, nil
}
if d.actualDatastore.GetName() == d.desiredDatastore.GetName() {
return controllerutil.OperationResultNone, nil
}
res, err := utilities.CreateOrUpdateWithConflict(ctx, d.Client, d.job, func() error {
d.job.SetLabels(map[string]string{
"tcp.kamaji.clastix.io/name": tenantControlPlane.GetName(),
"tcp.kamaji.clastix.io/namespace": tenantControlPlane.GetNamespace(),
"kamaji.clastix.io/component": "migrate",
})
d.job.Spec.Template.ObjectMeta.Labels = utilities.MergeMaps(d.job.Spec.Template.ObjectMeta.Labels, d.job.Spec.Template.ObjectMeta.Labels)
d.job.Spec.Template.Spec.ServiceAccountName = d.KamajiServiceAccount
d.job.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyOnFailure
if len(d.job.Spec.Template.Spec.Containers) == 0 {
d.job.Spec.Template.Spec.Containers = append(d.job.Spec.Template.Spec.Containers, corev1.Container{})
}
d.job.Spec.Template.Spec.Containers[0].Name = "migrate"
d.job.Spec.Template.Spec.Containers[0].Image = "clastix/kamaji:v0.1.1"
d.job.Spec.Template.Spec.Containers[0].Command = []string{"/kamaji"}
d.job.Spec.Template.Spec.Containers[0].Args = []string{
"migrate",
fmt.Sprintf("--tenant-control-plane=%s/%s", tenantControlPlane.GetNamespace(), tenantControlPlane.GetName()),
fmt.Sprintf("--target-datastore=%s", tenantControlPlane.Spec.DataStore),
}
return nil
})
if err != nil {
return res, err
}
switch res {
case controllerutil.OperationResultNone:
if len(d.job.Status.Conditions) == 0 {
break
}
condition := d.job.Status.Conditions[0]
if condition.Type == batchv1.JobComplete && condition.Status == corev1.ConditionTrue {
return controllerutil.OperationResultNone, nil
}
log.FromContext(ctx).Info("migration job not yet completed", "reason", condition.Reason, "message", condition.Message)
case controllerutil.OperationResultCreated:
break
default:
return "", fmt.Errorf("unexpected status %s from the migration job", res)
}
d.inProgress = true
return controllerutil.OperationResultNone, nil
}
func (d *DatastoreMigrate) GetName() string {
return "migrate"
}
func (d *DatastoreMigrate) ShouldStatusBeUpdated(context.Context, *kamajiv1alpha1.TenantControlPlane) bool {
return d.inProgress
}
func (d *DatastoreMigrate) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
if d.inProgress {
tenantControlPlane.Status.Kubernetes.Version.Status = &kamajiv1alpha1.VersionMigrating
}
return nil
}

View File

@@ -143,6 +143,7 @@ func (r *KubernetesDeploymentResource) deploymentTemplateLabels(ctx context.Cont
"component.kamaji.clastix.io/front-proxy-client-certificate": hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.Certificates.FrontProxyClient.SecretName),
"component.kamaji.clastix.io/service-account": hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.Certificates.SA.SecretName),
"component.kamaji.clastix.io/scheduler-kubeconfig": hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.KubeConfig.Scheduler.SecretName),
"component.kamaji.clastix.io/datastore": tenantControlPlane.Spec.DataStore,
}
return labels