From ecb51bc0f701cbfc5c0509fc7e292baf0a6eff4d Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Mon, 3 Feb 2020 17:34:10 +0100 Subject: [PATCH 1/2] move ensure all the namespaces exists --- cmd/clusterctl/pkg/client/cluster/mover.go | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/cmd/clusterctl/pkg/client/cluster/mover.go b/cmd/clusterctl/pkg/client/cluster/mover.go index 255ec19b2f35..d172d1b79f89 100644 --- a/cmd/clusterctl/pkg/client/cluster/mover.go +++ b/cmd/clusterctl/pkg/client/cluster/mover.go @@ -22,11 +22,13 @@ import ( "github.com/go-logr/logr" "github.com/pkg/errors" + 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/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" kerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" "sigs.k8s.io/controller-runtime/pkg/client" @@ -94,6 +96,11 @@ func (o *objectMover) move(graph *objectGraph, toProxy Proxy) error { return err } + // Ensure all the expected target namespaces are in place before creating objects. + if err := o.ensureNamespaces(graph, toProxy); err != nil { + return err + } + // Define the move sequence by processing the ownerReference chain, so we ensure that a Kubernetes object is moved only after its owners. // 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. // - All the Clusters should be moved first (group 1, processed in parallel) @@ -229,6 +236,72 @@ func setClusterPause(proxy Proxy, clusters []*node, value bool, log logr.Logger) return nil } +// ensureNamespaces ensures all the expected target namespaces are in place before creating objects. +func (o *objectMover) ensureNamespaces(graph *objectGraph, toProxy Proxy) error { + cs, err := toProxy.NewClient() + if err != nil { + return err + } + + namespaces := sets.NewString() + for _, node := range graph.getNodesWithClusterTenants() { + namespace := node.identity.Namespace + + // If the namespace was already processed, skip it. + if namespaces.Has(namespace) { + continue + } + namespaces.Insert(namespace) + + // Otherwise check if namespace exists (also dealing with RBAC restrictions). + ns := &corev1.Namespace{} + key := client.ObjectKey{ + Name: namespace, + } + + if err := cs.Get(ctx, key, ns); err == nil { + return nil + } + if apierrors.IsForbidden(err) { + namespaces := &corev1.NamespaceList{} + if err := cs.List(ctx, namespaces); err != nil { + return err + } + + namespaceExists := false + for _, ns := range namespaces.Items { + if ns.Name == namespace { + namespaceExists = true + break + } + } + if namespaceExists { + continue + } + } + if !apierrors.IsNotFound(err) { + return err + } + + // If the namespace does not exists, create it. + ns = &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + } + o.log.V(1).Info("Creating", ns.Kind, ns.Name) + if err := cs.Create(ctx, ns); err != nil && !apierrors.IsAlreadyExists(err) { + return err + } + } + + return nil +} + const ( retryCreateTargetObject = 3 retryIntervalCreateTargetObject = 1 * time.Second From ae41c8fec9c7e0cba406ebfddf56f54151980a11 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Mon, 3 Feb 2020 18:14:55 +0100 Subject: [PATCH 2/2] address comment --- cmd/clusterctl/pkg/client/cluster/mover.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cmd/clusterctl/pkg/client/cluster/mover.go b/cmd/clusterctl/pkg/client/cluster/mover.go index d172d1b79f89..800904e6105b 100644 --- a/cmd/clusterctl/pkg/client/cluster/mover.go +++ b/cmd/clusterctl/pkg/client/cluster/mover.go @@ -264,14 +264,20 @@ func (o *objectMover) ensureNamespaces(graph *objectGraph, toProxy Proxy) error } if apierrors.IsForbidden(err) { namespaces := &corev1.NamespaceList{} - if err := cs.List(ctx, namespaces); err != nil { - return err - } - namespaceExists := false - for _, ns := range namespaces.Items { - if ns.Name == namespace { - namespaceExists = true + for { + if err := cs.List(ctx, namespaces, client.Continue(namespaces.Continue)); err != nil { + return err + } + + for _, ns := range namespaces.Items { + if ns.Name == namespace { + namespaceExists = true + break + } + } + + if namespaces.Continue == "" { break } }