-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #670 from weaveworks/move-nodegroup-filters
Move nodegroup filters
- Loading branch information
Showing
8 changed files
with
529 additions
and
439 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package cmdutils | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/gobwas/glob" | ||
"github.com/kris-nova/logger" | ||
"github.com/pkg/errors" | ||
|
||
"k8s.io/apimachinery/pkg/util/sets" | ||
|
||
api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha4" | ||
"github.com/weaveworks/eksctl/pkg/cfn/manager" | ||
) | ||
|
||
// NodeGroupFilter holds filter configuration | ||
type NodeGroupFilter struct { | ||
IgnoreAllExisting bool | ||
|
||
existing sets.String | ||
only []glob.Glob | ||
onlySpec string | ||
} | ||
|
||
// NewNodeGroupFilter create new NodeGroupFilter instance | ||
func NewNodeGroupFilter() *NodeGroupFilter { | ||
return &NodeGroupFilter{ | ||
IgnoreAllExisting: true, | ||
|
||
existing: sets.NewString(), | ||
} | ||
} | ||
|
||
// ApplyOnlyFilter parses given globs for exclusive filtering | ||
func (f *NodeGroupFilter) ApplyOnlyFilter(globExprs []string, cfg *api.ClusterConfig) error { | ||
for _, expr := range globExprs { | ||
compiledExpr, err := glob.Compile(expr) | ||
if err != nil { | ||
return errors.Wrapf(err, "parsing glob filter %q", expr) | ||
} | ||
f.only = append(f.only, compiledExpr) | ||
} | ||
f.onlySpec = strings.Join(globExprs, ",") | ||
return f.onlyFilterMatchesAnything(cfg) | ||
} | ||
|
||
func (f *NodeGroupFilter) onlyFilterMatchesAnything(cfg *api.ClusterConfig) error { | ||
if len(f.only) == 0 { | ||
return nil | ||
} | ||
for _, ng := range cfg.NodeGroups { | ||
for _, compiledExpr := range f.only { | ||
if compiledExpr.Match(ng.Name) { | ||
return nil | ||
} | ||
} | ||
} | ||
return fmt.Errorf("no nodegroups match filter specification: %q", f.onlySpec) | ||
} | ||
|
||
// ApplyExistingFilter uses stackManager to list existing nodegroup stacks and configures | ||
// the filter accordingly | ||
func (f *NodeGroupFilter) ApplyExistingFilter(stackManager *manager.StackCollection) error { | ||
if !f.IgnoreAllExisting { | ||
return nil | ||
} | ||
|
||
existing, err := stackManager.ListNodeGroupStacks() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
f.existing.Insert(existing...) | ||
|
||
return nil | ||
} | ||
|
||
// Match checks given nodegroup against the filter | ||
func (f *NodeGroupFilter) Match(ng *api.NodeGroup) bool { | ||
if f.IgnoreAllExisting && f.existing.Has(ng.Name) { | ||
return false | ||
} | ||
|
||
for _, compiledExpr := range f.only { | ||
if compiledExpr.Match(ng.Name) { | ||
return true // return first match | ||
} | ||
} | ||
|
||
// if no globs were given, match everything, | ||
// if false - we haven't matched anything so far | ||
return len(f.only) == 0 | ||
} | ||
|
||
// MatchAll checks all nodegroups against the filter and returns all of | ||
// matching names as set | ||
func (f *NodeGroupFilter) MatchAll(cfg *api.ClusterConfig) sets.String { | ||
names := sets.NewString() | ||
for _, ng := range cfg.NodeGroups { | ||
if f.Match(ng) { | ||
names.Insert(ng.Name) | ||
} | ||
} | ||
return names | ||
} | ||
|
||
// LogInfo prints out a user-friendly message about how filter was applied | ||
func (f *NodeGroupFilter) LogInfo(cfg *api.ClusterConfig) { | ||
count := f.MatchAll(cfg).Len() | ||
filteredOutCount := len(cfg.NodeGroups) - count | ||
if filteredOutCount > 0 { | ||
reasons := []string{} | ||
if f.onlySpec != "" { | ||
reasons = append(reasons, fmt.Sprintf("--only=%q was given", f.onlySpec)) | ||
} | ||
if existingCount := f.existing.Len(); existingCount > 0 { | ||
reasons = append(reasons, fmt.Sprintf("%d nodegroup(s) (%s) already exist", existingCount, strings.Join(f.existing.List(), ", "))) | ||
} | ||
logger.Info("%d nodegroup(s) were filtered out: %s", filteredOutCount, strings.Join(reasons, ", ")) | ||
} | ||
} | ||
|
||
// CheckEachNodeGroup iterates over each nodegroup and calls check function if it matches the filter | ||
func (f *NodeGroupFilter) CheckEachNodeGroup(nodeGroups []*api.NodeGroup, check func(i int, ng *api.NodeGroup) error) error { | ||
for i, ng := range nodeGroups { | ||
if f.Match(ng) { | ||
if err := check(i, ng); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package cmdutils_test | ||
|
||
import ( | ||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
|
||
api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha4" | ||
|
||
. "github.com/weaveworks/eksctl/pkg/ctl/cmdutils" | ||
) | ||
|
||
var _ = Describe("nodegroup filter", func() { | ||
|
||
Context("CheckEachNodeGroup", func() { | ||
|
||
It("should iterate over unique nodegroups", func() { | ||
cfg := newClusterConfig() | ||
addGroupA(cfg) | ||
|
||
filter := NewNodeGroupFilter() | ||
names := []string{} | ||
|
||
filter.CheckEachNodeGroup(cfg.NodeGroups, func(i int, nodeGroup *api.NodeGroup) error { | ||
Expect(nodeGroup).To(Equal(cfg.NodeGroups[i])) | ||
names = append(names, nodeGroup.Name) | ||
return nil | ||
}) | ||
Expect(names).To(Equal([]string{"test-ng1a", "test-ng2a", "test-ng3a"})) | ||
|
||
names = []string{} | ||
cfg.NodeGroups[0].Name = "ng-x0" | ||
cfg.NodeGroups[1].Name = "ng-x1" | ||
cfg.NodeGroups[2].Name = "ng-x2" | ||
|
||
filter.CheckEachNodeGroup(cfg.NodeGroups, func(i int, nodeGroup *api.NodeGroup) error { | ||
Expect(nodeGroup).To(Equal(cfg.NodeGroups[i])) | ||
names = append(names, nodeGroup.Name) | ||
return nil | ||
}) | ||
Expect(names).To(Equal([]string{"ng-x0", "ng-x1", "ng-x2"})) | ||
}) | ||
|
||
It("should iterate over unique nodegroups and filter some out", func() { | ||
cfg := newClusterConfig() | ||
addGroupA(cfg) | ||
addGroupB(cfg) | ||
|
||
filter := NewNodeGroupFilter() | ||
names := []string{} | ||
|
||
filter.CheckEachNodeGroup(cfg.NodeGroups, func(i int, nodeGroup *api.NodeGroup) error { | ||
Expect(nodeGroup).To(Equal(cfg.NodeGroups[i])) | ||
names = append(names, nodeGroup.Name) | ||
return nil | ||
}) | ||
Expect(names).To(Equal([]string{"test-ng1a", "test-ng2a", "test-ng3a", "test-ng1b", "test-ng2b", "test-ng3b"})) | ||
|
||
names = []string{} | ||
|
||
err := filter.ApplyOnlyFilter([]string{"t?xyz?", "ab*z123?"}, cfg) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal(`no nodegroups match filter specification: "t?xyz?,ab*z123?"`)) | ||
|
||
err = filter.ApplyOnlyFilter([]string{"test-ng1?", "te*-ng3?"}, cfg) | ||
Expect(err).ToNot(HaveOccurred()) | ||
filter.CheckEachNodeGroup(cfg.NodeGroups, func(i int, nodeGroup *api.NodeGroup) error { | ||
Expect(nodeGroup).To(Equal(cfg.NodeGroups[i])) | ||
names = append(names, nodeGroup.Name) | ||
return nil | ||
}) | ||
Expect(names).To(Equal([]string{"test-ng1a", "test-ng3a", "test-ng1b", "test-ng3b"})) | ||
}) | ||
}) | ||
}) | ||
|
||
func newClusterConfig() *api.ClusterConfig { | ||
cfg := api.NewClusterConfig() | ||
|
||
cfg.Metadata.Name = "test-3x3-ngs" | ||
cfg.Metadata.Region = "eu-central-1" | ||
|
||
return cfg | ||
} | ||
|
||
func addGroupA(cfg *api.ClusterConfig) { | ||
var ng *api.NodeGroup | ||
|
||
ng = cfg.NewNodeGroup() | ||
ng.Name = "test-ng1a" | ||
ng.VolumeSize = 768 | ||
ng.VolumeType = "io1" | ||
ng.IAM.AttachPolicyARNs = []string{"foo"} | ||
ng.Labels = map[string]string{"group": "a", "seq": "1"} | ||
|
||
ng = cfg.NewNodeGroup() | ||
ng.Name = "test-ng2a" | ||
ng.IAM.AttachPolicyARNs = []string{"bar"} | ||
ng.Labels = map[string]string{"group": "a", "seq": "2"} | ||
|
||
ng = cfg.NewNodeGroup() | ||
ng.Name = "test-ng3a" | ||
ng.ClusterDNS = "1.2.3.4" | ||
ng.InstanceType = "m3.large" | ||
ng.AllowSSH = true | ||
ng.Labels = map[string]string{"group": "a", "seq": "3"} | ||
} | ||
|
||
func addGroupB(cfg *api.ClusterConfig) { | ||
var ng *api.NodeGroup | ||
|
||
ng = cfg.NewNodeGroup() | ||
ng.Name = "test-ng1b" | ||
ng.AllowSSH = true | ||
ng.Labels = map[string]string{"group": "b", "seq": "1"} | ||
|
||
ng = cfg.NewNodeGroup() | ||
ng.Name = "test-ng2b" | ||
ng.ClusterDNS = "4.2.8.14" | ||
ng.InstanceType = "m5.xlarge" | ||
ng.SecurityGroups.AttachIDs = []string{"sg-1", "sg-2"} | ||
ng.SecurityGroups.WithLocal = api.NewBoolFalse() | ||
ng.Labels = map[string]string{"group": "b", "seq": "1"} | ||
|
||
ng = cfg.NewNodeGroup() | ||
ng.Name = "test-ng3b" | ||
ng.VolumeSize = 192 | ||
ng.SecurityGroups.AttachIDs = []string{"sg-1", "sg-2"} | ||
ng.SecurityGroups.WithLocal = api.NewBoolFalse() | ||
ng.Labels = map[string]string{"group": "b", "seq": "1"} | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.