Skip to content

Commit 763d391

Browse files
authored
Merge pull request #16936 from justinsb/filter_instance_groups
Allow updating the cluster one instance group at a time
2 parents 6bb024d + b4306a3 commit 763d391

File tree

15 files changed

+189
-54
lines changed

15 files changed

+189
-54
lines changed

cmd/kops/update_cluster.go

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ import (
2727

2828
"github.com/spf13/cobra"
2929
"github.com/spf13/viper"
30+
"k8s.io/apimachinery/pkg/util/sets"
3031
"k8s.io/client-go/tools/clientcmd"
3132
"k8s.io/klog/v2"
3233
"k8s.io/kops/cmd/kops/util"
3334
"k8s.io/kops/pkg/apis/kops"
3435
"k8s.io/kops/pkg/assets"
3536
"k8s.io/kops/pkg/commands/commandutils"
3637
"k8s.io/kops/pkg/kubeconfig"
38+
"k8s.io/kops/pkg/predicates"
3739
"k8s.io/kops/upup/pkg/fi"
3840
"k8s.io/kops/upup/pkg/fi/cloudup"
3941
"k8s.io/kops/upup/pkg/fi/utils"
@@ -75,6 +77,14 @@ type UpdateClusterOptions struct {
7577
user string
7678
internal bool
7779

80+
// InstanceGroups is the list of instance groups to update;
81+
// if not specified, all instance groups will be updated
82+
InstanceGroups []string
83+
84+
// InstanceGroupRoles is the list of roles we should update
85+
// if not specified, all instance groups will be updated
86+
InstanceGroupRoles []string
87+
7888
Phase string
7989

8090
// LifecycleOverrides is a slice of taskName=lifecycle name values. This slice is used
@@ -106,6 +116,11 @@ func NewCmdUpdateCluster(f *util.Factory, out io.Writer) *cobra.Command {
106116
options := &UpdateClusterOptions{}
107117
options.InitDefaults()
108118

119+
allRoles := make([]string, 0, len(kops.AllInstanceGroupRoles))
120+
for _, r := range kops.AllInstanceGroupRoles {
121+
allRoles = append(allRoles, r.ToLowerString())
122+
}
123+
109124
cmd := &cobra.Command{
110125
Use: "cluster [CLUSTER]",
111126
Short: updateClusterShort,
@@ -132,6 +147,12 @@ func NewCmdUpdateCluster(f *util.Factory, out io.Writer) *cobra.Command {
132147
cmd.RegisterFlagCompletionFunc("user", completeKubecfgUser)
133148
cmd.Flags().BoolVar(&options.internal, "internal", options.internal, "Use the cluster's internal DNS name. Implies --create-kube-config")
134149
cmd.Flags().BoolVar(&options.AllowKopsDowngrade, "allow-kops-downgrade", options.AllowKopsDowngrade, "Allow an older version of kOps to update the cluster than last used")
150+
cmd.Flags().StringSliceVar(&options.InstanceGroups, "instance-group", options.InstanceGroups, "Instance groups to update (defaults to all if not specified)")
151+
cmd.RegisterFlagCompletionFunc("instance-group", completeInstanceGroup(f, &options.InstanceGroups, &options.InstanceGroupRoles))
152+
cmd.Flags().StringSliceVar(&options.InstanceGroupRoles, "instance-group-roles", options.InstanceGroupRoles, "Instance group roles to update ("+strings.Join(allRoles, ",")+")")
153+
cmd.RegisterFlagCompletionFunc("instance-group-roles", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
154+
return sets.NewString(allRoles...).Delete(options.InstanceGroupRoles...).List(), cobra.ShellCompDirectiveNoFileComp
155+
})
135156
cmd.Flags().StringVar(&options.Phase, "phase", options.Phase, "Subset of tasks to run: "+strings.Join(cloudup.Phases.List(), ", "))
136157
cmd.RegisterFlagCompletionFunc("phase", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
137158
return cloudup.Phases.List(), cobra.ShellCompDirectiveNoFileComp
@@ -285,24 +306,32 @@ func RunUpdateCluster(ctx context.Context, f *util.Factory, out io.Writer, c *Up
285306
lifecycleOverrideMap[taskName] = lifecycleOverride
286307
}
287308

309+
var instanceGroupFilters []predicates.Predicate[*kops.InstanceGroup]
310+
if len(c.InstanceGroups) != 0 {
311+
instanceGroupFilters = append(instanceGroupFilters, matchInstanceGroupNames(c.InstanceGroups))
312+
} else if len(c.InstanceGroupRoles) != 0 {
313+
instanceGroupFilters = append(instanceGroupFilters, matchInstanceGroupRoles(c.InstanceGroupRoles))
314+
}
315+
288316
cloud, err := cloudup.BuildCloud(cluster)
289317
if err != nil {
290318
return nil, err
291319
}
292320

293321
applyCmd := &cloudup.ApplyClusterCmd{
294-
Cloud: cloud,
295-
Clientset: clientset,
296-
Cluster: cluster,
297-
DryRun: isDryrun,
298-
AllowKopsDowngrade: c.AllowKopsDowngrade,
299-
RunTasksOptions: &c.RunTasksOptions,
300-
OutDir: c.OutDir,
301-
Phase: phase,
302-
TargetName: targetName,
303-
LifecycleOverrides: lifecycleOverrideMap,
304-
GetAssets: c.GetAssets,
305-
DeletionProcessing: deletionProcessing,
322+
Cloud: cloud,
323+
Clientset: clientset,
324+
Cluster: cluster,
325+
DryRun: isDryrun,
326+
AllowKopsDowngrade: c.AllowKopsDowngrade,
327+
RunTasksOptions: &c.RunTasksOptions,
328+
OutDir: c.OutDir,
329+
InstanceGroupFilter: predicates.AllOf(instanceGroupFilters...),
330+
Phase: phase,
331+
TargetName: targetName,
332+
LifecycleOverrides: lifecycleOverrideMap,
333+
GetAssets: c.GetAssets,
334+
DeletionProcessing: deletionProcessing,
306335
}
307336

308337
applyResults, err := applyCmd.Run(ctx)
@@ -518,3 +547,31 @@ func completeLifecycleOverrides(cmd *cobra.Command, args []string, toComplete st
518547
}
519548
return completions, cobra.ShellCompDirectiveNoFileComp
520549
}
550+
551+
// matchInstanceGroupNames returns a predicate that matches instance groups by name
552+
func matchInstanceGroupNames(names []string) predicates.Predicate[*kops.InstanceGroup] {
553+
return func(ig *kops.InstanceGroup) bool {
554+
for _, name := range names {
555+
if ig.ObjectMeta.Name == name {
556+
return true
557+
}
558+
}
559+
return false
560+
}
561+
}
562+
563+
// matchInstanceGroupRoles returns a predicate that matches instance groups by role
564+
func matchInstanceGroupRoles(roles []string) predicates.Predicate[*kops.InstanceGroup] {
565+
return func(ig *kops.InstanceGroup) bool {
566+
for _, role := range roles {
567+
instanceGroupRole, ok := kops.ParseInstanceGroupRole(role, true)
568+
if !ok {
569+
continue
570+
}
571+
if ig.Spec.Role == instanceGroupRole {
572+
return true
573+
}
574+
}
575+
return false
576+
}
577+
}

docs/cli/kops_update_cluster.md

Lines changed: 15 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/model/awsmodel/autoscalinggroup_test.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ func TestRootVolumeOptimizationFlag(t *testing.T) {
6565
b := AutoscalingGroupModelBuilder{
6666
AWSModelContext: &AWSModelContext{
6767
KopsModelContext: &model.KopsModelContext{
68-
IAMModelContext: iam.IAMModelContext{Cluster: cluster},
69-
SSHPublicKeys: k,
70-
InstanceGroups: igs,
68+
IAMModelContext: iam.IAMModelContext{Cluster: cluster},
69+
SSHPublicKeys: k,
70+
AllInstanceGroups: igs,
71+
InstanceGroups: igs,
7172
},
7273
},
7374
BootstrapScriptBuilder: &model.BootstrapScriptBuilder{
@@ -177,15 +178,17 @@ func TestAPIServerAdditionalSecurityGroupsWithNLB(t *testing.T) {
177178
b := AutoscalingGroupModelBuilder{
178179
AWSModelContext: &AWSModelContext{
179180
KopsModelContext: &model.KopsModelContext{
180-
IAMModelContext: iam.IAMModelContext{Cluster: cluster},
181-
SSHPublicKeys: [][]byte{[]byte(sshPublicKeyEntry)},
182-
InstanceGroups: igs,
181+
IAMModelContext: iam.IAMModelContext{Cluster: cluster},
182+
SSHPublicKeys: [][]byte{[]byte(sshPublicKeyEntry)},
183+
AllInstanceGroups: igs,
184+
InstanceGroups: igs,
183185
},
184186
},
185187
BootstrapScriptBuilder: &model.BootstrapScriptBuilder{
186188
KopsModelContext: &model.KopsModelContext{
187-
IAMModelContext: iam.IAMModelContext{Cluster: cluster},
188-
InstanceGroups: igs,
189+
IAMModelContext: iam.IAMModelContext{Cluster: cluster},
190+
AllInstanceGroups: igs,
191+
InstanceGroups: igs,
189192
},
190193
Lifecycle: fi.LifecycleSync,
191194
},

pkg/model/awsmodel/iam.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (b *IAMModelBuilder) Build(c *fi.CloudupModelBuilderContext) error {
6565

6666
// Collect Instance Profile ARNs and their associated Instance Group roles
6767
sharedProfileARNsToIGRole := make(map[string]kops.InstanceGroupRole)
68-
for _, ig := range b.InstanceGroups {
68+
for _, ig := range b.AllInstanceGroups {
6969
if ig.Spec.IAM != nil && ig.Spec.IAM.Profile != nil {
7070
specProfile := fi.ValueOf(ig.Spec.IAM.Profile)
7171
if matchingRole, ok := sharedProfileARNsToIGRole[specProfile]; ok {

pkg/model/azuremodel/testing.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ func newTestAzureModelContext() *AzureModelContext {
3232
IAMModelContext: iam.IAMModelContext{
3333
Cluster: cluster,
3434
},
35-
InstanceGroups: []*kops.InstanceGroup{ig},
36-
SSHPublicKeys: [][]byte{[]byte("ssh-rsa ...")},
35+
AllInstanceGroups: []*kops.InstanceGroup{ig},
36+
InstanceGroups: []*kops.InstanceGroup{ig},
37+
SSHPublicKeys: [][]byte{[]byte("ssh-rsa ...")},
3738
},
3839
}
3940
}

pkg/model/bootstrapscript_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,9 @@ func TestBootstrapUserData(t *testing.T) {
170170

171171
bs := &BootstrapScriptBuilder{
172172
KopsModelContext: &KopsModelContext{
173-
IAMModelContext: iam.IAMModelContext{Cluster: cluster},
174-
InstanceGroups: []*kops.InstanceGroup{group},
173+
IAMModelContext: iam.IAMModelContext{Cluster: cluster},
174+
AllInstanceGroups: []*kops.InstanceGroup{group},
175+
InstanceGroups: []*kops.InstanceGroup{group},
175176
},
176177
NodeUpConfigBuilder: &nodeupConfigBuilder{cluster: cluster},
177178
NodeUpAssets: map[architectures.Architecture]*assets.MirroredAsset{

pkg/model/components/etcdmanager/model_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ func LoadKopsModelContext(basedir string) (*model.KopsModelContext, error) {
8181
}
8282

8383
kopsContext := &model.KopsModelContext{
84-
IAMModelContext: iam.IAMModelContext{Cluster: spec.Cluster},
85-
InstanceGroups: spec.InstanceGroups,
84+
IAMModelContext: iam.IAMModelContext{Cluster: spec.Cluster},
85+
AllInstanceGroups: spec.InstanceGroups,
86+
InstanceGroups: spec.InstanceGroups,
8687
}
8788

8889
return kopsContext, nil

pkg/model/components/kubeapiserver/model_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ func LoadKopsModelContext(basedir string) (*model.KopsModelContext, error) {
7474
}
7575

7676
kopsContext := &model.KopsModelContext{
77-
IAMModelContext: iam.IAMModelContext{Cluster: spec.Cluster},
78-
InstanceGroups: spec.InstanceGroups,
77+
IAMModelContext: iam.IAMModelContext{Cluster: spec.Cluster},
78+
AllInstanceGroups: spec.InstanceGroups,
79+
InstanceGroups: spec.InstanceGroups,
7980
}
8081

8182
return kopsContext, nil

pkg/model/components/kubescheduler/model_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ func LoadKopsModelContext(basedir string) (*model.KopsModelContext, error) {
7777
}
7878

7979
kopsContext := &model.KopsModelContext{
80-
IAMModelContext: iam.IAMModelContext{Cluster: spec.Cluster},
81-
InstanceGroups: spec.InstanceGroups,
80+
IAMModelContext: iam.IAMModelContext{Cluster: spec.Cluster},
81+
AllInstanceGroups: spec.InstanceGroups,
82+
InstanceGroups: spec.InstanceGroups,
8283
}
8384

8485
for _, u := range spec.AdditionalObjects {

pkg/model/context.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,18 @@ const (
4747
// KopsModelContext is the kops model
4848
type KopsModelContext struct {
4949
iam.IAMModelContext
50+
51+
// AllInstanceGroups is the list of instance groups in the cluster.
52+
// Generally most tasks should use InstanceGroups instead,
53+
// but we sometimes need the full list for example when configuring cluster-wide IAM.
54+
AllInstanceGroups []*kops.InstanceGroup
55+
56+
// InstanceGroups is the list of instance groups in the cluster that are being processed.
57+
// This is a filtered list of AllInstanceGroups.
5058
InstanceGroups []*kops.InstanceGroup
51-
Region string
52-
SSHPublicKeys [][]byte
59+
60+
Region string
61+
SSHPublicKeys [][]byte
5362

5463
// AdditionalObjects holds cluster-asssociated configuration objects, other than the Cluster and InstanceGroups.
5564
AdditionalObjects kubemanifest.ObjectList
@@ -92,7 +101,7 @@ func (b *KopsModelContext) GatherSubnets(ig *kops.InstanceGroup) ([]*kops.Cluste
92101

93102
// FindInstanceGroup returns the instance group with the matching Name (or nil if not found)
94103
func (b *KopsModelContext) FindInstanceGroup(name string) *kops.InstanceGroup {
95-
for _, ig := range b.InstanceGroups {
104+
for _, ig := range b.AllInstanceGroups {
96105
if ig.ObjectMeta.Name == name {
97106
return ig
98107
}

0 commit comments

Comments
 (0)