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

Allow to set annotations (#499)

This commit is contained in:
Adam Janikowski 2019-11-11 14:11:27 +01:00 committed by GitHub
parent ba3a30911f
commit 115d03f0a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1243 additions and 39 deletions

View file

@ -17,14 +17,14 @@ rules:
resources: ["arangodeployments"]
verbs: ["*"]
- apiGroups: [""]
resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "events", "secrets"]
resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "events", "secrets", "serviceaccounts"]
verbs: ["*"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get"]
- apiGroups: ["policy"]
resources: ["poddisruptionbudgets"]
verbs: ["get", "create", "delete"]
verbs: ["*"]
- apiGroups: ["backup.arangodb.com"]
resources: ["arangobackuppolicies", "arangobackups"]
verbs: ["get", "list", "watch"]

View file

@ -100,6 +100,10 @@ Possible values are:
This setting specifies the list of image pull secrets for the docker image to use for all ArangoDB servers.
### `spec.annotations: map[string]string`
This setting set specified annotations to all ArangoDeployment owned resources (pods, services, PVC's, PDB's).
### `spec.storageEngine: string`
This setting specifies the type of storage engine used for all servers
@ -517,6 +521,10 @@ rules:
If you are using a different service account, please grant these rights
to that service account.
### `spec.<group>.annotations: map[string]string`
This setting set annotations overrides for pods in this group. Annotations are merged with `spec.annotations`.
### `spec.<group>.priorityClassName: string`
Priority class name for pods of this group. Will be forwarded to the pod spec. [Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/)

View file

@ -70,7 +70,8 @@ func (a *ArangoBackupPolicy) NewBackup(d *deployment.ArangoDeployment) *ArangoBa
Name: fmt.Sprintf("%s-%s", d.Name, utils.RandomString(8)),
Namespace: a.Namespace,
Labels: d.Labels,
Labels: d.Labels,
Annotations: d.Annotations,
Finalizers: []string{
FinalizerArangoBackup,

View file

@ -70,7 +70,8 @@ func (a *ArangoBackupPolicy) NewBackup(d *deployment.ArangoDeployment) *ArangoBa
Name: fmt.Sprintf("%s-%s", d.Name, utils.RandomString(8)),
Namespace: a.Namespace,
Labels: d.Labels,
Labels: d.Labels,
Annotations: d.Annotations,
Finalizers: []string{
FinalizerArangoBackup,

View file

@ -56,9 +56,12 @@ type DeploymentSpec struct {
DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"`
DisableIPv6 *bool `json:"disableIPv6,omitempty"`
NetworkAttachedVolumes *bool `json:"networkAttachedVolumes,omitempty"`
NetworkAttachedVolumes *bool `json:"networkAttachedVolumes,omitempty"`
RestoreFrom *string `json:"restoreFrom,omitempty"`
// Annotations specified the annotations added to all resources
Annotations map[string]string `json:"annotations,omitempty"`
RestoreFrom *string `json:"restoreFrom,omitempty"`
ExternalAccess ExternalAccessSpec `json:"externalAccess"`
RocksDB RocksDBSpec `json:"rocksdb"`
@ -105,6 +108,11 @@ func (s DeploymentSpec) GetEnvironment() Environment {
return EnvironmentOrDefault(s.Environment)
}
// GetAnnotations returns the annotations of this group
func (s DeploymentSpec) GetAnnotations() map[string]string {
return s.Annotations
}
// GetStorageEngine returns the value of storageEngine.
func (s DeploymentSpec) GetStorageEngine() StorageEngine {
return StorageEngineOrDefault(s.StorageEngine)

View file

@ -22,17 +22,32 @@
package v1
import time "time"
import "time"
type ServerGroup int
const (
ServerGroupUnknown ServerGroup = 0
ServerGroupSingle ServerGroup = 1
ServerGroupAgents ServerGroup = 2
ServerGroupDBServers ServerGroup = 3
ServerGroupCoordinators ServerGroup = 4
ServerGroupSyncMasters ServerGroup = 5
ServerGroupSyncWorkers ServerGroup = 6
ServerGroupSingleString = "single"
ServerGroupAgentsString = "agent"
ServerGroupDBServersString = "dbserver"
ServerGroupCoordinatorsString = "coordinator"
ServerGroupSyncMastersString = "syncmaster"
ServerGroupSyncWorkersString = "syncworker"
ServerGroupSingleAbbreviatedString = "sngl"
ServerGroupAgentsAbbreviatedString = "agnt"
ServerGroupDBServersAbbreviatedString = "prmr"
ServerGroupCoordinatorsAbbreviatedString = "crdn"
ServerGroupSyncMastersAbbreviatedString = "syma"
ServerGroupSyncWorkersAbbreviatedString = "sywo"
)
var (
@ -51,17 +66,17 @@ var (
func (g ServerGroup) AsRole() string {
switch g {
case ServerGroupSingle:
return "single"
return ServerGroupSingleString
case ServerGroupAgents:
return "agent"
return ServerGroupAgentsString
case ServerGroupDBServers:
return "dbserver"
return ServerGroupDBServersString
case ServerGroupCoordinators:
return "coordinator"
return ServerGroupCoordinatorsString
case ServerGroupSyncMasters:
return "syncmaster"
return ServerGroupSyncMastersString
case ServerGroupSyncWorkers:
return "syncworker"
return ServerGroupSyncWorkersString
default:
return "?"
}
@ -71,17 +86,17 @@ func (g ServerGroup) AsRole() string {
func (g ServerGroup) AsRoleAbbreviated() string {
switch g {
case ServerGroupSingle:
return "sngl"
return ServerGroupSingleAbbreviatedString
case ServerGroupAgents:
return "agnt"
return ServerGroupAgentsAbbreviatedString
case ServerGroupDBServers:
return "prmr"
return ServerGroupDBServersAbbreviatedString
case ServerGroupCoordinators:
return "crdn"
return ServerGroupCoordinatorsAbbreviatedString
case ServerGroupSyncMasters:
return "syma"
return ServerGroupSyncMastersAbbreviatedString
case ServerGroupSyncWorkers:
return "sywo"
return ServerGroupSyncWorkersAbbreviatedString
default:
return "?"
}
@ -140,3 +155,43 @@ func (g ServerGroup) IsExportMetrics() bool {
return false
}
}
// ServerGroupFromAbbreviatedRole returns ServerGroup from abbreviated role
func ServerGroupFromAbbreviatedRole(label string) ServerGroup {
switch label {
case ServerGroupSingleAbbreviatedString:
return ServerGroupSingle
case ServerGroupAgentsAbbreviatedString:
return ServerGroupAgents
case ServerGroupDBServersAbbreviatedString:
return ServerGroupDBServers
case ServerGroupCoordinatorsAbbreviatedString:
return ServerGroupCoordinators
case ServerGroupSyncMastersAbbreviatedString:
return ServerGroupSyncMasters
case ServerGroupSyncWorkersAbbreviatedString:
return ServerGroupSyncWorkers
default:
return ServerGroupUnknown
}
}
// ServerGroupFromAbbreviatedRole returns ServerGroup from role
func ServerGroupFromRole(label string) ServerGroup {
switch label {
case ServerGroupSingleString:
return ServerGroupSingle
case ServerGroupAgentsString:
return ServerGroupAgents
case ServerGroupDBServersString:
return ServerGroupDBServers
case ServerGroupCoordinatorsString:
return ServerGroupCoordinators
case ServerGroupSyncMastersString:
return ServerGroupSyncMasters
case ServerGroupSyncWorkersString:
return ServerGroupSyncWorkers
default:
return ServerGroupUnknown
}
}

View file

@ -52,6 +52,8 @@ type ServerGroupSpec struct {
Resources v1.ResourceRequirements `json:"resources,omitempty"`
// Tolerations specifies the tolerations added to Pods in this group.
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
// Annotations specified the annotations added to Pods in this group.
Annotations map[string]string `json:"annotations,omitempty"`
// ServiceAccountName specifies the name of the service account used for Pods in this group.
ServiceAccountName *string `json:"serviceAccountName,omitempty"`
// NodeSelector speficies a set of selectors for nodes
@ -133,6 +135,11 @@ func (s ServerGroupSpec) GetNodeSelector() map[string]string {
return s.NodeSelector
}
// GetAnnotations returns the annotations of this group
func (s ServerGroupSpec) GetAnnotations() map[string]string {
return s.Annotations
}
// GetArgs returns the value of args.
func (s ServerGroupSpec) GetArgs() []string {
return s.Args

View file

@ -27,7 +27,7 @@ package v1
import (
time "time"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@ -270,7 +270,7 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
}
if in.ImagePullPolicy != nil {
in, out := &in.ImagePullPolicy, &out.ImagePullPolicy
*out = new(v1.PullPolicy)
*out = new(corev1.PullPolicy)
**out = **in
}
if in.ImagePullSecrets != nil {
@ -293,6 +293,13 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
*out = new(bool)
**out = **in
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.RestoreFrom != nil {
in, out := &in.RestoreFrom, &out.RestoreFrom
*out = new(string)
@ -563,7 +570,7 @@ func (in *MemberStatus) DeepCopyInto(out *MemberStatus) {
}
if in.SideCarSpecs != nil {
in, out := &in.SideCarSpecs, &out.SideCarSpecs
*out = make(map[string]v1.Container, len(*in))
*out = make(map[string]corev1.Container, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
@ -834,11 +841,18 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
in.Resources.DeepCopyInto(&out.Resources)
if in.Tolerations != nil {
in, out := &in.Tolerations, &out.Tolerations
*out = make([]v1.Toleration, len(*in))
*out = make([]corev1.Toleration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.ServiceAccountName != nil {
in, out := &in.ServiceAccountName, &out.ServiceAccountName
*out = new(string)
@ -858,12 +872,12 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
}
if in.VolumeClaimTemplate != nil {
in, out := &in.VolumeClaimTemplate, &out.VolumeClaimTemplate
*out = new(v1.PersistentVolumeClaim)
*out = new(corev1.PersistentVolumeClaim)
(*in).DeepCopyInto(*out)
}
if in.Sidecars != nil {
in, out := &in.Sidecars, &out.Sidecars
*out = make([]v1.Container, len(*in))
*out = make([]corev1.Container, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}

View file

@ -56,9 +56,12 @@ type DeploymentSpec struct {
DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"`
DisableIPv6 *bool `json:"disableIPv6,omitempty"`
NetworkAttachedVolumes *bool `json:"networkAttachedVolumes,omitempty"`
NetworkAttachedVolumes *bool `json:"networkAttachedVolumes,omitempty"`
RestoreFrom *string `json:"restoreFrom,omitempty"`
// Annotations specified the annotations added to Pods in this group.
Annotations map[string]string `json:"annotations,omitempty"`
RestoreFrom *string `json:"restoreFrom,omitempty"`
ExternalAccess ExternalAccessSpec `json:"externalAccess"`
RocksDB RocksDBSpec `json:"rocksdb"`
@ -105,6 +108,11 @@ func (s DeploymentSpec) GetEnvironment() Environment {
return EnvironmentOrDefault(s.Environment)
}
// GetAnnotations returns the annotations of this group
func (s DeploymentSpec) GetAnnotations() map[string]string {
return s.Annotations
}
// GetStorageEngine returns the value of storageEngine.
func (s DeploymentSpec) GetStorageEngine() StorageEngine {
return StorageEngineOrDefault(s.StorageEngine)

View file

@ -27,12 +27,27 @@ import time "time"
type ServerGroup int
const (
ServerGroupUnknown ServerGroup = 0
ServerGroupSingle ServerGroup = 1
ServerGroupAgents ServerGroup = 2
ServerGroupDBServers ServerGroup = 3
ServerGroupCoordinators ServerGroup = 4
ServerGroupSyncMasters ServerGroup = 5
ServerGroupSyncWorkers ServerGroup = 6
ServerGroupSingleString = "single"
ServerGroupAgentsString = "agent"
ServerGroupDBServersString = "dbserver"
ServerGroupCoordinatorsString = "coordinator"
ServerGroupSyncMastersString = "syncmaster"
ServerGroupSyncWorkersString = "syncworker"
ServerGroupSingleAbbreviatedString = "sngl"
ServerGroupAgentsAbbreviatedString = "agnt"
ServerGroupDBServersAbbreviatedString = "prmr"
ServerGroupCoordinatorsAbbreviatedString = "crdn"
ServerGroupSyncMastersAbbreviatedString = "syma"
ServerGroupSyncWorkersAbbreviatedString = "sywo"
)
var (
@ -51,17 +66,17 @@ var (
func (g ServerGroup) AsRole() string {
switch g {
case ServerGroupSingle:
return "single"
return ServerGroupSingleString
case ServerGroupAgents:
return "agent"
return ServerGroupAgentsString
case ServerGroupDBServers:
return "dbserver"
return ServerGroupDBServersString
case ServerGroupCoordinators:
return "coordinator"
return ServerGroupCoordinatorsString
case ServerGroupSyncMasters:
return "syncmaster"
return ServerGroupSyncMastersString
case ServerGroupSyncWorkers:
return "syncworker"
return ServerGroupSyncWorkersString
default:
return "?"
}
@ -71,17 +86,17 @@ func (g ServerGroup) AsRole() string {
func (g ServerGroup) AsRoleAbbreviated() string {
switch g {
case ServerGroupSingle:
return "sngl"
return ServerGroupSingleAbbreviatedString
case ServerGroupAgents:
return "agnt"
return ServerGroupAgentsAbbreviatedString
case ServerGroupDBServers:
return "prmr"
return ServerGroupDBServersAbbreviatedString
case ServerGroupCoordinators:
return "crdn"
return ServerGroupCoordinatorsAbbreviatedString
case ServerGroupSyncMasters:
return "syma"
return ServerGroupSyncMastersAbbreviatedString
case ServerGroupSyncWorkers:
return "sywo"
return ServerGroupSyncWorkersAbbreviatedString
default:
return "?"
}
@ -140,3 +155,43 @@ func (g ServerGroup) IsExportMetrics() bool {
return false
}
}
// ServerGroupFromAbbreviatedRole returns ServerGroup from abbreviated role
func ServerGroupFromAbbreviatedRole(label string) ServerGroup {
switch label {
case ServerGroupSingleAbbreviatedString:
return ServerGroupSingle
case ServerGroupAgentsAbbreviatedString:
return ServerGroupAgents
case ServerGroupDBServersAbbreviatedString:
return ServerGroupDBServers
case ServerGroupCoordinatorsAbbreviatedString:
return ServerGroupCoordinators
case ServerGroupSyncMastersAbbreviatedString:
return ServerGroupSyncMasters
case ServerGroupSyncWorkersAbbreviatedString:
return ServerGroupSyncWorkers
default:
return ServerGroupUnknown
}
}
// ServerGroupFromAbbreviatedRole returns ServerGroup from role
func ServerGroupFromRole(label string) ServerGroup {
switch label {
case ServerGroupSingleString:
return ServerGroupSingle
case ServerGroupAgentsString:
return ServerGroupAgents
case ServerGroupDBServersString:
return ServerGroupDBServers
case ServerGroupCoordinatorsString:
return ServerGroupCoordinators
case ServerGroupSyncMastersString:
return ServerGroupSyncMasters
case ServerGroupSyncWorkersString:
return ServerGroupSyncWorkers
default:
return ServerGroupUnknown
}
}

View file

@ -52,6 +52,8 @@ type ServerGroupSpec struct {
Resources v1.ResourceRequirements `json:"resources,omitempty"`
// Tolerations specifies the tolerations added to Pods in this group.
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
// Annotations specified the annotations added to Pods in this group.
Annotations map[string]string `json:"annotations,omitempty"`
// ServiceAccountName specifies the name of the service account used for Pods in this group.
ServiceAccountName *string `json:"serviceAccountName,omitempty"`
// NodeSelector speficies a set of selectors for nodes
@ -133,6 +135,11 @@ func (s ServerGroupSpec) GetNodeSelector() map[string]string {
return s.NodeSelector
}
// GetAnnotations returns the annotations of this group
func (s ServerGroupSpec) GetAnnotations() map[string]string {
return s.Annotations
}
// GetArgs returns the value of args.
func (s ServerGroupSpec) GetArgs() []string {
return s.Args

View file

@ -293,6 +293,13 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
*out = new(bool)
**out = **in
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.RestoreFrom != nil {
in, out := &in.RestoreFrom, &out.RestoreFrom
*out = new(string)
@ -839,6 +846,13 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.ServiceAccountName != nil {
in, out := &in.ServiceAccountName, &out.ServiceAccountName
*out = new(string)

View file

@ -244,6 +244,10 @@ func (d *Deployment) run() {
log.Info().Msg("start running...")
}
if err := d.resources.EnsureAnnotations(); err != nil {
log.Warn().Err(err).Msg("unable to update annotations")
}
d.lookForServiceMonitorCRD()
inspectionInterval := maxInspectionInterval

View file

@ -172,6 +172,11 @@ func (d *Deployment) inspectDeployment(lastInterval util.Interval) util.Interval
d.CreateEvent(k8sutil.NewErrorEvent("PDB creation failed", err, d.apiObject))
}
if err := d.resources.EnsureAnnotations(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Annotation update failed", err, d.apiObject))
}
// Create access packages
if err := d.createAccessPackages(); err != nil {
hasError = true

View file

@ -0,0 +1,340 @@
//
// 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 Adam Janikowski
//
package resources
import (
"time"
"github.com/arangodb/kube-arangodb/pkg/apis/deployment"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/backup/utils"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
"github.com/rs/zerolog/log"
core "k8s.io/api/core/v1"
policy "k8s.io/api/policy/v1beta1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
typedCore "k8s.io/client-go/kubernetes/typed/core/v1"
policyTyped "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
)
func (r *Resources) EnsureAnnotations() error {
kubecli := r.context.GetKubeCli()
log.Info().Msgf("Ensuring annotations")
if err := ensureSecretsAnnotations(kubecli.CoreV1().Secrets(r.context.GetNamespace()),
deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
r.context.GetSpec().Annotations); err != nil {
return err
}
if err := ensureServiceAccountsAnnotations(kubecli.CoreV1().ServiceAccounts(r.context.GetNamespace()),
deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
r.context.GetSpec().Annotations); err != nil {
return err
}
if err := ensureServicesAnnotations(kubecli.CoreV1().Services(r.context.GetNamespace()),
deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
r.context.GetSpec().Annotations); err != nil {
return err
}
if err := ensurePdbsAnnotations(kubecli.PolicyV1beta1().PodDisruptionBudgets(r.context.GetNamespace()),
deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
r.context.GetSpec().Annotations); err != nil {
return err
}
if err := ensurePvcsAnnotations(kubecli.CoreV1().PersistentVolumeClaims(r.context.GetNamespace()),
deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
r.context.GetSpec().Annotations); err != nil {
return err
}
if err := ensurePodsAnnotations(kubecli.CoreV1().Pods(r.context.GetNamespace()),
deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
r.context.GetSpec().Annotations,
r.context.GetSpec()); err != nil {
return err
}
return nil
}
func ensureSecretsAnnotations(client typedCore.SecretInterface, kind, name, namespace string, annotations map[string]string) error {
secrets, err := k8sutil.GetSecretsForParent(client,
kind,
name,
namespace)
if err != nil {
return err
}
for _, secret := range secrets {
if !k8sutil.CompareAnnotations(secret.GetAnnotations(), annotations) {
log.Info().Msgf("Replacing annotations for Secret %s", secret.Name)
if err = setSecretAnnotations(client, secret, annotations); err != nil {
return err
}
}
}
return nil
}
func setSecretAnnotations(client typedCore.SecretInterface, secret *core.Secret, annotations map[string]string) error {
return utils.Retry(5, 200*time.Millisecond, func() error {
currentSecret, err := client.Get(secret.Name, meta.GetOptions{})
if err != nil {
return err
}
currentSecret.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentSecret.Annotations), annotations)
_, err = client.Update(currentSecret)
if err != nil {
return err
}
return nil
})
}
func ensureServiceAccountsAnnotations(client typedCore.ServiceAccountInterface, kind, name, namespace string, annotations map[string]string) error {
serviceAccounts, err := k8sutil.GetServiceAccountsForParent(client,
kind,
name,
namespace)
if err != nil {
return err
}
for _, serviceAccount := range serviceAccounts {
if !k8sutil.CompareAnnotations(serviceAccount.GetAnnotations(), annotations) {
log.Info().Msgf("Replacing annotations for ServiceAccount %s", serviceAccount.Name)
if err = setServiceAccountAnnotations(client, serviceAccount, annotations); err != nil {
return err
}
}
}
return nil
}
func setServiceAccountAnnotations(client typedCore.ServiceAccountInterface, serviceAccount *core.ServiceAccount, annotations map[string]string) error {
return utils.Retry(5, 200*time.Millisecond, func() error {
currentServiceAccount, err := client.Get(serviceAccount.Name, meta.GetOptions{})
if err != nil {
return err
}
currentServiceAccount.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentServiceAccount.Annotations), annotations)
_, err = client.Update(currentServiceAccount)
if err != nil {
return err
}
return nil
})
}
func ensureServicesAnnotations(client typedCore.ServiceInterface, kind, name, namespace string, annotations map[string]string) error {
services, err := k8sutil.GetServicesForParent(client,
kind,
name,
namespace)
if err != nil {
return err
}
for _, service := range services {
if !k8sutil.CompareAnnotations(service.GetAnnotations(), annotations) {
log.Info().Msgf("Replacing annotations for Service %s", service.Name)
if err = setServiceAnnotations(client, service, annotations); err != nil {
return err
}
}
}
return nil
}
func setServiceAnnotations(client typedCore.ServiceInterface, service *core.Service, annotations map[string]string) error {
return utils.Retry(5, 200*time.Millisecond, func() error {
currentService, err := client.Get(service.Name, meta.GetOptions{})
if err != nil {
return err
}
currentService.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentService.Annotations), annotations)
_, err = client.Update(currentService)
if err != nil {
return err
}
return nil
})
}
func ensurePdbsAnnotations(client policyTyped.PodDisruptionBudgetInterface, kind, name, namespace string, annotations map[string]string) error {
podDisruptionBudgets, err := k8sutil.GetPDBForParent(client,
kind,
name,
namespace)
if err != nil {
return err
}
for _, podDisruptionBudget := range podDisruptionBudgets {
if !k8sutil.CompareAnnotations(podDisruptionBudget.GetAnnotations(), annotations) {
log.Info().Msgf("Replacing annotations for PDB %s", podDisruptionBudget.Name)
if err = setPdbAnnotations(client, podDisruptionBudget, annotations); err != nil {
return err
}
}
}
return nil
}
func setPdbAnnotations(client policyTyped.PodDisruptionBudgetInterface, podDisruptionBudget *policy.PodDisruptionBudget, annotations map[string]string) error {
return utils.Retry(5, 200*time.Millisecond, func() error {
currentPodDistributionBudget, err := client.Get(podDisruptionBudget.Name, meta.GetOptions{})
if err != nil {
return err
}
currentPodDistributionBudget.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentPodDistributionBudget.Annotations), annotations)
_, err = client.Update(currentPodDistributionBudget)
if err != nil {
return err
}
return nil
})
}
func ensurePvcsAnnotations(client typedCore.PersistentVolumeClaimInterface, kind, name, namespace string, annotations map[string]string) error {
persistentVolumeClaims, err := k8sutil.GetPVCForParent(client,
kind,
name,
namespace)
if err != nil {
return err
}
for _, persistentVolumeClaim := range persistentVolumeClaims {
if !k8sutil.CompareAnnotations(persistentVolumeClaim.GetAnnotations(), annotations) {
log.Info().Msgf("Replacing annotations for PVC %s", persistentVolumeClaim.Name)
if err = setPvcAnnotations(client, persistentVolumeClaim, annotations); err != nil {
return err
}
}
}
return nil
}
func setPvcAnnotations(client typedCore.PersistentVolumeClaimInterface, persistentVolumeClaim *core.PersistentVolumeClaim, annotations map[string]string) error {
return utils.Retry(5, 200*time.Millisecond, func() error {
currentVolumeClaim, err := client.Get(persistentVolumeClaim.Name, meta.GetOptions{})
if err != nil {
return err
}
currentVolumeClaim.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentVolumeClaim.Annotations), annotations)
_, err = client.Update(currentVolumeClaim)
if err != nil {
return err
}
return nil
})
}
func getPodGroup(pod *core.Pod) api.ServerGroup {
if pod.Labels == nil {
return api.ServerGroupUnknown
}
return api.ServerGroupFromRole(pod.Labels[k8sutil.LabelKeyRole])
}
func ensurePodsAnnotations(client typedCore.PodInterface, kind, name, namespace string, annotations map[string]string, spec api.DeploymentSpec) error {
pods, err := k8sutil.GetPodsForParent(client,
kind,
name,
namespace)
if err != nil {
return err
}
for _, pod := range pods {
group := getPodGroup(pod)
mergedAnnotations := k8sutil.MergeAnnotations(annotations, spec.GetServerGroupSpec(group).Annotations)
if !k8sutil.CompareAnnotations(pod.GetAnnotations(), mergedAnnotations) {
log.Info().Msgf("Replacing annotations for Pod %s", pod.Name)
if err = setPodAnnotations(client, pod, mergedAnnotations); err != nil {
return err
}
}
}
return nil
}
func setPodAnnotations(client typedCore.PodInterface, pod *core.Pod, annotations map[string]string) error {
return utils.Retry(5, 200*time.Millisecond, func() error {
currentPod, err := client.Get(pod.Name, meta.GetOptions{})
if err != nil {
return err
}
currentPod.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentPod.Annotations), annotations)
_, err = client.Update(currentPod)
if err != nil {
return err
}
return nil
})
}

View file

@ -0,0 +1,187 @@
//
// 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 Adam Janikowski
//
package k8sutil
import (
core "k8s.io/api/core/v1"
policy "k8s.io/api/policy/v1beta1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
typedCore "k8s.io/client-go/kubernetes/typed/core/v1"
policyTyped "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
)
func IsChildResource(kind, name, namespace string, resource meta.Object) bool {
if resource == nil {
return false
}
if namespace != resource.GetNamespace() {
return false
}
ownerRef := resource.GetOwnerReferences()
if ownerRef == nil || len(ownerRef) == 0 {
return false
}
for _, owner := range ownerRef {
if owner.Kind != kind {
continue
}
if owner.Name != name {
continue
}
return true
}
return false
}
func GetSecretsForParent(client typedCore.SecretInterface, kind, name, namespace string) ([]*core.Secret, error) {
secrets, err := client.List(meta.ListOptions{})
if err != nil {
return nil, err
}
if len(secrets.Items) == 0 {
return []*core.Secret{}, nil
}
childSecrets := make([]*core.Secret, 0, len(secrets.Items))
for _, secret := range secrets.Items {
if IsChildResource(kind, name, namespace, &secret) {
childSecrets = append(childSecrets, secret.DeepCopy())
}
}
return childSecrets, nil
}
func GetPDBForParent(client policyTyped.PodDisruptionBudgetInterface, kind, name, namespace string) ([]*policy.PodDisruptionBudget, error) {
pdbs, err := client.List(meta.ListOptions{})
if err != nil {
return nil, err
}
if len(pdbs.Items) == 0 {
return []*policy.PodDisruptionBudget{}, nil
}
childPdbs := make([]*policy.PodDisruptionBudget, 0, len(pdbs.Items))
for _, pdb := range pdbs.Items {
if IsChildResource(kind, name, namespace, &pdb) {
childPdbs = append(childPdbs, pdb.DeepCopy())
}
}
return childPdbs, nil
}
func GetPVCForParent(client typedCore.PersistentVolumeClaimInterface, kind, name, namespace string) ([]*core.PersistentVolumeClaim, error) {
pvcs, err := client.List(meta.ListOptions{})
if err != nil {
return nil, err
}
if len(pvcs.Items) == 0 {
return []*core.PersistentVolumeClaim{}, nil
}
childPvcs := make([]*core.PersistentVolumeClaim, 0, len(pvcs.Items))
for _, pvc := range pvcs.Items {
if IsChildResource(kind, name, namespace, &pvc) {
childPvcs = append(childPvcs, pvc.DeepCopy())
}
}
return childPvcs, nil
}
func GetServicesForParent(client typedCore.ServiceInterface, kind, name, namespace string) ([]*core.Service, error) {
services, err := client.List(meta.ListOptions{})
if err != nil {
return nil, err
}
if len(services.Items) == 0 {
return []*core.Service{}, nil
}
childServices := make([]*core.Service, 0, len(services.Items))
for _, service := range services.Items {
if IsChildResource(kind, name, namespace, &service) {
childServices = append(childServices, service.DeepCopy())
}
}
return childServices, nil
}
func GetServiceAccountsForParent(client typedCore.ServiceAccountInterface, kind, name, namespace string) ([]*core.ServiceAccount, error) {
serviceAccounts, err := client.List(meta.ListOptions{})
if err != nil {
return nil, err
}
if len(serviceAccounts.Items) == 0 {
return []*core.ServiceAccount{}, nil
}
childServiceAccounts := make([]*core.ServiceAccount, 0, len(serviceAccounts.Items))
for _, serviceAccount := range serviceAccounts.Items {
if IsChildResource(kind, name, namespace, &serviceAccount) {
childServiceAccounts = append(childServiceAccounts, serviceAccount.DeepCopy())
}
}
return childServiceAccounts, nil
}
func GetPodsForParent(client typedCore.PodInterface, kind, name, namespace string) ([]*core.Pod, error) {
podList, err := client.List(meta.ListOptions{})
if err != nil {
return nil, err
}
if len(podList.Items) == 0 {
return []*core.Pod{}, nil
}
pods := make([]*core.Pod, 0, len(podList.Items))
for _, pod := range podList.Items {
if IsChildResource(kind, name, namespace, &pod) {
pods = append(pods, pod.DeepCopy())
}
}
return pods, nil
}

150
pkg/util/k8sutil/map.go Normal file
View file

@ -0,0 +1,150 @@
//
// 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 Adam Janikowski
//
package k8sutil
import "regexp"
const (
kubernetesAnnotationMatch = ".*kubernetes\\.io/.*"
arangoAnnotationMatch = ".*arangodb\\.com/"
)
var (
kubernetesAnnotationRegex *regexp.Regexp
arangoAnnotationRegex *regexp.Regexp
)
func init() {
r, err := regexp.Compile(kubernetesAnnotationMatch)
if err != nil {
panic(err)
}
kubernetesAnnotationRegex = r
r, err = regexp.Compile(arangoAnnotationMatch)
if err != nil {
panic(err)
}
arangoAnnotationRegex = r
}
// MergeAnnotations into one annotations map
func MergeAnnotations(annotations ...map[string]string) map[string]string {
ret := map[string]string{}
for _, annotationMap := range annotations {
if annotationMap == nil {
continue
}
for annotation, value := range annotationMap {
ret[annotation] = value
}
}
return ret
}
// IsSecuredAnnotation check if annotation key is from secured namespace
func IsSecuredAnnotation(key string) bool {
return kubernetesAnnotationRegex.MatchString(key) || arangoAnnotationRegex.MatchString(key)
}
func GetSecuredAnnotations(annotations map[string]string) map[string]string {
if annotations == nil {
return map[string]string{}
}
filteredAnnotations := map[string]string{}
for key, value := range annotations {
if !IsSecuredAnnotation(key) {
continue
}
filteredAnnotations[key] = value
}
return filteredAnnotations
}
func filterActualAnnotations(actual, expected map[string]string) map[string]string {
if actual == nil {
return nil
}
if expected == nil {
expected = map[string]string{}
}
actualFiltered := map[string]string{}
for key, value := range actual {
if _, ok := expected[key]; IsSecuredAnnotation(key) && !ok {
continue
}
actualFiltered[key] = value
}
return actualFiltered
}
// CompareAnnotations will compare annotations, but will ignore secured annotations which are present in
// actual but not specified in expected map
func CompareAnnotations(actual, expected map[string]string) bool {
actualFiltered := filterActualAnnotations(actual, expected)
if actualFiltered == nil && expected == nil {
return true
}
if (actualFiltered == nil && expected != nil && len(expected) == 0) ||
(expected == nil && actualFiltered != nil && len(actualFiltered) == 0) {
return true
}
if actualFiltered == nil || expected == nil {
return false
}
if len(actualFiltered) != len(expected) {
return false
}
for key, value := range expected {
existingValue, existing := actualFiltered[key]
if !existing {
return false
}
if existingValue != value {
return false
}
}
return true
}

340
tests/annotations_test.go Normal file
View file

@ -0,0 +1,340 @@
//
// 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 Adam Janikowski
//
package tests
import (
"testing"
"time"
"github.com/arangodb/kube-arangodb/pkg/apis/deployment"
"github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
"github.com/dchest/uniuri"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/require"
core "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/client"
)
func addAnnotation(t *testing.T, kubeClient kubernetes.Interface, arangoClient versioned.Interface, depl *api.ArangoDeployment, annotations map[string]string) {
object, err := arangoClient.DatabaseV1().ArangoDeployments(depl.GetNamespace()).Get(depl.GetName(), meta.GetOptions{})
require.NoError(t, err)
object.Spec.Annotations = annotations
object.Spec.Coordinators.Annotations = depl.Spec.Coordinators.Annotations
_, err = arangoClient.DatabaseV1().ArangoDeployments(depl.GetNamespace()).Update(object)
require.NoError(t, err)
ensureAnnotations(t, kubeClient, object)
}
func ensureAnnotationsTimeout(t *testing.T, client kubernetes.Interface, depl *api.ArangoDeployment) func() error {
return func() error {
if err := ensureSecretAnnotations(t, client, depl); err == nil || !isInterrupt(err) {
return err
}
if err := ensurePDBAnnotation(t, client, depl); err == nil || !isInterrupt(err) {
return err
}
if err := ensurePVCAnnotation(t, client, depl); err == nil || !isInterrupt(err) {
return err
}
if err := ensureServiceAnnotation(t, client, depl); err == nil || !isInterrupt(err) {
return err
}
if err := ensureServiceAccountAnnotation(t, client, depl); err == nil || !isInterrupt(err) {
return err
}
if err := ensurePodAnnotations(t, client, depl); err == nil || !isInterrupt(err) {
return err
}
return interrupt{}
}
}
func ensureSecretAnnotations(t *testing.T, client kubernetes.Interface, depl *api.ArangoDeployment) error {
secrets, err := k8sutil.GetSecretsForParent(client.CoreV1().Secrets(depl.Namespace), deployment.ArangoDeploymentResourceKind, depl.Name, depl.Namespace)
require.NoError(t, err)
require.True(t, len(secrets) > 0)
for _, secret := range secrets {
if !k8sutil.CompareAnnotations(secret.GetAnnotations(), depl.Spec.Annotations) {
log.Info().Msgf("Annotations for Secret does not match on %s", secret.Name)
return nil
}
}
return interrupt{}
}
func getPodGroup(pod *core.Pod) api.ServerGroup {
if pod.Labels == nil {
return api.ServerGroupUnknown
}
return api.ServerGroupFromRole(pod.Labels[k8sutil.LabelKeyRole])
}
func ensurePodAnnotations(t *testing.T, client kubernetes.Interface, depl *api.ArangoDeployment) error {
pods, err := k8sutil.GetPodsForParent(client.CoreV1().Pods(depl.Namespace), deployment.ArangoDeploymentResourceKind, depl.Name, depl.Namespace)
require.NoError(t, err)
require.True(t, len(pods) > 0)
for _, pod := range pods {
group := getPodGroup(pod)
combinedAnnotations := k8sutil.MergeAnnotations(depl.Spec.Annotations, depl.Spec.GetServerGroupSpec(group).Annotations)
if !k8sutil.CompareAnnotations(pod.GetAnnotations(), combinedAnnotations) {
log.Info().Msgf("Annotations for Pod does not match on %s", pod.Name)
return nil
}
}
return interrupt{}
}
func ensurePDBAnnotation(t *testing.T, client kubernetes.Interface, depl *api.ArangoDeployment) error {
podDisruptionBudgets, err := k8sutil.GetPDBForParent(client.PolicyV1beta1().PodDisruptionBudgets(depl.Namespace), deployment.ArangoDeploymentResourceKind, depl.Name, depl.Namespace)
require.NoError(t, err)
require.True(t, len(podDisruptionBudgets) > 0)
for _, podDisruptionBudget := range podDisruptionBudgets {
if !k8sutil.CompareAnnotations(podDisruptionBudget.GetAnnotations(), depl.Spec.Annotations) {
log.Info().Msgf("Annotations for PDB does not match on %s", podDisruptionBudget.Name)
return nil
}
}
return interrupt{}
}
func ensurePVCAnnotation(t *testing.T, client kubernetes.Interface, depl *api.ArangoDeployment) error {
persistentVolumeClaims, err := k8sutil.GetPVCForParent(client.CoreV1().PersistentVolumeClaims(depl.Namespace), deployment.ArangoDeploymentResourceKind, depl.Name, depl.Namespace)
require.NoError(t, err)
require.True(t, len(persistentVolumeClaims) > 0)
for _, persistentVolumeClaim := range persistentVolumeClaims {
if !k8sutil.CompareAnnotations(persistentVolumeClaim.GetAnnotations(), depl.Spec.Annotations) {
log.Info().Msgf("Annotations for PVC does not match on %s", persistentVolumeClaim.Name)
return nil
}
}
return interrupt{}
}
func ensureServiceAnnotation(t *testing.T, client kubernetes.Interface, depl *api.ArangoDeployment) error {
services, err := k8sutil.GetServicesForParent(client.CoreV1().Services(depl.Namespace), deployment.ArangoDeploymentResourceKind, depl.Name, depl.Namespace)
require.NoError(t, err)
require.True(t, len(services) > 0)
for _, service := range services {
if !k8sutil.CompareAnnotations(service.GetAnnotations(), depl.Spec.Annotations) {
log.Info().Msgf("Annotations for Service does not match on %s", service.Name)
return nil
}
}
return interrupt{}
}
func ensureServiceAccountAnnotation(t *testing.T, client kubernetes.Interface, depl *api.ArangoDeployment) error {
serviceAccounts, err := k8sutil.GetServiceAccountsForParent(client.CoreV1().ServiceAccounts(depl.Namespace), deployment.ArangoDeploymentResourceKind, depl.Name, depl.Namespace)
require.NoError(t, err)
for _, serviceAccount := range serviceAccounts {
if !k8sutil.CompareAnnotations(serviceAccount.GetAnnotations(), depl.Spec.Annotations) {
log.Info().Msgf("Annotations for Service Account does not match on %s", serviceAccount.Name)
return nil
}
}
return interrupt{}
}
func ensureAnnotations(t *testing.T, client kubernetes.Interface, depl *api.ArangoDeployment) {
if err := timeout(2*time.Second, 5*time.Minute, ensureAnnotationsTimeout(t, client, depl)); err != nil {
require.NoError(t, err)
}
}
func TestAnnotations(t *testing.T) {
longOrSkip(t)
c := client.MustNewInCluster()
kubecli := mustNewKubeClient(t)
ns := getNamespace(t)
// Prepare deployment config
depl := newDeployment("test-annotations-" + uniuri.NewLen(4))
depl.Spec.Mode = api.NewMode(api.DeploymentModeCluster)
depl.Spec.Environment = api.NewEnvironment(api.EnvironmentProduction)
depl.Spec.SetDefaults(depl.GetName())
// Create deployment
depl, err := c.DatabaseV1().ArangoDeployments(ns).Create(depl)
require.NoError(t, err)
defer deferedCleanupDeployment(c, depl.GetName(), ns)
// Wait for deployment to be ready
if _, err := waitUntilDeployment(c, depl.GetName(), ns, deploymentIsReady()); err != nil {
t.Fatalf("Deployment not running in time: %v", err)
}
t.Run("Add annotation", func(t *testing.T) {
annotations := map[string]string{
"annotation": uniuri.NewLen(8),
}
addAnnotation(t, kubecli, c, depl, annotations)
addAnnotation(t, kubecli, c, depl, nil)
})
t.Run("Add kubernetes annotation", func(t *testing.T) {
key := "kubernetes.io/test-only-annotation"
annotations := map[string]string{
key: uniuri.NewLen(8),
"annotation": uniuri.NewLen(8),
}
addAnnotation(t, kubecli, c, depl, annotations)
addAnnotation(t, kubecli, c, depl, nil)
secrets, err := k8sutil.GetSecretsForParent(kubecli.CoreV1().Secrets(depl.Namespace),
deployment.ArangoDeploymentResourceKind,
depl.Name,
depl.Namespace)
require.NoError(t, err)
require.True(t, len(secrets) > 0)
for _, secret := range secrets {
require.NotNil(t, secret.Annotations)
_, ok := secret.Annotations[key]
require.True(t, ok)
}
})
t.Run("Add arangodb annotation", func(t *testing.T) {
key := "arangodb.com/test-only-annotation"
annotations := map[string]string{
key: uniuri.NewLen(8),
"annotation": uniuri.NewLen(8),
}
addAnnotation(t, kubecli, c, depl, annotations)
addAnnotation(t, kubecli, c, depl, nil)
secrets, err := k8sutil.GetSecretsForParent(kubecli.CoreV1().Secrets(depl.Namespace),
deployment.ArangoDeploymentResourceKind,
depl.Name,
depl.Namespace)
require.NoError(t, err)
require.True(t, len(secrets) > 0)
for _, secret := range secrets {
require.NotNil(t, secret.Annotations)
_, ok := secret.Annotations[key]
require.True(t, ok)
}
})
t.Run("Replace annotation", func(t *testing.T) {
annotations := map[string]string{
"annotation": uniuri.NewLen(8),
}
addAnnotation(t, kubecli, c, depl, annotations)
annotations["annotation"] = uniuri.NewLen(16)
addAnnotation(t, kubecli, c, depl, annotations)
addAnnotation(t, kubecli, c, depl, nil)
})
t.Run("Add annotations", func(t *testing.T) {
annotations := map[string]string{
"annotation": uniuri.NewLen(8),
"annotation2": uniuri.NewLen(16),
}
addAnnotation(t, kubecli, c, depl, annotations)
addAnnotation(t, kubecli, c, depl, nil)
})
t.Run("Add annotations for group", func(t *testing.T) {
annotations := map[string]string{
"annotation": uniuri.NewLen(8),
"annotation2": uniuri.NewLen(16),
}
depl.Spec.Coordinators.Annotations = map[string]string{
"coordinator-only": uniuri.NewLen(32),
"annotation": uniuri.NewLen(8),
}
addAnnotation(t, kubecli, c, depl, annotations)
pods, err := k8sutil.GetPodsForParent(kubecli.CoreV1().Pods(depl.Namespace),
deployment.ArangoDeploymentResourceKind,
depl.Name,
depl.Namespace)
require.NoError(t, err)
require.True(t, len(pods) > 0)
for _, pod := range pods {
require.NotNil(t, pod.Annotations)
value, ok := pod.Annotations["annotation"]
_, coordOnly := pod.Annotations["coordinator-only"]
require.True(t, ok)
if getPodGroup(pod) == api.ServerGroupCoordinators {
require.Equal(t, depl.Spec.Coordinators.Annotations["annotation"], value)
require.True(t, coordOnly)
} else {
require.Equal(t, annotations["annotation"], value)
require.False(t, coordOnly)
}
}
depl.Spec.Coordinators.Annotations = nil
addAnnotation(t, kubecli, c, depl, nil)
})
}