mirror of
https://forge.liiib.re/indiehost/libre.sh/libre.sh.git
synced 2024-12-28 14:46:41 +00:00
feat: refactor with step logic, rename maintenance
This commit is contained in:
parent
a149dc1543
commit
3f4b6ab72c
5 changed files with 312 additions and 183 deletions
|
@ -32,7 +32,7 @@ type MigrationSpec struct {
|
||||||
//+kubebuilder:validation:Required
|
//+kubebuilder:validation:Required
|
||||||
TargetRef corev1.ObjectReference `json:"targetRef"`
|
TargetRef corev1.ObjectReference `json:"targetRef"`
|
||||||
//+kubebuilder:validation:Optional
|
//+kubebuilder:validation:Optional
|
||||||
Resume bool `json:"resume,omitempty"`
|
Rollback bool `json:"rollback,omitempty"`
|
||||||
//+kubebuilder:validation:Optional
|
//+kubebuilder:validation:Optional
|
||||||
DryRun bool `json:"dryRun,omitempty"`
|
DryRun bool `json:"dryRun,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,10 @@ type MigrationStatus struct {
|
||||||
ImportedBuckets []string `json:"importedBuckets,omitempty"`
|
ImportedBuckets []string `json:"importedBuckets,omitempty"`
|
||||||
//+optional
|
//+optional
|
||||||
ImportedPostgres []string `json:"importedPostgres,omitempty"`
|
ImportedPostgres []string `json:"importedPostgres,omitempty"`
|
||||||
//+optional
|
// +optional
|
||||||
MaintenanceOn bool `json:"maintenanceOn,omitempty"`
|
StartTime *metav1.Time `json:"startTime,omitempty"`
|
||||||
//+optional
|
// +optional
|
||||||
ProxingTraffic bool `json:"proxingTraffic,omitempty"`
|
CompletionTime *metav1.Time `json:"completionTime,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//+kubebuilder:object:root=true
|
//+kubebuilder:object:root=true
|
||||||
|
|
|
@ -345,6 +345,14 @@ func (in *MigrationStatus) DeepCopyInto(out *MigrationStatus) {
|
||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.StartTime != nil {
|
||||||
|
in, out := &in.StartTime, &out.StartTime
|
||||||
|
*out = (*in).DeepCopy()
|
||||||
|
}
|
||||||
|
if in.CompletionTime != nil {
|
||||||
|
in, out := &in.CompletionTime, &out.CompletionTime
|
||||||
|
*out = (*in).DeepCopy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MigrationStatus.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MigrationStatus.
|
||||||
|
|
|
@ -49,7 +49,7 @@ spec:
|
||||||
properties:
|
properties:
|
||||||
dryRun:
|
dryRun:
|
||||||
type: boolean
|
type: boolean
|
||||||
resume:
|
rollback:
|
||||||
type: boolean
|
type: boolean
|
||||||
selectors:
|
selectors:
|
||||||
description: Application selectors used by wader service
|
description: Application selectors used by wader service
|
||||||
|
@ -125,6 +125,9 @@ spec:
|
||||||
status:
|
status:
|
||||||
description: MigrationStatus defines the observed state of Migration
|
description: MigrationStatus defines the observed state of Migration
|
||||||
properties:
|
properties:
|
||||||
|
completionTime:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
conditions:
|
conditions:
|
||||||
items:
|
items:
|
||||||
description: "Condition contains details for one aspect of the current
|
description: "Condition contains details for one aspect of the current
|
||||||
|
@ -204,10 +207,9 @@ spec:
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
type: array
|
type: array
|
||||||
maintenanceOn:
|
startTime:
|
||||||
type: boolean
|
format: date-time
|
||||||
proxingTraffic:
|
type: string
|
||||||
type: boolean
|
|
||||||
version:
|
version:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
|
|
@ -24,7 +24,9 @@ import (
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/runtime/conditions"
|
"github.com/fluxcd/pkg/runtime/conditions"
|
||||||
"github.com/fluxcd/pkg/runtime/patch"
|
"github.com/fluxcd/pkg/runtime/patch"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
@ -53,22 +55,24 @@ type MigrationReconciler struct {
|
||||||
|
|
||||||
func (r *MigrationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
func (r *MigrationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
log := log.FromContext(ctx)
|
log := log.FromContext(ctx)
|
||||||
|
|
||||||
log.Info("Reconciling")
|
log.Info("Reconciling")
|
||||||
|
|
||||||
var mgrt lshportability.Migration
|
var mgrt lshportability.Migration
|
||||||
patcher, result := lshr.Initialize(ctx, r, req, &mgrt)
|
err := r.Client.Get(ctx, req.NamespacedName, &mgrt)
|
||||||
if result != nil {
|
if err != nil {
|
||||||
return result.Unwrap()
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
// TODO manage suspend in conditions ?
|
||||||
|
if mgrt.Spec.Suspend {
|
||||||
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if lshr.IsFinalizing(&mgrt) {
|
patcher := patch.NewSerialPatcher(&mgrt, r.Client)
|
||||||
return lshr.Finalize(ctx, r, patcher, &mgrt, func() error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: when lshr.Initialize sets to reconciling. May not be necessary
|
// TODO add annotation to force reconciliation
|
||||||
if conditions.IsReady(&mgrt) {
|
if !mgrt.Status.CompletionTime.IsZero() {
|
||||||
|
log.Info("skipping reconciliation because already completed")
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,53 +84,12 @@ func (r *MigrationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if mgrt.Spec.Resume || ShouldBeResumed(&mgrt) {
|
|
||||||
if mgrt.Status.ProxingTraffic {
|
|
||||||
_, err = c.ResumeTraffic(ctx, &protoportabilityv1alpha1.ResumeTrafficRequest{
|
|
||||||
Selectors: mgrt.Spec.Selectors,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
mgrt.Status.ProxingTraffic = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if ShouldBeResumed(&mgrt) {
|
|
||||||
mgrt.Spec.Suspend = true
|
|
||||||
mgrt.Status.ProxingTraffic = false
|
|
||||||
|
|
||||||
return ctrl.Result{}, lshr.Patch(ctx, r, patcher, &mgrt, lshr.PatchOpts{})
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctrl.Result{}, lshr.Complete(ctx, r, patcher, &mgrt, lshr.PatchOpts{})
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("getting app", "selectors", mgrt.Spec.Selectors)
|
log.Info("getting app", "selectors", mgrt.Spec.Selectors)
|
||||||
manifestsResp, err := c.GetApp(ctx, &protoportabilityv1alpha1.GetAppRequest{Selectors: mgrt.Spec.Selectors})
|
manifestsResp, err := c.GetApp(ctx, &protoportabilityv1alpha1.GetAppRequest{Selectors: mgrt.Spec.Selectors})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if mgrt.Spec.DryRun {
|
|
||||||
secret := corev1.Secret{}
|
|
||||||
secret.Namespace = mgrt.Namespace
|
|
||||||
secret.Name = fmt.Sprintf("%s-dry-run.migration.portability.libre.sh", mgrt.Name)
|
|
||||||
err = lshr.CreateOrPatch(ctx, r, &secret, func() error {
|
|
||||||
if secret.Data == nil {
|
|
||||||
secret.Data = make(map[string][]byte)
|
|
||||||
}
|
|
||||||
secret.Data["app.json"] = manifestsResp.Application
|
|
||||||
for i, m := range manifestsResp.ExtraManifests {
|
|
||||||
secret.Data[fmt.Sprintf("manifest-%d.json", i)] = m
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
return ctrl.Result{}, lshr.Complete(ctx, r, patcher, &mgrt, lshr.PatchOpts{})
|
|
||||||
}
|
|
||||||
|
|
||||||
app := &unstructured.Unstructured{}
|
app := &unstructured.Unstructured{}
|
||||||
err = app.UnmarshalJSON(manifestsResp.Application)
|
err = app.UnmarshalJSON(manifestsResp.Application)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -135,126 +98,67 @@ func (r *MigrationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
|
||||||
app.SetNamespace(mgrt.Namespace)
|
app.SetNamespace(mgrt.Namespace)
|
||||||
app.SetName(mgrt.Name)
|
app.SetName(mgrt.Name)
|
||||||
|
|
||||||
for _, m := range manifestsResp.ExtraManifests {
|
steps := []step{
|
||||||
obj := &unstructured.Unstructured{}
|
{name: "CreateApp", reconcile: r.CreateAppStepFunc(ctx, log, c, patcher, &mgrt, app, manifestsResp.ExtraManifests)},
|
||||||
err = obj.UnmarshalJSON(m)
|
{name: "Maintenance", reconcile: r.MaintenanceStepFunc(ctx, log, patcher, &mgrt, app)},
|
||||||
if err != nil {
|
{name: "ProxyTraffic", reconcile: r.ProxyTrafficStepFunc(ctx, log, c, patcher, &mgrt)},
|
||||||
return ctrl.Result{}, err
|
{name: "ImportPostgres", reconcile: r.ImportPostgresStepFunc(ctx, log, c, patcher, &mgrt, app)},
|
||||||
}
|
{name: "StartApp", reconcile: r.StartAppStepFunc(ctx, log, c, &mgrt, app)},
|
||||||
obj.SetNamespace(mgrt.Namespace)
|
{name: "RemoveMaintenance", reconcile: r.RemoveMaintenanceStepFunc(ctx, log, c, patcher, &mgrt, app)},
|
||||||
|
}
|
||||||
|
|
||||||
err = r.Create(ctx, obj)
|
if mgrt.Spec.DryRun {
|
||||||
if client.IgnoreAlreadyExists(err) != nil {
|
steps = []step{
|
||||||
|
{name: "DryRunStep", reconcile: r.DryRunStepFunc(ctx, log, c, patcher, &mgrt)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ShouldRollback(&mgrt) {
|
||||||
|
// TODO de we do some cleanup ?
|
||||||
|
steps = []step{
|
||||||
|
{name: "Rollback", reconcile: r.RollbackStepFunc(ctx, log, c, patcher, &mgrt)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mgrt.Status.StartTime.IsZero() {
|
||||||
|
now := metav1.Now()
|
||||||
|
mgrt.Status.StartTime = &now
|
||||||
|
for _, step := range steps {
|
||||||
|
conditions.MarkFalse(&mgrt, step.conditionType(), meta.ProgressingReason, "")
|
||||||
|
}
|
||||||
|
if err := r.patch(ctx, patcher, &mgrt); err != nil {
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("getting certs", "selectors", mgrt.Spec.Selectors)
|
for _, step := range steps {
|
||||||
certResp, err := c.GetCert(ctx, &protoportabilityv1alpha1.GetCertRequest{Selectors: mgrt.Spec.Selectors})
|
if conditions.IsTrue(&mgrt, step.conditionType()) {
|
||||||
if err != nil {
|
continue
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
secret := &corev1.Secret{}
|
|
||||||
lshr.SetResourceNamespacedName(app, secret, "tls")
|
|
||||||
err = lshr.CreateOrPatch(ctx, r, secret, func() error {
|
|
||||||
lshr.ApplyLabels(app, secret, nil)
|
|
||||||
secret.Type = corev1.SecretTypeTLS
|
|
||||||
|
|
||||||
secret.Data = make(map[string][]byte, 3)
|
|
||||||
secret.Data["tls.key"] = certResp.Key
|
|
||||||
secret.Data["tls.crt"] = certResp.Crt
|
|
||||||
secret.Data["ca.crt"] = certResp.Ca
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.Get(ctx, client.ObjectKeyFromObject(app), app)
|
|
||||||
if err != nil {
|
|
||||||
if !apierrors.IsNotFound(err) {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
}
|
||||||
log.Info("creating", "app", app.GetName())
|
res := step.reconcile()
|
||||||
err = importer.CreateApplication(ctx, r, app)
|
if res != nil {
|
||||||
if err != nil {
|
return res.Unwrap()
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
}
|
||||||
return ctrl.Result{Requeue: true}, nil
|
conditions.MarkTrue(&mgrt, step.conditionType(), meta.SucceededReason, "")
|
||||||
}
|
if err := r.patch(ctx, patcher, &mgrt); err != nil {
|
||||||
|
|
||||||
if importer.ShouldWaitForAppDeps(app) {
|
|
||||||
log.Info("waiting for app dependencies")
|
|
||||||
conditions.MarkStalled(&mgrt, lshmeta.DependenciesNotReady, "%s %s deps are not ready", app.GetKind(), app.GetName())
|
|
||||||
return ctrl.Result{RequeueAfter: 5 * time.Second}, lshr.Patch(ctx, r, patcher, &mgrt, lshr.PatchOpts{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mgrt.Status.MaintenanceOn {
|
|
||||||
log.Info("setting maintenance")
|
|
||||||
err = r.reconcileMaintenance(ctx, mgrt, app)
|
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
mgrt.Status.MaintenanceOn = true
|
|
||||||
|
|
||||||
return ctrl.Result{Requeue: true}, lshr.Patch(ctx, r, patcher, &mgrt, lshr.PatchOpts{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO improve
|
|
||||||
clusterSecret := &corev1.Secret{}
|
|
||||||
err = r.Get(ctx, types.NamespacedName{Namespace: "libresh-system", Name: "cluster-settings"}, clusterSecret)
|
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var clusterDomain string
|
|
||||||
if clusterSecret.Data["CLUSTER_DOMAIN"] == nil {
|
|
||||||
return ctrl.Result{}, fmt.Errorf("cluster domain is not defined in cluster-settings")
|
|
||||||
}
|
|
||||||
|
|
||||||
clusterDomain = string(clusterSecret.Data["CLUSTER_DOMAIN"])
|
|
||||||
log.Info("proxy traffic", "selectors", mgrt.Spec.Selectors, "destination", clusterDomain)
|
|
||||||
// TODO check if it is indempotent
|
|
||||||
if !mgrt.Status.ProxingTraffic {
|
|
||||||
_, err = c.ProxyTraffic(ctx, &protoportabilityv1alpha1.ProxyTrafficRequest{Selectors: mgrt.Spec.Selectors, Destination: clusterDomain})
|
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
mgrt.Status.ProxingTraffic = true
|
|
||||||
err = lshr.Patch(ctx, r, patcher, &mgrt, lshr.PatchOpts{})
|
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// From here if it fails, revert traffic
|
if mgrt.Status.CompletionTime.IsZero() {
|
||||||
|
now := metav1.Now()
|
||||||
err = r.importPostgress(ctx, c, patcher, &mgrt, app)
|
mgrt.Status.CompletionTime = &now
|
||||||
if err != nil {
|
if err := r.patch(ctx, patcher, &mgrt); err != nil {
|
||||||
conditions.MarkStalled(&mgrt, "PostgresImportFailed", err.Error())
|
return ctrl.Result{}, err
|
||||||
return ctrl.Result{}, lshr.Patch(ctx, r, patcher, &mgrt, lshr.PatchOpts{})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = DeleteApplicationCondition(ctx, r, app)
|
// TODO add done in X time
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
maintenance := &lshlifecycle.Maintenance{}
|
|
||||||
lshr.SetResourceNamespacedName(app, maintenance)
|
|
||||||
|
|
||||||
err = r.Delete(ctx, maintenance)
|
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
conditions.Delete(&mgrt, meta.ReconcilingCondition)
|
|
||||||
|
|
||||||
log.Info("done")
|
log.Info("done")
|
||||||
|
|
||||||
return ctrl.Result{}, lshr.Complete(ctx, r, patcher, &mgrt, lshr.PatchOpts{})
|
return ctrl.Result{}, nil
|
||||||
|
|
||||||
|
// TODO: Tolerated downtime
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupWithManager sets up the controller with the Manager.
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
@ -275,23 +179,240 @@ func (r *MigrationReconciler) OwnedConditions() []string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ShouldBeResumed(mgrt *lshportability.Migration) bool {
|
func (r *MigrationReconciler) patch(ctx context.Context, patcher *patch.SerialPatcher, mgrt *lshportability.Migration) error {
|
||||||
|
conditions.MarkTrue(mgrt, meta.ReadyCondition, meta.SucceededReason, "Migrate")
|
||||||
|
conditions.SetSummary(mgrt, meta.ReadyCondition,
|
||||||
|
conditions.WithStepCounter(),
|
||||||
|
)
|
||||||
|
return lshr.Patch(ctx, r, patcher, mgrt, lshr.PatchOpts{DisableReadyCondition: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ShouldRollback(mgrt *lshportability.Migration) bool {
|
||||||
switch conditions.GetReason(mgrt, meta.StalledCondition) {
|
switch conditions.GetReason(mgrt, meta.StalledCondition) {
|
||||||
case "PostgresImportFailed":
|
case "PostgresImportFailed":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return mgrt.Spec.Rollback
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteApplicationCondition(ctx context.Context, r lshr.Reconciler, app *unstructured.Unstructured) error {
|
type step struct {
|
||||||
patcher, err := patch.NewHelper(app, r)
|
name string
|
||||||
if err != nil {
|
reconcile func() *lshr.Result
|
||||||
return err
|
}
|
||||||
|
|
||||||
|
func (s *step) conditionType() string {
|
||||||
|
return s.name + "Step"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MigrationReconciler) DryRunStepFunc(ctx context.Context, log logr.Logger, c protoportabilityv1alpha1.WaderServiceClient, patcher *patch.SerialPatcher, mgrt *lshportability.Migration) func() *lshr.Result {
|
||||||
|
return func() *lshr.Result {
|
||||||
|
log.Info("getting app", "selectors", mgrt.Spec.Selectors)
|
||||||
|
manifestsResp, err := c.GetApp(ctx, &protoportabilityv1alpha1.GetAppRequest{Selectors: mgrt.Spec.Selectors})
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := corev1.Secret{}
|
||||||
|
secret.Namespace = mgrt.Namespace
|
||||||
|
secret.Name = fmt.Sprintf("%s-dry-run.migration.portability.libre.sh", mgrt.Name)
|
||||||
|
err = lshr.CreateOrPatch(ctx, r, &secret, func() error {
|
||||||
|
if secret.Data == nil {
|
||||||
|
secret.Data = make(map[string][]byte)
|
||||||
|
}
|
||||||
|
secret.Data["app.json"] = manifestsResp.Application
|
||||||
|
for i, m := range manifestsResp.ExtraManifests {
|
||||||
|
secret.Data[fmt.Sprintf("manifest-%d.json", i)] = m
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MigrationReconciler) CreateAppStepFunc(ctx context.Context, log logr.Logger, c protoportabilityv1alpha1.WaderServiceClient, patcher *patch.SerialPatcher, mgrt *lshportability.Migration, app *unstructured.Unstructured, extraManifests [][]byte) func() *lshr.Result {
|
||||||
|
return func() *lshr.Result {
|
||||||
|
for _, m := range extraManifests {
|
||||||
|
obj := &unstructured.Unstructured{}
|
||||||
|
err := obj.UnmarshalJSON(m)
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
obj.SetNamespace(mgrt.Namespace)
|
||||||
|
|
||||||
|
err = r.Create(ctx, obj)
|
||||||
|
if client.IgnoreAlreadyExists(err) != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("getting certs", "selectors", mgrt.Spec.Selectors)
|
||||||
|
certResp, err := c.GetCert(ctx, &protoportabilityv1alpha1.GetCertRequest{Selectors: mgrt.Spec.Selectors})
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := &corev1.Secret{}
|
||||||
|
lshr.SetResourceNamespacedName(app, secret, "tls")
|
||||||
|
err = lshr.CreateOrPatch(ctx, r, secret, func() error {
|
||||||
|
lshr.ApplyLabels(app, secret, nil)
|
||||||
|
secret.Type = corev1.SecretTypeTLS
|
||||||
|
|
||||||
|
secret.Data = make(map[string][]byte, 3)
|
||||||
|
secret.Data["tls.key"] = certResp.Key
|
||||||
|
secret.Data["tls.crt"] = certResp.Crt
|
||||||
|
secret.Data["ca.crt"] = certResp.Ca
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.Get(ctx, client.ObjectKeyFromObject(app), app)
|
||||||
|
if err != nil {
|
||||||
|
if !apierrors.IsNotFound(err) {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
log.Info("creating", "app", app.GetName())
|
||||||
|
err = importer.CreateApplication(ctx, r, app)
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lshr.WrapResult(ctrl.Result{Requeue: true}, lshr.Patch(ctx, r, patcher, mgrt, lshr.PatchOpts{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if importer.ShouldWaitForAppDeps(app) {
|
||||||
|
log.Info("waiting for app dependencies")
|
||||||
|
// TODO to create app condition ?
|
||||||
|
conditions.MarkStalled(mgrt, lshmeta.DependenciesNotReady, "%s %s deps are not ready", app.GetKind(), app.GetName())
|
||||||
|
return lshr.WrapResult(ctrl.Result{RequeueAfter: 5 * time.Second}, lshr.Patch(ctx, r, patcher, mgrt, lshr.PatchOpts{}))
|
||||||
|
}
|
||||||
|
conditions.Delete(mgrt, lshmeta.DependenciesNotReady)
|
||||||
|
// TODO remove stalled condition
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MigrationReconciler) MaintenanceStepFunc(ctx context.Context, log logr.Logger, patcher *patch.SerialPatcher, mgrt *lshportability.Migration, app client.Object) func() *lshr.Result {
|
||||||
|
return func() *lshr.Result {
|
||||||
|
log.Info("setting maintenance")
|
||||||
|
err := r.reconcileMaintenance(ctx, mgrt, app)
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MigrationReconciler) ProxyTrafficStepFunc(ctx context.Context, log logr.Logger, c protoportabilityv1alpha1.WaderServiceClient, patcher *patch.SerialPatcher, mgrt *lshportability.Migration) func() *lshr.Result {
|
||||||
|
return func() *lshr.Result {
|
||||||
|
// TODO improve
|
||||||
|
clusterSecret := &corev1.Secret{}
|
||||||
|
err := r.Get(ctx, types.NamespacedName{Namespace: "libresh-system", Name: "cluster-settings"}, clusterSecret)
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var clusterDomain string
|
||||||
|
if clusterSecret.Data["CLUSTER_DOMAIN"] == nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, fmt.Errorf("cluster domain is not defined in cluster-settings"))
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterDomain = string(clusterSecret.Data["CLUSTER_DOMAIN"])
|
||||||
|
log.Info("proxy traffic", "selectors", mgrt.Spec.Selectors, "destination", clusterDomain)
|
||||||
|
_, err = c.ProxyTraffic(ctx, &protoportabilityv1alpha1.ProxyTrafficRequest{Selectors: mgrt.Spec.Selectors, Destination: clusterDomain})
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MigrationReconciler) ImportPostgresStepFunc(ctx context.Context, log logr.Logger, c protoportabilityv1alpha1.WaderServiceClient, patcher *patch.SerialPatcher, mgrt *lshportability.Migration, app *unstructured.Unstructured) func() *lshr.Result {
|
||||||
|
return func() *lshr.Result {
|
||||||
|
err := r.importPostgress(ctx, c, patcher, mgrt, app)
|
||||||
|
if err != nil {
|
||||||
|
// TODO step PostgresImport
|
||||||
|
conditions.MarkStalled(mgrt, "PostgresImportFailed", err.Error())
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, lshr.Patch(ctx, r, patcher, mgrt, lshr.PatchOpts{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MigrationReconciler) StartAppStepFunc(ctx context.Context, log logr.Logger, c protoportabilityv1alpha1.WaderServiceClient, mgrt *lshportability.Migration, app *unstructured.Unstructured) func() *lshr.Result {
|
||||||
|
return func() *lshr.Result {
|
||||||
|
log.Info("starting app")
|
||||||
|
// TODO patch to only delete the condition without get
|
||||||
|
err := r.Get(ctx, client.ObjectKeyFromObject(app), app)
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
patcher, err := patch.NewHelper(app, r)
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conditions.Delete(conditions.UnstructuredSetter(app), lshportability.ImportInProgresStatus)
|
||||||
|
err = patcher.Patch(ctx, app,
|
||||||
|
patch.WithFieldOwner(r.Name()),
|
||||||
|
patch.WithOwnedConditions{Conditions: []string{lshportability.ImportInProgresStatus}})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MigrationReconciler) RemoveMaintenanceStepFunc(ctx context.Context, log logr.Logger, c protoportabilityv1alpha1.WaderServiceClient, patcher *patch.SerialPatcher, mgrt *lshportability.Migration, app *unstructured.Unstructured) func() *lshr.Result {
|
||||||
|
return func() *lshr.Result {
|
||||||
|
err := r.Get(ctx, client.ObjectKeyFromObject(app), app)
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
log.Info("waiting for app to be ready")
|
||||||
|
if conditions.IsReady(conditions.UnstructuredGetter(app)) {
|
||||||
|
maintenance := &lshlifecycle.Maintenance{}
|
||||||
|
// TODO avoid collusion in maintenance, only one maintenance per ingressRefs ?
|
||||||
|
maintenance.Namespace = mgrt.Namespace
|
||||||
|
maintenance.Name = fmt.Sprintf("%s-migration", mgrt.Name)
|
||||||
|
log.Info("removing maintenance")
|
||||||
|
err := r.Delete(ctx, maintenance)
|
||||||
|
if !apierrors.IsNotFound(err) {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return lshr.WrapResult(ctrl.Result{Requeue: true}, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MigrationReconciler) RollbackStepFunc(ctx context.Context, log logr.Logger, c protoportabilityv1alpha1.WaderServiceClient, patcher *patch.SerialPatcher, mgrt *lshportability.Migration) func() *lshr.Result {
|
||||||
|
return func() *lshr.Result {
|
||||||
|
if conditions.IsTrue(mgrt, "ProxyTraffic") {
|
||||||
|
_, err := c.ResumeTraffic(ctx, &protoportabilityv1alpha1.ResumeTrafficRequest{
|
||||||
|
Selectors: mgrt.Spec.Selectors,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return lshr.WrapResult(ctrl.Result{}, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
conditions.Delete(conditions.UnstructuredSetter(app), lshportability.ImportInProgresStatus)
|
|
||||||
return patcher.Patch(ctx, app,
|
|
||||||
patch.WithFieldOwner(r.Name()),
|
|
||||||
patch.WithOwnedConditions{Conditions: []string{lshportability.ImportInProgresStatus}})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,7 @@ import (
|
||||||
lshportability "libre.sh/api/portability/v1alpha1"
|
lshportability "libre.sh/api/portability/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *MigrationReconciler) reconcileMaintenance(ctx context.Context, mgrt lshportability.Migration, app client.Object) error {
|
func (r *MigrationReconciler) reconcileMaintenance(ctx context.Context, mgrt *lshportability.Migration, app client.Object) error {
|
||||||
if mgrt.Status.MaintenanceOn {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ingressList := netv1.IngressList{}
|
ingressList := netv1.IngressList{}
|
||||||
err := r.List(ctx, &ingressList, client.InNamespace(mgrt.Namespace), client.MatchingLabels(lshr.GetLabelSelector(app, nil)))
|
err := r.List(ctx, &ingressList, client.InNamespace(mgrt.Namespace), client.MatchingLabels(lshr.GetLabelSelector(app, nil)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -53,8 +49,10 @@ func (r *MigrationReconciler) reconcileMaintenance(ctx context.Context, mgrt lsh
|
||||||
|
|
||||||
maintenance := &lshlifecycle.Maintenance{}
|
maintenance := &lshlifecycle.Maintenance{}
|
||||||
lshr.SetResourceNamespacedName(app, maintenance)
|
lshr.SetResourceNamespacedName(app, maintenance)
|
||||||
|
maintenance.Namespace = mgrt.Namespace
|
||||||
|
maintenance.Name = fmt.Sprintf("%s-migration", mgrt.Name)
|
||||||
return lshr.CreateOrPatch(ctx, r, maintenance, func() error {
|
return lshr.CreateOrPatch(ctx, r, maintenance, func() error {
|
||||||
maintenance.Spec.IngressRefs = ingressRefs
|
maintenance.Spec.IngressRefs = ingressRefs
|
||||||
return controllerutil.SetControllerReference(app, maintenance, r.Scheme())
|
return controllerutil.SetControllerReference(mgrt, maintenance, r.Scheme())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue