diff --git a/CHANGELOG.md b/CHANGELOG.md index cf38a6eda..80841b236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - (Bugfix) Proper handling of --agency.retries argument - (Documentation) Do not use field type name for field URL hash - (Maintenance) Bump Go to 1.20.11 +- (Feature) License ArangoDeployment Fetcher ## [1.2.35](https://github.com/arangodb/kube-arangodb/tree/1.2.35) (2023-11-06) - (Maintenance) Update go-driver to v1.6.0, update IsNotFound() checks diff --git a/cmd/cmd.go b/cmd/cmd.go index a043f5347..e62092f30 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -52,7 +52,6 @@ import ( "github.com/arangodb/kube-arangodb/pkg/deployment/features" "github.com/arangodb/kube-arangodb/pkg/deployment/reconcile" "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/scheme" - "github.com/arangodb/kube-arangodb/pkg/license" "github.com/arangodb/kube-arangodb/pkg/logging" "github.com/arangodb/kube-arangodb/pkg/metrics/collector" "github.com/arangodb/kube-arangodb/pkg/operator" @@ -172,8 +171,6 @@ var ( backupProbe probe.ReadyProbe appsProbe probe.ReadyProbe k2KClusterSyncProbe probe.ReadyProbe - - licenseConfig license.Config ) func init() { @@ -239,9 +236,6 @@ func init() { if err := reconcile.ActionsConfigGlobal.Init(&cmdMain); err != nil { panic(err.Error()) } - if err := licenseConfig.Init(&cmdMain); err != nil { - panic(err.Error()) - } } func Execute() int { @@ -315,10 +309,6 @@ func executeMain(cmd *cobra.Command, args []string) { }) } - if err := licenseConfig.Enable(); err != nil { - logger.Err(err).Fatal("Failed to License checker process") - } - logger.Info("nice to meet you") // Print all enabled featured diff --git a/pkg/license/config.go b/pkg/license/config.go deleted file mode 100644 index 81cd7ea05..000000000 --- a/pkg/license/config.go +++ /dev/null @@ -1,91 +0,0 @@ -// -// DISCLAIMER -// -// Copyright 2023 ArangoDB GmbH, Cologne, Germany -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Copyright holder is ArangoDB GmbH, Cologne, Germany -// - -package license - -import ( - "os" - "time" - - "github.com/spf13/cobra" - - "github.com/arangodb/kube-arangodb/pkg/util/errors" - "github.com/arangodb/kube-arangodb/pkg/util/kclient" -) - -type Config struct { - Enabled bool - - Secret struct { - Namespace, Name, Key, ClientFactory string - } - - Env struct { - Env string - } - - Type string - - RefreshInterval time.Duration - RefreshTimeout time.Duration -} - -func (c *Config) Init(cmd *cobra.Command) error { - f := cmd.PersistentFlags() - - f.BoolVar(&c.Enabled, "license.enabled", true, "Define if LicenseManager is enabled") - f.StringVar(&c.Type, "license.type", "secret", "Define type of the license fetch, possible values are secret or env") - f.StringVar(&c.Secret.Namespace, "license.secret.namespace", "", "Define Secret Namespace for the Secret type of LicenseManager") - f.StringVar(&c.Secret.Name, "license.secret.name", "", "Define Secret Name for the Secret type of LicenseManager") - f.StringVar(&c.Secret.Key, "license.secret.key", "license", "Define Secret Key for the Secret type of LicenseManager") - f.StringVar(&c.Secret.ClientFactory, "license.secret.client-factory", "", "Define K8S Client Factory for the Secret type of LicenseManager") - f.StringVar(&c.Env.Env, "license.env.name", "ARANGODB_LICENSE", "Define Environment Variable name for the Env type of LicenseManager") - f.DurationVar(&c.RefreshInterval, "license.refresh.interval", 30*time.Second, "Refresh interval for LicenseManager") - f.DurationVar(&c.RefreshTimeout, "license.refresh.timeout", 3*time.Second, "Refresh timeout for LicenseManager") - - if err := f.MarkHidden("license.enabled"); err != nil { - return err - } - if err := f.MarkHidden("license.secret.client-factory"); err != nil { - return err - } - - return nil -} - -func (c Config) Enable() error { - if !c.Enabled { - return initManager(c, NewDisabledLoader()) - } - - switch c.Type { - case "secret": - return initManager(c, NewSecretLoader(kclient.GetFactory(c.Secret.ClientFactory), c.Secret.Namespace, c.Secret.Name, c.Secret.Key)) - - case "env": - if l, ok := os.LookupEnv(c.Env.Env); ok { - return initManager(c, NewConstantLoader(l)) - } - - return initManager(c, NewDisabledLoader()) - default: - return errors.Newf("Unsupported type for license.type: %s", c.Type) - } -} diff --git a/pkg/license/license.community.go b/pkg/license/license.community.go index 8ed4c176d..81340059f 100644 --- a/pkg/license/license.community.go +++ b/pkg/license/license.community.go @@ -20,15 +20,23 @@ package license -import "github.com/arangodb/kube-arangodb/pkg/util/assertion" +import ( + "context" -func checkLicense(license string) License { + "github.com/arangodb/kube-arangodb/pkg/util/assertion" +) + +func NewLicense(loader Loader) License { return emptyLicense{} } type emptyLicense struct { } +func (e emptyLicense) Refresh(ctx context.Context) error { + return nil +} + // Validate for the community returns that license is always missing, as it should be not used func (e emptyLicense) Validate(feature Feature, subFeatures ...Feature) Status { assertion.Assert(true, assertion.CommunityLicenseCheckKey, "Feature %s has been validated in the community version", feature) diff --git a/pkg/license/license.go b/pkg/license/license.go index 726730c8b..2903430b6 100644 --- a/pkg/license/license.go +++ b/pkg/license/license.go @@ -20,6 +20,10 @@ package license +import ( + "context" +) + type Status int const ( @@ -79,4 +83,6 @@ type License interface { // -- for each subFeature defined in subFeatures: // --- checks if subFeature or '*' is in the list of License Feature enabled SubFeatures Validate(feature Feature, subFeatures ...Feature) Status + + Refresh(ctx context.Context) error } diff --git a/pkg/license/loader_arangodeployment.go b/pkg/license/loader_arangodeployment.go new file mode 100644 index 000000000..7a598d667 --- /dev/null +++ b/pkg/license/loader_arangodeployment.go @@ -0,0 +1,101 @@ +// +// DISCLAIMER +// +// Copyright 2023 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package license + +import ( + "context" + "encoding/base64" + + "k8s.io/apimachinery/pkg/api/errors" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/arangodb/kube-arangodb/pkg/util/constants" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + "github.com/arangodb/kube-arangodb/pkg/util/kclient" +) + +func NewArengoDeploymentLicenseLoader(factory kclient.Factory, namespace, name string) Loader { + return arangoDeploymentLicenseLoader{ + factory: factory, + namespace: namespace, + name: name, + } +} + +type arangoDeploymentLicenseLoader struct { + factory kclient.Factory + + namespace, name string +} + +func (a arangoDeploymentLicenseLoader) Refresh(ctx context.Context) (string, bool, error) { + client, ok := a.factory.Client() + if !ok { + return "", false, nil + } + + deployment, err := client.Arango().DatabaseV1().ArangoDeployments(a.namespace).Get(ctx, a.name, meta.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return "", false, nil + } + + return "", false, err + } + + spec := deployment.GetAcceptedSpec() + + if !spec.License.HasSecretName() { + return "", false, nil + } + + secret, err := client.Kubernetes().CoreV1().Secrets(deployment.GetNamespace()).Get(ctx, spec.License.GetSecretName(), meta.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return "", false, nil + } + + return "", false, err + } + + var licenseData []byte + + if lic, ok := secret.Data[constants.SecretKeyV2License]; ok { + licenseData = lic + } else if lic2, ok := secret.Data[constants.SecretKeyV2Token]; ok { + licenseData = lic2 + } + + if len(licenseData) == 0 { + return "", false, nil + } + + if !k8sutil.IsJSON(licenseData) { + d, err := base64.StdEncoding.DecodeString(string(licenseData)) + if err != nil { + return "", false, err + } + + licenseData = d + } + + return string(licenseData), true, nil +} diff --git a/pkg/license/loader_constant.go b/pkg/license/loader_constant.go deleted file mode 100644 index 0efa50268..000000000 --- a/pkg/license/loader_constant.go +++ /dev/null @@ -1,35 +0,0 @@ -// -// DISCLAIMER -// -// Copyright 2023 ArangoDB GmbH, Cologne, Germany -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Copyright holder is ArangoDB GmbH, Cologne, Germany -// - -package license - -import ( - "context" -) - -func NewConstantLoader(license string) Loader { - return loaderConstant(license) -} - -type loaderConstant string - -func (l loaderConstant) Refresh(ctx context.Context) (string, bool, error) { - return string(l), false, nil -} diff --git a/pkg/license/loader_disabled.go b/pkg/license/loader_disabled.go deleted file mode 100644 index 15fe08e41..000000000 --- a/pkg/license/loader_disabled.go +++ /dev/null @@ -1,36 +0,0 @@ -// -// DISCLAIMER -// -// Copyright 2023 ArangoDB GmbH, Cologne, Germany -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Copyright holder is ArangoDB GmbH, Cologne, Germany -// - -package license - -import ( - "context" -) - -func NewDisabledLoader() Loader { - return loaderDisabled{} -} - -type loaderDisabled struct { -} - -func (l loaderDisabled) Refresh(ctx context.Context) (string, bool, error) { - return "", false, nil -} diff --git a/pkg/license/loader_secret.go b/pkg/license/loader_secret.go deleted file mode 100644 index 9bb519a5e..000000000 --- a/pkg/license/loader_secret.go +++ /dev/null @@ -1,72 +0,0 @@ -// -// DISCLAIMER -// -// Copyright 2023 ArangoDB GmbH, Cologne, Germany -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Copyright holder is ArangoDB GmbH, Cologne, Germany -// - -package license - -import ( - "context" - - "k8s.io/apimachinery/pkg/api/errors" - meta "k8s.io/apimachinery/pkg/apis/meta/v1" - - errors2 "github.com/arangodb/kube-arangodb/pkg/util/errors" - "github.com/arangodb/kube-arangodb/pkg/util/kclient" -) - -func NewSecretLoader(factory kclient.Factory, namespace, name, key string) Loader { - return secretLoader{ - factory: factory, - namespace: namespace, - name: name, - key: key, - } -} - -type secretLoader struct { - factory kclient.Factory - namespace, name, key string -} - -func (s secretLoader) Refresh(ctx context.Context) (string, bool, error) { - client, ok := s.factory.Client() - if !ok { - return "", false, errors2.Newf("Client is not yet ready") - } - - secret, err := client.Kubernetes().CoreV1().Secrets(s.namespace).Get(ctx, s.name, meta.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return "", false, nil - } - - return "", false, err - } - - if len(secret.Data) == 0 { - return "", false, nil - } - - license, ok := secret.Data[s.key] - if !ok { - return "", false, nil - } - - return string(license), true, nil -} diff --git a/pkg/license/logger.go b/pkg/license/logger.go deleted file mode 100644 index b7a7809ed..000000000 --- a/pkg/license/logger.go +++ /dev/null @@ -1,27 +0,0 @@ -// -// DISCLAIMER -// -// Copyright 2023 ArangoDB GmbH, Cologne, Germany -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Copyright holder is ArangoDB GmbH, Cologne, Germany -// - -package license - -import "github.com/arangodb/kube-arangodb/pkg/logging" - -var ( - logger = logging.Global().RegisterAndGetLogger("license", logging.Info) -) diff --git a/pkg/license/manager.go b/pkg/license/manager.go deleted file mode 100644 index f2d0eeb21..000000000 --- a/pkg/license/manager.go +++ /dev/null @@ -1,111 +0,0 @@ -// -// DISCLAIMER -// -// Copyright 2023 ArangoDB GmbH, Cologne, Germany -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Copyright holder is ArangoDB GmbH, Cologne, Germany -// - -package license - -import ( - "context" - "sync" - "time" - - "github.com/arangodb/kube-arangodb/pkg/util/errors" - "github.com/arangodb/kube-arangodb/pkg/util/shutdown" -) - -var ( - lock sync.Mutex - managerInstance Manager -) - -func initManager(config Config, loader Loader) error { - lock.Lock() - defer lock.Unlock() - - if managerInstance != nil { - return errors.Newf("Manager is already initialised") - } - - mgr := &manager{ - loader: loader, - config: config, - license: StatusMissing, - } - - go mgr.run(shutdown.Channel()) - - managerInstance = mgr - - return nil -} - -func ManagerInstance() Manager { - lock.Lock() - defer lock.Unlock() - - if managerInstance == nil { - panic("Manager not yet initialised") - } - - return managerInstance -} - -type Manager interface { -} - -type manager struct { - config Config - loader Loader - - license License -} - -func (m *manager) run(stopC <-chan struct{}) { - t := time.NewTicker(m.config.RefreshInterval) - defer t.Stop() - - for { - select { - case <-stopC: - return - case <-t.C: - // Lets do the refresh - logger.Debug("Refresh process started") - - license, ok, err := m.loadLicense() - if err != nil { - logger.Err(err).Warn("Unable to load license") - continue - } - - if !ok { - logger.Debug("License is missing") - continue - } - - m.license = checkLicense(license) - } - } -} - -func (m *manager) loadLicense() (string, bool, error) { - ctx, c := context.WithTimeout(context.Background(), m.config.RefreshTimeout) - defer c() - return m.loader.Refresh(ctx) -} diff --git a/pkg/util/k8sutil/license.go b/pkg/util/k8sutil/license.go index 8c05b1a93..af71c8c56 100644 --- a/pkg/util/k8sutil/license.go +++ b/pkg/util/k8sutil/license.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -59,14 +59,14 @@ func GetLicenseFromSecret(secret secret.Inspector, name string) (LicenseSecret, if v1, ok1 := s.Data[constants.SecretKeyV2License]; ok1 { // some customers put the raw JSON-encoded value, but operator and DB servers expect the base64-encoded value - if isJSONBytes(v1) { + if IsJSON(v1) { l.V2 = License(base64.StdEncoding.EncodeToString(v1)) } else { l.V2 = License(v1) } } else if v2, ok2 := s.Data[constants.SecretKeyV2Token]; ok2 { // some customers put the raw JSON-encoded value, but operator and DB servers expect the base64-encoded value - if isJSONBytes(v2) { + if IsJSON(v2) { l.V2 = License(base64.StdEncoding.EncodeToString(v2)) } else { l.V2 = License(v2) @@ -79,7 +79,7 @@ func GetLicenseFromSecret(secret secret.Inspector, name string) (LicenseSecret, return l, nil } -func isJSONBytes(s []byte) bool { +func IsJSON(s []byte) bool { var js json.RawMessage return json.Unmarshal(s, &js) == nil }