...

Source file src/edge-infra.dev/pkg/sds/remoteaccess/k8s/controllers/wireguardctl/vpnconfig_controller.go

Documentation: edge-infra.dev/pkg/sds/remoteaccess/k8s/controllers/wireguardctl

     1  package wireguardctl
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	corev1 "k8s.io/api/core/v1"
     8  	ctrl "sigs.k8s.io/controller-runtime"
     9  	"sigs.k8s.io/controller-runtime/pkg/builder"
    10  	"sigs.k8s.io/controller-runtime/pkg/client"
    11  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    12  	"sigs.k8s.io/controller-runtime/pkg/event"
    13  	"sigs.k8s.io/controller-runtime/pkg/handler"
    14  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    15  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    16  
    17  	"edge-infra.dev/pkg/edge/api/types"
    18  	v1cluster "edge-infra.dev/pkg/edge/apis/cluster/v1alpha1"
    19  	"edge-infra.dev/pkg/k8s/meta/status"
    20  	"edge-infra.dev/pkg/k8s/runtime/conditions"
    21  	ctrlreconcile "edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
    22  	"edge-infra.dev/pkg/k8s/runtime/patch"
    23  	"edge-infra.dev/pkg/lib/gcp/secretmanager"
    24  	"edge-infra.dev/pkg/sds/remoteaccess/constants"
    25  	v1vpnconfig "edge-infra.dev/pkg/sds/remoteaccess/k8s/apis/vpnconfigs/v1"
    26  	"edge-infra.dev/pkg/sds/remoteaccess/k8s/controllers/wireguardctl/vpnconfig"
    27  	"edge-infra.dev/pkg/sds/remoteaccess/wireguard/vpn"
    28  )
    29  
    30  type VPNController struct {
    31  	VPN         *vpn.VPN
    32  	Client      client.Client
    33  	Name        string
    34  	RequeueTime time.Duration
    35  }
    36  
    37  func NewVPNController(mgr ctrl.Manager, vpn *vpn.VPN) VPNController {
    38  	return VPNController{
    39  		VPN:         vpn,
    40  		Client:      mgr.GetClient(),
    41  		Name:        constants.WireguardControllerName,
    42  		RequeueTime: requeueTime,
    43  	}
    44  }
    45  
    46  // VPNConfigConditions defines the VPNConfig status conditions that
    47  // should be considered when determining overall readiness via the Ready condition.
    48  var VPNConfigConditions = ctrlreconcile.Conditions{
    49  	Target: status.ReadyCondition,
    50  	Owned: []string{
    51  		v1vpnconfig.VPNConfigured,
    52  		status.ReconcilingCondition,
    53  	},
    54  	Summarize: []string{
    55  		v1vpnconfig.VPNConfigured,
    56  		status.ReconcilingCondition,
    57  	},
    58  	NegativePolarity: []string{
    59  		status.ReconcilingCondition,
    60  	},
    61  }
    62  
    63  func (c *VPNController) SetupWithManager(mgr ctrl.Manager) error {
    64  	return ctrl.NewControllerManagedBy(mgr).
    65  		For(&v1vpnconfig.VPNConfig{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). // only reconcile changes to spec
    66  		WithEventFilter(createEventFilter()).
    67  		Watches(
    68  			// reconcile all VPNConfig objects on changes to the vpn/wireguard-relay service
    69  			&corev1.Service{},
    70  			handler.EnqueueRequestsFromMapFunc(c.createVPNConfigReconcileRequests),
    71  			builder.WithPredicates(isRelayServicePredicate()),
    72  		).
    73  		Watches(
    74  			// reconcile all VPNConfig objects on changes to the vpn/vpn-cidr configmap
    75  			&corev1.ConfigMap{},
    76  			handler.EnqueueRequestsFromMapFunc(c.createVPNConfigReconcileRequests),
    77  			builder.WithPredicates(isVPNConfigMapPredicate()),
    78  		).
    79  		Complete(c)
    80  }
    81  
    82  func createEventFilter() predicate.Predicate {
    83  	return predicate.Funcs{
    84  		CreateFunc: func(_ event.CreateEvent) bool {
    85  			return true
    86  		},
    87  		UpdateFunc: func(_ event.UpdateEvent) bool {
    88  			return true
    89  		},
    90  		DeleteFunc: func(e event.DeleteEvent) bool {
    91  			// for the relay service we reconcile deletes so we can update VPNConfig status
    92  			if _, ok := e.Object.(*corev1.Service); ok {
    93  				return true
    94  			}
    95  			// we do not reconcile delete events fo the VPNConfig as the finalizer results in deletions becoming updates to DeletionTimestamp
    96  			return false
    97  		},
    98  	}
    99  }
   100  
   101  func (c *VPNController) createVPNConfigReconcileRequests(ctx context.Context, _ client.Object) []reconcile.Request {
   102  	vpnConfigs := &v1vpnconfig.VPNConfigList{}
   103  	if err := c.Client.List(ctx, vpnConfigs, &client.ListOptions{Namespace: constants.VPNNamespace}); err != nil {
   104  		return nil
   105  	}
   106  
   107  	reconcileRequests := []reconcile.Request{}
   108  	for _, vpnConfig := range vpnConfigs.Items {
   109  		reconcileRequests = append(reconcileRequests, reconcile.Request{
   110  			NamespacedName: client.ObjectKey{
   111  				Namespace: vpnConfig.GetNamespace(),
   112  				Name:      vpnConfig.GetName(),
   113  			},
   114  		})
   115  	}
   116  	return reconcileRequests
   117  }
   118  
   119  func isRelayServicePredicate() predicate.Funcs {
   120  	return predicate.NewPredicateFuncs(func(object client.Object) bool {
   121  		return object.GetNamespace() == constants.VPNNamespace && object.GetName() == constants.RelayName
   122  	})
   123  }
   124  
   125  func isVPNConfigMapPredicate() predicate.Funcs {
   126  	return predicate.NewPredicateFuncs(func(object client.Object) bool {
   127  		return object.GetNamespace() == constants.VPNNamespace && object.GetName() == constants.VPNConfigMapName
   128  	})
   129  }
   130  
   131  func (c *VPNController) Reconcile(ctx context.Context, req ctrl.Request) (recResult ctrl.Result, recErr error) {
   132  	log := ctrl.LoggerFrom(ctx).WithName(c.Name)
   133  	var (
   134  		result = ctrlreconcile.ResultEmpty
   135  		err    error
   136  	)
   137  
   138  	// get the VPNConfig object to be reconciled
   139  	vpnConfig := &v1vpnconfig.VPNConfig{}
   140  	if err := c.Client.Get(ctx, req.NamespacedName, vpnConfig); err != nil {
   141  		log.Error(err, "unable to fetch VPNConfig")
   142  		return ctrl.Result{RequeueAfter: c.RequeueTime}, err
   143  	}
   144  
   145  	serialPatcher := patch.NewSerialPatcher(vpnConfig, c.Client)
   146  	defer func() {
   147  		recResult, recErr = c.summarizeAndPatch(ctx, vpnConfig, serialPatcher, result, err)
   148  		if result == ctrlreconcile.ResultRequeue {
   149  			recResult = ctrl.Result{RequeueAfter: c.RequeueTime}
   150  		}
   151  	}()
   152  
   153  	if err = ctrlreconcile.Progressing(ctx, vpnConfig, serialPatcher); err != nil {
   154  		return
   155  	}
   156  
   157  	// get the cluster CR
   158  	cluster := &v1cluster.Cluster{}
   159  	if err = c.Client.Get(ctx, client.ObjectKey{Name: vpnConfig.GetName()}, cluster); err != nil {
   160  		log.Error(err, "unable to fetch cluster")
   161  		return
   162  	}
   163  
   164  	// ensure finalizer is present so controller can handle clean-up on object deletion
   165  	if controllerutil.AddFinalizer(vpnConfig, v1vpnconfig.Finalizer) {
   166  		result = ctrlreconcile.ResultRequeue
   167  		return
   168  	}
   169  
   170  	// add cluster as ownerReference on VPNConfig
   171  	vpnconfig.AddOwnerReference(vpnConfig, cluster)
   172  
   173  	// create secret manager for project
   174  	sm, err := secretmanager.NewWithOptions(ctx, cluster.Spec.ProjectID)
   175  	if err != nil {
   176  		log.Error(err, "unable to create secret manager", "projectID", cluster.Spec.ProjectID)
   177  		return
   178  	}
   179  
   180  	// check if object is being updated or removed
   181  	if vpnConfig.ObjectMeta.DeletionTimestamp.IsZero() {
   182  		err = vpnconfig.Update(ctx, c.Client, sm, c.VPN, vpnConfig, cluster)
   183  	} else {
   184  		err = c.remove(ctx, sm, vpnConfig, cluster)
   185  	}
   186  	if err != nil {
   187  		log.Error(err, "unable to update VPNConfig")
   188  		conditions.MarkFalse(vpnConfig, v1vpnconfig.VPNConfigured, v1vpnconfig.VPNConfigFailed, "%v", err)
   189  		return
   190  	}
   191  
   192  	conditions.Delete(vpnConfig, status.ReconcilingCondition)
   193  	conditions.MarkTrue(vpnConfig, v1vpnconfig.VPNConfigured, v1vpnconfig.VPNConfigSuccessful, "successfully configured vpn")
   194  	result = ctrlreconcile.ResultSuccess
   195  	return
   196  }
   197  
   198  func (c *VPNController) remove(ctx context.Context, sm types.SecretManagerService, vpnConfig *v1vpnconfig.VPNConfig, cluster *v1cluster.Cluster) error {
   199  	if !controllerutil.ContainsFinalizer(vpnConfig, v1vpnconfig.Finalizer) {
   200  		return nil
   201  	}
   202  	controllerutil.RemoveFinalizer(vpnConfig, v1vpnconfig.Finalizer)
   203  	return vpnconfig.Remove(ctx, c.Client, sm, c.VPN, vpnConfig, cluster)
   204  }
   205  
   206  func (c *VPNController) summarizeAndPatch(ctx context.Context, vpnConfig *v1vpnconfig.VPNConfig, serialPatcher *patch.SerialPatcher, result ctrlreconcile.Result, err error) (ctrl.Result, error) {
   207  	s := ctrlreconcile.NewSummarizer(serialPatcher)
   208  	return s.SummarizeAndPatch(
   209  		ctx,
   210  		vpnConfig,
   211  		ctrlreconcile.WithResult(result),
   212  		ctrlreconcile.WithError(err),
   213  		ctrlreconcile.WithIgnoreNotFound(),
   214  		ctrlreconcile.WithFieldOwner(c.Name),
   215  		ctrlreconcile.WithConditions(VPNConfigConditions),
   216  		ctrlreconcile.WithProcessors(
   217  			ctrlreconcile.RecordReconcileReq,
   218  			ctrlreconcile.RecordResult,
   219  		),
   220  	)
   221  }
   222  

View as plain text