mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-15 17:51:03 +00:00
Allow to set annotations (#499)
This commit is contained in:
parent
ba3a30911f
commit
115d03f0a1
18 changed files with 1243 additions and 39 deletions
|
@ -17,14 +17,14 @@ rules:
|
||||||
resources: ["arangodeployments"]
|
resources: ["arangodeployments"]
|
||||||
verbs: ["*"]
|
verbs: ["*"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "events", "secrets"]
|
resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "events", "secrets", "serviceaccounts"]
|
||||||
verbs: ["*"]
|
verbs: ["*"]
|
||||||
- apiGroups: ["apps"]
|
- apiGroups: ["apps"]
|
||||||
resources: ["deployments", "replicasets"]
|
resources: ["deployments", "replicasets"]
|
||||||
verbs: ["get"]
|
verbs: ["get"]
|
||||||
- apiGroups: ["policy"]
|
- apiGroups: ["policy"]
|
||||||
resources: ["poddisruptionbudgets"]
|
resources: ["poddisruptionbudgets"]
|
||||||
verbs: ["get", "create", "delete"]
|
verbs: ["*"]
|
||||||
- apiGroups: ["backup.arangodb.com"]
|
- apiGroups: ["backup.arangodb.com"]
|
||||||
resources: ["arangobackuppolicies", "arangobackups"]
|
resources: ["arangobackuppolicies", "arangobackups"]
|
||||||
verbs: ["get", "list", "watch"]
|
verbs: ["get", "list", "watch"]
|
||||||
|
|
|
@ -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.
|
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`
|
### `spec.storageEngine: string`
|
||||||
|
|
||||||
This setting specifies the type of storage engine used for all servers
|
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
|
If you are using a different service account, please grant these rights
|
||||||
to that service account.
|
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`
|
### `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/)
|
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/)
|
||||||
|
|
|
@ -70,7 +70,8 @@ func (a *ArangoBackupPolicy) NewBackup(d *deployment.ArangoDeployment) *ArangoBa
|
||||||
Name: fmt.Sprintf("%s-%s", d.Name, utils.RandomString(8)),
|
Name: fmt.Sprintf("%s-%s", d.Name, utils.RandomString(8)),
|
||||||
Namespace: a.Namespace,
|
Namespace: a.Namespace,
|
||||||
|
|
||||||
Labels: d.Labels,
|
Labels: d.Labels,
|
||||||
|
Annotations: d.Annotations,
|
||||||
|
|
||||||
Finalizers: []string{
|
Finalizers: []string{
|
||||||
FinalizerArangoBackup,
|
FinalizerArangoBackup,
|
||||||
|
|
|
@ -70,7 +70,8 @@ func (a *ArangoBackupPolicy) NewBackup(d *deployment.ArangoDeployment) *ArangoBa
|
||||||
Name: fmt.Sprintf("%s-%s", d.Name, utils.RandomString(8)),
|
Name: fmt.Sprintf("%s-%s", d.Name, utils.RandomString(8)),
|
||||||
Namespace: a.Namespace,
|
Namespace: a.Namespace,
|
||||||
|
|
||||||
Labels: d.Labels,
|
Labels: d.Labels,
|
||||||
|
Annotations: d.Annotations,
|
||||||
|
|
||||||
Finalizers: []string{
|
Finalizers: []string{
|
||||||
FinalizerArangoBackup,
|
FinalizerArangoBackup,
|
||||||
|
|
|
@ -56,9 +56,12 @@ type DeploymentSpec struct {
|
||||||
DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"`
|
DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"`
|
||||||
DisableIPv6 *bool `json:"disableIPv6,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"`
|
ExternalAccess ExternalAccessSpec `json:"externalAccess"`
|
||||||
RocksDB RocksDBSpec `json:"rocksdb"`
|
RocksDB RocksDBSpec `json:"rocksdb"`
|
||||||
|
@ -105,6 +108,11 @@ func (s DeploymentSpec) GetEnvironment() Environment {
|
||||||
return EnvironmentOrDefault(s.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.
|
// GetStorageEngine returns the value of storageEngine.
|
||||||
func (s DeploymentSpec) GetStorageEngine() StorageEngine {
|
func (s DeploymentSpec) GetStorageEngine() StorageEngine {
|
||||||
return StorageEngineOrDefault(s.StorageEngine)
|
return StorageEngineOrDefault(s.StorageEngine)
|
||||||
|
|
|
@ -22,17 +22,32 @@
|
||||||
|
|
||||||
package v1
|
package v1
|
||||||
|
|
||||||
import time "time"
|
import "time"
|
||||||
|
|
||||||
type ServerGroup int
|
type ServerGroup int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
ServerGroupUnknown ServerGroup = 0
|
||||||
ServerGroupSingle ServerGroup = 1
|
ServerGroupSingle ServerGroup = 1
|
||||||
ServerGroupAgents ServerGroup = 2
|
ServerGroupAgents ServerGroup = 2
|
||||||
ServerGroupDBServers ServerGroup = 3
|
ServerGroupDBServers ServerGroup = 3
|
||||||
ServerGroupCoordinators ServerGroup = 4
|
ServerGroupCoordinators ServerGroup = 4
|
||||||
ServerGroupSyncMasters ServerGroup = 5
|
ServerGroupSyncMasters ServerGroup = 5
|
||||||
ServerGroupSyncWorkers ServerGroup = 6
|
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 (
|
var (
|
||||||
|
@ -51,17 +66,17 @@ var (
|
||||||
func (g ServerGroup) AsRole() string {
|
func (g ServerGroup) AsRole() string {
|
||||||
switch g {
|
switch g {
|
||||||
case ServerGroupSingle:
|
case ServerGroupSingle:
|
||||||
return "single"
|
return ServerGroupSingleString
|
||||||
case ServerGroupAgents:
|
case ServerGroupAgents:
|
||||||
return "agent"
|
return ServerGroupAgentsString
|
||||||
case ServerGroupDBServers:
|
case ServerGroupDBServers:
|
||||||
return "dbserver"
|
return ServerGroupDBServersString
|
||||||
case ServerGroupCoordinators:
|
case ServerGroupCoordinators:
|
||||||
return "coordinator"
|
return ServerGroupCoordinatorsString
|
||||||
case ServerGroupSyncMasters:
|
case ServerGroupSyncMasters:
|
||||||
return "syncmaster"
|
return ServerGroupSyncMastersString
|
||||||
case ServerGroupSyncWorkers:
|
case ServerGroupSyncWorkers:
|
||||||
return "syncworker"
|
return ServerGroupSyncWorkersString
|
||||||
default:
|
default:
|
||||||
return "?"
|
return "?"
|
||||||
}
|
}
|
||||||
|
@ -71,17 +86,17 @@ func (g ServerGroup) AsRole() string {
|
||||||
func (g ServerGroup) AsRoleAbbreviated() string {
|
func (g ServerGroup) AsRoleAbbreviated() string {
|
||||||
switch g {
|
switch g {
|
||||||
case ServerGroupSingle:
|
case ServerGroupSingle:
|
||||||
return "sngl"
|
return ServerGroupSingleAbbreviatedString
|
||||||
case ServerGroupAgents:
|
case ServerGroupAgents:
|
||||||
return "agnt"
|
return ServerGroupAgentsAbbreviatedString
|
||||||
case ServerGroupDBServers:
|
case ServerGroupDBServers:
|
||||||
return "prmr"
|
return ServerGroupDBServersAbbreviatedString
|
||||||
case ServerGroupCoordinators:
|
case ServerGroupCoordinators:
|
||||||
return "crdn"
|
return ServerGroupCoordinatorsAbbreviatedString
|
||||||
case ServerGroupSyncMasters:
|
case ServerGroupSyncMasters:
|
||||||
return "syma"
|
return ServerGroupSyncMastersAbbreviatedString
|
||||||
case ServerGroupSyncWorkers:
|
case ServerGroupSyncWorkers:
|
||||||
return "sywo"
|
return ServerGroupSyncWorkersAbbreviatedString
|
||||||
default:
|
default:
|
||||||
return "?"
|
return "?"
|
||||||
}
|
}
|
||||||
|
@ -140,3 +155,43 @@ func (g ServerGroup) IsExportMetrics() bool {
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -52,6 +52,8 @@ type ServerGroupSpec struct {
|
||||||
Resources v1.ResourceRequirements `json:"resources,omitempty"`
|
Resources v1.ResourceRequirements `json:"resources,omitempty"`
|
||||||
// Tolerations specifies the tolerations added to Pods in this group.
|
// Tolerations specifies the tolerations added to Pods in this group.
|
||||||
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
|
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 specifies the name of the service account used for Pods in this group.
|
||||||
ServiceAccountName *string `json:"serviceAccountName,omitempty"`
|
ServiceAccountName *string `json:"serviceAccountName,omitempty"`
|
||||||
// NodeSelector speficies a set of selectors for nodes
|
// NodeSelector speficies a set of selectors for nodes
|
||||||
|
@ -133,6 +135,11 @@ func (s ServerGroupSpec) GetNodeSelector() map[string]string {
|
||||||
return s.NodeSelector
|
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.
|
// GetArgs returns the value of args.
|
||||||
func (s ServerGroupSpec) GetArgs() []string {
|
func (s ServerGroupSpec) GetArgs() []string {
|
||||||
return s.Args
|
return s.Args
|
||||||
|
|
26
pkg/apis/deployment/v1/zz_generated.deepcopy.go
generated
26
pkg/apis/deployment/v1/zz_generated.deepcopy.go
generated
|
@ -27,7 +27,7 @@ package v1
|
||||||
import (
|
import (
|
||||||
time "time"
|
time "time"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
@ -270,7 +270,7 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
|
||||||
}
|
}
|
||||||
if in.ImagePullPolicy != nil {
|
if in.ImagePullPolicy != nil {
|
||||||
in, out := &in.ImagePullPolicy, &out.ImagePullPolicy
|
in, out := &in.ImagePullPolicy, &out.ImagePullPolicy
|
||||||
*out = new(v1.PullPolicy)
|
*out = new(corev1.PullPolicy)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
if in.ImagePullSecrets != nil {
|
if in.ImagePullSecrets != nil {
|
||||||
|
@ -293,6 +293,13 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
|
||||||
*out = new(bool)
|
*out = new(bool)
|
||||||
**out = **in
|
**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 {
|
if in.RestoreFrom != nil {
|
||||||
in, out := &in.RestoreFrom, &out.RestoreFrom
|
in, out := &in.RestoreFrom, &out.RestoreFrom
|
||||||
*out = new(string)
|
*out = new(string)
|
||||||
|
@ -563,7 +570,7 @@ func (in *MemberStatus) DeepCopyInto(out *MemberStatus) {
|
||||||
}
|
}
|
||||||
if in.SideCarSpecs != nil {
|
if in.SideCarSpecs != nil {
|
||||||
in, out := &in.SideCarSpecs, &out.SideCarSpecs
|
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 {
|
for key, val := range *in {
|
||||||
(*out)[key] = *val.DeepCopy()
|
(*out)[key] = *val.DeepCopy()
|
||||||
}
|
}
|
||||||
|
@ -834,11 +841,18 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
|
||||||
in.Resources.DeepCopyInto(&out.Resources)
|
in.Resources.DeepCopyInto(&out.Resources)
|
||||||
if in.Tolerations != nil {
|
if in.Tolerations != nil {
|
||||||
in, out := &in.Tolerations, &out.Tolerations
|
in, out := &in.Tolerations, &out.Tolerations
|
||||||
*out = make([]v1.Toleration, len(*in))
|
*out = make([]corev1.Toleration, len(*in))
|
||||||
for i := range *in {
|
for i := range *in {
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*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 {
|
if in.ServiceAccountName != nil {
|
||||||
in, out := &in.ServiceAccountName, &out.ServiceAccountName
|
in, out := &in.ServiceAccountName, &out.ServiceAccountName
|
||||||
*out = new(string)
|
*out = new(string)
|
||||||
|
@ -858,12 +872,12 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
|
||||||
}
|
}
|
||||||
if in.VolumeClaimTemplate != nil {
|
if in.VolumeClaimTemplate != nil {
|
||||||
in, out := &in.VolumeClaimTemplate, &out.VolumeClaimTemplate
|
in, out := &in.VolumeClaimTemplate, &out.VolumeClaimTemplate
|
||||||
*out = new(v1.PersistentVolumeClaim)
|
*out = new(corev1.PersistentVolumeClaim)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
if in.Sidecars != nil {
|
if in.Sidecars != nil {
|
||||||
in, out := &in.Sidecars, &out.Sidecars
|
in, out := &in.Sidecars, &out.Sidecars
|
||||||
*out = make([]v1.Container, len(*in))
|
*out = make([]corev1.Container, len(*in))
|
||||||
for i := range *in {
|
for i := range *in {
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,9 +56,12 @@ type DeploymentSpec struct {
|
||||||
DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"`
|
DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"`
|
||||||
DisableIPv6 *bool `json:"disableIPv6,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"`
|
ExternalAccess ExternalAccessSpec `json:"externalAccess"`
|
||||||
RocksDB RocksDBSpec `json:"rocksdb"`
|
RocksDB RocksDBSpec `json:"rocksdb"`
|
||||||
|
@ -105,6 +108,11 @@ func (s DeploymentSpec) GetEnvironment() Environment {
|
||||||
return EnvironmentOrDefault(s.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.
|
// GetStorageEngine returns the value of storageEngine.
|
||||||
func (s DeploymentSpec) GetStorageEngine() StorageEngine {
|
func (s DeploymentSpec) GetStorageEngine() StorageEngine {
|
||||||
return StorageEngineOrDefault(s.StorageEngine)
|
return StorageEngineOrDefault(s.StorageEngine)
|
||||||
|
|
|
@ -27,12 +27,27 @@ import time "time"
|
||||||
type ServerGroup int
|
type ServerGroup int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
ServerGroupUnknown ServerGroup = 0
|
||||||
ServerGroupSingle ServerGroup = 1
|
ServerGroupSingle ServerGroup = 1
|
||||||
ServerGroupAgents ServerGroup = 2
|
ServerGroupAgents ServerGroup = 2
|
||||||
ServerGroupDBServers ServerGroup = 3
|
ServerGroupDBServers ServerGroup = 3
|
||||||
ServerGroupCoordinators ServerGroup = 4
|
ServerGroupCoordinators ServerGroup = 4
|
||||||
ServerGroupSyncMasters ServerGroup = 5
|
ServerGroupSyncMasters ServerGroup = 5
|
||||||
ServerGroupSyncWorkers ServerGroup = 6
|
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 (
|
var (
|
||||||
|
@ -51,17 +66,17 @@ var (
|
||||||
func (g ServerGroup) AsRole() string {
|
func (g ServerGroup) AsRole() string {
|
||||||
switch g {
|
switch g {
|
||||||
case ServerGroupSingle:
|
case ServerGroupSingle:
|
||||||
return "single"
|
return ServerGroupSingleString
|
||||||
case ServerGroupAgents:
|
case ServerGroupAgents:
|
||||||
return "agent"
|
return ServerGroupAgentsString
|
||||||
case ServerGroupDBServers:
|
case ServerGroupDBServers:
|
||||||
return "dbserver"
|
return ServerGroupDBServersString
|
||||||
case ServerGroupCoordinators:
|
case ServerGroupCoordinators:
|
||||||
return "coordinator"
|
return ServerGroupCoordinatorsString
|
||||||
case ServerGroupSyncMasters:
|
case ServerGroupSyncMasters:
|
||||||
return "syncmaster"
|
return ServerGroupSyncMastersString
|
||||||
case ServerGroupSyncWorkers:
|
case ServerGroupSyncWorkers:
|
||||||
return "syncworker"
|
return ServerGroupSyncWorkersString
|
||||||
default:
|
default:
|
||||||
return "?"
|
return "?"
|
||||||
}
|
}
|
||||||
|
@ -71,17 +86,17 @@ func (g ServerGroup) AsRole() string {
|
||||||
func (g ServerGroup) AsRoleAbbreviated() string {
|
func (g ServerGroup) AsRoleAbbreviated() string {
|
||||||
switch g {
|
switch g {
|
||||||
case ServerGroupSingle:
|
case ServerGroupSingle:
|
||||||
return "sngl"
|
return ServerGroupSingleAbbreviatedString
|
||||||
case ServerGroupAgents:
|
case ServerGroupAgents:
|
||||||
return "agnt"
|
return ServerGroupAgentsAbbreviatedString
|
||||||
case ServerGroupDBServers:
|
case ServerGroupDBServers:
|
||||||
return "prmr"
|
return ServerGroupDBServersAbbreviatedString
|
||||||
case ServerGroupCoordinators:
|
case ServerGroupCoordinators:
|
||||||
return "crdn"
|
return ServerGroupCoordinatorsAbbreviatedString
|
||||||
case ServerGroupSyncMasters:
|
case ServerGroupSyncMasters:
|
||||||
return "syma"
|
return ServerGroupSyncMastersAbbreviatedString
|
||||||
case ServerGroupSyncWorkers:
|
case ServerGroupSyncWorkers:
|
||||||
return "sywo"
|
return ServerGroupSyncWorkersAbbreviatedString
|
||||||
default:
|
default:
|
||||||
return "?"
|
return "?"
|
||||||
}
|
}
|
||||||
|
@ -140,3 +155,43 @@ func (g ServerGroup) IsExportMetrics() bool {
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -52,6 +52,8 @@ type ServerGroupSpec struct {
|
||||||
Resources v1.ResourceRequirements `json:"resources,omitempty"`
|
Resources v1.ResourceRequirements `json:"resources,omitempty"`
|
||||||
// Tolerations specifies the tolerations added to Pods in this group.
|
// Tolerations specifies the tolerations added to Pods in this group.
|
||||||
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
|
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 specifies the name of the service account used for Pods in this group.
|
||||||
ServiceAccountName *string `json:"serviceAccountName,omitempty"`
|
ServiceAccountName *string `json:"serviceAccountName,omitempty"`
|
||||||
// NodeSelector speficies a set of selectors for nodes
|
// NodeSelector speficies a set of selectors for nodes
|
||||||
|
@ -133,6 +135,11 @@ func (s ServerGroupSpec) GetNodeSelector() map[string]string {
|
||||||
return s.NodeSelector
|
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.
|
// GetArgs returns the value of args.
|
||||||
func (s ServerGroupSpec) GetArgs() []string {
|
func (s ServerGroupSpec) GetArgs() []string {
|
||||||
return s.Args
|
return s.Args
|
||||||
|
|
14
pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go
generated
14
pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go
generated
|
@ -293,6 +293,13 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
|
||||||
*out = new(bool)
|
*out = new(bool)
|
||||||
**out = **in
|
**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 {
|
if in.RestoreFrom != nil {
|
||||||
in, out := &in.RestoreFrom, &out.RestoreFrom
|
in, out := &in.RestoreFrom, &out.RestoreFrom
|
||||||
*out = new(string)
|
*out = new(string)
|
||||||
|
@ -839,6 +846,13 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*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 {
|
if in.ServiceAccountName != nil {
|
||||||
in, out := &in.ServiceAccountName, &out.ServiceAccountName
|
in, out := &in.ServiceAccountName, &out.ServiceAccountName
|
||||||
*out = new(string)
|
*out = new(string)
|
||||||
|
|
|
@ -244,6 +244,10 @@ func (d *Deployment) run() {
|
||||||
log.Info().Msg("start running...")
|
log.Info().Msg("start running...")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := d.resources.EnsureAnnotations(); err != nil {
|
||||||
|
log.Warn().Err(err).Msg("unable to update annotations")
|
||||||
|
}
|
||||||
|
|
||||||
d.lookForServiceMonitorCRD()
|
d.lookForServiceMonitorCRD()
|
||||||
|
|
||||||
inspectionInterval := maxInspectionInterval
|
inspectionInterval := maxInspectionInterval
|
||||||
|
|
|
@ -172,6 +172,11 @@ func (d *Deployment) inspectDeployment(lastInterval util.Interval) util.Interval
|
||||||
d.CreateEvent(k8sutil.NewErrorEvent("PDB creation failed", err, d.apiObject))
|
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
|
// Create access packages
|
||||||
if err := d.createAccessPackages(); err != nil {
|
if err := d.createAccessPackages(); err != nil {
|
||||||
hasError = true
|
hasError = true
|
||||||
|
|
340
pkg/deployment/resources/annotations.go
Normal file
340
pkg/deployment/resources/annotations.go
Normal 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
|
||||||
|
})
|
||||||
|
}
|
187
pkg/util/k8sutil/annotations.go
Normal file
187
pkg/util/k8sutil/annotations.go
Normal 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
150
pkg/util/k8sutil/map.go
Normal 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
340
tests/annotations_test.go
Normal 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)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue