Skip to content

Commit 8b7ffa5

Browse files
authored
Merge pull request #2281 from fabriziopandini/clusterctl-upgrade-plan-apply
✨clusterctl: implement upgrade apply
2 parents 884c809 + 81695dc commit 8b7ffa5

File tree

11 files changed

+572
-51
lines changed

11 files changed

+572
-51
lines changed

cmd/clusterctl/cmd/upgrade.go

+50-1
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,44 @@ var upgradePlanCmd = &cobra.Command{
6262
},
6363
}
6464

65+
type upgradeApplyOptions struct {
66+
kubeconfig string
67+
managementGroup string
68+
contract string
69+
}
70+
71+
var ua = &upgradeApplyOptions{}
72+
73+
var upgradeApplyCmd = &cobra.Command{
74+
Use: "apply",
75+
Short: "Applies new versions of Cluster API providers in a management cluster",
76+
Long: LongDesc(`
77+
The upgrade apply command applies new versions of Cluster API providers as defined by clusterctl upgrade plan.
78+
79+
New version should be applied for each management groups, ensuring all the providers on the same cluster API version
80+
in order to guarantee the proper functioning of the management cluster.`),
81+
82+
Example: Examples(`
83+
# Upgrades all the providers in the capi-system/cluster-api to the latest version available which is compliant
84+
# to the v1alpha3 API Version of Cluster API (contract).
85+
clusterctl upgrade apply --management-group capi-system/cluster-api --contract v1alpha3`),
86+
87+
RunE: func(cmd *cobra.Command, args []string) error {
88+
return runUpgradeApply()
89+
},
90+
}
91+
6592
func init() {
6693
upgradePlanCmd.Flags().StringVarP(&up.kubeconfig, "kubeconfig", "", "", "Path to the kubeconfig file to use for accessing the management cluster. If empty, default rules for kubeconfig discovery will be used")
6794

6895
upgradeCmd.AddCommand(upgradePlanCmd)
6996

97+
upgradeApplyCmd.Flags().StringVarP(&ua.kubeconfig, "kubeconfig", "", "", "Path to the kubeconfig file to use for accessing the management cluster. If empty, default rules for kubeconfig discovery will be used")
98+
upgradeApplyCmd.Flags().StringVarP(&ua.managementGroup, "management-group", "", "", "The management group that should be upgraded")
99+
upgradeApplyCmd.Flags().StringVarP(&ua.contract, "contract", "", "", "The API Version of Cluster API (contract) the management group should upgrade to")
100+
101+
upgradeCmd.AddCommand(upgradeApplyCmd)
102+
70103
RootCmd.AddCommand(upgradeCmd)
71104
}
72105

@@ -114,7 +147,7 @@ func runUpgradePlan() error {
114147
if upgradeAvailable {
115148
fmt.Println("You can now apply the upgrade by executing the following command:")
116149
fmt.Println("")
117-
fmt.Println(fmt.Sprintf(" upgrade apply --management-group %s --cluster-api-version %s", plan.CoreProvider.InstanceName(), plan.Contract))
150+
fmt.Println(fmt.Sprintf(" upgrade apply --management-group %s --contract %s", plan.CoreProvider.InstanceName(), plan.Contract))
118151
} else {
119152
fmt.Println("You are already up to date!")
120153
}
@@ -146,3 +179,19 @@ func prettifyTargetVersion(version string) string {
146179
}
147180
return version
148181
}
182+
183+
func runUpgradeApply() error {
184+
c, err := client.New(cfgFile)
185+
if err != nil {
186+
return err
187+
}
188+
189+
if err := c.ApplyUpgrade(client.ApplyUpgradeOptions{
190+
Kubeconfig: ua.kubeconfig,
191+
ManagementGroup: ua.managementGroup,
192+
Contract: ua.contract,
193+
}); err != nil {
194+
return err
195+
}
196+
return nil
197+
}

cmd/clusterctl/pkg/client/client.go

