Skip to content

Commit 506cb9a

Browse files
authored
Merge pull request #2130 from fabriziopandini/clusterctl-move-cmd
✨clusterctl: add the clusterctl move command
2 parents 4f21ea6 + bb8a58c commit 506cb9a

File tree

6 files changed

+154
-9
lines changed

6 files changed

+154
-9
lines changed

cmd/clusterctl/cmd/move.go

+15
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20+
"fmt"
21+
2022
"github.com/pkg/errors"
2123
"github.com/spf13/cobra"
24+
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client"
2225
)
2326

2427
type moveOptions struct {
@@ -59,5 +62,17 @@ func init() {
5962
}
6063

6164
func runMove() error {
65+
c, err := client.New(cfgFile)
66+
if err != nil {
67+
return err
68+
}
69+
fmt.Println("Performing move...")
70+
if err := c.Move(client.MoveOptions{
71+
FromKubeconfig: mo.fromKubeconfig,
72+
ToKubeconfig: mo.toKubeconfig,
73+
Namespace: mo.namespace,
74+
}); err != nil {
75+
return err
76+
}
6277
return nil
6378
}

cmd/clusterctl/pkg/client/client.go

+17-7
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@ type DeleteOptions struct {
5454
Providers []string
5555
}
5656

57-
// Client is exposes the clusterctl high-level client library
57+
// MoveOptions carries the options supported by move.
58+
type MoveOptions struct {
59+
FromKubeconfig string
60+
ToKubeconfig string
61+
Namespace string
62+
}
63+
64+
// Client is exposes the clusterctl high-level client library.
5865
type Client interface {
5966
// GetProvidersConfig returns the list of providers configured for this instance of clusterctl.
6067
GetProvidersConfig() ([]Provider, error)
@@ -70,6 +77,9 @@ type Client interface {
7077

7178
// Delete deletes providers from a management cluster.
7279
Delete(options DeleteOptions) error
80+
81+
// Move moves all the Cluster API objects existing in a namespace (or from all the namespaces if empty) to a target management cluster.
82+
Move(options MoveOptions) error
7383
}
7484

7585
// clusterctlClient implements Client.
@@ -85,7 +95,7 @@ type ClusterClientFactory func(string) (cluster.Client, error)
8595
// Ensure clusterctlClient implements Client.
8696
var _ Client = &clusterctlClient{}
8797

88-
// NewOptions carries the options supported by New
98+
// NewOptions carries the options supported by New.
8999
type NewOptions struct {
90100
injectConfig config.Client
91101
injectRepositoryFactory RepositoryClientFactory
@@ -130,7 +140,7 @@ func newClusterctlClient(path string, options ...Option) (*clusterctlClient, err
130140
}
131141

132142
// if there is an injected config, use it, otherwise use the default one
133-
// provided by the config low level library
143+
// provided by the config low level library.
134144
configClient := cfg.injectConfig
135145
if configClient == nil {
136146
c, err := config.New(path)
@@ -140,13 +150,13 @@ func newClusterctlClient(path string, options ...Option) (*clusterctlClient, err
140150
configClient = c
141151
}
142152

143-
// if there is an injected RepositoryFactory, use it, otherwise use a default one
153+
// if there is an injected RepositoryFactory, use it, otherwise use a default one.
144154
repositoryClientFactory := cfg.injectRepositoryFactory
145155
if repositoryClientFactory == nil {
146156
repositoryClientFactory = defaultRepositoryFactory(configClient)
147157
}
148158

149-
// if there is an injected ClusterFactory, use it, otherwise use a default one
159+
// if there is an injected ClusterFactory, use it, otherwise use a default one.
150160
clusterClientFactory := cfg.injectClusterFactory
151161
if clusterClientFactory == nil {
152162
clusterClientFactory = defaultClusterFactory()
@@ -159,14 +169,14 @@ func newClusterctlClient(path string, options ...Option) (*clusterctlClient, err
159169
}, nil
160170
}
161171

162-
// defaultClusterFactory is a ClusterClientFactory func the uses the default client provided by the cluster low level library
172+
// defaultClusterFactory is a ClusterClientFactory func the uses the default client provided by the cluster low level library.
163173
func defaultClusterFactory() func(kubeconfig string) (cluster.Client, error) {
164174
return func(kubeconfig string) (cluster.Client, error) {
165175
return cluster.New(kubeconfig, cluster.Options{}), nil
166176
}
167177
}
168178

169-
// defaultRepositoryFactory is a RepositoryClientFactory func the uses the default client provided by the repository low level library
179+
// defaultRepositoryFactory is a RepositoryClientFactory func the uses the default client provided by the repository low level library.
170180
func defaultRepositoryFactory(configClient config.Client) func(providerConfig config.Provider) (repository.Client, error) {
171181
return func(providerConfig config.Provider) (repository.Client, error) {
172182
return repository.New(providerConfig, configClient.Variables(), repository.Options{})

cmd/clusterctl/pkg/client/client_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ func (f fakeClient) Delete(options DeleteOptions) error {
8282
return f.internalClient.Delete(options)
8383
}
8484

85+
func (f fakeClient) Move(options MoveOptions) error {
86+
return f.internalClient.Move(options)
87+
}
88+
8589
// newFakeClient returns a clusterctl client that allows to execute tests on a set of fake config, fake repositories and fake clusters.
8690
// you can use WithCluster and WithRepository to prepare for the test case.
8791
func newFakeClient(configClient config.Client) *fakeClient {

cmd/clusterctl/pkg/client/move.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package client
18+
19+
func (c *clusterctlClient) Move(options MoveOptions) error {
20+
// Get the client for interacting with the source management cluster.
21+
fromCluster, err := c.clusterClientFactory(options.FromKubeconfig)
22+
if err != nil {
23+
return err
24+
}
25+
26+
// Ensures the custom resource definitions required by clusterctl are in place.
27+
if err := fromCluster.ProviderInventory().EnsureCustomResourceDefinitions(); err != nil {
28+
return err
29+
}
30+
31+
// Get the client for interacting with the target management cluster.
32+
toCluster, err := c.clusterClientFactory(options.ToKubeconfig)
33+
if err != nil {
34+
return err
35+
}
36+
37+
// Ensures the custom resource definitions required by clusterctl are in place
38+
if err := toCluster.ProviderInventory().EnsureCustomResourceDefinitions(); err != nil {
39+
return err
40+
}
41+
42+
if err := fromCluster.ObjectMover().Move(options.Namespace, toCluster); err != nil {
43+
return err
44+
}
45+
46+
return nil
47+
}
+35
Original file line numberDiff line numberDiff line change
@@ -1 +1,36 @@
11
# clusterctl move
2+
3+
The `clusterctl move` command allows to move the Cluster API objects defining workload clusters, like e.g. Cluster, Machines,
4+
MachineDeployments, etc. from one management cluster to another management cluster.
5+
6+
<aside class="note warning">
7+
8+
<h1> Warning </h1>
9+
10+
Before running `clusterctl move`, the user should take care of preparing the target management cluster, including also installing
11+
all the required provider using `clusterctl init`.
12+
13+
The version of the providers installed in the target management cluster should be at least the same version of the
14+
corresponding provider in the source cluster.
15+
16+
</aside>
17+
18+
You can use:
19+
20+
```shell
21+
clusterctl move --to-kubeconfig="path-to-target-kubeconfig.yaml"
22+
```
23+
24+
To move all the Cluster API objects objects in the source management cluster; in case if you want to move only the
25+
Cluster API objects defined in a specific namespace, you can use the `--namespace` flag.
26+
27+
<aside class="note">
28+
29+
<h1> Pause Reconciliation </h1>
30+
31+
Before moving a `Cluster`, clusterctl sets the `Cluster.Spec.Paused` field to `true` stopping
32+
the controllers to reconcile the workload cluster _in the source management cluster_.
33+
34+
The `Cluster` object created in the target management cluster instead will be actively reconciled.
35+
36+
</aside>

docs/book/src/clusterctl/provider-contract.md

+36-2
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,25 @@ Templates writers should use the common variables to ensure consistency across p
180180
Additionally, value of the command argument to `clusterctl config cluster <cluster-name>` (`<cluster-name>` in this case), will
181181
be applied to every occurrence of the `${ CLUSTER_NAME }` variable.
182182

183+
## OwnerReferences chain
184+
185+
Each provider is responsible to ensure that all the providers resources (like e.g. `VSphereCluster`, `VSphereMachine`, `VSphereVM` etc.
186+
for the `vsphere` provider) MUST have a `Metadata.OwnerReferences` entry that links directly or indirectly to a `Cluster` object.
187+
188+
Please note that all the provider specific resources that are referenced by the Cluster API core objects will get the `OwnerReference`
189+
sets by the Cluster API core controllers, e.g.:
190+
191+
- The Cluster controller ensures that all the objects referenced in `Cluster.Spec.InfrastructureRef` get an `OwnerReference`
192+
that links directly to the corresponding `Cluster`.
193+
- The Machine controller ensures that all the objects referenced in `Machine.Spec.InfrastructureRef` get an `OwnerReference`
194+
that links to the corresponding `Machine`, and the `Machine` is linked to the `Cluster` through its own `OwnerReference` chain.
195+
196+
That means that, practically speaking, provider implementers are responsible for ensuring that the `OwnerReference`s
197+
are set only for objects that are not directly referenced by Cluster API core objects, e.g.:
198+
199+
- All the `VSphereVM` instances should get an `OwnerReference` that links to the corresponding `VSphereMachine`, and the `VSphereMachine`
200+
is linked to the `Cluster` through its own `OwnerReference` chain.
201+
183202
## Additional notes
184203

185204
### Components YAML transformations
@@ -216,10 +235,25 @@ If, for any reason, the provider authors/YAML designers decide not to comply wit
216235
The provider authors/YAML designers should be aware that it is their responsibility to ensure the proper
217236
functioning of all the `clusterctl` features both in single tenancy or multi-tenancy scenarios and/or document known limitations.
218237

219-
### Move constraints
238+
### Move
220239

221-
WIP
240+
Provider authors should be aware that `clusterctl move` command implements a discovery mechanism that considers:
241+
242+
* All the objects of Kind defined in one of the CRDs installed by clusterctl using `clusterctl init`.
243+
* `Secret` and `ConfigMap` objects.
244+
* the `OwnerReference` chain of the above objects.
245+
246+
`clusterctl move` does NOT consider any objects:
222247

248+
* Not included in the set of objects defined above.
249+
* Included in the set of objects defined above, but not directly or indirectly to a `Cluster` object through the `OwnerReference` chain.
250+
251+
If moving some of excluded object is required, the provider authors should create documentation describing the
252+
the exact move sequence to be executed by the user.
253+
254+
Additionally, provider authors should be aware that `clusterctl move` assumes all the provider's Controllers respect the
255+
`Cluster.Spec.Paused` field introduced in the v1alpha3 Cluster API specification.
256+
223257
### Adopt
224258

225259
WIP

0 commit comments

Comments
 (0)