From 418a751d0bee4220b21a12963cb73ff2c9edfa17 Mon Sep 17 00:00:00 2001 From: Sven Rosenzweig Date: Wed, 29 Jul 2026 12:24:09 +0200 Subject: [PATCH 1/2] Use FQDN as provider names Replace 'openconfig' with 'openconfig.networking.metal.ironcore.dev'. Signed-off-by: Sven Rosenzweig --- internal/provider/cisco/iosxr/provider.go | 2 +- internal/provider/cisco/nxos/provider.go | 2 +- internal/provider/openconfig/provider.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/provider/cisco/iosxr/provider.go b/internal/provider/cisco/iosxr/provider.go index 401237142..7a7b733d5 100644 --- a/internal/provider/cisco/iosxr/provider.go +++ b/internal/provider/cisco/iosxr/provider.go @@ -616,5 +616,5 @@ func (p *Provider) LoopbackInterfaceName(id int) (string, error) { } 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 697eec851..8e1a49a42 100644 --- a/internal/provider/cisco/nxos/provider.go +++ b/internal/provider/cisco/nxos/provider.go @@ -4334,5 +4334,5 @@ func NormalizeMACAddress(mac string) string { } 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 bc9236a15..8451ebfb4 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) } From 47109bd190c54536af28ccc095888c0636f2e8d2 Mon Sep 17 00:00:00 2001 From: Sven Rosenzweig Date: Wed, 29 Jul 2026 12:25:24 +0200 Subject: [PATCH 2/2] Migrate to Multi Provider Support With this commit, we do not use the provider flag anymore. The provider func now is loaded dynamically on every reconciliation. Requires the `device.spec.provider` field to be set on every device. Signed-off-by: Sven Rosenzweig --- Tiltfile | 3 +- api/core/v1alpha1/device_types.go | 10 +- api/core/v1alpha1/groupversion_info.go | 3 + ...devices.networking.metal.ironcore.dev.yaml | 10 +- cmd/main.go | 46 +--------- ...networking.metal.ironcore.dev_devices.yaml | 10 +- config/develop/manager_patch.yaml | 1 - docs/api-reference/index.md | 2 +- .../cisco/nx/bordergateway_controller.go | 25 ++--- .../cisco/nx/bordergateway_controller_test.go | 1 + internal/controller/cisco/nx/suite_test.go | 6 +- .../controller/cisco/nx/system_controller.go | 25 ++--- .../cisco/nx/system_controller_test.go | 1 + .../cisco/nx/vpcdomain_controller.go | 31 ++++--- .../cisco/nx/vpcdomain_controller_test.go | 5 +- internal/controller/core/aaa_controller.go | 25 ++--- internal/controller/core/acl_controller.go | 25 ++--- .../controller/core/acl_controller_test.go | 1 + internal/controller/core/banner_controller.go | 25 ++--- .../controller/core/banner_controller_test.go | 1 + internal/controller/core/bgp_controller.go | 25 ++--- .../controller/core/bgp_controller_test.go | 1 + .../controller/core/bgp_peer_controller.go | 25 ++--- .../core/bgp_peer_controller_test.go | 1 + .../controller/core/certificate_controller.go | 25 ++--- .../core/certificate_controller_test.go | 1 + .../core/configbackup_controller.go | 25 ++--- .../core/configbackup_controller_test.go | 1 + internal/controller/core/device_controller.go | 25 +++-- .../controller/core/device_controller_test.go | 92 +++++++++++++++++++ .../controller/core/dhcprelay_controller.go | 25 ++--- .../core/dhcprelay_controller_test.go | 8 ++ internal/controller/core/dns_controller.go | 25 ++--- .../controller/core/dns_controller_test.go | 1 + .../core/ethernetsegment_controller.go | 25 ++--- .../core/ethernetsegment_controller_test.go | 1 + .../core/evpninstance_controller.go | 25 ++--- .../core/evpninstance_controller_test.go | 1 + .../controller/core/interface_controller.go | 25 ++--- .../core/interface_controller_test.go | 3 + internal/controller/core/isis_controller.go | 25 ++--- .../controller/core/isis_controller_test.go | 2 + internal/controller/core/lldp_controller.go | 25 ++--- .../controller/core/lldp_controller_test.go | 8 ++ .../core/managementaccess_controller.go | 25 ++--- .../core/managementaccess_controller_test.go | 1 + internal/controller/core/ntp_controller.go | 25 ++--- .../controller/core/ntp_controller_test.go | 1 + internal/controller/core/nve_controller.go | 25 ++--- .../controller/core/nve_controller_test.go | 9 ++ internal/controller/core/ospf_controller.go | 25 ++--- .../controller/core/ospf_controller_test.go | 4 +- internal/controller/core/pim_controller.go | 25 ++--- .../controller/core/pim_controller_test.go | 2 + .../controller/core/prefixset_controller.go | 25 ++--- .../core/prefixset_controller_test.go | 1 + internal/controller/core/probe_controller.go | 25 ++--- .../controller/core/probe_controller_test.go | 1 + .../core/routingpolicy_controller.go | 25 ++--- .../core/routingpolicy_controller_test.go | 1 + internal/controller/core/snmp_controller.go | 25 ++--- .../controller/core/snmp_controller_test.go | 1 + internal/controller/core/suite_test.go | 29 +----- internal/controller/core/syslog_controller.go | 25 ++--- .../controller/core/syslog_controller_test.go | 1 + internal/controller/core/user_controller.go | 25 ++--- .../controller/core/user_controller_test.go | 1 + internal/controller/core/vlan_controller.go | 25 ++--- .../controller/core/vlan_controller_test.go | 1 + internal/controller/core/vrf_controller.go | 25 ++--- .../controller/core/vrf_controller_test.go | 1 + internal/controller/evpn/fabric_controller.go | 35 +++---- .../controller/evpn/fabric_controller_test.go | 16 ++-- internal/controller/evpn/suite_test.go | 3 +- internal/provider/provider.go | 38 ++++++++ internal/provisioning/http.go | 10 +- internal/provisioning/http_test.go | 18 +++- test/gnmi/gnmi_suite_test.go | 40 +------- test/gnmi/gnmi_test.go | 1 + .../aaa.txtar | 0 .../acl.txtar | 0 .../banner.txtar | 0 .../bgp_peer.txtar | 0 .../dhcprelay.txtar | 0 .../dns.txtar | 0 .../ethernetsegment.txtar.skip | 0 .../evpninstance.txtar | 0 .../interface_aggregate_l2_trunk.txtar | 0 .../interface_aggregate_l3.txtar | 0 .../interface_aggregate_vpc_stp_lacp.txtar | 0 .../interface_loopback.txtar | 0 .../interface_loopback_multi_addr.txtar | 0 .../interface_physical_ipv4.txtar | 0 ...interface_physical_switchport_access.txtar | 0 .../interface_physical_switchport_trunk.txtar | 0 .../interface_physical_unnumbered.txtar | 0 .../interface_routed_vlan.txtar | 0 .../isis.txtar | 0 .../lldp.txtar | 0 .../managementaccess.txtar | 0 .../ntp.txtar | 0 .../nve.txtar | 0 .../ospf.txtar | 0 .../pim.txtar | 0 .../routingpolicy.txtar | 0 .../snmp.txtar | 0 .../syslog.txtar | 0 .../system.txtar | 0 .../user.txtar | 0 .../vlan.txtar | 0 .../vpcdomain.txtar | 0 .../vrf.txtar | 0 .../aaa.txtar | 0 .../acl.txtar | 0 .../banner.txtar | 0 .../bgp.txtar | 0 .../bgppeer.txtar | 0 .../dns.txtar | 0 .../interface.txtar | 0 .../interface_aggregate_l2_trunk.txtar | 0 .../interface_aggregate_l3.txtar | 0 .../interface_loopback_multi_addr.txtar | 0 .../interface_physical_ipv4.txtar | 0 ...interface_physical_switchport_access.txtar | 0 .../interface_physical_switchport_trunk.txtar | 0 .../interface_physical_unnumbered.txtar | 0 .../interface_routed_vlan.txtar.skip | 0 .../isis.txtar | 0 .../lldp.txtar | 0 .../managementaccess.txtar | 0 .../prefixset.txtar | 0 .../routingpolicy.txtar | 0 .../syslog.txtar.skip | 0 .../user.txtar | 0 .../vrf.txtar | 0 135 files changed, 687 insertions(+), 529 deletions(-) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/aaa.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/acl.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/banner.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/bgp_peer.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/dhcprelay.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/dns.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/ethernetsegment.txtar.skip (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/evpninstance.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_aggregate_l2_trunk.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_aggregate_l3.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_aggregate_vpc_stp_lacp.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_loopback.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_loopback_multi_addr.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_physical_ipv4.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_physical_switchport_access.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_physical_switchport_trunk.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_physical_unnumbered.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/interface_routed_vlan.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/isis.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/lldp.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/managementaccess.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/ntp.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/nve.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/ospf.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/pim.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/routingpolicy.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/snmp.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/syslog.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/system.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/user.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/vlan.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/vpcdomain.txtar (100%) rename test/gnmi/testdata/{cisco-nxos-gnmi => nx.cisco.networking.metal.ironcore.dev}/vrf.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/aaa.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/acl.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/banner.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/bgp.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/bgppeer.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/dns.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface_aggregate_l2_trunk.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface_aggregate_l3.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface_loopback_multi_addr.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface_physical_ipv4.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface_physical_switchport_access.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface_physical_switchport_trunk.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface_physical_unnumbered.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/interface_routed_vlan.txtar.skip (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/isis.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/lldp.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/managementaccess.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/prefixset.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/routingpolicy.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/syslog.txtar.skip (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/user.txtar (100%) rename test/gnmi/testdata/{openconfig => openconfig.networking.metal.ironcore.dev}/vrf.txtar (100%) diff --git a/Tiltfile b/Tiltfile index eff1be14e..ad9857573 100644 --- a/Tiltfile +++ b/Tiltfile @@ -44,7 +44,7 @@ local_resource('crds', 'make install', deps=['api/'], labels=['operator']) provider = os.getenv('PROVIDER', 'openconfig') manager = kustomize('config/develop') -manager = str(manager).replace('--provider=openconfig', '--provider={}'.format(provider)) +manager = str(manager) debug = os.getenv('DEBUG', '') == 'true' if debug: @@ -60,6 +60,7 @@ k8s_resource('rustfs-create-buckets', resource_deps=['rustfs']) # Sample resources with manual trigger mode def device_yaml(): decoded = read_yaml_stream('./config/samples/v1alpha1_device.yaml') + decoded[0]['spec']['provider'] = provider ip = str(local("docker run --rm busybox:1.37.0 nslookup -type=a host.docker.internal 2>/dev/null | grep 'Address:' | tail -n 1 | awk '{print $2}' || echo ''", quiet=True)).rstrip('\n') if len(ip) > 0: decoded[0]['spec']['endpoint']['address'] = ip+':9339' diff --git a/api/core/v1alpha1/device_types.go b/api/core/v1alpha1/device_types.go index dfad42c52..e8ae4d9d9 100644 --- a/api/core/v1alpha1/device_types.go +++ b/api/core/v1alpha1/device_types.go @@ -24,10 +24,12 @@ type DeviceSpec struct { // +required Endpoint Endpoint `json:"endpoint"` - // Provider is the name of the provider plugin which is responsible for reconciling the CRD connected to the device - // +optional - // +immutable - Provider string `json:"provider,omitempty"` + // Provider identifies the provider implementation used to reconcile this Device and all CRDs that reference it. + // Immutable. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Provider is immutable" + Provider string `json:"provider"` // 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. diff --git a/api/core/v1alpha1/groupversion_info.go b/api/core/v1alpha1/groupversion_info.go index 3b0fdc9af..230050b22 100644 --- a/api/core/v1alpha1/groupversion_info.go +++ b/api/core/v1alpha1/groupversion_info.go @@ -190,6 +190,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 25bd723b3..991fc7ceb 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 @@ -195,9 +195,14 @@ spec: the Device and its associated objects. type: boolean provider: - description: Provider is the name of the provider plugin which is - responsible for reconciling the CRD connected to the device + description: |- + Provider identifies the provider implementation used to reconcile this Device and all CRDs that reference it. + Immutable. + minLength: 1 type: string + x-kubernetes-validations: + - message: Provider is immutable + rule: self == oldSelf provisioning: description: |- Provisioning is an optional configuration for the device provisioning process. @@ -304,6 +309,7 @@ spec: type: object required: - endpoint + - provider type: object status: description: |- diff --git a/cmd/main.go b/cmd/main.go index 5b13fcac4..d121a5633 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 { @@ -799,14 +759,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) @@ -835,7 +793,6 @@ func main() { //nolint:gocyclo Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("fabric-controller"), - Provider: prov, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "Failed to create controller", "controller", "Fabric") os.Exit(1) @@ -846,7 +803,6 @@ func main() { //nolint:gocyclo Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorder("probe-controller"), WatchFilterValue: watchFilterValue, - Provider: prov, Locker: locker, }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Probe") diff --git a/config/crd/bases/networking.metal.ironcore.dev_devices.yaml b/config/crd/bases/networking.metal.ironcore.dev_devices.yaml index 0e0f21526..ec6edf74a 100644 --- a/config/crd/bases/networking.metal.ironcore.dev_devices.yaml +++ b/config/crd/bases/networking.metal.ironcore.dev_devices.yaml @@ -192,9 +192,14 @@ spec: the Device and its associated objects. type: boolean provider: - description: Provider is the name of the provider plugin which is - responsible for reconciling the CRD connected to the device + description: |- + Provider identifies the provider implementation used to reconcile this Device and all CRDs that reference it. + Immutable. + minLength: 1 type: string + x-kubernetes-validations: + - message: Provider is immutable + rule: self == oldSelf provisioning: description: |- Provisioning is an optional configuration for the device provisioning process. @@ -301,6 +306,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 9f18a099a..f1f8b170a 100644 --- a/config/develop/manager_patch.yaml +++ b/config/develop/manager_patch.yaml @@ -4,7 +4,6 @@ - --leader-elect=false - --health-probe-bind-address=:8081 - --metrics-bind-address=:8443 - - --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 e7664c675..f55f0b4e8 100644 --- a/docs/api-reference/index.md +++ b/docs/api-reference/index.md @@ -1846,7 +1846,7 @@ _Appears in:_ | --- | --- | --- | --- | | `paused` _boolean_ | Paused can be used to prevent controllers from processing the Device and its associated objects. | false | Optional: \{\}
| | `endpoint` _[Endpoint](#endpoint)_ | Endpoint contains the connection information for the device. | | Required: \{\}
| -| `provider` _string_ | Provider is the name of the provider plugin which is responsible for reconciling the CRD connected to the device | | Optional: \{\}
| +| `provider` _string_ | Provider identifies the provider implementation used to reconcile this Device and all CRDs that reference it.
Immutable. | | MinLength: 1
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 d3ae701d6..f35c39dd5 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,24 +85,28 @@ 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 + } + + prov, err := provider.LoadProvider[Provider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + reason = v1alpha1.ProviderNotFoundReason + } 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: 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/cisco/nx/bordergateway_controller_test.go b/internal/controller/cisco/nx/bordergateway_controller_test.go index d72cb015d..b2df80e5f 100644 --- a/internal/controller/cisco/nx/bordergateway_controller_test.go +++ b/internal/controller/cisco/nx/bordergateway_controller_test.go @@ -29,6 +29,7 @@ var _ = Describe("BorderGateway Controller", func() { GenerateName: "test-bgw-", 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 89f8dbf7a..a3caa0eb1 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 752759241..1a7be4435 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,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[Provider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + reason = v1alpha1.ProviderNotFoundReason + } 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: 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/cisco/nx/system_controller_test.go b/internal/controller/cisco/nx/system_controller_test.go index ebd3670ab..ddb5dc1e6 100644 --- a/internal/controller/cisco/nx/system_controller_test.go +++ b/internal/controller/cisco/nx/system_controller_test.go @@ -27,6 +27,7 @@ var _ = Describe("System Controller", func() { GenerateName: "test-system-", 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 de1978c7a..cd635dfde 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,22 +86,28 @@ 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{ - Type: v1alpha1.ReadyCondition, - Status: metav1.ConditionFalse, - Reason: v1alpha1.NotImplementedReason, - Message: "Invalid provider configured for VPCDomain reconciler", - }) - return ctrl.Result{}, r.Status().Update(ctx, obj) - } - device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) if err != nil { return ctrl.Result{}, err } + prov, err := provider.LoadProvider[Provider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + reason = v1alpha1.ProviderNotFoundReason + } + if meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: reason, + Message: err.Error(), + }) { + return ctrl.Result{}, r.Status().Update(ctx, obj) + } + return ctrl.Result{}, nil + } + orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/cisco/nx/vpcdomain_controller_test.go b/internal/controller/cisco/nx/vpcdomain_controller_test.go index f4a1abd5d..682ea1a38 100644 --- a/internal/controller/cisco/nx/vpcdomain_controller_test.go +++ b/internal/controller/cisco/nx/vpcdomain_controller_test.go @@ -29,6 +29,7 @@ var _ = Describe("VPCDomain Controller", func() { GenerateName: "test-vpcdomain-", Namespace: metav1.NamespaceDefault, Spec: corev1.DeviceSpec{ + Provider: "test-provider", Endpoint: corev1.Endpoint{ Address: "192.168.10.2:9339", }, @@ -204,7 +205,7 @@ var _ = Describe("VPCDomain Controller", func() { deviceA := &corev1.Device{ 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 @@ -212,7 +213,7 @@ var _ = Describe("VPCDomain Controller", func() { By("Creating Device B") Expect(k8sClient.Create(ctx, &corev1.Device{ 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 058558740..e6ffde029 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,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.AAAProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/acl_controller.go b/internal/controller/core/acl_controller.go index 265cd6043..998f22e18 100644 --- a/internal/controller/core/acl_controller.go +++ b/internal/controller/core/acl_controller.go @@ -48,9 +48,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 } @@ -85,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.ACLProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/acl_controller_test.go b/internal/controller/core/acl_controller_test.go index bd2294365..9175dd60f 100644 --- a/internal/controller/core/acl_controller_test.go +++ b/internal/controller/core/acl_controller_test.go @@ -31,6 +31,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 06932a3fc..961c033ca 100644 --- a/internal/controller/core/banner_controller.go +++ b/internal/controller/core/banner_controller.go @@ -50,9 +50,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 } @@ -89,24 +86,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.BannerProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/banner_controller_test.go b/internal/controller/core/banner_controller_test.go index 22cdfc9a2..21dcaa565 100644 --- a/internal/controller/core/banner_controller_test.go +++ b/internal/controller/core/banner_controller_test.go @@ -29,6 +29,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 f906e03d7..e182028c7 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,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.BGPProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/bgp_controller_test.go b/internal/controller/core/bgp_controller_test.go index 8f7238b68..89ce7d1fc 100644 --- a/internal/controller/core/bgp_controller_test.go +++ b/internal/controller/core/bgp_controller_test.go @@ -28,6 +28,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 ea120e93f..62ad23c99 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,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.BGPPeerProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/bgp_peer_controller_test.go b/internal/controller/core/bgp_peer_controller_test.go index ee9fa6b0d..fa89c92b0 100644 --- a/internal/controller/core/bgp_peer_controller_test.go +++ b/internal/controller/core/bgp_peer_controller_test.go @@ -29,6 +29,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 4989c8a16..ab63898a9 100644 --- a/internal/controller/core/certificate_controller.go +++ b/internal/controller/core/certificate_controller.go @@ -50,9 +50,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 } @@ -88,24 +85,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.CertificateProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/certificate_controller_test.go b/internal/controller/core/certificate_controller_test.go index 33a2ccdc7..56c0d6859 100644 --- a/internal/controller/core/certificate_controller_test.go +++ b/internal/controller/core/certificate_controller_test.go @@ -40,6 +40,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 891800b10..1c043972c 100644 --- a/internal/controller/core/configbackup_controller.go +++ b/internal/controller/core/configbackup_controller.go @@ -57,9 +57,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 @@ -98,24 +95,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.ConfigBackupProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/configbackup_controller_test.go b/internal/controller/core/configbackup_controller_test.go index 0b8df0f71..961413bef 100644 --- a/internal/controller/core/configbackup_controller_test.go +++ b/internal/controller/core/configbackup_controller_test.go @@ -37,6 +37,7 @@ var _ = Describe("ConfigBackup Controller", func() { Namespace: metav1.NamespaceDefault, 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 7da0566b7..5374193e7 100644 --- a/internal/controller/core/device_controller.go +++ b/internal/controller/core/device_controller.go @@ -49,9 +49,6 @@ type DeviceReconciler 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 - // HeartbeatInterval is the duration after which the controller requeues the reconciliation, // regardless of changes. HeartbeatInterval time.Duration @@ -128,8 +125,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 @@ -211,7 +207,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 } @@ -221,7 +221,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 @@ -407,7 +407,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, @@ -470,7 +473,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 4a94dd5bd..2febef267 100644 --- a/internal/controller/core/device_controller_test.go +++ b/internal/controller/core/device_controller_test.go @@ -69,6 +69,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -153,6 +154,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -195,6 +197,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -250,6 +253,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -308,6 +312,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -334,6 +339,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -375,6 +381,85 @@ var _ = Describe("Device Controller", func() { }).Should(Succeed()) }) + It("Should transition from Running to Provisioning once the reset-phase annotation is set", func() { + By("Creating a Device") + device := &v1alpha1.Device{ + Name: name, + Namespace: metav1.NamespaceDefault, + Spec: v1alpha1.DeviceSpec{ + Endpoint: v1alpha1.Endpoint{ + Address: "192.168.10.5:9339", + SecretRef: &v1alpha1.SecretReference{ + Name: name, + }, + }, + Provider: "test-provider", + Provisioning: &v1alpha1.Provisioning{ + BootScript: v1alpha1.TemplateSource{ + Inline: new("boot nxos.bin"), + }, + Image: v1alpha1.Image{ + URL: "https://best-vendor-images.to/windows98", + Checksum: "d41d8cd98f00b204e9800998ecf8427e", + ChecksumType: v1alpha1.ChecksumTypeMD5, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, device)).To(Succeed()) + + By("Verifying the device transitions to Provisioning phase") + Eventually(func(g Gomega) { + resource := &v1alpha1.Device{} + g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed()) + g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseProvisioning)) + g.Expect(resource.Status.Conditions).To(HaveLen(3)) + g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition)) + g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition)) + g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse)) + g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.ReachableCondition)) + g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown)) + }).Should(Succeed()) + + By("Setting the device to Running phase") + orig := device.DeepCopy() + device.Status.Phase = v1alpha1.DevicePhaseRunning + Expect(k8sClient.Status().Patch(ctx, device, client.MergeFrom(orig))).To(Succeed()) + + By("Verifying the device transitions to Running phase") + Eventually(func(g Gomega) { + resource := &v1alpha1.Device{} + g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed()) + g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseRunning)) + g.Expect(resource.Status.Conditions).To(HaveLen(3)) + g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition)) + g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition)) + g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse)) + g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.ReachableCondition)) + g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue)) + }).Should(Succeed()) + + By("Adding the reset-phase annotation to the device") + Eventually(func(g Gomega) { + resource := &v1alpha1.Device{} + g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed()) + patch := resource.DeepCopy() + annotations := make(map[string]string) + annotations[v1alpha1.DeviceMaintenanceAnnotation] = v1alpha1.DeviceMaintenanceResetPhase + patch.SetAnnotations(annotations) + g.Expect(k8sClient.Patch(ctx, patch, client.MergeFrom(resource))).To(Succeed()) + }).Should(Succeed()) + + By("Verifying the device transitions to Provisioning phase and the annotation is removed") + Eventually(func(g Gomega) { + resource := &v1alpha1.Device{} + g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed()) + g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseProvisioning)) + _, exists := resource.Annotations[v1alpha1.DeviceMaintenanceAnnotation] + g.Expect(exists).To(BeFalse(), "Maintenance annotation should be removed after processing") + }).Should(Succeed()) + }) + It("Should set Reachable=False and Ready=Unknown when the device is unreachable", func() { By("Making the provider return a connect error") testProvider.SetConnectError(errors.New("connection refused")) @@ -394,6 +479,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -440,6 +526,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -491,6 +578,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", Provisioning: &v1alpha1.Provisioning{ Image: v1alpha1.Image{ URL: "http://example.com/nxos.bin", @@ -555,6 +643,7 @@ var _ = Describe("Device Controller", func() { Name: name, }, }, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -587,6 +676,7 @@ var _ = Describe("Device Controller", func() { Name: key.Name, Namespace: key.Namespace, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.5:9339", SecretRef: &v1alpha1.SecretReference{ @@ -668,6 +758,7 @@ var _ = Describe("Device Controller", func() { v1alpha1.DeviceMaintenanceAnnotation: v1alpha1.DeviceMaintenanceSkipProvisioning, }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.6:9339", SecretRef: &v1alpha1.SecretReference{ @@ -707,6 +798,7 @@ var _ = Describe("Device Controller", func() { GenerateName: name, Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.7:9339", SecretRef: &v1alpha1.SecretReference{ diff --git a/internal/controller/core/dhcprelay_controller.go b/internal/controller/core/dhcprelay_controller.go index 0f6accb68..a1ccc736b 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,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.DHCPRelayProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/dhcprelay_controller_test.go b/internal/controller/core/dhcprelay_controller_test.go index ac066a433..f8fb5db75 100644 --- a/internal/controller/core/dhcprelay_controller_test.go +++ b/internal/controller/core/dhcprelay_controller_test.go @@ -44,6 +44,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()) @@ -365,6 +366,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()) @@ -447,6 +449,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()) @@ -461,6 +464,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()) @@ -595,6 +599,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()) @@ -609,6 +614,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()) @@ -767,6 +773,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()) @@ -934,6 +941,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 89487b5c4..55f38fb6c 100644 --- a/internal/controller/core/dns_controller.go +++ b/internal/controller/core/dns_controller.go @@ -48,9 +48,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 } @@ -85,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.DNSProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/dns_controller_test.go b/internal/controller/core/dns_controller_test.go index d1eb18aa2..7a91b7ce0 100644 --- a/internal/controller/core/dns_controller_test.go +++ b/internal/controller/core/dns_controller_test.go @@ -29,6 +29,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 c98c6da30..84f701640 100644 --- a/internal/controller/core/ethernetsegment_controller.go +++ b/internal/controller/core/ethernetsegment_controller.go @@ -48,9 +48,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 @@ -89,24 +86,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.EthernetSegmentProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/ethernetsegment_controller_test.go b/internal/controller/core/ethernetsegment_controller_test.go index ff3bdccaa..3733858f9 100644 --- a/internal/controller/core/ethernetsegment_controller_test.go +++ b/internal/controller/core/ethernetsegment_controller_test.go @@ -30,6 +30,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 1aaf48fa8..8048fb61a 100644 --- a/internal/controller/core/evpninstance_controller.go +++ b/internal/controller/core/evpninstance_controller.go @@ -48,9 +48,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 } @@ -87,24 +84,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.EVPNInstanceProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/evpninstance_controller_test.go b/internal/controller/core/evpninstance_controller_test.go index dee7b738e..4ad87d3ea 100644 --- a/internal/controller/core/evpninstance_controller_test.go +++ b/internal/controller/core/evpninstance_controller_test.go @@ -30,6 +30,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 a28604d25..8123f5f9b 100644 --- a/internal/controller/core/interface_controller.go +++ b/internal/controller/core/interface_controller.go @@ -53,9 +53,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 @@ -97,24 +94,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.InterfaceProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/interface_controller_test.go b/internal/controller/core/interface_controller_test.go index 3adad9e1a..bada28f9a 100644 --- a/internal/controller/core/interface_controller_test.go +++ b/internal/controller/core/interface_controller_test.go @@ -33,6 +33,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()) @@ -1209,6 +1210,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()) @@ -1220,6 +1222,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 6e1ca9328..7511d3c4c 100644 --- a/internal/controller/core/isis_controller.go +++ b/internal/controller/core/isis_controller.go @@ -49,9 +49,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 } @@ -86,24 +83,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.ISISProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/isis_controller_test.go b/internal/controller/core/isis_controller_test.go index 47a40abd6..3129a9874 100644 --- a/internal/controller/core/isis_controller_test.go +++ b/internal/controller/core/isis_controller_test.go @@ -29,6 +29,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()) @@ -130,6 +131,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 d12261e2e..5b17d63f1 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,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.LLDPProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/lldp_controller_test.go b/internal/controller/core/lldp_controller_test.go index aacc05a05..84794703f 100644 --- a/internal/controller/core/lldp_controller_test.go +++ b/internal/controller/core/lldp_controller_test.go @@ -35,6 +35,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()) @@ -312,6 +313,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()) @@ -432,6 +434,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()) @@ -571,6 +574,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()) @@ -650,6 +654,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()) @@ -789,6 +794,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()) @@ -1018,6 +1024,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()) @@ -1197,6 +1204,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 9d256ac5f..ea1ff107f 100644 --- a/internal/controller/core/managementaccess_controller.go +++ b/internal/controller/core/managementaccess_controller.go @@ -48,9 +48,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 } @@ -85,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.ManagementAccessProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/managementaccess_controller_test.go b/internal/controller/core/managementaccess_controller_test.go index e896b9693..04847bf1b 100644 --- a/internal/controller/core/managementaccess_controller_test.go +++ b/internal/controller/core/managementaccess_controller_test.go @@ -29,6 +29,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 da8aff7ee..62d6a787f 100644 --- a/internal/controller/core/ntp_controller.go +++ b/internal/controller/core/ntp_controller.go @@ -48,9 +48,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 } @@ -85,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.NTPProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/ntp_controller_test.go b/internal/controller/core/ntp_controller_test.go index 7e1f7ae71..4c7fa81e1 100644 --- a/internal/controller/core/ntp_controller_test.go +++ b/internal/controller/core/ntp_controller_test.go @@ -29,6 +29,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 699de485b..dd2c39024 100644 --- a/internal/controller/core/nve_controller.go +++ b/internal/controller/core/nve_controller.go @@ -50,9 +50,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 @@ -86,24 +83,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.NVEProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/nve_controller_test.go b/internal/controller/core/nve_controller_test.go index 45f1c547b..bafef299c 100644 --- a/internal/controller/core/nve_controller_test.go +++ b/internal/controller/core/nve_controller_test.go @@ -35,6 +35,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -202,6 +203,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -319,6 +321,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -391,6 +394,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -484,6 +488,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -591,6 +596,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -689,6 +695,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) @@ -701,6 +708,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: testEndpointAddr}, + Provider: "test-provider", }, } Expect(k8sClient.Create(ctx, device2)).To(Succeed()) @@ -805,6 +813,7 @@ var _ = Describe("NVE Controller", func() { Namespace: metav1.NamespaceDefault, 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 c73421985..13e8bb8da 100644 --- a/internal/controller/core/ospf_controller.go +++ b/internal/controller/core/ospf_controller.go @@ -51,9 +51,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 @@ -92,24 +89,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.OSPFProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/ospf_controller_test.go b/internal/controller/core/ospf_controller_test.go index 548d41b70..ca88bfd7f 100644 --- a/internal/controller/core/ospf_controller_test.go +++ b/internal/controller/core/ospf_controller_test.go @@ -33,6 +33,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()) @@ -126,7 +127,7 @@ var _ = Describe("OSPF Controller", func() { BeforeEach(func() { device := &v1alpha1.Device{ GenerateName: "test-ospf-l2-intf-", Namespace: metav1.NamespaceDefault, - Spec: v1alpha1.DeviceSpec{Endpoint: v1alpha1.Endpoint{Address: "192.168.10.4:9339"}}, + Spec: v1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: v1alpha1.Endpoint{Address: "192.168.10.4:9339"}}, } Expect(k8sClient.Create(ctx, device)).To(Succeed()) name = device.Name @@ -213,6 +214,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 86170a749..476882bd6 100644 --- a/internal/controller/core/pim_controller.go +++ b/internal/controller/core/pim_controller.go @@ -49,9 +49,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 } @@ -86,24 +83,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.PIMProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/pim_controller_test.go b/internal/controller/core/pim_controller_test.go index f744b7eb1..8e32dc05d 100644 --- a/internal/controller/core/pim_controller_test.go +++ b/internal/controller/core/pim_controller_test.go @@ -30,6 +30,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()) @@ -123,6 +124,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 a4ff86109..fb33d9006 100644 --- a/internal/controller/core/prefixset_controller.go +++ b/internal/controller/core/prefixset_controller.go @@ -48,9 +48,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 } @@ -85,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.PrefixSetProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/prefixset_controller_test.go b/internal/controller/core/prefixset_controller_test.go index 4b633b37f..39784857d 100644 --- a/internal/controller/core/prefixset_controller_test.go +++ b/internal/controller/core/prefixset_controller_test.go @@ -30,6 +30,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/probe_controller.go b/internal/controller/core/probe_controller.go index fcb361826..e222ecf4f 100644 --- a/internal/controller/core/probe_controller.go +++ b/internal/controller/core/probe_controller.go @@ -50,9 +50,6 @@ type ProbeReconciler struct { // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - // Provider is the driver that will be used to execute probe assertions. - Provider provider.ProviderFunc - // Locker is used to synchronize operations on resources targeting the same device. Locker *resourcelock.ResourceLocker } @@ -78,24 +75,28 @@ func (r *ProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct return ctrl.Result{}, err } - prov, ok := r.Provider().(provider.ProbeProvider) - if !ok { + device, err := deviceutil.GetDeviceByName(ctx, r, obj.Namespace, obj.Spec.DeviceRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + prov, err := provider.LoadProvider[provider.ProbeProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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.ProbeProvider", + 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 reachable := conditions.Get(device, v1alpha1.ReachableCondition); reachable != nil && reachable.Status == metav1.ConditionFalse { conditions.Set(obj, metav1.Condition{ Type: v1alpha1.ReadyCondition, diff --git a/internal/controller/core/probe_controller_test.go b/internal/controller/core/probe_controller_test.go index 9c798329b..97a671fa1 100644 --- a/internal/controller/core/probe_controller_test.go +++ b/internal/controller/core/probe_controller_test.go @@ -30,6 +30,7 @@ var _ = Describe("Probe Controller", func() { GenerateName: "test-probe-", Namespace: metav1.NamespaceDefault, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.10.2:9339", }, diff --git a/internal/controller/core/routingpolicy_controller.go b/internal/controller/core/routingpolicy_controller.go index a465048de..a7846489d 100644 --- a/internal/controller/core/routingpolicy_controller.go +++ b/internal/controller/core/routingpolicy_controller.go @@ -48,9 +48,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 } @@ -85,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.RoutingPolicyProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/routingpolicy_controller_test.go b/internal/controller/core/routingpolicy_controller_test.go index 4594cd67f..4c0df2d63 100644 --- a/internal/controller/core/routingpolicy_controller_test.go +++ b/internal/controller/core/routingpolicy_controller_test.go @@ -32,6 +32,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 118349cc8..5bf0dbfa6 100644 --- a/internal/controller/core/snmp_controller.go +++ b/internal/controller/core/snmp_controller.go @@ -48,9 +48,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 } @@ -85,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.SNMPProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/snmp_controller_test.go b/internal/controller/core/snmp_controller_test.go index 33d26a160..52c4598e7 100644 --- a/internal/controller/core/snmp_controller_test.go +++ b/internal/controller/core/snmp_controller_test.go @@ -29,6 +29,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 15d38f8c5..48ca12a49 100644 --- a/internal/controller/core/suite_test.go +++ b/internal/controller/core/suite_test.go @@ -121,13 +121,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()) @@ -136,7 +135,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -146,7 +144,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -155,7 +152,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -164,7 +160,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -173,7 +168,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -182,7 +176,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -191,7 +184,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -200,7 +192,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -209,7 +200,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -218,7 +208,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -227,7 +216,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -236,7 +224,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -245,7 +232,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -254,7 +240,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -264,7 +249,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -274,7 +258,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -284,7 +267,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -294,7 +276,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -303,7 +284,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -313,7 +293,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -322,7 +301,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) @@ -331,7 +309,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -341,7 +318,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -351,7 +327,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, ObjectStorage: testS3Store, }).SetupWithManager(ctx, k8sManager) @@ -361,7 +336,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, RequeueInterval: time.Second, }).SetupWithManager(ctx, k8sManager) @@ -371,7 +345,6 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: prov, Locker: testLocker, }).SetupWithManager(ctx, k8sManager) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/core/syslog_controller.go b/internal/controller/core/syslog_controller.go index dffa03159..0bc3ab230 100644 --- a/internal/controller/core/syslog_controller.go +++ b/internal/controller/core/syslog_controller.go @@ -48,9 +48,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 } @@ -85,24 +82,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.SyslogProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/syslog_controller_test.go b/internal/controller/core/syslog_controller_test.go index 9feade252..25fcb7e2b 100644 --- a/internal/controller/core/syslog_controller_test.go +++ b/internal/controller/core/syslog_controller_test.go @@ -29,6 +29,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 e8524896a..c9d033055 100644 --- a/internal/controller/core/user_controller.go +++ b/internal/controller/core/user_controller.go @@ -50,9 +50,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 } @@ -88,24 +85,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.UserProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/user_controller_test.go b/internal/controller/core/user_controller_test.go index a5b1438af..8b816c012 100644 --- a/internal/controller/core/user_controller_test.go +++ b/internal/controller/core/user_controller_test.go @@ -31,6 +31,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 e071f2fc5..c3f90a8a2 100644 --- a/internal/controller/core/vlan_controller.go +++ b/internal/controller/core/vlan_controller.go @@ -48,9 +48,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 @@ -89,24 +86,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.VLANProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/vlan_controller_test.go b/internal/controller/core/vlan_controller_test.go index 985e2f46d..acfe2e2ba 100644 --- a/internal/controller/core/vlan_controller_test.go +++ b/internal/controller/core/vlan_controller_test.go @@ -30,6 +30,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 4d83999be..b5e6e5c59 100644 --- a/internal/controller/core/vrf_controller.go +++ b/internal/controller/core/vrf_controller.go @@ -48,9 +48,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 } @@ -87,24 +84,28 @@ 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 + } + + prov, err := provider.LoadProvider[provider.VRFProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + 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 - } - orig := obj.DeepCopy() if isPaused, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || err != nil { return ctrl.Result{}, err diff --git a/internal/controller/core/vrf_controller_test.go b/internal/controller/core/vrf_controller_test.go index d28ef4dbf..5e5c30ab6 100644 --- a/internal/controller/core/vrf_controller_test.go +++ b/internal/controller/core/vrf_controller_test.go @@ -31,6 +31,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/controller/evpn/fabric_controller.go b/internal/controller/evpn/fabric_controller.go index e243dbcbd..8ad0abdd9 100644 --- a/internal/controller/evpn/fabric_controller.go +++ b/internal/controller/evpn/fabric_controller.go @@ -6,6 +6,7 @@ package evpn import ( "cmp" "context" + "errors" "fmt" "net/netip" "slices" @@ -45,9 +46,6 @@ type FabricReconciler struct { // Recorder is used to record events for the controller. // More info: https://book.kubebuilder.io/reference/raising-events Recorder events.EventRecorder - - // Provider is the driver that will be used to create interfaces. - Provider provider.ProviderFunc } // +kubebuilder:rbac:groups=evpn.networking.metal.ironcore.dev,resources=fabrics,verbs=get;list;watch;create;update;patch;delete @@ -85,18 +83,6 @@ func (r *FabricReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c return ctrl.Result{}, err } - if _, ok := r.Provider().(provider.InterfaceProvider); !ok { - if meta.SetStatusCondition(&fabric.Status.Conditions, metav1.Condition{ - Type: v1alpha1.ReadyCondition, - Status: metav1.ConditionFalse, - Reason: v1alpha1.NotImplementedReason, - Message: "Provider does not implement provider.InterfaceProvider", - }) { - return ctrl.Result{}, r.Status().Update(ctx, fabric) - } - return ctrl.Result{}, nil - } - if !fabric.DeletionTimestamp.IsZero() { if controllerutil.ContainsFinalizer(fabric, evpnv1alpha1.FinalizerName) { if err := r.finalize(ctx, fabric); err != nil { @@ -528,7 +514,24 @@ func (r *FabricReconciler) reconcileLoopbackInterface(ctx context.Context, fabri return nil, reconcile.TerminalError(fmt.Errorf("parsing allocated address %q: %w", claim.Status.Value, err)) } - handle, err := r.Provider().(provider.InterfaceProvider).LoopbackInterfaceName(loopbackID) + prov, err := provider.LoadProvider[provider.InterfaceProvider](device.Spec.Provider) + if err != nil { + reason := v1alpha1.NotImplementedReason + if _, ok := errors.AsType[provider.NotFoundError](err); ok { //nolint:errcheck + reason = v1alpha1.ProviderNotFoundReason + } + if meta.SetStatusCondition(&device.Status.Conditions, metav1.Condition{ + Type: v1alpha1.ReadyCondition, + Status: metav1.ConditionFalse, + Reason: reason, + Message: err.Error(), + }) { + return nil, r.Status().Update(ctx, device) + } + return nil, nil //nolint:nilnil + } + + handle, err := prov.LoopbackInterfaceName(loopbackID) if err != nil { return nil, reconcile.TerminalError(fmt.Errorf("resolving loopback interface name for id %d: %w", loopbackID, err)) } diff --git a/internal/controller/evpn/fabric_controller_test.go b/internal/controller/evpn/fabric_controller_test.go index 345ab9bae..c183549a7 100644 --- a/internal/controller/evpn/fabric_controller_test.go +++ b/internal/controller/evpn/fabric_controller_test.go @@ -67,7 +67,7 @@ var _ = Describe("Fabric Controller", func() { GenerateName: "spine1-", Namespace: metav1.NamespaceDefault, Labels: map[string]string{"topology.kubernetes.io/zone": "test-zone", "role": "spine"}, - Spec: corev1alpha1.DeviceSpec{Endpoint: corev1alpha1.Endpoint{Address: "192.168.0.1:9339"}}, + Spec: corev1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: corev1alpha1.Endpoint{Address: "192.168.0.1:9339"}}, } Expect(k8sClient.Create(ctx, spine1)).To(Succeed()) DeferCleanup(func() { @@ -79,7 +79,7 @@ var _ = Describe("Fabric Controller", func() { GenerateName: "spine2-", Namespace: metav1.NamespaceDefault, Labels: map[string]string{"topology.kubernetes.io/zone": "test-zone", "role": "spine"}, - Spec: corev1alpha1.DeviceSpec{Endpoint: corev1alpha1.Endpoint{Address: "192.168.0.2:9339"}}, + Spec: corev1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: corev1alpha1.Endpoint{Address: "192.168.0.2:9339"}}, } Expect(k8sClient.Create(ctx, spine2)).To(Succeed()) DeferCleanup(func() { @@ -91,7 +91,7 @@ var _ = Describe("Fabric Controller", func() { GenerateName: "leaf1-", Namespace: metav1.NamespaceDefault, Labels: map[string]string{"topology.kubernetes.io/zone": "test-zone", "role": "leaf"}, - Spec: corev1alpha1.DeviceSpec{Endpoint: corev1alpha1.Endpoint{Address: "192.168.1.1:9339"}}, + Spec: corev1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: corev1alpha1.Endpoint{Address: "192.168.1.1:9339"}}, } Expect(k8sClient.Create(ctx, leaf1)).To(Succeed()) DeferCleanup(func() { @@ -103,7 +103,7 @@ var _ = Describe("Fabric Controller", func() { GenerateName: "leaf2-", Namespace: metav1.NamespaceDefault, Labels: map[string]string{"topology.kubernetes.io/zone": "test-zone", "role": "leaf"}, - Spec: corev1alpha1.DeviceSpec{Endpoint: corev1alpha1.Endpoint{Address: "192.168.1.2:9339"}}, + Spec: corev1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: corev1alpha1.Endpoint{Address: "192.168.1.2:9339"}}, } Expect(k8sClient.Create(ctx, leaf2)).To(Succeed()) DeferCleanup(func() { @@ -584,7 +584,7 @@ var _ = Describe("Fabric Controller", func() { GenerateName: "spine1-", Namespace: metav1.NamespaceDefault, Labels: map[string]string{"topology.kubernetes.io/zone": "test-zone", "role": "spine"}, - Spec: corev1alpha1.DeviceSpec{Endpoint: corev1alpha1.Endpoint{Address: "192.168.0.1:9339"}}, + Spec: corev1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: corev1alpha1.Endpoint{Address: "192.168.0.1:9339"}}, } Expect(k8sClient.Create(ctx, spine1)).To(Succeed()) DeferCleanup(func() { @@ -596,7 +596,7 @@ var _ = Describe("Fabric Controller", func() { GenerateName: "leaf1-", Namespace: metav1.NamespaceDefault, Labels: map[string]string{"topology.kubernetes.io/zone": "test-zone", "role": "leaf"}, - Spec: corev1alpha1.DeviceSpec{Endpoint: corev1alpha1.Endpoint{Address: "192.168.1.1:9339"}}, + Spec: corev1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: corev1alpha1.Endpoint{Address: "192.168.1.1:9339"}}, } Expect(k8sClient.Create(ctx, leaf1)).To(Succeed()) DeferCleanup(func() { @@ -784,7 +784,7 @@ var _ = Describe("Fabric Controller", func() { GenerateName: "spine1-", Namespace: metav1.NamespaceDefault, Labels: map[string]string{"topology.kubernetes.io/zone": "test-zone", "role": "spine"}, - Spec: corev1alpha1.DeviceSpec{Endpoint: corev1alpha1.Endpoint{Address: "192.168.0.1:9339"}}, + Spec: corev1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: corev1alpha1.Endpoint{Address: "192.168.0.1:9339"}}, } Expect(k8sClient.Create(ctx, spine1)).To(Succeed()) DeferCleanup(func() { @@ -796,7 +796,7 @@ var _ = Describe("Fabric Controller", func() { GenerateName: "leaf1-", Namespace: metav1.NamespaceDefault, Labels: map[string]string{"topology.kubernetes.io/zone": "test-zone", "role": "leaf"}, - Spec: corev1alpha1.DeviceSpec{Endpoint: corev1alpha1.Endpoint{Address: "192.168.1.1:9339"}}, + Spec: corev1alpha1.DeviceSpec{Provider: "test-provider", Endpoint: corev1alpha1.Endpoint{Address: "192.168.1.1:9339"}}, } Expect(k8sClient.Create(ctx, leaf1)).To(Succeed()) DeferCleanup(func() { diff --git a/internal/controller/evpn/suite_test.go b/internal/controller/evpn/suite_test.go index 3aaf4c923..e7c0cd617 100644 --- a/internal/controller/evpn/suite_test.go +++ b/internal/controller/evpn/suite_test.go @@ -100,11 +100,12 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) + provider.Register("test-provider", NewProvider) + err = (&FabricReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Recorder: recorder, - Provider: NewProvider, }).SetupWithManager(k8sManager) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 94164922e..36e789f5a 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. @@ -985,3 +987,39 @@ type ProviderConfig struct { //nolint:revive // stutter is intentional; Provider func (p ProviderConfig) Into(v any) error { return runtime.DefaultUnstructuredConverter.FromUnstructured(p.obj.Object, v) } + +type NotFoundError struct { + ProviderName string +} + +func (e NotFoundError) Error() string { + return fmt.Sprintf("provider %q is not registered", e.ProviderName) +} + +func (e NotFoundError) Is(target error) bool { + _, ok := target.(NotFoundError) + return ok +} + +type NotImplementedError[T any] struct { + ProviderName string +} + +func (e NotImplementedError[T]) Error() string { + var zero T + return fmt.Sprintf("provider %q does not implement %T", e.ProviderName, zero) +} + +// 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) (zero T, _ error) { + prov, err := Get(providerName) + if err != nil { + return zero, NotFoundError{ProviderName: providerName} + } + provider, ok := prov().(T) + if !ok { + return zero, NotImplementedError[T]{ProviderName: providerName} + } + return provider, nil +} diff --git a/internal/provisioning/http.go b/internal/provisioning/http.go index 83794f22d..8bb4a2d85 100644 --- a/internal/provisioning/http.go +++ b/internal/provisioning/http.go @@ -91,7 +91,6 @@ type HTTPServer struct { Mux *http.ServeMux Recorder events.EventRecorder ValidateSourceIP bool - Provider provider.ProvisioningProvider Port int } @@ -306,7 +305,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 9689fa2e7..14aebd0c5 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 ( @@ -37,6 +38,7 @@ var ( v1alpha1.DeviceSerialLabel: "ABC123", }, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.1.100:22", SecretRef: &v1alpha1.SecretReference{ @@ -75,6 +77,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 } @@ -405,6 +415,7 @@ func TestHandleProvisioningRequest(t *testing.T) { Labels: map[string]string{v1alpha1.DeviceSerialLabel: "ABC123"}, Spec: v1alpha1.DeviceSpec{ Endpoint: v1alpha1.Endpoint{Address: "192.168.1.200:22"}, + Provider: "test-provider", }, Status: v1alpha1.DeviceStatus{SerialNumber: "ABC123"}, }, @@ -421,6 +432,7 @@ func TestHandleProvisioningRequest(t *testing.T) { Namespace: "default", 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{ @@ -488,7 +500,6 @@ func TestHandleProvisioningRequest(t *testing.T) { Client: k8sClient, Logger: klog.NewKlogr(), ValidateSourceIP: tt.validateSourceIP, - Provider: new(MockProvider), } rr := httptest.NewRecorder() @@ -928,6 +939,7 @@ func TestGetMTLSClientCA(t *testing.T) { Namespace: "default", Labels: map[string]string{v1alpha1.DeviceSerialLabel: "ABC123"}, Spec: v1alpha1.DeviceSpec{ + Provider: "test-provider", Endpoint: v1alpha1.Endpoint{ Address: "192.168.1.100:22", }, @@ -950,6 +962,7 @@ func TestGetMTLSClientCA(t *testing.T) { Namespace: "default", 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{ @@ -980,6 +993,7 @@ func TestGetMTLSClientCA(t *testing.T) { Namespace: "default", 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{ @@ -1009,6 +1023,7 @@ func TestGetMTLSClientCA(t *testing.T) { Namespace: "default", 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{ @@ -1086,4 +1101,5 @@ func TestGetMTLSClientCA(t *testing.T) { func init() { utilruntime.Must(v1alpha1.AddToScheme(scheme.Scheme)) + provider.Register("test-provider", func() provider.Provider { return &MockProvider{} }) } diff --git a/test/gnmi/gnmi_suite_test.go b/test/gnmi/gnmi_suite_test.go index b1ba2b06e..03df6bef4 100644 --- a/test/gnmi/gnmi_suite_test.go +++ b/test/gnmi/gnmi_suite_test.go @@ -6,7 +6,6 @@ package gnmitest import ( "context" "fmt" - "os" "path/filepath" "testing" "time" @@ -30,7 +29,6 @@ import ( "github.com/ironcore-dev/network-operator/api/core/v1alpha1" nxcontroller "github.com/ironcore-dev/network-operator/internal/controller/cisco/nx" "github.com/ironcore-dev/network-operator/internal/controller/core" - "github.com/ironcore-dev/network-operator/internal/provider" "github.com/ironcore-dev/network-operator/internal/resourcelock" testserver "github.com/ironcore-dev/network-operator/test/gnmi/server" @@ -52,10 +50,6 @@ var ( gnmiServer *testserver.Server ) -// providerFunc is the provider function resolved during Ginkgo tree construction. -// NOTE: This is assigned in the Describe block (gnmi_test.go) which runs BEFORE BeforeSuite. -var providerFunc provider.ProviderFunc - // suiteCancel stops long-running components (gNMI server, controller manager) in AfterSuite. var suiteCancel context.CancelFunc @@ -73,11 +67,7 @@ var _ = BeforeSuite(func(ctx SpecContext) { SetDefaultEventuallyTimeout(60 * time.Second) SetDefaultEventuallyPollingInterval(time.Second) - By("resolving provider") - // PROVIDER env var already validated during tree construction (Describe block runs first) var err error - providerFunc, err = provider.Get(os.Getenv(ProviderEnvVar)) - Expect(err).NotTo(HaveOccurred()) By("initializing envtest environment") @@ -128,7 +118,7 @@ var _ = BeforeSuite(func(ctx SpecContext) { err = mgr.Add(locker) Expect(err).NotTo(HaveOccurred()) - registerControllers(ctx, mgr, recorder, providerFunc, locker) + registerControllers(ctx, mgr, recorder, locker) go func() { defer GinkgoRecover() @@ -160,14 +150,13 @@ var _ = AfterSuite(func(ctx SpecContext) { // registerControllers registers all controllers with the manager. // Add more controllers here as needed for testing. -func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events.FakeRecorder, providerFn provider.ProviderFunc, locker *resourcelock.ResourceLocker) { +func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events.FakeRecorder, locker *resourcelock.ResourceLocker) { var err error err = (&core.PrefixSetReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -176,7 +165,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -185,7 +173,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, RequeueInterval: time.Minute, }).SetupWithManager(ctx, mgr) @@ -195,7 +182,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, RequeueInterval: time.Minute, }).SetupWithManager(ctx, mgr) @@ -205,7 +191,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -214,7 +199,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -223,7 +207,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -232,7 +215,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, RequeueInterval: time.Minute, }).SetupWithManager(ctx, mgr) @@ -242,7 +224,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(mgr) Expect(err).NotTo(HaveOccurred()) @@ -251,7 +232,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -260,7 +240,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -269,7 +248,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, RequeueInterval: time.Minute, }).SetupWithManager(ctx, mgr) @@ -279,7 +257,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -288,7 +265,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, RequeueInterval: time.Minute, }).SetupWithManager(ctx, mgr) @@ -298,7 +274,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -307,7 +282,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, RequeueInterval: time.Minute, }).SetupWithManager(ctx, mgr) @@ -317,7 +291,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, RequeueInterval: time.Minute, }).SetupWithManager(ctx, mgr) @@ -327,7 +300,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -336,7 +308,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -345,7 +316,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -354,7 +324,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -363,7 +332,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, RequeueInterval: time.Minute, }).SetupWithManager(ctx, mgr) @@ -373,7 +341,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -382,7 +349,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -391,7 +357,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) @@ -400,7 +365,6 @@ func registerControllers(ctx context.Context, mgr ctrl.Manager, recorder *events Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: recorder, - Provider: providerFn, Locker: locker, }).SetupWithManager(ctx, mgr) Expect(err).NotTo(HaveOccurred()) diff --git a/test/gnmi/gnmi_test.go b/test/gnmi/gnmi_test.go index ae934975d..23b8b3d8c 100644 --- a/test/gnmi/gnmi_test.go +++ b/test/gnmi/gnmi_test.go @@ -101,6 +101,7 @@ var _ = Describe("gNMI requests tests", func() { GenerateName: "test-device-", Namespace: testNamespace, Spec: v1alpha1.DeviceSpec{ + Provider: envProvider, Endpoint: v1alpha1.Endpoint{ Address: gnmiServer.GRPCAddr(), }, diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/aaa.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/aaa.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/aaa.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/aaa.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/acl.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/acl.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/acl.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/acl.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/banner.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/banner.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/banner.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/banner.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/bgp_peer.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/bgp_peer.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/bgp_peer.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/bgp_peer.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/dhcprelay.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/dhcprelay.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/dhcprelay.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/dhcprelay.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/dns.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/dns.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/dns.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/dns.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/ethernetsegment.txtar.skip b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/ethernetsegment.txtar.skip similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/ethernetsegment.txtar.skip rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/ethernetsegment.txtar.skip diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/evpninstance.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/evpninstance.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/evpninstance.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/evpninstance.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_aggregate_l2_trunk.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_aggregate_l2_trunk.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_aggregate_l2_trunk.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_aggregate_l2_trunk.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_aggregate_l3.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_aggregate_l3.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_aggregate_l3.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_aggregate_l3.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_aggregate_vpc_stp_lacp.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_aggregate_vpc_stp_lacp.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_aggregate_vpc_stp_lacp.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_aggregate_vpc_stp_lacp.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_loopback.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_loopback.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_loopback.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_loopback.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_loopback_multi_addr.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_loopback_multi_addr.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_loopback_multi_addr.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_loopback_multi_addr.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_physical_ipv4.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_physical_ipv4.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_physical_ipv4.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_physical_ipv4.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_physical_switchport_access.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_physical_switchport_access.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_physical_switchport_access.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_physical_switchport_access.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_physical_switchport_trunk.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_physical_switchport_trunk.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_physical_switchport_trunk.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_physical_switchport_trunk.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_physical_unnumbered.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_physical_unnumbered.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_physical_unnumbered.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_physical_unnumbered.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/interface_routed_vlan.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_routed_vlan.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/interface_routed_vlan.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/interface_routed_vlan.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/isis.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/isis.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/isis.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/isis.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/lldp.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/lldp.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/lldp.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/lldp.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/managementaccess.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/managementaccess.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/managementaccess.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/managementaccess.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/ntp.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/ntp.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/ntp.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/ntp.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/nve.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/nve.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/nve.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/nve.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/ospf.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/ospf.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/ospf.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/ospf.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/pim.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/pim.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/pim.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/pim.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/routingpolicy.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/routingpolicy.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/routingpolicy.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/routingpolicy.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/snmp.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/snmp.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/snmp.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/snmp.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/syslog.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/syslog.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/syslog.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/syslog.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/system.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/system.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/system.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/system.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/user.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/user.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/user.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/user.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/vlan.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/vlan.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/vlan.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/vlan.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/vpcdomain.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/vpcdomain.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/vpcdomain.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/vpcdomain.txtar diff --git a/test/gnmi/testdata/cisco-nxos-gnmi/vrf.txtar b/test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/vrf.txtar similarity index 100% rename from test/gnmi/testdata/cisco-nxos-gnmi/vrf.txtar rename to test/gnmi/testdata/nx.cisco.networking.metal.ironcore.dev/vrf.txtar diff --git a/test/gnmi/testdata/openconfig/aaa.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/aaa.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/aaa.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/aaa.txtar diff --git a/test/gnmi/testdata/openconfig/acl.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/acl.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/acl.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/acl.txtar diff --git a/test/gnmi/testdata/openconfig/banner.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/banner.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/banner.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/banner.txtar diff --git a/test/gnmi/testdata/openconfig/bgp.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/bgp.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/bgp.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/bgp.txtar diff --git a/test/gnmi/testdata/openconfig/bgppeer.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/bgppeer.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/bgppeer.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/bgppeer.txtar diff --git a/test/gnmi/testdata/openconfig/dns.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/dns.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/dns.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/dns.txtar diff --git a/test/gnmi/testdata/openconfig/interface.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/interface.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface.txtar diff --git a/test/gnmi/testdata/openconfig/interface_aggregate_l2_trunk.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_aggregate_l2_trunk.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/interface_aggregate_l2_trunk.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_aggregate_l2_trunk.txtar diff --git a/test/gnmi/testdata/openconfig/interface_aggregate_l3.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_aggregate_l3.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/interface_aggregate_l3.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_aggregate_l3.txtar diff --git a/test/gnmi/testdata/openconfig/interface_loopback_multi_addr.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_loopback_multi_addr.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/interface_loopback_multi_addr.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_loopback_multi_addr.txtar diff --git a/test/gnmi/testdata/openconfig/interface_physical_ipv4.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_physical_ipv4.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/interface_physical_ipv4.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_physical_ipv4.txtar diff --git a/test/gnmi/testdata/openconfig/interface_physical_switchport_access.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_physical_switchport_access.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/interface_physical_switchport_access.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_physical_switchport_access.txtar diff --git a/test/gnmi/testdata/openconfig/interface_physical_switchport_trunk.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_physical_switchport_trunk.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/interface_physical_switchport_trunk.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_physical_switchport_trunk.txtar diff --git a/test/gnmi/testdata/openconfig/interface_physical_unnumbered.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_physical_unnumbered.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/interface_physical_unnumbered.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_physical_unnumbered.txtar diff --git a/test/gnmi/testdata/openconfig/interface_routed_vlan.txtar.skip b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_routed_vlan.txtar.skip similarity index 100% rename from test/gnmi/testdata/openconfig/interface_routed_vlan.txtar.skip rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/interface_routed_vlan.txtar.skip diff --git a/test/gnmi/testdata/openconfig/isis.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/isis.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/isis.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/isis.txtar diff --git a/test/gnmi/testdata/openconfig/lldp.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/lldp.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/lldp.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/lldp.txtar diff --git a/test/gnmi/testdata/openconfig/managementaccess.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/managementaccess.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/managementaccess.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/managementaccess.txtar diff --git a/test/gnmi/testdata/openconfig/prefixset.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/prefixset.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/prefixset.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/prefixset.txtar diff --git a/test/gnmi/testdata/openconfig/routingpolicy.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/routingpolicy.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/routingpolicy.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/routingpolicy.txtar diff --git a/test/gnmi/testdata/openconfig/syslog.txtar.skip b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/syslog.txtar.skip similarity index 100% rename from test/gnmi/testdata/openconfig/syslog.txtar.skip rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/syslog.txtar.skip diff --git a/test/gnmi/testdata/openconfig/user.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/user.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/user.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/user.txtar diff --git a/test/gnmi/testdata/openconfig/vrf.txtar b/test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/vrf.txtar similarity index 100% rename from test/gnmi/testdata/openconfig/vrf.txtar rename to test/gnmi/testdata/openconfig.networking.metal.ironcore.dev/vrf.txtar