Skip to content

Commit c949ce4

Browse files
Add EnqueueRequestForAnnotation enqueues Requests based on the presence of an annotation to watch resources
1 parent ea32729 commit c949ce4

File tree

4 files changed

+729
-2
lines changed

4 files changed

+729
-2
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"context"
21+
"github.com/go-logr/logr"
22+
"k8s.io/apimachinery/pkg/runtime/schema"
23+
"sigs.k8s.io/controller-runtime/pkg/handler"
24+
25+
appsv1 "k8s.io/api/apps/v1"
26+
corev1 "k8s.io/api/core/v1"
27+
"k8s.io/apimachinery/pkg/api/errors"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/types"
30+
31+
"sigs.k8s.io/controller-runtime/pkg/client"
32+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
33+
)
34+
35+
// reconcileReplicaSet reconciles ReplicaSets
36+
type reconcileReplicaSet struct {
37+
// client can be used to retrieve objects from the APIServer.
38+
client client.Client
39+
log logr.Logger
40+
}
41+
42+
// Implement reconcile.Reconciler so the controller can reconcile objects
43+
var _ reconcile.Reconciler = &reconcileReplicaSet{}
44+
45+
func (r *reconcileReplicaSet) Reconcile(request reconcile.Request) (reconcile.Result, error) {
46+
// set up a convenient log object so we don't have to type request over and over again
47+
log := r.log.WithValues("request", request)
48+
49+
// Fetch the ReplicaSet from the cache
50+
rs := &appsv1.ReplicaSet{}
51+
err := r.client.Get(context.TODO(), request.NamespacedName, rs)
52+
if errors.IsNotFound(err) {
53+
log.Error(nil, "Could not find ReplicaSet")
54+
return reconcile.Result{}, nil
55+
}
56+
57+
if err != nil {
58+
log.Error(err, "Could not fetch ReplicaSet")
59+
return reconcile.Result{}, err
60+
}
61+
62+
// Print the ReplicaSet
63+
log.Info("Reconciling ReplicaSet", "container name", rs.Spec.Template.Spec.Containers[0].Name)
64+
65+
// Check if the Pod already exists, if not create a new one
66+
podRs := &corev1.Pod{}
67+
err = r.client.Get(context.TODO(), types.NamespacedName{Name: rs.Name, Namespace: rs.Namespace}, podRs)
68+
if err != nil && errors.IsNotFound(err) {
69+
// Define a new Deployment
70+
pod := r.podForReplicasetWithWatchAnnotations(rs)
71+
err = r.client.Create(context.TODO(), pod)
72+
if err != nil {
73+
log.Error(err, "Failed to create new Pod.", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name)
74+
return reconcile.Result{}, err
75+
}
76+
return reconcile.Result{Requeue: true}, nil
77+
} else if err != nil {
78+
log.Error(err, "Failed to get Pod.")
79+
return reconcile.Result{}, err
80+
}
81+
82+
// Set the label if it is missing
83+
if rs.Labels == nil {
84+
rs.Labels = map[string]string{}
85+
}
86+
87+
if rs.Labels["hello"] == "world" {
88+
return reconcile.Result{}, nil
89+
}
90+
91+
// Update the ReplicaSet
92+
rs.Labels["hello"] = "world"
93+
err = r.client.Update(context.TODO(), rs)
94+
if err != nil {
95+
log.Error(err, "Could not write ReplicaSet")
96+
return reconcile.Result{}, err
97+
}
98+
99+
return reconcile.Result{}, nil
100+
}
101+
102+
// podForReplicasetWithWatchAnnotations returns a pod object with the annotations required to be watched with.
103+
func (r *reconcileReplicaSet) podForReplicasetWithWatchAnnotations(rs *appsv1.ReplicaSet) *corev1.Pod {
104+
pod := &corev1.Pod{
105+
ObjectMeta: metav1.ObjectMeta{
106+
Name: rs.Name,
107+
Namespace: rs.Namespace,
108+
},
109+
}
110+
annotation := schema.GroupKind{Group: "ReplicaSet", Kind: "apps"}
111+
handler.SetWatchOwnerAnnotation(rs,pod, annotation)
112+
return pod
113+
}

examples/annotationbasedwatch/main.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"k8s.io/apimachinery/pkg/runtime/schema"
21+
"os"
22+
23+
corev1 "k8s.io/api/core/v1"
24+
rbacv1 "k8s.io/api/rbac/v1"
25+
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
26+
"sigs.k8s.io/controller-runtime/pkg/client/config"
27+
"sigs.k8s.io/controller-runtime/pkg/controller"
28+
"sigs.k8s.io/controller-runtime/pkg/handler"
29+
logf "sigs.k8s.io/controller-runtime/pkg/log"
30+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
31+
"sigs.k8s.io/controller-runtime/pkg/manager"
32+
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
33+
"sigs.k8s.io/controller-runtime/pkg/source"
34+
)
35+
36+
var log = logf.Log.WithName("example-annotationbasedwatch")
37+
38+
func main() {
39+
logf.SetLogger(zap.Logger(false))
40+
entryLog := log.WithName("entrypoint")
41+
42+
// Setup a Manager
43+
entryLog.Info("setting up manager")
44+
mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{})
45+
if err != nil {
46+
entryLog.Error(err, "unable to set up overall controller manager")
47+
os.Exit(1)
48+
}
49+
50+
// Setup a new controller to reconcile ReplicaSets
51+
entryLog.Info("Setting up controller")
52+
c, err := controller.New("foo-controller", mgr, controller.Options{
53+
Reconciler: &reconcileReplicaSet{client: mgr.GetClient(), log: log.WithName("reconciler")},
54+
})
55+
if err != nil {
56+
entryLog.Error(err, "unable to set up individual controller")
57+
os.Exit(1)
58+
}
59+
60+
// Watch Pods that has the following annotations:
61+
// ...
62+
// annotations:
63+
// watch.kubebuilder.io/owner-namespaced-name:my-namespace/my-replicaset
64+
// watch.kubebuilder.io/owner-type:apps.ReplicaSet
65+
// ...
66+
// It will enqueue a Request to the primary-resource-namespace when some change occurs in a Pod resource with these
67+
// annotations
68+
annotation := schema.GroupKind{Group: "ReplicaSet", Kind: "apps"}
69+
if err := c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForAnnotation{annotation}); err != nil {
70+
entryLog.Error(err, "unable to watch Pods")
71+
os.Exit(1)
72+
}
73+
74+
// Watch ClusterRoles that has the following annotations:
75+
// ...
76+
// annotations:
77+
// watch.kubebuilder.io/owner-namespaced-name:my-namespace/my-replicaset
78+
// watch.kubebuilder.io/owner-type:apps.ReplicaSet
79+
// ...
80+
// It will enqueue a Request to the primary-resource-namespace when some change occurs in a ClusterRole resource with these
81+
// annotations
82+
if err := c.Watch(&source.Kind{
83+
// Watch cluster roles
84+
Type: &rbacv1.ClusterRole{}},
85+
86+
// Enqueue ReplicaSet reconcile requests using the
87+
// namespacedName annotation value in the request.
88+
&handler.EnqueueRequestForAnnotation{schema.GroupKind{Group:"ReplicaSet", Kind:"apps"}}); err != nil {
89+
entryLog.Error(err, "unable to watch Cluster Role")
90+
os.Exit(1)
91+
}
92+
93+
entryLog.Info("starting manager")
94+
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
95+
entryLog.Error(err, "unable to run manager")
96+
os.Exit(1)
97+
}
98+
}

0 commit comments

Comments
 (0)