Skip to content

Commit cc92043

Browse files
authored
check if the license has expired before beginning installation process (#719)
* check if the license has expired before beginning installation process * require that licenses have embedded cluster download enabled before installation * check another error * do not change tar file
1 parent 34ca47c commit cc92043

File tree

4 files changed

+175
-10
lines changed

4 files changed

+175
-10
lines changed

cmd/embedded-cluster/install.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func isAlreadyInstalled() (bool, error) {
136136
}
137137
}
138138

139-
func checkLicenseMatches(c *cli.Context) error {
139+
func checkLicenseMatches(licenseFile string) error {
140140
rel, err := release.GetChannelRelease()
141141
if err != nil {
142142
return fmt.Errorf("failed to get release from binary: %w", err) // this should only be if the release is malformed
@@ -146,20 +146,20 @@ func checkLicenseMatches(c *cli.Context) error {
146146
// 1. no release and no license, which is OK
147147
// 2. no license and a release, which is not OK
148148
// 3. a license and no release, which is not OK
149-
if rel == nil && c.String("license") == "" {
149+
if rel == nil && licenseFile == "" {
150150
// no license and no release, this is OK
151151
return nil
152-
} else if rel == nil && c.String("license") != "" {
152+
} else if rel == nil && licenseFile != "" {
153153
// license is present but no release, this means we would install without vendor charts and k0s overrides
154154
return fmt.Errorf("a license was provided but no release was found in binary, please rerun without the license flag")
155-
} else if rel != nil && c.String("license") == "" {
155+
} else if rel != nil && licenseFile == "" {
156156
// release is present but no license, this is not OK
157157
return fmt.Errorf("no license was provided for %s and one is required, please rerun with '--license <path to license file>'", rel.AppSlug)
158158
}
159159

160-
license, err := helpers.ParseLicense(c.String("license"))
160+
license, err := helpers.ParseLicense(licenseFile)
161161
if err != nil {
162-
return fmt.Errorf("unable to parse the license file at %q, please ensure it is not corrupt: %w", c.String("license"), err)
162+
return fmt.Errorf("unable to parse the license file at %q, please ensure it is not corrupt: %w", licenseFile, err)
163163
}
164164

165165
// Check if the license matches the application version data
@@ -173,6 +173,21 @@ func checkLicenseMatches(c *cli.Context) error {
173173
return fmt.Errorf("license channel %s (%s) does not match binary channel %s, please provide the correct license", license.Spec.ChannelID, license.Spec.ChannelName, rel.ChannelID)
174174
}
175175

176+
if license.Spec.Entitlements["expires_at"].Value.StrVal != "" {
177+
// read the expiration date, and check it against the current date
178+
expiration, err := time.Parse(time.RFC3339, license.Spec.Entitlements["expires_at"].Value.StrVal)
179+
if err != nil {
180+
return fmt.Errorf("unable to parse expiration date: %w", err)
181+
}
182+
if time.Now().After(expiration) {
183+
return fmt.Errorf("license expired on %s, please provide a valid license", expiration)
184+
}
185+
}
186+
187+
if !license.Spec.IsEmbeddedClusterDownloadEnabled {
188+
return fmt.Errorf("license does not have embedded cluster enabled, please provide a valid license")
189+
}
190+
176191
return nil
177192
}
178193

@@ -479,7 +494,7 @@ var installCommand = &cli.Command{
479494
return fmt.Errorf("unable to configure network manager: %w", err)
480495
}
481496
logrus.Debugf("checking license matches")
482-
if err := checkLicenseMatches(c); err != nil {
497+
if err := checkLicenseMatches(c.String("license")); err != nil {
483498
metricErr := fmt.Errorf("unable to check license: %w", err)
484499
metrics.ReportApplyFinished(c, metricErr)
485500
return err // do not return the metricErr, as we want the user to see the error message without a prefix
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package main
2+
3+
import (
4+
"github.com/replicatedhq/embedded-cluster/pkg/release"
5+
"github.com/stretchr/testify/require"
6+
"os"
7+
"path/filepath"
8+
"testing"
9+
)
10+
11+
func Test_checkLicenseMatches(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
licenseContents string
15+
wantErr string
16+
useRelease bool
17+
}{
18+
{
19+
name: "no license, no release",
20+
wantErr: "",
21+
},
22+
{
23+
name: "no license, with release",
24+
useRelease: true,
25+
wantErr: `no license was provided for embedded-cluster-smoke-test-staging-app and one is required, please rerun with '--license <path to license file>'`,
26+
},
27+
{
28+
name: "valid license, no release",
29+
licenseContents: `
30+
spec:
31+
appSlug: embedded-cluster-smoke-test-staging-app
32+
channelID: "2cHXb1RCttzpR0xvnNWyaZCgDBP"
33+
isEmbeddedClusterDownloadEnabled: true
34+
`,
35+
wantErr: "a license was provided but no release was found in binary, please rerun without the license flag",
36+
},
37+
{
38+
name: "valid license, with release",
39+
useRelease: true,
40+
licenseContents: `
41+
spec:
42+
appSlug: embedded-cluster-smoke-test-staging-app
43+
channelID: "2cHXb1RCttzpR0xvnNWyaZCgDBP"
44+
isEmbeddedClusterDownloadEnabled: true
45+
`,
46+
},
47+
{
48+
name: "expired license, with release",
49+
useRelease: true,
50+
licenseContents: `
51+
spec:
52+
appSlug: embedded-cluster-smoke-test-staging-app
53+
channelID: "2cHXb1RCttzpR0xvnNWyaZCgDBP"
54+
isEmbeddedClusterDownloadEnabled: true
55+
entitlements:
56+
expires_at:
57+
description: License Expiration
58+
signature: {}
59+
title: Expiration
60+
value: "2024-06-03T00:00:00Z"
61+
valueType: String
62+
`,
63+
wantErr: "license expired on 2024-06-03 00:00:00 +0000 UTC, please provide a valid license",
64+
},
65+
{
66+
name: "license with no expiration, with release",
67+
useRelease: true,
68+
licenseContents: `
69+
spec:
70+
appSlug: embedded-cluster-smoke-test-staging-app
71+
channelID: "2cHXb1RCttzpR0xvnNWyaZCgDBP"
72+
isEmbeddedClusterDownloadEnabled: true
73+
entitlements:
74+
expires_at:
75+
description: License Expiration
76+
signature: {}
77+
title: Expiration
78+
value: ""
79+
valueType: String
80+
`,
81+
},
82+
{
83+
name: "license with 100 year expiration, with release",
84+
useRelease: true,
85+
licenseContents: `
86+
spec:
87+
appSlug: embedded-cluster-smoke-test-staging-app
88+
channelID: "2cHXb1RCttzpR0xvnNWyaZCgDBP"
89+
isEmbeddedClusterDownloadEnabled: true
90+
entitlements:
91+
expires_at:
92+
description: License Expiration
93+
signature: {}
94+
title: Expiration
95+
value: "2124-06-03T00:00:00Z"
96+
valueType: String
97+
`,
98+
},
99+
{
100+
name: "embedded cluster not enabled, with release",
101+
useRelease: true,
102+
licenseContents: `
103+
spec:
104+
appSlug: embedded-cluster-smoke-test-staging-app
105+
channelID: "2cHXb1RCttzpR0xvnNWyaZCgDBP"
106+
isEmbeddedClusterDownloadEnabled: false
107+
`,
108+
wantErr: "license does not have embedded cluster enabled, please provide a valid license",
109+
},
110+
}
111+
for _, tt := range tests {
112+
t.Run(tt.name, func(t *testing.T) {
113+
req := require.New(t)
114+
115+
tmpdir, err := os.MkdirTemp("", "license")
116+
defer os.RemoveAll(tmpdir)
117+
req.NoError(err)
118+
119+
licenseFile, err := os.Create(tmpdir + "/license.yaml")
120+
req.NoError(err)
121+
_, err = licenseFile.Write([]byte(tt.licenseContents))
122+
req.NoError(err)
123+
124+
dataMap := map[string][]byte{}
125+
if tt.useRelease {
126+
dataMap["release.yaml"] = []byte(`
127+
# channel release object
128+
channelID: "2cHXb1RCttzpR0xvnNWyaZCgDBP"
129+
channelSlug: "CI"
130+
appSlug: "embedded-cluster-smoke-test-staging-app"
131+
versionLabel: testversion
132+
`)
133+
}
134+
err = release.SetReleaseDataForTests(dataMap)
135+
req.NoError(err)
136+
137+
if tt.licenseContents != "" {
138+
err = checkLicenseMatches(filepath.Join(tmpdir, "license.yaml"))
139+
} else {
140+
err = checkLicenseMatches("")
141+
}
142+
143+
if tt.wantErr != "" {
144+
req.EqualError(err, tt.wantErr)
145+
} else {
146+
req.NoError(err)
147+
}
148+
})
149+
}
150+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ require (
1717
github.com/replicatedhq/embedded-cluster-kinds v1.3.9
1818
github.com/replicatedhq/embedded-cluster-operator v0.36.0
1919
github.com/replicatedhq/embedded-cluster-utils v1.0.0
20-
github.com/replicatedhq/kotskinds v0.0.0-20240523174825-f4d441adb453
20+
github.com/replicatedhq/kotskinds v0.0.0-20240621115447-55148ce032e4
2121
github.com/replicatedhq/troubleshoot v0.93.1
2222
github.com/sirupsen/logrus v1.9.3
2323
github.com/stretchr/testify v1.9.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ github.com/replicatedhq/embedded-cluster-operator v0.36.0 h1:bVfprWclxojJx3bj+86
249249
github.com/replicatedhq/embedded-cluster-operator v0.36.0/go.mod h1:ARYlOmWODaibFtX04e4x5eeymYJmhLzpVC7hJfBt2Es=
250250
github.com/replicatedhq/embedded-cluster-utils v1.0.0 h1:Axdni1nYfl5zeOP9g5U79yvN8cRdClyU6hz0wV1Hmdc=
251251
github.com/replicatedhq/embedded-cluster-utils v1.0.0/go.mod h1:4JmMC2CwMCLxq05GEW3XSPPVotqyamAF/omrbB3pH+c=
252-
github.com/replicatedhq/kotskinds v0.0.0-20240523174825-f4d441adb453 h1:g8CQQ9R4gjIdoHuBX1LN1hmF3Omq2JfA040JfpfNVC8=
253-
github.com/replicatedhq/kotskinds v0.0.0-20240523174825-f4d441adb453/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
252+
github.com/replicatedhq/kotskinds v0.0.0-20240621115447-55148ce032e4 h1:nsNSod6wpFpaMUq1IqnU4y2XWQd2FEKtdr02UB2udkk=
253+
github.com/replicatedhq/kotskinds v0.0.0-20240621115447-55148ce032e4/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
254254
github.com/replicatedhq/troubleshoot v0.93.1 h1:JWdyk8TH//PUaDpUGhlcQIH4YVIWXoDbee6aOMTfOOk=
255255
github.com/replicatedhq/troubleshoot v0.93.1/go.mod h1:2GQAYOwWL0YMNkc5WOkA54/6AicoHwoSxyzhkzSmFM4=
256256
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=

0 commit comments

Comments
 (0)