package pxe import ( "context" "github.com/go-logr/logr" v1dnsmasq "github.com/kvaps/dnsmasq-controller/api/v1beta1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" "edge-infra.dev/pkg/k8s/runtime/controller" edgereconcile "edge-infra.dev/pkg/k8s/runtime/controller/reconcile" "edge-infra.dev/pkg/k8s/runtime/patch" "edge-infra.dev/pkg/k8s/runtime/sap" v1ienode "edge-infra.dev/pkg/sds/ien/k8s/apis/v1" v1pxe "edge-infra.dev/pkg/sds/ien/k8s/controllers/pxe/apis/v1" "edge-infra.dev/pkg/sds/ien/k8s/controllers/pxe/common" ) // +kubebuilder:rbac:groups=dsds.edge.ncr.com,resources=pxes,verbs=get;list;watch;update;patch // +kubebuilder:rbac:groups=dsds.edge.ncr.com,resources=pxes/status,verbs=get;create;patch;delete // +kubebuilder:rbac:groups=dsds.edge.ncr.com,resources=ienodes,verbs=get;list;watch // +kubebuilder:rbac:groups=dnsmasq.kvaps.cf,resources=dnsmasqoptions,namespace=pxe,verbs=get;create;update;patch;delete // +kubebuilder:rbac:groups=dnsmasq.kvaps.cf,resources=dhcphosts;dhcpoptions,namespace=pxe,verbs=get;create;patch;delete // +kubebuilder:rbac:groups=dnsmasq.kvaps.cf,resources=dnsmasqoptions,verbs=list;watch // +kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch // +kubebuilder:rbac:groups="",resources=configmaps,resourceNames=boot-options,namespace=pxe,verbs=get // +kubebuilder:rbac:groups="",resources=configmaps,resourceNames=ipxe-files,namespace=pxe,verbs=get;patch;delete // +kubebuilder:rbac:groups="",resources=configmaps,resourceNames=static-file-server-config,namespace=pxe,verbs=get;patch // +kubebuilder:rbac:groups="",resources=secrets,namespace=pxe,verbs=get // +kubebuilder:rbac:groups="",resources=configmaps;secrets;services,verbs=list;watch // +kubebuilder:rbac:groups="apps",resources=deployments,resourceNames=dnsmasq-controller;static-file-server,namespace=pxe,verbs=get;patch;delete // +kubebuilder:rbac:groups="apps",resources=deployments,verbs=list;watch // TODO: FIX(10563) Remove this temporary fix in 0.21 // +kubebuilder:rbac:groups="apps",resources=deployments,resourceNames=static-file-server-deployment,namespace=pxe,verbs=get;delete // +kubebuilder:rbac:groups="",resources=configmaps,resourceNames=static-file-server-configmap,namespace=pxe,verbs=get;delete var ( metricsBindAddress = ":9000" // avoid conflicting with dnsmasq controller binding healthBindAddress = ":9001" // avoid conflicting with dnsmasq controller binding ) type Reconciler struct { name string client client.Client manager *sap.ResourceManager } // Run the PXE controller func Run(log logr.Logger, opts ...controller.Option) error { ctrl.SetLogger(log) opts = append(opts, controller.WithMetricsAddress(metricsBindAddress)) mgr, err := createManager(opts...) if err != nil { return err } if err := registerControllers(mgr); err != nil { return err } // TODO: FIX(10563) Remove this temporary fix in 0.21 ctx := ctrl.LoggerInto(context.Background(), log) key := client.ObjectKey{ Namespace: common.PXENamespace, Name: "static-file-server-deployment", } deploy := &appsv1.Deployment{} err = mgr.GetAPIReader().Get(ctx, key, deploy) if client.IgnoreNotFound(err) != nil { return err } if err == nil { if err := mgr.GetClient().Delete(ctx, deploy); err != nil { return err } } key = client.ObjectKey{ Namespace: common.PXENamespace, Name: "static-file-server-configmap", } cm := &corev1.ConfigMap{} err = mgr.GetAPIReader().Get(ctx, key, cm) if client.IgnoreNotFound(err) != nil { return err } if err == nil { if err := mgr.GetClient().Delete(ctx, cm); err != nil { return err } } // start controllers registered to the manager return mgr.Start(ctrl.SetupSignalHandler()) } // createManager returns a new controller manager func createManager(ctrlOpts ...controller.Option) (ctrl.Manager, error) { cfg, opts := managerOptions(ctrlOpts...) return ctrl.NewManager(cfg, opts) } // managerOptions returns the rest configuration and manager options for the // controller manager func managerOptions(ctrlOpts ...controller.Option) (*rest.Config, manager.Options) { cfg, opts := controller.ProcessOptions(ctrlOpts...) opts.Scheme = scheme() opts.HealthProbeBindAddress = healthBindAddress opts.Cache = cacheOptions() return cfg, opts } // cacheOptions returns cache options that ensure that only the resources that // PXE controller needs to work with are cached func cacheOptions() cache.Options { return cache.Options{ ByObject: map[client.Object]cache.ByObject{ &v1pxe.PXE{}: { Field: fields.Everything(), }, &v1ienode.IENode{}: { Field: fields.Everything(), }, &corev1.Node{}: { Field: fields.Everything(), }, &corev1.ConfigMap{}: { Field: fields.SelectorFromSet(fields.Set{ "metadata.namespace": common.PXENamespace, }), }, &corev1.Secret{}: { Field: fields.SelectorFromSet(fields.Set{ "metadata.namespace": common.PXENamespace, }), }, &appsv1.Deployment{}: { Field: fields.SelectorFromSet(fields.Set{ "metadata.namespace": common.PXENamespace, }), }, &v1dnsmasq.DnsmasqOptions{}: { Field: fields.SelectorFromSet(fields.Set{ "metadata.namespace": common.PXENamespace, }), }, &v1dnsmasq.DhcpHosts{}: { Field: fields.SelectorFromSet(fields.Set{ "metadata.namespace": common.PXENamespace, }), }, &v1dnsmasq.DhcpOptions{}: { Field: fields.SelectorFromSet(fields.Set{ "metadata.namespace": common.PXENamespace, }), }, }, } } // registerControllers registers the PXE reconcilers to the controller manager func registerControllers(mgr ctrl.Manager) error { client := mgr.GetClient() scaler, err := NewScaler(mgr, client) if err != nil { return err } if err := scaler.SetupWithManager(mgr); err != nil { return err } provisioner, err := NewProvisioner(mgr, client) if err != nil { return err } return provisioner.SetupWithManager(mgr) } // scheme returns a scheme with the default schema as well as the iennode and // dnsmasq custom resources registered func scheme() *runtime.Scheme { scheme := runtime.NewScheme() utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(v1ienode.AddToScheme(scheme)) utilruntime.Must(v1pxe.AddToScheme(scheme)) utilruntime.Must(v1dnsmasq.AddToScheme(scheme)) return scheme } // summarize summarizes the result of the reconcile and patches the pxe object func (r *Reconciler) summarize(ctx context.Context, patcher *patch.SerialPatcher, pxe *v1pxe.PXE, recErr error) (ctrl.Result, error) { s := edgereconcile.NewSummarizer(patcher) return s.SummarizeAndPatch(ctx, pxe, edgereconcile.WithResult(edgereconcile.ResultEmpty), edgereconcile.WithError(recErr), edgereconcile.WithProcessors( edgereconcile.RecordReconcileReq, edgereconcile.RecordResult, ), edgereconcile.WithFieldOwner(r.name), ) }