1 package edgeinjector
2
3 import (
4 "context"
5 "fmt"
6 "time"
7
8 "sigs.k8s.io/controller-runtime/pkg/controller"
9
10 corev1 "k8s.io/api/core/v1"
11 "k8s.io/apimachinery/pkg/api/errors"
12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13 "k8s.io/apimachinery/pkg/types"
14 ctrl "sigs.k8s.io/controller-runtime"
15 "sigs.k8s.io/controller-runtime/pkg/builder"
16 "sigs.k8s.io/controller-runtime/pkg/client"
17 "sigs.k8s.io/controller-runtime/pkg/event"
18 "sigs.k8s.io/controller-runtime/pkg/predicate"
19
20 "edge-infra.dev/pkg/edge/clientutils"
21 "edge-infra.dev/pkg/edge/controllers/envctl/pkg/nameutils"
22 dsapi "edge-infra.dev/pkg/edge/datasync/apis/v1alpha1"
23 "edge-infra.dev/pkg/edge/k8objectsutils/ownerref"
24 "edge-infra.dev/pkg/lib/fog"
25 nodemeta "edge-infra.dev/pkg/sds/ien/node"
26 )
27
28 type PodSecretReconciler struct {
29 client.Client
30 Name string
31 RequeueTime time.Duration
32 PollingInterval time.Duration
33 ReconcileConcurrency int
34 }
35
36 func (r *PodSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
37 return ctrl.NewControllerManagedBy(mgr).
38 For(&corev1.Pod{}, builder.WithPredicates(podPredicate())).
39 WithOptions(controller.Options{MaxConcurrentReconciles: r.ReconcileConcurrency}).
40 WithEventFilter(predicate.NewPredicateFuncs(func(obj client.Object) bool {
41 if pod, ok := obj.(*corev1.Pod); ok {
42 hasSecretLabel := SecretLabelValue(pod, CouchDBSecret) != "" || SecretLabelValue(pod, NodeSecret) != ""
43 return hasSecretLabel && pod.Spec.NodeName != ""
44 }
45 return false
46 })).
47 Complete(r)
48 }
49
50 func podPredicate() predicate.Predicate {
51 return predicate.Funcs{
52 UpdateFunc: func(e event.UpdateEvent) bool {
53 return e.ObjectNew.GetDeletionTimestamp().IsZero()
54 },
55 CreateFunc: func(_ event.CreateEvent) bool {
56 return true
57 },
58 DeleteFunc: func(_ event.DeleteEvent) bool {
59 return false
60 },
61 }
62 }
63
64 func (r *PodSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
65 log := fog.FromContext(ctx)
66
67 pod := &corev1.Pod{}
68 if err := r.Client.Get(ctx, req.NamespacedName, pod); err != nil {
69 return ctrl.Result{}, nil
70 }
71
72 log.Info("reconciling for pod secret")
73
74 node := &corev1.Node{}
75 if err := r.Client.Get(ctx, types.NamespacedName{Name: pod.Spec.NodeName}, node); err != nil {
76 log.Error(err, "fail to get container's NodeName from pod, should have been filtered out", "nodeName", pod.Spec.NodeName)
77 return ctrl.Result{}, nil
78 }
79
80 for _, secretType := range []SecretType{CouchDBSecret, NodeSecret} {
81 secretName := SecretLabelValue(pod, secretType)
82 if secretName == "" {
83 continue
84 }
85 log := log.WithValues("nodeName", pod.Spec.NodeName, "secretName", secretName, "secretType", secretType)
86 if err := r.SecretExists(ctx, secretType, pod.Namespace, secretName); err == nil {
87 log.Info(fmt.Sprintf("secret: %s already exists", secretName))
88 continue
89 } else if !errors.IsNotFound(err) {
90 log.Error(err, "fail to check secret existence")
91 return ctrl.Result{}, err
92 }
93
94 ctx = fog.IntoContext(ctx, log)
95 var result ctrl.Result
96 var err error
97 switch secretType {
98 case CouchDBSecret:
99 result, err = r.CreatePodCouchDBSecret(ctx, node, pod)
100 case NodeSecret:
101 result, err = r.CreateNodeSecret(ctx, node, pod)
102 default:
103 err = fmt.Errorf("secret type not implemented: %s", secretType)
104 }
105 if err != nil {
106 log.Error(err, "fail to create secret for pod")
107 return result, err
108 }
109 if result.RequeueAfter > 0 {
110 log.Info("re-trying to create node secret", "RequeueTime", result.RequeueAfter)
111 return result, nil
112 }
113 }
114
115 log.Info("Successfully reconcile secret: " + r.PollingInterval.String())
116 return ctrl.Result{RequeueAfter: r.PollingInterval}, nil
117 }
118
119 func (r *PodSecretReconciler) CreateNodeSecret(ctx context.Context, node *corev1.Node, pod *corev1.Pod) (ctrl.Result, error) {
120 log := fog.FromContext(ctx)
121
122 info, err := nameutils.GetNodeInfo(*node, nameutils.LaneNumberFullLength)
123 if err != nil {
124 log.Error(err, "fail to get node info, must be a dsds node")
125 return ctrl.Result{RequeueAfter: r.RequeueTime}, nil
126 }
127
128 customLabels, err := GetNodeCustomLabels(node)
129 if err != nil {
130 log.Error(err, "fail to get node custom labels, must be a dsds node")
131 return ctrl.Result{RequeueAfter: r.RequeueTime}, nil
132 }
133
134 secretName := SecretLabelValue(pod, NodeSecret)
135 secret := BuildNodeSecret(pod, info, customLabels, secretName)
136
137 if err := clientutils.CreateOrUpdateSecret(ctx, r, secret); err != nil {
138 log.Error(err, "fail to create/update node env secret")
139 return ctrl.Result{}, nil
140 }
141
142 log.Info("successfully created node info secret")
143 return ctrl.Result{}, nil
144 }
145
146 func (r *PodSecretReconciler) CreatePodCouchDBSecret(ctx context.Context, node *corev1.Node, pod *corev1.Pod) (ctrl.Result, error) {
147 log := fog.FromContext(ctx)
148
149 isCP, err := nodemeta.IsControlPlaneNode(node)
150 if err != nil {
151 log.Error(err, "invalid node")
152 return ctrl.Result{RequeueAfter: r.RequeueTime}, nil
153 }
154 serverName := dsapi.CouchDBServerName(node)
155
156 secretName := SecretLabelValue(pod, CouchDBSecret)
157 user, err := r.BuildCouchDBUser(ctx, pod, serverName, secretName, !isCP)
158 if err != nil {
159 log.Error(err, "fail to build CouchDBUser")
160
161 return ctrl.Result{}, nil
162 }
163
164 if err := clientutils.CreateOrUpdateCouchDBUser(ctx, r, user); err != nil {
165 log.Error(err, "fail to create/update CouchDBUser", "CouchDBUser", user.Name)
166 return ctrl.Result{RequeueAfter: r.RequeueTime}, nil
167 }
168
169
170
171 log.Info("successfully created couchdb user", "CouchDBUser", user.Name)
172 return ctrl.Result{}, nil
173 }
174
175 func (r *PodSecretReconciler) BuildCouchDBUser(ctx context.Context, pod *corev1.Pod, serverName, secretName string, isTouchPoint bool) (*dsapi.CouchDBUser, error) {
176 log := fog.FromContext(ctx)
177 ownerRef, err := ownerref.GetOwnerRef(ctx, r, pod)
178 if err != nil {
179 log.Error(err, "fail to get valid owner reference for pod", "Valid OwnerRefs", "Deployment,DaemonSet,StatefulSet")
180
181 return nil, err
182 }
183 userOwnerRef := *ownerRef
184 f := false
185 userOwnerRef.Controller = &f
186
187 username := ownerref.ResourceName(serverName, userOwnerRef, pod.Namespace, pod.Spec.NodeName, isTouchPoint)
188 user := dsapi.NewCouchDBUser(dsapi.UserCredentials, username, serverName, CouchDBUserRole(pod))
189 user.Namespace = pod.Namespace
190
191
192 user.Spec.User.Secret.Namespace = pod.Namespace
193 user.Spec.User.Secret.Name = secretName
194
195 user.ObjectMeta.OwnerReferences = []metav1.OwnerReference{userOwnerRef}
196 return user, nil
197 }
198
199 func (r *PodSecretReconciler) SecretExists(ctx context.Context, st SecretType, namespace, name string) error {
200 s := &corev1.Secret{}
201 err := r.Client.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, s)
202 if err != nil {
203 return err
204 }
205 if st == NodeSecret {
206 return checkNodeSecret(s)
207 }
208 return nil
209 }
210
View as plain text