Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
ipamv1 "sigs.k8s.io/cluster-api/api/ipam/v1beta2"

"github.com/sapcc/argora/internal/controller"

Check failure on line 43 in cmd/manager/main.go

View workflow job for this annotation

GitHub Actions / Checks

could not import github.com/sapcc/argora/internal/controller (-: # github.com/sapcc/argora/internal/controller
"github.com/sapcc/argora/internal/credentials"
"github.com/sapcc/argora/internal/netbox"
"github.com/sapcc/argora/internal/status"
Expand All @@ -65,6 +65,7 @@
probeAddr string
leaderElectionNamespace string
netboxURL string
ironCoreNamespace string

enableLeaderElection bool
secureMetrics bool
Expand Down Expand Up @@ -165,7 +166,7 @@
setupLog.Info("argora", "version", bininfo.Version())

if flagVar.enableIronCore {
if err = controller.NewIronCoreReconciler(mgr, creds, status.NewClusterImportStatusHandler(mgr.GetClient()), netbox.NewNetbox(flagVar.netboxURL), flagVar.reconcileInterval).SetupWithManager(mgr, rateLimiter); err != nil {
if err = controller.NewIronCoreReconciler(mgr, creds, status.NewClusterImportStatusHandler(mgr.GetClient()), netbox.NewNetbox(flagVar.netboxURL), flagVar.reconcileInterval, flagVar.ironCoreNamespace).SetupWithManager(mgr, rateLimiter); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ironcore")
os.Exit(1)
}
Expand Down Expand Up @@ -226,6 +227,7 @@
flag.StringVar(&flagVariables.probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.StringVar(&flagVariables.leaderElectionNamespace, "leader-elect-ns", "kube-system", "The namespace in which the leader election resource will be created. This is only used if --leader-elect is set to true. Defaults to kube-system.")
flag.StringVar(&flagVariables.netboxURL, "netbox-url", "https://netbox-url", "The URL of the NetBox instance to connect to. If not set, the default value will be used.")
flag.StringVar(&flagVariables.ironCoreNamespace, "ironcore-namespace", "default", "The namespace in which IronCore resources (e.g. ServerNetworkConfig) are created.")

flag.BoolVar(&flagVariables.enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
flag.BoolVar(&flagVariables.secureMetrics, "metrics-secure", true, "If true (default), the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
Expand Down
105 changes: 104 additions & 1 deletion internal/controller/ironcore_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"golang.org/x/time/rate"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
Expand All @@ -46,16 +47,18 @@
statusHandler status.ClusterImportStatus
netBox netbox.Netbox
reconcileInterval time.Duration
namespace string
}

func NewIronCoreReconciler(mgr ctrl.Manager, creds *credentials.Credentials, statusHandler status.ClusterImportStatus, netBox netbox.Netbox, reconcileInterval time.Duration) *IronCoreReconciler {
func NewIronCoreReconciler(mgr ctrl.Manager, creds *credentials.Credentials, statusHandler status.ClusterImportStatus, netBox netbox.Netbox, reconcileInterval time.Duration, namespace string) *IronCoreReconciler {
return &IronCoreReconciler{
k8sClient: mgr.GetClient(),
scheme: mgr.GetScheme(),
credentials: creds,
statusHandler: statusHandler,
netBox: netBox,
reconcileInterval: reconcileInterval,
namespace: namespace,
}
}

Expand Down Expand Up @@ -85,6 +88,7 @@
// +kubebuilder:rbac:groups=metal.ironcore.dev,resources=servers,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=metal.ironcore.dev,resources=bmcs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=metal.ironcore.dev,resources=bmcsecrets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=metal.ironcore.dev,resources=servernetworkconfigs,verbs=get;list;watch;create;update;patch

func (r *IronCoreReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
Expand Down Expand Up @@ -229,6 +233,10 @@
return fmt.Errorf("unable to patch BMC labels: %w", err)
}

if err := r.updateServerNetworkConfig(ctx, device, commonLabels); err != nil {
return fmt.Errorf("unable to update ServerNetworkConfig: %w", err)
}

logger.Info("BMC custom resource already exists, will skip", "bmc", device.Name)
return nil
}
Expand All @@ -247,6 +255,10 @@

logger.Info("created BMC CR", "name", bmc.Name)

if err := r.createServerNetworkConfig(ctx, device, commonLabels); err != nil {
return fmt.Errorf("unable to create ServerNetworkConfig: %w", err)
}

if err := r.patchOwnerReference(ctx, bmc, bmcSecret); err != nil {
return err
}
Expand Down Expand Up @@ -344,6 +356,97 @@
return nil
}

func (r *IronCoreReconciler) buildExpectedInterfaces(device *models.Device) ([]metalv1alpha1.ExpectedNetworkInterface, error) {

Check failure on line 359 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Build

undefined: metalv1alpha1.ExpectedNetworkInterface

Check failure on line 359 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Checks

undefined: metalv1alpha1.ExpectedNetworkInterface

Check failure on line 359 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / CodeQL

undefined: metalv1alpha1.ExpectedNetworkInterface
ifaces, err := r.netBox.DCIM().GetInterfacesForDevice(device)
if err != nil {
return nil, fmt.Errorf("failed to get interfaces for device %s: %w", device.Name, err)
}

var expected []metalv1alpha1.ExpectedNetworkInterface

Check failure on line 365 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Build

undefined: metalv1alpha1.ExpectedNetworkInterface

Check failure on line 365 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Checks

undefined: metalv1alpha1.ExpectedNetworkInterface

Check failure on line 365 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / CodeQL

undefined: metalv1alpha1.ExpectedNetworkInterface
for _, iface := range ifaces {
if iface.MacAddress == "" {
continue
}
if iface.Type.Value == "lag" {
continue
}
if iface.MgmtOnly {
continue
}
if len(iface.ConnectedEndpoints) == 0 {
continue
}

endpoint := iface.ConnectedEndpoints[0]
if endpoint.Device.Name == "" {
continue
}

expected = append(expected, metalv1alpha1.ExpectedNetworkInterface{

Check failure on line 385 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Build

undefined: metalv1alpha1.ExpectedNetworkInterface

Check failure on line 385 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Checks

undefined: metalv1alpha1.ExpectedNetworkInterface

Check failure on line 385 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / CodeQL

undefined: metalv1alpha1.ExpectedNetworkInterface
Name: iface.Name,
MACAddress: iface.MacAddress,
Switch: endpoint.Device.Name,
Port: endpoint.Name,
})
}
return expected, nil
}

func (r *IronCoreReconciler) createServerNetworkConfig(ctx context.Context, device *models.Device, labels map[string]string) error {
ifaces, err := r.buildExpectedInterfaces(device)
if err != nil {
return err
}

snc := &metalv1alpha1.ServerNetworkConfig{

Check failure on line 401 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Build

undefined: metalv1alpha1.ServerNetworkConfig

Check failure on line 401 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Checks

undefined: metalv1alpha1.ServerNetworkConfig

Check failure on line 401 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / CodeQL

undefined: metalv1alpha1.ServerNetworkConfig
TypeMeta: metav1.TypeMeta{
APIVersion: metalv1alpha1.GroupVersion.String(),
Kind: "ServerNetworkConfig",
},
ObjectMeta: metav1.ObjectMeta{
Name: device.Name,
Namespace: r.namespace,
Labels: labels,
},
Spec: metalv1alpha1.ServerNetworkConfigSpec{

Check failure on line 411 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Build

undefined: metalv1alpha1.ServerNetworkConfigSpec

Check failure on line 411 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Checks

undefined: metalv1alpha1.ServerNetworkConfigSpec

Check failure on line 411 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / CodeQL

undefined: metalv1alpha1.ServerNetworkConfigSpec
ServerRef: corev1.LocalObjectReference{Name: device.Name},
Interfaces: ifaces,
},
}

if err := r.k8sClient.Create(ctx, snc); err != nil && !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("failed to create ServerNetworkConfig for %s: %w", device.Name, err)
}
return nil
}

func (r *IronCoreReconciler) updateServerNetworkConfig(ctx context.Context, device *models.Device, labels map[string]string) error {
ifaces, err := r.buildExpectedInterfaces(device)
if err != nil {
return err
}

snc := &metalv1alpha1.ServerNetworkConfig{}

Check failure on line 429 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Build

undefined: metalv1alpha1.ServerNetworkConfig

Check failure on line 429 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / Checks

undefined: metalv1alpha1.ServerNetworkConfig) (typecheck)

Check failure on line 429 in internal/controller/ironcore_controller.go

View workflow job for this annotation

GitHub Actions / CodeQL

undefined: metalv1alpha1.ServerNetworkConfig
if err := r.k8sClient.Get(ctx, client.ObjectKey{Name: device.Name, Namespace: r.namespace}, snc); err != nil {
if apierrors.IsNotFound(err) {
return r.createServerNetworkConfig(ctx, device, labels)
}
return fmt.Errorf("failed to get ServerNetworkConfig for %s: %w", device.Name, err)
}

sncBase := snc.DeepCopy()
snc.Spec.Interfaces = ifaces
if snc.Labels == nil {
snc.Labels = make(map[string]string)
}
maps.Copy(snc.Labels, labels)

if err := r.k8sClient.Patch(ctx, snc, client.MergeFrom(sncBase)); err != nil {
return fmt.Errorf("failed to patch ServerNetworkConfig for %s: %w", device.Name, err)
}
return nil
}

func getOobIP(device *models.Device) (string, error) {
oobIP := device.OOBIp.Address
ip, _, err := net.ParseCIDR(oobIP)
Expand Down
Loading