diff --git a/Tiltfile b/Tiltfile index 825ff85f8..de65b9851 100644 --- a/Tiltfile +++ b/Tiltfile @@ -21,10 +21,8 @@ docker_build('controller:latest', '.', only=[ local_resource('controller-gen', 'make generate', deps=['api/', 'hack/boilerplate.go.txt']) local_resource('crds', 'make install', deps=['api/']) -provider = os.getenv('PROVIDER', 'openconfig') - manager = kustomize('config/develop') -manager = str(manager).replace('--provider=openconfig', '--provider={}'.format(provider)) +manager = str(manager) k8s_yaml(blob(manager)) k8s_resource('network-operator-controller-manager', resource_deps=['controller-gen']) diff --git a/api/core/v1alpha1/device_types.go b/api/core/v1alpha1/device_types.go index a9a7e6cb6..f3b0881f6 100644 --- a/api/core/v1alpha1/device_types.go +++ b/api/core/v1alpha1/device_types.go @@ -24,6 +24,12 @@ type DeviceSpec struct { // +required Endpoint Endpoint `json:"endpoint"` + // Provider is the name of the responsible for reconciling the object connected to the device. + // ToDo: Make this a required field once we have multiple providers implemented. + // ToDo: make this of type uri e.g. nx.cisco.networking.metal.ironcore.dev + // +optional + Provider string `json:"provider,omitempty"` + // Provisioning is an optional configuration for the device provisioning process. // It can be used to provide initial configuration templates or scripts that are applied during the device provisioning. // +optional diff --git a/api/core/v1alpha1/groupversion_info.go b/api/core/v1alpha1/groupversion_info.go index b1d017686..3d65b21f8 100644 --- a/api/core/v1alpha1/groupversion_info.go +++ b/api/core/v1alpha1/groupversion_info.go @@ -181,6 +181,9 @@ const ( // ErrorReason indicates that an error occurred while reconciling the resource. ErrorReason = "Error" + // ProviderNotFoundReason indicates that the provider specified in the resource's device.spec.provider field was not found. + ProviderNotFoundReason = "ProviderNotFound" + // WaitingForDependenciesReason indicates that the resource is waiting for its dependencies to be ready. WaitingForDependenciesReason = "WaitingForDependencies" diff --git a/charts/network-operator/templates/crd/devices.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/devices.networking.metal.ironcore.dev.yaml index 16aef9365..7ec03a40e 100644 --- a/charts/network-operator/templates/crd/devices.networking.metal.ironcore.dev.yaml +++ b/charts/network-operator/templates/crd/devices.networking.metal.ironcore.dev.yaml @@ -194,6 +194,12 @@ spec: description: Paused can be used to prevent controllers from processing the Device and its associated objects. type: boolean + provider: + description: |- + Provider is the name of the responsible for reconciling the object connected to the device. + ToDo: Make this a required field once we have multiple providers implemented. + ToDo: make this of type uri e.g. nx.cisco.networking.metal.ironcore.dev + type: string provisioning: description: |- Provisioning is an optional configuration for the device provisioning process. diff --git a/cmd/main.go b/cmd/main.go index afb5498ba..210ba5df0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -48,7 +48,6 @@ import ( nxcontroller "github.com/ironcore-dev/network-operator/internal/controller/cisco/nx" corecontroller "github.com/ironcore-dev/network-operator/internal/controller/core" poolcontroller "github.com/ironcore-dev/network-operator/internal/controller/pool" - "github.com/ironcore-dev/network-operator/internal/provider" "github.com/ironcore-dev/network-operator/internal/provisioning" "github.com/ironcore-dev/network-operator/internal/resourcelock" tftpserver "github.com/ironcore-dev/network-operator/internal/tftp" @@ -94,7 +93,6 @@ func main() { //nolint:gocyclo var tlsOpts []func(*tls.Config) var watchNamespace string var watchFilterValue string - var providerName string var requeueInterval time.Duration var heartbeatInterval time.Duration var tftpPort int @@ -120,7 +118,6 @@ func main() { //nolint:gocyclo flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") flag.StringVar(&watchNamespace, "namespace", "", "Namespace that the controller watches to reconcile api objects. If unspecified, the controller watches for api objects across all namespaces.") flag.StringVar(&watchFilterValue, "watch-filter", "", fmt.Sprintf("Label value that the controller watches to reconcile api objects. Label key is always %q. If unspecified, the controller watches for all api objects.", v1alpha1.WatchLabel)) - flag.StringVar(&providerName, "provider", "openconfig", "The provider to use for the controller. If not specified, the default provider is used. Available providers: "+strings.Join(provider.Providers(), ", ")) flag.DurationVar(&requeueInterval, "requeue-interval", time.Hour, "The interval after which Kubernetes resources should be reconciled again regardless of whether they have changed.") flag.DurationVar(&heartbeatInterval, "heartbeat-interval", 30*time.Second, "The interval after which the controller retries a reachability check on each device.") flag.IntVar(&tftpPort, "tftp-port", 1069, "The port on which the inline TFTP server listens. Set to 0 to disable the TFTP server.") @@ -256,13 +253,6 @@ func main() { //nolint:gocyclo os.Exit(1) } - setupLog.Info("Using provider", "provider", providerName) - prov, err := provider.Get(providerName) - if err != nil { - setupLog.Error(err, "failed to get provider", "provider", providerName) - os.Exit(1) - } - ctx := ctrl.SetupSignalHandler() if lockerNamespace == "" { @@ -306,11 +296,11 @@ func main() { //nolint:gocyclo } if err := (&corecontroller.DeviceReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorder("device-controller"), - WatchFilterValue: watchFilterValue, - Provider: prov, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorder("device-controller"), + WatchFilterValue: watchFilterValue, + // Provider: prov, HeartbeatInterval: heartbeatInterval, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Device") @@ -322,7 +312,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("interface-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -335,7 +324,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("banner-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Banner") @@ -347,7 +335,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("user-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "User") @@ -359,7 +346,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("dns-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "DNS") @@ -371,7 +357,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("ntp-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "NTP") @@ -383,7 +368,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("acl-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "AccessControlList") @@ -395,7 +379,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("certificate-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Certificate") @@ -407,7 +390,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("snmp-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SNMP") @@ -419,7 +401,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("syslog-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Syslog") @@ -431,7 +412,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("managementaccess-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ManagementAccess") @@ -443,7 +423,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("isis-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ISIS") @@ -455,7 +434,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("pim-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "PIM") @@ -467,7 +445,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("bgp-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -480,7 +457,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("bgppeer-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -493,7 +469,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("lldp-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -506,7 +481,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("ospf-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -519,7 +493,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("vlan-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -532,7 +505,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("vrf-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VRF") @@ -544,7 +516,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("cisco-nx-vpcdomain-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -557,7 +528,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("nve-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -570,7 +540,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("cisco-nx-system-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "System") @@ -582,7 +551,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("evpn-instance-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "EVPNInstance") @@ -594,7 +562,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("aaa-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "AAA") @@ -606,7 +573,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("prefixset-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "PrefixSet") @@ -618,7 +584,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("routingpolicy-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "RoutingPolicy") @@ -630,7 +595,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("cisco-nx-border-gateway-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "BorderGateway") @@ -642,7 +606,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("dhcprelay-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -655,7 +618,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("ethernetsegment-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, RequeueInterval: requeueInterval, }).SetupWithManager(ctx, mgr); err != nil { @@ -779,14 +741,12 @@ func main() { //nolint:gocyclo // is implemented and the port is set to a non-zero value. // The server is added to the manager so it starts after the cache is synced // and shuts down gracefully when the manager stops. - provisioningProvider, ok := prov().(provider.ProvisioningProvider) - if provisioningHTTPPort != 0 && ok { + if provisioningHTTPPort != 0 { provisioningServer := &provisioning.HTTPServer{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("provisioning"), Recorder: mgr.GetEventRecorder("provisioning"), ValidateSourceIP: provisioningHTTPValidateSourceIP, - Provider: provisioningProvider, Port: provisioningHTTPPort, } setupLog.Info("Adding provisioning HTTP server to manager", "port", provisioningHTTPPort, "validateSourceIP", provisioningHTTPValidateSourceIP) diff --git a/config/crd/bases/networking.metal.ironcore.dev_devices.yaml b/config/crd/bases/networking.metal.ironcore.dev_devices.yaml index 67379c014..90c518dd8 100644 --- a/config/crd/bases/networking.metal.ironcore.dev_devices.yaml +++ b/config/crd/bases/networking.metal.ironcore.dev_devices.yaml @@ -191,6 +191,12 @@ spec: description: Paused can be used to prevent controllers from processing the Device and its associated objects. type: boolean + provider: + description: |- + Provider is the name of the responsible for reconciling the object connected to the device. + ToDo: Make this a required field once we have multiple providers implemented. + ToDo: make this of type uri e.g. nx.cisco.networking.metal.ironcore.dev + type: string provisioning: description: |- Provisioning is an optional configuration for the device provisioning process. diff --git a/config/develop/manager_patch.yaml b/config/develop/manager_patch.yaml index 4fddb21a4..3736922fb 100644 --- a/config/develop/manager_patch.yaml +++ b/config/develop/manager_patch.yaml @@ -3,7 +3,6 @@ value: - --leader-elect=false - --health-probe-bind-address=:8081 - - --provider=openconfig - --requeue-interval=30s - --max-concurrent-reconciles=5 - --zap-log-level=3 diff --git a/docs/api-reference/index.md b/docs/api-reference/index.md index 6a39c2b3b..9509edada 100644 --- a/docs/api-reference/index.md +++ b/docs/api-reference/index.md @@ -1319,6 +1319,7 @@ _Appears in:_ | --- | --- | --- | --- | | `paused` _boolean_ | Paused can be used to prevent controllers from processing the Device and its associated objects. | false | Optional: \{\}
| | `endpoint` _[Endpoint](#endpoint)_ | Endpoint contains the connection information for the device. | | Required: \{\}
| +| `provider` _string_ | Provider is the name of the responsible for reconciling the object connected to the device.
ToDo: Make this a required field once we have multiple providers implemented.
ToDo: make this of type uri e.g. nx.cisco.networking.metal.ironcore.dev | | Optional: \{\}
| | `provisioning` _[Provisioning](#provisioning)_ | Provisioning is an optional configuration for the device provisioning process.
It can be used to provide initial configuration templates or scripts that are applied during the device provisioning. | | Optional: \{\}
| diff --git a/internal/controller/cisco/nx/bordergateway_controller.go b/internal/controller/cisco/nx/bordergateway_controller.go index 1e9b2324f..b77dced69 100644 --- a/internal/controller/cisco/nx/bordergateway_controller.go +++ b/internal/controller/cisco/nx/bordergateway_controller.go @@ -51,9 +51,6 @@ type BorderGatewayReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the bordergateway. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -88,22 +85,39 @@ func (r *BorderGatewayReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{}, err } - prov, ok := r.Provider().(Provider) - if !ok { + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, Status: metav1.ConditionFalse, - Reason: v1alpha1.ErrorReason, - Message: "Invalid provider configured for BorderGateway reconciler", + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), }) { return ctrl.Result{}, r.Status().Update(ctx, obj) } return ctrl.Result{}, nil } + prov, ok := p().(Provider) - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err + if !ok { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.NotImplementedReason, + Message: "Provider does not implement provider.BorderGatewayProvider", + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil } if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { diff --git a/internal/controller/cisco/nx/bordergateway_controller_test.go b/internal/controller/cisco/nx/bordergateway_controller_test.go index bc6e27b97..8e0a93f10 100644 --- a/internal/controller/cisco/nx/bordergateway_controller_test.go +++ b/internal/controller/cisco/nx/bordergateway_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("BorderGateway Controller", func() { Namespace: metav1.NamespaceDefault, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, diff --git a/internal/controller/cisco/nx/suite_test.go b/internal/controller/cisco/nx/suite_test.go index 215331851..1020aefb6 100644 --- a/internal/controller/cisco/nx/suite_test.go +++ b/internal/controller/cisco/nx/suite_test.go @@ -116,13 +116,12 @@ var _ = BeforeSuite(func() { _, err = k8sManager.GetCache().GetInformer(ctx, &coordinationv1.Lease{}) Expect(err).NotTo(HaveOccurred()) - prov := func() provider.Provider { return testProvider } + provider.Register("test-provider", func() provider.Provider { return testProvider }) err = (&SystemReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -131,7 +130,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: scheme.Scheme, Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -140,7 +138,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -153,7 +150,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, HeartbeatInterval: 10 * time.Minute, }).SetupWithManager(k8sManager) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/cisco/nx/system_controller.go b/internal/controller/cisco/nx/system_controller.go index 58551fea2..74f60836b 100644 --- a/internal/controller/cisco/nx/system_controller.go +++ b/internal/controller/cisco/nx/system_controller.go @@ -48,9 +48,6 @@ type SystemReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the system. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -85,22 +82,39 @@ func (r *SystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, err } - prov, ok := r.Provider().(Provider) - if !ok { + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, Status: metav1.ConditionFalse, - Reason: v1alpha1.ErrorReason, - Message: "Invalid provider configured for System reconciler", + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), }) { return ctrl.Result{}, r.Status().Update(ctx, obj) } return ctrl.Result{}, nil } + prov, ok := p().(Provider) - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err + if !ok { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.NotImplementedReason, + Message: "Provider does not implement provider.SystemProvider", + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil } if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { diff --git a/internal/controller/cisco/nx/system_controller_test.go b/internal/controller/cisco/nx/system_controller_test.go index 1553a5c6d..f77e844a3 100644 --- a/internal/controller/cisco/nx/system_controller_test.go +++ b/internal/controller/cisco/nx/system_controller_test.go @@ -29,6 +29,7 @@ var _ = Describe("System Controller", func() { Namespace: metav1.NamespaceDefault, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, diff --git a/internal/controller/cisco/nx/vpcdomain_controller.go b/internal/controller/cisco/nx/vpcdomain_controller.go index a6320c5f0..bff820fa6 100644 --- a/internal/controller/cisco/nx/vpcdomain_controller.go +++ b/internal/controller/cisco/nx/vpcdomain_controller.go @@ -51,9 +51,6 @@ type VPCDomainReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the vPC - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -89,20 +86,39 @@ func (r *VPCDomainReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - prov, ok := r.Provider().(Provider) - if !ok { - meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, Status: metav1.ConditionFalse, - Reason: v1alpha1.NotImplementedReason, - Message: "Invalid provider configured for VPCDomain reconciler", - }) - return ctrl.Result{}, r.Status().Update(ctx, obj) + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil } + prov, ok := p().(Provider) - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err + if !ok { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.NotImplementedReason, + Message: "Provider does not implement provider.VPCDomainProvider", + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil } if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { diff --git a/internal/controller/cisco/nx/vpcdomain_controller_test.go b/internal/controller/cisco/nx/vpcdomain_controller_test.go index 5af9d41cc..10322a5aa 100644 --- a/internal/controller/cisco/nx/vpcdomain_controller_test.go +++ b/internal/controller/cisco/nx/vpcdomain_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("VPCDomain Controller", func() { Namespace: metav1.NamespaceDefault, }, Spec: corev1.DeviceSpec{ + Provider: "test-provider", Endpoint: corev1.Endpoint{ Address: "192.168.10.2:9339", }, @@ -208,7 +209,7 @@ var _ = Describe("VPCDomain Controller", func() { GenerateName: "vpc-dep-", Namespace: metav1.NamespaceDefault, }, - Spec: corev1.DeviceSpec{Endpoint: corev1.Endpoint{Address: "192.168.10.2:9339"}}, + Spec: corev1.DeviceSpec{Provider: "test-provider", Endpoint: corev1.Endpoint{Address: "192.168.10.2:9339"}}, } Expect(k8sClient.Create(ctx, deviceA)).To(Succeed()) name = deviceA.Name @@ -216,7 +217,7 @@ var _ = Describe("VPCDomain Controller", func() { By("Creating Device B") Expect(k8sClient.Create(ctx, &corev1.Device{ ObjectMeta: metav1.ObjectMeta{Name: name + "-b", Namespace: metav1.NamespaceDefault}, - Spec: corev1.DeviceSpec{Endpoint: corev1.Endpoint{Address: "192.168.10.3:9339"}}, + Spec: corev1.DeviceSpec{Provider: "test-provider", Endpoint: corev1.Endpoint{Address: "192.168.10.3:9339"}}, })).To(Succeed()) By("Creating physical interfaces on Device A and B") diff --git a/internal/controller/core/aaa_controller.go b/internal/controller/core/aaa_controller.go index c38b1061e..e83d8d528 100644 --- a/internal/controller/core/aaa_controller.go +++ b/internal/controller/core/aaa_controller.go @@ -46,9 +46,6 @@ type AAAReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the AAA configuration. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -84,7 +81,29 @@ func (r *AAAReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.AAAProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.AAAProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -97,11 +116,6 @@ func (r *AAAReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{}, err } diff --git a/internal/controller/core/acl_controller.go b/internal/controller/core/acl_controller.go index eba3251ef..ac6443680 100644 --- a/internal/controller/core/acl_controller.go +++ b/internal/controller/core/acl_controller.go @@ -49,9 +49,6 @@ type AccessControlListReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the accesscontrollist. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -86,7 +83,29 @@ func (r *AccessControlListReconciler) Reconcile(ctx context.Context, req ctrl.Re return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.ACLProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.ACLProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -99,11 +118,6 @@ func (r *AccessControlListReconciler) Reconcile(ctx context.Context, req ctrl.Re return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/acl_controller_test.go b/internal/controller/core/acl_controller_test.go index dda2bd847..4b2ab2927 100644 --- a/internal/controller/core/acl_controller_test.go +++ b/internal/controller/core/acl_controller_test.go @@ -33,6 +33,7 @@ var _ = Describe("AccessControlList Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/banner_controller.go b/internal/controller/core/banner_controller.go index 9e474648e..c4bc58086 100644 --- a/internal/controller/core/banner_controller.go +++ b/internal/controller/core/banner_controller.go @@ -51,9 +51,6 @@ type BannerReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the banner. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -90,7 +87,29 @@ func (r *BannerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.BannerProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.BannerProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -103,11 +122,6 @@ func (r *BannerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/banner_controller_test.go b/internal/controller/core/banner_controller_test.go index 0a944ce95..60dd4d978 100644 --- a/internal/controller/core/banner_controller_test.go +++ b/internal/controller/core/banner_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("Banner Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/bgp_controller.go b/internal/controller/core/bgp_controller.go index 58e1f6892..7bbf7cd91 100644 --- a/internal/controller/core/bgp_controller.go +++ b/internal/controller/core/bgp_controller.go @@ -56,9 +56,6 @@ type BGPReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the bgp. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -99,7 +96,29 @@ func (r *BGPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.BGPProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.BGPProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -112,11 +131,6 @@ func (r *BGPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/bgp_controller_test.go b/internal/controller/core/bgp_controller_test.go index e1a44db42..f10e60169 100644 --- a/internal/controller/core/bgp_controller_test.go +++ b/internal/controller/core/bgp_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("BGP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/bgp_peer_controller.go b/internal/controller/core/bgp_peer_controller.go index 72ec7ee4d..5238a5b59 100644 --- a/internal/controller/core/bgp_peer_controller.go +++ b/internal/controller/core/bgp_peer_controller.go @@ -59,9 +59,6 @@ type BGPPeerReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the bgppeer. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -103,7 +100,29 @@ func (r *BGPPeerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.BGPPeerProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.BGPPeerProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -116,11 +135,6 @@ func (r *BGPPeerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/bgp_peer_controller_test.go b/internal/controller/core/bgp_peer_controller_test.go index df8588957..9ad49fa7c 100644 --- a/internal/controller/core/bgp_peer_controller_test.go +++ b/internal/controller/core/bgp_peer_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("BGPPeer Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/certificate_controller.go b/internal/controller/core/certificate_controller.go index 640a8c75d..5c0276fdb 100644 --- a/internal/controller/core/certificate_controller.go +++ b/internal/controller/core/certificate_controller.go @@ -51,9 +51,6 @@ type CertificateReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the certificate. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -89,7 +86,29 @@ func (r *CertificateReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.CertificateProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.CertificateProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -102,11 +121,6 @@ func (r *CertificateReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/certificate_controller_test.go b/internal/controller/core/certificate_controller_test.go index 8d345b18f..67448e579 100644 --- a/internal/controller/core/certificate_controller_test.go +++ b/internal/controller/core/certificate_controller_test.go @@ -42,6 +42,7 @@ var _ = Describe("Certificate Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/device_controller.go b/internal/controller/core/device_controller.go index 9fa361d08..f6b749827 100644 --- a/internal/controller/core/device_controller.go +++ b/internal/controller/core/device_controller.go @@ -50,7 +50,7 @@ type DeviceReconciler struct { Recorder events.EventRecorder // Provider is the driver that will be used to create & delete the interface. - Provider provider.ProviderFunc + // Provider provider.ProviderFunc // HeartbeatInterval is the duration after which the controller requeues the reconciliation, // regardless of changes. @@ -129,7 +129,7 @@ func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, nil } - if _, ok := r.Provider().(provider.ProvisioningProvider); !ok { + if _, err := provider.LoadProvisioningProvider(obj.Spec.Provider); err != nil { // Skip provisioning if the provider does not support it. log.Info("Provider does not support provisioning, skipping") obj.Status.Phase = v1alpha1.DevicePhaseFailed @@ -193,7 +193,11 @@ func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, nil } log.Info("Device provisioning completed, running post provisioning checks") - prov, _ := r.Provider().(provider.ProvisioningProvider) + prov, err := provider.LoadProvisioningProvider(obj.Spec.Provider) + if err != nil { + log.Error(err, "Failed to load provisioning provider") + return ctrl.Result{}, err + } if ok := prov.VerifyProvisioned(ctx, conn, obj); !ok { return ctrl.Result{RequeueAfter: r.HeartbeatInterval}, nil } @@ -203,7 +207,7 @@ func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, nil case v1alpha1.DevicePhaseRunning: - if prov, ok := r.Provider().(provider.DeviceProvider); ok { + if prov, err := provider.LoadProvider[provider.DeviceProvider](obj.Spec.Provider); err == nil { if err := r.reconcile(ctx, obj, prov, conn); err != nil { log.Error(err, "Failed to reconcile resource") return ctrl.Result{}, err @@ -384,7 +388,10 @@ func (r *DeviceReconciler) reconcile(ctx context.Context, device *v1alpha1.Devic } func (r *DeviceReconciler) reconcileMinimal(ctx context.Context, device *v1alpha1.Device, conn *deviceutil.Connection) (reterr error) { - prov := r.Provider() + prov, err := provider.LoadProvider[provider.DeviceProvider](device.Spec.Provider) + if err != nil { + return fmt.Errorf("failed to load device provider: %w", err) + } if err := prov.Connect(ctx, conn); err != nil { conditions.Set(device, metav1.Condition{ Type: v1alpha1.ReachableCondition, @@ -440,7 +447,11 @@ func (r *DeviceReconciler) reconcileMaintenance(ctx context.Context, obj *v1alph v1alpha1.DeviceMaintenanceFactoryReset, v1alpha1.DeviceMaintenanceReprovision: - prov := r.Provider() + prov, err := provider.LoadProvider[provider.DeviceProvider](obj.Spec.Provider) + if err != nil { + return fmt.Errorf("failed to load device provider: %w", err) + } + if err := prov.Connect(ctx, conn); err != nil { return fmt.Errorf("failed to connect to device: %w", err) } diff --git a/internal/controller/core/device_controller_test.go b/internal/controller/core/device_controller_test.go index 7ff8af567..22d8a515f 100644 --- a/internal/controller/core/device_controller_test.go +++ b/internal/controller/core/device_controller_test.go @@ -72,6 +72,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -160,6 +161,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -204,6 +206,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -261,6 +264,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -321,6 +325,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -349,6 +354,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -404,6 +410,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ BootScript: v1alpha1.TemplateSource{ Inline: new("boot nxos.bin"), @@ -491,6 +498,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -539,6 +547,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -592,6 +601,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -658,6 +668,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/dhcprelay_controller.go b/internal/controller/core/dhcprelay_controller.go index ec88c2a47..e7cfda254 100644 --- a/internal/controller/core/dhcprelay_controller.go +++ b/internal/controller/core/dhcprelay_controller.go @@ -51,9 +51,6 @@ type DHCPRelayReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the dhcp relay configuration. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -92,7 +89,29 @@ func (r *DHCPRelayReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.DHCPRelayProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.DHCPRelayProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -105,11 +124,6 @@ func (r *DHCPRelayReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/dhcprelay_controller_test.go b/internal/controller/core/dhcprelay_controller_test.go index e440f1b03..149383b4f 100644 --- a/internal/controller/core/dhcprelay_controller_test.go +++ b/internal/controller/core/dhcprelay_controller_test.go @@ -46,6 +46,7 @@ var _ = Describe("DHCPRelay Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.50:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -391,6 +392,7 @@ var _ = Describe("DHCPRelay Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.51:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -478,6 +480,7 @@ var _ = Describe("DHCPRelay Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.52:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -494,6 +497,7 @@ var _ = Describe("DHCPRelay Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.53:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, otherDevice)).To(Succeed()) @@ -637,6 +641,7 @@ var _ = Describe("DHCPRelay Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.57:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -653,6 +658,7 @@ var _ = Describe("DHCPRelay Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.58:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, otherDevice)).To(Succeed()) @@ -822,6 +828,7 @@ var _ = Describe("DHCPRelay Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.54:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -1004,6 +1011,7 @@ var _ = Describe("DHCPRelay Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.55:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/dns_controller.go b/internal/controller/core/dns_controller.go index 6abe047ad..391f75e91 100644 --- a/internal/controller/core/dns_controller.go +++ b/internal/controller/core/dns_controller.go @@ -49,9 +49,6 @@ type DNSReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the dns. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -86,7 +83,29 @@ func (r *DNSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.DNSProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.DNSProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -99,11 +118,6 @@ func (r *DNSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/dns_controller_test.go b/internal/controller/core/dns_controller_test.go index 13fa24508..3e7cddd52 100644 --- a/internal/controller/core/dns_controller_test.go +++ b/internal/controller/core/dns_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("DNS Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/ethernetsegment_controller.go b/internal/controller/core/ethernetsegment_controller.go index 8f0a3ee21..099736a23 100644 --- a/internal/controller/core/ethernetsegment_controller.go +++ b/internal/controller/core/ethernetsegment_controller.go @@ -49,9 +49,6 @@ type EthernetSegmentReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the ethernetsegment. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -90,7 +87,29 @@ func (r *EthernetSegmentReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.EthernetSegmentProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.EthernetSegmentProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -103,11 +122,6 @@ func (r *EthernetSegmentReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/ethernetsegment_controller_test.go b/internal/controller/core/ethernetsegment_controller_test.go index 84f7cd983..49842bfcc 100644 --- a/internal/controller/core/ethernetsegment_controller_test.go +++ b/internal/controller/core/ethernetsegment_controller_test.go @@ -32,6 +32,7 @@ var _ = Describe("EthernetSegment Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/evpninstance_controller.go b/internal/controller/core/evpninstance_controller.go index 724c293b4..fe93001b7 100644 --- a/internal/controller/core/evpninstance_controller.go +++ b/internal/controller/core/evpninstance_controller.go @@ -49,9 +49,6 @@ type EVPNInstanceReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the evpninstance. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -88,7 +85,29 @@ func (r *EVPNInstanceReconciler) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.EVPNInstanceProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.EVPNInstanceProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -101,11 +120,6 @@ func (r *EVPNInstanceReconciler) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/evpninstance_controller_test.go b/internal/controller/core/evpninstance_controller_test.go index 0b3ca6ebe..3bd2fefdf 100644 --- a/internal/controller/core/evpninstance_controller_test.go +++ b/internal/controller/core/evpninstance_controller_test.go @@ -32,6 +32,7 @@ var _ = Describe("EVPNInstance Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/interface_controller.go b/internal/controller/core/interface_controller.go index 8e5f7a46d..e43cf6a2b 100644 --- a/internal/controller/core/interface_controller.go +++ b/internal/controller/core/interface_controller.go @@ -54,9 +54,6 @@ type InterfaceReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the interface. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -65,6 +62,34 @@ type InterfaceReconciler struct { RequeueInterval time.Duration } +// handleProviderError handles provider loading errors and updates the object status condition accordingly. +// Returns (handled, error) where handled=true means the error was processed and reconciliation should return. +func (r *InterfaceReconciler) handleProviderError(ctx context.Context, obj *v1alpha1.Interface, providerName string, err error) (bool, error) { + if errors.Is(err, provider.NotFoundError{}) { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", providerName), + }) { + return true, r.Status().Update(ctx, obj) + } + return true, nil + } + if errors.Is(err, provider.NotImplementedError{}) { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.NotImplementedReason, + Message: "Provider does not implement provider.InterfaceProvider", + }) { + return true, r.Status().Update(ctx, obj) + } + return true, nil + } + return false, err +} + // +kubebuilder:rbac:groups=networking.metal.ironcore.dev,resources=interfaces,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=networking.metal.ironcore.dev,resources=interfaces/status,verbs=get;update;patch // +kubebuilder:rbac:groups=networking.metal.ironcore.dev,resources=interfaces/finalizers,verbs=update @@ -98,24 +123,22 @@ func (r *InterfaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.InterfaceProvider) - if !ok { - if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ - Type: v1alpha1.ReadyCondition, - Status: metav1.ConditionFalse, - Reason: v1alpha1.NotImplementedReason, - Message: "Provider does not implement provider.InterfaceProvider", - }) { - return ctrl.Result{}, r.Status().Update(ctx, obj) - } - return ctrl.Result{}, nil - } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) if err != nil { return ctrl.Result{}, err } + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + prov, err := provider.LoadProvider[provider.InterfaceProvider](device.Spec.Provider) + if err != nil { + if handled, herr := r.handleProviderError(ctx, obj, device.Spec.Provider, err); handled { + return ctrl.Result{}, herr + } + } + if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/interface_controller_test.go b/internal/controller/core/interface_controller_test.go index a250cb2af..cf9109ca5 100644 --- a/internal/controller/core/interface_controller_test.go +++ b/internal/controller/core/interface_controller_test.go @@ -35,6 +35,7 @@ var _ = Describe("Interface Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -1276,6 +1277,7 @@ var _ = Describe("Interface Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.10:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, localDevice)).To(Succeed()) @@ -1289,6 +1291,7 @@ var _ = Describe("Interface Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.11:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, remoteDevice)).To(Succeed()) diff --git a/internal/controller/core/isis_controller.go b/internal/controller/core/isis_controller.go index 00c77033b..3e0a3a132 100644 --- a/internal/controller/core/isis_controller.go +++ b/internal/controller/core/isis_controller.go @@ -50,9 +50,6 @@ type ISISReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the isis. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -87,7 +84,29 @@ func (r *ISISReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.ISISProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.ISISProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -100,11 +119,6 @@ func (r *ISISReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/isis_controller_test.go b/internal/controller/core/isis_controller_test.go index 3331cf3eb..a966dbd56 100644 --- a/internal/controller/core/isis_controller_test.go +++ b/internal/controller/core/isis_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("ISIS Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -138,6 +139,7 @@ var _ = Describe("ISIS Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/lldp_controller.go b/internal/controller/core/lldp_controller.go index 4a4471b45..695301514 100644 --- a/internal/controller/core/lldp_controller.go +++ b/internal/controller/core/lldp_controller.go @@ -51,9 +51,6 @@ type LLDPReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the LLDP. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -87,22 +84,39 @@ func (r *LLDPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.LLDPProvider) - if !ok { + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, Status: metav1.ConditionFalse, - Reason: v1alpha1.NotImplementedReason, - Message: "Provider does not implement provider LLDPProvider", + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), }) { return ctrl.Result{}, r.Status().Update(ctx, obj) } return ctrl.Result{}, nil } + prov, ok := p().(provider.LLDPProvider) - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err + if !ok { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.NotImplementedReason, + Message: "Provider does not implement provider.LLDPProvider", + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil } if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { diff --git a/internal/controller/core/lldp_controller_test.go b/internal/controller/core/lldp_controller_test.go index f47e833da..3367e4984 100644 --- a/internal/controller/core/lldp_controller_test.go +++ b/internal/controller/core/lldp_controller_test.go @@ -37,6 +37,7 @@ var _ = Describe("LLDP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -334,6 +335,7 @@ var _ = Describe("LLDP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.6:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -456,6 +458,7 @@ var _ = Describe("LLDP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -604,6 +607,7 @@ var _ = Describe("LLDP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.3:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -688,6 +692,7 @@ var _ = Describe("LLDP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.99:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, otherDevice)).To(Succeed()) @@ -839,6 +844,7 @@ var _ = Describe("LLDP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.4:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -1081,6 +1087,7 @@ var _ = Describe("LLDP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.7:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -1272,6 +1279,7 @@ var _ = Describe("LLDP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.8:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/managementaccess_controller.go b/internal/controller/core/managementaccess_controller.go index 1cd63f6d2..10ca3eb7c 100644 --- a/internal/controller/core/managementaccess_controller.go +++ b/internal/controller/core/managementaccess_controller.go @@ -49,9 +49,6 @@ type ManagementAccessReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the managementaccess. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -86,7 +83,29 @@ func (r *ManagementAccessReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.ManagementAccessProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.ManagementAccessProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -99,11 +118,6 @@ func (r *ManagementAccessReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/managementaccess_controller_test.go b/internal/controller/core/managementaccess_controller_test.go index bc52d7479..57f39e5a5 100644 --- a/internal/controller/core/managementaccess_controller_test.go +++ b/internal/controller/core/managementaccess_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("ManagementAccess Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/ntp_controller.go b/internal/controller/core/ntp_controller.go index 806799a50..4000388f6 100644 --- a/internal/controller/core/ntp_controller.go +++ b/internal/controller/core/ntp_controller.go @@ -49,9 +49,6 @@ type NTPReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the ntp. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -86,7 +83,29 @@ func (r *NTPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.NTPProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.NTPProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -99,11 +118,6 @@ func (r *NTPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/ntp_controller_test.go b/internal/controller/core/ntp_controller_test.go index c51dab2bb..43acc5de0 100644 --- a/internal/controller/core/ntp_controller_test.go +++ b/internal/controller/core/ntp_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("NTP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/nve_controller.go b/internal/controller/core/nve_controller.go index bb032f748..da9aa736d 100644 --- a/internal/controller/core/nve_controller.go +++ b/internal/controller/core/nve_controller.go @@ -51,9 +51,6 @@ type NetworkVirtualizationEdgeReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the dns. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -87,22 +84,39 @@ func (r *NetworkVirtualizationEdgeReconciler) Reconcile(ctx context.Context, req return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.NVEProvider) - if !ok { + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, Status: metav1.ConditionFalse, - Reason: v1alpha1.NotImplementedReason, - Message: "Provider does not implement provider NVEProvider", + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), }) { return ctrl.Result{}, r.Status().Update(ctx, obj) } return ctrl.Result{}, nil } + prov, ok := p().(provider.NVEProvider) - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err + if !ok { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.NotImplementedReason, + Message: "Provider does not implement provider.NVEProvider", + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil } if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { diff --git a/internal/controller/core/nve_controller_test.go b/internal/controller/core/nve_controller_test.go index 0ce53e624..b0b238a95 100644 --- a/internal/controller/core/nve_controller_test.go +++ b/internal/controller/core/nve_controller_test.go @@ -38,6 +38,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -206,6 +207,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -324,6 +326,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -398,6 +401,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -492,6 +496,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -600,6 +605,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -699,6 +705,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -714,6 +721,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device2)).To(Succeed()) @@ -818,6 +826,7 @@ var _ = Describe("NVE Controller", func() { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/ospf_controller.go b/internal/controller/core/ospf_controller.go index 2e69c38fe..589553274 100644 --- a/internal/controller/core/ospf_controller.go +++ b/internal/controller/core/ospf_controller.go @@ -52,9 +52,6 @@ type OSPFReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the ospf. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -93,7 +90,29 @@ func (r *OSPFReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.OSPFProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.OSPFProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -106,11 +125,6 @@ func (r *OSPFReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/ospf_controller_test.go b/internal/controller/core/ospf_controller_test.go index 183991695..1ce039dbc 100644 --- a/internal/controller/core/ospf_controller_test.go +++ b/internal/controller/core/ospf_controller_test.go @@ -32,6 +32,7 @@ var _ = Describe("OSPF Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -137,6 +138,7 @@ var _ = Describe("OSPF Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.3:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/pim_controller.go b/internal/controller/core/pim_controller.go index 5bd8d03ea..d8b264054 100644 --- a/internal/controller/core/pim_controller.go +++ b/internal/controller/core/pim_controller.go @@ -50,9 +50,6 @@ type PIMReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the pim. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -87,7 +84,29 @@ func (r *PIMReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.PIMProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.PIMProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -100,11 +119,6 @@ func (r *PIMReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/pim_controller_test.go b/internal/controller/core/pim_controller_test.go index 241796bf7..c36800ddd 100644 --- a/internal/controller/core/pim_controller_test.go +++ b/internal/controller/core/pim_controller_test.go @@ -32,6 +32,7 @@ var _ = Describe("PIM Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -131,6 +132,7 @@ var _ = Describe("PIM Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/prefixset_controller.go b/internal/controller/core/prefixset_controller.go index 73a220631..76799a74d 100644 --- a/internal/controller/core/prefixset_controller.go +++ b/internal/controller/core/prefixset_controller.go @@ -49,9 +49,6 @@ type PrefixSetReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the prefixset. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -86,7 +83,29 @@ func (r *PrefixSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.PrefixSetProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.PrefixSetProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -99,11 +118,6 @@ func (r *PrefixSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/prefixset_controller_test.go b/internal/controller/core/prefixset_controller_test.go index 5560d2af0..ea4a40d1b 100644 --- a/internal/controller/core/prefixset_controller_test.go +++ b/internal/controller/core/prefixset_controller_test.go @@ -32,6 +32,7 @@ var _ = Describe("PrefixSet Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/routingpolicy_controller.go b/internal/controller/core/routingpolicy_controller.go index 63acc8c37..0f0aa4182 100644 --- a/internal/controller/core/routingpolicy_controller.go +++ b/internal/controller/core/routingpolicy_controller.go @@ -49,9 +49,6 @@ type RoutingPolicyReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the routingpolicy. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -86,7 +83,29 @@ func (r *RoutingPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.RoutingPolicyProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.RoutingPolicyProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -99,11 +118,6 @@ func (r *RoutingPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/routingpolicy_controller_test.go b/internal/controller/core/routingpolicy_controller_test.go index 8ee79ae21..37df446f1 100644 --- a/internal/controller/core/routingpolicy_controller_test.go +++ b/internal/controller/core/routingpolicy_controller_test.go @@ -34,6 +34,7 @@ var _ = Describe("RoutingPolicy Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/snmp_controller.go b/internal/controller/core/snmp_controller.go index 2c2012e62..bba8be1a0 100644 --- a/internal/controller/core/snmp_controller.go +++ b/internal/controller/core/snmp_controller.go @@ -49,9 +49,6 @@ type SNMPReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the snmp. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -86,7 +83,29 @@ func (r *SNMPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.SNMPProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.SNMPProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -99,11 +118,6 @@ func (r *SNMPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/snmp_controller_test.go b/internal/controller/core/snmp_controller_test.go index 8a32f1f49..8bc1c7c1d 100644 --- a/internal/controller/core/snmp_controller_test.go +++ b/internal/controller/core/snmp_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("SNMP Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/suite_test.go b/internal/controller/core/suite_test.go index 78443d627..2e50e4e4b 100644 --- a/internal/controller/core/suite_test.go +++ b/internal/controller/core/suite_test.go @@ -115,13 +115,12 @@ var _ = BeforeSuite(func() { _, err = k8sManager.GetCache().GetInformer(ctx, &coordinationv1.Lease{}) Expect(err).NotTo(HaveOccurred()) - prov := func() provider.Provider { return testProvider } + provider.Register("test-provider", func() provider.Provider { return testProvider }) err = (&DeviceReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, HeartbeatInterval: time.Second, }).SetupWithManager(k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -130,7 +129,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -140,7 +138,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -149,7 +146,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -158,7 +154,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -167,7 +162,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -176,7 +170,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -185,7 +178,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -194,7 +186,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -203,7 +194,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -212,7 +202,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -221,7 +210,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -230,7 +218,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -239,7 +226,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -248,7 +234,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -258,7 +243,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -268,7 +252,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -278,7 +261,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -288,7 +270,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -297,7 +278,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -307,7 +287,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -316,7 +295,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -325,7 +303,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -335,7 +312,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -345,7 +321,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/core/syslog_controller.go b/internal/controller/core/syslog_controller.go index 12d8dd364..93945a1b1 100644 --- a/internal/controller/core/syslog_controller.go +++ b/internal/controller/core/syslog_controller.go @@ -49,9 +49,6 @@ type SyslogReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the syslog. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -86,7 +83,29 @@ func (r *SyslogReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.SyslogProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.SyslogProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -99,11 +118,6 @@ func (r *SyslogReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/syslog_controller_test.go b/internal/controller/core/syslog_controller_test.go index fc16816b0..b3be65a07 100644 --- a/internal/controller/core/syslog_controller_test.go +++ b/internal/controller/core/syslog_controller_test.go @@ -31,6 +31,7 @@ var _ = Describe("Syslog Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/user_controller.go b/internal/controller/core/user_controller.go index ff16800a3..a053ded3e 100644 --- a/internal/controller/core/user_controller.go +++ b/internal/controller/core/user_controller.go @@ -51,9 +51,6 @@ type UserReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the user. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -89,7 +86,29 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.UserProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.UserProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -102,11 +121,6 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/user_controller_test.go b/internal/controller/core/user_controller_test.go index 308660ea1..52a0c813c 100644 --- a/internal/controller/core/user_controller_test.go +++ b/internal/controller/core/user_controller_test.go @@ -33,6 +33,7 @@ var _ = Describe("User Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/vlan_controller.go b/internal/controller/core/vlan_controller.go index c86866302..470d5c106 100644 --- a/internal/controller/core/vlan_controller.go +++ b/internal/controller/core/vlan_controller.go @@ -49,9 +49,6 @@ type VLANReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the vlan. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker @@ -90,7 +87,29 @@ func (r *VLANReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.VLANProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.VLANProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -103,11 +122,6 @@ func (r *VLANReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/vlan_controller_test.go b/internal/controller/core/vlan_controller_test.go index 51a078a48..2f347e174 100644 --- a/internal/controller/core/vlan_controller_test.go +++ b/internal/controller/core/vlan_controller_test.go @@ -32,6 +32,7 @@ var _ = Describe("VLAN Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/controller/core/vrf_controller.go b/internal/controller/core/vrf_controller.go index 05a1cb30a..6336613fe 100644 --- a/internal/controller/core/vrf_controller.go +++ b/internal/controller/core/vrf_controller.go @@ -49,9 +49,6 @@ type VRFReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to create & delete the isis. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -88,7 +85,29 @@ func (r *VRFReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.VRFProvider) + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + if device.Spec.Provider == "" { + return ctrl.Result{}, fmt.Errorf("device %q has no provider set", device.Name) + } + + p, err := provider.Get(device.Spec.Provider) + if err != nil { + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: v1alpha1.ProviderNotFoundReason, + Message: fmt.Sprintf("Provider %q is not registered", device.Spec.Provider), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + prov, ok := p().(provider.VRFProvider) + if !ok { if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ Type: v1alpha1.ReadyCondition, @@ -101,11 +120,6 @@ func (r *VRFReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl return ctrl.Result{}, nil } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) - if err != nil { - return ctrl.Result{}, err - } - if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil { return ctrl.Result{Requeue: requeue}, err } diff --git a/internal/controller/core/vrf_controller_test.go b/internal/controller/core/vrf_controller_test.go index 58ecb8622..34903eb92 100644 --- a/internal/controller/core/vrf_controller_test.go +++ b/internal/controller/core/vrf_controller_test.go @@ -33,6 +33,7 @@ var _ = Describe("VRF Controller", func() { Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) diff --git a/internal/provider/cisco/iosxr/provider.go b/internal/provider/cisco/iosxr/provider.go index c05255a09..a88262c6e 100644 --- a/internal/provider/cisco/iosxr/provider.go +++ b/internal/provider/cisco/iosxr/provider.go @@ -575,5 +575,5 @@ func (p *Provider) GetPeerStatus(ctx context.Context, req *provider.BGPPeerStatu } func init() { - provider.Register("cisco-iosxr-gnmi", NewProvider) + provider.Register("iosxr.cisco.networking.metal.ironcore.dev", NewProvider) } diff --git a/internal/provider/cisco/nxos/provider.go b/internal/provider/cisco/nxos/provider.go index 381e5ac9a..c6619861f 100644 --- a/internal/provider/cisco/nxos/provider.go +++ b/internal/provider/cisco/nxos/provider.go @@ -3822,5 +3822,5 @@ func (p *Provider) DeleteAAA(ctx context.Context, req *provider.DeleteAAARequest } func init() { - provider.Register("cisco-nxos-gnmi", NewProvider) + provider.Register("nx.cisco.networking.metal.ironcore.dev", NewProvider) } diff --git a/internal/provider/openconfig/provider.go b/internal/provider/openconfig/provider.go index 6ad131ff0..6f7becc2c 100644 --- a/internal/provider/openconfig/provider.go +++ b/internal/provider/openconfig/provider.go @@ -55,5 +55,5 @@ func (p *Provider) Disconnect(_ context.Context, _ *deviceutil.Connection) error } func init() { - provider.Register("openconfig", NewProvider) + provider.Register("openconfig.networking.metal.ironcore.dev", NewProvider) } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 4ba8ff86f..789d78aba 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -824,3 +824,56 @@ type ProviderConfig struct { //nolint:revive // stutter is intentional; Provider func (p ProviderConfig) Into(v any) error { return runtime.DefaultUnstructuredConverter.FromUnstructured(p.obj.Object, v) } + +type NotFoundError struct { + providerName string +} + +type NotImplementedError struct { + reason string +} + +func (e NotImplementedError) Error() string { + return e.reason +} + +func (e NotFoundError) Error() string { + return e.providerName +} + +func (e NotFoundError) Is(target error) bool { + _, ok := target.(NotFoundError) + return ok +} + +func (e NotImplementedError) Is(target error) bool { + _, ok := target.(NotImplementedError) + return ok +} + +// Load Provider +func LoadProvider[T Provider](providerName string) (T, error) { + prov, err := Get(providerName) + if err != nil { + var zero T + return zero, err + } + genericProvider, ok := prov().(T) + if !ok { + var zero T + return zero, fmt.Errorf("provider %q does not implement the required interface", providerName) + } + return genericProvider, nil +} + +func LoadProvisioningProvider(providerName string) (ProvisioningProvider, error) { + prov, err := Get(providerName) + if err != nil { + return nil, err + } + provisioningProvider, ok := prov().(ProvisioningProvider) + if !ok { + return nil, NotImplementedError{reason: fmt.Sprintf("provider %q does not implement ProvisioningProvider interface", providerName)} + } + return provisioningProvider, nil +} diff --git a/internal/provisioning/http.go b/internal/provisioning/http.go index a414297b2..b46bc8597 100644 --- a/internal/provisioning/http.go +++ b/internal/provisioning/http.go @@ -91,8 +91,8 @@ type HTTPServer struct { Mux *http.ServeMux Recorder events.EventRecorder ValidateSourceIP bool - Provider provider.ProvisioningProvider - Port int + // Provider provider.ProvisioningProvider + Port int } func (s *HTTPServer) Start(ctx context.Context) error { @@ -306,7 +306,14 @@ func (s *HTTPServer) HandleProvisioningRequest(w http.ResponseWriter, r *http.Re return } - hashedPassword, hashAlgorithm, err := s.Provider.HashProvisioningPassword(string(pass)) + prov, err := provider.LoadProvisioningProvider(device.Spec.Provider) + if err != nil { + s.Logger.Error(err, "Failed to get provider", "provider", device.Spec.Provider, "device", device.Name, "error", err) + http.Error(w, "Failed to get provider", http.StatusPreconditionRequired) + return + } + + hashedPassword, hashAlgorithm, err := prov.HashProvisioningPassword(string(pass)) if err != nil { s.Logger.Error(err, "Failed to hash provisioning password", "device", device.Name) http.Error(w, "Failed to hash provisioning password", http.StatusInternalServerError) diff --git a/internal/provisioning/http_test.go b/internal/provisioning/http_test.go index 4b5477552..345e50f55 100644 --- a/internal/provisioning/http_test.go +++ b/internal/provisioning/http_test.go @@ -27,6 +27,7 @@ import ( "github.com/ironcore-dev/network-operator/api/core/v1alpha1" "github.com/ironcore-dev/network-operator/internal/deviceutil" + "github.com/ironcore-dev/network-operator/internal/provider" ) var ( @@ -39,6 +40,7 @@ var ( }, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.1.100:22", SecretRef: &v1alpha1.SecretReference{ @@ -79,6 +81,14 @@ type MockProvider struct { mock.Mock } +func (m *MockProvider) Connect(ctx context.Context, conn *deviceutil.Connection) error { + return nil +} + +func (m *MockProvider) Disconnect(ctx context.Context, conn *deviceutil.Connection) error { + return nil +} + func (m *MockProvider) HashProvisioningPassword(password string) (string, string, error) { return "hashedpass", "sha256", nil } @@ -419,6 +429,7 @@ func TestHandleProvisioningRequest(t *testing.T) { }, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: "192.168.1.200:22"}, + Provider: "test-provider", }, Status: v1alpha1.DeviceStatus{SerialNumber: "ABC123"}, }, @@ -437,6 +448,7 @@ func TestHandleProvisioningRequest(t *testing.T) { Labels: map[string]string{v1alpha1.DeviceSerialLabel: "ABC123"}, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.1.100:22", SecretRef: &v1alpha1.SecretReference{ @@ -506,7 +518,6 @@ func TestHandleProvisioningRequest(t *testing.T) { Client: k8sClient, Logger: klog.NewKlogr(), ValidateSourceIP: tt.validateSourceIP, - Provider: new(MockProvider), } rr := httptest.NewRecorder() @@ -986,6 +997,7 @@ func TestGetMTLSClientCA(t *testing.T) { Labels: map[string]string{v1alpha1.DeviceSerialLabel: "ABC123"}, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.1.100:22", }, @@ -1010,6 +1022,7 @@ func TestGetMTLSClientCA(t *testing.T) { Labels: map[string]string{v1alpha1.DeviceSerialLabel: "ABC123"}, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.1.100:22", TLS: &v1alpha1.TLS{ @@ -1042,6 +1055,7 @@ func TestGetMTLSClientCA(t *testing.T) { Labels: map[string]string{v1alpha1.DeviceSerialLabel: "ABC123"}, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.1.100:22", TLS: &v1alpha1.TLS{ @@ -1073,6 +1087,7 @@ func TestGetMTLSClientCA(t *testing.T) { Labels: map[string]string{v1alpha1.DeviceSerialLabel: "ABC123"}, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.1.100:22", TLS: &v1alpha1.TLS{ @@ -1152,4 +1167,5 @@ func TestGetMTLSClientCA(t *testing.T) { func init() { utilruntime.Must(v1alpha1.AddToScheme(scheme.Scheme)) + provider.Register("test-provider", func() provider.Provider { return &MockProvider{} }) }