Add Rabbitmq

Change-Id: I9edabda696c341d86936e3ded06c5fd2e2cc94ed
This commit is contained in:
okozachenko 2020-04-01 14:17:12 -07:00
parent b9c5ef85d0
commit cd2aad7abd
19 changed files with 1099 additions and 2 deletions

View File

@ -12,6 +12,8 @@
repository: vexxhost/memcached
- context: images/memcached_exporter
repository: vexxhost/memcached_exporter
- context: images/rabbitmq
repository: vexxhost/rabbitmq
- context: .
repository: vexxhost/openstack-operator
@ -65,4 +67,4 @@
- openstack-operator:images:upload
promote:
jobs:
- openstack-operator:images:promote
- openstack-operator:images:promote

View File

@ -86,4 +86,6 @@ images:
docker build images/mcrouter -t vexxhost/mcrouter:latest
docker build images/mcrouter_exporter -t vexxhost/mcrouter_exporter:latest
docker build images/memcached -t vexxhost/memcached:latest
docker build images/memcached_exporter -t vexxhost/memcached_exporter:latest
docker build images/memcached_exporter -t vexxhost/memcached_exporter:latest
docker build images/rabbitmq -t vexxhost/rabbitmq:latest

View File

@ -0,0 +1,65 @@
package v1alpha1
import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// RabbitmqPolicySpec defines the Rabbitmq Policy Spec for the Vhost
type RabbitmqPolicyDefinitionSpec struct {
Vhost string `json:"vhost,omitempty"`
Name string `json:"name"`
Pattern string `json:"pattern"`
Definition RabbitmqPolicyDefinition `json:"definition"`
Priority int64 `json:"priority"`
ApplyTo string `json:"apply-to"`
}
// RabbitmqPolicyDefinition defines the Rabbitmq Policy content
type RabbitmqPolicyDefinition struct {
FederationUpstreamSet string `json:"federation-upstream-set,omitempty"`
HaMode string `json:"ha-mode,omitempty"`
HaParams int `json:"ha-params,omitempty"`
HaSyncMode string `json:"ha-sync-mode,omitempty"`
Expires int `json:"expires,omitempty"`
MessageTTL int `json:"message-ttl,omitempty"`
MaxLen int `json:"max-length,omitempty"`
MaxLenBytes int `json:"max-length-bytes,omitempty"`
}
// RabbitmqSpec defines the desired state of Rabbitmq
type RabbitmqSpec struct {
AuthSecret string `json:"authSecret"`
Policies []RabbitmqPolicyDefinitionSpec `json:"policies,omitempty"`
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
}
// RabbitmqStatus defines the observed state of Rabbitmq
type RabbitmqStatus struct {
// +kubebuilder:validation:Default=Pending
Phase string `json:"phase"`
}
// Rabbitmq is the Schema for the Rabbitmqs API
// +kubebuilder:object:root=true
type Rabbitmq struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec RabbitmqSpec `json:"spec,omitempty"`
Status RabbitmqStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// RabbitmqList contains a list of Rabbitmq
type RabbitmqList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Rabbitmq `json:"items"`
}
func init() {
SchemeBuilder.Register(&Rabbitmq{}, &RabbitmqList{})
}

View File

