mirror of
https://github.com/fluxcd/flagger.git
synced 2026-04-15 06:57:34 +00:00
Merge pull request #709 from worldtiki/exclude-labels
Copy labels from canary to primary workloads based on prefix rules
This commit is contained in:
@@ -125,6 +125,7 @@ Parameter | Description | Default
|
||||
`serviceAccount.name` | The name of the service account to create or use. If not set and `serviceAccount.create` is `true`, a name is generated using the Flagger fullname | `""`
|
||||
`serviceAccount.annotations` | Annotations for service account | `{}`
|
||||
`ingressAnnotationsPrefix` | Annotations prefix for ingresses | `custom.ingress.kubernetes.io`
|
||||
`includeLabelPrefix` | List of prefixes of labels that are copied when creating primary deployments or daemonsets. Use * to include all | `""`
|
||||
`rbac.create` | If `true`, create and use RBAC resources | `true`
|
||||
`rbac.pspEnabled` | If `true`, create and use a restricted pod security policy | `false`
|
||||
`crd.create` | If `true`, create Flagger's CRDs (should be enabled for Helm v2 only) | `false`
|
||||
|
||||
@@ -106,6 +106,9 @@ spec:
|
||||
{{- if .Values.ingressAnnotationsPrefix }}
|
||||
- -ingress-annotations-prefix={{ .Values.ingressAnnotationsPrefix }}
|
||||
{{- end }}
|
||||
{{- if .Values.includeLabelPrefix }}
|
||||
- -include-label-prefix={{ .Values.includeLabelPrefix }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingressClass }}
|
||||
- -ingress-class={{ .Values.ingressClass }}
|
||||
{{- end }}
|
||||
|
||||
+5
-1
@@ -43,6 +43,7 @@ var (
|
||||
logLevel string
|
||||
port string
|
||||
msteamsURL string
|
||||
includeLabelPrefix string
|
||||
slackURL string
|
||||
slackUser string
|
||||
slackChannel string
|
||||
@@ -74,6 +75,7 @@ func init() {
|
||||
flag.StringVar(&slackChannel, "slack-channel", "", "Slack channel.")
|
||||
flag.StringVar(&eventWebhook, "event-webhook", "", "Webhook for publishing flagger events")
|
||||
flag.StringVar(&msteamsURL, "msteams-url", "", "MS Teams incoming webhook URL.")
|
||||
flag.StringVar(&includeLabelPrefix, "include-label-prefix", "", "List of prefixes of labels that are copied when creating primary deployments or daemonsets. Use * to include all.")
|
||||
flag.IntVar(&threadiness, "threadiness", 2, "Worker concurrency.")
|
||||
flag.BoolVar(&zapReplaceGlobals, "zap-replace-globals", false, "Whether to change the logging level of the global zap logger.")
|
||||
flag.StringVar(&zapEncoding, "zap-encoding", "json", "Zap logger encoding.")
|
||||
@@ -184,7 +186,9 @@ func main() {
|
||||
configTracker = &canary.NopTracker{}
|
||||
}
|
||||
|
||||
canaryFactory := canary.NewFactory(kubeClient, flaggerClient, configTracker, labels, logger)
|
||||
includeLabelPrefixArray := strings.Split(includeLabelPrefix, ",")
|
||||
|
||||
canaryFactory := canary.NewFactory(kubeClient, flaggerClient, configTracker, labels, includeLabelPrefixArray, logger)
|
||||
|
||||
c := controller.NewController(
|
||||
kubeClient,
|
||||
|
||||
@@ -22,11 +22,12 @@ var (
|
||||
|
||||
// DaemonSetController is managing the operations for Kubernetes DaemonSet kind
|
||||
type DaemonSetController struct {
|
||||
kubeClient kubernetes.Interface
|
||||
flaggerClient clientset.Interface
|
||||
logger *zap.SugaredLogger
|
||||
configTracker Tracker
|
||||
labels []string
|
||||
kubeClient kubernetes.Interface
|
||||
flaggerClient clientset.Interface
|
||||
logger *zap.SugaredLogger
|
||||
configTracker Tracker
|
||||
labels []string
|
||||
includeLabelPrefix []string
|
||||
}
|
||||
|
||||
func (c *DaemonSetController) ScaleToZero(cd *flaggerv1.Canary) error {
|
||||
@@ -76,7 +77,7 @@ func (c *DaemonSetController) ScaleFromZero(cd *flaggerv1.Canary) error {
|
||||
// Initialize creates the primary DaemonSet, scales down the canary DaemonSet,
|
||||
// and returns the pod selector label and container ports
|
||||
func (c *DaemonSetController) Initialize(cd *flaggerv1.Canary) (err error) {
|
||||
err = c.createPrimaryDaemonSet(cd)
|
||||
err = c.createPrimaryDaemonSet(cd, c.includeLabelPrefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("createPrimaryDaemonSet failed: %w", err)
|
||||
}
|
||||
@@ -200,7 +201,7 @@ func (c *DaemonSetController) GetMetadata(cd *flaggerv1.Canary) (string, string,
|
||||
return label, labelValue, ports, nil
|
||||
}
|
||||
|
||||
func (c *DaemonSetController) createPrimaryDaemonSet(cd *flaggerv1.Canary) error {
|
||||
func (c *DaemonSetController) createPrimaryDaemonSet(cd *flaggerv1.Canary, includeLabelPrefix []string) error {
|
||||
targetName := cd.Spec.TargetRef.Name
|
||||
primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name)
|
||||
|
||||
@@ -215,6 +216,9 @@ func (c *DaemonSetController) createPrimaryDaemonSet(cd *flaggerv1.Canary) error
|
||||
targetName, cd.Namespace, canaryDae.Spec.UpdateStrategy.Type)
|
||||
}
|
||||
|
||||
// Create the labels map but filter unwanted labels
|
||||
labels := includeLabelsByPrefix(canaryDae.Labels, includeLabelPrefix)
|
||||
|
||||
label, labelValue, err := c.getSelectorLabel(canaryDae)
|
||||
primaryLabelValue := fmt.Sprintf("%s-primary", labelValue)
|
||||
if err != nil {
|
||||
@@ -239,11 +243,10 @@ func (c *DaemonSetController) createPrimaryDaemonSet(cd *flaggerv1.Canary) error
|
||||
// create primary daemonset
|
||||
primaryDae = &appsv1.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: primaryName,
|
||||
Namespace: cd.Namespace,
|
||||
Labels: map[string]string{
|
||||
label: primaryLabelValue,
|
||||
},
|
||||
Name: primaryName,
|
||||
Namespace: cd.Namespace,
|
||||
Labels: makePrimaryLabels(labels, primaryLabelValue, label),
|
||||
Annotations: canaryDae.Annotations,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
*metav1.NewControllerRef(cd, schema.GroupVersionKind{
|
||||
Group: flaggerv1.SchemeGroupVersion.Group,
|
||||
|
||||
@@ -20,18 +20,19 @@ import (
|
||||
|
||||
// DeploymentController is managing the operations for Kubernetes Deployment kind
|
||||
type DeploymentController struct {
|
||||
kubeClient kubernetes.Interface
|
||||
flaggerClient clientset.Interface
|
||||
logger *zap.SugaredLogger
|
||||
configTracker Tracker
|
||||
labels []string
|
||||
kubeClient kubernetes.Interface
|
||||
flaggerClient clientset.Interface
|
||||
logger *zap.SugaredLogger
|
||||
configTracker Tracker
|
||||
labels []string
|
||||
includeLabelPrefix []string
|
||||
}
|
||||
|
||||
// Initialize creates the primary deployment, hpa,
|
||||
// scales to zero the canary deployment and returns the pod selector label and container ports
|
||||
func (c *DeploymentController) Initialize(cd *flaggerv1.Canary) (err error) {
|
||||
primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name)
|
||||
if err := c.createPrimaryDeployment(cd); err != nil {
|
||||
if err := c.createPrimaryDeployment(cd, c.includeLabelPrefix); err != nil {
|
||||
return fmt.Errorf("createPrimaryDeployment failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -202,15 +203,18 @@ func (c *DeploymentController) GetMetadata(cd *flaggerv1.Canary) (string, string
|
||||
|
||||
return label, labelValue, ports, nil
|
||||
}
|
||||
func (c *DeploymentController) createPrimaryDeployment(cd *flaggerv1.Canary) error {
|
||||
func (c *DeploymentController) createPrimaryDeployment(cd *flaggerv1.Canary, includeLabelPrefix []string) error {
|
||||
targetName := cd.Spec.TargetRef.Name
|
||||
primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name)
|
||||
|
||||
canaryDep, err := c.kubeClient.AppsV1().Deployments(cd.Namespace).Get(context.TODO(), targetName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deplyoment %s.%s get query error: %w", targetName, cd.Namespace, err)
|
||||
return fmt.Errorf("deployment %s.%s get query error: %w", targetName, cd.Namespace, err)
|
||||
}
|
||||
|
||||
// Create the labels map but filter unwanted labels
|
||||
labels := includeLabelsByPrefix(canaryDep.Labels, includeLabelPrefix)
|
||||
|
||||
label, labelValue, err := c.getSelectorLabel(canaryDep)
|
||||
primaryLabelValue := fmt.Sprintf("%s-primary", labelValue)
|
||||
if err != nil {
|
||||
@@ -240,11 +244,10 @@ func (c *DeploymentController) createPrimaryDeployment(cd *flaggerv1.Canary) err
|
||||
// create primary deployment
|
||||
primaryDep = &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: primaryName,
|
||||
Namespace: cd.Namespace,
|
||||
Labels: map[string]string{
|
||||
label: primaryLabelValue,
|
||||
},
|
||||
Name: primaryName,
|
||||
Namespace: cd.Namespace,
|
||||
Labels: makePrimaryLabels(labels, primaryLabelValue, label),
|
||||
Annotations: canaryDep.Annotations,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
*metav1.NewControllerRef(cd, schema.GroupVersionKind{
|
||||
Group: flaggerv1.SchemeGroupVersion.Group,
|
||||
|
||||
+19
-15
@@ -8,34 +8,38 @@ import (
|
||||
)
|
||||
|
||||
type Factory struct {
|
||||
kubeClient kubernetes.Interface
|
||||
flaggerClient clientset.Interface
|
||||
logger *zap.SugaredLogger
|
||||
configTracker Tracker
|
||||
labels []string
|
||||
kubeClient kubernetes.Interface
|
||||
flaggerClient clientset.Interface
|
||||
logger *zap.SugaredLogger
|
||||
configTracker Tracker
|
||||
labels []string
|
||||
includeLabelPrefix []string
|
||||
}
|
||||
|
||||
func NewFactory(kubeClient kubernetes.Interface,
|
||||
flaggerClient clientset.Interface,
|
||||
configTracker Tracker,
|
||||
labels []string,
|
||||
includeLabelPrefix []string,
|
||||
logger *zap.SugaredLogger) *Factory {
|
||||
return &Factory{
|
||||
kubeClient: kubeClient,
|
||||
flaggerClient: flaggerClient,
|
||||
logger: logger,
|
||||
configTracker: configTracker,
|
||||
labels: labels,
|
||||
kubeClient: kubeClient,
|
||||
flaggerClient: flaggerClient,
|
||||
logger: logger,
|
||||
configTracker: configTracker,
|
||||
labels: labels,
|
||||
includeLabelPrefix: includeLabelPrefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (factory *Factory) Controller(kind string) Controller {
|
||||
deploymentCtrl := &DeploymentController{
|
||||
logger: factory.logger,
|
||||
kubeClient: factory.kubeClient,
|
||||
flaggerClient: factory.flaggerClient,
|
||||
labels: factory.labels,
|
||||
configTracker: factory.configTracker,
|
||||
logger: factory.logger,
|
||||
kubeClient: factory.kubeClient,
|
||||
flaggerClient: factory.flaggerClient,
|
||||
labels: factory.labels,
|
||||
configTracker: factory.configTracker,
|
||||
includeLabelPrefix: factory.includeLabelPrefix,
|
||||
}
|
||||
daemonSetCtrl := &DaemonSetController{
|
||||
logger: factory.logger,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
@@ -75,6 +76,20 @@ func makeAnnotations(annotations map[string]string) (map[string]string, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func includeLabelsByPrefix(labels map[string]string, includeLabelPrefixes []string) map[string]string {
|
||||
filteredLabels := make(map[string]string)
|
||||
for key, value := range labels {
|
||||
for _, includeLabelPrefix := range includeLabelPrefixes {
|
||||
if includeLabelPrefix == "*" || strings.HasPrefix(key, includeLabelPrefix) {
|
||||
filteredLabels[key] = value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filteredLabels
|
||||
}
|
||||
|
||||
func makePrimaryLabels(labels map[string]string, labelValue string, label string) map[string]string {
|
||||
res := make(map[string]string)
|
||||
for k, v := range labels {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package canary
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIncludeLabelsByPrefix(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"foo": "foo-value",
|
||||
"bar": "bar-value",
|
||||
"lorem": "ipsum",
|
||||
}
|
||||
includeLabelPrefix := []string{"foo", "lor"}
|
||||
|
||||
filteredLabels := includeLabelsByPrefix(labels, includeLabelPrefix)
|
||||
|
||||
assert.Equal(t, filteredLabels, map[string]string{
|
||||
"foo": "foo-value",
|
||||
"lorem": "ipsum",
|
||||
// bar excluded
|
||||
})
|
||||
}
|
||||
|
||||
func TestIncludeLabelsByPrefixWithWildcard(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"foo": "foo-value",
|
||||
"bar": "bar-value",
|
||||
"lorem": "ipsum",
|
||||
}
|
||||
includeLabelPrefix := []string{"*"}
|
||||
|
||||
filteredLabels := includeLabelsByPrefix(labels, includeLabelPrefix)
|
||||
|
||||
assert.Equal(t, filteredLabels, map[string]string{
|
||||
"foo": "foo-value",
|
||||
"bar": "bar-value",
|
||||
"lorem": "ipsum",
|
||||
})
|
||||
}
|
||||
|
||||
func TestMakePrimaryLabels(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"lorem": "ipsum",
|
||||
"foo": "old-bar",
|
||||
}
|
||||
|
||||
primaryLabels := makePrimaryLabels(labels, "new-bar", "foo")
|
||||
|
||||
assert.Equal(t, primaryLabels, map[string]string{
|
||||
"lorem": "ipsum", // values from old map
|
||||
"foo": "new-bar", // overriden value for a specific label
|
||||
})
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func newDaemonSetFixture(c *flaggerv1.Canary) daemonSetFixture {
|
||||
KubeClient: kubeClient,
|
||||
FlaggerClient: flaggerClient,
|
||||
}
|
||||
canaryFactory := canary.NewFactory(kubeClient, flaggerClient, configTracker, []string{"app", "name"}, logger)
|
||||
canaryFactory := canary.NewFactory(kubeClient, flaggerClient, configTracker, []string{"app", "name"}, []string{""}, logger)
|
||||
|
||||
ctrl := &Controller{
|
||||
kubeClient: kubeClient,
|
||||
|
||||
@@ -115,7 +115,7 @@ func newDeploymentFixture(c *flaggerv1.Canary) fixture {
|
||||
KubeClient: kubeClient,
|
||||
FlaggerClient: flaggerClient,
|
||||
}
|
||||
canaryFactory := canary.NewFactory(kubeClient, flaggerClient, configTracker, []string{"app", "name"}, logger)
|
||||
canaryFactory := canary.NewFactory(kubeClient, flaggerClient, configTracker, []string{"app", "name"}, []string{""}, logger)
|
||||
|
||||
ctrl := &Controller{
|
||||
kubeClient: kubeClient,
|
||||
|
||||
Reference in New Issue
Block a user