Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match labels #133

Merged
merged 12 commits into from
Aug 3, 2023
9 changes: 6 additions & 3 deletions controllers/selfnoderemediation_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,9 @@ func verifyNodeIsUnschedulable() *v1.Node {
func verifySelfNodeRemediationPodExist() {
podList := &v1.PodList{}
selector := labels.NewSelector()
requirement, _ := labels.NewRequirement("app", selection.Equals, []string{"self-node-remediation-agent"})
selector = selector.Add(*requirement)
nameRequirement, _ := labels.NewRequirement("app.kubernetes.io/name", selection.Equals, []string{"self-node-remediation"})
componentRequirement, _ := labels.NewRequirement("app.kubernetes.io/component", selection.Equals, []string{"agent"})
selector = selector.Add(*nameRequirement, *componentRequirement)

EventuallyWithOffset(1, func() (int, error) {
err := k8sClient.Client.List(context.Background(), podList, &client.ListOptions{LabelSelector: selector})
Expand All @@ -567,7 +568,9 @@ func createSNR(strategy v1alpha1.RemediationStrategyType) {
func createSelfNodeRemediationPod() {
pod := &v1.Pod{}
pod.Spec.NodeName = unhealthyNodeName
pod.Labels = map[string]string{"app": "self-node-remediation-agent"}
pod.Labels = map[string]string{"app.kubernetes.io/name": "self-node-remediation",
"app.kubernetes.io/component": "agent"}

pod.Name = "self-node-remediation"
pod.Namespace = namespace
container := v1.Container{
Expand Down
40 changes: 40 additions & 0 deletions controllers/selfnoderemediationconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import (
"os"

"github.com/go-logr/logr"
pkgerrors "github.com/pkg/errors"

v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
Expand All @@ -38,6 +40,10 @@ import (
"github.com/medik8s/self-node-remediation/pkg/render"
)

const (
lastChangedAnnotationKey = "snr.medik8s.io/force-deletion-revision"
)

// SelfNodeRemediationConfigReconciler reconciles a SelfNodeRemediationConfig object
type SelfNodeRemediationConfigReconciler struct {
client.Client
Expand Down Expand Up @@ -143,6 +149,9 @@ func (r *SelfNodeRemediationConfigReconciler) syncConfigDaemonSet(ctx context.Co

// Sync DaemonSets
for _, obj := range objs {
if err := r.removeOldDsOnOperatorUpdate(ctx, obj.GetName(), obj.GetAnnotations()[lastChangedAnnotationKey]); err != nil {
return err
}
err = r.syncK8sResource(ctx, snrConfig, obj)
if err != nil {
logger.Error(err, "Couldn't sync self-node-remediation daemons objects")
Expand Down Expand Up @@ -243,3 +252,34 @@ func (r *SelfNodeRemediationConfigReconciler) convertTolerationsToUnstructed(tol
}
return convertedTolerations, nil
}

func (r *SelfNodeRemediationConfigReconciler) removeOldDsOnOperatorUpdate(ctx context.Context, dsName string, lastVersion string) error {
ds := &v1.DaemonSet{}
key := types.NamespacedName{
Namespace: r.Namespace,
Name: dsName,
}

if err := r.Client.Get(ctx, key, ds); err != nil {
if !errors.IsNotFound(err) {
r.Log.Error(err, "snr install/update failed error when trying to fetch old daemonset")
return pkgerrors.Wrap(err, "unable to fetch daemon set")
}
r.Log.Info("snr didn't find old daemonset to be deleted")
return nil

}

if lastChangedFoundVal, _ := ds.Annotations[lastChangedAnnotationKey]; lastChangedFoundVal == lastVersion {
//ds is up-to-date this is not an update scenario
return nil
}

if err := r.Client.Delete(ctx, ds); err != nil {
r.Log.Error(err, "snr update failed could not delete old daemonset")
return pkgerrors.Wrap(err, "unable to delete old daemon set")
}
r.Log.Info("snr update old daemonset deleted")
return nil

}
97 changes: 89 additions & 8 deletions controllers/selfnoderemediationconfig_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ var _ = Describe("snrc controller Test", func() {
})

Context("DS installation", func() {
key := types.NamespacedName{
Namespace: namespace,
Name: dsName,
}

JustBeforeEach(func() {
Expect(k8sClient.Create(context.Background(), config)).To(Succeed())
DeferCleanup(func() {
Expand Down Expand Up @@ -69,10 +74,6 @@ var _ = Describe("snrc controller Test", func() {
})

It("Daemonset should be created", func() {
key := types.NamespacedName{
Namespace: namespace,
Name: dsName,
}
Eventually(func() error {
return k8sClient.Get(context.Background(), key, ds)
}, 10*time.Second, 250*time.Millisecond).Should(BeNil())
Expand All @@ -96,10 +97,6 @@ var _ = Describe("snrc controller Test", func() {
config.Spec.CustomDsTolerations = []corev1.Toleration{expectedToleration}
})
It("Daemonset should have customized tolerations", func() {
key := types.NamespacedName{
Namespace: namespace,
Name: dsName,
}
Eventually(func() error {
return k8sClient.Get(context.Background(), key, ds)
}, 10*time.Second, 250*time.Millisecond).Should(BeNil())
Expand All @@ -122,6 +119,49 @@ var _ = Describe("snrc controller Test", func() {

})
})

Context("DS Recreation on Operator Update", func() {
var timeToWaitForDsUpdate = 6 * time.Second
var oldDsVersion, currentDsVersion = "0", "1"

JustBeforeEach(func() {
Expect(k8sClient.Create(context.Background(), ds)).To(Succeed())
Eventually(func() error {
return k8sClient.Get(context.Background(), key, ds)
}, 2*time.Second, 250*time.Millisecond).Should(BeNil())

})
When("ds version has not changed", func() {
BeforeEach(func() {
ds = generateDs(dsName, namespace, currentDsVersion)
})
It("Daemonset should not recreated", func() {
//Wait to make sure DS isn't recreated
time.Sleep(timeToWaitForDsUpdate)
Expect(k8sClient.Get(context.Background(), key, ds)).To(BeNil())
Expect(ds.Annotations["original-ds"]).To(Equal("true"))

})
})

When("ds version has changed", func() {
BeforeEach(func() {
//creating an DS with old version
ds = generateDs(dsName, namespace, oldDsVersion)
})
It("Daemonset should be recreated", func() {
//Wait until DS is recreated
Eventually(func() bool {
if err := k8sClient.Get(context.Background(), key, ds); err == nil {
return ds.Annotations["snr.medik8s.io/force-deletion-revision"] == currentDsVersion
}
return false
}, timeToWaitForDsUpdate, 250*time.Millisecond).Should(BeTrue())
Expect(ds.Annotations["original-ds"]).To(Equal(""))

})
})
})
})

Context("SNRC defaults", func() {
Expand Down Expand Up @@ -219,3 +259,44 @@ func getEnvVarMap(vars []corev1.EnvVar) map[string]corev1.EnvVar {
}
return m
}

func generateDs(name, namespace, version string) *appsv1.DaemonSet {
return &appsv1.DaemonSet{
TypeMeta: metav1.TypeMeta{
Kind: "DaemonSet",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Annotations: map[string]string{"snr.medik8s.io/force-deletion-revision": version, "original-ds": "true"},
},
Spec: appsv1.DaemonSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "example",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "example",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "example-container",
Image: "busybox",
Command: []string{
"/bin/sh",
"-c",
"while true; do echo hello; sleep 10;done",
},
},
},
},
},
},
}
}
9 changes: 6 additions & 3 deletions install/self-node-remediation-deamonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ metadata:
namespace: {{.Namespace}}
labels:
k8s-app: self-node-remediation
annotations:
snr.medik8s.io/force-deletion-revision: "1"
spec:
selector:
matchLabels:
control-plane: controller-manager
app.kubernetes.io/name: self-node-remediation
app.kubernetes.io/component: agent
template:
metadata:
creationTimestamp: null
labels:
control-plane: controller-manager
app: self-node-remediation-agent
app.kubernetes.io/name: self-node-remediation
app.kubernetes.io/component: agent
spec:
volumes:
- name: devices
Expand Down
5 changes: 3 additions & 2 deletions pkg/utils/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ func GetSelfNodeRemediationAgentPod(nodeName string, r client.Reader) (*v1.Pod,
podList := &v1.PodList{}

selector := labels.NewSelector()
requirement, _ := labels.NewRequirement("app", selection.Equals, []string{"self-node-remediation-agent"})
selector = selector.Add(*requirement)
nameRequirement, _ := labels.NewRequirement("app.kubernetes.io/name", selection.Equals, []string{"self-node-remediation"})
componentRequirement, _ := labels.NewRequirement("app.kubernetes.io/component", selection.Equals, []string{"agent"})
selector = selector.Add(*nameRequirement, *componentRequirement)

err := r.List(context.Background(), podList, &client.ListOptions{LabelSelector: selector})
if err != nil {
Expand Down