Skip to content

Commit 686bd3d

Browse files
crdantclaude
andauthored
Adds v1beta2 license support (#336)
* Update kotskinds dependency to support v1beta2 License API Updates kotskinds from v0.0.0-20230724164735-f83482cc9cfe to v0.0.0-20251023161058-b6489d3d51c5 to gain access to the new v1beta2 License API. This version includes both v1beta1 and v1beta2 license types, allowing the SDK to support both API versions during a gradual migration period. This is the first step toward supporting the new v1beta2 License API while maintaining backward compatibility with existing v1beta1 licenses. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Add LicenseWrapper type to abstract v1beta1/v1beta2 differences Introduces a new LicenseWrapper type that can hold either a v1beta1 or v1beta2 License object, providing a unified interface to access common license fields regardless of the API version. The wrapper includes: - Version detection methods (IsV1, IsV2) - Accessor methods for all common license fields (AppSlug, LicenseID, CustomerName, etc.) - Support for all boolean feature flags (IsAirgapSupported, IsGitOpsSupported, etc.) This abstraction allows the SDK to work with both license versions transparently, eliminating the need for version checks throughout the codebase. The wrapper always has exactly one field populated (either V1 or V2), never both. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Update license loading and storage to use LicenseWrapper Modifies LoadLicenseFromBytes to detect and parse both v1beta1 and v1beta2 license formats, returning a LicenseWrapper that encapsulates the appropriate version. The loader now accepts both API versions and creates the correct wrapper type based on the detected GVK. Updates the Store interface and InMemoryStore implementation to use LicenseWrapper instead of the concrete v1beta1.License type. This enables the SDK to store and retrieve licenses of either version transparently. Key changes: - LoadLicenseFromBytes returns LicenseWrapper instead of *v1beta1.License - Store interface methods accept/return LicenseWrapper - InMemoryStore properly deep copies the correct license version - Uses wrapper accessor methods (GetAppSlug, GetLicenseType) instead of direct field access This completes the core infrastructure needed to support both license API versions throughout the SDK while maintaining a clean, version- agnostic API surface. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Adds SHA-256 signature verification for v1beta2 licenses Refactors signature verification to support both v1beta1 (MD5) and v1beta2 (SHA-256) licenses: - Refactors VerifySignature() to accept LicenseWrapper and dispatch to version-specific verification - Adds verifyV1Signature() for backward-compatible MD5 verification of v1beta1 licenses - Adds verifyV2Signature() for SHA-256 verification of v1beta2 licenses - Implements VerifySHA256() helper function using RSA-PSS with SHA-256 hashing This enables the SDK to verify v1beta2 licenses using the more secure SHA-256 algorithm instead of MD5, while maintaining full backward compatibility for existing v1beta1 licenses. [Phase 3 of v1beta2 license support] 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Refactor codebase to use LicenseWrapper throughout Updates all code to use the LicenseWrapper type instead of direct kotsv1beta1.License references. This completes the integration of the abstraction layer that supports both v1beta1 and v1beta2 license APIs. Changes include: - Update function signatures in pkg/license/, pkg/report/, pkg/handlers/, pkg/integration/, pkg/apiserver/, and pkg/upstream/ to accept LicenseWrapper - Replace direct license.Spec.* accesses with wrapper getter methods - Update imports to use licensetypes package - Modify LicenseInfo struct to support both v1 and v2 entitlements natively - Fix tests to construct and use LicenseWrapper pattern - Update mock store to work with LicenseWrapper This refactoring maintains backward compatibility with v1beta1 licenses while enabling future v1beta2 license support through the wrapper abstraction. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Updates pact contract tests to use LicenseWrapper Refactors pact test files to use the LicenseWrapper type instead of raw kotsv1beta1.License pointers, ensuring the contract tests match the new LicenseWrapper-based API signatures introduced in the license abstraction layer. Changes include: - Wrapping v1beta1.License instances in LicenseWrapper{V1: ...} in test setup - Updating mock store expectations to return LicenseWrapper types - Adding licensetypes import to all affected pact test files This maintains pact contract compatibility while supporting both v1beta1 and v1beta2 license APIs through the abstraction layer. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Update v1beta2 license signature handling to match backend Changes: - Update InnerSignature struct to support dual-algorithm signatures (v1beta1 uses MD5, v1beta2 uses SHA-256) - Change v1beta2.License field from Signature256 to Signature - Update v1beta2 verification to use V2KeySignature and V2LicenseSignature - Add complete field-by-field validation for v1beta2 licenses - Add entitlement signature verification for both v1beta1 and v1beta2 - Add GetV2AppPublicKey helper function for v1beta2 entitlements - Update kotskinds to latest version from main The SDK now correctly unmarshals signatures from vandoor's dual-algorithm format where both MD5 and SHA-256 signatures are present in the same inner signature structure. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix entitlement signature verification to match vandoor format Vandoor signs entitlement values using fmt.Sprint(), not JSON marshaling. Changed verification to use fmt.Sprint() to match vandoor's signing format. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Update kotskinds dependency to get ValidateLicense methods Updates github.com/replicatedhq/kotskinds from v0.0.0-20251024162531-2174a5b85a4d to v0.0.0-20251024204505-044aa5d007d5 to get the ValidateLicense() methods for both v1beta1 and v1beta2 licenses. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Migrate license validation to use kotskinds ValidateLicense Replaces custom crypto implementation with kotskinds' built-in ValidateLicense() method for both v1beta1 and v1beta2 licenses. This removes approximately 493 lines of custom RSA signature verification, license field validation, and entitlement signature checking code. The ValidateLicense() method handles: - MD5 signature verification for v1beta1 licenses - SHA-256 signature verification for v1beta2 licenses - License field integrity checks - Entitlement signature validation - Old and new signature format support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Bump chart version to 1.10.0 Updates chart version and appVersion from 1.0.0 to 1.10.0 to reflect license validation improvements. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Restores version placeholder * Add v2 signature field to LicenseFieldSignature for v1beta2 support The LicenseFieldSignature struct was only capturing v1 (MD5) signatures, causing v2 (SHA-256) signatures from v1beta2 licenses to be discarded during JSON unmarshaling. This resulted in empty signature objects when calling the /license/fields endpoint with v1beta2 licenses. Updated LicenseFieldSignature to include both v1 and v2 fields to support signature validation for both v1beta1 and v1beta2 license formats. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Migrate replicated-sdk to use kotskinds/pkg/licensewrapper This commit completes Phase 2 of the LicenseWrapper consolidation by migrating replicated-sdk to use the shared licensewrapper package from kotskinds instead of maintaining its own duplicate implementation. Changes: - Updated go.mod to reference kotskinds with pkg/licensewrapper support - Removed local pkg/license/types/license_wrapper.go (18 methods) - Updated all imports from local licensetypes.LicenseWrapper to kotskinds licensewrapper.LicenseWrapper - Simplified pkg/license/util.go to wrap kotskinds loader functions - Regenerated mock store to reflect new types - Updated 15 files across the codebase: - pkg/store (store_interface.go, memory_store.go, mock/mock_store.go) - pkg/license (license.go, signature.go, util.go) - pkg/report (instance.go, util.go) - pkg/integration, pkg/upstream, pkg/handlers, pkg/apiserver All tests pass and signature validation works correctly using kotskinds ValidateLicense methods. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Update kotskinds dependency to commit 174e89c9 for EntitlementFieldWrapper Updates kotskinds from b7dd48f3cb2b to 174e89c93554 (commit 174e89c9). This brings in the merged PR #44 from kotskinds main branch which includes: - EntitlementFieldWrapper type for version-agnostic entitlement access - GetEntitlements() method returning wrapped entitlements - Accessor methods: GetTitle(), GetDescription(), GetValue(), GetValueType(), IsHidden(), GetSignature() This enables replicated-sdk to use EntitlementFieldWrapper to simplify entitlement access and eliminate version-specific conditional logic. Related: kotskinds PR #44 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Refactor LicenseIsExpired() to use EntitlementFieldWrapper Replaces duplicated version-specific conditional logic with unified GetEntitlements() access from kotskinds EntitlementFieldWrapper. Changes: - Replace if wrapper.V1/V2 != nil blocks with single GetEntitlements() call - Use ent.GetValueType() instead of direct val.ValueType access - Use ent.GetValue().StrVal instead of direct val.Value.StrVal access - Eliminates 14 lines of duplicated code (36 lines -> 24 lines) Benefits: - Single code path for all license versions - Version-agnostic entitlement access - Forward-compatible with v1beta3+ - More maintainable and easier to understand All existing tests pass without modification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Refactor licenseInfoFromWrapper() to use EntitlementFieldWrapper Updates HTTP handler to use GetEntitlements() internally while maintaining backward-compatible API response structure. Changes: - Replace direct wrapper.V1.Spec.Entitlements access with GetEntitlements() - Use wrapper.IsV1() and wrapper.IsV2() helper methods - Add conversion loop to extract underlying EntitlementField structs - Maintain identical API response structure (v1Entitlements/v2Entitlements) Benefits: - Uses unified GetEntitlements() API internally - Maintains full backward compatibility - API consumers see no change in response structure - Cleaner version checking with IsV1()/IsV2() methods All existing tests pass without modification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix test imports to use licensewrapper from kotskinds Updates test files to import licensewrapper from kotskinds instead of the deleted pkg/license/types package. Changes: - Replace licensetypes import with licensewrapper from kotskinds - Update all licensetypes.LicenseWrapper references to licensewrapper.LicenseWrapper Files updated: - pact/license_test.go - pact/instance_test.go - pact/custom_metrics_test.go - pkg/integration/integration_test.go - pkg/report/util_test.go - pkg/report/instance_test.go All pkg/ tests pass successfully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Use unified entitlements field with EntitlementFieldWrapper BREAKING CHANGE: LicenseInfo JSON response structure has changed Previously, the LicenseInfo API response included version-specific entitlements fields: - v1Entitlements: map[string]kotsv1beta1.EntitlementField - v2Entitlements: map[string]kotsv1beta2.EntitlementField Now, there is a single unified entitlements field: - entitlements: map[string]EntitlementFieldWrapper The EntitlementFieldWrapper provides version-agnostic access to entitlement data through accessor methods: - GetTitle() - GetDescription() - GetValue() - GetValueType() - IsHidden() - GetSignature() Benefits: - Eliminates version-specific conditional logic in API consumers - Provides single, consistent entitlements structure - Simplifies licenseInfoFromWrapper() implementation - Removes dependency on kotsv1beta1 and kotsv1beta2 imports All tests pass with this change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix entitlements JSON serialization to use EntitlementField not wrapper The LicenseInfo API response now correctly serializes entitlements as EntitlementField objects instead of EntitlementFieldWrapper objects. Previously, the JSON would have looked like: ```json { "entitlements": { "some_field": { "V1": { "title": "...", "value": "..." }, "V2": null } } } ``` Now it correctly serializes as: ```json { "entitlements": { "some_field": { "title": "...", "value": "...", "valueType": "..." } } } ``` Implementation: - Convert EntitlementFieldWrapper map to EntitlementField map - For V1 licenses: use EntitlementField directly - For V2 licenses: convert v1beta2.EntitlementField to v1beta1.EntitlementField (they are structurally identical, only signature field differs) - This maintains the unified entitlements API while providing proper JSON structure All tests pass with this change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix: Return version-specific EntitlementField in API response BREAKING CHANGE (REVERTED): This fixes the previous commit that incorrectly converted v1beta2 licenses to v1beta1 format in the API response. The API now correctly returns: - v1beta1.EntitlementField for v1beta1 licenses (with MD5 signature in v1 field) - v1beta2.EntitlementField for v1beta2 licenses (with SHA-256 signature in v2 field) This preserves the actual license format and signature data instead of incorrectly converting v2 signatures to v1 format. Implementation: - Changed Entitlements field type from map[string]EntitlementField to interface{} - Detect license version with wrapper.IsV1() / wrapper.IsV2() - Return appropriate version-specific map - Preserves signature format integrity This is the correct approach - the API response should reflect the actual license version rather than forcing everything into v1 format. All tests pass with this change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 5d54c81 commit 686bd3d

24 files changed

+253
-472
lines changed

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/replicatedhq/replicated-sdk
22

3-
go 1.24.0
3+
go 1.24.6
44

55
require (
66
github.com/blang/semver v3.5.1+incompatible
@@ -11,7 +11,7 @@ require (
1111
github.com/pact-foundation/pact-go v1.10.0
1212
github.com/pkg/errors v0.9.1
1313
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
14-
github.com/replicatedhq/kotskinds v0.0.0-20230724164735-f83482cc9cfe
14+
github.com/replicatedhq/kotskinds v0.0.0-20251029124314-174e89c93554
1515
github.com/robfig/cron/v3 v3.0.1
1616
github.com/spf13/cobra v1.10.1
1717
github.com/spf13/pflag v1.0.10
@@ -127,7 +127,7 @@ require (
127127
golang.org/x/time v0.12.0 // indirect
128128
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
129129
google.golang.org/grpc v1.72.1 // indirect
130-
google.golang.org/protobuf v1.36.5 // indirect
130+
google.golang.org/protobuf v1.36.8 // indirect
131131
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
132132
gopkg.in/inf.v0 v0.9.1 // indirect
133133
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
129129
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
130130
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
131131
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
132-
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
133-
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
132+
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
133+
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
134134
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
135135
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
136136
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
324324
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ=
325325
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
326326
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
327-
github.com/replicatedhq/kotskinds v0.0.0-20230724164735-f83482cc9cfe h1:3AJInd06UxzqHmgy8+24CPsT2tYSE0zToJZyuX9q+MA=
328-
github.com/replicatedhq/kotskinds v0.0.0-20230724164735-f83482cc9cfe/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
327+
github.com/replicatedhq/kotskinds v0.0.0-20251029124314-174e89c93554 h1:a9vLewcXgVC/vclEak7CV0gsSYhYinjnWDoUkzrqN4w=
328+
github.com/replicatedhq/kotskinds v0.0.0-20251029124314-174e89c93554/go.mod h1:+k4PHo2wukoU9kdiKrqqgi89Wmj+9AiwppYGVK11zig=
329329
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
330330
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
331331
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
550550
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
551551
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
552552
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
553-
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
554-
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
553+
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
554+
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
555555
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
556556
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
557557
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

pact/custom_metrics_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
1515
"github.com/replicatedhq/replicated-sdk/pkg/handlers"
1616
"github.com/replicatedhq/replicated-sdk/pkg/k8sutil"
17+
licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper"
1718
"github.com/replicatedhq/replicated-sdk/pkg/store"
1819
"github.com/replicatedhq/replicated-sdk/pkg/util"
1920
"k8s.io/client-go/kubernetes/fake"
@@ -75,7 +76,7 @@ func TestSendCustomAppMetrics(t *testing.T) {
7576
pactInteraction()
7677

7778
storeOptions := store.InitInMemoryStoreOptions{
78-
License: license,
79+
License: licensewrapper.LicenseWrapper{V1: license},
7980
LicenseFields: nil,
8081
ReplicatedAppEndpoint: license.Spec.Endpoint,
8182
ChannelID: license.Spec.ChannelID,

pact/instance_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
1212
appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types"
1313
"github.com/replicatedhq/replicated-sdk/pkg/k8sutil"
14+
licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper"
1415
"github.com/replicatedhq/replicated-sdk/pkg/report"
1516
mock_store "github.com/replicatedhq/replicated-sdk/pkg/store/mock"
1617
"github.com/replicatedhq/replicated-sdk/pkg/util"
@@ -39,12 +40,12 @@ func TestSendInstanceData(t *testing.T) {
3940
{
4041
name: "successful instance data request",
4142
mockStoreExpectations: func() {
42-
mockStore.EXPECT().GetLicense().Return(&v1beta1.License{
43+
mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{
4344
Spec: v1beta1.LicenseSpec{
4445
LicenseID: "replicated-sdk-instance-customer-0-license",
4546
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
4647
},
47-
})
48+
}})
4849
mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace")
4950
mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id")
5051
mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app")
@@ -91,12 +92,12 @@ func TestSendInstanceData(t *testing.T) {
9192
{
9293
name: "expired license should return error",
9394
mockStoreExpectations: func() {
94-
mockStore.EXPECT().GetLicense().Return(&v1beta1.License{
95+
mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{
9596
Spec: v1beta1.LicenseSpec{
9697
LicenseID: "replicated-sdk-instance-customer-2-license",
9798
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
9899
},
99-
})
100+
}})
100101
mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace")
101102
mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id")
102103
mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app")
@@ -143,12 +144,12 @@ func TestSendInstanceData(t *testing.T) {
143144
{
144145
name: "nonexistent license should return error",
145146
mockStoreExpectations: func() {
146-
mockStore.EXPECT().GetLicense().Return(&v1beta1.License{
147+
mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{
147148
Spec: v1beta1.LicenseSpec{
148149
LicenseID: "replicated-sdk-instance-customer-nonexistent-license",
149150
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
150151
},
151-
})
152+
}})
152153
mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace")
153154
mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id")
154155
mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app")
@@ -195,12 +196,12 @@ func TestSendInstanceData(t *testing.T) {
195196
{
196197
name: "unauthenticated instance data request should return error",
197198
mockStoreExpectations: func() {
198-
mockStore.EXPECT().GetLicense().Return(&v1beta1.License{
199+
mockStore.EXPECT().GetLicense().Return(licensewrapper.LicenseWrapper{V1: &v1beta1.License{
199200
Spec: v1beta1.LicenseSpec{
200201
LicenseID: "replicated-sdk-instance-customer-0-license",
201202
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
202203
},
203-
})
204+
}})
204205
mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace")
205206
mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id")
206207
mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app")

pact/license_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/pact-foundation/pact-go/dsl"
1111
"github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
12+
licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper"
1213
"github.com/replicatedhq/replicated-sdk/pkg/license"
1314
)
1415

@@ -48,7 +49,7 @@ func TestGetLatestLicense(t *testing.T) {
4849
// }
4950

5051
type args struct {
51-
license *v1beta1.License
52+
license licensewrapper.LicenseWrapper
5253
endpoint string
5354
}
5455
tests := []struct {
@@ -96,13 +97,13 @@ func TestGetLatestLicense(t *testing.T) {
9697
{
9798
name: "no license found",
9899
args: args{
99-
license: &v1beta1.License{
100+
license: licensewrapper.LicenseWrapper{V1: &v1beta1.License{
100101
Spec: v1beta1.LicenseSpec{
101102
LicenseID: "not-a-customer-license",
102103
AppSlug: "replicated-sdk-license-app",
103104
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
104105
},
105-
},
106+
}},
106107
},
107108
pactInteraction: func() {
108109
pact.
@@ -126,13 +127,13 @@ func TestGetLatestLicense(t *testing.T) {
126127
{
127128
name: "no app found",
128129
args: args{
129-
license: &v1beta1.License{
130+
license: licensewrapper.LicenseWrapper{V1: &v1beta1.License{
130131
Spec: v1beta1.LicenseSpec{
131132
LicenseID: "replicated-sdk-license-customer-0-license",
132133
AppSlug: "not-an-app",
133134
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
134135
},
135-
},
136+
}},
136137
},
137138
pactInteraction: func() {
138139
pact.
@@ -156,13 +157,13 @@ func TestGetLatestLicense(t *testing.T) {
156157
{
157158
name: "license is not for this app",
158159
args: args{
159-
license: &v1beta1.License{
160+
license: licensewrapper.LicenseWrapper{V1: &v1beta1.License{
160161
Spec: v1beta1.LicenseSpec{
161162
LicenseID: "replicated-sdk-license-customer-0-license",
162163
AppSlug: "replicated-sdk-instance-app",
163164
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
164165
},
165-
},
166+
}},
166167
},
167168
pactInteraction: func() {
168169
pact.
@@ -187,13 +188,13 @@ func TestGetLatestLicense(t *testing.T) {
187188
{
188189
name: "license is archived",
189190
args: args{
190-
license: &v1beta1.License{
191+
license: licensewrapper.LicenseWrapper{V1: &v1beta1.License{
191192
Spec: v1beta1.LicenseSpec{
192193
LicenseID: "replicated-sdk-license-customer-archived-license",
193194
AppSlug: "replicated-sdk-license-app",
194195
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
195196
},
196-
},
197+
}},
197198
},
198199
pactInteraction: func() {
199200
pact.

pkg/apiserver/bootstrap.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55

66
"github.com/cenkalti/backoff/v4"
77
"github.com/pkg/errors"
8-
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
8+
licensewrapper "github.com/replicatedhq/kotskinds/pkg/licensewrapper"
99
"github.com/replicatedhq/replicated-sdk/pkg/appstate"
1010
appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types"
1111
"github.com/replicatedhq/replicated-sdk/pkg/heartbeat"
@@ -56,40 +56,40 @@ func bootstrap(params APIServerParams) error {
5656
}
5757
}
5858

59-
var unverifiedLicense *kotsv1beta1.License
59+
var unverifiedWrapper licensewrapper.LicenseWrapper
6060
if len(params.LicenseBytes) > 0 {
61-
l, err := sdklicense.LoadLicenseFromBytes(params.LicenseBytes)
61+
wrapper, err := sdklicense.LoadLicenseFromBytes(params.LicenseBytes)
6262
if err != nil {
6363
return errors.Wrap(err, "failed to parse license from base64")
6464
}
65-
unverifiedLicense = l
65+
unverifiedWrapper = wrapper
6666
} else if params.IntegrationLicenseID != "" {
67-
l, err := sdklicense.GetLicenseByID(params.IntegrationLicenseID, params.ReplicatedAppEndpoint)
67+
wrapper, err := sdklicense.GetLicenseByID(params.IntegrationLicenseID, params.ReplicatedAppEndpoint)
6868
if err != nil {
6969
return backoff.Permanent(errors.Wrap(err, "failed to get license by id for integration license id"))
7070
}
71-
if l.Spec.LicenseType != "dev" {
71+
if wrapper.GetLicenseType() != "dev" {
7272
return errors.New("integration license must be a dev license")
7373
}
74-
unverifiedLicense = l
74+
unverifiedWrapper = wrapper
7575
}
7676

77-
verifiedLicense, err := sdklicense.VerifySignature(unverifiedLicense)
77+
verifiedWrapper, err := sdklicense.VerifySignature(unverifiedWrapper)
7878
if err != nil {
7979
return backoff.Permanent(errors.Wrap(err, "failed to verify license signature"))
8080
}
8181

8282
if !util.IsAirgap() {
8383
// sync license
84-
licenseData, err := sdklicense.GetLatestLicense(verifiedLicense, params.ReplicatedAppEndpoint)
84+
licenseData, err := sdklicense.GetLatestLicense(verifiedWrapper, params.ReplicatedAppEndpoint)
8585
if err != nil {
8686
return errors.Wrap(err, "failed to get latest license")
8787
}
88-
verifiedLicense = licenseData.License
88+
verifiedWrapper = licenseData.License
8989
}
9090

9191
// check license expiration
92-
expired, err := sdklicense.LicenseIsExpired(verifiedLicense)
92+
expired, err := sdklicense.LicenseIsExpired(verifiedWrapper)
9393
if err != nil {
9494
return errors.Wrap(err, "failed to check if license is expired")
9595
}
@@ -99,16 +99,16 @@ func bootstrap(params APIServerParams) error {
9999

100100
channelID := params.ChannelID
101101
if channelID == "" {
102-
channelID = verifiedLicense.Spec.ChannelID
102+
channelID = verifiedWrapper.GetChannelID()
103103
}
104104

105105
channelName := params.ChannelName
106106
if channelName == "" {
107-
channelName = verifiedLicense.Spec.ChannelName
107+
channelName = verifiedWrapper.GetChannelName()
108108
}
109109

110110
store.InitInMemory(store.InitInMemoryStoreOptions{
111-
License: verifiedLicense,
111+
License: verifiedWrapper,
112112
LicenseFields: params.LicenseFields,
113113
AppName: params.AppName,
114114
ChannelID: channelID,

0 commit comments

Comments
 (0)