@ -257,3 +257,142 @@ func (in *MemcachedStatus) DeepCopy() *MemcachedStatus {
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
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 Rabbitmq.
func (in *Rabbitmq) DeepCopy() *Rabbitmq {
if in == nil {
return nil
}
out := new(Rabbitmq)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Rabbitmq) 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 *RabbitmqList) DeepCopyInto(out *RabbitmqList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Rabbitmq, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RabbitmqList.
func (in *RabbitmqList) DeepCopy() *RabbitmqList {
if in == nil {
return nil
}
out := new(RabbitmqList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *RabbitmqList) 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 *RabbitmqPolicyDefinition) DeepCopyInto(out *RabbitmqPolicyDefinition) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RabbitmqPolicyDefinition.
func (in *RabbitmqPolicyDefinition) DeepCopy() *RabbitmqPolicyDefinition {
if in == nil {
return nil
}
out := new(RabbitmqPolicyDefinition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RabbitmqPolicyDefinitionSpec) DeepCopyInto(out *RabbitmqPolicyDefinitionSpec) {
*out = *in
out.Definition = in.Definition
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RabbitmqPolicyDefinitionSpec.
func (in *RabbitmqPolicyDefinitionSpec) DeepCopy() *RabbitmqPolicyDefinitionSpec {
if in == nil {
return nil
}
out := new(RabbitmqPolicyDefinitionSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RabbitmqSpec) DeepCopyInto(out *RabbitmqSpec) {
*out = *in
if in.Policies != nil {
in, out := &in.Policies, &out.Policies
*out = make([]RabbitmqPolicyDefinitionSpec, len(*in))
copy(*out, *in)
}
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 RabbitmqSpec.
func (in *RabbitmqSpec) DeepCopy() *RabbitmqSpec {
if in == nil {
return nil
}
out := new(RabbitmqSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RabbitmqStatus) DeepCopyInto(out *RabbitmqStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RabbitmqStatus.
func (in *RabbitmqStatus) DeepCopy() *RabbitmqStatus {
if in == nil {
return nil
}
out := new(RabbitmqStatus)
in.DeepCopyInto(out)
return out
}

View File

@ -133,6 +133,47 @@ func (c *ContainerBuilder) Probe(handler v1.Handler, readyInterval int32, liveIn
return c
}
// EnvVarFromString register one environment variable set from the string pair.
func (c *ContainerBuilder) EnvVarFromString(name string, value string) *ContainerBuilder {
c.obj.Env = append(c.obj.Env, corev1.EnvVar{
Name: name,
Value: value,
})
return c
}
// EnvVarFromConfigMap register one environment variable set from the configMap.
func (c *ContainerBuilder) EnvVarFromConfigMap(name string, cfmName string, cfmKey string) *ContainerBuilder {
c.obj.Env = append(c.obj.Env, corev1.EnvVar{
Name: name,
ValueFrom: &corev1.EnvVarSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: cfmName,
},
Key: cfmKey,
},
},
})
return c
}
// EnvVarFromSecret register one environment variable set from the secret.
func (c *ContainerBuilder) EnvVarFromSecret(name string, scName string, scKey string) *ContainerBuilder {
c.obj.Env = append(c.obj.Env, corev1.EnvVar{
Name: name,
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: scName,
},
Key: scKey,
},
},
})
return c
}
// Build returns the object after making certain assertions
func (c *ContainerBuilder) Build() (corev1.Container, error) {
if c.securityContext == nil {

78
builders/pvc.go Normal file
View File

@ -0,0 +1,78 @@
package builders
import (
"github.com/alecthomas/units"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PersistentVolumeClaimBuilder defines the interface to build a PVC
type PersistentVolumeClaimBuilder struct {
obj *corev1.PersistentVolumeClaim
}
// PVC returns a new PVC builder
func PersistentVolumeClaim(existing *corev1.PersistentVolumeClaim) *PersistentVolumeClaimBuilder {
return &PersistentVolumeClaimBuilder{
obj: existing,
}
}
func (pvc *PersistentVolumeClaimBuilder) ReadWriteOnce() *PersistentVolumeClaimBuilder {
pvc.obj.Spec.AccessModes = []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}
return pvc
}
// Resources defines the resource configuration for the PV
func (pvc *PersistentVolumeClaimBuilder) Resources(storage int64) *PersistentVolumeClaimBuilder {
storage = storage * int64(units.Megabyte)
pvc.obj.Spec.Resources = v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: *resource.NewQuantity(storage, resource.DecimalSI),
},
}
return pvc
}
func (pvc *PersistentVolumeClaimBuilder) ReadOnlyMany() *PersistentVolumeClaimBuilder {
pvc.obj.Spec.AccessModes = []corev1.PersistentVolumeAccessMode{"ReadOnlyMany"}
return pvc
}
func (pvc *PersistentVolumeClaimBuilder) ReadWriteMany() *PersistentVolumeClaimBuilder {
pvc.obj.Spec.AccessModes = []corev1.PersistentVolumeAccessMode{"ReadWriteMany"}
return pvc
}
func (pvc *PersistentVolumeClaimBuilder) Selector(selector metav1.LabelSelector) *PersistentVolumeClaimBuilder {
pvc.obj.Spec.Selector = &selector
return pvc
}
func (pvc *PersistentVolumeClaimBuilder) VolumeName(name string) *PersistentVolumeClaimBuilder {
pvc.obj.Spec.VolumeName = name
return pvc
}
func (pvc *PersistentVolumeClaimBuilder) StorageClassName(name string) *PersistentVolumeClaimBuilder {
pvc.obj.Spec.StorageClassName = &name
return pvc
}
func (pvc *PersistentVolumeClaimBuilder) Block() *PersistentVolumeClaimBuilder {
*pvc.obj.Spec.VolumeMode = corev1.PersistentVolumeBlock
return pvc
}
func (pvc *PersistentVolumeClaimBuilder) Filesystem() *PersistentVolumeClaimBuilder {
*pvc.obj.Spec.VolumeMode = corev1.PersistentVolumeFilesystem
return pvc
}
// Build returns a complete PVC object
func (pvc *PersistentVolumeClaimBuilder) Build() (corev1.PersistentVolumeClaim, error) {
return *pvc.obj, nil
}

50
builders/secret.go Normal file
View File

@ -0,0 +1,50 @@
package builders
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// SecretBuilder defines the interface to build a Secret
type SecretBuilder struct {
obj *corev1.Secret
owner metav1.Object
scheme *runtime.Scheme
}
// Secret returns a new secret builder
func Secret(existing *corev1.Secret, owner metav1.Object, scheme *runtime.Scheme) *SecretBuilder {
existing.Data = map[string][]byte{}
existing.StringData = map[string]string{}
return &SecretBuilder{
obj: existing,
owner: owner,
scheme: scheme,
}
}
// Data sets a key inside this Secret
func (cm *SecretBuilder) Data(key, value string) *SecretBuilder {
cm.obj.Data[key] = []byte(value)
return cm
}
// StringData sets a key inside this Secret
func (cm *SecretBuilder) StringData(key, value string) *SecretBuilder {
cm.obj.StringData[key] = value
return cm
}
// SecretType sets the secret type
func (cm *SecretBuilder) SecretType(value string) *SecretBuilder {
cm.obj.Type = corev1.SecretType(value)
return cm
}
// Build returns a complete Secret object
func (cm *SecretBuilder) Build() error {
return controllerutil.SetControllerReference(cm.owner, cm.obj, cm.scheme)
}

76
builders/statefulset.go Normal file
View File

@ -0,0 +1,76 @@
package builders
import (
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// StatefulSetBuilder defines the interface to build a StatefulSet
type StatefulSetBuilder struct {
obj *appsv1.StatefulSet
podTemplateSpec *PodTemplateSpecBuilder
pvcs []*PersistentVolumeClaimBuilder
scheme *runtime.Scheme
labels map[string]string
owner metav1.Object
}
// StatefulSet returns a new StatefulSet builder
func StatefulSet(existing *appsv1.StatefulSet, owner metav1.Object, scheme *runtime.Scheme) *StatefulSetBuilder {
return &StatefulSetBuilder{
obj: existing,
labels: map[string]string{},
owner: owner,
scheme: scheme,
}
}
// Labels specifies labels for the StatefulSet
func (d *StatefulSetBuilder) Labels(labels map[string]string) *StatefulSetBuilder {
d.labels = labels
d.obj.ObjectMeta.Labels = d.labels
return d
}
// Replicas defines the number of replicas
func (d *StatefulSetBuilder) Replicas(replicas int32) *StatefulSetBuilder {
d.obj.Spec.Replicas = pointer.Int32Ptr(replicas)
return d
}
// PodTemplateSpec defines a builder for the pod template spec
func (d *StatefulSetBuilder) PodTemplateSpec(podTemplateSpec *PodTemplateSpecBuilder) *StatefulSetBuilder {
d.podTemplateSpec = podTemplateSpec
return d
}
// PVCs defines a builder array for the PVC spec
func (d *StatefulSetBuilder) PVCs(pvcs ...*PersistentVolumeClaimBuilder) *StatefulSetBuilder {
d.pvcs = pvcs
return d
}
// Build creates a final StatefulSet objet
func (d *StatefulSetBuilder) Build() error {
podTemplateSpec, err := d.podTemplateSpec.Labels(d.labels).Build()
if err != nil {
return err
}
d.obj.Spec.Template = podTemplateSpec
for _, c := range d.pvcs {
pvc, err := c.Build()
if err != nil {
return err
}
d.obj.Spec.VolumeClaimTemplates = append(d.obj.Spec.VolumeClaimTemplates, pvc)
}
return controllerutil.SetControllerReference(d.owner, d.obj, d.scheme)
}

View File

@ -33,6 +33,28 @@ func (v *VolumeBuilder) FromConfigMap(name string) *VolumeBuilder {
return v
}
// FromSecret sets the source of the volume from a Secret
func (v *VolumeBuilder) FromSecret(name string) *VolumeBuilder {
v.obj.VolumeSource = corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: name,
DefaultMode: pointer.Int32Ptr(420),
},
}
return v
}
// FromPersistentVolumeClaim sets the source of the volume from a PVC
func (v *VolumeBuilder) FromPersistentVolumeClaim(name string) *VolumeBuilder {
v.obj.VolumeSource = corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: name,
ReadOnly: false,
},
}
return v
}
// Build returns the object after checking assertions
func (v *VolumeBuilder) Build() corev1.Volume {
return *v.obj

View File

@ -0,0 +1,149 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: rabbitmqs.infrastructure.vexxhost.cloud
spec:
group: infrastructure.vexxhost.cloud
names:
kind: Rabbitmq
listKind: RabbitmqList
plural: rabbitmqs
singular: rabbitmq
scope: Namespaced
validation:
openAPIV3Schema:
description: Rabbitmq is the Schema for the Rabbitmqs API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: RabbitmqSpec defines the desired state of Rabbitmq
properties:
authSecret:
type: string
nodeSelector:
additionalProperties:
type: string
type: object
policies:
items:
description: RabbitmqPolicySpec defines the Rabbitmq Policy Spec for
the Vhost
properties:
apply-to:
type: string
definition:
description: RabbitmqPolicyDefinition defines the Rabbitmq Policy
content
properties:
expires:
type: integer
federation-upstream-set:
type: string
ha-mode:
type: string
ha-params:
type: integer
ha-sync-mode:
type: string
max-length:
type: integer
max-length-bytes:
type: integer
message-ttl:
type: integer
type: object
name:
type: string
pattern:
type: string
priority:
format: int64
type: integer
vhost:
type: string
required:
- apply-to
- definition
- name
- pattern
- priority
type: object
type: array
tolerations:
items:
description: The pod this Toleration is attached to tolerates any
taint that matches the triple <key,value,effect> using the matching
operator <operator>.
properties:
effect:
description: Effect indicates the taint effect to match. Empty
means match all taint effects. When specified, allowed values
are NoSchedule, PreferNoSchedule and NoExecute.
type: string
key:
description: Key is the taint key that the toleration applies
to. Empty means match all taint keys. If the key is empty, operator
must be Exists; this combination means to match all values and
all keys.
type: string
operator:
description: Operator represents a key's relationship to the value.
Valid operators are Exists and Equal. Defaults to Equal. Exists
is equivalent to wildcard for value, so that a pod can tolerate
all taints of a particular category.
type: string
tolerationSeconds:
description: TolerationSeconds represents the period of time the
toleration (which must be of effect NoExecute, otherwise this
field is ignored) tolerates the taint. By default, it is not
set, which means tolerate the taint forever (do not evict).
Zero and negative values will be treated as 0 (evict immediately)
by the system.
format: int64
type: integer
value:
description: Value is the taint value the toleration matches to.
If the operator is Exists, the value should be empty, otherwise
just a regular string.
type: string
type: object
type: array
required:
- authSecret
type: object
status:
description: RabbitmqStatus defines the observed state of Rabbitmq
properties:
phase:
type: string
required:
- phase
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@ -43,6 +43,19 @@ rules:
- patch
- update
- watch
- apiGroups:
- ""
resources:
- secrets
- services
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- infrastructure.vexxhost.cloud
resources:
@ -83,6 +96,26 @@ rules:
- get
- patch
- update
- apiGroups:
- infrastructure.vexxhost.cloud
resources:
- rabbitmqs
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- infrastructure.vexxhost.cloud
resources:
- rabbitmqs/status
verbs:
- get
- patch
- update
- apiGroups:
- monitoring.coreos.com
resources:

View File

@ -0,0 +1,149 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: rabbitmqs.infrastructure.vexxhost.cloud
spec:
group: infrastructure.vexxhost.cloud
names:
kind: Rabbitmq
listKind: RabbitmqList
plural: rabbitmqs
singular: rabbitmq
scope: Namespaced
validation:
openAPIV3Schema:
description: Rabbitmq is the Schema for the Rabbitmqs API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: RabbitmqSpec defines the desired state of Rabbitmq
properties:
authSecret:
type: string
nodeSelector:
additionalProperties:
type: string
type: object
policies:
items:
description: RabbitmqPolicySpec defines the Rabbitmq Policy Spec for
the Vhost
properties:
apply-to:
type: string
definition:
description: RabbitmqPolicyDefinition defines the Rabbitmq Policy
content
properties:
expires:
type: integer
federation-upstream-set:
type: string
ha-mode:
type: string
ha-params:
type: integer
ha-sync-mode:
type: string
max-length:
type: integer
max-length-bytes:
type: integer
message-ttl:
type: integer
type: object
name:
type: string
pattern:
type: string
priority:
format: int64
type: integer
vhost:
type: string
required:
- apply-to
- definition
- name
- pattern
- priority
type: object
type: array
tolerations:
items:
description: The pod this Toleration is attached to tolerates any
taint that matches the triple <key,value,effect> using the matching
operator <operator>.
properties:
effect:
description: Effect indicates the taint effect to match. Empty
means match all taint effects. When specified, allowed values
are NoSchedule, PreferNoSchedule and NoExecute.
type: string
key:
description: Key is the taint key that the toleration applies
to. Empty means match all taint keys. If the key is empty, operator
must be Exists; this combination means to match all values and
all keys.
type: string
operator:
description: Operator represents a key's relationship to the value.
Valid operators are Exists and Equal. Defaults to Equal. Exists
is equivalent to wildcard for value, so that a pod can tolerate
all taints of a particular category.
type: string
tolerationSeconds:
description: TolerationSeconds represents the period of time the
toleration (which must be of effect NoExecute, otherwise this
field is ignored) tolerates the taint. By default, it is not
set, which means tolerate the taint forever (do not evict).
Zero and negative values will be treated as 0 (evict immediately)
by the system.
format: int64
type: integer
value:
description: Value is the taint value the toleration matches to.
If the operator is Exists, the value should be empty, otherwise
just a regular string.
type: string
type: object
type: array
required:
- authSecret
type: object
status:
description: RabbitmqStatus defines the observed state of Rabbitmq
properties:
phase:
type: string
required:
- phase
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@ -4,6 +4,7 @@
resources:
- bases/infrastructure.vexxhost.cloud_mcrouters.yaml
- bases/infrastructure.vexxhost.cloud_memcacheds.yaml
- bases/infrastructure.vexxhost.cloud_rabbitmqs.yaml
- bases/monitoring.coreos.com_podmonitors.yaml
- bases/monitoring.coreos.com_prometheusrules.yaml
# +kubebuilder:scaffold:crdkustomizeresource

View File

@ -43,6 +43,19 @@ rules:
- patch
- update
- watch
- apiGroups:
- ""
resources:
- secrets
- services
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- infrastructure.vexxhost.cloud
resources:
@ -83,6 +96,26 @@ rules:
- get
- patch
- update
- apiGroups:
- infrastructure.vexxhost.cloud
resources:
- rabbitmqs
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- infrastructure.vexxhost.cloud
resources:
- rabbitmqs/status
verbs:
- get
- patch
- update
- apiGroups:
- monitoring.coreos.com
resources:

View File

@ -0,0 +1,15 @@
apiVersion: infrastructure.vexxhost.cloud/v1alpha1
kind: Rabbitmq
metadata:
name: sample
spec:
authSecret: rabbitmq-sample
---
apiVersion: v1
metadata:
name: rabbitmq-sample
namespace: default
data:
password: Y0dGemMzZHZjbVE9
username: ZFhObGNnPT0=
kind: Secret

View File

@ -0,0 +1,18 @@
# When the default StorageClass is "", use this.
---
kind: PersistentVolume
apiVersion: v1
metadata:
name: data
labels:
app.kubernetes.io/instance: sample
app.kubernetes.io/managed-by: openstack-operator
app.kubernetes.io/name: rabbitmq
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/opt/pv/rabbitmq"
persistentVolumeReclaimPolicy: Delete

View File

@ -0,0 +1,208 @@
package controllers
import (
"context"
"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"
)
// RabbitmqReconciler reconciles a Rabbitmq object
type RabbitmqReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
}
const (
_rabbitmqDefaultUsernameCfgKey = "username"
_rabbitmqDefaultPasswordCfgKey = "password"
_rabbitmqBuiltinMetricPort = 15692
_rabbitmqPort = 5672
_rabbitmqRunAsUser = 999
_rabbitmqRunAsGroup = 999
)
// +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=rabbitmqs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=rabbitmqs/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=secrets;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 Rabbitmq instances
func (r *RabbitmqReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
log := r.Log.WithValues("rabbitmq", req.NamespacedName)
var Rabbitmq infrastructurev1alpha1.Rabbitmq
if err := r.Get(ctx, req.NamespacedName, &Rabbitmq); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Labels
labels := map[string]string{
"app.kubernetes.io/name": "rabbitmq",
"app.kubernetes.io/instance": req.Name,
"app.kubernetes.io/managed-by": "openstack-operator",
}
// Deployment
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: fmt.Sprintf("rabbitmq-%s", req.Name),
},
}
op, err := utils.CreateOrUpdate(ctx, r, deployment, func() error {
return builders.Deployment(deployment, &Rabbitmq, r.Scheme).
Labels(labels).
Replicas(1).
PodTemplateSpec(
builders.PodTemplateSpec().
PodSpec(
builders.PodSpec().
NodeSelector(Rabbitmq.Spec.NodeSelector).
Tolerations(Rabbitmq.Spec.Tolerations).
Containers(
builders.Container("rabbitmq", "vexxhost/rabbitmq:latest").
EnvVarFromSecret("RABBITMQ_DEFAULT_USER", Rabbitmq.Spec.AuthSecret, _rabbitmqDefaultUsernameCfgKey).
EnvVarFromSecret("RABBITMQ_DEFAULT_PASS", Rabbitmq.Spec.AuthSecret, _rabbitmqDefaultPasswordCfgKey).
Port("rabbitmq", _rabbitmqPort).
Port("metrics", _rabbitmqBuiltinMetricPort).
PortProbe("rabbitmq", 15, 30).
Resources(500, 512, 500, 2).
SecurityContext(
builders.SecurityContext().
RunAsUser(_rabbitmqRunAsUser).
RunAsGroup(_rabbitmqRunAsGroup),
),
),
),
).
Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "Deployment").WithValues("op", op).Info("Reconciled")
// PodMonitor
podMonitor := &monitoringv1.PodMonitor{
TypeMeta: metav1.TypeMeta{
APIVersion: "monitoring.coreos.com/v1",
Kind: "PodMonitor",
},
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 {
return builders.PodMonitor(podMonitor, &Rabbitmq, r.Scheme).
Selector(map[string]string{
"app.kubernetes.io/name": "rabbitmq",
}).
PodMetricsEndpoints(
builders.PodMetricsEndpoint().
Port("metrics").
Path("/metrics").
Interval("15s"),
).Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "rabbitmq-podmonitor").WithValues("op", op).Info("Reconciled")
// Alertrule
alertRule := &monitoringv1.PrometheusRule{
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: "rabbitmq-alertrule",
},
}
op, err = utils.CreateOrUpdate(ctx, r, alertRule, func() error {
return builders.PrometheusRule(alertRule, &Rabbitmq, r.Scheme).
RuleGroups(builders.RuleGroup().
Name("rabbitmq-rule").
Rules(
builders.Rule().
Alert("RabbitmqDown").
Message("Rabbitmq node down.").
Priority(1).
Expr("rabbitmq_up == 0"),
builders.Rule().
Alert("RabbitmqTooManyConnections").
Message("RabbitMQ instance has too many connections.").
Priority(1).
Expr("rabbitmq_connectionsTotal > 1000"),
builders.Rule().
Alert("RabbitmqTooManyMessagesInQueue").
Message("Queue is filling up.").
Priority(1).
Expr("rabbitmq_queue_messages_ready > 1000"),
builders.Rule().
Alert("RabbitmqSlowQueueConsuming").
Message("Queue messages are consumed slowly.").
Priority(1).
Expr("time() - rabbitmq_queue_head_message_timestamp > 60"),
).
Interval("1m")).
Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "rabbitmq-alertrule").WithValues("op", op).Info("Reconciled")
// Service
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: req.Namespace,
Name: fmt.Sprintf("rabbitmq-%s", req.Name),
},
}
op, err = utils.CreateOrUpdate(ctx, r, service, func() error {
return builders.Service(service, &Rabbitmq, r.Scheme).
Port("epmd", 4369).
Port("amqp", 5671).
Port("distport", 25672).
Selector(labels).
Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "Service").WithValues("op", op).Info("Reconciled")
return ctrl.Result{}, nil
}
// SetupWithManager initializes the controller with primary manager
func (r *RabbitmqReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&infrastructurev1alpha1.Rabbitmq{}).
Owns(&appsv1.Deployment{}).
Owns(&corev1.Service{}).
Owns(&monitoringv1.PodMonitor{}).
Owns(&monitoringv1.PrometheusRule{}).
Complete(r)
}

View File

@ -0,0 +1,4 @@
FROM rabbitmq:latest
RUN rabbitmq-plugins enable --offline rabbitmq_prometheus && \
rabbitmq-plugins enable --offline rabbitmq_management && \
rabbitmq-plugins enable --offline rabbitmq_peer_discovery_k8s

12
main.go
View File

@ -57,6 +57,7 @@ func main() {
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
// Create manager
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
@ -69,6 +70,7 @@ func main() {
os.Exit(1)
}
// Setup controllers with manager
if err = (&controllers.McrouterReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Mcrouter"),
@ -77,6 +79,7 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "Mcrouter")
os.Exit(1)
}
if err = (&controllers.MemcachedReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Memcached"),
@ -85,6 +88,15 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "Memcached")
os.Exit(1)
}
if err = (&controllers.RabbitmqReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Rabbitmq"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Rabbitmq")
os.Exit(1)
}
// +kubebuilder:scaffold:builder
setupLog.Info("starting manager", "revision", version.Revision)