Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Add Kustomize support
- Improve Helm 3 support
- Allow to customize ID Pod selectors
- Add Label and Envs Pod customization

## [1.0.3](https://github.com/arangodb/kube-arangodb/tree/1.0.3) (2020-05-25)
- Prevent deletion of not known PVC's
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/deployment/v1/deployment_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ type DeploymentSpec struct {

// Annotations specified the annotations added to all resources
Annotations map[string]string `json:"annotations,omitempty"`
// Labels specified the labels added to all resources
Labels map[string]string `json:"labels,omitempty"`

RestoreFrom *string `json:"restoreFrom,omitempty"`

Expand Down
30 changes: 30 additions & 0 deletions pkg/apis/deployment/v1/server_group_env_var.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// DISCLAIMER
//
// Copyright 2020 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 v1

type ServerGroupEnvVars []ServerGroupEnvVar

type ServerGroupEnvVar struct {
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
Value string `json:"value,omitempty"`
}
4 changes: 4 additions & 0 deletions pkg/apis/deployment/v1/server_group_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ type ServerGroupSpec struct {
Tolerations []core.Toleration `json:"tolerations,omitempty"`
// Annotations specified the annotations added to Pods in this group.
Annotations map[string]string `json:"annotations,omitempty"`
// Labels specified the labels added to Pods in this group.
Labels map[string]string `json:"labels,omitempty"`
// Envs allow to specify additional envs in this group.
Envs ServerGroupEnvVars `json:"envs,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
Expand Down
55 changes: 55 additions & 0 deletions pkg/apis/deployment/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/deployment/deployment_inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ func (d *Deployment) ensureResources(lastInterval util.Interval, cachedStatus in
return minInspectionInterval, errors.Wrapf(err, "Annotation update failed")
}

if err := d.resources.EnsureLabels(cachedStatus); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Labels update failed")
}

return lastInterval, nil
}

Expand Down
12 changes: 11 additions & 1 deletion pkg/deployment/patch/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,18 @@ const (

var _ json.Marshaler = &Path{}

func EscapePatchElement(element string) string {
return strings.ReplaceAll(element, "/", "~1") // https://tools.ietf.org/html/rfc6901#section-3
}

func NewPath(items ...string) Path {
return items
i := make([]string, len(items))

for id, item := range items {
i[id] = EscapePatchElement(item)
}

return i
}

type Path []string
Expand Down
14 changes: 9 additions & 5 deletions pkg/deployment/reconcile/plan_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"io/ioutil"
"testing"

policy "k8s.io/api/policy/v1beta1"

"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"

backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
Expand Down Expand Up @@ -533,10 +535,12 @@ func TestCreatePlan(t *testing.T) {
ExpectedLog string
ExpectedEvent *k8sutil.Event

Pods map[string]*core.Pod
Secrets map[string]*core.Secret
Services map[string]*core.Service
PVCS map[string]*core.PersistentVolumeClaim
Pods map[string]*core.Pod
Secrets map[string]*core.Secret
Services map[string]*core.Service
PVCS map[string]*core.PersistentVolumeClaim
ServiceAccounts map[string]*core.ServiceAccount
PDBS map[string]*policy.PodDisruptionBudget
}{
{
Name: "Can not create plan for single deployment",
Expand Down Expand Up @@ -796,7 +800,7 @@ func TestCreatePlan(t *testing.T) {
if testCase.Helper != nil {
testCase.Helper(testCase.context.ArangoDeployment)
}
err, _ := r.CreatePlan(ctx, inspector.NewInspectorFromData(testCase.Pods, testCase.Secrets, testCase.PVCS, testCase.Services))
err, _ := r.CreatePlan(ctx, inspector.NewInspectorFromData(testCase.Pods, testCase.Secrets, testCase.PVCS, testCase.Services, testCase.ServiceAccounts, testCase.PDBS))

// Assert
if testCase.ExpectedEvent != nil {
Expand Down
65 changes: 38 additions & 27 deletions pkg/deployment/resources/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (r *Resources) EnsureAnnotations(cachedStatus inspector.Inspector) error {
}

if err := ensureServiceAccountsAnnotations(kubecli.CoreV1().ServiceAccounts(r.context.GetNamespace()),
cachedStatus,
deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
Expand All @@ -71,6 +72,7 @@ func (r *Resources) EnsureAnnotations(cachedStatus inspector.Inspector) error {
}

if err := ensurePdbsAnnotations(kubecli.PolicyV1beta1().PodDisruptionBudgets(r.context.GetNamespace()),
cachedStatus,
deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
Expand Down Expand Up @@ -137,22 +139,20 @@ func setSecretAnnotations(client typedCore.SecretInterface, secret *core.Secret,
})
}

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 {
func ensureServiceAccountsAnnotations(client typedCore.ServiceAccountInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string) error {
if err := cachedStatus.IterateServiceAccounts(func(serviceAccount *core.ServiceAccount) error {
if !k8sutil.CompareAnnotations(serviceAccount.GetAnnotations(), annotations) {
log.Info().Msgf("Replacing annotations for ServiceAccount %s", serviceAccount.Name)
if err = setServiceAccountAnnotations(client, serviceAccount, annotations); err != nil {
if err := setServiceAccountAnnotations(client, serviceAccount, annotations); err != nil {
return err
}
}

return nil
}, func(serviceAccount *core.ServiceAccount) bool {
return k8sutil.IsChildResource(kind, name, namespace, serviceAccount)
}); err != nil {
return err
}

return nil
Expand Down Expand Up @@ -213,22 +213,20 @@ func setServiceAnnotations(client typedCore.ServiceInterface, service *core.Serv
})
}

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 {
func ensurePdbsAnnotations(client policyTyped.PodDisruptionBudgetInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string) error {
if err := cachedStatus.IteratePodDisruptionBudgets(func(podDisruptionBudget *policy.PodDisruptionBudget) error {
if !k8sutil.CompareAnnotations(podDisruptionBudget.GetAnnotations(), annotations) {
log.Info().Msgf("Replacing annotations for PDB %s", podDisruptionBudget.Name)
if err = setPdbAnnotations(client, podDisruptionBudget, annotations); err != nil {
log.Info().Msgf("Replacing annotations for PodDisruptionBudget %s", podDisruptionBudget.Name)
if err := setPdbAnnotations(client, podDisruptionBudget, annotations); err != nil {
return err
}
}

return nil
}, func(podDisruptionBudget *policy.PodDisruptionBudget) bool {
return k8sutil.IsChildResource(kind, name, namespace, podDisruptionBudget)
}); err != nil {
return err
}

return nil
Expand Down Expand Up @@ -289,17 +287,23 @@ func setPvcAnnotations(client typedCore.PersistentVolumeClaimInterface, persiste
})
}

func getPodGroup(pod *core.Pod) api.ServerGroup {
if pod.Labels == nil {
func getObjectGroup(obj meta.Object) api.ServerGroup {
l := obj.GetLabels()
if len(l) == 0 {
return api.ServerGroupUnknown
}

return api.ServerGroupFromRole(pod.Labels[k8sutil.LabelKeyRole])
group, ok := l[k8sutil.LabelKeyRole]
if !ok {
return api.ServerGroupUnknown
}

return api.ServerGroupFromRole(group)
}

func ensurePodsAnnotations(client typedCore.PodInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string, spec api.DeploymentSpec) error {
if err := cachedStatus.IteratePods(func(pod *core.Pod) error {
group := getPodGroup(pod)
group := getObjectGroup(pod)
mergedAnnotations := k8sutil.MergeAnnotations(annotations, spec.GetServerGroupSpec(group).Annotations)

if !k8sutil.CompareAnnotations(pod.GetAnnotations(), mergedAnnotations) {
Expand Down Expand Up @@ -336,3 +340,10 @@ func setPodAnnotations(client typedCore.PodInterface, pod *core.Pod, annotations
return nil
})
}

func (r *Resources) isChildResource(obj meta.Object) bool {
return k8sutil.IsChildResource(deployment.ArangoDeploymentResourceKind,
r.context.GetAPIObject().GetName(),
r.context.GetAPIObject().GetNamespace(),
obj)
}
48 changes: 37 additions & 11 deletions pkg/deployment/resources/inspector/inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package inspector

import (
core "k8s.io/api/core/v1"
policy "k8s.io/api/policy/v1beta1"
"k8s.io/client-go/kubernetes"
)

Expand All @@ -48,19 +49,36 @@ func NewInspector(k kubernetes.Interface, namespace string) (Inspector, error) {
return nil, err
}

return NewInspectorFromData(pods, secrets, pvcs, services), nil
serviceAccounts, err := serviceAccountsToMap(k, namespace)
if err != nil {
return nil, err
}

podDisruptionBudgets, err := podDisruptionBudgetsToMap(k, namespace)
if err != nil {
return nil, err
}

return NewInspectorFromData(pods, secrets, pvcs, services, serviceAccounts, podDisruptionBudgets), nil
}

func NewEmptyInspector() Inspector {
return NewInspectorFromData(nil, nil, nil, nil)
return NewInspectorFromData(nil, nil, nil, nil, nil, nil)
}

func NewInspectorFromData(pods map[string]*core.Pod, secrets map[string]*core.Secret, pvcs map[string]*core.PersistentVolumeClaim, services map[string]*core.Service) Inspector {
func NewInspectorFromData(pods map[string]*core.Pod,
secrets map[string]*core.Secret,
pvcs map[string]*core.PersistentVolumeClaim,
services map[string]*core.Service,
serviceAccounts map[string]*core.ServiceAccount,
podDisruptionBudgets map[string]*policy.PodDisruptionBudget) Inspector {
return &inspector{
pods: pods,
secrets: secrets,
pvcs: pvcs,
services: services,
pods: pods,
secrets: secrets,
pvcs: pvcs,
services: services,
serviceAccounts: serviceAccounts,
podDisruptionBudgets: podDisruptionBudgets,
}
}

Expand All @@ -76,11 +94,19 @@ type Inspector interface {

Service(name string) (*core.Service, bool)
IterateServices(action ServiceAction, filters ...ServiceFilter) error

ServiceAccount(name string) (*core.ServiceAccount, bool)
IterateServiceAccounts(action ServiceAccountAction, filters ...ServiceAccountFilter) error

PodDisruptionBudget(name string) (*policy.PodDisruptionBudget, bool)
IteratePodDisruptionBudgets(action PodDisruptionBudgetAction, filters ...PodDisruptionBudgetFilter) error
}

type inspector struct {
pods map[string]*core.Pod
secrets map[string]*core.Secret
pvcs map[string]*core.PersistentVolumeClaim
services map[string]*core.Service
pods map[string]*core.Pod
secrets map[string]*core.Secret
pvcs map[string]*core.PersistentVolumeClaim
services map[string]*core.Service
serviceAccounts map[string]*core.ServiceAccount
podDisruptionBudgets map[string]*policy.PodDisruptionBudget
}
Loading