mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
Feature/restart pods on sidecar changes (#420)
* added side car changes to pod rotation * change log * go fmt * sidecar test
This commit is contained in:
parent
b192e7b0a4
commit
08ecc445cd
11 changed files with 569 additions and 7 deletions
|
@ -1,6 +1,7 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
## [0.3.13]() (XXXX-XX-XX)
|
## [0.3.13]() (XXXX-XX-XX)
|
||||||
|
- Added side car changed to pod rotation criterium
|
||||||
- Added ArangoDB version and image id to member status
|
- Added ArangoDB version and image id to member status
|
||||||
|
|
||||||
## [0.3.12](https://github.com/arangodb/kube-arangodb/tree/0.3.12) (2019-07-04)
|
## [0.3.12](https://github.com/arangodb/kube-arangodb/tree/0.3.12) (2019-07-04)
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
|
|
||||||
package v1alpha
|
package v1alpha
|
||||||
|
|
||||||
import "github.com/arangodb/kube-arangodb/pkg/util"
|
import (
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
// DeploymentStatus contains the status part of a Cluster resource.
|
// DeploymentStatus contains the status part of a Cluster resource.
|
||||||
type DeploymentStatus struct {
|
type DeploymentStatus struct {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
package v1alpha
|
package v1alpha
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
driver "github.com/arangodb/go-driver"
|
driver "github.com/arangodb/go-driver"
|
||||||
|
@ -54,6 +55,8 @@ type MemberStatus struct {
|
||||||
IsInitialized bool `json:"initialized"`
|
IsInitialized bool `json:"initialized"`
|
||||||
// CleanoutJobID holds the ID of the agency job for cleaning out this server
|
// CleanoutJobID holds the ID of the agency job for cleaning out this server
|
||||||
CleanoutJobID string `json:"cleanout-job-id,omitempty"`
|
CleanoutJobID string `json:"cleanout-job-id,omitempty"`
|
||||||
|
// SideCarSpecs contains list of specifications specified for side cars
|
||||||
|
SideCarSpecs map[string]v1.Container
|
||||||
// ArangoVersion holds the ArangoDB version in member
|
// ArangoVersion holds the ArangoDB version in member
|
||||||
ArangoVersion driver.Version `json:"arango-version,omitempty"`
|
ArangoVersion driver.Version `json:"arango-version,omitempty"`
|
||||||
//ImageId holds the members ArangoDB image ID
|
//ImageId holds the members ArangoDB image ID
|
||||||
|
@ -70,6 +73,7 @@ func (s MemberStatus) Equal(other MemberStatus) bool {
|
||||||
s.Conditions.Equal(other.Conditions) &&
|
s.Conditions.Equal(other.Conditions) &&
|
||||||
s.IsInitialized == other.IsInitialized &&
|
s.IsInitialized == other.IsInitialized &&
|
||||||
s.CleanoutJobID == other.CleanoutJobID &&
|
s.CleanoutJobID == other.CleanoutJobID &&
|
||||||
|
reflect.DeepEqual(s.SideCarSpecs, other.SideCarSpecs) &&
|
||||||
s.ArangoVersion == other.ArangoVersion &&
|
s.ArangoVersion == other.ArangoVersion &&
|
||||||
s.ImageID == other.ImageID
|
s.ImageID == other.ImageID
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,9 @@ type ActionContext interface {
|
||||||
GetDeploymentHealth() (driver.ClusterHealth, error)
|
GetDeploymentHealth() (driver.ClusterHealth, error)
|
||||||
// InvalidateSyncStatus resets the sync state to false and triggers an inspection
|
// InvalidateSyncStatus resets the sync state to false and triggers an inspection
|
||||||
InvalidateSyncStatus()
|
InvalidateSyncStatus()
|
||||||
|
// GetSpec returns a copy of the spec
|
||||||
|
GetSpec() api.DeploymentSpec
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newActionContext creates a new ActionContext implementation.
|
// newActionContext creates a new ActionContext implementation.
|
||||||
|
@ -109,6 +112,10 @@ func (ac *actionContext) GetMode() api.DeploymentMode {
|
||||||
return ac.context.GetSpec().GetMode()
|
return ac.context.GetSpec().GetMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ac *actionContext) GetSpec() api.DeploymentSpec {
|
||||||
|
return ac.context.GetSpec()
|
||||||
|
}
|
||||||
|
|
||||||
// GetDeploymentHealth returns a copy of the latest known state of cluster health
|
// GetDeploymentHealth returns a copy of the latest known state of cluster health
|
||||||
func (ac *actionContext) GetDeploymentHealth() (driver.ClusterHealth, error) {
|
func (ac *actionContext) GetDeploymentHealth() (driver.ClusterHealth, error) {
|
||||||
return ac.context.GetDeploymentHealth()
|
return ac.context.GetDeploymentHealth()
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
|
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
@ -89,6 +90,7 @@ func (a *actionRotateMember) Start(ctx context.Context) (bool, error) {
|
||||||
}
|
}
|
||||||
// Update status
|
// Update status
|
||||||
m.Phase = api.MemberPhaseRotating
|
m.Phase = api.MemberPhaseRotating
|
||||||
|
|
||||||
if err := a.actionCtx.UpdateMember(m); err != nil {
|
if err := a.actionCtx.UpdateMember(m); err != nil {
|
||||||
return false, maskAny(err)
|
return false, maskAny(err)
|
||||||
}
|
}
|
||||||
|
@ -117,6 +119,15 @@ func (a *actionRotateMember) CheckProgress(ctx context.Context) (bool, bool, err
|
||||||
m.Phase = api.MemberPhaseNone
|
m.Phase = api.MemberPhaseNone
|
||||||
m.RecentTerminations = nil // Since we're rotating, we do not care about old terminations.
|
m.RecentTerminations = nil // Since we're rotating, we do not care about old terminations.
|
||||||
m.CleanoutJobID = ""
|
m.CleanoutJobID = ""
|
||||||
|
|
||||||
|
group := a.action.Group
|
||||||
|
var groupSpec = a.actionCtx.GetSpec().GetServerGroupSpec(group)
|
||||||
|
// Check for missing side cars in
|
||||||
|
m.SideCarSpecs = make(map[string]v1.Container)
|
||||||
|
for _, specSidecar := range groupSpec.GetSidecars() {
|
||||||
|
m.SideCarSpecs[specSidecar.Name] = *specSidecar.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
if err := a.actionCtx.UpdateMember(m); err != nil {
|
if err := a.actionCtx.UpdateMember(m); err != nil {
|
||||||
return false, false, maskAny(err)
|
return false, false, maskAny(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,17 +23,18 @@
|
||||||
package reconcile
|
package reconcile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
driver "github.com/arangodb/go-driver"
|
driver "github.com/arangodb/go-driver"
|
||||||
upgraderules "github.com/arangodb/go-upgrade-rules"
|
upgraderules "github.com/arangodb/go-upgrade-rules"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
|
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// upgradeDecision is the result of an upgrade check.
|
// upgradeDecision is the result of an upgrade check.
|
||||||
|
@ -394,9 +395,106 @@ func podNeedsRotation(log zerolog.Logger, p v1.Pod, apiObject metav1.Object, spe
|
||||||
return true, "Resource Requirements changed"
|
return true, "Resource Requirements changed"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var memberStatus, _, _ = status.Members.MemberStatusByPodName(p.GetName())
|
||||||
|
if memberStatus.SideCarSpecs == nil {
|
||||||
|
memberStatus.SideCarSpecs = make(map[string]v1.Container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for missing side cars in
|
||||||
|
for _, specSidecar := range groupSpec.GetSidecars() {
|
||||||
|
var stateSidecar v1.Container
|
||||||
|
if stateSidecar, found = memberStatus.SideCarSpecs[specSidecar.Name]; !found {
|
||||||
|
return true, "Sidecar " + specSidecar.Name + " not found in running pod " + p.GetName()
|
||||||
|
}
|
||||||
|
if sideCarRequireRotation(specSidecar.DeepCopy(), &stateSidecar) {
|
||||||
|
return true, "Sidecar " + specSidecar.Name + " requires rotation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range memberStatus.SideCarSpecs {
|
||||||
|
var found = false
|
||||||
|
for _, specSidecar := range groupSpec.GetSidecars() {
|
||||||
|
if name == specSidecar.Name {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return true, "Sidecar " + name + " no longer in specification"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sideCarRequireRotation checks if side car requires rotation including default parameters
|
||||||
|
func sideCarRequireRotation(wanted, given *v1.Container) bool {
|
||||||
|
if !reflect.DeepEqual(wanted.Args, given.Args) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(wanted.Command, given.Command) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(wanted.Env, given.Env) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(wanted.EnvFrom, given.EnvFrom) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.Image != given.Image {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.ImagePullPolicy != given.ImagePullPolicy {
|
||||||
|
if wanted.ImagePullPolicy != "Always" || !strings.HasSuffix(given.Image, ":latest") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if wanted.Lifecycle != given.Lifecycle {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.LivenessProbe != given.LivenessProbe {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(wanted.Ports, given.Ports) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.ReadinessProbe != given.ReadinessProbe {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(wanted.Resources, given.Resources) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.SecurityContext != given.SecurityContext {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.Stdin != given.Stdin {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.StdinOnce != given.StdinOnce {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.TerminationMessagePath != given.TerminationMessagePath {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.TerminationMessagePolicy != given.TerminationMessagePolicy {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.TTY != given.TTY {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(wanted.VolumeDevices, given.VolumeDevices) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(wanted.VolumeMounts, given.VolumeMounts) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if wanted.WorkingDir != given.WorkingDir {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// resourcesRequireRotation returns true if the resource requirements have changed such that a rotation is required
|
// resourcesRequireRotation returns true if the resource requirements have changed such that a rotation is required
|
||||||
func resourcesRequireRotation(wanted, given v1.ResourceRequirements) bool {
|
func resourcesRequireRotation(wanted, given v1.ResourceRequirements) bool {
|
||||||
checkList := func(wanted, given v1.ResourceList) bool {
|
checkList := func(wanted, given v1.ResourceList) bool {
|
||||||
|
|
|
@ -71,6 +71,7 @@ func (r *Resources) cleanupRemovedClusterMembers() error {
|
||||||
r.health.mutex.Unlock()
|
r.health.mutex.Unlock()
|
||||||
|
|
||||||
// Only accept recent cluster health values
|
// Only accept recent cluster health values
|
||||||
|
|
||||||
healthAge := time.Since(ts)
|
healthAge := time.Since(ts)
|
||||||
if healthAge > maxClusterHealthAge {
|
if healthAge > maxClusterHealthAge {
|
||||||
log.Info().Dur("age", healthAge).Msg("Cleanup longer than max cluster health. Exiting")
|
log.Info().Dur("age", healthAge).Msg("Cleanup longer than max cluster health. Exiting")
|
||||||
|
|
|
@ -108,7 +108,7 @@ func TestLoadBalancingSourceRanges(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.Logf("Service %s cannot be found, waiting for some time...", eaServiceName)
|
t.Logf("Service %s cannot be found, waiting for some time...", eaServiceName)
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
counter += 1
|
counter++
|
||||||
if counter >= 60 {
|
if counter >= 60 {
|
||||||
t.Fatalf("Could not find service %s within 60 seconds, giving up.", eaServiceName)
|
t.Fatalf("Could not find service %s within 60 seconds, giving up.", eaServiceName)
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ func TestLoadBalancingSourceRanges(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Logf("Service %s cannot be found, waiting for some more time...", eaServiceName)
|
t.Logf("Service %s cannot be found, waiting for some more time...", eaServiceName)
|
||||||
counter += 1
|
counter++
|
||||||
if counter >= 60 {
|
if counter >= 60 {
|
||||||
t.Fatalf("Could not find changed service %s within 60 seconds, giving up.", eaServiceName)
|
t.Fatalf("Could not find changed service %s within 60 seconds, giving up.", eaServiceName)
|
||||||
}
|
}
|
||||||
|
|
387
tests/sidecar_test.go
Normal file
387
tests/sidecar_test.go
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
//
|
||||||
|
// DISCLAIMER
|
||||||
|
//
|
||||||
|
// Copyright 2018 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
|
||||||
|
//
|
||||||
|
// Author Kaveh Vahedipour <kaveh@arangodb.com>
|
||||||
|
//
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
driver "github.com/arangodb/go-driver"
|
||||||
|
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
|
||||||
|
kubeArangoClient "github.com/arangodb/kube-arangodb/pkg/client"
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
|
"github.com/dchest/uniuri"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sideCarTest struct {
|
||||||
|
shortTest bool
|
||||||
|
name string
|
||||||
|
mode api.DeploymentMode
|
||||||
|
version string
|
||||||
|
image string
|
||||||
|
imageTag string
|
||||||
|
sideCars map[string][]v1.Container
|
||||||
|
}
|
||||||
|
|
||||||
|
type SideCarTest interface {
|
||||||
|
IsShortTest() bool
|
||||||
|
Mode() api.DeploymentMode
|
||||||
|
Name() string
|
||||||
|
Image() string
|
||||||
|
Version() driver.Version
|
||||||
|
GroupSideCars(string) []v1.Container
|
||||||
|
AddSideCar(string, v1.Container)
|
||||||
|
ClearGroupSideCars(group string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sideCarTest) IsShortTest() bool {
|
||||||
|
return s.shortTest
|
||||||
|
}
|
||||||
|
func (s *sideCarTest) Name() string {
|
||||||
|
return s.name
|
||||||
|
}
|
||||||
|
func (s *sideCarTest) Mode() api.DeploymentMode {
|
||||||
|
return s.mode
|
||||||
|
}
|
||||||
|
func (s *sideCarTest) Version() driver.Version {
|
||||||
|
return driver.Version(s.version)
|
||||||
|
}
|
||||||
|
func (s *sideCarTest) GroupSideCars(group string) []v1.Container {
|
||||||
|
if s.sideCars == nil {
|
||||||
|
s.sideCars = make(map[string][]v1.Container)
|
||||||
|
}
|
||||||
|
return s.sideCars[group]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sideCarTest) AddSideCar(group string, container v1.Container) {
|
||||||
|
if s.sideCars == nil {
|
||||||
|
s.sideCars = make(map[string][]v1.Container)
|
||||||
|
}
|
||||||
|
s.sideCars[group] = append(s.sideCars[group], container)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sideCarTest) Image() string {
|
||||||
|
imageName := "arangodb/arangodb"
|
||||||
|
if s.image != "" {
|
||||||
|
imageName = s.image
|
||||||
|
}
|
||||||
|
imageTag := "latest"
|
||||||
|
if s.imageTag != "" {
|
||||||
|
imageTag = s.imageTag
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%s", imageName, imageTag)
|
||||||
|
}
|
||||||
|
func (s *sideCarTest) ClearGroupSideCars(group string) {
|
||||||
|
s.sideCars[group] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSideCars tests side car functionality
|
||||||
|
func TestSideCars(t *testing.T) {
|
||||||
|
runSideCarTest(t, &sideCarTest{
|
||||||
|
version: "3.4.7",
|
||||||
|
name: "sidecar-tests",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSideCarTest(t *testing.T, spec SideCarTest) {
|
||||||
|
|
||||||
|
if !spec.IsShortTest() {
|
||||||
|
longOrSkip(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := getNamespace(t)
|
||||||
|
kubecli := mustNewKubeClient(t)
|
||||||
|
c := kubeArangoClient.MustNewInCluster()
|
||||||
|
|
||||||
|
depl := newDeployment(fmt.Sprintf("tu-%s-%s", spec.Name(), uniuri.NewLen(4)))
|
||||||
|
depl.Spec.Mode = api.NewMode(spec.Mode())
|
||||||
|
depl.Spec.TLS = api.TLSSpec{} // should auto-generate cert
|
||||||
|
depl.Spec.Image = util.NewString(spec.Image())
|
||||||
|
depl.Spec.SetDefaults(depl.GetName()) // this must be last
|
||||||
|
|
||||||
|
// Create deployment
|
||||||
|
deployment, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Create deployment failed: %v", err)
|
||||||
|
}
|
||||||
|
defer deferedCleanupDeployment(c, depl.GetName(), ns)
|
||||||
|
|
||||||
|
// Wait for deployment to be ready
|
||||||
|
deployment, err = waitUntilDeployment(c, depl.GetName(), ns, deploymentIsReady())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Deployment not running in time: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a database client
|
||||||
|
ctx := context.Background()
|
||||||
|
DBClient := mustNewArangodDatabaseClient(ctx, kubecli, deployment, t, nil)
|
||||||
|
if err := waitUntilArangoDeploymentHealthy(deployment, DBClient, kubecli, spec.Version()); err != nil {
|
||||||
|
t.Fatalf("Deployment not healthy in time: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sidecar to coordinators
|
||||||
|
var coordinators = api.ServerGroupCoordinators.AsRole()
|
||||||
|
var dbservers = api.ServerGroupDBServers.AsRole()
|
||||||
|
var agents = api.ServerGroupAgents.AsRole()
|
||||||
|
|
||||||
|
var name = "nginx"
|
||||||
|
var image = "nginx:1.7.9"
|
||||||
|
|
||||||
|
spec.AddSideCar(coordinators, v1.Container{Image: image, Name: name})
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to add %s to group %s", name, coordinators)
|
||||||
|
} else {
|
||||||
|
t.Logf("Add %s sidecar to group %s ...", name, coordinators)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd1 := []string{"sh", "-c", "sleep 3600"}
|
||||||
|
cmd2 := []string{"sh", "-c", "sleep 1800"}
|
||||||
|
cmd := []string{"sh"}
|
||||||
|
args := []string{"-c", "sleep 3600"}
|
||||||
|
|
||||||
|
// Add 2nd sidecar to coordinators
|
||||||
|
image = "busybox"
|
||||||
|
name = "sleeper"
|
||||||
|
spec.AddSideCar(coordinators, v1.Container{Image: image, Name: name, Command: cmd1})
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to add %s to group %s", name, coordinators)
|
||||||
|
} else {
|
||||||
|
t.Logf("Add sidecar %s to group %s ...", name, coordinators)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update command line of second sidecar
|
||||||
|
spec.GroupSideCars(coordinators)[1].Command = cmd2
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to update %s in group %s with new command line", name, coordinators)
|
||||||
|
} else {
|
||||||
|
t.Logf("Update %s in group %s with new command line ...", name, coordinators)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change command line args of second sidecar
|
||||||
|
spec.GroupSideCars(coordinators)[1].Command = cmd
|
||||||
|
spec.GroupSideCars(coordinators)[1].Args = args
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to update %s in group %s with new command line arguments", name, coordinators)
|
||||||
|
} else {
|
||||||
|
t.Logf("Update %s in group %s with new command line arguments ...", name, coordinators)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change environment variables of second container
|
||||||
|
spec.GroupSideCars(coordinators)[1].Env = []v1.EnvVar{
|
||||||
|
{Name: "Hello", Value: "World"}, {Name: "Pi", Value: "3.14159265359"}, {Name: "Two", Value: "2"}}
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to change environment variables of %s sidecars for %s", name, coordinators)
|
||||||
|
} else {
|
||||||
|
t.Logf("Change environment variables of %s sidecars for %s ...", name, coordinators)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade side car image
|
||||||
|
name = spec.GroupSideCars(coordinators)[0].Name
|
||||||
|
spec.GroupSideCars(coordinators)[0].Image = "nginx:1.7.10"
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to update %s in group %s with new image", name, coordinators)
|
||||||
|
} else {
|
||||||
|
t.Logf("Update image of sidecar %s in group %s ...", name, coordinators)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update side car image with new pull policy
|
||||||
|
spec.GroupSideCars(coordinators)[0].ImagePullPolicy = v1.PullPolicy("Always")
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to update %s in group %s with new image pull policy", name, coordinators)
|
||||||
|
} else {
|
||||||
|
t.Logf("Update %s in group %s with new image pull policy ...", name, coordinators)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all sidecars again
|
||||||
|
spec.ClearGroupSideCars(coordinators)
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to remove all sidecars from group %s", coordinators)
|
||||||
|
} else {
|
||||||
|
t.Logf("Remove all sidecars from group %s ...", coordinators)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding containers to coordinators and db servers
|
||||||
|
image = "busybox"
|
||||||
|
name = "sleeper"
|
||||||
|
spec.AddSideCar(coordinators, v1.Container{Image: image, Name: name, Command: cmd1})
|
||||||
|
spec.AddSideCar(dbservers, v1.Container{Image: image, Name: name, Command: cmd1})
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
depl.DBServers.Sidecars = spec.GroupSideCars(dbservers)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to add a container to both coordinators and db servers")
|
||||||
|
} else {
|
||||||
|
t.Logf("Add %s sidecar to %s and %s ...", name, coordinators, dbservers)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear containers from both groups
|
||||||
|
spec.ClearGroupSideCars(coordinators)
|
||||||
|
spec.ClearGroupSideCars(dbservers)
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
depl.DBServers.Sidecars = spec.GroupSideCars(dbservers)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to delete all containers from both coordinators and db servers")
|
||||||
|
} else {
|
||||||
|
t.Logf("Remove all sidecars ...")
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding containers to agents again
|
||||||
|
spec.AddSideCar(agents, v1.Container{Image: image, Name: name, Command: cmd1})
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to add a %s sidecar to %s", name, agents)
|
||||||
|
} else {
|
||||||
|
t.Logf("Add a %s sidecar to %s ...", name, agents)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear containers from coordinators and add to db servers
|
||||||
|
spec.ClearGroupSideCars(agents)
|
||||||
|
spec.AddSideCar(dbservers, v1.Container{Image: image, Name: name, Command: cmd1})
|
||||||
|
deployment, err = updateDeployment(c, depl.GetName(), ns,
|
||||||
|
func(depl *api.DeploymentSpec) {
|
||||||
|
depl.Coordinators.Sidecars = spec.GroupSideCars(coordinators)
|
||||||
|
depl.DBServers.Sidecars = spec.GroupSideCars(dbservers)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to delete %s containers and add %s sidecars to %s", agents, name, dbservers)
|
||||||
|
} else {
|
||||||
|
t.Logf("Delete %s containers and add %s sidecars to %s", agents, name, dbservers)
|
||||||
|
}
|
||||||
|
err = waitUntilClusterSidecarsEqualSpec(t, spec.Mode(), *depl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("... failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Log("... done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
removeDeployment(c, depl.GetName(), ns)
|
||||||
|
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -49,6 +50,7 @@ import (
|
||||||
|
|
||||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
|
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
|
||||||
rapi "github.com/arangodb/kube-arangodb/pkg/apis/replication/v1alpha"
|
rapi "github.com/arangodb/kube-arangodb/pkg/apis/replication/v1alpha"
|
||||||
|
cl "github.com/arangodb/kube-arangodb/pkg/client"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
|
"github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/arangod"
|
"github.com/arangodb/kube-arangodb/pkg/util/arangod"
|
||||||
|
@ -157,7 +159,7 @@ func getEnterpriseImageOrSkip(t *testing.T) string {
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
const TestEnterpriseLicenseKeySecretName = "arangodb-jenkins-license-key"
|
const testEnterpriseLicenseKeySecretName = "arangodb-jenkins-license-key"
|
||||||
|
|
||||||
func getEnterpriseLicenseKey() string {
|
func getEnterpriseLicenseKey() string {
|
||||||
return strings.TrimSpace(os.Getenv("ENTERPRISELICENSE"))
|
return strings.TrimSpace(os.Getenv("ENTERPRISELICENSE"))
|
||||||
|
@ -267,7 +269,7 @@ func newDeployment(name string) *api.ArangoDeployment {
|
||||||
Spec: api.DeploymentSpec{
|
Spec: api.DeploymentSpec{
|
||||||
ImagePullPolicy: util.NewPullPolicy(v1.PullAlways),
|
ImagePullPolicy: util.NewPullPolicy(v1.PullAlways),
|
||||||
License: api.LicenseSpec{
|
License: api.LicenseSpec{
|
||||||
SecretName: util.NewString(TestEnterpriseLicenseKeySecretName),
|
SecretName: util.NewString(testEnterpriseLicenseKeySecretName),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -558,6 +560,55 @@ func createEqualVersionsPredicate(version driver.Version) func(driver.VersionInf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clusterSidecarsEqualSpec returns nil if sidecars from spec and cluster match
|
||||||
|
func waitUntilClusterSidecarsEqualSpec(t *testing.T, spec api.DeploymentMode, depl api.ArangoDeployment) error {
|
||||||
|
|
||||||
|
c := cl.MustNewInCluster()
|
||||||
|
ns := getNamespace(t)
|
||||||
|
|
||||||
|
var noGood int
|
||||||
|
for start := time.Now(); time.Since(start) < 600*time.Second; {
|
||||||
|
|
||||||
|
// Fetch latest status so we know all member details
|
||||||
|
apiObject, err := c.DatabaseV1alpha().ArangoDeployments(ns).Get(depl.GetName(), metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get deployment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// How many pods not matching
|
||||||
|
noGood = 0
|
||||||
|
|
||||||
|
// Check member after another
|
||||||
|
apiObject.ForeachServerGroup(func(group api.ServerGroup, spec api.ServerGroupSpec, status *api.MemberStatusList) error {
|
||||||
|
for _, m := range *status {
|
||||||
|
if len(m.SideCarSpecs) != len(spec.GetSidecars()) {
|
||||||
|
noGood++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, scar := range spec.GetSidecars() {
|
||||||
|
mcar, found := m.SideCarSpecs[scar.Name]
|
||||||
|
if found {
|
||||||
|
if !reflect.DeepEqual(mcar, scar) {
|
||||||
|
noGood++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
noGood++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, &apiObject.Status)
|
||||||
|
|
||||||
|
if noGood == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
return maskAny(fmt.Errorf("%d pods with unmatched sidecars", noGood))
|
||||||
|
}
|
||||||
|
|
||||||
// clusterHealthEqualsSpec returns nil when the given health matches
|
// clusterHealthEqualsSpec returns nil when the given health matches
|
||||||
// with the given deployment spec.
|
// with the given deployment spec.
|
||||||
func clusterHealthEqualsSpec(h driver.ClusterHealth, spec api.DeploymentSpec) error {
|
func clusterHealthEqualsSpec(h driver.ClusterHealth, spec api.DeploymentSpec) error {
|
||||||
|
|
Loading…
Reference in a new issue