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
47
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{})).
66 WithEventFilter(createEventFilter()).
67 Watches(
68
69 &corev1.Service{},
70 handler.EnqueueRequestsFromMapFunc(c.createVPNConfigReconcileRequests),
71 builder.WithPredicates(isRelayServicePredicate()),
72 ).
73 Watches(
74
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
92 if _, ok := e.Object.(*corev1.Service); ok {
93 return true
94 }
95
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
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
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
165 if controllerutil.AddFinalizer(vpnConfig, v1vpnconfig.Finalizer) {
166 result = ctrlreconcile.ResultRequeue
167 return
168 }
169
170
171 vpnconfig.AddOwnerReference(vpnConfig, cluster)
172
173
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
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