From e275f99ffb6d4c56f68becbdf32c3a989314fac3 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Mon, 14 Apr 2025 13:34:10 -0400 Subject: [PATCH 1/8] use EC join command response type from EC kinds --- go.mod | 5 ++-- go.sum | 6 +++-- pkg/embeddedcluster/upgrade.go | 2 +- .../embedded_cluster_node_join_command.go | 24 +++++++++---------- ...embedded_cluster_node_join_command_test.go | 21 +++++++++------- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index bdbe7d2abb..298c90bd43 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/replicatedhq/kots -go 1.24.0 +go 1.24.1 require ( cloud.google.com/go/storage v1.45.0 @@ -49,7 +49,7 @@ require ( github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250411154749-d20d2f980f0c + github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414165528-9a70c8108ced github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a github.com/replicatedhq/kurlkinds v1.5.0 github.com/replicatedhq/troubleshoot v0.117.0 @@ -414,6 +414,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.1.0 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/logutils v1.0.0 // indirect + github.com/k0sproject/dig v0.2.0 // indirect github.com/kubernetes-csi/external-snapshotter/client/v7 v7.0.0 // indirect github.com/miekg/dns v1.1.63 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect diff --git a/go.sum b/go.sum index 2c8e0fcb4b..1cc9ed4ff9 100644 --- a/go.sum +++ b/go.sum @@ -1545,6 +1545,8 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/k0sproject/dig v0.2.0 h1:cNxEIl96g9kqSMfPSZLhpnZ0P8bWXKv08nxvsMHop5w= +github.com/k0sproject/dig v0.2.0/go.mod h1:rBcqaQlJpcKdt2x/OE/lPvhGU50u/e95CSm5g/r4s78= github.com/k0sproject/k0s v1.30.10-0.20250117153350-dcf3c22bb568 h1:JSfvTBrsNMWDISDUMVRZV6hP5eRusBS6d0Gv2lA4lSA= github.com/k0sproject/k0s v1.30.10-0.20250117153350-dcf3c22bb568/go.mod h1:Nmj+slwFht6ile7OHHGiSrcRRGmrA9U9PzjnG9/6gc0= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= @@ -1861,8 +1863,8 @@ github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0 github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250411154749-d20d2f980f0c h1:QoAn+gFKypZPuybDUIzYQt/hzSLOMwTab/csazCAZQ8= -github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250411154749-d20d2f980f0c/go.mod h1:DZVH5BSrkKaZPYO6psQYRZgzPdwaxrh8CpJYW95D76E= +github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414165528-9a70c8108ced h1:2w1EoI+TVv/9kznNRbS31bzvFdBtRIo961owNg1maCI= +github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414165528-9a70c8108ced/go.mod h1:+f76CfnrG1XqQyRvRaJ0+xMkRP9uvlWjx4hFySfnfnw= github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a h1:aNZ7qcuEmPGIUIIfxF7c0sdKR2+zL2vc5r2V8j8a49I= github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I= github.com/replicatedhq/kurlkinds v1.5.0 h1:zZ0PKNeh4kXvSzVGkn62DKTo314GxhXg1TSB3azURMc= diff --git a/pkg/embeddedcluster/upgrade.go b/pkg/embeddedcluster/upgrade.go index 7bf7d0c976..3e11651e18 100644 --- a/pkg/embeddedcluster/upgrade.go +++ b/pkg/embeddedcluster/upgrade.go @@ -80,7 +80,7 @@ func startClusterUpgrade( newInstall.Spec.Config = &newcfg newInstall.Spec.LicenseInfo = &embeddedclusterv1beta1.LicenseInfo{ IsDisasterRecoverySupported: license.Spec.IsDisasterRecoverySupported, - IsMultiNodeEnabled: license.Spec.IsEmbeddedClusterMultiNodeEnabled, + //IsMultiNodeEnabled: license.Spec.IsEmbeddedClusterMultiNodeEnabled, } log.Printf("Starting cluster upgrade to version %s...", newcfg.Version) diff --git a/pkg/handlers/embedded_cluster_node_join_command.go b/pkg/handlers/embedded_cluster_node_join_command.go index 5efc2f88c4..7a5b042fcb 100644 --- a/pkg/handlers/embedded_cluster_node_join_command.go +++ b/pkg/handlers/embedded_cluster_node_join_command.go @@ -5,7 +5,8 @@ import ( "fmt" "net/http" - ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" + "github.com/google/uuid" + ecJoin "github.com/replicatedhq/embedded-cluster/kinds/types/join" "github.com/replicatedhq/kots/pkg/embeddedcluster" "github.com/replicatedhq/kots/pkg/k8sutil" @@ -19,16 +20,6 @@ type GenerateEmbeddedClusterNodeJoinCommandResponse struct { Command []string `json:"command"` } -type GetEmbeddedClusterNodeJoinCommandResponse struct { - ClusterID string `json:"clusterID"` - K0sJoinCommand string `json:"k0sJoinCommand"` - K0sToken string `json:"k0sToken"` - EmbeddedClusterVersion string `json:"embeddedClusterVersion"` - AirgapRegistryAddress string `json:"airgapRegistryAddress"` - TCPConnectionsRequired []string `json:"tcpConnectionsRequired"` - InstallationSpec ecv1beta1.InstallationSpec `json:"installationSpec,omitempty"` -} - type GenerateEmbeddedClusterNodeJoinCommandRequest struct { Roles []string `json:"roles"` } @@ -178,8 +169,15 @@ func (h *Handler) GetEmbeddedClusterNodeJoinCommand(w http.ResponseWriter, r *ht return } - JSON(w, http.StatusOK, GetEmbeddedClusterNodeJoinCommandResponse{ - ClusterID: install.Spec.ClusterID, + clusterUUID, err := uuid.Parse(install.Spec.ClusterID) + if err != nil { + logger.Error(fmt.Errorf("failed to parse cluster id: %w", err)) + w.WriteHeader(http.StatusInternalServerError) + return + } + + JSON(w, http.StatusOK, ecJoin.JoinCommandResponse{ + ClusterID: clusterUUID, K0sJoinCommand: k0sJoinCommand, K0sToken: k0sToken, EmbeddedClusterVersion: ecVersion, diff --git a/pkg/handlers/embedded_cluster_node_join_command_test.go b/pkg/handlers/embedded_cluster_node_join_command_test.go index f1e1f6da24..de5528f502 100644 --- a/pkg/handlers/embedded_cluster_node_join_command_test.go +++ b/pkg/handlers/embedded_cluster_node_join_command_test.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/google/uuid" "net/http" "net/http/httptest" "testing" @@ -12,6 +13,7 @@ import ( gomock "github.com/golang/mock/gomock" embeddedclusterv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" + "github.com/replicatedhq/embedded-cluster/kinds/types/join" "github.com/replicatedhq/kots/pkg/handlers/kubeclient" "github.com/replicatedhq/kots/pkg/store" mockstore "github.com/replicatedhq/kots/pkg/store/mock" @@ -33,13 +35,14 @@ type testNodeJoinCommandHarness struct { token string getRoles func(t *testing.T, token string) ([]string, error) embeddedClusterID string - validateBody func(t *testing.T, h *testNodeJoinCommandHarness, r *GetEmbeddedClusterNodeJoinCommandResponse) + validateBody func(t *testing.T, h *testNodeJoinCommandHarness, r *join.JoinCommandResponse) } func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { scheme := runtime.NewScheme() corev1.AddToScheme(scheme) embeddedclusterv1beta1.AddToScheme(scheme) + ecUUID := uuid.New().String() tests := []testNodeJoinCommandHarness{ { @@ -50,7 +53,7 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { { name: "store returns error", httpStatus: http.StatusInternalServerError, - embeddedClusterID: "cluster-id", + embeddedClusterID: ecUUID, getRoles: func(*testing.T, string) ([]string, error) { return nil, fmt.Errorf("some error") }, @@ -58,7 +61,7 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { { name: "store gets passed the provided token", httpStatus: http.StatusInternalServerError, - embeddedClusterID: "cluster-id", + embeddedClusterID: ecUUID, token: "some-token", getRoles: func(t *testing.T, token string) ([]string, error) { require.Equal(t, "some-token", token) @@ -68,8 +71,8 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { { name: "bootstrap token secret creation succeeds and it matches returned K0SToken", httpStatus: http.StatusOK, - embeddedClusterID: "cluster-id", - validateBody: func(t *testing.T, h *testNodeJoinCommandHarness, r *GetEmbeddedClusterNodeJoinCommandResponse) { + embeddedClusterID: ecUUID, + validateBody: func(t *testing.T, h *testNodeJoinCommandHarness, r *join.JoinCommandResponse) { req := require.New(t) // Check that a secret was created with the cluster bootstrap token var secrets corev1.SecretList @@ -100,6 +103,7 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { }, Spec: embeddedclusterv1beta1.InstallationSpec{ BinaryName: "my-app", + ClusterID: ecUUID, Config: &embeddedclusterv1beta1.ConfigSpec{ Version: "v1.100.0", Roles: embeddedclusterv1beta1.Roles{ @@ -144,8 +148,8 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { { name: "tcp connections required are returned based on the controller role provided", httpStatus: http.StatusOK, - embeddedClusterID: "cluster-id", - validateBody: func(t *testing.T, h *testNodeJoinCommandHarness, r *GetEmbeddedClusterNodeJoinCommandResponse) { + embeddedClusterID: ecUUID, + validateBody: func(t *testing.T, h *testNodeJoinCommandHarness, r *join.JoinCommandResponse) { req := require.New(t) req.Equal([]string{ @@ -166,6 +170,7 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { }, Spec: embeddedclusterv1beta1.InstallationSpec{ BinaryName: "my-app", + ClusterID: ecUUID, Config: &embeddedclusterv1beta1.ConfigSpec{ Version: "v1.100.0", Roles: embeddedclusterv1beta1.Roles{ @@ -274,7 +279,7 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { } // Run the body validation function if provided - var body GetEmbeddedClusterNodeJoinCommandResponse + var body join.JoinCommandResponse req.NoError(json.NewDecoder(response.Body).Decode(&body)) if test.validateBody != nil { test.validateBody(t, &test, &body) From ad4d376bbf4021898ba20511f0c6394e2f9d4b8e Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Mon, 14 Apr 2025 14:05:01 -0400 Subject: [PATCH 2/8] return kots app version label if the app is installed --- .../embedded_cluster_node_join_command.go | 17 ++++ ...embedded_cluster_node_join_command_test.go | 88 ++++++++++++++++++- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/pkg/handlers/embedded_cluster_node_join_command.go b/pkg/handlers/embedded_cluster_node_join_command.go index 7a5b042fcb..87d0cd932a 100644 --- a/pkg/handlers/embedded_cluster_node_join_command.go +++ b/pkg/handlers/embedded_cluster_node_join_command.go @@ -176,6 +176,22 @@ func (h *Handler) GetEmbeddedClusterNodeJoinCommand(w http.ResponseWriter, r *ht return } + var currentAppVersionLabel string + // attempt to get the current app version label from the installed app + installedApps, err := store.GetStore().ListInstalledApps() + if err == nil && len(installedApps) > 0 { + appVersion, err := store.GetStore().GetAppVersion(installedApps[0].ID, installedApps[0].CurrentSequence) + if err != nil { + logger.Error(fmt.Errorf("failed to get app version: %w", err)) + w.WriteHeader(http.StatusInternalServerError) + return + } + currentAppVersionLabel = appVersion.VersionLabel + } else { + // if there are no installed apps, we can't get the current app version label + logger.Info("no installed apps found") + } + JSON(w, http.StatusOK, ecJoin.JoinCommandResponse{ ClusterID: clusterUUID, K0sJoinCommand: k0sJoinCommand, @@ -184,5 +200,6 @@ func (h *Handler) GetEmbeddedClusterNodeJoinCommand(w http.ResponseWriter, r *ht AirgapRegistryAddress: airgapRegistryAddress, TCPConnectionsRequired: endpoints, InstallationSpec: install.Spec, + AppVersionLabel: currentAppVersionLabel, }) } diff --git a/pkg/handlers/embedded_cluster_node_join_command_test.go b/pkg/handlers/embedded_cluster_node_join_command_test.go index de5528f502..077481f919 100644 --- a/pkg/handlers/embedded_cluster_node_join_command_test.go +++ b/pkg/handlers/embedded_cluster_node_join_command_test.go @@ -6,6 +6,8 @@ import ( "encoding/json" "fmt" "github.com/google/uuid" + versionTypes "github.com/replicatedhq/kots/pkg/api/version/types" + appTypes "github.com/replicatedhq/kots/pkg/app/types" "net/http" "net/http/httptest" "testing" @@ -35,6 +37,7 @@ type testNodeJoinCommandHarness struct { token string getRoles func(t *testing.T, token string) ([]string, error) embeddedClusterID string + appVersionLabel string validateBody func(t *testing.T, h *testNodeJoinCommandHarness, r *join.JoinCommandResponse) } @@ -72,6 +75,7 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { name: "bootstrap token secret creation succeeds and it matches returned K0SToken", httpStatus: http.StatusOK, embeddedClusterID: ecUUID, + appVersionLabel: "test-app-version", validateBody: func(t *testing.T, h *testNodeJoinCommandHarness, r *join.JoinCommandResponse) { req := require.New(t) // Check that a secret was created with the cluster bootstrap token @@ -94,7 +98,11 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { decompressed, err := util.GunzipData(decodedK0SToken) req.NoError(err) - require.Containsf(t, string(decompressed), fmt.Sprintf("token: %s", expectedToken), "expected K0sToken:\n%s\nto contain the generated bootstrap token: %s", string(decompressed), expectedToken) + req.Containsf(string(decompressed), fmt.Sprintf("token: %s", expectedToken), "expected K0sToken:\n%s\nto contain the generated bootstrap token: %s", string(decompressed), expectedToken) + + // returned embedded cluster and app version should match the expected values + req.Equal(r.AppVersionLabel, "test-app-version") + req.Equal(r.ClusterID.String(), ecUUID) }, kbClient: fake.NewClientBuilder().WithScheme(scheme).WithObjects( &embeddedclusterv1beta1.Installation{ @@ -149,6 +157,7 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { name: "tcp connections required are returned based on the controller role provided", httpStatus: http.StatusOK, embeddedClusterID: ecUUID, + appVersionLabel: "test-app-version", validateBody: func(t *testing.T, h *testNodeJoinCommandHarness, r *join.JoinCommandResponse) { req := require.New(t) @@ -232,6 +241,65 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { }, ).Build(), }, + { + name: "If there is no installed app, the app version label should be empty", + httpStatus: http.StatusOK, + embeddedClusterID: ecUUID, + validateBody: func(t *testing.T, h *testNodeJoinCommandHarness, r *join.JoinCommandResponse) { + req := require.New(t) + // returned embedded cluster and app version should match the expected values + req.Equal(r.AppVersionLabel, "") + req.Equal(r.ClusterID.String(), ecUUID) + }, + kbClient: fake.NewClientBuilder().WithScheme(scheme).WithObjects( + &embeddedclusterv1beta1.Installation{ + ObjectMeta: metav1.ObjectMeta{ + Name: time.Now().Format("20060102150405"), + }, + Spec: embeddedclusterv1beta1.InstallationSpec{ + BinaryName: "my-app", + ClusterID: ecUUID, + Config: &embeddedclusterv1beta1.ConfigSpec{ + Version: "v1.100.0", + Roles: embeddedclusterv1beta1.Roles{ + Controller: embeddedclusterv1beta1.NodeRole{ + Name: "controller-role", + }, + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-root-ca.crt", + Namespace: "kube-system", + }, + Data: map[string]string{"ca.crt": "some-ca-cert"}, + }, + &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "controller 1", + Labels: map[string]string{ + "node-role.kubernetes.io/control-plane": "true", + }, + }, + Status: corev1.NodeStatus{ + Conditions: []corev1.NodeCondition{ + { + Type: corev1.NodeReady, + Status: corev1.ConditionTrue, + }, + }, + Addresses: []corev1.NodeAddress{ + { + Type: corev1.NodeInternalIP, + Address: "192.168.0.100", + }, + }, + }, + }, + ).Build(), + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -256,6 +324,24 @@ func TestGetEmbeddedClusterNodeJoinCommand(t *testing.T) { return []string{"controller-role", "worker-role"}, nil }) + if test.appVersionLabel != "" { + mockStore.EXPECT().ListInstalledApps().AnyTimes().DoAndReturn(func() ([]*appTypes.App, error) { + return []*appTypes.App{ + { + ID: "test-app-id", + CurrentSequence: 1, + }, + }, nil + }) + mockStore.EXPECT().GetAppVersion("test-app-id", int64(1)).AnyTimes().DoAndReturn(func(appID string, sequence int64) (*versionTypes.AppVersion, error) { + return &versionTypes.AppVersion{ + VersionLabel: test.appVersionLabel, + }, nil + }) + } else if test.httpStatus == http.StatusOK { + mockStore.EXPECT().ListInstalledApps().AnyTimes().Return([]*appTypes.App{}, nil) + } + // There's an early check in the handler for the presence of `EMBEDDED_CLUSTER_ID` env var // so we need to set it here whenever the test requires it if test.embeddedClusterID != "" { From 538ad77b2204dd3e516eaca2e165e07a6f08322a Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Mon, 14 Apr 2025 17:03:24 -0400 Subject: [PATCH 3/8] update types again --- go.mod | 2 +- go.sum | 4 ++-- pkg/embeddedcluster/upgrade.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 298c90bd43..e68d8652f0 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414165528-9a70c8108ced + github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414204401-2087178424d1 github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a github.com/replicatedhq/kurlkinds v1.5.0 github.com/replicatedhq/troubleshoot v0.117.0 diff --git a/go.sum b/go.sum index 1cc9ed4ff9..5ed26af6a0 100644 --- a/go.sum +++ b/go.sum @@ -1863,8 +1863,8 @@ github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0 github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414165528-9a70c8108ced h1:2w1EoI+TVv/9kznNRbS31bzvFdBtRIo961owNg1maCI= -github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414165528-9a70c8108ced/go.mod h1:+f76CfnrG1XqQyRvRaJ0+xMkRP9uvlWjx4hFySfnfnw= +github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414204401-2087178424d1 h1:YhqtOVI0gpuK3HlYbK0U81EEk25mNX3PBR16JzCnLy4= +github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414204401-2087178424d1/go.mod h1:+f76CfnrG1XqQyRvRaJ0+xMkRP9uvlWjx4hFySfnfnw= github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a h1:aNZ7qcuEmPGIUIIfxF7c0sdKR2+zL2vc5r2V8j8a49I= github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I= github.com/replicatedhq/kurlkinds v1.5.0 h1:zZ0PKNeh4kXvSzVGkn62DKTo314GxhXg1TSB3azURMc= diff --git a/pkg/embeddedcluster/upgrade.go b/pkg/embeddedcluster/upgrade.go index 3e11651e18..7bf7d0c976 100644 --- a/pkg/embeddedcluster/upgrade.go +++ b/pkg/embeddedcluster/upgrade.go @@ -80,7 +80,7 @@ func startClusterUpgrade( newInstall.Spec.Config = &newcfg newInstall.Spec.LicenseInfo = &embeddedclusterv1beta1.LicenseInfo{ IsDisasterRecoverySupported: license.Spec.IsDisasterRecoverySupported, - //IsMultiNodeEnabled: license.Spec.IsEmbeddedClusterMultiNodeEnabled, + IsMultiNodeEnabled: license.Spec.IsEmbeddedClusterMultiNodeEnabled, } log.Printf("Starting cluster upgrade to version %s...", newcfg.Version) From 5fbe477744d9101e9a5d8f08ff38079de56394d8 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Tue, 15 Apr 2025 18:50:52 -0400 Subject: [PATCH 4/8] update with unified kinds --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e68d8652f0..181115ba96 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414204401-2087178424d1 + github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250415224730-0f6eb6643335 github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a github.com/replicatedhq/kurlkinds v1.5.0 github.com/replicatedhq/troubleshoot v0.117.0 diff --git a/go.sum b/go.sum index 5ed26af6a0..de4a871fe8 100644 --- a/go.sum +++ b/go.sum @@ -1863,8 +1863,8 @@ github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0 github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414204401-2087178424d1 h1:YhqtOVI0gpuK3HlYbK0U81EEk25mNX3PBR16JzCnLy4= -github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250414204401-2087178424d1/go.mod h1:+f76CfnrG1XqQyRvRaJ0+xMkRP9uvlWjx4hFySfnfnw= +github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250415224730-0f6eb6643335 h1:PHC8qBJWV7+gfj1icO6dcM3eU5ofURdpcRdr2aj6sDw= +github.com/replicatedhq/embedded-cluster/kinds v1.15.1-0.20250415224730-0f6eb6643335/go.mod h1:+f76CfnrG1XqQyRvRaJ0+xMkRP9uvlWjx4hFySfnfnw= github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a h1:aNZ7qcuEmPGIUIIfxF7c0sdKR2+zL2vc5r2V8j8a49I= github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I= github.com/replicatedhq/kurlkinds v1.5.0 h1:zZ0PKNeh4kXvSzVGkn62DKTo314GxhXg1TSB3azURMc= From e99df563fd41297d4023ad7119ea72f1d8be802c Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Wed, 16 Apr 2025 11:10:52 -0400 Subject: [PATCH 5/8] cleanup imports --- pkg/handlers/embedded_cluster_node_join_command.go | 4 ++-- pkg/handlers/embedded_cluster_node_join_command_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/handlers/embedded_cluster_node_join_command.go b/pkg/handlers/embedded_cluster_node_join_command.go index 87d0cd932a..83b3a504de 100644 --- a/pkg/handlers/embedded_cluster_node_join_command.go +++ b/pkg/handlers/embedded_cluster_node_join_command.go @@ -6,7 +6,7 @@ import ( "net/http" "github.com/google/uuid" - ecJoin "github.com/replicatedhq/embedded-cluster/kinds/types/join" + "github.com/replicatedhq/embedded-cluster/kinds/types/join" "github.com/replicatedhq/kots/pkg/embeddedcluster" "github.com/replicatedhq/kots/pkg/k8sutil" @@ -192,7 +192,7 @@ func (h *Handler) GetEmbeddedClusterNodeJoinCommand(w http.ResponseWriter, r *ht logger.Info("no installed apps found") } - JSON(w, http.StatusOK, ecJoin.JoinCommandResponse{ + JSON(w, http.StatusOK, join.JoinCommandResponse{ ClusterID: clusterUUID, K0sJoinCommand: k0sJoinCommand, K0sToken: k0sToken, diff --git a/pkg/handlers/embedded_cluster_node_join_command_test.go b/pkg/handlers/embedded_cluster_node_join_command_test.go index 077481f919..db88fcbba3 100644 --- a/pkg/handlers/embedded_cluster_node_join_command_test.go +++ b/pkg/handlers/embedded_cluster_node_join_command_test.go @@ -5,17 +5,17 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/google/uuid" - versionTypes "github.com/replicatedhq/kots/pkg/api/version/types" - appTypes "github.com/replicatedhq/kots/pkg/app/types" "net/http" "net/http/httptest" "testing" "time" gomock "github.com/golang/mock/gomock" + "github.com/google/uuid" embeddedclusterv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" "github.com/replicatedhq/embedded-cluster/kinds/types/join" + versionTypes "github.com/replicatedhq/kots/pkg/api/version/types" + appTypes "github.com/replicatedhq/kots/pkg/app/types" "github.com/replicatedhq/kots/pkg/handlers/kubeclient" "github.com/replicatedhq/kots/pkg/store" mockstore "github.com/replicatedhq/kots/pkg/store/mock" From bd731a6023a76575ac5bfa12c5d4e73ea4a04441 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Wed, 16 Apr 2025 12:40:44 -0400 Subject: [PATCH 6/8] add CurrentSequence comment --- pkg/handlers/embedded_cluster_node_join_command.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/handlers/embedded_cluster_node_join_command.go b/pkg/handlers/embedded_cluster_node_join_command.go index 83b3a504de..0f4527e4ed 100644 --- a/pkg/handlers/embedded_cluster_node_join_command.go +++ b/pkg/handlers/embedded_cluster_node_join_command.go @@ -180,6 +180,10 @@ func (h *Handler) GetEmbeddedClusterNodeJoinCommand(w http.ResponseWriter, r *ht // attempt to get the current app version label from the installed app installedApps, err := store.GetStore().ListInstalledApps() if err == nil && len(installedApps) > 0 { + // "CurrentSequence" is the latest available version of the app in a non-embedded cluster. + // However, in an embedded cluster, the "CurrentSequence" is the latest version of the app. + // This is because EC uses the new upgrade flow, which only downloads and renders app releases when + // the app version is being installed. appVersion, err := store.GetStore().GetAppVersion(installedApps[0].ID, installedApps[0].CurrentSequence) if err != nil { logger.Error(fmt.Errorf("failed to get app version: %w", err)) From e76aa850bbfc62f62ddcf9c0e044d2a15802b2a4 Mon Sep 17 00:00:00 2001 From: Salah Al Saleh Date: Wed, 16 Apr 2025 09:47:11 -0700 Subject: [PATCH 7/8] Update pkg/handlers/embedded_cluster_node_join_command.go --- pkg/handlers/embedded_cluster_node_join_command.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/handlers/embedded_cluster_node_join_command.go b/pkg/handlers/embedded_cluster_node_join_command.go index 0f4527e4ed..3e1413aa0a 100644 --- a/pkg/handlers/embedded_cluster_node_join_command.go +++ b/pkg/handlers/embedded_cluster_node_join_command.go @@ -181,9 +181,9 @@ func (h *Handler) GetEmbeddedClusterNodeJoinCommand(w http.ResponseWriter, r *ht installedApps, err := store.GetStore().ListInstalledApps() if err == nil && len(installedApps) > 0 { // "CurrentSequence" is the latest available version of the app in a non-embedded cluster. - // However, in an embedded cluster, the "CurrentSequence" is the latest version of the app. - // This is because EC uses the new upgrade flow, which only downloads and renders app releases when - // the app version is being installed. + // However, in an embedded cluster, the "CurrentSequence" is also the currently deployed version of the app. + // This is because EC uses the new upgrade flow, which only creates a new app version when + // the app version gets deployed. And because rollbacks are not supported in embedded cluster yet. appVersion, err := store.GetStore().GetAppVersion(installedApps[0].ID, installedApps[0].CurrentSequence) if err != nil { logger.Error(fmt.Errorf("failed to get app version: %w", err)) From 6fb20679bb0d817a491559832fe2307a5de45091 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Wed, 16 Apr 2025 13:07:15 -0400 Subject: [PATCH 8/8] return error if fail to get apps --- pkg/handlers/embedded_cluster_node_join_command.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/handlers/embedded_cluster_node_join_command.go b/pkg/handlers/embedded_cluster_node_join_command.go index 3e1413aa0a..db16fa4870 100644 --- a/pkg/handlers/embedded_cluster_node_join_command.go +++ b/pkg/handlers/embedded_cluster_node_join_command.go @@ -179,7 +179,11 @@ func (h *Handler) GetEmbeddedClusterNodeJoinCommand(w http.ResponseWriter, r *ht var currentAppVersionLabel string // attempt to get the current app version label from the installed app installedApps, err := store.GetStore().ListInstalledApps() - if err == nil && len(installedApps) > 0 { + if err != nil { + logger.Error(fmt.Errorf("failed to list installed apps: %w", err)) + w.WriteHeader(http.StatusInternalServerError) + return + } else if len(installedApps) > 0 { // "CurrentSequence" is the latest available version of the app in a non-embedded cluster. // However, in an embedded cluster, the "CurrentSequence" is also the currently deployed version of the app. // This is because EC uses the new upgrade flow, which only creates a new app version when