postgres-operator/pkg/cluster/comparison.go

160 lines
6.3 KiB
Go

package cluster
import (
"reflect"
u "github.com/zalando-incubator/postgres-operator/pkg/util"
"k8s.io/api/core/v1"
"k8s.io/api/apps/v1beta1"
)
type ContainerCondition func(a, b *v1.Container) bool
type StatefulSetCondition func(a, b *v1beta1.StatefulSet) bool
type VolumeClaimCondition func(a, b *v1.PersistentVolumeClaim) bool
type ResourceCheck struct {
containerCondition ContainerCondition
statefulSetCondition StatefulSetCondition
volumeClaimCondition VolumeClaimCondition
result Result
reason string
}
type Result struct {
needUpdate *bool
needsRollUpdate *bool
needsReplace *bool
}
func (c *Cluster) NewCheck(msg string, cond interface{}, result Result) ResourceCheck {
switch cond.(type) {
case ContainerCondition:
return ResourceCheck{
reason: msg,
containerCondition: cond.(ContainerCondition),
result: result,
}
case StatefulSetCondition:
return ResourceCheck{
reason: msg,
statefulSetCondition: cond.(StatefulSetCondition),
result: result,
}
case VolumeClaimCondition:
return ResourceCheck{
reason: msg,
volumeClaimCondition: cond.(VolumeClaimCondition),
result: result,
}
default:
c.logger.Errorf("Undefined check condition type: %v", cond)
return ResourceCheck{}
}
}
func (c *Cluster) getStatefulSetChecks() []ResourceCheck {
return []ResourceCheck{
c.NewCheck("new statefulset's number of replicas doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool { return a.Spec.Replicas != b.Spec.Replicas },
Result{needUpdate: u.True()}),
c.NewCheck("new statefulset's annotations doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool { return !reflect.DeepEqual(a.Annotations, b.Annotations) },
Result{needUpdate: u.True()}),
c.NewCheck("new statefulset's serviceAccountName service asccount name doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool {
return len(a.Spec.Template.Spec.Containers) != len(b.Spec.Template.Spec.Containers)
}, Result{needsRollUpdate: u.True()}),
c.NewCheck("new statefulset's serviceAccountName service asccount name doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool {
return a.Spec.Template.Spec.ServiceAccountName !=
b.Spec.Template.Spec.ServiceAccountName
}, Result{needsRollUpdate: u.True(), needsReplace: u.True()}),
c.NewCheck("new statefulset's terminationGracePeriodSeconds doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool {
return a.Spec.Template.Spec.TerminationGracePeriodSeconds !=
b.Spec.Template.Spec.TerminationGracePeriodSeconds
}, Result{needsRollUpdate: u.True(), needsReplace: u.True()}),
c.NewCheck("new statefulset's pod affinity doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool {
return !reflect.DeepEqual(a.Spec.Template.Spec.Affinity,
b.Spec.Template.Spec.Affinity)
}, Result{needsRollUpdate: u.True(), needsReplace: u.True()}),
// Some generated fields like creationTimestamp make it not possible to
// use DeepCompare on Spec.Template.ObjectMeta
c.NewCheck("new statefulset's metadata labels doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool {
return !reflect.DeepEqual(a.Spec.Template.Labels, b.Spec.Template.Labels)
}, Result{needsRollUpdate: u.True(), needsReplace: u.True()}),
c.NewCheck("new statefulset's pod template metadata annotations doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool {
return !reflect.DeepEqual(a.Spec.Template.Annotations, b.Spec.Template.Annotations)
}, Result{needUpdate: u.True(), needsRollUpdate: u.True(), needsReplace: u.True()}),
c.NewCheck("new statefulset's volumeClaimTemplates contains different number of volumes to the old one",
func(a, b *v1beta1.StatefulSet) bool {
return len(a.Spec.VolumeClaimTemplates) != len(b.Spec.VolumeClaimTemplates)
}, Result{needsReplace: u.True()}),
c.NewCheck("new statefulset's selector doesn't match the current one",
func(a, b *v1beta1.StatefulSet) bool {
if a.Spec.Selector == nil || b.Spec.Selector == nil {
return false
}
return !reflect.DeepEqual(a.Spec.Selector.MatchLabels, b.Spec.Selector.MatchLabels)
}, Result{needsReplace: u.True()}),
}
}
func (c *Cluster) getContainerChecks() []ResourceCheck {
return []ResourceCheck{
c.NewCheck("new statefulset's container %d name doesn't match the current one",
func(a, b *v1.Container) bool { return a.Name != b.Name },
Result{needsRollUpdate: u.True()}),
c.NewCheck("new statefulset's container %d image doesn't match the current one",
func(a, b *v1.Container) bool { return a.Image != b.Image },
Result{needsRollUpdate: u.True()}),
c.NewCheck("new statefulset's container %d ports don't match the current one",
func(a, b *v1.Container) bool { return !reflect.DeepEqual(a.Ports, b.Ports) },
Result{needsRollUpdate: u.True()}),
c.NewCheck("new statefulset's container %d resources don't match the current ones",
func(a, b *v1.Container) bool { return !compareResources(&a.Resources, &b.Resources) },
Result{needsRollUpdate: u.True()}),
c.NewCheck("new statefulset's container %d environment doesn't match the current one",
func(a, b *v1.Container) bool { return !reflect.DeepEqual(a.Env, b.Env) },
Result{needsRollUpdate: u.True()}),
c.NewCheck("new statefulset's container %d environment sources don't match the current one",
func(a, b *v1.Container) bool { return !reflect.DeepEqual(a.EnvFrom, b.EnvFrom) },
Result{needsRollUpdate: u.True()}),
}
}
func (c *Cluster) getVolumeClaimChecks() []ResourceCheck {
return []ResourceCheck{
c.NewCheck("new statefulset's name for volume %d doesn't match the current one",
func(a, b *v1.PersistentVolumeClaim) bool { return a.Name != b.Name },
Result{needsReplace: u.True()}),
c.NewCheck("new statefulset's annotations for volume %q doesn't match the current one",
func(a, b *v1.PersistentVolumeClaim) bool {
return !reflect.DeepEqual(a.Annotations, b.Annotations)
},
Result{needsReplace: u.True()}),
c.NewCheck("new statefulset's volumeClaimTemplates specification for volume %q doesn't match the current one",
func(a, b *v1.PersistentVolumeClaim) bool { return !reflect.DeepEqual(a.Spec, b.Spec) },
Result{needsRollUpdate: u.True()}),
}
}