diff --git a/Tiltfile b/Tiltfile
index 6605ef886..65ac24991 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..d4bcd7f17 100644
--- a/api/core/v1alpha1/device_types.go
+++ b/api/core/v1alpha1/device_types.go
@@ -24,6 +24,11 @@ type DeviceSpec struct {
// +required
Endpoint Endpoint `json:"endpoint"`
+ // Provider identifies the network provider plugin used to reconcile this Device and all CRDs that reference it.
+ // +required
+ // +immutable
+ 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 2bac14340..0ada11111 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..ccf98b20d 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,10 @@ spec:
description: Paused can be used to prevent controllers from processing
the Device and its associated objects.
type: boolean
+ provider:
+ description: Provider identifies the network provider plugin used
+ to reconcile this Device and all CRDs that reference it.
+ type: string
provisioning:
description: |-
Provisioning is an optional configuration for the device provisioning process.
@@ -300,6 +304,7 @@ spec:
type: object
required:
- endpoint
+ - provider
type: object
status:
description: |-
diff --git a/cmd/main.go b/cmd/main.go
index 530804326..6fa3e3839 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -50,7 +50,6 @@ import (
evpncontroller "github.com/ironcore-dev/network-operator/internal/controller/evpn"
poolcontroller "github.com/ironcore-dev/network-operator/internal/controller/pool"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
- "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"
@@ -97,7 +96,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
@@ -123,7 +121,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.")
@@ -259,13 +256,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 == "" {
@@ -313,7 +303,6 @@ func main() { //nolint:gocyclo
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")
@@ -325,7 +314,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 {
@@ -338,7 +326,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")
@@ -350,7 +337,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")
@@ -362,7 +348,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")
@@ -374,7 +359,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")
@@ -386,7 +370,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")
@@ -398,7 +381,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")
@@ -410,7 +392,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")
@@ -422,7 +403,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")
@@ -434,7 +414,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")
@@ -446,7 +425,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")
@@ -458,7 +436,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")
@@ -470,7 +447,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 {
@@ -483,7 +459,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 {
@@ -496,7 +471,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 {
@@ -509,7 +483,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 {
@@ -522,7 +495,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 {
@@ -535,7 +507,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")
@@ -547,7 +518,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 {
@@ -560,7 +530,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 {
@@ -573,7 +542,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")
@@ -585,7 +553,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")
@@ -597,7 +564,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")
@@ -609,7 +575,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")
@@ -621,7 +586,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")
@@ -633,7 +597,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")
@@ -645,7 +608,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 {
@@ -658,7 +620,6 @@ func main() { //nolint:gocyclo
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorder("configbackup-controller"),
WatchFilterValue: watchFilterValue,
- Provider: prov,
Locker: locker,
}).SetupWithManager(ctx, mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ConfigBackup")
@@ -670,7 +631,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 {
@@ -794,14 +754,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..2d10778c2 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_devices.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_devices.yaml
@@ -191,6 +191,10 @@ spec:
description: Paused can be used to prevent controllers from processing
the Device and its associated objects.
type: boolean
+ provider:
+ description: Provider identifies the network provider plugin used
+ to reconcile this Device and all CRDs that reference it.
+ type: string
provisioning:
description: |-
Provisioning is an optional configuration for the device provisioning process.
@@ -297,6 +301,7 @@ spec:
type: object
required:
- endpoint
+ - provider
type: object
status:
description: |-
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 af84cc064..23b5ef7d4 100644
--- a/docs/api-reference/index.md
+++ b/docs/api-reference/index.md
@@ -1786,6 +1786,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 identifies the network provider plugin used to reconcile this Device and all CRDs that reference it. | | Required: \{\}
|
| `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 d6a8ec9c5..3fd3b21be 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 ff04a99a6..553267d5b 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 860a82909..801d7469f 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..6bfec44f5 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,24 +81,32 @@ func (r *AAAReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.AAAProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.AAAProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.AAAProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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..1e57a416b 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,24 +83,32 @@ func (r *AccessControlListReconciler) Reconcile(ctx context.Context, req ctrl.Re
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.ACLProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.ACLProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.AccessControlListProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 45718d5af..5e61ca42d 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..37d31ea39 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,24 +87,32 @@ func (r *BannerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.BannerProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.BannerProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.BannerProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 411ccf2d4..3513a43d8 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..066b2be72 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,24 +96,32 @@ func (r *BGPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.BGPProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.BGPProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.BGPProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 2a70a6c2e..279751e35 100644
--- a/internal/controller/core/bgp_controller_test.go
+++ b/internal/controller/core/bgp_controller_test.go
@@ -30,6 +30,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..0671024cb 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,24 +100,32 @@ func (r *BGPPeerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.BGPPeerProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.BGPPeerProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.BGPPeerProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 767ad455e..691a427e6 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..276e10a70 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,24 +86,32 @@ func (r *CertificateReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.CertificateProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.CertificateProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.CertificateProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 501f8f0e5..35d9a3c1a 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/configbackup_controller.go b/internal/controller/core/configbackup_controller.go
index 74c9ba653..b3ccb897f 100644
--- a/internal/controller/core/configbackup_controller.go
+++ b/internal/controller/core/configbackup_controller.go
@@ -51,9 +51,6 @@ type ConfigBackupReconciler 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 config backup.
- Provider provider.ProviderFunc
-
// Locker is used to synchronize operations on resources targeting the same device.
Locker *resourcelock.ResourceLocker
}
@@ -80,24 +77,32 @@ func (r *ConfigBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.ConfigBackupProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.ConfigBackupProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.ConfigBackupProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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/configbackup_controller_test.go b/internal/controller/core/configbackup_controller_test.go
index a39ff0275..ec0e2be46 100644
--- a/internal/controller/core/configbackup_controller_test.go
+++ b/internal/controller/core/configbackup_controller_test.go
@@ -32,6 +32,7 @@ var _ = Describe("ConfigBackup Controller", func() {
},
Spec: v1alpha1.DeviceSpec{
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..2617baae4 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.
@@ -128,8 +128,7 @@ func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c
obj.Status.Phase = v1alpha1.DevicePhaseRunning
return ctrl.Result{}, nil
}
-
- if _, ok := r.Provider().(provider.ProvisioningProvider); !ok {
+ if _, err := provider.LoadProvider[provider.ProvisioningProvider](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 +192,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.LoadProvider[provider.ProvisioningProvider](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 +206,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 +387,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 +446,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 bb4e5238a..375513a7e 100644
--- a/internal/controller/core/device_controller_test.go
+++ b/internal/controller/core/device_controller_test.go
@@ -70,6 +70,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -158,6 +159,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
Provisioning: &v1alpha1.Provisioning{
Image: v1alpha1.Image{
URL: "http://example.com/nxos.bin",
@@ -202,6 +204,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
Provisioning: &v1alpha1.Provisioning{
Image: v1alpha1.Image{
URL: "http://example.com/nxos.bin",
@@ -259,6 +262,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
Provisioning: &v1alpha1.Provisioning{
Image: v1alpha1.Image{
URL: "http://example.com/nxos.bin",
@@ -319,6 +323,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -347,6 +352,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -402,6 +408,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
Provisioning: &v1alpha1.Provisioning{
BootScript: v1alpha1.TemplateSource{
Inline: new("boot nxos.bin"),
@@ -489,6 +496,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -537,6 +545,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
Provisioning: &v1alpha1.Provisioning{
Image: v1alpha1.Image{
URL: "http://example.com/nxos.bin",
@@ -590,6 +599,7 @@ var _ = Describe("Device Controller", func() {
Name: name,
},
},
+ Provider: "test-provider",
Provisioning: &v1alpha1.Provisioning{
Image: v1alpha1.Image{
URL: "http://example.com/nxos.bin",
@@ -656,6 +666,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..f4429bb1b 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,24 +89,32 @@ func (r *DHCPRelayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.DHCPRelayProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.DHCPRelayProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.DHCPRelayProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 e11406d5c..9f7649b67 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())
@@ -383,6 +384,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())
@@ -469,6 +471,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())
@@ -485,6 +488,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())
@@ -627,6 +631,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())
@@ -643,6 +648,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())
@@ -811,6 +817,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())
@@ -986,6 +993,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..e7d53fd82 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,24 +83,32 @@ func (r *DNSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.DNSProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.DNSProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.DNSProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 4b5c86c94..fac4535bb 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..f74bdaa9b 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,24 +87,32 @@ func (r *EthernetSegmentReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.EthernetSegmentProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.EthernetSegmentProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.EthernetSegmentProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 bff49709e..4d948b941 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..3c37a8c84 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,24 +85,32 @@ func (r *EVPNInstanceReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.EVPNInstanceProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.EVPNInstanceProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.EVPNInstanceProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 1165bc63d..381445a01 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..9ad0e66fd 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
@@ -98,24 +95,32 @@ func (r *InterfaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.InterfaceProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.InterfaceProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.InterfaceProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 ab497fb8a..117e115e3 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())
@@ -1282,6 +1283,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())
@@ -1295,6 +1297,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..5dc391e17 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,24 +84,32 @@ func (r *ISISReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.ISISProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.ISISProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.ISISProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 f6d635ac1..6bacae7ed 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())
@@ -136,6 +137,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..446f4e366 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,24 +84,32 @@ 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.LLDPProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
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: reason,
+ Message: err.Error(),
}) {
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 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/lldp_controller_test.go b/internal/controller/core/lldp_controller_test.go
index 8b17a1c8e..a0a4a8966 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())
@@ -328,6 +329,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())
@@ -452,6 +454,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())
@@ -599,6 +602,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())
@@ -682,6 +686,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())
@@ -833,6 +838,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())
@@ -1074,6 +1080,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())
@@ -1263,6 +1270,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..d29fe2a36 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,24 +83,32 @@ func (r *ManagementAccessReconciler) Reconcile(ctx context.Context, req ctrl.Req
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.ManagementAccessProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.ManagementAccessProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.ManagementAccessProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 d5b10b730..224759d1f 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..8f3dfcc3b 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,24 +83,32 @@ func (r *NTPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.NTPProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.NTPProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.NTPProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 972e9c75b..6eb7e3d5a 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..7b4d97e35 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,24 +84,32 @@ 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.NVEProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
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: reason,
+ Message: err.Error(),
}) {
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 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/nve_controller_test.go b/internal/controller/core/nve_controller_test.go
index 7accaf843..3e6d920e0 100644
--- a/internal/controller/core/nve_controller_test.go
+++ b/internal/controller/core/nve_controller_test.go
@@ -37,6 +37,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())
@@ -325,6 +327,7 @@ var _ = Describe("NVE Controller", func() {
},
Spec: v1alpha1.DeviceSpec{
Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -399,6 +402,7 @@ var _ = Describe("NVE Controller", func() {
},
Spec: v1alpha1.DeviceSpec{
Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -494,6 +498,7 @@ var _ = Describe("NVE Controller", func() {
},
Spec: v1alpha1.DeviceSpec{
Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -603,6 +608,7 @@ var _ = Describe("NVE Controller", func() {
},
Spec: v1alpha1.DeviceSpec{
Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -703,6 +709,7 @@ var _ = Describe("NVE Controller", func() {
},
Spec: v1alpha1.DeviceSpec{
Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device)).To(Succeed())
@@ -717,6 +724,7 @@ var _ = Describe("NVE Controller", func() {
},
Spec: v1alpha1.DeviceSpec{
Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr},
+ Provider: "test-provider",
},
}
Expect(k8sClient.Create(ctx, device2)).To(Succeed())
@@ -823,6 +831,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..5beee572b 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,24 +90,32 @@ func (r *OSPFReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.OSPFProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.OSPFProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.OSPFProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 e7c7d0a9c..01c0a808b 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())
@@ -135,6 +136,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..cc01596ba 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,24 +84,32 @@ func (r *PIMReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.PIMProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.PIMProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.PIMProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 427e58d41..e4b8c7afa 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())
@@ -129,6 +130,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..7b020087d 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,24 +83,32 @@ func (r *PrefixSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.PrefixSetProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.PrefixSetProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.PrefixSetProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 540a886ff..e132c59f6 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..0b190ec72 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,24 +83,32 @@ func (r *RoutingPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Reques
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.RoutingPolicyProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.RoutingPolicyProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.RoutingPolicyProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 d9579f6da..43b6349df 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..05ea6599b 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,24 +83,32 @@ func (r *SNMPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.SNMPProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.SNMPProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.SNMPProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 145dfee47..c57ad388c 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 051a89864..e9ac6eebf 100644
--- a/internal/controller/core/suite_test.go
+++ b/internal/controller/core/suite_test.go
@@ -117,13 +117,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())
@@ -132,7 +131,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
@@ -142,7 +140,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -151,7 +148,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -160,7 +156,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -169,7 +164,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -178,7 +172,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -187,7 +180,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -196,7 +188,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -205,7 +196,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -214,7 +204,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -223,7 +212,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -232,7 +220,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -241,7 +228,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -250,7 +236,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
@@ -260,7 +245,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
@@ -270,7 +254,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
@@ -280,7 +263,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
@@ -290,7 +272,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -299,7 +280,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
@@ -309,7 +289,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -318,7 +297,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -327,7 +305,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
@@ -337,7 +314,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
@@ -347,7 +323,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
}).SetupWithManager(ctx, k8sManager)
Expect(err).NotTo(HaveOccurred())
@@ -356,7 +331,6 @@ var _ = BeforeSuite(func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: recorder,
- Provider: prov,
Locker: testLocker,
RequeueInterval: time.Second,
}).SetupWithManager(ctx, k8sManager)
diff --git a/internal/controller/core/syslog_controller.go b/internal/controller/core/syslog_controller.go
index 12d8dd364..06a37c161 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,24 +83,32 @@ func (r *SyslogReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.SyslogProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.SyslogProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.SyslogProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 8e477ca06..37d40d6f6 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..6b5038e79 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,24 +86,32 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.UserProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.UserProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.UserProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 94cf845e6..f433d9cde 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..9a37a00f6 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,24 +87,32 @@ func (r *VLANReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.VLANProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.VLANProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.VLANProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 edc1b8cd5..e9fb2b95f 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..1a2592559 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,24 +85,32 @@ func (r *VRFReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- prov, ok := r.Provider().(provider.VRFProvider)
- 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)
+ }
+
+ prov, err := provider.LoadProvider[provider.VRFProvider](device.Spec.Provider)
+ if err != nil {
+ reason := v1alpha1.NotImplementedReason
+ if errors.Is(err, provider.NotFoundError{}) {
+ reason = v1alpha1.ProviderNotFoundReason
+ }
if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: v1alpha1.ReadyCondition,
Status: metav1.ConditionFalse,
- Reason: v1alpha1.NotImplementedReason,
- Message: "Provider does not implement provider.VRFProvider",
+ Reason: reason,
+ Message: err.Error(),
}) {
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 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 e9d76ff51..3b6c3e4a8 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 39d9aa68a..0d8d246b4 100644
--- a/internal/provider/cisco/iosxr/provider.go
+++ b/internal/provider/cisco/iosxr/provider.go
@@ -612,5 +612,5 @@ func (p *Provider) DeletePrefixSet(ctx context.Context, req *provider.PrefixSetR
}
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 83a54ecd3..02bbf55f0 100644
--- a/internal/provider/cisco/nxos/provider.go
+++ b/internal/provider/cisco/nxos/provider.go
@@ -4016,5 +4016,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 1ad8a2f18..f81847804 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -51,6 +51,8 @@ type MaintenanceProvider interface {
// ProvisioningProvider is the interface for the realization of the provisioning-related operations over different providers.
type ProvisioningProvider interface {
+ Provider
+
// Reprovision prepares the device for reprovisioning by resetting it and reenabling provisioning mechanisms.
Reprovision(context.Context, *deviceutil.Connection) error
// HashProvisioningPassword takes a plaintext password and returns the hashed password along with the hash type.
@@ -899,3 +901,45 @@ 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 {
+ Message string
+}
+
+type NotImplementedError struct {
+ Message string
+}
+
+func (e NotImplementedError) Error() string {
+ return e.Message
+}
+
+func (e NotFoundError) Error() string {
+ return e.Message
+}
+
+func (e NotFoundError) Is(target error) bool {
+ _, ok := target.(NotFoundError)
+ return ok
+}
+
+func (e NotImplementedError) Is(target error) bool {
+ _, ok := target.(NotImplementedError)
+ return ok
+}
+
+// LoadProvider returns a provider instance cast to the requested interface type T.
+// Returns NotFoundError if the provider is not registered, NotImplementedError if it does not implement T.
+func LoadProvider[T Provider](providerName string) (T, error) {
+ prov, err := Get(providerName)
+ if err != nil {
+ var zero T
+ return zero, NotFoundError{Message: fmt.Sprintf("Provider %q is not registered", providerName)}
+ }
+ genericProvider, ok := prov().(T)
+ if !ok {
+ var zero T
+ return zero, NotImplementedError{Message: fmt.Sprintf("provider %q does not implement %T", providerName, zero)}
+ }
+ return genericProvider, nil
+}
diff --git a/internal/provisioning/http.go b/internal/provisioning/http.go
index a414297b2..318fa66f8 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.LoadProvider[provider.ProvisioningProvider](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{} })
}