@@ -22,11 +22,13 @@ import (
22
22
23
23
"github.com/go-logr/logr"
24
24
"github.com/pkg/errors"
25
+ corev1 "k8s.io/api/core/v1"
25
26
apierrors "k8s.io/apimachinery/pkg/api/errors"
26
27
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
28
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28
29
"k8s.io/apimachinery/pkg/types"
29
30
kerrors "k8s.io/apimachinery/pkg/util/errors"
31
+ "k8s.io/apimachinery/pkg/util/sets"
30
32
"k8s.io/apimachinery/pkg/util/wait"
31
33
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
32
34
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -94,6 +96,11 @@ func (o *objectMover) move(graph *objectGraph, toProxy Proxy) error {
94
96
return err
95
97
}
96
98
99
+ // Ensure all the expected target namespaces are in place before creating objects.
100
+ if err := o .ensureNamespaces (graph , toProxy ); err != nil {
101
+ return err
102
+ }
103
+
97
104
// Define the move sequence by processing the ownerReference chain, so we ensure that a Kubernetes object is moved only after its owners.
98
105
// The sequence is bases on object graph nodes, each one representing a Kubernetes object; nodes are grouped, so bulk of nodes can be moved in parallel. e.g.
99
106
// - All the Clusters should be moved first (group 1, processed in parallel)
@@ -229,6 +236,67 @@ func setClusterPause(proxy Proxy, clusters []*node, value bool, log logr.Logger)
229
236
return nil
230
237
}
231
238
239
+ // ensureNamespaces ensures all the expected target namespaces are in place before creating objects.
240
+ func (o * objectMover ) ensureNamespaces (graph * objectGraph , toProxy Proxy ) error {
241
+ cs , err := toProxy .NewClient ()
242
+ if err != nil {
243
+ return err
244
+ }
245
+
246
+ namespaces := sets .NewString ()
247
+ for _ , node := range graph .getNodesWithClusterTenants () {
248
+ namespace := node .identity .Namespace
249
+
250
+ // If the namespace was already processed, skip it.
251
+ if namespaces .Has (namespace ) {
252
+ continue
253
+ }
254
+ namespaces .Insert (namespace )
255
+
256
+ // Otherwise check if namespace exists (also dealing with RBAC restrictions).
257
+ ns := & corev1.Namespace {}
258
+ key := client.ObjectKey {
259
+ Name : namespace ,
260
+ }
261
+
262
+ if err = cs .Get (ctx , key , ns ); err == nil {
263
+ return nil
264
+ }
265
+ if apierrors .IsForbidden (err ) {
266
+ namespaces := & corev1.NamespaceList {}
267
+ if err := cs .List (ctx , namespaces ); err != nil {
268
+ return err
269
+ }
270
+
271
+ for _ , ns := range namespaces .Items {
272
+ if ns .Name == namespace {
273
+ return nil
274
+ }
275
+ }
276
+ }
277
+ if ! apierrors .IsNotFound (err ) {
278
+ return err
279
+ }
280
+
281
+ // If the namespace does not exists, create it.
282
+ ns = & corev1.Namespace {
283
+ TypeMeta : metav1.TypeMeta {
284
+ APIVersion : "v1" ,
285
+ Kind : "Namespace" ,
286
+ },
287
+ ObjectMeta : metav1.ObjectMeta {
288
+ Name : namespace ,
289
+ },
290
+ }
291
+ o .log .V (1 ).Info ("Creating" , ns .Kind , ns .Name )
292
+ if err = cs .Create (ctx , ns ); err != nil && ! apierrors .IsAlreadyExists (err ) {
293
+ return err
294
+ }
295
+ }
296
+
297
+ return nil
298
+ }
299
+
232
300
const (
233
301
retryCreateTargetObject = 3
234
302
retryIntervalCreateTargetObject = 1 * time .Second
0 commit comments