Merge labels with CR

Change-Id: Ia92c7a75cfdd24321b8e27939c1de2fe05bf1426
This commit is contained in:
okozachenko 2020-04-06 08:44:10 -07:00
parent dde446cc40
commit 5acd3683ad
15 changed files with 157 additions and 127 deletions

View File

@ -52,6 +52,7 @@
check:
jobs:
- golangci-lint
- golang-go-test
- openstack-operator:linters:chart
- openstack-operator:images:build
- openstack-operator:functional:

View File

@ -14,15 +14,15 @@ endif
all: manager
# Run tests
test: generate fmt vet manifests
test: generate fmt vet manifests lint
go test ./... -coverprofile cover.out
# Build manager binary
manager: generate fmt vet
manager: generate fmt vet lint
go build -o bin/manager main.go
# Run against the configured Kubernetes cluster in ~/.kube/config
run: generate fmt vet manifests
run: generate fmt vet manifests lint
go run ./main.go
# Install CRDs into a cluster
@ -48,6 +48,9 @@ manifests: controller-gen
fmt:
go fmt ./...
# Run golangci-lint code
lint:
golangci-lint run
# Run go vet against code
vet:
go vet ./...

View File

@ -25,6 +25,11 @@ func PodMonitor(existing *monitoringv1.PodMonitor, owner metav1.Object, scheme *
}
}
func (pm *PodMonitorBuilder) Labels(labels map[string]string) *PodMonitorBuilder {
pm.obj.Labels = labels
return pm
}
func (pm *PodMonitorBuilder) Selector(matchLabels map[string]string) *PodMonitorBuilder {
pm.obj.Spec.Selector = metav1.LabelSelector{
MatchLabels: matchLabels,

View File

@ -24,6 +24,11 @@ func PrometheusRule(existing *monitoringv1.PrometheusRule, owner metav1.Object,
}
}
func (pm *PrometheusRuleBuilder) Labels(labels map[string]string) *PrometheusRuleBuilder {
pm.obj.Labels = labels
return pm
}
// RuleGroups returns the ruleGroups
func (pm *PrometheusRuleBuilder) RuleGroups(ruleGroups ...*RuleGroupBuilder) *PrometheusRuleBuilder {
pm.ruleGroups = ruleGroups

View File

@ -2,6 +2,8 @@ apiVersion: infrastructure.vexxhost.cloud/v1alpha1
kind: Mcrouter
metadata:
name: sample
labels:
prometheus: helm
spec:
route: PoolRoute|default
pools:

View File

@ -2,5 +2,7 @@ apiVersion: infrastructure.vexxhost.cloud/v1alpha1
kind: Memcached
metadata:
name: sample
labels:
monitoring: haha
spec:
megabytes: 128

View File

@ -16,7 +16,8 @@ import (
monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1"
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
"opendev.org/vexxhost/openstack-operator/builders"
"opendev.org/vexxhost/openstack-operator/utils"
"opendev.org/vexxhost/openstack-operator/utils/baseutils"
"opendev.org/vexxhost/openstack-operator/utils/k8sutils"
)
// McrouterReconciler reconciles a Mcrouter object
@ -44,10 +45,15 @@ func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
}
// Labels
typeLabels := baseutils.MergeMapsWithoutOverwrite(map[string]string{
"app.kubernetes.io/name": "mcrouter",
"app.kubernetes.io/managed-by": "openstack-operator",
}, mcrouter.Labels)
labels := map[string]string{
"app.kubernetes.io/name": "mcrouter",
"app.kubernetes.io/instance": req.Name,
"app.kubernetes.io/managed-by": "openstack-operator",
"app.kubernetes.io/instance": req.Name,
}
// ConfigMap
@ -57,7 +63,7 @@ func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Name: fmt.Sprintf("mcrouter-%s", req.Name),
},
}
op, err := utils.CreateOrUpdate(ctx, r, configMap, func() error {
op, err := k8sutils.CreateOrUpdate(ctx, r, configMap, func() error {
b, err := json.Marshal(mcrouter.Spec)
if err != nil {
@ -80,7 +86,7 @@ func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Name: fmt.Sprintf("mcrouter-%s", req.Name),
},
}
op, err = utils.CreateOrUpdate(ctx, r, deployment, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, deployment, func() error {
return builders.Deployment(deployment, &mcrouter, r.Scheme).
Labels(labels).
Replicas(2).
@ -131,15 +137,12 @@ func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: "mcrouter-podmonitor",
Labels: map[string]string{
"app.kubernetes.io/name": "mcrouter",
"app.kubernetes.io/managed-by": "openstack-operator",
},
},
}
op, err = utils.CreateOrUpdate(ctx, r, podMonitor, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, podMonitor, func() error {
return builders.PodMonitor(podMonitor, &mcrouter, r.Scheme).
Labels(typeLabels).
Selector(map[string]string{
"app.kubernetes.io/name": "mcrouter",
}).
@ -163,13 +166,13 @@ func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Name: "mcrouter-alertrule",
},
}
op, err = utils.CreateOrUpdate(ctx, r, alertRule, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, alertRule, func() error {
return builders.PrometheusRule(alertRule, &mcrouter, r.Scheme).
Labels(typeLabels).
RuleGroups(builders.RuleGroup().
Name("mcrouter-rule").
Rules(
builders.Rule().
Alert("McrouterBackendDown").
Message("Backend Memcached servers are down.").
@ -196,7 +199,7 @@ func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Name: fmt.Sprintf("mcrouter-%s", req.Name),
},
}
op, err = utils.CreateOrUpdate(ctx, r, service, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, service, func() error {
return builders.Service(service, &mcrouter, r.Scheme).
Port("mcrouter", 11211).
Selector(labels).

View File

@ -35,7 +35,8 @@ import (
monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1"
"opendev.org/vexxhost/openstack-operator/builders"
"opendev.org/vexxhost/openstack-operator/utils"
"opendev.org/vexxhost/openstack-operator/utils/baseutils"
"opendev.org/vexxhost/openstack-operator/utils/k8sutils"
)
// MemcachedReconciler reconciles a Memcached object
@ -66,11 +67,21 @@ func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
size := memcached.Spec.Megabytes / 2
// Labels
typeLabels := baseutils.MergeMapsWithoutOverwrite(map[string]string{
"app.kubernetes.io/name": "memcached",
"app.kubernetes.io/managed-by": "openstack-operator",
}, memcached.Labels)
labels := map[string]string{
"app.kubernetes.io/name": "memcached",
"app.kubernetes.io/instance": req.Name,
"app.kubernetes.io/managed-by": "openstack-operator",
"app.kubernetes.io/instance": req.Name,
}
mcrouterLabels := baseutils.MergeMapsWithoutOverwrite(map[string]string{
"app.kubernetes.io/name": "memcached",
"app.kubernetes.io/managed-by": "openstack-operator",
"app.kubernetes.io/instance": req.Name,
}, memcached.Labels)
// Deployment
deployment := &appsv1.Deployment{
@ -81,7 +92,7 @@ func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
},
}
op, err := utils.CreateOrUpdate(ctx, r, deployment, func() error {
op, err := k8sutils.CreateOrUpdate(ctx, r, deployment, func() error {
return builders.Deployment(deployment, &memcached, r.Scheme).
Labels(labels).
Replicas(2).
@ -119,7 +130,6 @@ func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
log.WithValues("resource", "Deployment").WithValues("op", op).Info("Reconciled")
// PodMonitor
podMonitor := &monitoringv1.PodMonitor{
TypeMeta: metav1.TypeMeta{
APIVersion: "monitoring.coreos.com/v1",
@ -128,15 +138,12 @@ func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: "memcached-podmonitor",
Labels: map[string]string{
"app.kubernetes.io/name": "memcached",
"app.kubernetes.io/managed-by": "openstack-operator",
},
},
}
op, err = utils.CreateOrUpdate(ctx, r, podMonitor, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, podMonitor, func() error {
return builders.PodMonitor(podMonitor, &memcached, r.Scheme).
Labels(typeLabels).
Selector(map[string]string{
"app.kubernetes.io/name": "memcached",
}).
@ -184,9 +191,10 @@ func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Name: "memcached-alertrule",
},
}
op, err = utils.CreateOrUpdate(ctx, r, alertRule, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, alertRule, func() error {
return builders.PrometheusRule(alertRule, &memcached, r.Scheme).
Labels(typeLabels).
RuleGroups(builders.RuleGroup().
Name("memcached-rule").
Rules(
@ -213,7 +221,7 @@ func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: fmt.Sprintf("memcached-%s", req.Name),
Labels: labels,
Labels: mcrouterLabels,
},
}
op, err = controllerutil.CreateOrUpdate(ctx, r, mcrouter, func() error {

View File

@ -15,7 +15,8 @@ import (
monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1"
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
"opendev.org/vexxhost/openstack-operator/builders"
"opendev.org/vexxhost/openstack-operator/utils"
"opendev.org/vexxhost/openstack-operator/utils/baseutils"
"opendev.org/vexxhost/openstack-operator/utils/k8sutils"
)
// RabbitmqReconciler reconciles a Rabbitmq object
@ -52,10 +53,15 @@ func (r *RabbitmqReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
}
// Labels
typeLabels := baseutils.MergeMapsWithoutOverwrite(map[string]string{
"app.kubernetes.io/name": "rabbitmq",
"app.kubernetes.io/managed-by": "openstack-operator",
}, Rabbitmq.Labels)
labels := map[string]string{
"app.kubernetes.io/name": "rabbitmq",
"app.kubernetes.io/instance": req.Name,
"app.kubernetes.io/managed-by": "openstack-operator",
"app.kubernetes.io/instance": req.Name,
}
// Deployment
@ -65,7 +71,7 @@ func (r *RabbitmqReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Name: fmt.Sprintf("rabbitmq-%s", req.Name),
},
}
op, err := utils.CreateOrUpdate(ctx, r, deployment, func() error {
op, err := k8sutils.CreateOrUpdate(ctx, r, deployment, func() error {
return builders.Deployment(deployment, &Rabbitmq, r.Scheme).
Labels(labels).
Replicas(1).
@ -107,15 +113,12 @@ func (r *RabbitmqReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: "rabbitmq-podmonitor",
Labels: map[string]string{
"app.kubernetes.io/name": "rabbitmq",
"app.kubernetes.io/managed-by": "openstack-operator",
},
},
}
op, err = utils.CreateOrUpdate(ctx, r, podMonitor, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, podMonitor, func() error {
return builders.PodMonitor(podMonitor, &Rabbitmq, r.Scheme).
Labels(typeLabels).
Selector(map[string]string{
"app.kubernetes.io/name": "rabbitmq",
}).
@ -139,8 +142,9 @@ func (r *RabbitmqReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Name: "rabbitmq-alertrule",
},
}
op, err = utils.CreateOrUpdate(ctx, r, alertRule, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, alertRule, func() error {
return builders.PrometheusRule(alertRule, &Rabbitmq, r.Scheme).
Labels(typeLabels).
RuleGroups(builders.RuleGroup().
Name("rabbitmq-rule").
Rules(
@ -180,7 +184,7 @@ func (r *RabbitmqReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Name: fmt.Sprintf("rabbitmq-%s", req.Name),
},
}
op, err = utils.CreateOrUpdate(ctx, r, service, func() error {
op, err = k8sutils.CreateOrUpdate(ctx, r, service, func() error {
return builders.Service(service, &Rabbitmq, r.Scheme).
Port("epmd", 4369).
Port("amqp", 5671).

View File

@ -1,86 +0,0 @@
/*
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.
*/
package controllers
import (
"path/filepath"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
// +kubebuilder:scaffold:imports
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
}
var err error
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = infrastructurev1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = infrastructurev1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = infrastructurev1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})

3
go.mod
View File

@ -6,8 +6,7 @@ require (
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
github.com/go-logr/logr v0.1.0
github.com/google/go-cmp v0.3.0
github.com/onsi/ginkgo v1.11.0
github.com/onsi/gomega v1.8.1
github.com/stretchr/testify v1.5.1
k8s.io/api v0.17.2
k8s.io/apimachinery v0.17.2
k8s.io/client-go v0.17.2

3
go.sum
View File

@ -264,12 +264,15 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=

26
utils/baseutils/map.go Normal file
View File

@ -0,0 +1,26 @@
package baseutils
// MergeMaps merges all maps in the list
func MergeMaps(MapList ...map[string]string) map[string]string {
var baseMap = make(map[string]string)
for _, imap := range MapList {
for k, v := range imap {
baseMap[k] = v
}
}
return baseMap
}
// MergeMapsWithoutOverwrite merges all maps in the list without overwriting. The priority is the same as the sequence of the list.
func MergeMapsWithoutOverwrite(MapList ...map[string]string) map[string]string {
var baseMap = make(map[string]string)
for _, imap := range MapList {
for k, v := range imap {
if _, ok := baseMap[k]; !ok {
baseMap[k] = v
}
}
}
return baseMap
}

View File

@ -0,0 +1,55 @@
package baseutils
import (
"testing"
"github.com/stretchr/testify/assert"
)
func assertMergeMaps(t *testing.T, cr, instance, expected map[string]string) {
merged := MergeMapsWithoutOverwrite(cr, instance)
assert.Equal(t, expected, merged)
}
func TestMergeMapsWithNoInstanceLabels(t *testing.T) {
cr := map[string]string{
"foo": "bar",
}
instance := map[string]string{}
expected := map[string]string{
"foo": "bar",
}
assertMergeMaps(t, cr, instance, expected)
}
func TestMergeMapsWithDifferentInstanceLabels(t *testing.T) {
cr := map[string]string{
"foo": "bar",
}
instance := map[string]string{
"more": "options",
}
expected := map[string]string{
"foo": "bar",
"more": "options",
}
assertMergeMaps(t, cr, instance, expected)
}
func TestMergeMapsWithCustomResourceLabelOverride(t *testing.T) {
cr := map[string]string{
"foo": "bar",
}
instance := map[string]string{
"foo": "bar2",
"more": "options",
}
expected := map[string]string{
"foo": "bar",
"more": "options",
}
assertMergeMaps(t, cr, instance, expected)
}

View File

@ -1,4 +1,4 @@
package utils
package k8sutils
import (
"context"