|
| 1 | +/* |
| 2 | +Copyright 2022. |
| 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 controllers |
| 18 | + |
| 19 | +import ( |
| 20 | + "context" |
| 21 | + "errors" |
| 22 | + "fmt" |
| 23 | + |
| 24 | + kmmv1beta1 "github.com/kubernetes-sigs/kernel-module-management/api/v1beta1" |
| 25 | + "github.com/kubernetes-sigs/kernel-module-management/internal/api" |
| 26 | + "github.com/kubernetes-sigs/kernel-module-management/internal/buildsign" |
| 27 | + "github.com/kubernetes-sigs/kernel-module-management/internal/kernel" |
| 28 | + "github.com/kubernetes-sigs/kernel-module-management/internal/mbsc" |
| 29 | + "github.com/kubernetes-sigs/kernel-module-management/internal/utils" |
| 30 | + v1 "k8s.io/api/core/v1" |
| 31 | + ctrl "sigs.k8s.io/controller-runtime" |
| 32 | + "sigs.k8s.io/controller-runtime/pkg/client" |
| 33 | + "sigs.k8s.io/controller-runtime/pkg/log" |
| 34 | + "sigs.k8s.io/controller-runtime/pkg/reconcile" |
| 35 | +) |
| 36 | + |
| 37 | +const MBSCReconcilerName = "MBSCReconciler" |
| 38 | + |
| 39 | +// mbscReconciler reconciles a ModuleBuldSignConfig object |
| 40 | +type mbscReconciler struct { |
| 41 | + reconHelperAPI mbscReconcilerHelperAPI |
| 42 | +} |
| 43 | + |
| 44 | +func NewMBSCReconciler( |
| 45 | + client client.Client, |
| 46 | + buildSignAPI buildsign.Manager, |
| 47 | + mbscAPI mbsc.MBSC, |
| 48 | +) *mbscReconciler { |
| 49 | + reconHelperAPI := newMBSCReconcilerHelper(client, buildSignAPI, mbscAPI) |
| 50 | + return &mbscReconciler{ |
| 51 | + reconHelperAPI: reconHelperAPI, |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +// SetupWithManager sets up the controller with the Manager. |
| 56 | +func (r *mbscReconciler) SetupWithManager(mgr ctrl.Manager, kernelLabel string) error { |
| 57 | + return ctrl.NewControllerManagedBy(mgr). |
| 58 | + For(&kmmv1beta1.ModuleBuildSignConfig{}). |
| 59 | + Owns(&v1.Pod{}). |
| 60 | + Named(MBSCReconcilerName). |
| 61 | + Complete( |
| 62 | + reconcile.AsReconciler[*kmmv1beta1.ModuleBuildSignConfig](mgr.GetClient(), r), |
| 63 | + ) |
| 64 | +} |
| 65 | + |
| 66 | +//+kubebuilder:rbac:groups=kmm.sigs.x-k8s.io,resources=modulebuildsignconfigs,verbs=get;list;watch;update;patch;create |
| 67 | +//+kubebuilder:rbac:groups=kmm.sigs.x-k8s.io,resources=modulebuildsignconfigs/status,verbs=get;update;patch |
| 68 | +//+kubebuilder:rbac:groups="core",resources=secrets,verbs=get;list;watch |
| 69 | +//+kubebuilder:rbac:groups="core",resources=configmaps,verbs=get;list;watch |
| 70 | +//+kubebuilder:rbac:groups="core",resources=pods,verbs=create;list;watch;delete |
| 71 | + |
| 72 | +func (r *mbscReconciler) Reconcile(ctx context.Context, mbscObj *kmmv1beta1.ModuleBuildSignConfig) (ctrl.Result, error) { |
| 73 | + res := ctrl.Result{} |
| 74 | + |
| 75 | + logger := log.FromContext(ctx) |
| 76 | + |
| 77 | + if mbscObj.GetDeletionTimestamp() != nil { |
| 78 | + // [TODO] delete build/sign pods |
| 79 | + return res, nil |
| 80 | + } |
| 81 | + |
| 82 | + err := r.reconHelperAPI.updateStatus(ctx, mbscObj) |
| 83 | + if err != nil { |
| 84 | + return res, fmt.Errorf("failed to update MSBC %s status based on the result of the builds: %v", mbscObj.Name, err) |
| 85 | + } |
| 86 | + |
| 87 | + err = r.reconHelperAPI.processImagesSpecs(ctx, mbscObj) |
| 88 | + if err != nil { |
| 89 | + return res, fmt.Errorf("failed to process images of MSBC %s: %v", mbscObj.Name, err) |
| 90 | + } |
| 91 | + |
| 92 | + logger.Info("run garbage collector for build/sign pods") |
| 93 | + err = r.reconHelperAPI.garbageCollect(ctx, mbscObj) |
| 94 | + if err != nil { |
| 95 | + return res, fmt.Errorf("failed to run garbage collector for MBSC %s: %v", mbscObj.Name, err) |
| 96 | + } |
| 97 | + |
| 98 | + return res, nil |
| 99 | +} |
| 100 | + |
| 101 | +//go:generate mockgen -source=mbsc_reconciler.go -package=controllers -destination=mock_mbsc_reconciler.go mbscReconcilerHelperAPI |
| 102 | + |
| 103 | +type mbscReconcilerHelperAPI interface { |
| 104 | + updateStatus(ctx context.Context, mbscObj *kmmv1beta1.ModuleBuildSignConfig) error |
| 105 | + processImagesSpecs(ctx context.Context, mbscObj *kmmv1beta1.ModuleBuildSignConfig) error |
| 106 | + garbageCollect(ctx context.Context, mbscObj *kmmv1beta1.ModuleBuildSignConfig) error |
| 107 | +} |
| 108 | + |
| 109 | +type mbscReconcilerHelper struct { |
| 110 | + client client.Client |
| 111 | + buildSignAPI buildsign.Manager |
| 112 | + mbscAPI mbsc.MBSC |
| 113 | +} |
| 114 | + |
| 115 | +func newMBSCReconcilerHelper(client client.Client, buildSignAPI buildsign.Manager, mbscAPI mbsc.MBSC) mbscReconcilerHelperAPI { |
| 116 | + return &mbscReconcilerHelper{ |
| 117 | + client: client, |
| 118 | + buildSignAPI: buildSignAPI, |
| 119 | + mbscAPI: mbscAPI, |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +func (mrh *mbscReconcilerHelper) updateStatus(ctx context.Context, mbscObj *kmmv1beta1.ModuleBuildSignConfig) error { |
| 124 | + errs := make([]error, 0, len(mbscObj.Spec.Images)) |
| 125 | + patchFrom := client.MergeFrom(mbscObj.DeepCopy()) |
| 126 | + for _, imageSpec := range mbscObj.Spec.Images { |
| 127 | + status, err := mrh.buildSignAPI.GetStatus(ctx, mbscObj.Name, mbscObj.Namespace, imageSpec.ModuleImageSpec.KernelVersion, |
| 128 | + imageSpec.Action, &mbscObj.ObjectMeta) |
| 129 | + if err != nil || status == kmmv1beta1.BuildOrSignStatus("") { |
| 130 | + // either we could not get the status or the status is empty |
| 131 | + errs = append(errs, err) |
| 132 | + continue |
| 133 | + } |
| 134 | + mrh.mbscAPI.SetImageStatus(mbscObj, imageSpec.Image, imageSpec.Action, status) |
| 135 | + } |
| 136 | + |
| 137 | + err := mrh.client.Status().Patch(ctx, mbscObj, patchFrom) |
| 138 | + errs = append(errs, err) |
| 139 | + return errors.Join(errs...) |
| 140 | +} |
| 141 | + |
| 142 | +func (mrh *mbscReconcilerHelper) processImagesSpecs(ctx context.Context, mbscObj *kmmv1beta1.ModuleBuildSignConfig) error { |
| 143 | + logger := log.FromContext(ctx) |
| 144 | + errs := make([]error, 0, len(mbscObj.Spec.Images)) |
| 145 | + for _, imageSpec := range mbscObj.Spec.Images { |
| 146 | + imageStatus := mrh.mbscAPI.GetImageStatus(mbscObj, imageSpec.Image, imageSpec.Action) |
| 147 | + if imageStatus == kmmv1beta1.ActionSuccess { |
| 148 | + // in case action succeeded - skip to the next image. Otherwise, action had not been handled yet, or is being handled, or already failed. |
| 149 | + // in that case the Sync API will take care of what is needed to be done |
| 150 | + continue |
| 151 | + } |
| 152 | + mld := createMLD(mbscObj, &imageSpec.ModuleImageSpec) |
| 153 | + err := mrh.buildSignAPI.Sync(ctx, mld, true, &mbscObj.ObjectMeta, imageSpec.Action) |
| 154 | + if err != nil { |
| 155 | + errs = append(errs, err) |
| 156 | + logger.Info(utils.WarnString("sync for image %s, action %s failed: %v"), imageSpec.Image, imageSpec.Action, err) |
| 157 | + } |
| 158 | + } |
| 159 | + return errors.Join(errs...) |
| 160 | +} |
| 161 | + |
| 162 | +func (mrh *mbscReconcilerHelper) garbageCollect(ctx context.Context, mbscObj *kmmv1beta1.ModuleBuildSignConfig) error { |
| 163 | + logger := log.FromContext(ctx) |
| 164 | + |
| 165 | + // Garbage collect for successfully finished build pods |
| 166 | + deleted, err := mrh.buildSignAPI.GarbageCollect(ctx, mbscObj.Name, mbscObj.Namespace, kmmv1beta1.BuildImage, &mbscObj.ObjectMeta) |
| 167 | + if err != nil { |
| 168 | + return fmt.Errorf("could not garbage collect build objects: %v", err) |
| 169 | + } |
| 170 | + |
| 171 | + logger.Info("Garbage-collected Build objects", "names", deleted) |
| 172 | + |
| 173 | + // Garbage collect for successfully finished sign pods |
| 174 | + deleted, err = mrh.buildSignAPI.GarbageCollect(ctx, mbscObj.Name, mbscObj.Namespace, kmmv1beta1.SignImage, &mbscObj.ObjectMeta) |
| 175 | + if err != nil { |
| 176 | + return fmt.Errorf("could not garbage collect sign objects: %v", err) |
| 177 | + } |
| 178 | + |
| 179 | + logger.Info("Garbage-collected Sign objects", "names", deleted) |
| 180 | + |
| 181 | + return nil |
| 182 | +} |
| 183 | + |
| 184 | +func createMLD(mbscObj *kmmv1beta1.ModuleBuildSignConfig, imageSpec *kmmv1beta1.ModuleImageSpec) *api.ModuleLoaderData { |
| 185 | + return &api.ModuleLoaderData{ |
| 186 | + Name: mbscObj.Name, |
| 187 | + Namespace: mbscObj.Namespace, |
| 188 | + ContainerImage: imageSpec.Image, |
| 189 | + Build: imageSpec.Build, |
| 190 | + Sign: imageSpec.Sign, |
| 191 | + Owner: mbscObj, |
| 192 | + KernelVersion: imageSpec.KernelVersion, |
| 193 | + KernelNormalizedVersion: kernel.NormalizeVersion(imageSpec.KernelVersion), |
| 194 | + ImageRepoSecret: mbscObj.Spec.ImageRepoSecret, |
| 195 | + RegistryTLS: imageSpec.RegistryTLS, |
| 196 | + } |
| 197 | +} |
0 commit comments