Skip to content

Commit b12a789

Browse files
authored
preserve license on automated airgap installs (#5275)
1 parent 0aead8d commit b12a789

File tree

6 files changed

+59
-17
lines changed

6 files changed

+59
-17
lines changed

cmd/kots/cli/install.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func InstallCmd() *cobra.Command {
108108
}
109109
}
110110

111-
license, err := getLicense(v)
111+
license, licenseData, err := getLicense(v)
112112
if err != nil {
113113
return errors.Wrap(err, "failed to get license")
114114
}
@@ -306,6 +306,7 @@ func InstallCmd() *cobra.Command {
306306
ApplicationMetadata: applicationMetadata.Manifest,
307307
UpstreamURI: upstream,
308308
License: license,
309+
LicenseData: licenseData,
309310
ConfigValues: configValues,
310311
Airgap: isAirgap,
311312
ProgressWriter: os.Stdout,
@@ -843,17 +844,22 @@ func getRegistryConfig(v *viper.Viper, clientset kubernetes.Interface, appSlug s
843844
}, nil
844845
}
845846

846-
func getLicense(v *viper.Viper) (*kotsv1beta1.License, error) {
847+
func getLicense(v *viper.Viper) (*kotsv1beta1.License, string, error) {
847848
if v.GetString("license-file") == "" {
848-
return nil, nil
849+
return nil, "", nil
849850
}
850851

851-
license, err := kotsutil.LoadLicenseFromPath(ExpandDir(v.GetString("license-file")))
852+
licenseData, err := os.ReadFile(ExpandDir(v.GetString("license-file")))
852853
if err != nil {
853-
return nil, errors.Wrap(err, "failed to parse license file")
854+
return nil, "", errors.Wrap(err, "failed to read license file")
854855
}
855856

856-
return license, nil
857+
license, err := kotsutil.LoadLicenseFromBytes(licenseData)
858+
if err != nil {
859+
return nil, "", errors.Wrap(err, "failed to parse license file")
860+
}
861+
862+
return license, string(licenseData), nil
857863
}
858864

859865
func getHttpProxyEnv(v *viper.Viper) map[string]string {

cmd/kots/cli/pull.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func PullCmd() *cobra.Command {
4444
}
4545
}
4646

47-
license, err := getLicense(v)
47+
license, _, err := getLicense(v)
4848
if err != nil {
4949
return errors.Wrap(err, "failed to get license")
5050
}

pkg/kotsadm/license.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,7 @@ func ensureLicenseSecret(deployOptions *types.DeployOptions, clientset *kubernet
4343
return false, nil
4444
}
4545

46-
s := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme, scheme.Scheme)
47-
var b bytes.Buffer
48-
if err := s.Encode(deployOptions.License, &b); err != nil {
49-
return false, errors.Wrap(err, "failed to encode license")
50-
}
51-
52-
_, err = clientset.CoreV1().Secrets(deployOptions.Namespace).Create(context.TODO(), kotsadmobjects.LicenseSecret(deployOptions.Namespace, deployOptions.License.Spec.AppSlug, deployOptions.Airgap, b.String()), metav1.CreateOptions{})
46+
_, err = clientset.CoreV1().Secrets(deployOptions.Namespace).Create(context.TODO(), kotsadmobjects.LicenseSecret(deployOptions.Namespace, deployOptions.License.Spec.AppSlug, deployOptions.Airgap, deployOptions.LicenseData), metav1.CreateOptions{})
5347
if err != nil {
5448
return false, errors.Wrap(err, "failed to create license secret")
5549
}

