...

Source file src/edge-infra.dev/pkg/edge/webhooks/edge-injector/pod_secret_controller.go

Documentation: edge-infra.dev/pkg/edge/webhooks/edge-injector

     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  		// pod has invalid reference, must be re-created
   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  	// note: couchctl manages the CouchDBUser secret lifecycle
   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  		// TODO ignore error
   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  	// Secret to be created by couchctl
   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