Skip to content

Commit

Permalink
SriovNetwork: react on namespace creation
Browse files Browse the repository at this point in the history
If a user creates an SriovNetwork with a network namespace
that doesn't exist, retrying reconciling with an exponential
backoff is not efficient, as the routing will fail until the namespace
is created.

This patch makes the controller watch Namespace resource
creation and reconcile the related SriovNetworks if needed.

Signed-off-by: Andrea Panattoni <[email protected]>
  • Loading branch information
zeeke committed Dec 22, 2023
1 parent b39233d commit aaaab92
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 2 deletions.
34 changes: 33 additions & 1 deletion controllers/sriovnetwork_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ import (
"reflect"

netattdefv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"

"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
Expand Down Expand Up @@ -135,6 +137,13 @@ func (r *SriovNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Request
err = r.Get(context.TODO(), types.NamespacedName{Name: netAttDef.Name, Namespace: netAttDef.Namespace}, found)
if err != nil {
if errors.IsNotFound(err) {
targetNamespace := &corev1.Namespace{}
err = r.Get(ctx, types.NamespacedName{Name: netAttDef.Namespace}, targetNamespace)
if errors.IsNotFound(err) {
reqLogger.Info("Target namespace doesn't exist, NetworkAttachmentDefinition will be created when namespace is available", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
return reconcile.Result{}, nil
}

reqLogger.Info("NetworkAttachmentDefinition CR not exist, creating")
err = r.Create(context.TODO(), netAttDef)
if err != nil {
Expand Down Expand Up @@ -168,8 +177,31 @@ func (r *SriovNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Request

// SetupWithManager sets up the controller with the Manager.
func (r *SriovNetworkReconciler) SetupWithManager(mgr ctrl.Manager) error {
// Reconcile when the target namespace is created after the SriovNetwork object.
namespaceHandler := handler.Funcs{
CreateFunc: func(e event.CreateEvent, q workqueue.RateLimitingInterface) {
networkList := sriovnetworkv1.SriovNetworkList{}
err := r.List(context.Background(),
&networkList,
client.MatchingFields{"spec.networkNamespace": e.Object.GetName()},
)
if err != nil {
log.Log.WithName("SriovNetworkReconciler").
Info("Can't list SriovNetworks for namespace", "resource", e.Object.GetName(), "error", err)
}

for _, network := range networkList.Items {
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
Namespace: network.Namespace,
Name: network.Name,
}})
}
},
}

return ctrl.NewControllerManagedBy(mgr).
For(&sriovnetworkv1.SriovNetwork{}).
Watches(&source.Kind{Type: &netattdefv1.NetworkAttachmentDefinition{}}, &handler.EnqueueRequestForObject{}).
Watches(&source.Kind{Type: &corev1.Namespace{}}, &namespaceHandler).
Complete(r)
}
50 changes: 50 additions & 0 deletions controllers/sriovnetwork_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"time"

netattdefv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
dynclient "sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -212,5 +214,53 @@ var _ = Describe("SriovNetwork Controller", func() {
Expect(err).NotTo(HaveOccurred())
})
})

Context("When the target NetworkNamespace doesn't exists", func() {
It("should create the NetAttachDef when the namespace is created", func() {
cr := sriovnetworkv1.SriovNetwork{
ObjectMeta: metav1.ObjectMeta{
Name: "test-missing-namespace",
Namespace: testNamespace,
},
Spec: sriovnetworkv1.SriovNetworkSpec{
NetworkNamespace: "ns-xxx",
ResourceName: "resource_missing_namespace",
IPAM: `{"type":"dhcp"}`,
Vlan: 200,
},
}
var err error
expect := util.GenerateExpectedNetConfig(&cr)

err = k8sClient.Create(goctx.TODO(), &cr)
Expect(err).NotTo(HaveOccurred())

defer func() {
err = k8sClient.Delete(goctx.TODO(), &cr)
Expect(err).NotTo(HaveOccurred())
}()

// Sleep 3 seconds to be sure the Reconcile loop has been invoked. This can be improved by exposing some information (e.g. the error)
// in the SriovNetwork.Status field.
time.Sleep(3 * time.Second)

netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
err = k8sClient.Get(ctx, types.NamespacedName{Name: cr.GetName(), Namespace: "ns-xxx"}, netAttDef)
Expect(err).To(HaveOccurred())

err = k8sClient.Create(goctx.TODO(), &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "ns-xxx"},
})
Expect(err).NotTo(HaveOccurred())

err = util.WaitForNamespacedObject(netAttDef, k8sClient, "ns-xxx", cr.GetName(), util.RetryInterval, util.Timeout)
Expect(err).NotTo(HaveOccurred())

anno := netAttDef.GetAnnotations()
Expect(anno["k8s.v1.cni.cncf.io/resourceName"]).To(Equal("openshift.io/" + cr.Spec.ResourceName))
Expect(strings.TrimSpace(netAttDef.Spec.Config)).To(Equal(expect))
})
})

})
})
8 changes: 7 additions & 1 deletion controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
. "github.com/onsi/gomega"
openshiftconfigv1 "github.com/openshift/api/config/v1"
mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
"go.uber.org/zap/zapcore"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -66,7 +67,12 @@ const (
)

var _ = BeforeSuite(func(done Done) {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
logf.SetLogger(zap.New(
zap.WriteTo(GinkgoWriter),
zap.UseDevMode(true),
func(o *zap.Options) {
o.TimeEncoder = zapcore.RFC3339NanoTimeEncoder
}))

// Go to project root directory
os.Chdir("..")
Expand Down

0 comments on commit aaaab92

Please sign in to comment.