Skip to content

🐛clusterctl: move ensure all the namespaces exists #2245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
79 changes: 79 additions & 0 deletions cmd/clusterctl/pkg/client/cluster/mover.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -229,6 +236,78 @@ 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{}
namespaceExists := false
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
}
}
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
Expand Down