diff --git a/api/v1alpha1/mcrouter_types.go b/api/v1alpha1/mcrouter_types.go deleted file mode 100755 index 182553f1..00000000 --- a/api/v1alpha1/mcrouter_types.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2020 VEXXHOST, Inc. -// -// 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 v1alpha1 - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// McrouterPoolSpec defines the desired state of an Mcrouter pool -type McrouterPoolSpec struct { - Servers []string `json:"servers"` -} - -// McrouterSpec defines the desired state of Mcrouter -type McrouterSpec struct { - Pools map[string]McrouterPoolSpec `json:"pools"` - Route string `json:"route"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - Tolerations []v1.Toleration `json:"tolerations,omitempty"` -} - -// McrouterStatus defines the observed state of Mcrouter -type McrouterStatus struct { - // +kubebuilder:validation:Default=Pending - Phase string `json:"phase"` -} - -// +kubebuilder:object:root=true - -// Mcrouter is the Schema for the mcrouters API -type Mcrouter struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec McrouterSpec `json:"spec,omitempty"` - Status McrouterStatus `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// McrouterList contains a list of Mcrouter -type McrouterList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Mcrouter `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Mcrouter{}, &McrouterList{}) -} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index df5b3427..4782fbe4 100755 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -23,136 +23,6 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Mcrouter) DeepCopyInto(out *Mcrouter) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mcrouter. -func (in *Mcrouter) DeepCopy() *Mcrouter { - if in == nil { - return nil - } - out := new(Mcrouter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Mcrouter) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *McrouterList) DeepCopyInto(out *McrouterList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Mcrouter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new McrouterList. -func (in *McrouterList) DeepCopy() *McrouterList { - if in == nil { - return nil - } - out := new(McrouterList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *McrouterList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *McrouterPoolSpec) DeepCopyInto(out *McrouterPoolSpec) { - *out = *in - if in.Servers != nil { - in, out := &in.Servers, &out.Servers - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new McrouterPoolSpec. -func (in *McrouterPoolSpec) DeepCopy() *McrouterPoolSpec { - if in == nil { - return nil - } - out := new(McrouterPoolSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *McrouterSpec) DeepCopyInto(out *McrouterSpec) { - *out = *in - if in.Pools != nil { - in, out := &in.Pools, &out.Pools - *out = make(map[string]McrouterPoolSpec, len(*in)) - for key, val := range *in { - (*out)[key] = *val.DeepCopy() - } - } - if in.NodeSelector != nil { - in, out := &in.NodeSelector, &out.NodeSelector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Tolerations != nil { - in, out := &in.Tolerations, &out.Tolerations - *out = make([]v1.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new McrouterSpec. -func (in *McrouterSpec) DeepCopy() *McrouterSpec { - if in == nil { - return nil - } - out := new(McrouterSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *McrouterStatus) DeepCopyInto(out *McrouterStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new McrouterStatus. -func (in *McrouterStatus) DeepCopy() *McrouterStatus { - if in == nil { - return nil - } - out := new(McrouterStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Rabbitmq) DeepCopyInto(out *Rabbitmq) { *out = *in diff --git a/builders/mcrouter.go b/builders/mcrouter.go deleted file mode 100644 index 15daedbb..00000000 --- a/builders/mcrouter.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2020 VEXXHOST, Inc. -// -// 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 builders - -import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "k8s.io/apimachinery/pkg/runtime" - infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" -) - -// McrouterBuilder defines the interface to build a Mcrouter -type McrouterBuilder struct { - obj *infrastructurev1alpha1.Mcrouter - poolSpecs map[string]*McrouterPoolSpecBuilder - owner metav1.Object - scheme *runtime.Scheme -} - -// Mcrouter returns a new mcrouter builder -func Mcrouter(existing *infrastructurev1alpha1.Mcrouter, owner metav1.Object, scheme *runtime.Scheme) *McrouterBuilder { - if existing.Spec.Pools == nil { - existing.Spec.Pools = map[string]infrastructurev1alpha1.McrouterPoolSpec{} - } - return &McrouterBuilder{ - obj: existing, - poolSpecs: map[string]*McrouterPoolSpecBuilder{}, - owner: owner, - scheme: scheme, - } -} - -// Labels specifies labels for the Mcrouter -func (z *McrouterBuilder) Labels(labels map[string]string) *McrouterBuilder { - z.obj.ObjectMeta.Labels = labels - return z -} - -// NodeSelector defines a NodeSelector for Mcrouter -func (z *McrouterBuilder) NodeSelector(selector map[string]string) *McrouterBuilder { - z.obj.Spec.NodeSelector = selector - return z -} - -// Tolerations defines tolerations for Mcrouter -func (z *McrouterBuilder) Tolerations(tolerations []v1.Toleration) *McrouterBuilder { - z.obj.Spec.Tolerations = tolerations - return z -} - -// Route defines route for Mcrouter -func (z *McrouterBuilder) Route(route string) *McrouterBuilder { - z.obj.Spec.Route = route - return z -} - -// Pool defines one set pool for Mcrouter -func (z *McrouterBuilder) Pool(poolName string, poolSpec *McrouterPoolSpecBuilder) *McrouterBuilder { - z.poolSpecs[poolName] = poolSpec - return z -} - -// Build returns a complete Mcrouter object -func (z *McrouterBuilder) Build() error { - for key, value := range z.poolSpecs { - pool, err := value.Build() - if err != nil { - return err - } - z.obj.Spec.Pools[key] = pool - } - return controllerutil.SetControllerReference(z.owner, z.obj, z.scheme) -} diff --git a/builders/mcrouter_pool_spec.go b/builders/mcrouter_pool_spec.go deleted file mode 100644 index bdfa75da..00000000 --- a/builders/mcrouter_pool_spec.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 VEXXHOST, Inc. -// -// 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 builders - -import ( - infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1" -) - -// McrouterPoolSpecBuilder defines the interface to build a McrouterPoolSpec -type McrouterPoolSpecBuilder struct { - obj *infrastructurev1alpha1.McrouterPoolSpec -} - -// McrouterPoolSpec returns a new mcrouterPoolSpec builder -func McrouterPoolSpec() *McrouterPoolSpecBuilder { - poolSpec := &infrastructurev1alpha1.McrouterPoolSpec{ - Servers: []string{}, - } - return &McrouterPoolSpecBuilder{ - obj: poolSpec, - } -} - -// Servers specifies servers for the McrouterPoolSpec -func (ps *McrouterPoolSpecBuilder) Servers(servers []string) *McrouterPoolSpecBuilder { - ps.obj.Servers = servers - return ps -} - -// Build returns a complete McrouterPoolSpec object -func (ps *McrouterPoolSpecBuilder) Build() (infrastructurev1alpha1.McrouterPoolSpec, error) { - return *ps.obj, nil -} diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index e5a96ad3..e53a48bf 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -24,6 +24,8 @@ spec: args: - run - -m + - openstack_operator.mcrouter + - -m - openstack_operator.memcached resources: limits: diff --git a/controllers/mcrouter_controller.go b/controllers/mcrouter_controller.go deleted file mode 100755 index 567b387f..00000000 --- a/controllers/mcrouter_controller.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2020 VEXXHOST, Inc. -// -// 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 ( - "context" - "encoding/json" - "fmt" - - "github.com/go-logr/logr" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - 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/baseutils" - "opendev.org/vexxhost/openstack-operator/utils/k8sutils" -) - -// McrouterReconciler reconciles a Mcrouter object -type McrouterReconciler struct { - client.Client - Log logr.Logger - Scheme *runtime.Scheme -} - -// +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=mcrouters,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=mcrouters/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=prometheusrules,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=podmonitors,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=core,resources=configmaps;services,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete - -// Reconcile does the reconcilication of Mcrouter instances -func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { - ctx := context.Background() - log := r.Log.WithValues("mcrouter", req.NamespacedName) - - var mcrouter infrastructurev1alpha1.Mcrouter - if err := r.Get(ctx, req.NamespacedName, &mcrouter); err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - // 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/managed-by": "openstack-operator", - "app.kubernetes.io/instance": req.Name, - } - - // ConfigMap - if res, err := r.ReconcileConfigMap(ctx, req, &mcrouter, log); err != nil || res != (ctrl.Result{}) { - return res, err - } - - // Deployment - if res, err := r.ReconcileDeployment(ctx, req, &mcrouter, log, labels); err != nil || res != (ctrl.Result{}) { - return res, err - } - - // PodMonitor - if res, err := r.ReconcilePodMonitor(ctx, req, &mcrouter, log, typeLabels); err != nil || res != (ctrl.Result{}) { - return res, err - } - - // Alertrule - if res, err := r.ReconcilePrometheusRule(ctx, req, &mcrouter, log, typeLabels); err != nil || res != (ctrl.Result{}) { - return res, err - } - - // Service - if res, err := r.ReconcileService(ctx, req, &mcrouter, log, labels); err != nil || res != (ctrl.Result{}) { - return res, err - } - return ctrl.Result{}, nil - -} - -// SetupWithManager initializes the controller with primary manager -func (r *McrouterReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&infrastructurev1alpha1.Mcrouter{}). - Owns(&corev1.ConfigMap{}). - Owns(&appsv1.Deployment{}). - Owns(&corev1.Service{}). - Owns(&monitoringv1.PodMonitor{}). - Owns(&monitoringv1.PrometheusRule{}). - Complete(r) -} - -// ReconcileService reconciles the service -func (r *McrouterReconciler) ReconcileService(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger, labels map[string]string) (ctrl.Result, error) { - service := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: req.Namespace, - Name: fmt.Sprintf("mcrouter-%s", req.Name), - }, - } - op, err := k8sutils.CreateOrUpdate(ctx, r, service, func() error { - return builders.Service(service, mcrouter, r.Scheme). - Port("mcrouter", 11211). - Selector(labels). - Build() - }) - if err != nil { - return ctrl.Result{}, err - } - log.WithValues("resource", "Service").WithValues("op", op).Info("Reconciled") - return ctrl.Result{}, nil -} - -// ReconcilePodMonitor reconciles the podMonitor -func (r *McrouterReconciler) ReconcilePodMonitor(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger, labels map[string]string) (ctrl.Result, error) { - podMonitor := &monitoringv1.PodMonitor{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "monitoring.coreos.com/v1", - Kind: "PodMonitor", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: req.Namespace, - Name: "mcrouter-podmonitor", - }, - } - op, err := k8sutils.CreateOrUpdate(ctx, r, podMonitor, func() error { - return builders.PodMonitor(podMonitor, mcrouter, r.Scheme). - Labels(labels). - Selector(map[string]string{ - "app.kubernetes.io/name": "mcrouter", - }). - PodMetricsEndpoints( - builders.PodMetricsEndpoint(). - Port("metrics"). - Path("/metrics"). - Interval("15s"), - ).Build() - - }) - if err != nil { - return ctrl.Result{}, err - } - log.WithValues("resource", "mcrouter-podmonitor").WithValues("op", op).Info("Reconciled") - return ctrl.Result{}, nil -} - -// ReconcilePrometheusRule reconciles the prometheusRule -func (r *McrouterReconciler) ReconcilePrometheusRule(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger, labels map[string]string) (ctrl.Result, error) { - alertRule := &monitoringv1.PrometheusRule{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: req.Namespace, - Name: "mcrouter-alertrule", - }, - } - op, err := k8sutils.CreateOrUpdate(ctx, r, alertRule, func() error { - return builders.PrometheusRule(alertRule, mcrouter, r.Scheme). - Labels(labels). - RuleGroups(builders.RuleGroup(). - Name("mcrouter-rule"). - Rules( - builders.Rule(). - Alert("McrouterDown"). - Priority(2). - Expr("mcrouter_up != 1"), - - builders.Rule(). - Alert("McrouterAllBackendsDown"). - Priority(2). - Expr("mcrouter_servers{state='up'} / mcrouter_servers == 0"), - builders.Rule(). - Alert("McrouterBackendDown"). - Priority(3). - Expr("mcrouter_servers{state='down'} != 0"), - ). - Interval("1m")). - Build() - }) - if err != nil { - return ctrl.Result{}, err - } - log.WithValues("resource", "mcrouter-alertrule").WithValues("op", op).Info("Reconciled") - return ctrl.Result{}, nil -} - -// ReconcileDeployment reconciles the deployment -func (r *McrouterReconciler) ReconcileDeployment(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger, labels map[string]string) (ctrl.Result, error) { - deployment := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: req.Namespace, - Name: fmt.Sprintf("mcrouter-%s", req.Name), - }, - } - op, err := k8sutils.CreateOrUpdate(ctx, r, deployment, func() error { - return builders.Deployment(deployment, mcrouter, r.Scheme). - Labels(labels). - Replicas(2). - PodTemplateSpec( - builders.PodTemplateSpec(). - PodSpec( - builders.PodSpec(). - NodeSelector(mcrouter.Spec.NodeSelector). - Tolerations(mcrouter.Spec.Tolerations). - Containers( - builders.Container("mcrouter", "vexxhost/mcrouter:latest"). - Args("-p", "11211", "-f", "/data/config.json"). - Port("mcrouter", 11211).PortProbe("mcrouter", 10, 30). - Resources(500, 128, 500, 2). - Volume("config", "/data"). - SecurityContext( - builders.SecurityContext(). - RunAsUser(999). - RunAsGroup(999), - ), - builders.Container("exporter", "vexxhost/mcrouter-exporter:latest"). - Args("-mcrouter.address", "localhost:11211"). - Port("metrics", 9442).HTTPProbe("metrics", "/metrics", 10, 30). - Resources(500, 128, 500, 2). - SecurityContext( - builders.SecurityContext(). - RunAsUser(1001), - ), - ). - Volumes( - builders.Volume("config").FromConfigMap(fmt.Sprintf("mcrouter-%s", req.Name)), - ), - ), - ). - Build() - }) - if err != nil { - return ctrl.Result{}, err - } - log.WithValues("resource", "Deployment").WithValues("op", op).Info("Reconciled") - return ctrl.Result{}, nil -} - -// ReconcileConfigMap reconciles the configMap -func (r *McrouterReconciler) ReconcileConfigMap(ctx context.Context, req ctrl.Request, mcrouter *infrastructurev1alpha1.Mcrouter, log logr.Logger) (ctrl.Result, error) { - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: req.Namespace, - Name: fmt.Sprintf("mcrouter-%s", req.Name), - }, - } - op, err := k8sutils.CreateOrUpdate(ctx, r, configMap, func() error { - b, err := json.Marshal(mcrouter.Spec) - if err != nil { - return err - } - return builders.ConfigMap(configMap, mcrouter, r.Scheme). - Data("config.json", string(b)). - Build() - }) - if err != nil { - return ctrl.Result{}, err - } - log.WithValues("resource", "ConfigMap").WithValues("op", op).Info("Reconciled") - return ctrl.Result{}, nil -} diff --git a/main.go b/main.go index 74afc085..68206ac0 100755 --- a/main.go +++ b/main.go @@ -74,7 +74,6 @@ func main() { designateClientBuilder.SetAuthFailed() // Setup controllers with manager - setupMcrouterReconciler(mgr) setupRabbitmqReconciler(mgr) setupZoneReconciler(mgr, designateClientBuilder) setupDesignateReconciler(mgr, designateClientBuilder) @@ -113,18 +112,6 @@ func setupDesignateReconciler(mgr ctrl.Manager, designateClientBuilder *openstac } } -// setupMcrouterReconciler setups the Mcrouter controller with manager -func setupMcrouterReconciler(mgr ctrl.Manager) { - if err := (&controllers.McrouterReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("Mcrouter"), - Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "Mcrouter") - os.Exit(1) - } -} - // setupRabbitmqReconciler setups the Rabbitmq controller with manager func setupRabbitmqReconciler(mgr ctrl.Manager) { if err := (&controllers.RabbitmqReconciler{ diff --git a/openstack_operator/mcrouter.py b/openstack_operator/mcrouter.py new file mode 100644 index 00000000..b5da8630 --- /dev/null +++ b/openstack_operator/mcrouter.py @@ -0,0 +1,66 @@ +# Copyright 2020 VEXXHOST, Inc. +# +# 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. + +"""Mcrouter Operator + +This module maintains the operator for Mcrouter, it takes care of creating +the appropriate deployments, Mcrouter, pod monitors and Prometheus rules. +""" + +import json +import kopf + +from openstack_operator import utils + + +class McrouterSpecEncoder(json.JSONEncoder): + """McrouterSpecEncoder makes kopf dictview class JSON serializable""" + + def default(self, o): # pylint: disable=E0202 + return o.__dict__["_src"]["spec"] + + +@kopf.on.resume('infrastructure.vexxhost.cloud', 'v1alpha1', 'mcrouters') +@kopf.on.create('infrastructure.vexxhost.cloud', 'v1alpha1', 'mcrouters') +def create_or_resume(name, spec, **_): + """Create and re-sync any Mcrouter instances + + This function is called when a new resource is created but also when we + start the service up for the first time. + """ + data = McrouterSpecEncoder().encode(spec) + utils.create_or_update('mcrouter/configmap.yml.j2', + name=name, data=data) + utils.create_or_update('mcrouter/deployment.yml.j2', + name=name, spec=spec) + utils.create_or_update('mcrouter/service.yml.j2', + name=name, spec=spec) + utils.create_or_update('mcrouter/podmonitor.yml.j2', + name=name, spec=spec) + utils.create_or_update('mcrouter/prometheusrule.yml.j2', + name=name, spec=spec) + + +@kopf.on.update('infrastructure.vexxhost.cloud', 'v1alpha1', 'mcrouters') +def update(name, spec, **_): + """Update a Mcrouter + + This function updates the deployment for Mcrouter if there are any + changes that happen within it. + """ + data = McrouterSpecEncoder().encode(spec) + utils.create_or_update('mcrouter/configmap.yml.j2', + name=name, data=data) + utils.create_or_update('mcrouter/deployment.yml.j2', + name=name, spec=spec) diff --git a/openstack_operator/objects.py b/openstack_operator/objects.py index 72b99029..23533e85 100644 --- a/openstack_operator/objects.py +++ b/openstack_operator/objects.py @@ -22,9 +22,11 @@ It also inclues a ``dict`` with mappings which allows doing reverse-lookups from combinations of apiVersion and kind to the exact model. """ +from pykube.objects import ConfigMap from pykube.objects import Deployment from pykube.objects import NamespacedAPIObject from pykube.objects import Pod +from pykube.objects import Service class Mcrouter(NamespacedAPIObject): @@ -53,7 +55,9 @@ class PrometheusRule(NamespacedAPIObject): MAPPING = { "v1": { + "ConfigMap": ConfigMap, "Pod": Pod, + "Service": Service, }, "apps/v1": { "Deployment": Deployment, diff --git a/openstack_operator/templates/mcrouter/configmap.yml.j2 b/openstack_operator/templates/mcrouter/configmap.yml.j2 new file mode 100644 index 00000000..393a964a --- /dev/null +++ b/openstack_operator/templates/mcrouter/configmap.yml.j2 @@ -0,0 +1,21 @@ +--- +# Copyright 2020 VEXXHOST, Inc. +# +# 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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: mcrouter-{{ name }} +data: + config.json: '{{ data }}' \ No newline at end of file diff --git a/openstack_operator/templates/mcrouter/deployment.yml.j2 b/openstack_operator/templates/mcrouter/deployment.yml.j2 new file mode 100644 index 00000000..9ae1dc03 --- /dev/null +++ b/openstack_operator/templates/mcrouter/deployment.yml.j2 @@ -0,0 +1,98 @@ +--- +# Copyright 2020 VEXXHOST, Inc. +# +# 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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mcrouter-{{ name }} + labels: + {{ labels("mcrouter", name) | indent(4) }} +spec: + replicas: 2 + selector: + matchLabels: + {{ labels("mcrouter", name) | indent(6) }} + template: + metadata: + labels: + {{ labels("mcrouter", name) | indent(8) }} + spec: + containers: + - name: mcrouter + image: vexxhost/mcrouter:latest + args: ["-p", "11211", "-f", "/data/config.json"] + imagePullPolicy: Always + ports: + - name: mcrouter + containerPort: 11211 + livenessProbe: + tcpSocket: + port: mcrouter + readinessProbe: + tcpSocket: + port: mcrouter + resources: + limits: + cpu: 50m + ephemeral-storage: 50M + memory: 128M + requests: + cpu: 10m + ephemeral-storage: 50M + memory: 64M + securityContext: + runAsUser: 1001 + volumeMounts: + - mountPath: /data + name: config + - name: exporter + image: vexxhost/mcrouter_exporter:latest + args: ["-mcrouter.address", "localhost:11211"] + imagePullPolicy: Always + ports: + - name: metrics + containerPort: 9442 + livenessProbe: + httpGet: + path: /metrics + port: metrics + readinessProbe: + httpGet: + path: /metrics + port: metrics + resources: + limits: + cpu: 100m + ephemeral-storage: 10M + memory: 64Mi + requests: + cpu: 50m + ephemeral-storage: 10M + memory: 32Mi + securityContext: + runAsUser: 1001 + volumes: + - configMap: + defaultMode: 420 + name: mcrouter-{{ name }} + name: config +{% if 'nodeSelector' in spec %} + nodeSelector: + {{ spec.nodeSelector | to_yaml | indent(8) }} +{% endif %} +{% if 'tolerations' in spec %} + tolerations: + {{ spec.tolerations | to_yaml | indent(8) }} +{% endif %} \ No newline at end of file diff --git a/openstack_operator/templates/mcrouter/podmonitor.yml.j2 b/openstack_operator/templates/mcrouter/podmonitor.yml.j2 new file mode 100644 index 00000000..22e13d52 --- /dev/null +++ b/openstack_operator/templates/mcrouter/podmonitor.yml.j2 @@ -0,0 +1,29 @@ +--- +# Copyright 2020 VEXXHOST, Inc. +# +# 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. + +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: mcrouter-podmonitor + labels: + {{ labels("mcrouter", name) | indent(4) }} +spec: + podMetricsEndpoints: + - interval: 15s + path: /metrics + port: metrics + selector: + matchLabels: + {{ labels("mcrouter", name) | indent(6) }} diff --git a/openstack_operator/templates/mcrouter/prometheusrule.yml.j2 b/openstack_operator/templates/mcrouter/prometheusrule.yml.j2 new file mode 100644 index 00000000..dae204a0 --- /dev/null +++ b/openstack_operator/templates/mcrouter/prometheusrule.yml.j2 @@ -0,0 +1,39 @@ +--- +# Copyright 2020 VEXXHOST, Inc. +# +# 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. + +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: mcrouter-alertrule + labels: + {{ labels("mcrouter", name) | indent(4) }} +spec: + groups: + - name: down + rules: + - alert: McrouterDown + expr: "mcrouter_up == 0" + annotations: + priority: P2 + - name: backend-down + rules: + - alert: McrouterAllBackendsDown + expr: "mcrouter_servers{state='up'} / mcrouter_servers == 0" + annotations: + priority: P2 + - alert: McrouterBackendDown + expr: "mcrouter_servers{state='down'} != 0" + annotations: + priority: P3 diff --git a/openstack_operator/templates/mcrouter/service.yml.j2 b/openstack_operator/templates/mcrouter/service.yml.j2 new file mode 100644 index 00000000..df07cff3 --- /dev/null +++ b/openstack_operator/templates/mcrouter/service.yml.j2 @@ -0,0 +1,28 @@ +--- +# Copyright 2020 VEXXHOST, Inc. +# +# 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. + +apiVersion: v1 +kind: Service +metadata: + name: mcrouter-{{ name }} +spec: + serviceType: ClusterIP + ports: + - name: mcrouter + port: 11211 + protocol: TCP + targetPort: mcrouter + selector: + {{ labels("mcrouter", name) | indent(4) }}