Skip to content

Commit

Permalink
feat(v2): enable v2 restore (#1776)
Browse files Browse the repository at this point in the history
  • Loading branch information
emosbaugh authored Jan 31, 2025
1 parent b511d51 commit 7266fe1
Show file tree
Hide file tree
Showing 17 changed files with 116 additions and 181 deletions.
32 changes: 3 additions & 29 deletions cmd/installer/cli/install2.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import (
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
)
Expand Down Expand Up @@ -343,9 +342,7 @@ func runInstall2(ctx context.Context, name string, flags Install2CmdFlags, metri
return fmt.Errorf("unable to install extensions: %w", err)
}

// mark that the installation as installed as everything has been applied
in.Status.State = ecv1beta1.InstallationStateInstalled
if err := kubeutils.UpdateInstallationStatus(ctx, kcli, in); err != nil {
if err := kubeutils.SetInstallationState(ctx, kcli, in, ecv1beta1.InstallationStateInstalled, "Installed"); err != nil {
return fmt.Errorf("unable to update installation: %w", err)
}

Expand Down Expand Up @@ -653,7 +650,6 @@ func recordInstallation(ctx context.Context, kcli client.Client, flags Install2C
Name: time.Now().Format("20060102150405"),
},
Spec: ecv1beta1.InstallationSpec{
SourceType: ecv1beta1.InstallationSourceTypeCRD,
ClusterID: metrics.ClusterID().String(),
MetricsBaseURL: metrics.BaseURL(flags.license),
AirGap: flags.isAirgap,
Expand All @@ -668,12 +664,12 @@ func recordInstallation(ctx context.Context, kcli client.Client, flags Install2C
},
},
}
if err := kcli.Create(ctx, installation); err != nil {
if err := kubeutils.CreateInstallation(ctx, kcli, installation); err != nil {
return nil, fmt.Errorf("create installation: %w", err)
}

// the kubernetes api does not allow us to set the state of an object when creating it
err = setInstallationState(ctx, installation, ecv1beta1.InstallationStateKubernetesInstalled)
err = kubeutils.SetInstallationState(ctx, kcli, installation, ecv1beta1.InstallationStateKubernetesInstalled, "Kubernetes installed")
if err != nil {
return nil, fmt.Errorf("set installation state to KubernetesInstalled: %w", err)
}
Expand All @@ -682,28 +678,6 @@ func recordInstallation(ctx context.Context, kcli client.Client, flags Install2C
return installation, nil
}

func setInstallationState(ctx context.Context, installation *ecv1beta1.Installation, state string) error {
kcli, err := kubeutils.KubeClient()
if err != nil {
return fmt.Errorf("create kube client: %w", err)
}

// retry on all errors
return retry.OnError(retry.DefaultRetry, func(_ error) bool { return true }, func() error {
err := kcli.Get(ctx, client.ObjectKey{Name: installation.Name}, installation)
if err != nil {
return fmt.Errorf("get installation: %w", err)
}

installation.Status.State = state

if err := kcli.Status().Update(ctx, installation); err != nil {
return fmt.Errorf("update installation status: %w", err)
}
return nil
})
}

func createECNamespace(ctx context.Context, kcli client.Client) error {
ns := corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Expand Down
2 changes: 1 addition & 1 deletion cmd/installer/cli/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func RestoreCmd(ctx context.Context, name string) *cobra.Command {
var s3Store s3BackupStore

cmd := &cobra.Command{
Use: "restore",
Use: "restore-legacy",
Short: fmt.Sprintf("Restore a %s cluster", name),
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := preRunInstall2(cmd, &flags); err != nil {
Expand Down
31 changes: 23 additions & 8 deletions cmd/installer/cli/restore2.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func Restore2Cmd(ctx context.Context, name string) *cobra.Command {
var skipStoreValidation bool

cmd := &cobra.Command{
Use: "restore2",
Use: "restore",
Short: fmt.Sprintf("Restore a %s cluster", name),
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := preRunInstall2(cmd, &flags); err != nil {
Expand Down Expand Up @@ -544,6 +544,22 @@ func runRestoreExtensions(ctx context.Context, flags Install2CmdFlags) error {
}

func runRestoreApp(ctx context.Context, backupToRestore *disasterrecovery.ReplicatedBackup) error {
logrus.Debugf("setting installation status to installed")
kcli, err := kubeutils.KubeClient()
if err != nil {
return fmt.Errorf("create kube client: %w", err)
}

in, err := kubeutils.GetLatestInstallation(ctx, kcli)
if err != nil {
return fmt.Errorf("get latest installation: %w", err)
}

err = kubeutils.SetInstallationState(ctx, kcli, in, ecv1beta1.InstallationStateInstalled, "Installed")
if err != nil {
return fmt.Errorf("update installation status: %w", err)
}

logrus.Debugf("restoring app from backup %q", backupToRestore.GetName())
if err := restoreFromReplicatedBackup(ctx, *backupToRestore, disasterRecoveryComponentApp, true); err != nil {
return err
Expand Down Expand Up @@ -1490,16 +1506,15 @@ func restoreReconcileInstallationFromRuntimeConfig(ctx context.Context) error {
in.Spec.RuntimeConfig = &ecv1beta1.RuntimeConfigSpec{}
}

// We allow the user to override the port with a flag to the restore command.
in.Spec.RuntimeConfig.LocalArtifactMirror.Port = runtimeconfig.LocalArtifactMirrorPort()

if err := kubeutils.UpdateInstallation(ctx, kcli, in); err != nil {
err = kubeutils.UpdateInstallation(ctx, kcli, in, func(in *ecv1beta1.Installation) {
in.Spec.RuntimeConfig.LocalArtifactMirror.Port = runtimeconfig.LocalArtifactMirrorPort()
})
if err != nil {
return fmt.Errorf("update installation: %w", err)
}

in.Status.State = ecv1beta1.InstallationStateKubernetesInstalled

if err := kubeutils.UpdateInstallationStatus(ctx, kcli, in); err != nil {
err = kubeutils.SetInstallationState(ctx, kcli, in, ecv1beta1.InstallationStateKubernetesInstalled, "Kubernetes installed")
if err != nil {
return fmt.Errorf("update installation status: %w", err)
}

Expand Down
8 changes: 8 additions & 0 deletions e2e/scripts/resume-restore.exp
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,14 @@ expect {
}
}

expect {
-timeout 240 "Extensions installed!" {}
timeout {
puts "\n\nFailed to find 'extensions installed' spinner."
exit 1
}
}

expect {
"Restoring application" {}
timeout {
Expand Down
4 changes: 2 additions & 2 deletions operator/controllers/installation_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ func (r *InstallationReconciler) Reconcile(ctx context.Context, req ctrl.Request
// are going to operate only on the newest one (sorting by installation
// name).
log := ctrl.LoggerFrom(ctx)
installs, err := kubeutils.ListCRDInstallations(ctx, r.Client)
installs, err := kubeutils.ListInstallations(ctx, r.Client)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to list installations: %w", err)
}
Expand Down Expand Up @@ -609,7 +609,7 @@ func (r *InstallationReconciler) Reconcile(ctx context.Context, req ctrl.Request
}

// save the installation status. nothing more to do with it.
if err := r.Status().Update(ctx, in.DeepCopy()); err != nil {
if err := r.Status().Update(ctx, in); err != nil {
if k8serrors.IsConflict(err) {
return ctrl.Result{}, fmt.Errorf("failed to update status: conflict")
}
Expand Down
2 changes: 1 addition & 1 deletion operator/pkg/cli/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func UpgradeCmd() *cobra.Command {
if err != nil {
return fmt.Errorf("apply installation: %w", err)
}
previousInstallation, err := kubeutils.GetPreviousCRDInstallation(cmd.Context(), cli, installation)
previousInstallation, err := kubeutils.GetPreviousInstallation(cmd.Context(), cli, installation)
if err != nil {
return fmt.Errorf("get previous installation: %w", err)
}
Expand Down
23 changes: 6 additions & 17 deletions operator/pkg/k8sutil/installation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,19 @@ import (
"fmt"

ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
"github.com/replicatedhq/embedded-cluster/pkg/kubeutils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func SetInstallationState(ctx context.Context, cli client.Client, name string, state string, reason string, pendingCharts ...string) error {
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
updatedIn := ecv1beta1.Installation{}
if err := cli.Get(ctx, types.NamespacedName{Name: name}, &updatedIn); err != nil {
return fmt.Errorf("get crd installation before updating status: %w", err)
}

updatedIn.Status.SetState(state, reason, pendingCharts)

if err := cli.Status().Update(ctx, &updatedIn); err != nil {
return fmt.Errorf("update crd installation status: %w", err)
}
return nil
})
if err != nil {
return fmt.Errorf("persistent conflict error, failed to update installation %s status: %w", name, err)
in := &ecv1beta1.Installation{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
}
return nil
return kubeutils.SetInstallationState(ctx, cli, in, state, reason, pendingCharts...)
}

func CheckConditionStatus(inStat ecv1beta1.InstallationStatus, conditionName string) metav1.ConditionStatus {
Expand Down
12 changes: 7 additions & 5 deletions operator/pkg/upgrade/installation.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ func CreateInstallation(ctx context.Context, cli client.Client, original *ecv1be

// check if the installation already exists - this function can be called multiple times
// if the installation is already created, we can just return
if in, err := kubeutils.GetCRDInstallation(ctx, cli, in.Name); err == nil {
if in, err := kubeutils.GetInstallation(ctx, cli, in.Name); err == nil {
log.Info(fmt.Sprintf("Installation %s already exists", in.Name))
return nil
}
log.Info(fmt.Sprintf("Creating installation %s", in.Name))

err := cli.Create(ctx, in)
err := kubeutils.CreateInstallation(ctx, cli, in)
if err != nil {
return fmt.Errorf("create installation: %w", err)
}
Expand All @@ -41,13 +41,15 @@ func CreateInstallation(ctx context.Context, cli client.Client, original *ecv1be
// reApplyInstallation updates the installation spec to match what's in the configmap used by the upgrade job.
// This is required because the installation CRD may have been updated as part of this upgrade, and additional fields may be present now.
func reApplyInstallation(ctx context.Context, cli client.Client, in *ecv1beta1.Installation) error {
existingIn, err := kubeutils.GetCRDInstallation(ctx, cli, in.Name)
existingIn, err := kubeutils.GetInstallation(ctx, cli, in.Name)
if err != nil {
return fmt.Errorf("get installation: %w", err)
}

existingIn.Spec = *in.Spec.DeepCopy() // copy the spec in, in case there were fields added to the spec
if err := kubeutils.UpdateInstallation(ctx, cli, existingIn); err != nil {
err = kubeutils.UpdateInstallation(ctx, cli, existingIn, func(ex *ecv1beta1.Installation) {
ex.Spec = *in.Spec.DeepCopy() // copy the spec in, in case there were fields added to the spec
})
if err != nil {
return fmt.Errorf("update installation: %w", err)
}

Expand Down
2 changes: 1 addition & 1 deletion operator/pkg/upgrade/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func upgradeExtensions(ctx context.Context, cli client.Client, in *ecv1beta1.Ins
return fmt.Errorf("set installation state: %w", err)
}

previous, err := kubeutils.GetPreviousCRDInstallation(ctx, cli, in)
previous, err := kubeutils.GetPreviousInstallation(ctx, cli, in)
if err != nil {
return fmt.Errorf("get previous installation: %w", err)
}
Expand Down
7 changes: 3 additions & 4 deletions pkg/addons/embeddedclusteroperator/embeddedclusteroperator.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func (e *EmbeddedClusterOperator) Outro(ctx context.Context, cli client.Client,
}
}

installation := ecv1beta1.Installation{
installation := &ecv1beta1.Installation{
TypeMeta: metav1.TypeMeta{
APIVersion: ecv1beta1.GroupVersion.String(),
Kind: "Installation",
Expand All @@ -284,12 +284,11 @@ func (e *EmbeddedClusterOperator) Outro(ctx context.Context, cli client.Client,
},
},
}
if err := cli.Create(ctx, &installation); err != nil {
if err := cli.Create(ctx, installation); err != nil {
return fmt.Errorf("unable to create installation: %w", err)
}

// we wait for the installation to exist here because items do not show up in the apiserver instantaneously after being created
if err := kubeutils.WaitAndMarkInstallation(ctx, cli, installation.Name, ecv1beta1.InstallationStateKubernetesInstalled); err != nil {
if err := kubeutils.SetInstallationState(ctx, cli, installation, ecv1beta1.InstallationStateKubernetesInstalled, "Kubernetes installed"); err != nil {
return fmt.Errorf("unable to wait and mark installation: %w", err)
}

Expand Down
21 changes: 14 additions & 7 deletions pkg/addons2/adminconsole/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,32 @@ func (a *AdminConsole) GenerateHelmValues(ctx context.Context, kcli client.Clien
copiedValues["isAirgap"] = "false"
}

extraEnv := []map[string]interface{}{
{
"name": "ENABLE_IMPROVED_DR",
"value": "true",
},
}

if a.Proxy != nil {
copiedValues["extraEnv"] = []map[string]interface{}{
{
extraEnv = append(extraEnv,
map[string]interface{}{
"name": "HTTP_PROXY",
"value": a.Proxy.HTTPProxy,
},
{
map[string]interface{}{
"name": "HTTPS_PROXY",
"value": a.Proxy.HTTPSProxy,
},
{
map[string]interface{}{
"name": "NO_PROXY",
"value": a.Proxy.NoProxy,
},
}
} else {
delete(copiedValues, "extraEnv")
)
}

copiedValues["extraEnv"] = extraEnv

copiedValues, err = helm.SetValue(copiedValues, "kurlProxy.nodePort", runtimeconfig.AdminConsolePort())
if err != nil {
return nil, errors.Wrap(err, "set kurlProxy.nodePort")
Expand Down
5 changes: 3 additions & 2 deletions pkg/addons2/highavailability.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,10 @@ func EnableHA(ctx context.Context, kcli client.Client, isAirgap bool, serviceCID
if err != nil {
return errors.Wrap(err, "get latest installation")
}
in.Spec.HighAvailability = true

if err := kubeutils.UpdateInstallation(ctx, kcli, in); err != nil {
if err := kubeutils.UpdateInstallation(ctx, kcli, in, func(in *ecv1beta1.Installation) {
in.Spec.HighAvailability = true
}); err != nil {
return errors.Wrap(err, "update installation")
}

Expand Down
4 changes: 0 additions & 4 deletions pkg/dryrun/kubeutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ func (k *KubeUtils) WaitForKubernetes(ctx context.Context, cli client.Client) <-
return errCh
}

func (k *KubeUtils) WaitAndMarkInstallation(ctx context.Context, cli client.Client, name string, state string) error {
return nil
}

func (k *KubeUtils) KubeClient() (client.Client, error) {
return KubeClient()
}
2 changes: 2 additions & 0 deletions pkg/extensions/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,7 @@ func Install(ctx context.Context, isAirgap bool) error {
}
}

loading.Infof("Extensions installed!")

return nil
}
6 changes: 4 additions & 2 deletions pkg/highavailability/enable.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
"github.com/replicatedhq/embedded-cluster/pkg/constants"
"github.com/replicatedhq/embedded-cluster/pkg/kubeutils"
"github.com/replicatedhq/embedded-cluster/pkg/spinner"
Expand Down Expand Up @@ -63,8 +64,9 @@ func EnableHA(ctx context.Context, kcli client.Client) error {
}

loading.Debugf("updating installation")
in.Spec.HighAvailability = true
if err := kubeutils.UpdateInstallation(ctx, kcli, in); err != nil {
if err := kubeutils.UpdateInstallation(ctx, kcli, in, func(in *ecv1beta1.Installation) {
in.Spec.HighAvailability = true
}); err != nil {
return fmt.Errorf("unable to update installation: %w", err)
}
}
Expand Down
5 changes: 0 additions & 5 deletions pkg/kubeutils/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ type KubeUtilsInterface interface {
IsDaemonsetReady(ctx context.Context, cli client.Client, ns, name string) (bool, error)
IsJobComplete(ctx context.Context, cli client.Client, ns, name string, completions int32) (bool, error)
WaitForKubernetes(ctx context.Context, cli client.Client) <-chan error
WaitAndMarkInstallation(ctx context.Context, cli client.Client, name string, state string) error
KubeClient() (client.Client, error)
}

Expand Down Expand Up @@ -119,10 +118,6 @@ func WaitForKubernetes(ctx context.Context, cli client.Client) <-chan error {
return kb.WaitForKubernetes(ctx, cli)
}

func WaitAndMarkInstallation(ctx context.Context, cli client.Client, name string, state string) error {
return kb.WaitAndMarkInstallation(ctx, cli, name, state)
}

func KubeClient() (client.Client, error) {
return kb.KubeClient()
}
Loading

0 comments on commit 7266fe1

Please sign in to comment.