pkg/kotsadm/types/deployoptions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type DeployOptions struct {
2525
LimitRange *corev1.LimitRange
2626
IsOpenShift bool
2727
License *kotsv1beta1.License
28+
LicenseData string
2829
ConfigValues *kotsv1beta1.ConfigValues
2930
AppVersionLabel string
3031
Airgap bool

pkg/license/signature.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,6 @@ func verifyLicenseData(outerLicense *kotsv1beta1.License, innerLicense *kotsv1be
164164
if outerLicense.Spec.IsSupportBundleUploadSupported != innerLicense.Spec.IsSupportBundleUploadSupported {
165165
return fmt.Errorf("\"IsSupportBundleUploadSupported\" field has changed to %t (license) from %t (within signature)", outerLicense.Spec.IsSupportBundleUploadSupported, innerLicense.Spec.IsSupportBundleUploadSupported)
166166
}
167-
if outerLicense.Spec.IsEmbeddedClusterMultiNodeEnabled != innerLicense.Spec.IsEmbeddedClusterMultiNodeEnabled {
168-
return fmt.Errorf("\"IsEmbeddedClusterMultiNodeEnabled\" field has changed to %t (license) from %t (within signature)", outerLicense.Spec.IsEmbeddedClusterMultiNodeEnabled, innerLicense.Spec.IsEmbeddedClusterMultiNodeEnabled)
169-
}
170167
if outerLicense.Spec.IsSemverRequired != innerLicense.Spec.IsSemverRequired {
171168
return fmt.Errorf("\"IsSemverRequired\" field has changed to %t (license) from %t (within signature)", outerLicense.Spec.IsSemverRequired, innerLicense.Spec.IsSemverRequired)
172169
}

pkg/license/signature_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,50 @@ spec:
226226
wantErr: true,
227227
wantErrMsg: `"endpoint" field has changed to "https://replicated.app.modified" (license) from "https://replicated.app" (within signature)`,
228228
},
229+
{
230+
name: "isEmbeddedClusterMultiNodeEnabled field changed",
231+
license: `apiVersion: kots.io/v1beta1
232+
kind: License
233+
metadata:
234+
creationTimestamp: null
235+
name: salahecdev
236+
spec:
237+
appSlug: embedded-cluster-smoke-test-staging-app
238+
channelID: 2lq8qMex7BwLxz6W2Yww1oa37bL
239+
channelName: Salah
240+
channels:
241+
- channelID: 2lq8qMex7BwLxz6W2Yww1oa37bL
242+
channelName: Salah
243+
channelSlug: salah
244+
endpoint: https://ec-e2e-replicated-app.testcluster.net
245+
isDefault: true
246+
replicatedProxyDomain: ec-e2e-proxy.testcluster.net
247+
customerEmail: [email protected]
248+
customerName: Salah EC Dev
249+
endpoint: https://ec-e2e-replicated-app.testcluster.net
250+
entitlements:
251+
expires_at:
252+
description: License Expiration
253+
title: Expiration
254+
value: ""
255+
valueType: String
256+
isAirgapSupported: true
257+
isDisasterRecoverySupported: true
258+
isEmbeddedClusterDownloadEnabled: true
259+
isSnapshotSupported: true
260+
isSupportBundleUploadSupported: true
261+
licenseID: 2lhxc32VuCTVO2LrhLo44iDaIjl
262+
licenseSequence: 21
263+
licenseType: dev
264+
replicatedProxyDomain: ec-e2e-proxy.testcluster.net
265+
signature: eyJsaWNlbnNlRGF0YSI6ImV5SmhjR2xXWlhKemFXOXVJam9pYTI5MGN5NXBieTkyTVdKbGRHRXhJaXdpYTJsdVpDSTZJa3hwWTJWdWMyVWlMQ0p0WlhSaFpHRjBZU0k2ZXlKdVlXMWxJam9pYzJGc1lXaGxZMlJsZGlKOUxDSnpjR1ZqSWpwN0lteHBZMlZ1YzJWSlJDSTZJakpzYUhoak16SldkVU5VVms4eVRISm9URzgwTkdsRVlVbHFiQ0lzSW14cFkyVnVjMlZVZVhCbElqb2laR1YySWl3aVkzVnpkRzl0WlhKT1lXMWxJam9pVTJGc1lXZ2dSVU1nUkdWMklpd2lZM1Z6ZEc5dFpYSkZiV0ZwYkNJNkluTmhiR0ZvUUhKbGNHeHBZMkYwWldRdVkyOXRJaXdpWVhCd1UyeDFaeUk2SW1WdFltVmtaR1ZrTFdOc2RYTjBaWEl0YzIxdmEyVXRkR1Z6ZEMxemRHRm5hVzVuTFdGd2NDSXNJbU5vWVc1dVpXeEpSQ0k2SWpKc2NUaHhUV1Y0TjBKM1RIaDZObGN5V1hkM01XOWhNemRpVENJc0ltTm9ZVzV1Wld4T1lXMWxJam9pVTJGc1lXZ2lMQ0pqYUdGdWJtVnNjeUk2VzNzaVkyaGhibTVsYkVsRUlqb2lNbXh4T0hGTlpYZzNRbmRNZUhvMlZ6SlpkM2N4YjJFek4ySk1JaXdpWTJoaGJtNWxiRk5zZFdjaU9pSnpZV3hoYUNJc0ltTm9ZVzV1Wld4T1lXMWxJam9pVTJGc1lXZ2lMQ0pwYzBSbFptRjFiSFFpT25SeWRXVXNJbVZ1WkhCdmFXNTBJam9pYUhSMGNITTZMeTlsWXkxbE1tVXRjbVZ3YkdsallYUmxaQzFoY0hBdWRHVnpkR05zZFhOMFpYSXVibVYwSWl3aWNtVndiR2xqWVhSbFpGQnliM2g1Ukc5dFlXbHVJam9pWldNdFpUSmxMWEJ5YjNoNUxuUmxjM1JqYkhWemRHVnlMbTVsZENKOVhTd2liR2xqWlc1elpWTmxjWFZsYm1ObElqb3lNU3dpWlc1a2NHOXBiblFpT2lKb2RIUndjem92TDJWakxXVXlaUzF5WlhCc2FXTmhkR1ZrTFdGd2NDNTBaWE4wWTJ4MWMzUmxjaTV1WlhRaUxDSnlaWEJzYVdOaGRHVmtVSEp2ZUhsRWIyMWhhVzRpT2lKbFl5MWxNbVV0Y0hKdmVIa3VkR1Z6ZEdOc2RYTjBaWEl1Ym1WMElpd2laVzUwYVhSc1pXMWxiblJ6SWpwN0ltVjRjR2x5WlhOZllYUWlPbnNpZEdsMGJHVWlPaUpGZUhCcGNtRjBhVzl1SWl3aVpHVnpZM0pwY0hScGIyNGlPaUpNYVdObGJuTmxJRVY0Y0dseVlYUnBiMjRpTENKMllXeDFaU0k2SWlJc0luWmhiSFZsVkhsd1pTSTZJbE4wY21sdVp5SXNJbk5wWjI1aGRIVnlaU0k2ZTMxOWZTd2lhWE5CYVhKbllYQlRkWEJ3YjNKMFpXUWlPblJ5ZFdVc0ltbHpVMjVoY0hOb2IzUlRkWEJ3YjNKMFpXUWlPblJ5ZFdVc0ltbHpSR2x6WVhOMFpYSlNaV052ZG1WeWVWTjFjSEJ2Y25SbFpDSTZkSEoxWlN3aWFYTk9aWGRMYjNSelZXbEZibUZpYkdWa0lqcDBjblZsTENKcGMxTjFjSEJ2Y25SQ2RXNWtiR1ZWY0d4dllXUlRkWEJ3YjNKMFpXUWlPblJ5ZFdVc0ltbHpSVzFpWldSa1pXUkRiSFZ6ZEdWeVJHOTNibXh2WVdSRmJtRmliR1ZrSWpwMGNuVmxMQ0pwYzBWdFltVmtaR1ZrUTJ4MWMzUmxjazExYkhScFRtOWtaVVZ1WVdKc1pXUWlPblJ5ZFdVc0ltbHpTMjkwYzBsdWMzUmhiR3hGYm1GaWJHVmtJanAwY25WbGZYMD0iLCJpbm5lclNpZ25hdHVyZSI6ImV5SnNhV05sYm5ObFUybG5ibUYwZFhKbElqb2liekk0ZEdwVFlrdENjeTh4VW5sd2RFSjRjV3RRVFV4T1QwZFdWSE5wTVVKNlMxTkZjVEZsVmxJeWIwSXJRM2N4VjFaTFEzTnNUamxDYmpOU1RuZHdla1ZZYUhsS1JUTnNSSE5KUmpocU5FRjVRVTU2YkZaT1ZYSnhlVFJEVlZwMk5GTkxTV1JNUlZSTmJIbDRVbmRNVnpScVpHaDVhelp1UVZSU1FXdHZNVlE1ZVdkd1RVWXhWak5IZVVoTE1rMXVRaXR5Y2toWk0yUlJZV1l2TTFGaGRscElZbkZOVmtoelJGUXJaVXQ1VlRCVFdEaDVXbEZ6YmtWWFUyTXlUek55UkRKbVUzcHVjR3hzVVhsUFFVRjFaWFl5UWk5NGRsRTFObEJEY0V4TVZIVk1jM0UwYXpsc1J6QkNNazlEWVdkS1RrOHhRVVpqYVhOeGFtaG9lVGc1WmlzeFdFTmhjSE5rYWxSbU4zQldZVGM0Y1RKdFVGQmhTREp2ZUVkUWFFeHpXbXBSZG1oRlkxRXhhRlF5TUZKTU5DOUxkREIxZWtrM1ZVOTFkMFl2V0hoQ1pVaGlVRFZLT0U1eFdFUlJVRmRuVnl0aU1WVkJQVDBpTENKd2RXSnNhV05MWlhraU9pSXRMUzB0TFVKRlIwbE9JRkJWUWt4SlF5QkxSVmt0TFMwdExWeHVUVWxKUWtscVFVNUNaMnR4YUd0cFJ6bDNNRUpCVVVWR1FVRlBRMEZST0VGTlNVbENRMmRMUTBGUlJVRTBWSFZGVTBaMVZXSkxXVTh4V0hGVWRWVTNVMXh1YkUwMVZXRXZkV0ZITTNjeGJHRjJXVW94Vms5cE0yWkNaMUpTUm1Fd04wazFUMGxxZGtwU1NWRlVkMlExUnk5Vk5tOXJhbEpoVFZneE5tWTRZemgwYUZ4dWRXUlJhVEU0YWtZNFRtWmxkVXhFY0c1bE5WSmhTa05zTlc5WmEwOURRVmhuZEdKSmRVaHdSa1I0UzBjM1FUVmtNWFpXUkcxUWFubGtaVUp6U0haMlExeHVaWGgzY1VGS2VFZGtNamxOU0hOQllVTldUWHBsVW5SV09VVktkMFZ1TDJSdE9VVnpZMGhpUnpnMllscGpZWGxFWmpOblRYSXhNamN3T1RGUFRrOVdSVnh1T1ZWR1pVWldZV3hoTW5GbE5URmlNRGszTmtka1kzUnNSWEoyZG1OUVRWcHdTbVFyZWpKVGRtTnhVMlJLY0U5WlFqRXphbVJXYW5OTmRtRkJTVzh4VDF4dU4xcEZVMFZwVWtGNk5VaGFjSHAyV1c0emNpOXhhMHg2U0dScFRVTk9SeXRtZW1SbGR6ZGxaVUp0WTNrcmExQlZXRmd6WkdGeU0yRmFWeXR0WmtOU09WeHViRkZKUkVGUlFVSmNiaTB0TFMwdFJVNUVJRkJWUWt4SlF5QkxSVmt0TFMwdExWeHVJaXdpYTJWNVUybG5ibUYwZFhKbElqb2laWGxLZW1GWFpIVlpXRkl4WTIxVmFVOXBTbEJWYkhCd1ZGWldjVmt4VG1GUk1qaDNXWHByTUdKRE9UVlBSM2hPVjFkb1JXRXpXWGRrYmsxNldtMXdVbU5GV2pKTmJteFJaVmhaZGxZeVRrZGxSbGx5VWxad1dGbHBPVWhTYlhnellraE5OV0pwT1hGWmEyd3daV3RHVUdNeWFHeFJNMmR5VTFWc2VWZElVbTlPTTFFd1lWaHNUMDlYU2xGYWFtaHdXWGwwTWxKVWJEVmhWRTUxVlVkTk1rNHlWWFpOTVVKcVVWVldWRkV3V1RKWmJIQjNWVWRHTW1KNldYcGtiVTE1WTBSa1VGWlhaelZYV0Zwb1RsTTVlbEV6UWxOUFZVWnpZM3BzZVZOcmFHNWlWRUY2VWtod2VGWlZhRFZpZW1oVFZESldlbUZWVWtkaVJYaHRWRlZHYldWc1RrWmFSVGxIVGpOR2VVMHpaRkppV0VGMlRtMU9NRTFYYUU5TE1XUnNVVlZhVWxkVE9VdFNWbWd5WkZodmNsVkVTWEpNTUdReFlqSldWMDVVUWxwUmJrNTVWREIzTTFaWFNrcFVWWFJXV2xaT1RGbFhUbFpoV0VGMlRURmFjazlIU25aaGVtaHNaV3hGTTB3eGFESlhibkJZVEROTmQxWllhSFJoTTBaTFZtcFZNRXg2WkZCTk1rWmFUMVJHUzFVeWJGSmthbVJzVkZaS1JWWkZOV3RqVlVaUFRVVXhTRlJXVWxaaFNHeHdVVmhzV2xNemNGbGpNbkJJV2pCTmVXVlhOWEZQUkZKMVRXeFZlRTF0ZEVaTk1XaHJURE5zYTFKWFl6bFFVMGx6U1cxa2MySXlTbWhpUlhSc1pWVnNhMGxxYjJsYVIxVjVXWHBKTTA1VVdURk9iVkYzVGtkSmVGbHRTWGRhYWtVeFdUSlpNMDFIV1hkYVYwVjVXVlJKYVdaUlBUMGlmUT09In0=
266+
`,
267+
// kots versions <= v1.124.15 do not preserve the license structure for automated airgap installs
268+
// which causes the license verification for isEmbeddedClusterMultiNodeEnabled to fail
269+
// in kots versions that have this license field.
270+
wantErr: false,
271+
wantErrMsg: "",
272+
},
229273
}
230274
for _, tt := range tests {
231275
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)