1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Feature] License ArangoDeployment Fetcher (#1485)

This commit is contained in:
Adam Janikowski 2023-11-13 13:14:35 +01:00 committed by GitHub
parent 828350d86a
commit 5ebc821941
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 122 additions and 388 deletions

View file

@ -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

View file

@ -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

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
)

View file

@ -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)
}

View file

@ -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
}