@@ -18,35 +18,31 @@ package namespace
1818
1919import (
2020 "context"
21- "encoding/json"
2221 "fmt"
2322 "time"
2423
25- jsonpatch "github.com/evanphx/json-patch"
2624 kcpcache "github.com/kcp-dev/apimachinery/v2/pkg/cache"
2725 kcpcorev1informers "github.com/kcp-dev/client-go/informers/core/v1"
2826 kcpkubernetesclientset "github.com/kcp-dev/client-go/kubernetes"
29- corev1listers "github.com/kcp-dev/client-go/listers/core/v1"
3027 "github.com/kcp-dev/logicalcluster/v3"
3128
3229 corev1 "k8s.io/api/core/v1"
33- "k8s.io/apimachinery/pkg/api/equality"
3430 "k8s.io/apimachinery/pkg/api/errors"
35- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3631 "k8s.io/apimachinery/pkg/labels"
37- "k8s.io/apimachinery/pkg/types "
32+ utilerrors "k8s.io/apimachinery/pkg/util/errors "
3833 "k8s.io/apimachinery/pkg/util/runtime"
3934 "k8s.io/apimachinery/pkg/util/wait"
35+ corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
4036 "k8s.io/client-go/tools/cache"
4137 "k8s.io/client-go/util/workqueue"
4238 "k8s.io/klog/v2"
4339 "k8s.io/kube-openapi/pkg/util/sets"
4440
4541 "github.com/kcp-dev/kcp/pkg/logging"
4642 "github.com/kcp-dev/kcp/pkg/reconciler/apis/apiexport"
43+ "github.com/kcp-dev/kcp/pkg/reconciler/committer"
4744 schedulingv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/scheduling/v1alpha1"
4845 schedulingv1alpha1informers "github.com/kcp-dev/kcp/sdk/client/informers/externalversions/scheduling/v1alpha1"
49- schedulingv1alpha1listers "github.com/kcp-dev/kcp/sdk/client/listers/scheduling/v1alpha1"
5046)
5147
5248const (
@@ -64,21 +60,20 @@ func NewController(
6460
6561 c := & controller {
6662 queue : queue ,
67- enqueueAfter : func (ns * corev1.Namespace , duration time.Duration ) {
68- key , err := kcpcache .MetaClusterNamespaceKeyFunc (ns )
69- if err != nil {
70- runtime .HandleError (err )
71- return
72- }
73- queue .AddAfter (key , duration )
74- },
7563
7664 kubeClusterClient : kubeClusterClient ,
7765
78- namespaceLister : namespaceInformer .Lister (),
79-
80- placementLister : placementInformer .Lister (),
81- placementIndexer : placementInformer .Informer ().GetIndexer (),
66+ listNamespaces : func (clusterName logicalcluster.Name ) ([]* corev1.Namespace , error ) {
67+ return namespaceInformer .Cluster (clusterName ).Lister ().List (labels .Everything ())
68+ },
69+ getNamespace : func (clusterName logicalcluster.Name , name string ) (* corev1.Namespace , error ) {
70+ return namespaceInformer .Cluster (clusterName ).Lister ().Get (name )
71+ },
72+ listPlacements : func (clusterName logicalcluster.Name ) ([]* schedulingv1alpha1.Placement , error ) {
73+ return placementInformer .Cluster (clusterName ).Lister ().List (labels .Everything ())
74+ },
75+ commit : committer.NewCommitter [* Namespace , Patcher , * NamespaceSpec , * NamespaceStatus ](kubeClusterClient .CoreV1 ().Namespaces ()),
76+ now : time .Now ,
8277 }
8378
8479 // namespaceBlocklist holds a set of namespaces that should never be synced from kcp to physical clusters.
@@ -112,17 +107,24 @@ func NewController(
112107
113108// controller.
114109type controller struct {
115- queue workqueue.RateLimitingInterface
116- enqueueAfter func (* corev1.Namespace , time.Duration )
110+ queue workqueue.RateLimitingInterface
117111
118112 kubeClusterClient kcpkubernetesclientset.ClusterInterface
119113
120- namespaceLister corev1listers.NamespaceClusterLister
121-
122- placementLister schedulingv1alpha1listers.PlacementClusterLister
123- placementIndexer cache.Indexer
114+ listNamespaces func (clusterName logicalcluster.Name ) ([]* corev1.Namespace , error )
115+ getNamespace func (clusterName logicalcluster.Name , name string ) (* corev1.Namespace , error )
116+ listPlacements func (clusterName logicalcluster.Name ) ([]* schedulingv1alpha1.Placement , error )
117+ commit CommitFunc
118+ now func () time.Time
124119}
125120
121+ type Namespace = corev1.Namespace
122+ type NamespaceSpec = corev1.NamespaceSpec
123+ type NamespaceStatus = corev1.NamespaceStatus
124+ type Patcher = corev1client.NamespaceInterface
125+ type Resource = committer.Resource [* NamespaceSpec , * NamespaceStatus ]
126+ type CommitFunc = func (ctx context.Context , original , updated * Resource ) error
127+
126128func (c * controller ) enqueueNamespace (obj interface {}) {
127129 key , err := kcpcache .DeletionHandlingMetaClusterNamespaceKeyFunc (obj )
128130 if err != nil {
@@ -147,14 +149,14 @@ func (c *controller) enqueuePlacement(obj interface{}) {
147149 return
148150 }
149151
150- nss , err := c .namespaceLister . Cluster (clusterName ). List ( labels . Everything () )
152+ namespaces , err := c .listNamespaces (clusterName )
151153 if err != nil {
152154 runtime .HandleError (err )
153155 return
154156 }
155157
156158 logger := logging .WithObject (logging .WithReconciler (klog .Background (), ControllerName ), obj .(* schedulingv1alpha1.Placement ))
157- for _ , ns := range nss {
159+ for _ , ns := range namespaces {
158160 logger = logging .WithObject (logger , ns )
159161
160162 nsKey , err := kcpcache .MetaClusterNamespaceKeyFunc (ns )
@@ -222,55 +224,29 @@ func (c *controller) process(ctx context.Context, key string) error {
222224 return nil
223225 }
224226
225- obj , err := c .namespaceLister . Cluster (clusterName ). Get ( name )
227+ ns , err := c .getNamespace (clusterName , name )
226228 if err != nil {
227229 if errors .IsNotFound (err ) {
228230 return nil // object deleted before we handled it
229231 }
230232 return err
231233 }
232- old := obj
233- obj = obj .DeepCopy ()
234+ old := ns
235+ ns = ns .DeepCopy ()
234236
235- logger = logging .WithObject (logger , obj )
237+ logger = logging .WithObject (logger , ns )
236238 ctx = klog .NewContext (ctx , logger )
237239
238- reconcileErr := c .reconcile (ctx , obj )
239-
240- // If the object being reconciled changed as a result, update it.
241- if ! equality .Semantic .DeepEqual (old .Status , obj .Status ) {
242- oldData , err := json .Marshal (corev1.Namespace {
243- Status : old .Status ,
244- })
245- if err != nil {
246- return fmt .Errorf ("failed to Marshal old data for placement %s|%s: %w" , clusterName , name , err )
247- }
248-
249- newData , err := json .Marshal (corev1.Namespace {
250- ObjectMeta : metav1.ObjectMeta {
251- UID : old .UID ,
252- ResourceVersion : old .ResourceVersion ,
253- }, // to ensure they appear in the patch as preconditions
254- Status : obj .Status ,
255- })
256- if err != nil {
257- return fmt .Errorf ("failed to Marshal new data for LocationDomain %s|%s: %w" , clusterName , name , err )
258- }
259-
260- patchBytes , err := jsonpatch .CreateMergePatch (oldData , newData )
261- if err != nil {
262- return fmt .Errorf ("failed to create patch for LocationDomain %s|%s: %w" , clusterName , name , err )
263- }
264- logger .WithValues ("patch" , string (patchBytes )).V (2 ).Info ("patching Namespace" )
265- _ , uerr := c .kubeClusterClient .Cluster (clusterName .Path ()).CoreV1 ().Namespaces ().Patch (ctx , obj .Name , types .MergePatchType , patchBytes , metav1.PatchOptions {}, "status" )
266- return uerr
240+ var errs []error
241+ if err := c .reconcile (ctx , key , ns ); err != nil {
242+ errs = append (errs , err )
267243 }
268244
269- return reconcileErr
270- }
245+ oldResource := & Resource {ObjectMeta : old .ObjectMeta , Spec : & old .Spec , Status : & old .Status }
246+ newResource := & Resource {ObjectMeta : ns .ObjectMeta , Spec : & ns .Spec , Status : & ns .Status }
247+ if err := c .commit (ctx , oldResource , newResource ); err != nil {
248+ errs = append (errs , err )
249+ }
271250
272- func (c * controller ) patchNamespace (ctx context.Context , clusterName logicalcluster.Path , name string , pt types.PatchType , data []byte , opts metav1.PatchOptions , subresources ... string ) (* corev1.Namespace , error ) {
273- logger := klog .FromContext (ctx )
274- logger .WithValues ("patch" , string (data )).V (2 ).Info ("patching Namespace" )
275- return c .kubeClusterClient .Cluster (clusterName ).CoreV1 ().Namespaces ().Patch (ctx , name , pt , data , opts , subresources ... )
251+ return utilerrors .NewAggregate (errs )
276252}
0 commit comments