Skip to content

Commit 38dcb31

Browse files
authored
Merge pull request #307 from arangodb/feature/license-key
License Key
2 parents fb851e3 + ae3190b commit 38dcb31

29 files changed

+360
-107
lines changed

Jenkinsfile.groovy

+6-1
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,16 @@ def buildTestSteps(Map myParams, String kubeConfigRoot, String kubeconfig) {
8181
return {
8282
timestamps {
8383
timeout(time: myParams.LONG ? 180 : 30) {
84-
withCredentials([string(credentialsId: 'ENTERPRISEIMAGE', variable: 'DEFAULTENTERPRISEIMAGE')]) {
84+
withCredentials([
85+
string(credentialsId: 'ENTERPRISEIMAGE', variable: 'DEFAULTENTERPRISEIMAGE'),
86+
string(credentialsId: 'ENTERPRISELICENSE', variable: 'DEFAULTENTERPRISELICENSE'),
87+
]) {
8588
withEnv([
8689
"CLEANDEPLOYMENTS=1",
8790
"DEPLOYMENTNAMESPACE=${myParams.TESTNAMESPACE}-${env.GIT_COMMIT}",
8891
"DOCKERNAMESPACE=${myParams.DOCKERNAMESPACE}",
8992
"ENTERPRISEIMAGE=${myParams.ENTERPRISEIMAGE}",
93+
"ENTERPRISELICENSE=${myParams.ENTERPRISELICENSE}",
9094
"ARANGODIMAGE=${myParams.ARANGODIMAGE}",
9195
"IMAGETAG=jenkins-test",
9296
"KUBECONFIG=${kubeConfigRoot}/${kubeconfig}",
@@ -132,6 +136,7 @@ pipeline {
132136
string(name: 'TESTNAMESPACE', defaultValue: 'jenkins', description: 'TESTNAMESPACE sets the kubernetes namespace to ru tests in (this must be short!!)', )
133137
string(name: 'ENTERPRISEIMAGE', defaultValue: '', description: 'ENTERPRISEIMAGE sets the docker image used for enterprise tests', )
134138
string(name: 'ARANGODIMAGE', defaultValue: '', description: 'ARANGODIMAGE sets the docker image used for tests (except enterprise and update tests)', )
139+
string(name: 'ENTERPRISELICENSE', defaultValue: '', description: 'ENTERPRISELICENSE sets the enterprise license key for enterprise tests', )
135140
string(name: 'TESTOPTIONS', defaultValue: '', description: 'TESTOPTIONS is used to pass additional test options to the integration test', )
136141
}
137142
stages {

Makefile

+5-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ endif
7373
ifndef ENTERPRISEIMAGE
7474
ENTERPRISEIMAGE := $(DEFAULTENTERPRISEIMAGE)
7575
endif
76+
ifndef ENTERPRISELICENSE
77+
ENTERPRISELICENSE := $(DEFAULTENTERPRISELICENSE)
78+
endif
7679
DASHBOARDBUILDIMAGE := kube-arangodb-dashboard-builder
7780

7881
ifndef ALLOWCHAOS
@@ -307,7 +310,8 @@ endif
307310
kubectl apply -f $(MANIFESTPATHDEPLOYMENTREPLICATION)
308311
kubectl apply -f $(MANIFESTPATHTEST)
309312
$(ROOTDIR)/scripts/kube_create_storage.sh $(DEPLOYMENTNAMESPACE)
310-
$(ROOTDIR)/scripts/kube_run_tests.sh $(DEPLOYMENTNAMESPACE) $(TESTIMAGE) "$(ARANGODIMAGE)" "$(ENTERPRISEIMAGE)" $(TESTTIMEOUT) $(TESTLENGTHOPTIONS)
313+
$(ROOTDIR)/scripts/kube_create_license_key_secret.sh "$(DEPLOYMENTNAMESPACE)" '$(ENTERPRISELICENSE)'
314+
$(ROOTDIR)/scripts/kube_run_tests.sh $(DEPLOYMENTNAMESPACE) $(TESTIMAGE) "$(ARANGODIMAGE)" '$(ENTERPRISEIMAGE)' $(TESTTIMEOUT) $(TESTLENGTHOPTIONS)
311315

312316
$(DURATIONTESTBIN): $(GOBUILDDIR) $(SOURCES)
313317
@mkdir -p $(BINDIR)

dashboard/assets.go

+65-65
Large diffs are not rendered by default.

docs/Manual/Deployment/Kubernetes/DeploymentResource.md

+6
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,12 @@ The default is `false`.
332332

333333
This setting cannot be changed after the deployment has been created.
334334

335+
### `spec.license.secretName: string`
336+
337+
This setting specifies the name of a kubernetes `Secret` that contains
338+
the license key token used for enterprise images. This value is not used for
339+
the community edition.
340+
335341
### `spec.<group>.count: number`
336342

337343
This setting specifies the number of servers to start for the given group.

pkg/apis/deployment/v1alpha/deployment_spec.go

+5
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ type DeploymentSpec struct {
6060
Authentication AuthenticationSpec `json:"auth"`
6161
TLS TLSSpec `json:"tls"`
6262
Sync SyncSpec `json:"sync"`
63+
License LicenseSpec `json:"license"`
6364

6465
Single ServerGroupSpec `json:"single"`
6566
Agents ServerGroupSpec `json:"agents"`
@@ -204,6 +205,7 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) {
204205
if s.DisableIPv6 == nil {
205206
s.DisableIPv6 = util.NewBoolOrNil(source.DisableIPv6)
206207
}
208+
s.License.SetDefaultsFrom(source.License)
207209
s.ExternalAccess.SetDefaultsFrom(source.ExternalAccess)
208210
s.RocksDB.SetDefaultsFrom(source.RocksDB)
209211
s.Authentication.SetDefaultsFrom(source.Authentication)
@@ -272,6 +274,9 @@ func (s *DeploymentSpec) Validate() error {
272274
if err := s.Chaos.Validate(); err != nil {
273275
return maskAny(errors.Wrap(err, "spec.chaos"))
274276
}
277+
if err := s.License.Validate(); err != nil {
278+
return maskAny(errors.Wrap(err, "spec.licenseKey"))
279+
}
275280
return nil
276281
}
277282

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package v1alpha
22+
23+
import (
24+
"github.com/arangodb/kube-arangodb/pkg/util"
25+
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
26+
)
27+
28+
// LicenseSpec holds the license related information
29+
type LicenseSpec struct {
30+
SecretName *string `json:"secretName,omitempty"`
31+
}
32+
33+
// HasSecretName returns true if a license key secret name was set
34+
func (s LicenseSpec) HasSecretName() bool {
35+
return s.SecretName != nil
36+
}
37+
38+
// GetSecretName returns the license key if set. Empty string otherwise.
39+
func (s LicenseSpec) GetSecretName() string {
40+
if s.HasSecretName() {
41+
return *s.SecretName
42+
}
43+
44+
return ""
45+
}
46+
47+
// Validate validates the LicenseSpec
48+
func (s LicenseSpec) Validate() error {
49+
if s.HasSecretName() {
50+
if err := k8sutil.ValidateResourceName(s.GetSecretName()); err != nil {
51+
return err
52+
}
53+
}
54+
55+
return nil
56+
}
57+
58+
// SetDefaultsFrom fills all values not set in s with values from other
59+
func (s LicenseSpec) SetDefaultsFrom(other LicenseSpec) {
60+
if !s.HasSecretName() {
61+
s.SecretName = util.NewStringOrNil(other.SecretName)
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package v1alpha
2+
3+
import (
4+
"testing"
5+
6+
"github.com/arangodb/kube-arangodb/pkg/util"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestLicenseSpecValidation(t *testing.T) {
11+
assert.Nil(t, LicenseSpec{SecretName: nil}.Validate())
12+
assert.Nil(t, LicenseSpec{SecretName: util.NewString("some-name")}.Validate())
13+
14+
assert.Error(t, LicenseSpec{SecretName: util.NewString("@@")}.Validate())
15+
}

pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/deployment/context_impl.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,6 @@ func (d *Deployment) DeleteSecret(secretName string) error {
377377

378378
// GetExpectedPodArguments creates command line arguments for a server in the given group with given ID.
379379
func (d *Deployment) GetExpectedPodArguments(apiObject metav1.Object, deplSpec api.DeploymentSpec, group api.ServerGroup,
380-
agents api.MemberStatusList, id string) []string {
381-
return d.resources.GetExpectedPodArguments(apiObject, deplSpec, group, agents, id)
380+
agents api.MemberStatusList, id string, version driver.Version) []string {
381+
return d.resources.GetExpectedPodArguments(apiObject, deplSpec, group, agents, id, version)
382382
}

pkg/deployment/deployment_inspector.go

+6
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ func (d *Deployment) inspectDeployment(lastInterval util.Interval) util.Interval
8080
d.CreateEvent(k8sutil.NewErrorEvent("Secret hash validation failed", err, d.apiObject))
8181
}
8282

83+
// Check for LicenseKeySecret
84+
if err := d.resources.ValidateLicenseKeySecret(); err != nil {
85+
hasError = true
86+
d.CreateEvent(k8sutil.NewErrorEvent("License Key Secret invalid", err, d.apiObject))
87+
}
88+
8389
// Is the deployment in a good state?
8490
status, _ := d.GetStatus()
8591
if status.Conditions.IsTrue(api.ConditionTypeSecretsChanged) {

pkg/deployment/images.go

+20-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636

3737
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
3838
"github.com/arangodb/kube-arangodb/pkg/util/arangod"
39+
"github.com/arangodb/kube-arangodb/pkg/util/constants"
3940
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
4041
)
4142

@@ -111,10 +112,21 @@ func (ib *imagesBuilder) fetchArangoDBImageIDAndVersion(ctx context.Context, ima
111112
// Check if pod exists
112113
if pod, err := ib.KubeCli.CoreV1().Pods(ns).Get(podName, metav1.GetOptions{}); err == nil {
113114
// Pod found
115+
if k8sutil.IsPodFailed(pod) {
116+
// Wait some time before deleting the pod
117+
if time.Now().After(pod.GetCreationTimestamp().Add(30 * time.Second)) {
118+
if err := ib.KubeCli.CoreV1().Pods(ns).Delete(podName, nil); err != nil && !k8sutil.IsNotFound(err) {
119+
log.Warn().Err(err).Msg("Failed to delete Image ID Pod")
120+
return false, nil
121+
}
122+
}
123+
return false, nil
124+
}
114125
if !k8sutil.IsPodReady(pod) {
115126
log.Debug().Msg("Image ID Pod is not yet ready")
116127
return true, nil
117128
}
129+
118130
if len(pod.Status.ContainerStatuses) == 0 {
119131
log.Warn().Msg("Empty list of ContainerStatuses")
120132
return true, nil
@@ -178,7 +190,14 @@ func (ib *imagesBuilder) fetchArangoDBImageIDAndVersion(ctx context.Context, ima
178190
tolerations = k8sutil.AddTolerationIfNotFound(tolerations, k8sutil.NewNoExecuteToleration(k8sutil.TolerationKeyNodeAlphaUnreachable, shortDur))
179191
serviceAccountName := ""
180192

181-
if err := k8sutil.CreateArangodPod(ib.KubeCli, true, ib.APIObject, role, id, podName, "", image, "", "", ib.Spec.GetImagePullPolicy(), "", false, terminationGracePeriod, args, nil, nil, nil, nil,
193+
env := make(map[string]k8sutil.EnvValue)
194+
if ib.Spec.License.HasSecretName() {
195+
env[constants.EnvArangoLicenseKey] = k8sutil.EnvValue{
196+
SecretName: ib.Spec.License.GetSecretName(),
197+
SecretKey: constants.SecretKeyToken,
198+
}
199+
}
200+
if err := k8sutil.CreateArangodPod(ib.KubeCli, true, ib.APIObject, role, id, podName, "", image, "", "", ib.Spec.GetImagePullPolicy(), "", false, terminationGracePeriod, args, env, nil, nil, nil,
182201
tolerations, serviceAccountName, "", ""); err != nil {
183202
log.Debug().Err(err).Msg("Failed to create image ID pod")
184203
return true, maskAny(err)

pkg/deployment/reconcile/action_cleanout_member.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func (a *actionCleanoutMember) CheckProgress(ctx context.Context) (bool, bool, e
9797
}
9898
// do not try to clean out a pod that was not initialized
9999
if !m.IsInitialized {
100-
return false, true, nil
100+
return true, false, nil
101101
}
102102
c, err := a.actionCtx.GetDatabaseClient(ctx)
103103
if err != nil {

pkg/deployment/reconcile/context.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,5 @@ type Context interface {
9292
DeleteSecret(secretName string) error
9393
// GetExpectedPodArguments creates command line arguments for a server in the given group with given ID.
9494
GetExpectedPodArguments(apiObject metav1.Object, deplSpec api.DeploymentSpec, group api.ServerGroup,
95-
agents api.MemberStatusList, id string) []string
95+
agents api.MemberStatusList, id string, version driver.Version) []string
9696
}

pkg/deployment/reconcile/plan_builder.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func createPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
196196
newPlan = createUpgradeMemberPlan(log, m, group, "Version upgrade", spec.GetImage(), status)
197197
} else {
198198
// Upgrade is not needed, see if rotation is needed
199-
if rotNeeded, reason := podNeedsRotation(log, *p, apiObject, spec, group, status.Members.Agents, m.ID, context); rotNeeded {
199+
if rotNeeded, reason := podNeedsRotation(log, *p, apiObject, spec, group, status, m.ID, context); rotNeeded {
200200
newPlan = createRotateMemberPlan(log, m, group, reason)
201201
}
202202
}
@@ -305,7 +305,7 @@ func podNeedsUpgrading(p v1.Pod, spec api.DeploymentSpec, images api.ImageInfoLi
305305
// given deployment spec.
306306
// When true is returned, a reason for the rotation is already returned.
307307
func podNeedsRotation(log zerolog.Logger, p v1.Pod, apiObject metav1.Object, spec api.DeploymentSpec,
308-
group api.ServerGroup, agents api.MemberStatusList, id string,
308+
group api.ServerGroup, status api.DeploymentStatus, id string,
309309
context PlanBuilderContext) (bool, string) {
310310
groupSpec := spec.GetServerGroupSpec(group)
311311

@@ -319,8 +319,13 @@ func podNeedsRotation(log zerolog.Logger, p v1.Pod, apiObject metav1.Object, spe
319319
return true, "Server container not found"
320320
}
321321

322+
podImageInfo, found := status.Images.GetByImageID(c.Image)
323+
if !found {
324+
return false, "Server Image not found"
325+
}
326+
322327
// Check arguments
323-
expectedArgs := strings.Join(context.GetExpectedPodArguments(apiObject, spec, group, agents, id), " ")
328+
expectedArgs := strings.Join(context.GetExpectedPodArguments(apiObject, spec, group, status.Members.Agents, id, podImageInfo.ArangoDBVersion), " ")
324329
actualArgs := strings.Join(getContainerArgs(c), " ")
325330
if expectedArgs != actualArgs {
326331
log.Debug().

pkg/deployment/reconcile/plan_builder_context.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
package reconcile
2424

2525
import (
26+
driver "github.com/arangodb/go-driver"
2627
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
2728
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
2829
"k8s.io/api/core/v1"
@@ -44,7 +45,7 @@ type PlanBuilderContext interface {
4445
GetPvc(pvcName string) (*v1.PersistentVolumeClaim, error)
4546
// GetExpectedPodArguments creates command line arguments for a server in the given group with given ID.
4647
GetExpectedPodArguments(apiObject metav1.Object, deplSpec api.DeploymentSpec, group api.ServerGroup,
47-
agents api.MemberStatusList, id string) []string
48+
agents api.MemberStatusList, id string, version driver.Version) []string
4849
}
4950

5051
// newPlanBuilderContext creates a PlanBuilderContext from the given context

pkg/deployment/reconcile/plan_builder_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"k8s.io/api/core/v1"
3333
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3434

35+
driver "github.com/arangodb/go-driver"
3536
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
3637
"github.com/arangodb/kube-arangodb/pkg/util"
3738
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
@@ -64,7 +65,7 @@ func (c *testContext) GetPvc(pvcName string) (*v1.PersistentVolumeClaim, error)
6465

6566
// GetExpectedPodArguments creates command line arguments for a server in the given group with given ID.
6667
func (c *testContext) GetExpectedPodArguments(apiObject metav1.Object, deplSpec api.DeploymentSpec, group api.ServerGroup,
67-
agents api.MemberStatusList, id string) []string {
68+
agents api.MemberStatusList, id string, version driver.Version) []string {
6869
return nil // not implemented
6970
}
7071

0 commit comments

Comments
 (0)