diff --git a/pkg/api/handlers/types/types.go b/pkg/api/handlers/types/types.go
index b97e2b559c..7d3301a33f 100644
--- a/pkg/api/handlers/types/types.go
+++ b/pkg/api/handlers/types/types.go
@@ -37,6 +37,7 @@ type ResponseApp struct {
UpdateCheckerSpec string `json:"updateCheckerSpec"`
AutoDeploy apptypes.AutoDeploy `json:"autoDeploy"`
Namespace string `json:"namespace"`
+ AppState string `json:"appState"`
IsGitOpsSupported bool `json:"isGitOpsSupported"`
IsIdentityServiceSupported bool `json:"isIdentityServiceSupported"`
diff --git a/pkg/embeddedcluster/monitor.go b/pkg/embeddedcluster/monitor.go
index 4573423f2e..a0ee5ef401 100644
--- a/pkg/embeddedcluster/monitor.go
+++ b/pkg/embeddedcluster/monitor.go
@@ -8,6 +8,7 @@ import (
"time"
"github.com/replicatedhq/embedded-cluster-operator/api/v1beta1"
+ appstatetypes "github.com/replicatedhq/kots/pkg/appstate/types"
"github.com/replicatedhq/kots/pkg/logger"
"github.com/replicatedhq/kots/pkg/store"
"github.com/replicatedhq/kots/pkg/util"
@@ -21,7 +22,7 @@ var stateMut = sync.Mutex{}
// - The app has an embedded cluster configuration.
// - The app embedded cluster configuration differs from the current embedded cluster config.
// - The current cluster config (as part of the Installation object) already exists in the cluster.
-func MaybeStartClusterUpgrade(ctx context.Context, client kubernetes.Interface, store store.Store, conf *v1beta1.Config) error {
+func MaybeStartClusterUpgrade(ctx context.Context, store store.Store, conf *v1beta1.Config, appID string) error {
if conf == nil {
return nil
}
@@ -43,13 +44,37 @@ func MaybeStartClusterUpgrade(ctx context.Context, client kubernetes.Interface,
} else if !upgrade {
return nil
}
- if err := startClusterUpgrade(ctx, spec); err != nil {
- return fmt.Errorf("failed to start cluster upgrade: %w", err)
- }
- go watchClusterState(ctx, store)
+ // we need to wait for the application to be ready before we can start the upgrade.
+ ticker := time.NewTicker(5 * time.Second)
+ defer ticker.Stop()
- return nil
+ for {
+ select {
+ case <-ctx.Done():
+ return nil
+ case <-ticker.C:
+ }
+
+ appStatus, err := store.GetAppStatus(appID)
+ if err != nil {
+ return fmt.Errorf("failed to get app status: %w", err)
+ }
+
+ if appStatus.State != appstatetypes.StateReady {
+ logger.Infof("waiting for app to be ready before starting cluster upgrade. current state: %s", appStatus.State)
+ continue
+ }
+
+ if err := startClusterUpgrade(ctx, spec); err != nil {
+ return fmt.Errorf("failed to start cluster upgrade: %w", err)
+ }
+ logger.Info("started cluster upgrade")
+
+ go watchClusterState(ctx, store)
+
+ return nil
+ }
}
// InitClusterState initializes the cluster state in the database. This should be called when the
diff --git a/pkg/handlers/app.go b/pkg/handlers/app.go
index 2b3bc05720..6872854ee5 100644
--- a/pkg/handlers/app.go
+++ b/pkg/handlers/app.go
@@ -10,6 +10,7 @@ import (
"github.com/gorilla/mux"
"github.com/pkg/errors"
+ embeddedclusterv1beta1 "github.com/replicatedhq/embedded-cluster-operator/api/v1beta1"
"github.com/replicatedhq/kots/pkg/airgap"
downstreamtypes "github.com/replicatedhq/kots/pkg/api/downstream/types"
"github.com/replicatedhq/kots/pkg/api/handlers/types"
@@ -228,6 +229,12 @@ func responseAppFromApp(a *apptypes.App) (*types.ResponseApp, error) {
return nil, errors.Wrap(err, "failed to get license")
}
+ appStatus, err := store.GetStore().GetAppStatus(a.ID)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to get app status")
+ }
+ appState := string(appStatus.State)
+
downstreams, err := store.GetStore().ListDownstreamsForApp(a.ID)
if err != nil {
return nil, errors.Wrap(err, "failed to list downstreams for app")
@@ -320,9 +327,12 @@ func responseAppFromApp(a *apptypes.App) (*types.ResponseApp, error) {
}
if util.IsEmbeddedCluster() {
- embeddedClusterConfig, err := store.GetStore().GetEmbeddedClusterConfigForVersion(a.ID, a.CurrentSequence)
- if err != nil {
- return nil, errors.Wrap(err, "failed to get embedded cluster config")
+ var embeddedClusterConfig *embeddedclusterv1beta1.Config
+ if appVersions.CurrentVersion != nil {
+ embeddedClusterConfig, err = store.GetStore().GetEmbeddedClusterConfigForVersion(a.ID, appVersions.CurrentVersion.Sequence)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to get embedded cluster config")
+ }
}
if embeddedClusterConfig != nil {
@@ -365,6 +375,7 @@ func responseAppFromApp(a *apptypes.App) (*types.ResponseApp, error) {
IsConfigurable: a.IsConfigurable,
UpdateCheckerSpec: a.UpdateCheckerSpec,
AutoDeploy: a.AutoDeploy,
+ AppState: appState,
IsGitOpsSupported: isGitopsSupported,
IsIdentityServiceSupported: license.Spec.IsIdentityServiceSupported,
IsAppIdentityServiceSupported: isAppIdentityServiceSupported,
diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go
index b5a2398d8f..7b5c4f306e 100644
--- a/pkg/operator/operator.go
+++ b/pkg/operator/operator.go
@@ -407,23 +407,16 @@ func (o *Operator) DeployApp(appID string, sequence int64) (deployed bool, deplo
}
deployed, err = o.client.DeployApp(deployArgs)
if err != nil {
- if util.IsEmbeddedCluster() {
- go func() {
- logger.Info("app deploy failed, starting cluster upgrade in the background")
- err2 := embeddedcluster.MaybeStartClusterUpgrade(context.Background(), o.k8sClientset, o.store, kotsKinds.EmbeddedClusterConfig)
- if err2 != nil {
- logger.Error(errors.Wrap(err2, "failed to start cluster upgrade"))
- }
- logger.Info("cluster upgrade started")
- }()
- }
-
return false, errors.Wrap(err, "failed to deploy app")
}
- err = embeddedcluster.MaybeStartClusterUpgrade(context.TODO(), o.k8sClientset, o.store, kotsKinds.EmbeddedClusterConfig)
- if err != nil {
- return false, errors.Wrap(err, "failed to start cluster upgrade")
+ if deployed {
+ go func() {
+ err = embeddedcluster.MaybeStartClusterUpgrade(context.TODO(), o.store, kotsKinds.EmbeddedClusterConfig, app.ID)
+ if err != nil {
+ logger.Error(errors.Wrap(err, "failed to start cluster upgrade"))
+ }
+ }()
}
return deployed, nil
diff --git a/web/src/features/AppVersionHistory/AppVersionHistoryRow.tsx b/web/src/features/AppVersionHistory/AppVersionHistoryRow.tsx
index 3b6243aae3..c3dc02d6ac 100644
--- a/web/src/features/AppVersionHistory/AppVersionHistoryRow.tsx
+++ b/web/src/features/AppVersionHistory/AppVersionHistoryRow.tsx
@@ -175,6 +175,12 @@ function AppVersionHistoryRow(props: Props) {
if (isHelmManaged) {
return false;
}
+ if (
+ Utilities.isPendingClusterUpgrade(selectedApp) &&
+ version.status === "deployed"
+ ) {
+ return true;
+ }
if (version.status === "deploying") {
return true;
}
@@ -465,6 +471,23 @@ function AppVersionHistoryRow(props: Props) {
});
if (!isPastVersion && !isPendingDeployedVersion) {
+ if (
+ Utilities.isPendingClusterUpgrade(selectedApp) &&
+ version.status === "deployed"
+ ) {
+ return (
+
+