Skip to content

Commit f02b6e7

Browse files
feat: support for multi channel licenses (#4767)
* feat: support for multi channel licenses --------- Co-authored-by: Salah Al Saleh <[email protected]>
1 parent 141e0b8 commit f02b6e7

File tree

76 files changed

+1380
-277
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1380
-277
lines changed

.github/actions/kots-e2e/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ runs:
9595
- name: execute suite "${{ inputs.test-focus }}"
9696
env:
9797
TESTIM_ACCESS_TOKEN: ${{ inputs.testim-access-token }}
98+
REPLICATED_API_TOKEN: ${{ inputs.replicated-api-token }}
9899
KOTS_NAMESPACE: ${{ inputs.kots-namespace }}
99100
run: |
100101
make -C e2e test \

.github/workflows/build-test.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,40 @@ jobs:
10851085
kots-dockerhub-username: '${{ secrets.E2E_DOCKERHUB_USERNAME }}'
10861086
kots-dockerhub-password: '${{ secrets.E2E_DOCKERHUB_PASSWORD }}'
10871087

1088+
validate-change-channel:
1089+
runs-on: ubuntu-20.04
1090+
needs: [ enable-tests, can-run-ci, build-kots, build-kotsadm, build-e2e, build-kurl-proxy, build-migrations, push-minio, push-rqlite ]
1091+
strategy:
1092+
fail-fast: false
1093+
matrix:
1094+
cluster: [
1095+
{distribution: kind, version: v1.28.0}
1096+
]
1097+
steps:
1098+
- name: Checkout
1099+
uses: actions/checkout@v4
1100+
- name: download e2e deps
1101+
uses: actions/download-artifact@v4
1102+
with:
1103+
name: e2e
1104+
path: e2e/bin/
1105+
- run: docker load -i e2e/bin/e2e-deps.tar
1106+
- run: chmod +x e2e/bin/*
1107+
- name: download kots binary
1108+
uses: actions/download-artifact@v4
1109+
with:
1110+
name: kots
1111+
path: bin/
1112+
- run: chmod +x bin/*
1113+
- uses: ./.github/actions/kots-e2e
1114+
with:
1115+
test-focus: 'Change Channel'
1116+
kots-namespace: 'change-channel'
1117+
k8s-distribution: ${{ matrix.cluster.distribution }}
1118+
k8s-version: ${{ matrix.cluster.version }}
1119+
replicated-api-token: '${{ secrets.C11Y_MATRIX_TOKEN }}'
1120+
kots-dockerhub-username: '${{ secrets.E2E_DOCKERHUB_USERNAME }}'
1121+
kots-dockerhub-password: '${{ secrets.E2E_DOCKERHUB_PASSWORD }}'
10881122

10891123
validate-minimal-rbac-override:
10901124
runs-on: ubuntu-20.04
@@ -4130,6 +4164,7 @@ jobs:
41304164
- validate-backup-and-restore
41314165
- validate-no-required-config
41324166
- validate-config
4167+
- validate-change-channel
41334168
# non-testim tests
41344169
- validate-minimal-rbac
41354170
- validate-minimal-rbac-override

cmd/kots/cli/install.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
kotsadmtypes "github.com/replicatedhq/kots/pkg/kotsadm/types"
3232
"github.com/replicatedhq/kots/pkg/kotsutil"
3333
"github.com/replicatedhq/kots/pkg/kurl"
34+
kotslicense "github.com/replicatedhq/kots/pkg/license"
3435
"github.com/replicatedhq/kots/pkg/logger"
3536
"github.com/replicatedhq/kots/pkg/metrics"
3637
preflighttypes "github.com/replicatedhq/kots/pkg/preflight/types"
@@ -162,6 +163,14 @@ func InstallCmd() *cobra.Command {
162163
}()
163164

164165
upstream := pull.RewriteUpstream(args[0])
166+
preferredChannelSlug, err := extractPreferredChannelSlug(upstream)
167+
if err != nil {
168+
return errors.Wrap(err, "failed to extract preferred channel slug")
169+
}
170+
license, err = kotslicense.VerifyAndUpdateLicense(log, license, preferredChannelSlug, isAirgap)
171+
if err != nil {
172+
return errors.Wrap(err, "failed to verify and update license")
173+
}
165174

166175
namespace := v.GetString("namespace")
167176

@@ -278,6 +287,7 @@ func InstallCmd() *cobra.Command {
278287
IncludeMinio: v.GetBool("with-minio"),
279288
IncludeMinioSnapshots: v.GetBool("with-minio"),
280289
StrictSecurityContext: v.GetBool("strict-security-context"),
290+
RequestedChannelSlug: preferredChannelSlug,
281291

282292
RegistryConfig: *registryConfig,
283293

cmd/kots/cli/pull.go

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import (
77

88
"github.com/pkg/errors"
99
"github.com/replicatedhq/kots/pkg/k8sutil"
10+
"github.com/replicatedhq/kots/pkg/kotsutil"
11+
kotslicense "github.com/replicatedhq/kots/pkg/license"
1012
"github.com/replicatedhq/kots/pkg/logger"
1113
"github.com/replicatedhq/kots/pkg/pull"
1214
registrytypes "github.com/replicatedhq/kots/pkg/registry/types"
1315
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
14-
"github.com/replicatedhq/kotskinds/client/kotsclientset/scheme"
1516
"github.com/spf13/cobra"
1617
"github.com/spf13/viper"
1718
)
@@ -43,11 +44,13 @@ func PullCmd() *cobra.Command {
4344
}
4445
}
4546

46-
appSlug, err := getAppSlugForPull(args[0], v.GetString("license-file"))
47+
license, err := getLicense(v)
4748
if err != nil {
48-
return errors.Wrap(err, "failed to determine app slug")
49+
return errors.Wrap(err, "failed to get license")
4950
}
5051

52+
appSlug := getAppSlugForPull(args[0], license)
53+
5154
namespace, err := getNamespaceOrDefault(v.GetString("namespace"))
5255
if err != nil {
5356
return errors.Wrap(err, "failed to get namespace")
@@ -98,13 +101,30 @@ func PullCmd() *cobra.Command {
98101
}
99102

100103
upstream := pull.RewriteUpstream(args[0])
101-
renderDir, err := pull.Pull(upstream, pullOptions)
104+
preferredChannelSlug, err := extractPreferredChannelSlug(upstream)
102105
if err != nil {
103-
return errors.Wrap(err, "failed to pull")
106+
return errors.Wrap(err, "failed to extract preferred channel slug")
104107
}
105108

106109
log := logger.NewCLILogger(cmd.OutOrStdout())
107110
log.Initialize()
111+
112+
// If we are passed a multi-channel license, verify that the requested channel is in the license
113+
// so that we can warn the user immediately if it is not.
114+
license, err = kotslicense.VerifyAndUpdateLicense(log, license, preferredChannelSlug, false)
115+
if err != nil {
116+
return errors.Wrap(err, "failed to verify and update license")
117+
}
118+
pullOptions.AppSelectedChannelID, err = kotsutil.FindChannelIDInLicense(preferredChannelSlug, license)
119+
if err != nil { // should never happen since we just verified the channel
120+
return errors.Wrap(err, "failed to find channel ID in license")
121+
}
122+
123+
renderDir, err := pull.Pull(upstream, pullOptions)
124+
if err != nil {
125+
return errors.Wrap(err, "failed to pull")
126+
}
127+
108128
log.Info("Kubernetes application files created in %s", renderDir)
109129
if len(v.GetStringSlice("downstream")) == 0 {
110130
log.Info("To deploy, run kubectl apply -k %s", path.Join(renderDir, "overlays", "midstream"))
@@ -146,28 +166,10 @@ func PullCmd() *cobra.Command {
146166
return cmd
147167
}
148168

149-
func getAppSlugForPull(uri string, licenseFile string) (string, error) {
169+
func getAppSlugForPull(uri string, license *kotsv1beta1.License) string {
150170
appSlug := strings.Split(uri, "/")[0]
151-
if licenseFile == "" {
152-
return appSlug, nil
153-
}
154-
155-
licenseData, err := os.ReadFile(licenseFile)
156-
if err != nil {
157-
return "", errors.Wrap(err, "failed to read license file")
158-
}
159-
160-
decode := scheme.Codecs.UniversalDeserializer().Decode
161-
decoded, gvk, err := decode(licenseData, nil, nil)
162-
if err != nil {
163-
return "", errors.Wrap(err, "unable to decode license file")
164-
}
165-
166-
if gvk.Group != "kots.io" || gvk.Version != "v1beta1" || gvk.Kind != "License" {
167-
return "", errors.New("not an application license")
171+
if license == nil {
172+
return appSlug
168173
}
169-
170-
license := decoded.(*kotsv1beta1.License)
171-
172-
return license.Spec.AppSlug, nil
174+
return license.Spec.AppSlug
173175
}

cmd/kots/cli/util.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/pkg/errors"
10+
"github.com/replicatedhq/kots/pkg/replicatedapp"
1011
"github.com/replicatedhq/kots/pkg/util"
1112
)
1213

@@ -51,3 +52,20 @@ func splitEndpointAndNamespace(endpoint string) (string, string) {
5152
}
5253
return registryEndpoint, registryNamespace
5354
}
55+
56+
func extractPreferredChannelSlug(upstreamURI string) (string, error) {
57+
u, err := url.ParseRequestURI(upstreamURI)
58+
if err != nil {
59+
return "", errors.Wrap(err, "failed to parse uri")
60+
}
61+
62+
replicatedUpstream, err := replicatedapp.ParseReplicatedURL(u)
63+
if err != nil {
64+
return "", errors.Wrap(err, "failed to parse replicated url")
65+
}
66+
67+
if replicatedUpstream.Channel != nil {
68+
return *replicatedUpstream.Channel, nil
69+
}
70+
return "stable", nil
71+
}

cmd/kots/cli/util_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,52 @@ func Test_getHostFromEndpoint(t *testing.T) {
9595
})
9696
}
9797
}
98+
99+
func Test_extractPreferredChannelSlug(t *testing.T) {
100+
type args struct {
101+
upstreamURI string
102+
}
103+
tests := []struct {
104+
name string
105+
args args
106+
want string
107+
wantErr bool
108+
}{
109+
{
110+
"no channel",
111+
args{
112+
upstreamURI: "replicated://app-slug",
113+
},
114+
"stable", // default channel
115+
false,
116+
},
117+
{
118+
"with channel",
119+
args{
120+
upstreamURI: "replicated://app-slug/channel",
121+
},
122+
"channel",
123+
false,
124+
},
125+
{
126+
"invalid uri",
127+
args{
128+
upstreamURI: "junk",
129+
},
130+
"",
131+
true,
132+
},
133+
}
134+
for _, tt := range tests {
135+
t.Run(tt.name, func(t *testing.T) {
136+
got, err := extractPreferredChannelSlug(tt.args.upstreamURI)
137+
if (err != nil) != tt.wantErr {
138+
t.Errorf("extractPreferredChannelSlug() error = %v, wantErr %v", err, tt.wantErr)
139+
return
140+
}
141+
if got != tt.want {
142+
t.Errorf("extractPreferredChannelSlug() = %v, want %v", got, tt.want)
143+
}
144+
})
145+
}
146+
}

e2e/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ test:
3232
docker run --rm -i --net host \
3333
-e TESTIM_ACCESS_TOKEN \
3434
-v $(BIN_DIR)/e2e.test:/usr/local/bin/e2e.test \
35+
-e REPLICATED_API_TOKEN \
3536
-v $(KOTS_BIN_DIR)/kots:/usr/local/bin/kots \
3637
-v $(KOTS_BIN_DIR)/kots:/usr/local/bin/kubectl-kots \
3738
-v $(PLAYWRIGHT_DIR)/playwright-report:/playwright/playwright-report \

e2e/e2e_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ var _ = Describe("E2E", func() {
221221
Entry(nil, inventory.MultiAppTest()),
222222
Entry(nil, inventory.NewSupportBundle()),
223223
Entry(nil, inventory.NewGitOps()),
224+
Entry(nil, inventory.NewChangeChannel()),
224225
)
225226

226227
})

e2e/inventory/inventory.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,16 @@ func NewGitOps() Test {
169169
}
170170
}
171171

172+
func NewChangeChannel() Test {
173+
return Test{
174+
ID: "change-channel",
175+
Name: "Change Channel",
176+
Namespace: "change-channel",
177+
AppSlug: "change-channel",
178+
UpstreamURI: "change-channel/automated",
179+
}
180+
}
181+
172182
func SetupRegressionTest(kubectlCLI *kubectl.CLI) TestimParams {
173183
cmd := kubectlCLI.Command(
174184
context.Background(),
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
apiVersion: kots.io/v1beta1
2+
kind: License
3+
metadata:
4+
name: github-action
5+
spec:
6+
appSlug: change-channel
7+
channelID: 2k6j62KPRQLjO0tF9zZB6zJJukg
8+
channelName: Automated
9+
channels:
10+
- channelID: 2k6j62KPRQLjO0tF9zZB6zJJukg
11+
channelName: Automated
12+
channelSlug: automated
13+
endpoint: https://replicated.app
14+
isDefault: true
15+
customerName: github-action
16+
endpoint: https://replicated.app
17+
entitlements:
18+
expires_at:
19+
description: License Expiration
20+
signature: {}
21+
title: Expiration
22+
value: ""
23+
valueType: String
24+
isKotsInstallEnabled: true
25+
isNewKotsUiEnabled: true
26+
licenseID: 2k6jempcB6aQyddcIEBSTj0Bvvv
27+
licenseSequence: 16
28+
licenseType: trial
29+
signature: eyJsaWNlbnNlRGF0YSI6ImV5SmhjR2xXWlhKemFXOXVJam9pYTI5MGN5NXBieTkyTVdKbGRHRXhJaXdpYTJsdVpDSTZJa3hwWTJWdWMyVWlMQ0p0WlhSaFpHRjBZU0k2ZXlKdVlXMWxJam9pWjJsMGFIVmlMV0ZqZEdsdmJpSjlMQ0p6Y0dWaklqcDdJbXhwWTJWdWMyVkpSQ0k2SWpKck5tcGxiWEJqUWpaaFVYbGtaR05KUlVKVFZHb3dRbloyZGlJc0lteHBZMlZ1YzJWVWVYQmxJam9pZEhKcFlXd2lMQ0pqZFhOMGIyMWxjazVoYldVaU9pSm5hWFJvZFdJdFlXTjBhVzl1SWl3aVlYQndVMngxWnlJNkltTm9ZVzVuWlMxamFHRnVibVZzSWl3aVkyaGhibTVsYkVsRUlqb2lNbXMyYWpZeVMxQlNVVXhxVHpCMFJqbDZXa0kyZWtwS2RXdG5JaXdpWTJoaGJtNWxiRTVoYldVaU9pSkJkWFJ2YldGMFpXUWlMQ0pqYUdGdWJtVnNjeUk2VzNzaVkyaGhibTVsYkVsRUlqb2lNbXMyYWpZeVMxQlNVVXhxVHpCMFJqbDZXa0kyZWtwS2RXdG5JaXdpWTJoaGJtNWxiRk5zZFdjaU9pSmhkWFJ2YldGMFpXUWlMQ0pqYUdGdWJtVnNUbUZ0WlNJNklrRjFkRzl0WVhSbFpDSXNJbWx6UkdWbVlYVnNkQ0k2ZEhKMVpTd2laVzVrY0c5cGJuUWlPaUpvZEhSd2N6b3ZMM0psY0d4cFkyRjBaV1F1WVhCd0luMWRMQ0pzYVdObGJuTmxVMlZ4ZFdWdVkyVWlPakUyTENKbGJtUndiMmx1ZENJNkltaDBkSEJ6T2k4dmNtVndiR2xqWVhSbFpDNWhjSEFpTENKbGJuUnBkR3hsYldWdWRITWlPbnNpWlhod2FYSmxjMTloZENJNmV5SjBhWFJzWlNJNklrVjRjR2x5WVhScGIyNGlMQ0prWlhOamNtbHdkR2x2YmlJNklreHBZMlZ1YzJVZ1JYaHdhWEpoZEdsdmJpSXNJblpoYkhWbElqb2lJaXdpZG1Gc2RXVlVlWEJsSWpvaVUzUnlhVzVuSWl3aWMybG5ibUYwZFhKbElqcDdmWDE5TENKcGMwNWxkMHR2ZEhOVmFVVnVZV0pzWldRaU9uUnlkV1VzSW1selMyOTBjMGx1YzNSaGJHeEZibUZpYkdWa0lqcDBjblZsZlgwPSIsImlubmVyU2lnbmF0dXJlIjoiZXlKc2FXTmxibk5sVTJsbmJtRjBkWEpsSWpvaWRsRmpjWGt6T1hRellYQnRabWhTVFdoR01HdE5jblp2YlhST1pGZDBaR3Q1UW1zNVZWbHBaM2MzT0ZKMU56Z3Jaa0V5ZW1aVVRGTXJNV1JMUkRCcVNWcG5SbkZhYkRCRlJVaHJWbmRWUld0bE5IWk9RV2QyY1VsdWJrcDJXVGxNWWpKNGVUTk1NMDFQY1VjMk1GZERZV1l3VGxkUVpWTm9jbTVHYldSSmVXcG1SRFpOTUhsWVVUWndZVkpaZVU0MWFsaEdPVUZVY0dWVU5VSXpWVnBYVkdwb1R6QTJPVk5GYVdkUGEyeExjRlYzU3pZclRGbENPV2xEVWk5bWMyNXdkbU5vV0ROYU1HSjVSSGhtZEZnMFF6Rk1XWEF4VVc5MWRITjZNMkl2TVZZMWRXOUdMemd6YlZGUVRsQXplVEZrT1hadVFYazFlV1VyWVd4cE1HTm1RalZuYmpoTFYybERkRTFsVHk5aGNHRnFZbTlVTmpoTVdGUXZRbWhTUnpaQlJVTTBaWFp4Vkd4aVNYTXhZVTFOWW0wM2VIcGFlVFo2Y2twNVFWRXJjVzA1TmxWVVpGUjRiekJ4VUdKRFJrVndWSGhyYzJKQlBUMGlMQ0p3ZFdKc2FXTkxaWGtpT2lJdExTMHRMVUpGUjBsT0lGQlZRa3hKUXlCTFJWa3RMUzB0TFZ4dVRVbEpRa2xxUVU1Q1oydHhhR3RwUnpsM01FSkJVVVZHUVVGUFEwRlJPRUZOU1VsQ1EyZExRMEZSUlVFeFFWRmFRa2xEZW5wemRXTnVXV1JNVlM5YU1GeHVNSGsxTVc5U1EzaGxNVzFRWTBvNVUxbzRORlJqZUVScFVVZFNTRzluV2pZMVQyWklWV3g1V21Sb09FeEpOSFpYZFRKRGJVVnpkbVppV1VOS05VWlRTRnh1UlhSeVkzWnhURTlUWTJVemNsaHZhR2RNVmpkNVRtaFZNa3cxUm1vMWFuRXhkelU0WjBWbGVHTXJZbTUxVVdvME16TXJOR2s0VkZWTk1tNUZVVzFKY1Z4dU1tZHNOa1JxUWk5Sk1YWnNVMjQxTmtvdk1IVmhSWHBaVjFBck4ySkhlbTEzT0dNeWQycEJhRTFyUkZJck5USlBRM2hHY1dGaFExbDBaemROTDBWbE5seHVaWFIzWVRGNFREUlNSMlJCSzNkTFYzaDRWa1VyYjAxMlluQjROMXA1WWxJdldERjBRazlvZWtaSVdISjBaa2RCYm5CWE1sQkRjbVJ4Um1oMWVsWlZMMXh1TUhWSVRuVk1URkV5ZVVaemNFVjJUa1V4YlVKRFpqZHpUR2hNY205M1RVRlBWRnAxWkZJNGFtMXJTMnhJWlROT2MzUnlUekl2ZVVadWN6bHZWRnBoTWx4dWJYZEpSRUZSUVVKY2JpMHRMUzB0UlU1RUlGQlZRa3hKUXlCTFJWa3RMUzB0TFZ4dUlpd2lhMlY1VTJsbmJtRjBkWEpsSWpvaVpYbEtlbUZYWkhWWldGSXhZMjFWYVU5cFNsQmhhbVJ4VWtaT2MwMHljSEJhVjFKMVlsZDRiV05GTVhsVFJXUnRUVzVCTldJd1VUUmFSMFY0WTBka2VrOVhUbFpoVjJSRllXdHNTbEpXYUhwbGF6VlBWakZPVkU1SVJrMWhhWFJMVlhwc2JXSXhWa1pTTUhoNFUzazROV0l6VGxKV2JtUXhVbTVXTldNeFVsSmllWFJEWWpGQk0wMUhVbHBqYkc4d1lUSTViRk13UlhoaFdHUmFZbnBhV0dGVWJFcFphMk15U3pCUmQxTkhjRWRrYlhnelVtMVZNbUpUZEV0a2JsSlBZak5yTldSR1ZuRlRXRkpUVmpGSmVtRnJNVmRWUkZwUllsVTRkMWRYVWt4UFNFMTZWMWRhVldOdVRrOWlibWgxV2pGUk1WWlZUVEZhVkVKM1UxVTVWMXBYVGpOVlZrSkhWa2hqZGxSc1dsQmlSVnBxWVRCS05rOUlRbHBsVjBaTVpESlZNbE5YZEhWT1EzUndVbXBDVTJSVVVsQk9NalZDWVRBNGNsRnNhR3BUYlZZeldYcGtRMVZVUms5bFJXUmhVV3hGTkU1VmR6VmtSM2N4WTBaa2NsTkZUbmRMTWxadFVqSkpNV1J1UmpKU1NFcHRWVlZhZEdNelJrWlhVemxIV1dwYVVWZFliSE5OYlU1RVZucHNSVmx1YUhWWFIzUnJZbFpqTVU5VVdsWlhiVkV6VDFadmNsZFlVbmRNTVdSdFpWZEtlVlpHYnpOaVIxSnpaRzVzYjJNd05XaGpTRlpYWTFST1VrNXNiSFZhTVdoMFRXNXdOVnBIWXpsUVUwbHpTVzFrYzJJeVNtaGlSWFJzWlZWc2EwbHFiMmxaYlZKc1dsUlZNazVVV1hkWk1scHBUa1JPYWs5WFNYbFBSMHB0VDFSb2JGbFhUbWhhYlVVeVRrUlphV1pSUFQwaWZRPT0ifQ==
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { test, expect } from '@playwright/test';
2+
import { login, uploadLicense } from '../shared';
3+
4+
const CUSTOMER_ID = '2k6jemHbYgZFqtwgyjqiVfjRQqi';
5+
const APP_ID = '2k6j65t0STtrZ1emyP5PUqBIQ23';
6+
const AUTOMATED_CHANNEL_ID = '2k6j62KPRQLjO0tF9zZB6zJJukg';
7+
const ALTERNATE_CHANNEL_ID = '2k6j61j49IPyDyQlbmRZJsxy3TP';
8+
9+
test('change channel', async ({ page }) => {
10+
test.slow();
11+
await changeChannel(AUTOMATED_CHANNEL_ID);
12+
await login(page);
13+
await uploadLicense(page, expect);
14+
await page.getByRole('button', { name: 'Deploy' }).click();
15+
await expect(page.locator('#app')).toContainText('Automated');
16+
await changeChannel(ALTERNATE_CHANNEL_ID);
17+
18+
await page.getByText('Sync license').click();
19+
20+
await expect(page.getByLabel('Next step')).toContainText('License synced', { timeout: 10000 });
21+
await page.getByRole('button', { name: 'Ok, got it!' }).click();
22+
23+
await expect(page.locator('#app')).toContainText('Alternate');
24+
await expect(page.locator('#app')).toContainText('1.0.3', { timeout: 10000 });
25+
await expect(page.locator('#app')).toContainText('Upstream Update', { timeout: 10000 });
26+
27+
await page.getByRole('button', { name: 'Deploy', exact: true }).click();
28+
await page.getByRole('button', { name: 'Yes, Deploy' }).click();
29+
30+
await expect(page.locator('#app')).toContainText('Currently deployed version', { timeout: 15000 });
31+
await expect(page.getByText('v1.0.0')).not.toBeVisible();
32+
await expect(page.getByText('1.0.3')).toBeVisible();
33+
});
34+
35+
async function changeChannel(channelId: string) {
36+
await fetch(`https://api.replicated.com/vendor/v3/customer/${CUSTOMER_ID}`, {
37+
method: 'PUT',
38+
headers: {
39+
'Content-Type': 'application/json',
40+
'Authorization': process.env.REPLICATED_API_TOKEN,
41+
},
42+
body: JSON.stringify({
43+
"app_id": APP_ID,
44+
"name": "github-action", // customer name
45+
"channels": [
46+
{
47+
"channel_id": channelId,
48+
"pinned_channel_sequence": null,
49+
"is_default_for_customer": true
50+
}
51+
]
52+
})
53+
}).then(response => {
54+
if (!response.ok) {
55+
throw new Error(`Unexpected status code: ${response.status}`);
56+
}
57+
});
58+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ require (
4949
github.com/pkg/errors v0.9.1
5050
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
5151
github.com/replicatedhq/embedded-cluster-kinds v1.4.7
52-
github.com/replicatedhq/kotskinds v0.0.0-20240621084729-1eb1e3eac6f2
52+
github.com/replicatedhq/kotskinds v0.0.0-20240718194123-1018dd404e95
5353
github.com/replicatedhq/kurlkinds v1.5.0
5454
github.com/replicatedhq/troubleshoot v0.95.1
5555
github.com/replicatedhq/yaml/v3 v3.0.0-beta5-replicatedhq

0 commit comments

Comments
 (0)