diff --git a/go.mod b/go.mod index 4b7b5a8a..c8dfb7a4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/replicatedhq/replicated-sdk -go 1.24.0 +go 1.24.6 require ( github.com/blang/semver v3.5.1+incompatible @@ -11,7 +11,7 @@ require ( github.com/pact-foundation/pact-go v1.10.0 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - github.com/replicatedhq/kotskinds v0.0.0-20230724164735-f83482cc9cfe + github.com/replicatedhq/kotskinds v0.0.0-20251029124314-174e89c93554 github.com/robfig/cron/v3 v3.0.1 github.com/spf13/cobra v1.10.1 github.com/spf13/pflag v1.0.10 @@ -127,7 +127,7 @@ require ( golang.org/x/time v0.12.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/grpc v1.72.1 // indirect - google.golang.org/protobuf v1.36.5 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index f9fcf698..b18e95c4 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= -github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -324,8 +324,8 @@ github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= -github.com/replicatedhq/kotskinds v0.0.0-20230724164735-f83482cc9cfe h1:3AJInd06UxzqHmgy8+24CPsT2tYSE0zToJZyuX9q+MA= -github.com/replicatedhq/kotskinds v0.0.0-20230724164735-f83482cc9cfe/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I= +github.com/replicatedhq/kotskinds v0.0.0-20251029124314-174e89c93554 h1:a9vLewcXgVC/vclEak7CV0gsSYhYinjnWDoUkzrqN4w= +github.com/replicatedhq/kotskinds v0.0.0-20251029124314-174e89c93554/go.mod h1:+k4PHo2wukoU9kdiKrqqgi89Wmj+9AiwppYGVK11zig= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -550,8 +550,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/pact/custom_metrics_test.go b/pact/custom_metrics_test.go index ce27517b..57f4efa1 100644 --- a/pact/custom_metrics_test.go +++ b/pact/custom_metrics_test.go @@ -14,6 +14,7 @@ import ( "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" "github.com/replicatedhq/replicated-sdk/pkg/handlers" "github.com/replicatedhq/replicated-sdk/pkg/k8sutil" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/store" "github.com/replicatedhq/replicated-sdk/pkg/util" "k8s.io/client-go/kubernetes/fake" @@ -75,7 +76,7 @@ func TestSendCustomAppMetrics(t *testing.T) { pactInteraction() storeOptions := store.InitInMemoryStoreOptions{ - License: license, + License: licensewrapper.LicenseWrapper{V1: license}, LicenseFields: nil, ReplicatedAppEndpoint: license.Spec.Endpoint, ChannelID: license.Spec.ChannelID, diff --git a/pact/instance_test.go b/pact/instance_test.go index 8864b135..06e0a115 100644 --- a/pact/instance_test.go +++ b/pact/instance_test.go @@ -11,6 +11,7 @@ import ( "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types" "github.com/replicatedhq/replicated-sdk/pkg/k8sutil" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/report" mock_store "github.com/replicatedhq/replicated-sdk/pkg/store/mock" "github.com/replicatedhq/replicated-sdk/pkg/util" @@ -39,12 +40,12 @@ func TestSendInstanceData(t *testing.T) { { name: "successful instance data request", mockStoreExpectations: func() { - mockStore.EXPECT().GetLicense().Return(&v1beta1.License{ + mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "replicated-sdk-instance-customer-0-license", Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port), }, - }) + }}) mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace") mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id") mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app") @@ -91,12 +92,12 @@ func TestSendInstanceData(t *testing.T) { { name: "expired license should return error", mockStoreExpectations: func() { - mockStore.EXPECT().GetLicense().Return(&v1beta1.License{ + mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "replicated-sdk-instance-customer-2-license", Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port), }, - }) + }}) mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace") mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id") mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app") @@ -143,12 +144,12 @@ func TestSendInstanceData(t *testing.T) { { name: "nonexistent license should return error", mockStoreExpectations: func() { - mockStore.EXPECT().GetLicense().Return(&v1beta1.License{ + mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "replicated-sdk-instance-customer-nonexistent-license", Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port), }, - }) + }}) mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace") mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id") mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app") @@ -195,12 +196,12 @@ func TestSendInstanceData(t *testing.T) { { name: "unauthenticated instance data request should return error", mockStoreExpectations: func() { - mockStore.EXPECT().GetLicense().Return(&v1beta1.License{ + mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "replicated-sdk-instance-customer-0-license", Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port), }, - }) + }}) mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace") mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id") mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app") diff --git a/pact/license_test.go b/pact/license_test.go index 057c1ff8..cf4baf21 100644 --- a/pact/license_test.go +++ b/pact/license_test.go @@ -9,6 +9,7 @@ import ( "github.com/pact-foundation/pact-go/dsl" "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/license" ) @@ -48,7 +49,7 @@ func TestGetLatestLicense(t *testing.T) { // } type args struct { - license *v1beta1.License + license licensewrapper.LicenseWrapper endpoint string } tests := []struct { @@ -96,13 +97,13 @@ func TestGetLatestLicense(t *testing.T) { { name: "no license found", args: args{ - license: &v1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "not-a-customer-license", AppSlug: "replicated-sdk-license-app", Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port), }, - }, + }}, }, pactInteraction: func() { pact. @@ -126,13 +127,13 @@ func TestGetLatestLicense(t *testing.T) { { name: "no app found", args: args{ - license: &v1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "replicated-sdk-license-customer-0-license", AppSlug: "not-an-app", Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port), }, - }, + }}, }, pactInteraction: func() { pact. @@ -156,13 +157,13 @@ func TestGetLatestLicense(t *testing.T) { { name: "license is not for this app", args: args{ - license: &v1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "replicated-sdk-license-customer-0-license", AppSlug: "replicated-sdk-instance-app", Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port), }, - }, + }}, }, pactInteraction: func() { pact. @@ -187,13 +188,13 @@ func TestGetLatestLicense(t *testing.T) { { name: "license is archived", args: args{ - license: &v1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "replicated-sdk-license-customer-archived-license", AppSlug: "replicated-sdk-license-app", Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port), }, - }, + }}, }, pactInteraction: func() { pact. diff --git a/pkg/apiserver/bootstrap.go b/pkg/apiserver/bootstrap.go index 804ff011..eec4a119 100644 --- a/pkg/apiserver/bootstrap.go +++ b/pkg/apiserver/bootstrap.go @@ -5,7 +5,7 @@ import ( "github.com/cenkalti/backoff/v4" "github.com/pkg/errors" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/appstate" appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types" "github.com/replicatedhq/replicated-sdk/pkg/heartbeat" @@ -56,40 +56,40 @@ func bootstrap(params APIServerParams) error { } } - var unverifiedLicense *kotsv1beta1.License + var unverifiedWrapper licensewrapper.LicenseWrapper if len(params.LicenseBytes) > 0 { - l, err := sdklicense.LoadLicenseFromBytes(params.LicenseBytes) + wrapper, err := sdklicense.LoadLicenseFromBytes(params.LicenseBytes) if err != nil { return errors.Wrap(err, "failed to parse license from base64") } - unverifiedLicense = l + unverifiedWrapper = wrapper } else if params.IntegrationLicenseID != "" { - l, err := sdklicense.GetLicenseByID(params.IntegrationLicenseID, params.ReplicatedAppEndpoint) + wrapper, err := sdklicense.GetLicenseByID(params.IntegrationLicenseID, params.ReplicatedAppEndpoint) if err != nil { return backoff.Permanent(errors.Wrap(err, "failed to get license by id for integration license id")) } - if l.Spec.LicenseType != "dev" { + if wrapper.GetLicenseType() != "dev" { return errors.New("integration license must be a dev license") } - unverifiedLicense = l + unverifiedWrapper = wrapper } - verifiedLicense, err := sdklicense.VerifySignature(unverifiedLicense) + verifiedWrapper, err := sdklicense.VerifySignature(unverifiedWrapper) if err != nil { return backoff.Permanent(errors.Wrap(err, "failed to verify license signature")) } if !util.IsAirgap() { // sync license - licenseData, err := sdklicense.GetLatestLicense(verifiedLicense, params.ReplicatedAppEndpoint) + licenseData, err := sdklicense.GetLatestLicense(verifiedWrapper, params.ReplicatedAppEndpoint) if err != nil { return errors.Wrap(err, "failed to get latest license") } - verifiedLicense = licenseData.License + verifiedWrapper = licenseData.License } // check license expiration - expired, err := sdklicense.LicenseIsExpired(verifiedLicense) + expired, err := sdklicense.LicenseIsExpired(verifiedWrapper) if err != nil { return errors.Wrap(err, "failed to check if license is expired") } @@ -99,16 +99,16 @@ func bootstrap(params APIServerParams) error { channelID := params.ChannelID if channelID == "" { - channelID = verifiedLicense.Spec.ChannelID + channelID = verifiedWrapper.GetChannelID() } channelName := params.ChannelName if channelName == "" { - channelName = verifiedLicense.Spec.ChannelName + channelName = verifiedWrapper.GetChannelName() } store.InitInMemory(store.InitInMemoryStoreOptions{ - License: verifiedLicense, + License: verifiedWrapper, LicenseFields: params.LicenseFields, AppName: params.AppName, ChannelID: channelID, diff --git a/pkg/handlers/license.go b/pkg/handlers/license.go index 8ba27c55..5bc53f7b 100644 --- a/pkg/handlers/license.go +++ b/pkg/handlers/license.go @@ -7,6 +7,8 @@ import ( "github.com/gorilla/mux" "github.com/pkg/errors" kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + kotsv1beta2 "github.com/replicatedhq/kotskinds/apis/kots/v1beta2" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" sdklicense "github.com/replicatedhq/replicated-sdk/pkg/license" sdklicensetypes "github.com/replicatedhq/replicated-sdk/pkg/license/types" "github.com/replicatedhq/replicated-sdk/pkg/logger" @@ -15,41 +17,41 @@ import ( ) type LicenseInfo struct { - LicenseID string `json:"licenseID"` - AppSlug string `json:"appSlug"` - ChannelName string `json:"channelName"` - CustomerName string `json:"customerName"` - CustomerEmail string `json:"customerEmail"` - LicenseType string `json:"licenseType"` - ChannelID string `json:"channelID"` - LicenseSequence int64 `json:"licenseSequence"` - IsAirgapSupported bool `json:"isAirgapSupported"` - IsGitOpsSupported bool `json:"isGitOpsSupported"` - IsIdentityServiceSupported bool `json:"isIdentityServiceSupported"` - IsGeoaxisSupported bool `json:"isGeoaxisSupported"` - IsSnapshotSupported bool `json:"isSnapshotSupported"` - IsSupportBundleUploadSupported bool `json:"isSupportBundleUploadSupported"` - IsSemverRequired bool `json:"isSemverRequired"` - Endpoint string `json:"endpoint"` - Entitlements map[string]kotsv1beta1.EntitlementField `json:"entitlements"` + LicenseID string `json:"licenseID"` + AppSlug string `json:"appSlug"` + ChannelName string `json:"channelName"` + CustomerName string `json:"customerName"` + CustomerEmail string `json:"customerEmail"` + LicenseType string `json:"licenseType"` + ChannelID string `json:"channelID"` + LicenseSequence int64 `json:"licenseSequence"` + IsAirgapSupported bool `json:"isAirgapSupported"` + IsGitOpsSupported bool `json:"isGitOpsSupported"` + IsIdentityServiceSupported bool `json:"isIdentityServiceSupported"` + IsGeoaxisSupported bool `json:"isGeoaxisSupported"` + IsSnapshotSupported bool `json:"isSnapshotSupported"` + IsSupportBundleUploadSupported bool `json:"isSupportBundleUploadSupported"` + IsSemverRequired bool `json:"isSemverRequired"` + Endpoint string `json:"endpoint"` + Entitlements interface{} `json:"entitlements,omitempty"` } func GetLicenseInfo(w http.ResponseWriter, r *http.Request) { - license := store.GetStore().GetLicense() + wrapper := store.GetStore().GetLicense() if !util.IsAirgap() { - l, err := sdklicense.GetLatestLicense(license, store.GetStore().GetReplicatedAppEndpoint()) + l, err := sdklicense.GetLatestLicense(wrapper, store.GetStore().GetReplicatedAppEndpoint()) if err != nil { logger.Error(errors.Wrap(err, "failed to get latest license")) - JSONCached(w, http.StatusOK, licenseInfoFromLicense(license)) + JSONCached(w, http.StatusOK, licenseInfoFromWrapper(wrapper)) return } - license = l.License - store.GetStore().SetLicense(license) + wrapper = l.License + store.GetStore().SetLicense(wrapper) } - JSON(w, http.StatusOK, licenseInfoFromLicense(license)) + JSON(w, http.StatusOK, licenseInfoFromWrapper(wrapper)) } func GetLicenseFields(w http.ResponseWriter, r *http.Request) { @@ -107,24 +109,48 @@ func GetLicenseField(w http.ResponseWriter, r *http.Request) { JSON(w, http.StatusOK, licenseFields[fieldName]) } -func licenseInfoFromLicense(license *kotsv1beta1.License) LicenseInfo { +func licenseInfoFromWrapper(wrapper licensewrapper.LicenseWrapper) LicenseInfo { + // Convert EntitlementFieldWrapper map to version-specific EntitlementField map + // Return v1 format for v1 licenses, v2 format for v2 licenses + var entitlements interface{} + wrappedEntitlements := wrapper.GetEntitlements() + if wrappedEntitlements != nil { + if wrapper.IsV1() { + v1Entitlements := make(map[string]kotsv1beta1.EntitlementField, len(wrappedEntitlements)) + for key, wrapped := range wrappedEntitlements { + if wrapped.V1 != nil { + v1Entitlements[key] = *wrapped.V1 + } + } + entitlements = v1Entitlements + } else if wrapper.IsV2() { + v2Entitlements := make(map[string]kotsv1beta2.EntitlementField, len(wrappedEntitlements)) + for key, wrapped := range wrappedEntitlements { + if wrapped.V2 != nil { + v2Entitlements[key] = *wrapped.V2 + } + } + entitlements = v2Entitlements + } + } + return LicenseInfo{ - LicenseID: license.Spec.LicenseID, - AppSlug: license.Spec.AppSlug, - ChannelName: license.Spec.ChannelName, - CustomerName: license.Spec.CustomerName, - CustomerEmail: license.Spec.CustomerEmail, - LicenseType: license.Spec.LicenseType, - ChannelID: license.Spec.ChannelID, - LicenseSequence: license.Spec.LicenseSequence, - IsAirgapSupported: license.Spec.IsAirgapSupported, - IsGitOpsSupported: license.Spec.IsGitOpsSupported, - IsIdentityServiceSupported: license.Spec.IsIdentityServiceSupported, - IsGeoaxisSupported: license.Spec.IsGeoaxisSupported, - IsSnapshotSupported: license.Spec.IsSnapshotSupported, - IsSupportBundleUploadSupported: license.Spec.IsSupportBundleUploadSupported, - IsSemverRequired: license.Spec.IsSemverRequired, - Endpoint: license.Spec.Endpoint, - Entitlements: license.Spec.Entitlements, + LicenseID: wrapper.GetLicenseID(), + AppSlug: wrapper.GetAppSlug(), + ChannelName: wrapper.GetChannelName(), + CustomerName: wrapper.GetCustomerName(), + CustomerEmail: wrapper.GetCustomerEmail(), + LicenseType: wrapper.GetLicenseType(), + ChannelID: wrapper.GetChannelID(), + LicenseSequence: wrapper.GetLicenseSequence(), + IsAirgapSupported: wrapper.IsAirgapSupported(), + IsGitOpsSupported: wrapper.IsGitOpsSupported(), + IsIdentityServiceSupported: wrapper.IsIdentityServiceSupported(), + IsGeoaxisSupported: wrapper.IsGeoaxisSupported(), + IsSnapshotSupported: wrapper.IsSnapshotSupported(), + IsSupportBundleUploadSupported: wrapper.IsSupportBundleUploadSupported(), + IsSemverRequired: wrapper.IsSemverRequired(), + Endpoint: wrapper.GetEndpoint(), + Entitlements: entitlements, } } diff --git a/pkg/handlers/middleware.go b/pkg/handlers/middleware.go index 246d11bb..702e5c3c 100644 --- a/pkg/handlers/middleware.go +++ b/pkg/handlers/middleware.go @@ -46,7 +46,7 @@ func RequireValidLicenseIDMiddleware(next http.Handler) http.Handler { return } - if store.GetStore().GetLicense().Spec.LicenseID != licenseID { + if store.GetStore().GetLicense().GetLicenseID() != licenseID { response := types.ErrorResponse{Error: "license ID is not valid"} JSON(w, http.StatusUnauthorized, response) return diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index c81b6267..26aec184 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -6,7 +6,7 @@ import ( "sync" "github.com/pkg/errors" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/util" kuberneteserrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -20,8 +20,8 @@ const ( var replicatedSecretLock = sync.Mutex{} -func IsEnabled(ctx context.Context, clientset kubernetes.Interface, namespace string, license *kotsv1beta1.License) (bool, error) { - if license == nil || license.Spec.LicenseType != "dev" { +func IsEnabled(ctx context.Context, clientset kubernetes.Interface, namespace string, wrapper licensewrapper.LicenseWrapper) (bool, error) { + if (wrapper.V1 == nil && wrapper.V2 == nil) || wrapper.GetLicenseType() != "dev" { return false, nil } diff --git a/pkg/integration/integration_test.go b/pkg/integration/integration_test.go index 5d94a719..275353d3 100644 --- a/pkg/integration/integration_test.go +++ b/pkg/integration/integration_test.go @@ -5,6 +5,7 @@ import ( "testing" kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/util" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -16,7 +17,7 @@ func TestIntegration_IsEnabled(t *testing.T) { type args struct { clientset kubernetes.Interface namespace string - license *kotsv1beta1.License + license licensewrapper.LicenseWrapper } tests := []struct { name string @@ -51,11 +52,11 @@ func TestIntegration_IsEnabled(t *testing.T) { }}, }), namespace: "default", - license: &kotsv1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &kotsv1beta1.License{ Spec: kotsv1beta1.LicenseSpec{ LicenseType: "dev", }, - }, + }}, }, want: true, wantErr: false, @@ -78,11 +79,11 @@ func TestIntegration_IsEnabled(t *testing.T) { }}, }), namespace: "default", - license: &kotsv1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &kotsv1beta1.License{ Spec: kotsv1beta1.LicenseSpec{ LicenseType: "paid", }, - }, + }}, }, want: false, wantErr: false, @@ -105,11 +106,11 @@ func TestIntegration_IsEnabled(t *testing.T) { }}, }), namespace: "default", - license: &kotsv1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &kotsv1beta1.License{ Spec: kotsv1beta1.LicenseSpec{ LicenseType: "dev", }, - }, + }}, }, want: false, wantErr: false, @@ -130,11 +131,11 @@ func TestIntegration_IsEnabled(t *testing.T) { }}, }), namespace: "default", - license: &kotsv1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &kotsv1beta1.License{ Spec: kotsv1beta1.LicenseSpec{ LicenseType: "dev", }, - }, + }}, }, want: true, wantErr: false, @@ -157,11 +158,11 @@ func TestIntegration_IsEnabled(t *testing.T) { }}, }), namespace: "default", - license: &kotsv1beta1.License{ + license: licensewrapper.LicenseWrapper{V1: &kotsv1beta1.License{ Spec: kotsv1beta1.LicenseSpec{ LicenseType: "dev", }, - }, + }}, }, want: true, wantErr: false, diff --git a/pkg/license/license.go b/pkg/license/license.go index c906c20e..332e2d4e 100644 --- a/pkg/license/license.go +++ b/pkg/license/license.go @@ -7,7 +7,7 @@ import ( "time" "github.com/pkg/errors" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/license/types" "github.com/replicatedhq/replicated-sdk/pkg/report" "github.com/replicatedhq/replicated-sdk/pkg/store" @@ -20,10 +20,10 @@ const ( type LicenseData struct { LicenseBytes []byte - License *kotsv1beta1.License + License licensewrapper.LicenseWrapper } -func GetLicenseByID(licenseID string, endpoint string) (*kotsv1beta1.License, error) { +func GetLicenseByID(licenseID string, endpoint string) (licensewrapper.LicenseWrapper, error) { if endpoint == "" { endpoint = defaultReplicatedAppEndpoint } @@ -31,19 +31,19 @@ func GetLicenseByID(licenseID string, endpoint string) (*kotsv1beta1.License, er licenseData, err := getLicenseFromAPI(url, licenseID) if err != nil { - return nil, errors.Wrap(err, "failed to get license from api") + return licensewrapper.LicenseWrapper{}, errors.Wrap(err, "failed to get license from api") } return licenseData.License, nil } -func GetLatestLicense(license *kotsv1beta1.License, endpoint string) (*LicenseData, error) { +func GetLatestLicense(wrapper licensewrapper.LicenseWrapper, endpoint string) (*LicenseData, error) { if endpoint == "" { - endpoint = license.Spec.Endpoint + endpoint = wrapper.GetEndpoint() } - url := fmt.Sprintf("%s/license/%s", endpoint, license.Spec.AppSlug) + url := fmt.Sprintf("%s/license/%s", endpoint, wrapper.GetAppSlug()) - licenseData, err := getLicenseFromAPI(url, license.Spec.LicenseID) + licenseData, err := getLicenseFromAPI(url, wrapper.GetLicenseID()) if err != nil { return nil, errors.Wrap(err, "failed to get license from api") } @@ -89,28 +89,37 @@ func getLicenseFromAPI(url string, licenseID string) (*LicenseData, error) { return data, nil } -func LicenseIsExpired(license *kotsv1beta1.License) (bool, error) { - val, found := license.Spec.Entitlements["expires_at"] +func LicenseIsExpired(wrapper licensewrapper.LicenseWrapper) (bool, error) { + entitlements := wrapper.GetEntitlements() + if entitlements == nil { + return false, nil + } + + ent, found := entitlements["expires_at"] if !found { return false, nil } - if val.ValueType != "" && val.ValueType != "String" { - return false, errors.Errorf("expires_at must be type String: %s", val.ValueType) + + valueType := ent.GetValueType() + if valueType != "" && valueType != "String" { + return false, errors.Errorf("expires_at must be type String: %s", valueType) } - if val.Value.StrVal == "" { + + expiresAtValue := ent.GetValue().StrVal + if expiresAtValue == "" { return false, nil } - partsed, err := time.Parse(time.RFC3339, val.Value.StrVal) + parsed, err := time.Parse(time.RFC3339, expiresAtValue) if err != nil { return false, errors.Wrap(err, "failed to parse expiration time") } - return partsed.Before(time.Now()), nil + return parsed.Before(time.Now()), nil } -func GetLatestLicenseFields(license *kotsv1beta1.License, endpoint string) (types.LicenseFields, error) { +func GetLatestLicenseFields(wrapper licensewrapper.LicenseWrapper, endpoint string) (types.LicenseFields, error) { if endpoint == "" { - endpoint = license.Spec.Endpoint + endpoint = wrapper.GetEndpoint() } url := fmt.Sprintf("%s/license/fields", endpoint) @@ -119,7 +128,7 @@ func GetLatestLicenseFields(license *kotsv1beta1.License, endpoint string) (type return nil, errors.Wrap(err, "failed to call newrequest") } - req.SetBasicAuth(license.Spec.LicenseID, license.Spec.LicenseID) + req.SetBasicAuth(wrapper.GetLicenseID(), wrapper.GetLicenseID()) instanceData := report.GetInstanceData(store.GetStore()) report.InjectInstanceDataHeaders(req, instanceData) @@ -147,9 +156,9 @@ func GetLatestLicenseFields(license *kotsv1beta1.License, endpoint string) (type return licenseFields, nil } -func GetLatestLicenseField(license *kotsv1beta1.License, endpoint string, fieldName string) (*types.LicenseField, error) { +func GetLatestLicenseField(wrapper licensewrapper.LicenseWrapper, endpoint string, fieldName string) (*types.LicenseField, error) { if endpoint == "" { - endpoint = license.Spec.Endpoint + endpoint = wrapper.GetEndpoint() } url := fmt.Sprintf("%s/license/field/%s", endpoint, fieldName) @@ -158,7 +167,7 @@ func GetLatestLicenseField(license *kotsv1beta1.License, endpoint string, fieldN return nil, errors.Wrap(err, "failed to call newrequest") } - req.SetBasicAuth(license.Spec.LicenseID, license.Spec.LicenseID) + req.SetBasicAuth(wrapper.GetLicenseID(), wrapper.GetLicenseID()) instanceData := report.GetInstanceData(store.GetStore()) report.InjectInstanceDataHeaders(req, instanceData) diff --git a/pkg/license/signature.go b/pkg/license/signature.go index e16e5da0..03acc0af 100644 --- a/pkg/license/signature.go +++ b/pkg/license/signature.go @@ -1,291 +1,41 @@ package license import ( - "crypto" - "crypto/rsa" - "crypto/x509" - "encoding/json" - "encoding/pem" - "fmt" - "github.com/pkg/errors" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" -) - -var ( - ErrSignatureInvalid = errors.New("signature is invalid") - ErrSignatureMissing = errors.New("signature is missing") + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" ) -type InnerSignature struct { - LicenseSignature []byte `json:"licenseSignature"` - PublicKey string `json:"publicKey"` - KeySignature []byte `json:"keySignature"` -} - -type OuterSignature struct { - LicenseData []byte `json:"licenseData"` - InnerSignature []byte `json:"innerSignature"` -} - -type KeySignature struct { - Signature []byte `json:"signature"` - GlobalKeyId string `json:"globalKeyId"` -} - -type LicenseDataError struct { - message string -} - -func (e LicenseDataError) Error() string { - return e.message -} - -func VerifySignature(license *kotsv1beta1.License) (*kotsv1beta1.License, error) { - outerSignature := &OuterSignature{} - if err := json.Unmarshal(license.Spec.Signature, outerSignature); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal license outer signature") - } - - isOldFormat := len(outerSignature.InnerSignature) == 0 - if isOldFormat { - return verifyOldSignature(license) - } - - innerSignature := &InnerSignature{} - if err := json.Unmarshal(outerSignature.InnerSignature, innerSignature); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal license inner signature") - } - - keySignature := &KeySignature{} - if err := json.Unmarshal(innerSignature.KeySignature, keySignature); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal key signature") - } - - globalKeyPEM, ok := PublicKeys[keySignature.GlobalKeyId] - if !ok { - return nil, errors.New("unknown global key") - } - - // verify that the app public key is properly signed with a replicated private key - if err := Verify([]byte(innerSignature.PublicKey), keySignature.Signature, globalKeyPEM); err != nil { - return nil, errors.Wrap(err, "failed to verify key signature") - } - // verify that the license data is properly signed with the app private key - if err := Verify(outerSignature.LicenseData, innerSignature.LicenseSignature, []byte(innerSignature.PublicKey)); err != nil { - return nil, errors.Wrap(err, "failed to verify license signature") - } - - verifiedLicense := &kotsv1beta1.License{} - if err := json.Unmarshal(outerSignature.LicenseData, verifiedLicense); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal license data") - } - - if err := verifyLicenseData(license, verifiedLicense); err != nil { - return nil, LicenseDataError{message: err.Error()} - } - - verifiedLicense.Spec.Signature = license.Spec.Signature - - return verifiedLicense, nil -} - -func Verify(message, signature, publicKeyPEM []byte) error { - pubBlock, _ := pem.Decode(publicKeyPEM) - publicKey, err := x509.ParsePKIXPublicKey(pubBlock.Bytes) - if err != nil { - return errors.Wrap(err, "failed to load public key from PEM") - } - - var opts rsa.PSSOptions - opts.SaltLength = rsa.PSSSaltLengthAuto - - newHash := crypto.MD5 - pssh := newHash.New() - pssh.Write(message) - hashed := pssh.Sum(nil) - - err = rsa.VerifyPSS(publicKey.(*rsa.PublicKey), newHash, hashed, signature, &opts) - if err != nil { - // this ordering makes errors.Cause a little more useful - return errors.Wrap(ErrSignatureInvalid, err.Error()) - } - - return nil -} - -func verifyLicenseData(outerLicense *kotsv1beta1.License, innerLicense *kotsv1beta1.License) error { - if outerLicense.Spec.AppSlug != innerLicense.Spec.AppSlug { - return errors.New("\"appSlug\" field has changed") - } - if outerLicense.Spec.Endpoint != innerLicense.Spec.Endpoint { - return errors.New("\"endpoint\" field has changed") - } - if outerLicense.Spec.ChannelID != innerLicense.Spec.ChannelID { - return errors.New("\"channelID\" field has changed") - } - if outerLicense.Spec.ChannelName != innerLicense.Spec.ChannelName { - return errors.New("\"channelName\" field has changed") - } - if outerLicense.Spec.LicenseSequence != innerLicense.Spec.LicenseSequence { - return errors.New("\"licenseSequence\" field has changed") - } - if outerLicense.Spec.LicenseID != innerLicense.Spec.LicenseID { - return errors.New("\"licenseID\" field has changed") - } - if outerLicense.Spec.LicenseType != innerLicense.Spec.LicenseType { - return errors.New("\"LicenseType\" field has changed") - } - if outerLicense.Spec.IsAirgapSupported != innerLicense.Spec.IsAirgapSupported { - return errors.New("\"IsAirgapSupported\" field has changed") - } - if outerLicense.Spec.IsGitOpsSupported != innerLicense.Spec.IsGitOpsSupported { - return errors.New("\"IsGitOpsSupported\" field has changed") - } - if outerLicense.Spec.IsIdentityServiceSupported != innerLicense.Spec.IsIdentityServiceSupported { - return errors.New("\"IsIdentityServiceSupported\" field has changed") - } - if outerLicense.Spec.IsGeoaxisSupported != innerLicense.Spec.IsGeoaxisSupported { - return errors.New("\"IsGeoaxisSupported\" field has changed") - } - if outerLicense.Spec.IsSnapshotSupported != innerLicense.Spec.IsSnapshotSupported { - return errors.New("\"IsSnapshotSupported\" field has changed") - } - if outerLicense.Spec.IsSupportBundleUploadSupported != innerLicense.Spec.IsSupportBundleUploadSupported { - return errors.New("\"IsSupportBundleUploadSupported\" field has changed") - } - if outerLicense.Spec.IsSemverRequired != innerLicense.Spec.IsSemverRequired { - return errors.New("\"IsSemverRequired\" field has changed") - } - - // Check entitlements - if len(outerLicense.Spec.Entitlements) != len(innerLicense.Spec.Entitlements) { - return errors.New("\"entitlements\" field has changed") - } - for k, outerEntitlement := range outerLicense.Spec.Entitlements { - innerEntitlement, ok := innerLicense.Spec.Entitlements[k] - if !ok { - return errors.New("entitlement not found in the inner license") - } - if outerEntitlement.Value.Value() != innerEntitlement.Value.Value() { - return errors.New("one or more of the entitlements values have changed") - } - if outerEntitlement.Title != innerEntitlement.Title { - return errors.New("one or more of the entitlements titles have changed") - } - if outerEntitlement.Description != innerEntitlement.Description { - return errors.New("one or more of the entitlements descriptions have changed") - } - if outerEntitlement.IsHidden != innerEntitlement.IsHidden { - return errors.New("one or more of the entitlements hidden flags have changed") - } - if outerEntitlement.ValueType != innerEntitlement.ValueType { - return errors.New("one or more of the entitlements value types have changed") +// VerifySignature verifies a license wrapper using the appropriate algorithm +func VerifySignature(wrapper licensewrapper.LicenseWrapper) (licensewrapper.LicenseWrapper, error) { + if wrapper.V1 != nil { + // Use kotskinds built-in validation for v1beta1 licenses + _, err := wrapper.V1.ValidateLicense() + if err != nil { + return licensewrapper.LicenseWrapper{}, err } + // ValidateLicense() verifies all signatures and field integrity + // Return the original wrapper since the license is already verified + return wrapper, nil } - return nil -} - -func verifyOldSignature(license *kotsv1beta1.License) (*kotsv1beta1.License, error) { - signature := &InnerSignature{} - if err := json.Unmarshal(license.Spec.Signature, signature); err != nil { - // old licenses's signature is a single space character - if len(license.Spec.Signature) == 0 || len(license.Spec.Signature) == 1 { - return nil, ErrSignatureMissing + if wrapper.V2 != nil { + // Use kotskinds built-in validation for v1beta2 licenses + _, err := wrapper.V2.ValidateLicense() + if err != nil { + return licensewrapper.LicenseWrapper{}, err } - return nil, errors.Wrap(err, "failed to unmarshal license signature") - } - - keySignature := &KeySignature{} - if err := json.Unmarshal(signature.KeySignature, keySignature); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal key signature") - } - - globalKeyPEM, ok := PublicKeys[keySignature.GlobalKeyId] - if !ok { - return nil, errors.New("unknown global key") + // ValidateLicense() verifies all signatures and field integrity + // Return the original wrapper since the license is already verified + return wrapper, nil } - if err := Verify([]byte(signature.PublicKey), keySignature.Signature, globalKeyPEM); err != nil { - return nil, errors.Wrap(err, "failed to verify key signature") - } - - licenseMessage, err := getMessageFromLicense(license) - if err != nil { - return nil, errors.Wrap(err, "failed to convert license to message") - } - - if err := Verify(licenseMessage, signature.LicenseSignature, []byte(signature.PublicKey)); err != nil { - return nil, errors.Wrap(err, "failed to verify license signature") - } - - return license, nil + return licensewrapper.LicenseWrapper{}, errors.New("license wrapper is empty") } -func getMessageFromLicense(license *kotsv1beta1.License) ([]byte, error) { - // JSON marshaller will sort map keys automatically. - fields := map[string]string{ - "apiVersion": license.APIVersion, - "kind": license.Kind, - "metadata.name": license.GetObjectMeta().GetName(), - "spec.licenseID": license.Spec.LicenseID, - "spec.appSlug": license.Spec.AppSlug, - "spec.channelName": license.Spec.ChannelName, - "spec.endpoint": license.Spec.Endpoint, - "spec.isAirgapSupported": fmt.Sprintf("%t", license.Spec.IsAirgapSupported), - } - if license.Spec.LicenseSequence > 0 { - fields["spec.licenseSequence"] = fmt.Sprintf("%d", license.Spec.LicenseSequence) - } - for k, v := range license.Spec.Entitlements { - key := fmt.Sprintf("spec.entitlements.%s", k) - val := map[string]string{ - "title": v.Title, - "description": v.Description, - "value": fmt.Sprintf("%v", v.Value.Value()), - } - valStr, err := json.Marshal(val) - if err != nil { - return nil, errors.Wrapf(err, "failed to marshal entitlement value: %s", k) - } - fields[key] = string(valStr) - } - message, err := json.Marshal(fields) - if err != nil { - return nil, errors.Wrap(err, "failed to marshal message JSON") - } - - return message, err -} -func GetAppPublicKey(license *kotsv1beta1.License) ([]byte, error) { - // old licenses's signature is a single space character - if len(license.Spec.Signature) == 0 || len(license.Spec.Signature) == 1 { - return nil, ErrSignatureMissing - } - - innerSignature := &InnerSignature{} - outerSignature := &OuterSignature{} - if err := json.Unmarshal(license.Spec.Signature, outerSignature); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal license outer signature") - } - isOldFormat := len(outerSignature.InnerSignature) == 0 - if isOldFormat { - if err := json.Unmarshal(license.Spec.Signature, innerSignature); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal license signature") - } - } else { - if err := json.Unmarshal(outerSignature.InnerSignature, innerSignature); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal license inner signature") - } - } - - return []byte(innerSignature.PublicKey), nil -} diff --git a/pkg/license/signature_test.go b/pkg/license/signature_test.go index 66c767a2..3737bb30 100644 --- a/pkg/license/signature_test.go +++ b/pkg/license/signature_test.go @@ -117,7 +117,7 @@ spec: signature: eyJsaWNlbnNlRGF0YSI6ImV5SmhjR2xXWlhKemFXOXVJam9pYTI5MGN5NXBieTkyTVdKbGRHRXhJaXdpYTJsdVpDSTZJa3hwWTJWdWMyVWlMQ0p0WlhSaFpHRjBZU0k2ZXlKdVlXMWxJam9pZEdWemRHTjFjM1J2YldWeUluMHNJbk53WldNaU9uc2liR2xqWlc1elpVbEVJam9pTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54SWl3aWJHbGpaVzV6WlZSNWNHVWlPaUp3Y205a0lpd2lZM1Z6ZEc5dFpYSk9ZVzFsSWpvaVZHVnpkQ0JEZFhOMGIyMWxjaUlzSW1Gd2NGTnNkV2NpT2lKdGVTMWhjSEF0Ylc5a2FXWnBaV1FpTENKamFHRnVibVZzU1VRaU9pSXhkblZ6U1ZsYVRFRldlRTFITm5FM05qQlBTbTFTUzJvMWFUVWlMQ0pqYUdGdWJtVnNUbUZ0WlNJNklrMTVJRU5vWVc1dVpXd2lMQ0pzYVdObGJuTmxVMlZ4ZFdWdVkyVWlPamNzSW1WdVpIQnZhVzUwSWpvaWFIUjBjSE02THk5eVpYQnNhV05oZEdWa0xtRndjQzV0YjJScFptbGxaQ0lzSW1WdWRHbDBiR1Z0Wlc1MGN5STZleUppYjI5c1gyWnBaV3hrSWpwN0luUnBkR3hsSWpvaVFtOXZiQ0JHYVdWc1pDSXNJblpoYkhWbElqcDBjblZsTENKMllXeDFaVlI1Y0dVaU9pSkNiMjlzWldGdUluMHNJbVY0Y0dseVpYTmZZWFFpT25zaWRHbDBiR1VpT2lKRmVIQnBjbUYwYVc5dUlpd2laR1Z6WTNKcGNIUnBiMjRpT2lKTWFXTmxibk5sSUVWNGNHbHlZWFJwYjI0aUxDSjJZV3gxWlNJNklqSXdNekF0TURjdE1qZFVNREE2TURBNk1EQmFJaXdpZG1Gc2RXVlVlWEJsSWpvaVUzUnlhVzVuSW4wc0ltaHBaR1JsYmw5bWFXVnNaQ0k2ZXlKMGFYUnNaU0k2SWtocFpHUmxiaUJHYVdWc1pDSXNJblpoYkhWbElqb2lkR2hwY3lCcGN5QnpaV055WlhRaUxDSjJZV3gxWlZSNWNHVWlPaUpUZEhKcGJtY2lMQ0pwYzBocFpHUmxiaUk2ZEhKMVpYMHNJbWx1ZEY5bWFXVnNaQ0k2ZXlKMGFYUnNaU0k2SWtsdWRDQkdhV1ZzWkNJc0luWmhiSFZsSWpveE1qTXNJblpoYkhWbFZIbHdaU0k2SWtsdWRHVm5aWElpZlN3aWMzUnlhVzVuWDJacFpXeGtJanA3SW5ScGRHeGxJam9pVTNSeWFXNW5SbWxsYkdRaUxDSjJZV3gxWlNJNkluTnBibWRzWlNCc2FXNWxJSFJsZUhRaUxDSjJZV3gxWlZSNWNHVWlPaUpUZEhKcGJtY2lmU3dpZEdWNGRGOW1hV1ZzWkNJNmV5SjBhWFJzWlNJNklsUmxlSFFnUm1sbGJHUWlMQ0oyWVd4MVpTSTZJbTExYkhScFhHNXNhVzVsWEc1MFpYaDBJaXdpZG1Gc2RXVlVlWEJsSWpvaVZHVjRkQ0o5ZlN3aWFYTkJhWEpuWVhCVGRYQndiM0owWldRaU9uUnlkV1VzSW1selIybDBUM0J6VTNWd2NHOXlkR1ZrSWpwMGNuVmxMQ0pwYzFOdVlYQnphRzkwVTNWd2NHOXlkR1ZrSWpwMGNuVmxmWDA9IiwiaW5uZXJTaWduYXR1cmUiOiJleUpzYVdObGJuTmxVMmxuYm1GMGRYSmxJam9pYUhneE1XTXZUR1ozUTNoVE5YRmtRWEJGU1hGdVRrMU9NMHBLYTJzNFZHZFhSVVpzVDFKVlJ6UjJjR1YzZEZoV1YzbG1lamRZY0hBd1ExazJZamRyUVRSS2N6TklhR3d3YkZJMFdUQTFMemN2UVVkQ2FEZFZNSGczUkhaTVozUXpVM00wYm5GTFZTdFhXRXBTVHpKWVFVRnZSME4xZFRWR1RGcHJRVWhYY1RSUVFtMXphSFY2Y1ZsdmNucHhlbGhGWVZWVlpFUlVkVXhDTW1nNWFIZ3dXRWhQUmxwUk16bHVkbTlPUjJaT2R5OTRTVmRaZEhSUGRYZHZhMncyTVZsb1JVeFZlRmQxU1ZSRmMwTlVhM2xtTVRNd09IazVSbFJzWlRKeVYyZEVlSEZNYTBSUFNXVXlPRWwzUzJSQkwySXdWVUl5VEZGbVRWcHdWemwyUTNCSkwybHlWek5uYmpaeU5WWjNWMjB2U1dweWJtNDNSelJrVmpadVYzcFRkMGhQUTJSdWEwMTRNRXQ1VVVOa0wxQjFaWEpUYjNSdVEwOXRTMDEzWlRSTGJqaERkMU5YVVRRNGRURkRNbTFpV1VzeGRYTlpOM1YzUFQwaUxDSndkV0pzYVdOTFpYa2lPaUl0TFMwdExVSkZSMGxPSUZCVlFreEpReUJMUlZrdExTMHRMVnh1VFVsSlFrbHFRVTVDWjJ0eGFHdHBSemwzTUVKQlVVVkdRVUZQUTBGUk9FRk5TVWxDUTJkTFEwRlJSVUZ6TkhKdlVIcDFhV1JNZVhOMmIxWTJkemxhTkZ4dVdHRmliME5tWTJNeGFHZFZhQ3N3V1VkS2NFNURSVXhyTjBaTFF5OTJhemR6ZERsR05tY3dUMjlrU0VSbGVYZFJXa2hLZFU1TVpsUnNRbEJHUTJOaU5seHVObTlzVEZOeWNGQTRjbFUzU0d4SGJsRkVSMFJNYVhkS1EyaGtSRGRVVUdSM2FXdHBkMHRGY201aldqaEdaalZsU25vd2RETmlUWFpyVDJaVVluSkJiRnh1WWtGQ1kwbzVNVmxVT1hKdVVXOXFkVWN4UldKUVRqaEZWblI2TWxZNE5IZHViR2Q0TUhCd2JEVjRPSFpOYlhwcE1ISnVibEZVV1VGamJ6WnFhMnBJTTF4dVRuTlVkWE4xUzFkdlJGUjVNWE5yZGtSUk9IbEJZV0ptWTNNME4zWnNRazAwU0RGT1JFNHZSSFJhWWxZdllubDJia0o2YkM4eFZrVnpURmRqWlZWcFRGeHVSWEYxT0VkeWF5dFFVRGQyUkdSd2JFUjNjWFpQV2t4RmRYazNkamhuUm01U09WUlVSV3ByTlVvNWRuWlVTR2RtU25VemVubEVPR2xLWTBSRE5YcHFPVnh1YjFGSlJFRlJRVUpjYmkwdExTMHRSVTVFSUZCVlFreEpReUJMUlZrdExTMHRMVnh1SWl3aWEyVjVVMmxuYm1GMGRYSmxJam9pWlhsS2VtRlhaSFZaV0ZJeFkyMVZhVTlwU2pCUldIQjJXVE5LVms1NmFGaFNSMlJzVVRKb2NtTklXa1ZVVlRsRldqQktXVTFGUmtaVFJFNUZVMGhLYkUxclRUTkxNSEJFVkROR2VGTnROVVJVVlRWVlltMDFiVnBGUm5sWldIQjZaRVJqTVZaSGFFeFBXRUpVVWtacmRrd3diek5aTUZaSlVteFdWRXd5T1VoV1JXeHNWa1ZPTUZSSE1WWlJNR04zVkd4R2JGa3pTblJUUm1zMFZVWk9hMVpWU2pCVU1WbDNZbXQwY0ZSclZuQmpia0poVFZjNWFtSldiSEZaYTNob1UyeHNWV0pGUmtWWGJVWnZWakZLVUZkcWJGSmhXRVp1V2xkb1EyRnVRak5TUjNNd1lWWkpOVTVXVmxkV1ZUVnlUMGhLYjFsVlRYbGhiVGcwVjBkYWVGbHFWbFppYlhoeFpFWkZkMDU1Y3pCaFZsSkpWRVpPTm1WRk1IcGxWWFJ2VFVaR1ZtRXdWVFJSVnpsSFVsaEtVRTFZUmxCU01WcFJVMVJDTmxsV2FIcFdWWEJ0WTBSU2JFMVVRazlPVjNSU1ZucFdUMU5XWTNaU1ZYUkZVMGhzYlU5VmJGaGtNMUl3WTFWc1lXTlhSakJTYTA1RVlVWmtjbUo2VmtSU00wSllUREkxUmsxWVl6SmxWM1JKVlZoQk1sVXhTbEppU0Zwd1VrVXdNRlpFVWt0VU1rWnNVVmQwYzFSV1VrMVVWV055V1RCYVRHSXpaRTlUVm05NVlraE9SR1JzVG5aUmFrWmFaVmRPVGxOVlNteGFiRXB1Wld0U2RVMHhSVGxRVTBselNXMWtjMkl5U21oaVJYUnNaVlZzYTBscWIybFpiVkpzV2xSVk1rNVVXWGRaTWxwcFRrUk9hazlYU1hsUFIwcHRUMVJvYkZsWFRtaGFiVVV5VGtSWmFXWlJQVDBpZlE9PSJ9 `, wantErr: true, - wantErrMsg: "failed to verify license signature: crypto/rsa: verification error: signature is invalid", + wantErrMsg: "v1 license signature verification failed: signature verification failed: crypto/rsa: verification error", }, { name: "licenseID field changed", @@ -170,7 +170,7 @@ spec: signature: eyJsaWNlbnNlRGF0YSI6ImV5SmhjR2xXWlhKemFXOXVJam9pYTI5MGN5NXBieTkyTVdKbGRHRXhJaXdpYTJsdVpDSTZJa3hwWTJWdWMyVWlMQ0p0WlhSaFpHRjBZU0k2ZXlKdVlXMWxJam9pZEdWemRHTjFjM1J2YldWeUluMHNJbk53WldNaU9uc2liR2xqWlc1elpVbEVJam9pTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54SWl3aWJHbGpaVzV6WlZSNWNHVWlPaUp3Y205a0lpd2lZM1Z6ZEc5dFpYSk9ZVzFsSWpvaVZHVnpkQ0JEZFhOMGIyMWxjaUlzSW1Gd2NGTnNkV2NpT2lKdGVTMWhjSEFpTENKamFHRnVibVZzU1VRaU9pSXhkblZ6U1ZsYVRFRldlRTFITm5FM05qQlBTbTFTUzJvMWFUVWlMQ0pqYUdGdWJtVnNUbUZ0WlNJNklrMTVJRU5vWVc1dVpXd2lMQ0pzYVdObGJuTmxVMlZ4ZFdWdVkyVWlPamNzSW1WdVpIQnZhVzUwSWpvaWFIUjBjSE02THk5eVpYQnNhV05oZEdWa0xtRndjQ0lzSW1WdWRHbDBiR1Z0Wlc1MGN5STZleUppYjI5c1gyWnBaV3hrSWpwN0luUnBkR3hsSWpvaVFtOXZiQ0JHYVdWc1pDSXNJblpoYkhWbElqcDBjblZsTENKMllXeDFaVlI1Y0dVaU9pSkNiMjlzWldGdUluMHNJbVY0Y0dseVpYTmZZWFFpT25zaWRHbDBiR1VpT2lKRmVIQnBjbUYwYVc5dUlpd2laR1Z6WTNKcGNIUnBiMjRpT2lKTWFXTmxibk5sSUVWNGNHbHlZWFJwYjI0aUxDSjJZV3gxWlNJNklqSXdNekF0TURjdE1qZFVNREE2TURBNk1EQmFJaXdpZG1Gc2RXVlVlWEJsSWpvaVUzUnlhVzVuSW4wc0ltaHBaR1JsYmw5bWFXVnNaQ0k2ZXlKMGFYUnNaU0k2SWtocFpHUmxiaUJHYVdWc1pDSXNJblpoYkhWbElqb2lkR2hwY3lCcGN5QnpaV055WlhRaUxDSjJZV3gxWlZSNWNHVWlPaUpUZEhKcGJtY2lMQ0pwYzBocFpHUmxiaUk2ZEhKMVpYMHNJbWx1ZEY5bWFXVnNaQ0k2ZXlKMGFYUnNaU0k2SWtsdWRDQkdhV1ZzWkNJc0luWmhiSFZsSWpveE1qTXNJblpoYkhWbFZIbHdaU0k2SWtsdWRHVm5aWElpZlN3aWMzUnlhVzVuWDJacFpXeGtJanA3SW5ScGRHeGxJam9pVTNSeWFXNW5SbWxsYkdRaUxDSjJZV3gxWlNJNkluTnBibWRzWlNCc2FXNWxJSFJsZUhRaUxDSjJZV3gxWlZSNWNHVWlPaUpUZEhKcGJtY2lmU3dpZEdWNGRGOW1hV1ZzWkNJNmV5SjBhWFJzWlNJNklsUmxlSFFnUm1sbGJHUWlMQ0oyWVd4MVpTSTZJbTExYkhScFhHNXNhVzVsWEc1MFpYaDBJaXdpZG1Gc2RXVlVlWEJsSWpvaVZHVjRkQ0o5ZlN3aWFYTkJhWEpuWVhCVGRYQndiM0owWldRaU9uUnlkV1VzSW1selIybDBUM0J6VTNWd2NHOXlkR1ZrSWpwMGNuVmxMQ0pwYzFOdVlYQnphRzkwVTNWd2NHOXlkR1ZrSWpwMGNuVmxmWDA9IiwiaW5uZXJTaWduYXR1cmUiOiJleUpzYVdObGJuTmxVMmxuYm1GMGRYSmxJam9pYUhneE1XTXZUR1ozUTNoVE5YRmtRWEJGU1hGdVRrMU9NMHBLYTJzNFZHZFhSVVpzVDFKVlJ6UjJjR1YzZEZoV1YzbG1lamRZY0hBd1ExazJZamRyUVRSS2N6TklhR3d3YkZJMFdUQTFMemN2UVVkQ2FEZFZNSGczUkhaTVozUXpVM00wYm5GTFZTdFhXRXBTVHpKWVFVRnZSME4xZFRWR1RGcHJRVWhYY1RSUVFtMXphSFY2Y1ZsdmNucHhlbGhGWVZWVlpFUlVkVXhDTW1nNWFIZ3dXRWhQUmxwUk16bHVkbTlPUjJaT2R5OTRTVmRaZEhSUGRYZHZhMncyTVZsb1JVeFZlRmQxU1ZSRmMwTlVhM2xtTVRNd09IazVSbFJzWlRKeVYyZEVlSEZNYTBSUFNXVXlPRWwzUzJSQkwySXdWVUl5VEZGbVRWcHdWemwyUTNCSkwybHlWek5uYmpaeU5WWjNWMjB2U1dweWJtNDNSelJrVmpadVYzcFRkMGhQUTJSdWEwMTRNRXQ1VVVOa0wxQjFaWEpUYjNSdVEwOXRTMDEzWlRSTGJqaERkMU5YVVRRNGRURkRNbTFpV1VzeGRYTlpOM1YzUFQwaUxDSndkV0pzYVdOTFpYa2lPaUl0TFMwdExVSkZSMGxPSUZCVlFreEpReUJMUlZrdExTMHRMVnh1VFVsSlFrbHFRVTVDWjJ0eGFHdHBSemwzTUVKQlVVVkdRVUZQUTBGUk9FRk5TVWxDUTJkTFEwRlJSVUZ6TkhKdlVIcDFhV1JNZVhOMmIxWTJkemxhTkZ4dVdHRmliME5tWTJNeGFHZFZhQ3N3V1VkS2NFNURSVXhyTjBaTFF5OTJhemR6ZERsR05tY3dUMjlrU0VSbGVYZFJXa2hLZFU1TVpsUnNRbEJHUTJOaU5seHVObTlzVEZOeWNGQTRjbFUzU0d4SGJsRkVSMFJNYVhkS1EyaGtSRGRVVUdSM2FXdHBkMHRGY201aldqaEdaalZsU25vd2RETmlUWFpyVDJaVVluSkJiRnh1WWtGQ1kwbzVNVmxVT1hKdVVXOXFkVWN4UldKUVRqaEZWblI2TWxZNE5IZHViR2Q0TUhCd2JEVjRPSFpOYlhwcE1ISnVibEZVV1VGamJ6WnFhMnBJTTF4dVRuTlVkWE4xUzFkdlJGUjVNWE5yZGtSUk9IbEJZV0ptWTNNME4zWnNRazAwU0RGT1JFNHZSSFJhWWxZdllubDJia0o2YkM4eFZrVnpURmRqWlZWcFRGeHVSWEYxT0VkeWF5dFFVRGQyUkdSd2JFUjNjWFpQV2t4RmRYazNkamhuUm01U09WUlVSV3ByTlVvNWRuWlVTR2RtU25VemVubEVPR2xLWTBSRE5YcHFPVnh1YjFGSlJFRlJRVUpjYmkwdExTMHRSVTVFSUZCVlFreEpReUJMUlZrdExTMHRMVnh1SWl3aWEyVjVVMmxuYm1GMGRYSmxJam9pWlhsS2VtRlhaSFZaV0ZJeFkyMVZhVTlwU2pCUldIQjJXVE5LVms1NmFGaFNSMlJzVVRKb2NtTklXa1ZVVlRsRldqQktXVTFGUmtaVFJFNUZVMGhLYkUxclRUTkxNSEJFVkROR2VGTnROVVJVVlRWVlltMDFiVnBGUm5sWldIQjZaRVJqTVZaSGFFeFBXRUpVVWtacmRrd3diek5aTUZaSlVteFdWRXd5T1VoV1JXeHNWa1ZPTUZSSE1WWlJNR04zVkd4R2JGa3pTblJUUm1zMFZVWk9hMVpWU2pCVU1WbDNZbXQwY0ZSclZuQmpia0poVFZjNWFtSldiSEZaYTNob1UyeHNWV0pGUmtWWGJVWnZWakZLVUZkcWJGSmhXRVp1V2xkb1EyRnVRak5TUjNNd1lWWkpOVTVXVmxkV1ZUVnlUMGhLYjFsVlRYbGhiVGcwVjBkYWVGbHFWbFppYlhoeFpFWkZkMDU1Y3pCaFZsSkpWRVpPTm1WRk1IcGxWWFJ2VFVaR1ZtRXdWVFJSVnpsSFVsaEtVRTFZUmxCU01WcFJVMVJDTmxsV2FIcFdWWEJ0WTBSU2JFMVVRazlPVjNSU1ZucFdUMU5XWTNaU1ZYUkZVMGhzYlU5VmJGaGtNMUl3WTFWc1lXTlhSakJTYTA1RVlVWmtjbUo2VmtSU00wSllUREkxUmsxWVl6SmxWM1JKVlZoQk1sVXhTbEppU0Zwd1VrVXdNRlpFVWt0VU1rWnNVVmQwYzFSV1VrMVVWV055V1RCYVRHSXpaRTlUVm05NVlraE9SR1JzVG5aUmFrWmFaVmRPVGxOVlNteGFiRXB1Wld0U2RVMHhSVGxRVTBselNXMWtjMkl5U21oaVJYUnNaVlZzYTBscWIybFpiVkpzV2xSVk1rNVVXWGRaTWxwcFRrUk9hazlYU1hsUFIwcHRUMVJvYkZsWFRtaGFiVVV5VGtSWmFXWlJQVDBpZlE9PSJ9 `, wantErr: true, - wantErrMsg: `"licenseID" field has changed`, + wantErrMsg: `license data validation failed: "licenseID" field has changed to "1vusOokxAVp1tkRGuyxnF23PJcq-modified" (license) from "1vusOokxAVp1tkRGuyxnF23PJcq" (within signature)`, }, { name: "endpoint field changed", @@ -223,7 +223,7 @@ spec: signature: eyJsaWNlbnNlRGF0YSI6ImV5SmhjR2xXWlhKemFXOXVJam9pYTI5MGN5NXBieTkyTVdKbGRHRXhJaXdpYTJsdVpDSTZJa3hwWTJWdWMyVWlMQ0p0WlhSaFpHRjBZU0k2ZXlKdVlXMWxJam9pZEdWemRHTjFjM1J2YldWeUluMHNJbk53WldNaU9uc2liR2xqWlc1elpVbEVJam9pTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54SWl3aWJHbGpaVzV6WlZSNWNHVWlPaUp3Y205a0lpd2lZM1Z6ZEc5dFpYSk9ZVzFsSWpvaVZHVnpkQ0JEZFhOMGIyMWxjaUlzSW1Gd2NGTnNkV2NpT2lKdGVTMWhjSEFpTENKamFHRnVibVZzU1VRaU9pSXhkblZ6U1ZsYVRFRldlRTFITm5FM05qQlBTbTFTUzJvMWFUVWlMQ0pqYUdGdWJtVnNUbUZ0WlNJNklrMTVJRU5vWVc1dVpXd2lMQ0pzYVdObGJuTmxVMlZ4ZFdWdVkyVWlPamNzSW1WdVpIQnZhVzUwSWpvaWFIUjBjSE02THk5eVpYQnNhV05oZEdWa0xtRndjQ0lzSW1WdWRHbDBiR1Z0Wlc1MGN5STZleUppYjI5c1gyWnBaV3hrSWpwN0luUnBkR3hsSWpvaVFtOXZiQ0JHYVdWc1pDSXNJblpoYkhWbElqcDBjblZsTENKMllXeDFaVlI1Y0dVaU9pSkNiMjlzWldGdUluMHNJbVY0Y0dseVpYTmZZWFFpT25zaWRHbDBiR1VpT2lKRmVIQnBjbUYwYVc5dUlpd2laR1Z6WTNKcGNIUnBiMjRpT2lKTWFXTmxibk5sSUVWNGNHbHlZWFJwYjI0aUxDSjJZV3gxWlNJNklqSXdNekF0TURjdE1qZFVNREE2TURBNk1EQmFJaXdpZG1Gc2RXVlVlWEJsSWpvaVUzUnlhVzVuSW4wc0ltaHBaR1JsYmw5bWFXVnNaQ0k2ZXlKMGFYUnNaU0k2SWtocFpHUmxiaUJHYVdWc1pDSXNJblpoYkhWbElqb2lkR2hwY3lCcGN5QnpaV055WlhRaUxDSjJZV3gxWlZSNWNHVWlPaUpUZEhKcGJtY2lMQ0pwYzBocFpHUmxiaUk2ZEhKMVpYMHNJbWx1ZEY5bWFXVnNaQ0k2ZXlKMGFYUnNaU0k2SWtsdWRDQkdhV1ZzWkNJc0luWmhiSFZsSWpveE1qTXNJblpoYkhWbFZIbHdaU0k2SWtsdWRHVm5aWElpZlN3aWMzUnlhVzVuWDJacFpXeGtJanA3SW5ScGRHeGxJam9pVTNSeWFXNW5SbWxsYkdRaUxDSjJZV3gxWlNJNkluTnBibWRzWlNCc2FXNWxJSFJsZUhRaUxDSjJZV3gxWlZSNWNHVWlPaUpUZEhKcGJtY2lmU3dpZEdWNGRGOW1hV1ZzWkNJNmV5SjBhWFJzWlNJNklsUmxlSFFnUm1sbGJHUWlMQ0oyWVd4MVpTSTZJbTExYkhScFhHNXNhVzVsWEc1MFpYaDBJaXdpZG1Gc2RXVlVlWEJsSWpvaVZHVjRkQ0o5ZlN3aWFYTkJhWEpuWVhCVGRYQndiM0owWldRaU9uUnlkV1VzSW1selIybDBUM0J6VTNWd2NHOXlkR1ZrSWpwMGNuVmxMQ0pwYzFOdVlYQnphRzkwVTNWd2NHOXlkR1ZrSWpwMGNuVmxmWDA9IiwiaW5uZXJTaWduYXR1cmUiOiJleUpzYVdObGJuTmxVMmxuYm1GMGRYSmxJam9pYUhneE1XTXZUR1ozUTNoVE5YRmtRWEJGU1hGdVRrMU9NMHBLYTJzNFZHZFhSVVpzVDFKVlJ6UjJjR1YzZEZoV1YzbG1lamRZY0hBd1ExazJZamRyUVRSS2N6TklhR3d3YkZJMFdUQTFMemN2UVVkQ2FEZFZNSGczUkhaTVozUXpVM00wYm5GTFZTdFhXRXBTVHpKWVFVRnZSME4xZFRWR1RGcHJRVWhYY1RSUVFtMXphSFY2Y1ZsdmNucHhlbGhGWVZWVlpFUlVkVXhDTW1nNWFIZ3dXRWhQUmxwUk16bHVkbTlPUjJaT2R5OTRTVmRaZEhSUGRYZHZhMncyTVZsb1JVeFZlRmQxU1ZSRmMwTlVhM2xtTVRNd09IazVSbFJzWlRKeVYyZEVlSEZNYTBSUFNXVXlPRWwzUzJSQkwySXdWVUl5VEZGbVRWcHdWemwyUTNCSkwybHlWek5uYmpaeU5WWjNWMjB2U1dweWJtNDNSelJrVmpadVYzcFRkMGhQUTJSdWEwMTRNRXQ1VVVOa0wxQjFaWEpUYjNSdVEwOXRTMDEzWlRSTGJqaERkMU5YVVRRNGRURkRNbTFpV1VzeGRYTlpOM1YzUFQwaUxDSndkV0pzYVdOTFpYa2lPaUl0TFMwdExVSkZSMGxPSUZCVlFreEpReUJMUlZrdExTMHRMVnh1VFVsSlFrbHFRVTVDWjJ0eGFHdHBSemwzTUVKQlVVVkdRVUZQUTBGUk9FRk5TVWxDUTJkTFEwRlJSVUZ6TkhKdlVIcDFhV1JNZVhOMmIxWTJkemxhTkZ4dVdHRmliME5tWTJNeGFHZFZhQ3N3V1VkS2NFNURSVXhyTjBaTFF5OTJhemR6ZERsR05tY3dUMjlrU0VSbGVYZFJXa2hLZFU1TVpsUnNRbEJHUTJOaU5seHVObTlzVEZOeWNGQTRjbFUzU0d4SGJsRkVSMFJNYVhkS1EyaGtSRGRVVUdSM2FXdHBkMHRGY201aldqaEdaalZsU25vd2RETmlUWFpyVDJaVVluSkJiRnh1WWtGQ1kwbzVNVmxVT1hKdVVXOXFkVWN4UldKUVRqaEZWblI2TWxZNE5IZHViR2Q0TUhCd2JEVjRPSFpOYlhwcE1ISnVibEZVV1VGamJ6WnFhMnBJTTF4dVRuTlVkWE4xUzFkdlJGUjVNWE5yZGtSUk9IbEJZV0ptWTNNME4zWnNRazAwU0RGT1JFNHZSSFJhWWxZdllubDJia0o2YkM4eFZrVnpURmRqWlZWcFRGeHVSWEYxT0VkeWF5dFFVRGQyUkdSd2JFUjNjWFpQV2t4RmRYazNkamhuUm01U09WUlVSV3ByTlVvNWRuWlVTR2RtU25VemVubEVPR2xLWTBSRE5YcHFPVnh1YjFGSlJFRlJRVUpjYmkwdExTMHRSVTVFSUZCVlFreEpReUJMUlZrdExTMHRMVnh1SWl3aWEyVjVVMmxuYm1GMGRYSmxJam9pWlhsS2VtRlhaSFZaV0ZJeFkyMVZhVTlwU2pCUldIQjJXVE5LVms1NmFGaFNSMlJzVVRKb2NtTklXa1ZVVlRsRldqQktXVTFGUmtaVFJFNUZVMGhLYkUxclRUTkxNSEJFVkROR2VGTnROVVJVVlRWVlltMDFiVnBGUm5sWldIQjZaRVJqTVZaSGFFeFBXRUpVVWtacmRrd3diek5aTUZaSlVteFdWRXd5T1VoV1JXeHNWa1ZPTUZSSE1WWlJNR04zVkd4R2JGa3pTblJUUm1zMFZVWk9hMVpWU2pCVU1WbDNZbXQwY0ZSclZuQmpia0poVFZjNWFtSldiSEZaYTNob1UyeHNWV0pGUmtWWGJVWnZWakZLVUZkcWJGSmhXRVp1V2xkb1EyRnVRak5TUjNNd1lWWkpOVTVXVmxkV1ZUVnlUMGhLYjFsVlRYbGhiVGcwVjBkYWVGbHFWbFppYlhoeFpFWkZkMDU1Y3pCaFZsSkpWRVpPTm1WRk1IcGxWWFJ2VFVaR1ZtRXdWVFJSVnpsSFVsaEtVRTFZUmxCU01WcFJVMVJDTmxsV2FIcFdWWEJ0WTBSU2JFMVVRazlPVjNSU1ZucFdUMU5XWTNaU1ZYUkZVMGhzYlU5VmJGaGtNMUl3WTFWc1lXTlhSakJTYTA1RVlVWmtjbUo2VmtSU00wSllUREkxUmsxWVl6SmxWM1JKVlZoQk1sVXhTbEppU0Zwd1VrVXdNRlpFVWt0VU1rWnNVVmQwYzFSV1VrMVVWV055V1RCYVRHSXpaRTlUVm05NVlraE9SR1JzVG5aUmFrWmFaVmRPVGxOVlNteGFiRXB1Wld0U2RVMHhSVGxRVTBselNXMWtjMkl5U21oaVJYUnNaVlZzYTBscWIybFpiVkpzV2xSVk1rNVVXWGRaTWxwcFRrUk9hazlYU1hsUFIwcHRUMVJvYkZsWFRtaGFiVVV5VGtSWmFXWlJQVDBpZlE9PSJ9 `, wantErr: true, - wantErrMsg: `"endpoint" field has changed`, + wantErrMsg: `license data validation failed: "endpoint" field has changed to "https://replicated.app.modified" (license) from "https://replicated.app" (within signature)`, }, } for _, tt := range tests { diff --git a/pkg/license/types/types.go b/pkg/license/types/types.go index 541ccc9b..c3ac3b2f 100644 --- a/pkg/license/types/types.go +++ b/pkg/license/types/types.go @@ -11,7 +11,8 @@ type LicenseField struct { } type LicenseFieldSignature struct { - V1 string `json:"v1,omitempty" yaml:"v1,omitempty"` // this is a base64 encoded string because yaml.Unmarshal doesn't automatically convert base64 to []byte like json.Unmarshal does + V1 string `json:"v1,omitempty" yaml:"v1,omitempty"` // v1beta1: base64 encoded MD5 signature + V2 string `json:"v2,omitempty" yaml:"v2,omitempty"` // v1beta2: base64 encoded SHA-256 signature } type LicenseFields map[string]LicenseField diff --git a/pkg/license/util.go b/pkg/license/util.go index d23453bf..602ef1ac 100644 --- a/pkg/license/util.go +++ b/pkg/license/util.go @@ -1,37 +1,17 @@ package license import ( - "os" - - "github.com/pkg/errors" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - kotsscheme "github.com/replicatedhq/kotskinds/client/kotsclientset/scheme" - "k8s.io/client-go/kubernetes/scheme" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" ) -func init() { - kotsscheme.AddToScheme(scheme.Scheme) -} - -func LoadLicenseFromPath(licenseFilePath string) (*kotsv1beta1.License, error) { - licenseData, err := os.ReadFile(licenseFilePath) - if err != nil { - return nil, errors.Wrap(err, "failed to read license file") - } - - return LoadLicenseFromBytes(licenseData) +// LoadLicenseFromPath loads a license from a file path. +// This function now wraps the licensewrapper package implementation. +func LoadLicenseFromPath(licenseFilePath string) (licensewrapper.LicenseWrapper, error) { + return licensewrapper.LoadLicenseFromPath(licenseFilePath) } -func LoadLicenseFromBytes(data []byte) (*kotsv1beta1.License, error) { - decode := scheme.Codecs.UniversalDeserializer().Decode - obj, gvk, err := decode([]byte(data), nil, nil) - if err != nil { - return nil, errors.Wrap(err, "failed to decode license data") - } - - if gvk.Group != "kots.io" || gvk.Version != "v1beta1" || gvk.Kind != "License" { - return nil, errors.Errorf("unexpected GVK: %s", gvk.String()) - } - - return obj.(*kotsv1beta1.License), nil +// LoadLicenseFromBytes deserializes license YAML/JSON bytes into a LicenseWrapper. +// This function now wraps the licensewrapper package implementation. +func LoadLicenseFromBytes(data []byte) (licensewrapper.LicenseWrapper, error) { + return licensewrapper.LoadLicenseFromBytes(data) } diff --git a/pkg/report/custom_app_metrics.go b/pkg/report/custom_app_metrics.go index 167fed9a..f3b9008e 100644 --- a/pkg/report/custom_app_metrics.go +++ b/pkg/report/custom_app_metrics.go @@ -34,7 +34,7 @@ func SendAirgapCustomAppMetrics(clientset kubernetes.Interface, sdkStore store.S Events: []CustomAppMetricsReportEvent{ { ReportedAt: time.Now().UTC().UnixMilli(), - LicenseID: sdkStore.GetLicense().Spec.LicenseID, + LicenseID: sdkStore.GetLicense().GetLicenseID(), InstanceID: sdkStore.GetAppID(), Data: data, }, @@ -49,11 +49,11 @@ func SendAirgapCustomAppMetrics(clientset kubernetes.Interface, sdkStore store.S } func SendOnlineCustomAppMetrics(sdkStore store.Store, data map[string]interface{}) error { - license := sdkStore.GetLicense() + wrapper := sdkStore.GetLicense() endpoint := sdkStore.GetReplicatedAppEndpoint() if endpoint == "" { - endpoint = license.Spec.Endpoint + endpoint = wrapper.GetEndpoint() } u, err := url.Parse(endpoint) @@ -84,7 +84,7 @@ func SendOnlineCustomAppMetrics(sdkStore store.Store, data map[string]interface{ return errors.Wrap(err, "call newrequest") } - req.SetBasicAuth(license.Spec.LicenseID, license.Spec.LicenseID) + req.SetBasicAuth(wrapper.GetLicenseID(), wrapper.GetLicenseID()) req.Header.Set("Content-Type", "application/json") instanceData := GetInstanceData(sdkStore) diff --git a/pkg/report/instance.go b/pkg/report/instance.go index 98dcd056..93cb90a9 100644 --- a/pkg/report/instance.go +++ b/pkg/report/instance.go @@ -10,7 +10,7 @@ import ( "time" "github.com/pkg/errors" - "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/buildversion" "github.com/replicatedhq/replicated-sdk/pkg/k8sutil" "github.com/replicatedhq/replicated-sdk/pkg/logger" @@ -24,9 +24,9 @@ import ( var instanceDataMtx sync.Mutex func SendInstanceData(clientset kubernetes.Interface, sdkStore store.Store) error { - license := sdkStore.GetLicense() + wrapper := sdkStore.GetLicense() - canReport, err := canReport(clientset, sdkStore.GetNamespace(), license) + canReport, err := canReport(clientset, sdkStore.GetNamespace(), wrapper) if err != nil { return errors.Wrap(err, "failed to check if can report") } @@ -44,10 +44,10 @@ func SendInstanceData(clientset kubernetes.Interface, sdkStore store.Store) erro instanceData := GetInstanceData(sdkStore) if util.IsAirgap() { - return SendAirgapInstanceData(clientset, sdkStore.GetNamespace(), license.Spec.LicenseID, instanceData) + return SendAirgapInstanceData(clientset, sdkStore.GetNamespace(), wrapper.GetLicenseID(), instanceData) } - return SendOnlineInstanceData(license, instanceData) + return SendOnlineInstanceData(wrapper, instanceData) } func SendAirgapInstanceData(clientset kubernetes.Interface, namespace string, licenseID string, instanceData *types.InstanceData) error { @@ -90,7 +90,7 @@ func SendAirgapInstanceData(clientset kubernetes.Interface, namespace string, li return nil } -func SendOnlineInstanceData(license *v1beta1.License, instanceData *types.InstanceData) error { +func SendOnlineInstanceData(wrapper licensewrapper.LicenseWrapper, instanceData *types.InstanceData) error { // build the request body reqPayload := map[string]interface{}{} if err := InjectInstanceDataPayload(reqPayload, instanceData); err != nil { @@ -104,14 +104,14 @@ func SendOnlineInstanceData(license *v1beta1.License, instanceData *types.Instan // Get endpoint from store if available, otherwise fall back to license endpoint endpoint := store.GetStore().GetReplicatedAppEndpoint() if endpoint == "" { - endpoint = license.Spec.Endpoint + endpoint = wrapper.GetEndpoint() } postReq, err := util.NewRequest("POST", fmt.Sprintf("%s/kots_metrics/license_instance/info", endpoint), bytes.NewBuffer(reqBody)) if err != nil { return errors.Wrap(err, "failed to create http request") } - postReq.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", license.Spec.LicenseID, license.Spec.LicenseID))))) + postReq.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", wrapper.GetLicenseID(), wrapper.GetLicenseID()))))) postReq.Header.Set("Content-Type", "application/json") InjectInstanceDataHeaders(postReq, instanceData) diff --git a/pkg/report/instance_test.go b/pkg/report/instance_test.go index 1b296545..35052229 100644 --- a/pkg/report/instance_test.go +++ b/pkg/report/instance_test.go @@ -10,6 +10,7 @@ import ( "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types" "github.com/replicatedhq/replicated-sdk/pkg/k8sutil" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/store" mock_store "github.com/replicatedhq/replicated-sdk/pkg/store/mock" "github.com/replicatedhq/replicated-sdk/pkg/util" @@ -59,12 +60,12 @@ func Test_SendInstanceData(t *testing.T) { }, isAirgap: false, mockStoreExpectations: func() { - mockStore.EXPECT().GetLicense().Return(&v1beta1.License{ + mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "test-license-id", Endpoint: mockServer.URL, }, - }) + }}) mockStore.EXPECT().GetNamespace().Times(2).Return("test-namespace") mockStore.EXPECT().GetReplicatedID().Return("test-cluster-id") mockStore.EXPECT().GetAppID().Return("test-app") @@ -96,12 +97,12 @@ func Test_SendInstanceData(t *testing.T) { }, isAirgap: true, mockStoreExpectations: func() { - mockStore.EXPECT().GetLicense().Return(&v1beta1.License{ + mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{ Spec: v1beta1.LicenseSpec{ LicenseID: "test-license-id", Endpoint: mockServer.URL, }, - }) + }}) mockStore.EXPECT().GetNamespace().Times(3).Return("test-namespace") mockStore.EXPECT().GetReplicatedID().Return("test-cluster-id") mockStore.EXPECT().GetAppID().Return("test-app") diff --git a/pkg/report/util.go b/pkg/report/util.go index 1aef2166..964db26e 100644 --- a/pkg/report/util.go +++ b/pkg/report/util.go @@ -8,7 +8,7 @@ import ( "strconv" "github.com/pkg/errors" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/logger" "github.com/replicatedhq/replicated-sdk/pkg/report/types" "github.com/replicatedhq/replicated-sdk/pkg/util" @@ -109,8 +109,8 @@ func GetInstanceDataHeaders(instanceData *types.InstanceData) map[string]string return headers } -func canReport(clientset kubernetes.Interface, namespace string, license *kotsv1beta1.License) (bool, error) { - if util.IsDevEnv() && !util.IsDevLicense(license) { +func canReport(clientset kubernetes.Interface, namespace string, wrapper licensewrapper.LicenseWrapper) (bool, error) { + if util.IsDevEnv() && wrapper.GetLicenseType() != "dev" { // don't send reports from our dev env to our production services even if this is a production license return false, nil } diff --git a/pkg/report/util_test.go b/pkg/report/util_test.go index e1d07785..40e6281b 100644 --- a/pkg/report/util_test.go +++ b/pkg/report/util_test.go @@ -5,6 +5,7 @@ import ( appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types" "github.com/replicatedhq/replicated-sdk/pkg/k8sutil" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" metatypes "github.com/replicatedhq/replicated-sdk/pkg/meta/types" "github.com/replicatedhq/replicated-sdk/pkg/report/types" "github.com/replicatedhq/replicated-sdk/pkg/util" @@ -206,7 +207,7 @@ func TestCanReport(t *testing.T) { t.Setenv(k, v) } - got, err := canReport(tt.clientset, tt.namespace, nil) + got, err := canReport(tt.clientset, tt.namespace, licensewrapper.LicenseWrapper{}) if (err != nil) != tt.wantErr { t.Errorf("canReport() error = %v, wantErr %v", err, tt.wantErr) } diff --git a/pkg/store/memory_store.go b/pkg/store/memory_store.go index a9d0fa49..bfcbefc7 100644 --- a/pkg/store/memory_store.go +++ b/pkg/store/memory_store.go @@ -3,17 +3,17 @@ package store import ( "strings" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types" - sdklicensetypes "github.com/replicatedhq/replicated-sdk/pkg/license/types" + licensetypes "github.com/replicatedhq/replicated-sdk/pkg/license/types" upstreamtypes "github.com/replicatedhq/replicated-sdk/pkg/upstream/types" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" ) type InMemoryStore struct { replicatedID string appID string - license *kotsv1beta1.License - licenseFields sdklicensetypes.LicenseFields + license licensewrapper.LicenseWrapper + licenseFields licensetypes.LicenseFields appSlug string appName string channelID string @@ -36,8 +36,8 @@ type InMemoryStore struct { type InitInMemoryStoreOptions struct { ReplicatedID string AppID string - License *kotsv1beta1.License - LicenseFields sdklicensetypes.LicenseFields + License licensewrapper.LicenseWrapper + LicenseFields licensetypes.LicenseFields AppName string ChannelID string ChannelName string @@ -56,7 +56,7 @@ func InitInMemory(options InitInMemoryStoreOptions) { SetStore(&InMemoryStore{ replicatedID: options.ReplicatedID, appID: options.AppID, - appSlug: options.License.Spec.AppSlug, + appSlug: options.License.GetAppSlug(), license: options.License, licenseFields: options.LicenseFields, appName: options.AppName, @@ -82,26 +82,35 @@ func (s *InMemoryStore) GetAppID() string { return s.appID } -func (s *InMemoryStore) GetLicense() *kotsv1beta1.License { +func (s *InMemoryStore) GetLicense() licensewrapper.LicenseWrapper { return s.license } -func (s *InMemoryStore) SetLicense(license *kotsv1beta1.License) { - s.license = license.DeepCopy() +func (s *InMemoryStore) SetLicense(license licensewrapper.LicenseWrapper) { + // DeepCopy appropriate version + if license.V1 != nil { + s.license = licensewrapper.LicenseWrapper{ + V1: license.V1.DeepCopy(), + } + } else if license.V2 != nil { + s.license = licensewrapper.LicenseWrapper{ + V2: license.V2.DeepCopy(), + } + } } -func (s *InMemoryStore) GetLicenseFields() sdklicensetypes.LicenseFields { +func (s *InMemoryStore) GetLicenseFields() licensetypes.LicenseFields { return s.licenseFields } -func (s *InMemoryStore) SetLicenseFields(licenseFields sdklicensetypes.LicenseFields) { +func (s *InMemoryStore) SetLicenseFields(licenseFields licensetypes.LicenseFields) { // copy by value not reference if licenseFields == nil { s.licenseFields = nil return } if s.licenseFields == nil { - s.licenseFields = sdklicensetypes.LicenseFields{} + s.licenseFields = licensetypes.LicenseFields{} } for k, v := range licenseFields { s.licenseFields[k] = v @@ -109,7 +118,7 @@ func (s *InMemoryStore) SetLicenseFields(licenseFields sdklicensetypes.LicenseFi } func (s *InMemoryStore) IsDevLicense() bool { - return s.license.Spec.LicenseType == "dev" + return s.license.GetLicenseType() == "dev" } func (s *InMemoryStore) GetAppSlug() string { diff --git a/pkg/store/mock/mock_store.go b/pkg/store/mock/mock_store.go index 74baec5b..41a10452 100644 --- a/pkg/store/mock/mock_store.go +++ b/pkg/store/mock/mock_store.go @@ -8,7 +8,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - v1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" types "github.com/replicatedhq/replicated-sdk/pkg/appstate/types" types0 "github.com/replicatedhq/replicated-sdk/pkg/license/types" types1 "github.com/replicatedhq/replicated-sdk/pkg/upstream/types" @@ -148,10 +148,10 @@ func (mr *MockStoreMockRecorder) GetChannelSequence() *gomock.Call { } // GetLicense mocks base method. -func (m *MockStore) GetLicense() *v1beta1.License { +func (m *MockStore) GetLicense() licensewrapper.LicenseWrapper { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLicense") - ret0, _ := ret[0].(*v1beta1.License) + ret0, _ := ret[0].(licensewrapper.LicenseWrapper) return ret0 } @@ -356,7 +356,7 @@ func (mr *MockStoreMockRecorder) SetAppStatus(status interface{}) *gomock.Call { } // SetLicense mocks base method. -func (m *MockStore) SetLicense(license *v1beta1.License) { +func (m *MockStore) SetLicense(license licensewrapper.LicenseWrapper) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetLicense", license) } diff --git a/pkg/store/store_interface.go b/pkg/store/store_interface.go index d9f2a10a..a011fe26 100644 --- a/pkg/store/store_interface.go +++ b/pkg/store/store_interface.go @@ -1,10 +1,10 @@ package store import ( - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types" - sdklicensetypes "github.com/replicatedhq/replicated-sdk/pkg/license/types" + licensetypes "github.com/replicatedhq/replicated-sdk/pkg/license/types" upstreamtypes "github.com/replicatedhq/replicated-sdk/pkg/upstream/types" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" ) var ( @@ -16,10 +16,10 @@ var ( type Store interface { GetReplicatedID() string GetAppID() string - GetLicense() *kotsv1beta1.License - SetLicense(license *kotsv1beta1.License) - GetLicenseFields() sdklicensetypes.LicenseFields - SetLicenseFields(licenseFields sdklicensetypes.LicenseFields) + GetLicense() licensewrapper.LicenseWrapper + SetLicense(license licensewrapper.LicenseWrapper) + GetLicenseFields() licensetypes.LicenseFields + SetLicenseFields(licenseFields licensetypes.LicenseFields) IsDevLicense() bool GetAppSlug() string GetAppName() string diff --git a/pkg/upstream/replicated.go b/pkg/upstream/replicated.go index a30ef8b7..4a8336dc 100644 --- a/pkg/upstream/replicated.go +++ b/pkg/upstream/replicated.go @@ -9,17 +9,17 @@ import ( "net/url" "github.com/pkg/errors" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper" "github.com/replicatedhq/replicated-sdk/pkg/report" "github.com/replicatedhq/replicated-sdk/pkg/store" types "github.com/replicatedhq/replicated-sdk/pkg/upstream/types" "github.com/replicatedhq/replicated-sdk/pkg/util" ) -func GetUpdates(sdkStore store.Store, license *kotsv1beta1.License, currentCursor types.ReplicatedCursor) ([]types.ChannelRelease, error) { +func GetUpdates(sdkStore store.Store, wrapper licensewrapper.LicenseWrapper, currentCursor types.ReplicatedCursor) ([]types.ChannelRelease, error) { endpoint := sdkStore.GetReplicatedAppEndpoint() if endpoint == "" { - endpoint = license.Spec.Endpoint + endpoint = wrapper.GetEndpoint() } u, err := url.Parse(endpoint) @@ -34,18 +34,18 @@ func GetUpdates(sdkStore store.Store, license *kotsv1beta1.License, currentCurso // build the request url query params channelSequenceStr := fmt.Sprintf("%d", currentCursor.ChannelSequence) - if currentCursor.ChannelID != license.Spec.ChannelID { + if currentCursor.ChannelID != wrapper.GetChannelID() { // channel has changed, so we need to reset the channel sequence channelSequenceStr = "" } urlValues := url.Values{} urlValues.Set("channelSequence", channelSequenceStr) - urlValues.Add("licenseSequence", fmt.Sprintf("%d", license.Spec.LicenseSequence)) + urlValues.Add("licenseSequence", fmt.Sprintf("%d", wrapper.GetLicenseSequence())) urlValues.Add("isSemverSupported", "true") urlValues.Add("sortOrder", "desc") - url := fmt.Sprintf("%s://%s/release/%s/pending?%s", u.Scheme, hostname, license.Spec.AppSlug, urlValues.Encode()) + url := fmt.Sprintf("%s://%s/release/%s/pending?%s", u.Scheme, hostname, wrapper.GetAppSlug(), urlValues.Encode()) instanceData := report.GetInstanceData(sdkStore) @@ -63,7 +63,7 @@ func GetUpdates(sdkStore store.Store, license *kotsv1beta1.License, currentCurso if err != nil { return nil, errors.Wrap(err, "failed to call newrequest") } - req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", license.Spec.LicenseID, license.Spec.LicenseID))))) + req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", wrapper.GetLicenseID(), wrapper.GetLicenseID()))))) req.Header.Set("Content-Type", "application/json") report.InjectInstanceDataHeaders(req, instanceData)