+3-6
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,6 @@ type MoveOptions struct {
120120
Namespace string
121121
}
122122

123-
// PlanUpgradeOptions carries the options supported by upgrade plan.
124-
type PlanUpgradeOptions struct {
125-
// Kubeconfig file to use for accessing the management cluster. If empty, default rules for kubeconfig discovery will be used.
126-
Kubeconfig string
127-
}
128-
129123
// Client is exposes the clusterctl high-level client library.
130124
type Client interface {
131125
// GetProvidersConfig returns the list of providers configured for this instance of clusterctl.
@@ -152,6 +146,9 @@ type Client interface {
152146
// - Upgrade to the latest version in the the v1alpha2 series: ....
153147
// - Upgrade to the latest version in the the v1alpha3 series: ....
154148
PlanUpgrade(options PlanUpgradeOptions) ([]UpgradePlan, error)
149+
150+
// ApplyUpgrade executes an upgrade plan.
151+
ApplyUpgrade(options ApplyUpgradeOptions) error
155152
}
156153

157154
// clusterctlClient implements Client.

cmd/clusterctl/pkg/client/client_test.go

+39-10
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func TestNewFakeClient(t *testing.T) {
4646
WithFile("v1.0", "components.yaml", []byte("content"))
4747

4848
// create a fake cluster, eventually adding some existing runtime objects to it
49-
cluster1 := newFakeCluster("cluster1").
49+
cluster1 := newFakeCluster("cluster1", config1).
5050
WithObjs()
5151

5252
// create a new fakeClient that allows to execute tests on the fake config, the fake repositories and the fake cluster.
@@ -92,6 +92,10 @@ func (f fakeClient) PlanUpgrade(options PlanUpgradeOptions) ([]UpgradePlan, erro
9292
return f.internalClient.PlanUpgrade(options)
9393
}
9494

95+
func (f fakeClient) ApplyUpgrade(options ApplyUpgradeOptions) error {
96+
return f.internalClient.ApplyUpgrade(options)
97+
}
98+
9599
// newFakeClient returns a clusterctl client that allows to execute tests on a set of fake config, fake repositories and fake clusters.
96100
// you can use WithCluster and WithRepository to prepare for the test case.
97101
func newFakeClient(configClient config.Client) *fakeClient {
@@ -143,20 +147,29 @@ func (f *fakeClient) WithRepository(repositoryClient repository.Client) *fakeCli
143147
// newFakeCluster returns a fakeClusterClient that
144148
// internally uses a FakeProxy (based on the controller-runtime FakeClient).
145149
// You can use WithObjs to pre-load a set of runtime objects in the cluster.
146-
func newFakeCluster(kubeconfig string) *fakeClusterClient {
147-
configClient := newFakeConfig()
148-
fakeProxy := test.NewFakeProxy()
150+
func newFakeCluster(kubeconfig string, configClient config.Client) *fakeClusterClient {
151+
fake := &fakeClusterClient{
152+
kubeconfig: kubeconfig,
153+
repositories: map[string]repository.Client{},
154+
}
155+
156+
fake.fakeProxy = test.NewFakeProxy()
149157
pollImmediateWaiter := func(interval, timeout time.Duration, condition wait.ConditionFunc) error {
150158
return nil
151159
}
152160

153-
client := cluster.New("", configClient, cluster.InjectProxy(fakeProxy), cluster.InjectPollImmediateWaiter(pollImmediateWaiter))
161+
fake.internalclient = cluster.New("", configClient,
162+
cluster.InjectProxy(fake.fakeProxy),
163+
cluster.InjectPollImmediateWaiter(pollImmediateWaiter),
164+
cluster.InjectRepositoryFactory(func(provider config.Provider, configVariablesClient config.VariablesClient, options ...repository.Option) (repository.Client, error) {
165+
if _, ok := fake.repositories[provider.Name()]; !ok {
166+
return nil, errors.Errorf("Repository for kubeconfig %q does not exists.", provider.Name())
167+
}
168+
return fake.repositories[provider.Name()], nil
169+
}),
170+
)
154171

155-
return &fakeClusterClient{
156-
kubeconfig: kubeconfig,
157-
fakeProxy: fakeProxy,
158-
internalclient: client,
159-
}
172+
return fake
160173
}
161174

162175
type fakeCertManagerClient struct {
@@ -172,6 +185,7 @@ func (p *fakeCertManagerClient) EnsureWebHook() error {
172185
type fakeClusterClient struct {
173186
kubeconfig string
174187
fakeProxy *test.FakeProxy
188+
repositories map[string]repository.Client
175189
internalclient cluster.Client
176190
}
177191

@@ -219,6 +233,11 @@ func (f *fakeClusterClient) WithProviderInventory(name string, providerType clus
219233
return f
220234
}
221235

236+
func (f *fakeClusterClient) WithRepository(repositoryClient repository.Client) *fakeClusterClient {
237+
f.repositories[repositoryClient.Name()] = repositoryClient
238+
return f
239+
}
240+
222241
// newFakeConfig return a fake implementation of the client for low-level config library.
223242
// The implementation uses a FakeReader that stores configuration settings in a map; you can use
224243
// the WithVar or WithProvider methods to set the map values.
@@ -315,6 +334,16 @@ func (f *fakeRepositoryClient) WithDefaultVersion(version string) *fakeRepositor
315334
return f
316335
}
317336

337+
func (f *fakeRepositoryClient) WithVersions(version ...string) *fakeRepositoryClient {
338+
f.fakeRepository.WithVersions(version...)
339+
return f
340+
}
341+
342+
func (f *fakeRepositoryClient) WithMetadata(version string, metadata *clusterctlv1.Metadata) *fakeRepositoryClient {
343+
f.fakeRepository.WithMetadata(version, metadata)
344+
return f
345+
}
346+
318347
func (f *fakeRepositoryClient) WithFile(version, path string, content []byte) *fakeRepositoryClient {
319348
f.fakeRepository.WithFile(version, path, content)
320349
return f

cmd/clusterctl/pkg/client/cluster/client.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func (c *clusterClient) ObjectMover() ObjectMover {
114114
}
115115

116116
func (c *clusterClient) ProviderUpgrader() ProviderUpgrader {
117-
return newProviderUpgrader(c.configClient, c.repositoryClientFactory, c.ProviderInventory())
117+
return newProviderUpgrader(c.configClient, c.repositoryClientFactory, c.ProviderInventory(), c.ProviderComponents())
118118
}
119119

120120
// Option is a configuration option supplied to New

cmd/clusterctl/pkg/client/cluster/installer.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,7 @@ func (i *providerInstaller) Add(components repository.Components) error {
5353
func (i *providerInstaller) Install() ([]repository.Components, error) {
5454
ret := make([]repository.Components, 0, len(i.installQueue))
5555
for _, components := range i.installQueue {
56-
if err := i.providerComponents.Create(components); err != nil {
57-
return nil, err
58-
}
59-
60-
if err := i.providerInventory.Create(components.InventoryObject()); err != nil {
56+
if err := installComponentsAndUpdateInventory(components, i.providerComponents, i.providerInventory); err != nil {
6157
return nil, err
6258
}
6359

@@ -66,6 +62,18 @@ func (i *providerInstaller) Install() ([]repository.Components, error) {
6662
return ret, nil
6763
}
6864

65+
func installComponentsAndUpdateInventory(components repository.Components, providerComponents ComponentsClient, providerInventory InventoryClient) error {
66+
if err := providerComponents.Create(components); err != nil {
67+
return err
68+
}
69+
70+
if err := providerInventory.Create(components.InventoryObject()); err != nil {
71+
return err
72+
}
73+
74+
return nil
75+
}
76+
6977
func newProviderInstaller(proxy Proxy, providerMetadata InventoryClient, providerComponents ComponentsClient) *providerInstaller {
7078
return &providerInstaller{
7179
proxy: proxy,

0 commit comments

Comments
 (0)