This is an automated email from the ASF dual-hosted git repository. astefanutti pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/master by this push: new e4f52eb Create pdbTrait that adds PodDisruptionBudget for every integration e4f52eb is described below commit e4f52eb45ce9374dd2067bf5f628b4a40f7b3e87 Author: mmelko <mme...@redhat.com> AuthorDate: Thu Oct 22 13:49:16 2020 +0200 Create pdbTrait that adds PodDisruptionBudget for every integration --- deploy/operator-role-kubernetes.yaml | 11 ++++ deploy/operator-role-olm.yaml | 11 ++++ deploy/operator-role-openshift.yaml | 11 ++++ deploy/resources.go | 12 ++-- pkg/trait/pdb.go | 109 +++++++++++++++++++++++++++++++ pkg/trait/pdb_test.go | 122 +++++++++++++++++++++++++++++++++++ pkg/trait/trait_register.go | 1 + 7 files changed, 271 insertions(+), 6 deletions(-) diff --git a/deploy/operator-role-kubernetes.yaml b/deploy/operator-role-kubernetes.yaml index 26c4c48..f194602 100644 --- a/deploy/operator-role-kubernetes.yaml +++ b/deploy/operator-role-kubernetes.yaml @@ -48,6 +48,17 @@ rules: - update - watch - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - update + - list + - patch +- apiGroups: - rbac.authorization.k8s.io resources: - roles diff --git a/deploy/operator-role-olm.yaml b/deploy/operator-role-olm.yaml index 53fa59f..d488dd1 100644 --- a/deploy/operator-role-olm.yaml +++ b/deploy/operator-role-olm.yaml @@ -48,6 +48,17 @@ rules: - update - watch - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - update + - patch + - list +- apiGroups: - rbac.authorization.k8s.io resources: - roles diff --git a/deploy/operator-role-openshift.yaml b/deploy/operator-role-openshift.yaml index 31696bc..8b20303 100644 --- a/deploy/operator-role-openshift.yaml +++ b/deploy/operator-role-openshift.yaml @@ -48,6 +48,17 @@ rules: - update - watch - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - update + - list + - patch +- apiGroups: - rbac.authorization.k8s.io resources: - roles diff --git a/deploy/resources.go b/deploy/resources.go index a47f1d4..f432da1 100644 --- a/deploy/resources.go +++ b/deploy/resources.go @@ -231,9 +231,9 @@ var assets = func() http.FileSystem { "/operator-role-kubernetes.yaml": &vfsgen۰CompressedFileInfo{ name: "operator-role-kubernetes.yaml", modTime: time.Time{}, - uncompressedSize: 2244, + uncompressedSize: 2375, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x54\xc1\x6e\x1b\x37\x10\xbd\xf3\x2b\x1e\xb4\x97\xa4\xb0\xe4\xb6\xa7\x42\x3d\xa9\x8e\xdd\x0a\x0d\x24\xc0\xab\x34\xc8\x91\xe2\x8e\x56\x53\x73\x39\x2c\xc9\x95\xec\x7e\x7d\x41\xee\x2a\x91\xa3\x04\xe8\x21\x88\xf7\xa2\x21\x77\xf6\xcd\x9b\xf7\x46\x53\x61\xfa\xed\x1e\x55\xe1\x2d\x1b\x72\x91\x1a\x24\x41\xda\x13\x16\x5e\x9b\x3d\xa1\x96\x5d\x3a\xea\x40\xb8\x93\xde\x35\x3a\xb1\x38\xbc\x5a\xd4\x77\xaf\xd1\xbb\x86\x02\xc4\x11\x [...] + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x54\xc1\x72\xdb\x36\x10\xbd\xf3\x2b\xde\x88\x97\xa4\x63\xc9\x6d\x4f\x1d\xf5\xa4\x26\x76\xab\x69\x46\x9a\x31\x95\x66\x72\x04\xc1\x15\xb5\x35\x88\x45\x01\x50\xb2\xfa\xf5\x1d\x40\x54\x22\x9b\xce\x8c\x0f\x9e\x86\x17\x2d\xc0\xe5\xdb\xb7\xfb\x9e\xb6\xc4\xf4\xf5\x9e\xa2\xc4\x07\xd6\x64\x03\x35\x88\x82\xb8\x23\x2c\x9c\xd2\x3b\x42\x25\xdb\x78\x50\x9e\x70\x2b\xbd\x6d\x54\x64\xb1\x78\xb3\xa8\x6e\xdf\xa2\xb7\x0d\x79\x88\x25\x [...] }, "/operator-role-olm-cluster.yaml": &vfsgen۰CompressedFileInfo{ name: "operator-role-olm-cluster.yaml", @@ -245,16 +245,16 @@ var assets = func() http.FileSystem { "/operator-role-olm.yaml": &vfsgen۰CompressedFileInfo{ name: "operator-role-olm.yaml", modTime: time.Time{}, - uncompressedSize: 3958, + uncompressedSize: 4089, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x56\x41\x8f\xdb\x36\x13\xbd\xeb\x57\x0c\xec\x4b\xf2\x61\x2d\x7f\xed\xa9\x70\x4f\x6e\xb2\xdb\x1a\x0d\xbc\xc0\x7a\xd3\x20\xc7\x31\x35\x96\xa6\x26\x39\x2c\x49\xd9\x71\x7e\x7d\x41\x4a\x4e\xe4\x68\xb7\x49\x81\x00\xae\x2f\xa6\x86\xa3\x99\x37\xef\x0d\x47\x9c\xc2\xec\xfb\xfd\x8a\x29\xbc\x61\x45\x36\x50\x05\x51\x20\x36\x04\x4b\x87\xaa\x21\xd8\xc8\x2e\x1e\xd1\x13\xdc\x49\x6b\x2b\x8c\x2c\x16\x5e\x2c\x37\x77\x2f\xa1\xb5\x15\x [...] + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x56\x51\x8f\xdb\x36\x0c\x7e\xf7\xaf\x20\x92\x97\x76\xb8\x38\xdb\x9e\x86\xec\x29\x6b\xef\xb6\x60\x45\x0e\xb8\x5c\x57\xf4\x91\x96\x19\x9b\x8b\x2c\x6a\x92\x9c\x34\xfd\xf5\x83\x64\xa7\xe7\xd4\x77\xeb\x15\x38\x2c\x79\x89\x4c\xd1\xe4\x47\x7e\x9f\x68\x4d\x61\xf6\x72\xbf\x6c\x0a\xef\x58\x91\xf1\x54\x42\x10\x08\x35\xc1\xd2\xa2\xaa\x09\x36\xb2\x0d\x07\x74\x04\x37\xd2\x9a\x12\x03\x8b\x81\x57\xcb\xcd\xcd\x6b\x68\x4d\x49\x0e\x [...] }, "/operator-role-openshift.yaml": &vfsgen۰CompressedFileInfo{ name: "operator-role-openshift.yaml", modTime: time.Time{}, - uncompressedSize: 3022, + uncompressedSize: 3153, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x54\x41\x8f\xdb\x46\x0f\xbd\xeb\x57\x10\xd2\x25\xf9\xb0\x96\xbf\xf6\x54\xb8\x27\x37\xd9\x6d\x8d\x06\x36\xb0\x72\x1a\xe4\x48\x8d\x68\x89\xdd\xd1\x70\x3a\x33\xb2\xe2\xfe\xfa\x42\x23\x39\xb1\xa3\x5d\xa4\x05\x02\xd8\x17\x53\x1c\xea\xf1\xf1\x3d\x6a\x32\x58\x7c\xbf\x5f\x92\xc1\x3b\x56\x64\x3c\x55\x10\x04\x42\x43\xb0\xb6\xa8\x1a\x82\x42\x0e\xa1\x47\x47\xf0\x20\x9d\xa9\x30\xb0\x18\x78\xb5\x2e\x1e\x5e\x43\x67\x2a\x72\x20\x [...] + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x54\xc1\x8e\xdb\x36\x10\xbd\xeb\x2b\x06\xd2\x25\x29\xd6\x76\xdb\x53\xe1\x9e\xdc\x64\xb7\x35\x1a\xd8\xc0\xda\x69\x90\xe3\x88\x1a\x4b\xd3\xa5\x38\x2c\x49\xd9\x71\xbf\xbe\x20\x25\x27\xf2\x6a\x17\x49\x81\x00\xeb\x8b\x47\xc3\xd1\x9b\x37\xf3\x9e\x58\xc0\xec\xfb\xfd\xb2\x02\xde\xb1\x22\xe3\xa9\x82\x20\x10\x1a\x82\x95\x45\xd5\x10\xec\xe4\x10\x4e\xe8\x08\xee\xa4\x33\x15\x06\x16\x03\xaf\x56\xbb\xbb\xd7\xd0\x99\x8a\x1c\x88\x [...] }, "/operator-role-servicemonitors.yaml": &vfsgen۰CompressedFileInfo{ name: "operator-role-servicemonitors.yaml", diff --git a/pkg/trait/pdb.go b/pkg/trait/pdb.go new file mode 100644 index 0000000..7b273ce --- /dev/null +++ b/pkg/trait/pdb.go @@ -0,0 +1,109 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. +*/ + +package trait + +import ( + "fmt" + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + "k8s.io/api/policy/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +// The Pdb trait allows to configure the PodDisruptionBudget resource. +// +// +camel-k:trait=pdb +type pdbTrait struct { + BaseTrait `property:",squash"` + MaxUnavailable string `property:"max-unavailable" json:"maxUnavailable,omitempty"` + MinAvailable string `property:"min-available" json:"minAvailable,omitempty"` +} + +func newPdbTrait() Trait { + return &pdbTrait{ + BaseTrait: NewBaseTrait("pdb", 900), + } +} + +func (t *pdbTrait) Configure(e *Environment) (bool, error) { + if t.Enabled == nil || !*t.Enabled { + return false, nil + } + + strategy, err := e.DetermineControllerStrategy() + if err != nil { + return false, fmt.Errorf("unable to determine the controller stratedy") + } + + if strategy == ControllerStrategyCronJob { + return false, fmt.Errorf("poddisruptionbudget isn't supported with cron-job controller strategy") + } + + if t.MaxUnavailable != "" && t.MinAvailable != "" { + return false, fmt.Errorf("both minAvailable and maxUnavailable can't be set simultaneously") + } + + return e.IntegrationInPhase( + v1.IntegrationPhaseDeploying, + v1.IntegrationPhaseRunning, + ), nil +} + +func (t *pdbTrait) Apply(e *Environment) error { + if pdb, err := t.generatePodDisruptionBudget(e); err == nil { + e.Resources.Add(pdb) + } else { + return err + } + return nil +} + +func (t *pdbTrait) generatePodDisruptionBudget(e *Environment) (*v1beta1.PodDisruptionBudget, error) { + if t.MaxUnavailable == "" && t.MinAvailable == "" { + t.MaxUnavailable = "1" + } + + integration := e.Integration + spec := v1beta1.PodDisruptionBudgetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + v1.IntegrationLabel: integration.Name, + }, + }, + } + + var min, max intstr.IntOrString + + if t.MaxUnavailable != "" { + max = intstr.Parse(t.MaxUnavailable) + spec.MaxUnavailable = &max + + } else { + min = intstr.Parse(t.MinAvailable) + spec.MinAvailable = &min + } + + return &v1beta1.PodDisruptionBudget{ + ObjectMeta: metav1.ObjectMeta{ + Name: integration.Name, + Namespace: integration.Namespace, + Labels: integration.Labels, + }, + Spec: spec, + }, nil +} diff --git a/pkg/trait/pdb_test.go b/pkg/trait/pdb_test.go new file mode 100644 index 0000000..d2e09aa --- /dev/null +++ b/pkg/trait/pdb_test.go @@ -0,0 +1,122 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. +*/ + +package trait + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/api/policy/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + "github.com/apache/camel-k/pkg/util/kubernetes" +) + +func TestConfigurePdbTraitDoesSucceed(t *testing.T) { + pdbTrait, environment, _ := createPdbTest() + configured, err := pdbTrait.Configure(environment) + + assert.True(t, configured) + assert.Nil(t, err) +} + +func TestConfigurePdbTraitDoesNotSucceed(t *testing.T) { + pdbTrait, environment, _ := createPdbTest() + + pdbTrait.MinAvailable = "1" + pdbTrait.MaxUnavailable = "2" + configured, err := pdbTrait.Configure(environment) + assert.NotNil(t, err) + assert.False(t, configured) +} +func TestPdbIsCreatedWithoutParametersEnabled(t *testing.T) { + pdbTrait, environment, _ := createPdbTest() + + pdb := pdbCreatedCheck(pdbTrait, environment, t) + assert.Equal(t, int32(1), pdb.Spec.MaxUnavailable.IntVal) +} + +func TestPdbIsCreatedWithMaxUnavailable(t *testing.T) { + pdbTrait, environment, _ := createPdbTest() + pdbTrait.MaxUnavailable = "1" + + pdb := pdbCreatedCheck(pdbTrait, environment, t) + assert.Equal(t, int32(1), pdb.Spec.MaxUnavailable.IntVal) +} + +func TestPdbIsCreatedWithMinAvailable(t *testing.T) { + pdbTrait, environment, _ := createPdbTest() + pdbTrait.MinAvailable = "2" + + pdb := pdbCreatedCheck(pdbTrait, environment, t) + assert.Equal(t, int32(2), pdb.Spec.MinAvailable.IntVal) +} + +func pdbCreatedCheck(pdbTrait *pdbTrait, environment *Environment, t *testing.T) *v1beta1.PodDisruptionBudget { + err := pdbTrait.Apply(environment) + assert.Nil(t, err) + pdb := findPdb(environment.Resources) + + assert.NotNil(t, pdb) + assert.Equal(t, environment.Integration.Name, pdb.Name) + assert.Equal(t, environment.Integration.Namespace, pdb.Namespace) + assert.Equal(t, environment.Integration.Labels, pdb.Labels) + return pdb +} + +func findPdb(resources *kubernetes.Collection) *v1beta1.PodDisruptionBudget { + for _, a := range resources.Items() { + if _, ok := a.(*v1beta1.PodDisruptionBudget); ok { + return a.(*v1beta1.PodDisruptionBudget) + } + } + return nil +} + +func createPdbTest() (*pdbTrait, *Environment, *appsv1.Deployment) { + trait := newPdbTrait().(*pdbTrait) + enabled := true + trait.Enabled = &enabled + + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "integration-name", + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{}, + }, + } + + environment := &Environment{ + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "integration-name", + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseDeploying, + }, + }, + Resources: kubernetes.NewCollection(deployment), + } + + return trait, environment, deployment +} diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go index 30c4356..ef171c7 100644 --- a/pkg/trait/trait_register.go +++ b/pkg/trait/trait_register.go @@ -46,4 +46,5 @@ func init() { AddToTraits(newIstioTrait) AddToTraits(newIngressTrait) AddToTraits(newOwnerTrait) + AddToTraits(newPdbTrait) }