Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ linters-settings:
importas:
no-unaliased: true
alias:
- alias: lmanager
pkg: github.com/arangodb/kube-arangodb/pkg/license_manager
- alias: pbImplMetaV1
pkg: github.com/arangodb/kube-arangodb/integrations/meta/v1
- alias: pbMetaV1
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- (Feature) Simplify Operator ID Process
- (Feature) (License) Activation API Integration
- (Feature) (Platform) Chart & Service Kubernetes Events
- (Feature) (Platform) Registry Secret

## [1.3.1](https://github.com/arangodb/kube-arangodb/tree/1.3.1) (2025-10-07)
- (Documentation) Add ArangoPlatformStorage Docs & Examples
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/deployment/v1/deployment_status_license.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ type DeploymentStatusLicense struct {
// Hash Defines the License Hash
Hash string `json:"hash,omitempty"`

// InputHash Defines the Input License Hash
InputHash string `json:"inputHash,omitempty"`

// Expires Defines the expiration time of the License
Expires meta.Time `json:"expires,omitempty"`

Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/deployment/v2alpha1/deployment_status_license.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ type DeploymentStatusLicense struct {
// Hash Defines the License Hash
Hash string `json:"hash,omitempty"`

// InputHash Defines the Input License Hash
InputHash string `json:"inputHash,omitempty"`

// Expires Defines the expiration time of the License
Expires meta.Time `json:"expires,omitempty"`

Expand Down
7 changes: 7 additions & 0 deletions pkg/deployment/pod/encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ func GetEncryptionFolderSecretName(name string) string {
return n
}

// GetLicenseRegistryCredentialsSecretName returns the secret name for storing registry credentials used to pull licensed images
func GetLicenseRegistryCredentialsSecretName(name string) string {
n := fmt.Sprintf("%s-rlm", name)

return n
}

func IsEncryptionEnabled(i Input) bool {
return i.Deployment.RocksDB.IsEncrypted()
}
Expand Down
56 changes: 53 additions & 3 deletions pkg/deployment/reconcile/action_license_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@ import (

api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/client"
"github.com/arangodb/kube-arangodb/pkg/license_manager"
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
lmanager "github.com/arangodb/kube-arangodb/pkg/license_manager"
"github.com/arangodb/kube-arangodb/pkg/platform/inventory"
"github.com/arangodb/kube-arangodb/pkg/util"
utilConstants "github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/globals"
ugrpc "github.com/arangodb/kube-arangodb/pkg/util/grpc"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/patcher"
)

func newLicenseGenerateAction(action api.Action, actionCtx ActionContext) Action {
Expand Down Expand Up @@ -84,7 +87,7 @@ func (a *actionLicenseGenerate) Start(ctx context.Context) (bool, error) {
return true, nil
}

var req license_manager.LicenseRequest
var req lmanager.LicenseRequest
did, err := inventory.ExtractDeploymentID(ctx, c.Connection())
if err != nil {
a.log.Err(err).Error("Unable to get deployment id")
Expand Down Expand Up @@ -112,7 +115,7 @@ func (a *actionLicenseGenerate) Start(ctx context.Context) (bool, error) {
req.TTL = util.NewType(ugrpc.NewObject(durationpb.New(q.Duration)))
}

lm, err := license_manager.NewClient(license_manager.ArangoLicenseManagerEndpoint, l.API.ClientID, l.API.ClientSecret)
lm, err := lmanager.NewClient(lmanager.ArangoLicenseManagerEndpoint, l.API.ClientID, l.API.ClientSecret)
if err != nil {
a.log.Err(err).Error("Unable to create inventory client")
return true, nil
Expand Down Expand Up @@ -167,13 +170,60 @@ func (a *actionLicenseGenerate) Start(ctx context.Context) (bool, error) {
expires = time.Now().Add(time.Duration(math.Round(float64(time.Until(license.Expires())) * api.LicenseExpirationGraceRatio)))
}

cache := a.actionCtx.ACS().CurrentClusterCache()

if s, ok := cache.Secret().V1().GetSimple(pod.GetLicenseRegistryCredentialsSecretName(a.actionCtx.GetName())); ok {
if string(util.Optional(s.Data, utilConstants.ChecksumKey, []byte{})) != l.API.Hash() {
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checksum comparison is duplicated in both the if and else branches (lines 176 and 246 in plan_builder_license.go). Consider extracting this logic into a helper function to improve maintainability and ensure consistent validation logic across the codebase.

Copilot uses AI. Check for mistakes.
// Update

token, err := lm.RegistryConfig(ctx, lmanager.ArangoLicenseManagerEndpoint, l.API.ClientID, &l.API.ClientSecret, lmanager.StageDev, lmanager.StageQA, lmanager.StagePrd)
if err != nil {
a.log.Err(err).Debug("Failed to generate License Registry")
return true, nil
}

if _, _, err := patcher.Patcher[*core.Secret](ctx, cache.Client().Kubernetes().CoreV1().Secrets(a.actionCtx.GetNamespace()), s, meta.PatchOptions{},
patcher.PatchSecretData(map[string][]byte{
core.DockerConfigJsonKey: token,
utilConstants.ChecksumKey: []byte(l.API.Hash()),
})); err != nil {
a.log.Err(err).Debug("Failed to patch License Secret")
return true, nil
}
}
} else {
token, err := lm.RegistryConfig(ctx, lmanager.ArangoLicenseManagerEndpoint, l.API.ClientID, &l.API.ClientSecret, lmanager.StageDev, lmanager.StageQA, lmanager.StagePrd)
if err != nil {
a.log.Err(err).Debug("Failed to generate License Registry")
return true, nil
}

if _, err := cache.Client().Kubernetes().CoreV1().Secrets(a.actionCtx.GetNamespace()).Create(ctx, &core.Secret{
ObjectMeta: meta.ObjectMeta{
Name: pod.GetLicenseRegistryCredentialsSecretName(a.actionCtx.GetName()),
OwnerReferences: []meta.OwnerReference{
a.actionCtx.GetAPIObject().AsOwner(),
},
},
Data: map[string][]byte{
core.DockerConfigJsonKey: token,
utilConstants.ChecksumKey: []byte(l.API.Hash()),
},
Type: core.SecretTypeDockerConfigJson,
}, meta.CreateOptions{}); err != nil {
Comment on lines +201 to +213
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Secret creation logic (lines 201-213) duplicates the data structure from the patch operation (lines 186-189). Consider extracting a helper function that constructs the Secret data map to avoid duplication and ensure consistency between create and update operations.

Copilot uses AI. Check for mistakes.
a.log.Err(err).Debug("Failed to create License Secret")
return true, nil
}
}

if err := a.actionCtx.WithStatusUpdate(ctx, func(s *api.DeploymentStatus) bool {
s.License = &api.DeploymentStatusLicense{
ID: generatedLicense.ID,
Hash: license.Hash,
Expires: meta.Time{Time: license.Expires()},
Mode: api.LicenseModeAPI,
Regenerate: meta.Time{Time: expires},
InputHash: l.API.Hash(),
}
return true
}); err != nil {
Expand Down
7 changes: 4 additions & 3 deletions pkg/deployment/reconcile/action_license_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,10 @@ func (a *actionLicenseSet) Start(ctx context.Context) (bool, error) {

if err := a.actionCtx.WithStatusUpdate(ctx, func(s *api.DeploymentStatus) bool {
s.License = &api.DeploymentStatusLicense{
Hash: license.Hash,
Expires: meta.Time{Time: license.Expires()},
Mode: api.LicenseModeKey,
Hash: license.Hash,
Expires: meta.Time{Time: license.Expires()},
Mode: api.LicenseModeKey,
InputHash: l.V2.V2Hash(),
}
return true
}); err != nil {
Expand Down
20 changes: 19 additions & 1 deletion pkg/deployment/reconcile/plan_builder_license.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import (
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/actions"
"github.com/arangodb/kube-arangodb/pkg/deployment/client"
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
sharedReconcile "github.com/arangodb/kube-arangodb/pkg/deployment/reconcile/shared"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/arangod"
utilConstants "github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/globals"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
Expand Down Expand Up @@ -165,7 +168,7 @@ func (r *Reconciler) updateClusterLicenseKey(ctx context.Context, spec api.Deplo
return nil
}

if status.License.Hash != license.Hash {
if status.License.Hash != license.Hash || status.License.InputHash != l.V2.V2Hash() {
return api.Plan{actions.NewClusterAction(api.ActionTypeLicenseClean, "Removing license reference - Invalid Hash")}
}

Expand Down Expand Up @@ -223,6 +226,11 @@ func (r *Reconciler) updateClusterLicenseAPI(ctx context.Context, spec api.Deplo
return nil
}

if status.License.InputHash != l.API.Hash() {
// Invalid hash, cleanup
return api.Plan{actions.NewClusterAction(api.ActionTypeLicenseClean, "Removing license reference - Invalid Input")}
}

if currentLicense.Hash != status.License.Hash {
// Invalid hash, cleanup
return api.Plan{actions.NewClusterAction(api.ActionTypeLicenseClean, "Removing license reference - Invalid Hash")}
Expand All @@ -232,5 +240,15 @@ func (r *Reconciler) updateClusterLicenseAPI(ctx context.Context, spec api.Deplo
return api.Plan{actions.NewClusterAction(api.ActionTypeLicenseClean, "Removing license reference - Regeneration Required")}
}

cache := r.context.ACS().CurrentClusterCache()

if s, ok := cache.Secret().V1().GetSimple(pod.GetLicenseRegistryCredentialsSecretName(r.context.GetName())); ok {
if string(util.Optional(s.Data, utilConstants.ChecksumKey, []byte{})) != l.API.Hash() {
return api.Plan{actions.NewClusterAction(api.ActionTypeLicenseClean, "Removing license reference - Registry Change Required")}
}
} else {
return api.Plan{actions.NewClusterAction(api.ActionTypeLicenseClean, "Removing license reference - Registry Change Required")}
}

return nil
}
18 changes: 18 additions & 0 deletions pkg/deployment/resources/arango_profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
schedulerPodResourcesApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/pod/resources"
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
integrationsSidecar "github.com/arangodb/kube-arangodb/pkg/integrations/sidecar"
"github.com/arangodb/kube-arangodb/pkg/metrics"
"github.com/arangodb/kube-arangodb/pkg/util"
Expand Down Expand Up @@ -190,6 +191,7 @@ func (r *Resources) EnsureArangoProfiles(ctx context.Context, cachedStatus inspe
r.arangoDeploymentCATemplate(),
r.templateKubernetesEnvs(),
r.templateResourceEnvs(),
r.templateImagePullSecrets(),
)
if err != nil {
return "", nil, err
Expand Down Expand Up @@ -361,6 +363,22 @@ func (r *Resources) templateKubernetesEnvs() *schedulerApi.ProfileTemplate {
}
}

func (r *Resources) templateImagePullSecrets() *schedulerApi.ProfileTemplate {
if _, ok := r.context.ACS().CurrentClusterCache().Secret().V1().GetSimple(pod.GetLicenseRegistryCredentialsSecretName(r.name)); ok {
return &schedulerApi.ProfileTemplate{
Pod: &schedulerPodApi.Pod{
Image: &schedulerPodResourcesApi.Image{
ImagePullSecrets: []string{
pod.GetLicenseRegistryCredentialsSecretName(r.name),
},
},
},
}
}

return &schedulerApi.ProfileTemplate{}
}

func (r *Resources) templateResourceEnvs() *schedulerApi.ProfileTemplate {
return &schedulerApi.ProfileTemplate{
Container: &schedulerApi.ProfileContainerTemplate{
Expand Down
28 changes: 28 additions & 0 deletions pkg/license_manager/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package license_manager

import (
"context"
"encoding/json"
"fmt"
goHttp "net/http"

Expand Down Expand Up @@ -76,6 +77,7 @@ type Client interface {
License(ctx context.Context, req LicenseRequest) (LicenseResponse, error)

Registry(ctx context.Context) (RegistryResponse, error)
RegistryConfig(ctx context.Context, endpoint, id string, token *string, stages ...Stage) ([]byte, error)
}

type LicenseRequest struct {
Expand All @@ -98,6 +100,32 @@ type client struct {
conn driver.Connection
}

func (c client) RegistryConfig(ctx context.Context, endpoint, id string, token *string, stages ...Stage) ([]byte, error) {
var t string

if token != nil {
t = *token
} else {
tk, err := c.Registry(ctx)
if err != nil {
return nil, err
}
t = tk.Token
}

r, err := NewRegistryAuth(endpoint, id, t, stages...)
if err != nil {
return nil, err
}

data, err := json.Marshal(r)
if err != nil {
return nil, err
}

return data, nil
}

func (c client) License(ctx context.Context, req LicenseRequest) (LicenseResponse, error) {
return arangod.PostRequest[LicenseRequest, LicenseResponse](ctx, c.conn, req, "_api", "v1", "license").AcceptCode(200).Response()
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/platform/license_activate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"github.com/spf13/cobra"

"github.com/arangodb/kube-arangodb/pkg/deployment/client"
"github.com/arangodb/kube-arangodb/pkg/license_manager"
lmanager "github.com/arangodb/kube-arangodb/pkg/license_manager"
"github.com/arangodb/kube-arangodb/pkg/logging"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/cli"
Expand Down Expand Up @@ -84,7 +84,7 @@ func licenseActivateRun(cmd *cobra.Command, args []string) error {
}
}

func licenseActivateExecute(cmd *cobra.Command, logger logging.Logger, mc license_manager.Client) error {
func licenseActivateExecute(cmd *cobra.Command, logger logging.Logger, mc lmanager.Client) error {
conn, err := flagDeployment.Connection(cmd)
if err != nil {
return err
Expand All @@ -103,7 +103,7 @@ func licenseActivateExecute(cmd *cobra.Command, logger logging.Logger, mc licens

l.Info("Generating License")

lic, err := mc.License(cmd.Context(), license_manager.LicenseRequest{
lic, err := mc.License(cmd.Context(), lmanager.LicenseRequest{
DeploymentID: util.NewType(inv.DeploymentId),
Inventory: util.NewType(ugrpc.NewObject(inv)),
})
Expand Down
4 changes: 2 additions & 2 deletions pkg/platform/license_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

"github.com/spf13/cobra"

"github.com/arangodb/kube-arangodb/pkg/license_manager"
lmanager "github.com/arangodb/kube-arangodb/pkg/license_manager"
"github.com/arangodb/kube-arangodb/pkg/platform/inventory"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/cli"
Expand Down Expand Up @@ -74,7 +74,7 @@ func licenseGenerateRun(cmd *cobra.Command, args []string) error {

l.Info("Generating License")

lic, err := mc.License(cmd.Context(), license_manager.LicenseRequest{
lic, err := mc.License(cmd.Context(), lmanager.LicenseRequest{
DeploymentID: util.NewType(did),
Inventory: util.NewType(ugrpc.NewObject(inv)),
})
Expand Down
17 changes: 2 additions & 15 deletions pkg/platform/license_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package platform

import (
"encoding/json"
"fmt"
"os"

Expand All @@ -30,7 +29,7 @@ import (
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"

manager2 "github.com/arangodb/kube-arangodb/pkg/license_manager"
lmanager "github.com/arangodb/kube-arangodb/pkg/license_manager"
"github.com/arangodb/kube-arangodb/pkg/util/cli"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
)
Expand Down Expand Up @@ -86,21 +85,9 @@ func licenseSecretRun(cmd *cobra.Command, args []string) error {
return err
}

secret, err := mc.Registry(cmd.Context())
if err != nil {
return err
}

logger.Info("Creating new Registry Token")

r, err := manager2.NewRegistryAuth(endpoint, id, secret.Token, manager2.ParseStages(stages...)...)
if err != nil {
return err
}

logger.Info("New Registry Token Created")

data, err := json.Marshal(r)
data, err := mc.RegistryConfig(cmd.Context(), endpoint, id, nil, lmanager.ParseStages(stages...)...)
if err != nil {
return err
}
Expand Down
Loading