Skip to content

Commit d91f905

Browse files
authored
Merge pull request #26 from onmetal/20-reusable-switches
added switches package
2 parents 667cea5 + 46be563 commit d91f905

File tree

4 files changed

+307
-0
lines changed

4 files changed

+307
-0
lines changed

cmdutils/switches/switches.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright 2021 OnMetal authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package switches provides new type that implements flag.Value interface -- Switches.
16+
// It can be used for enabling/disabling controllers/webhooks in your controller manager.
17+
package switches
18+
19+
import (
20+
"encoding/csv"
21+
"fmt"
22+
"strings"
23+
24+
"sigs.k8s.io/kustomize/kyaml/sets"
25+
)
26+
27+
const (
28+
All = "*"
29+
30+
disablePrefix = "-"
31+
)
32+
33+
type Switches struct {
34+
settings map[string]bool
35+
}
36+
37+
// New creates an instance of Switches
38+
func New(settings []string) *Switches {
39+
s := &Switches{
40+
settings: make(map[string]bool),
41+
}
42+
s.setSettings(settings)
43+
return s
44+
}
45+
46+
// Disable prepends disablePrefix prefix to an item name
47+
func Disable(name string) string {
48+
return disablePrefix + name
49+
}
50+
51+
func (s *Switches) String() string {
52+
return fmt.Sprintf("%v", s.settings)
53+
}
54+
55+
func (s *Switches) Set(val string) error {
56+
var (
57+
err error
58+
settings []string
59+
)
60+
61+
if val != "" {
62+
stringReader := strings.NewReader(val)
63+
csvReader := csv.NewReader(stringReader)
64+
65+
settings, err = csvReader.Read()
66+
if err != nil {
67+
return fmt.Errorf("failed to set switches value: %w", err)
68+
}
69+
70+
// Validate that all specified controllers are known
71+
for _, v := range settings {
72+
trimmed := strings.TrimPrefix(v, disablePrefix)
73+
if _, ok := s.settings[trimmed]; trimmed != All && !ok {
74+
return fmt.Errorf("unknown item: %s", trimmed)
75+
}
76+
}
77+
} else {
78+
settings = []string{""}
79+
}
80+
81+
s.setSettings(settings)
82+
return nil
83+
}
84+
85+
// Enabled checks if item is enabled
86+
func (s *Switches) Enabled(name string) bool {
87+
return s.settings[name]
88+
}
89+
90+
// All returns names of all items set in settings
91+
func (s *Switches) All() sets.String {
92+
names := make(sets.String, len(s.settings))
93+
for k := range s.settings {
94+
names.Insert(k)
95+
}
96+
97+
return names
98+
}
99+
100+
// DisabledByDefault returns names of all disabled items
101+
func (s *Switches) DisabledByDefault() sets.String {
102+
names := make(sets.String)
103+
for k, enabled := range s.settings {
104+
if !enabled {
105+
names.Insert(k)
106+
}
107+
}
108+
109+
return names
110+
}
111+
112+
func (s *Switches) Type() string {
113+
return "Switches"
114+
}
115+
116+
func (s *Switches) setSettings(settings []string) {
117+
if len(settings) == 1 && settings[0] == "" {
118+
return
119+
}
120+
121+
var isDefault bool
122+
for _, v := range settings {
123+
if v == All {
124+
isDefault = true
125+
break
126+
}
127+
}
128+
129+
if !isDefault {
130+
for k := range s.settings {
131+
s.settings[k] = false
132+
}
133+
}
134+
135+
for _, v := range settings {
136+
if v == All {
137+
continue
138+
}
139+
s.settings[strings.TrimPrefix(v, disablePrefix)] = !strings.HasPrefix(v, disablePrefix)
140+
}
141+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2021 OnMetal authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package switches
16+
17+
import (
18+
"testing"
19+
20+
. "github.com/onsi/ginkgo"
21+
. "github.com/onsi/gomega"
22+
)
23+
24+
func TestCondition(t *testing.T) {
25+
RegisterFailHandler(Fail)
26+
RunSpecs(t, "Condition Suite")
27+
}

cmdutils/switches/switches_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright 2021 OnMetal authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package switches
15+
16+
import (
17+
"flag"
18+
19+
. "github.com/onsi/ginkgo"
20+
. "github.com/onsi/gomega"
21+
"github.com/spf13/pflag"
22+
"sigs.k8s.io/kustomize/kyaml/sets"
23+
)
24+
25+
var _ = Describe("CMD Switches", func() {
26+
Context("Testing Switches interface", func() {
27+
It("should disable runner", func() {
28+
s := New([]string{"runner-a", Disable("runner-b")})
29+
Expect(s.Enabled("runner-a")).To(BeTrue())
30+
Expect(s.Enabled("runner-b")).To(BeFalse())
31+
})
32+
It("should return all items", func() {
33+
s := New([]string{"runner-a", Disable("runner-b")})
34+
35+
expected := make(sets.String)
36+
expected.Insert("runner-a", "runner-b")
37+
Expect(s.All()).To(Equal(expected))
38+
})
39+
It("should return all disabled items", func() {
40+
s := New([]string{"runner-a", Disable("runner-b")})
41+
42+
expected := make(sets.String)
43+
expected.Insert("runner-b")
44+
Expect(s.DisabledByDefault()).To(Equal(expected))
45+
})
46+
It("should return string", func() {
47+
s := New([]string{"runner-a", Disable("runner-b")})
48+
49+
Expect(s.String()).To(Equal("map[runner-a:true runner-b:false]"))
50+
})
51+
})
52+
53+
Context("Testing flag package behavior", func() {
54+
It("should keep default settings when no flag is passed", func() {
55+
fs := flag.NewFlagSet("", flag.ExitOnError)
56+
controllers := New([]string{"runner-a", Disable("runner-b"), "runner-c"})
57+
fs.Var(controllers, "controllers", "")
58+
59+
Expect(fs.Parse([]string{})).NotTo(HaveOccurred())
60+
Expect(controllers.Enabled("runner-a")).To(BeTrue())
61+
Expect(controllers.Enabled("runner-b")).To(BeFalse())
62+
Expect(controllers.Enabled("runner-c")).To(BeTrue())
63+
})
64+
It("should keep default settings when * is passed", func() {
65+
fs := flag.NewFlagSet("", flag.ExitOnError)
66+
controllers := New([]string{"runner-a", Disable("runner-b"), "runner-c"})
67+
fs.Var(controllers, "controllers", "")
68+
69+
Expect(fs.Parse([]string{"--controllers=*"})).NotTo(HaveOccurred())
70+
Expect(controllers.Enabled("runner-a")).To(BeTrue())
71+
Expect(controllers.Enabled("runner-b")).To(BeFalse())
72+
Expect(controllers.Enabled("runner-c")).To(BeTrue())
73+
})
74+
It("should override default settings", func() {
75+
fs := flag.NewFlagSet("", flag.ExitOnError)
76+
controllers := New([]string{"runner-a", Disable("runner-b"), "runner-c"})
77+
fs.Var(controllers, "controllers", "")
78+
79+
Expect(fs.Parse([]string{"--controllers=runner-a,-runner-c"})).NotTo(HaveOccurred())
80+
Expect(controllers.Enabled("runner-a")).To(BeTrue())
81+
Expect(controllers.Enabled("runner-b")).To(BeFalse())
82+
Expect(controllers.Enabled("runner-c")).To(BeFalse())
83+
})
84+
It("should override some of default settings", func() {
85+
fs := flag.NewFlagSet("", flag.ExitOnError)
86+
controllers := New([]string{"runner-a", Disable("runner-b"), "runner-c"})
87+
fs.Var(controllers, "controllers", "")
88+
89+
Expect(fs.Parse([]string{"--controllers=*,-runner-a"})).NotTo(HaveOccurred())
90+
Expect(controllers.Enabled("runner-a")).To(BeFalse())
91+
Expect(controllers.Enabled("runner-b")).To(BeFalse())
92+
Expect(controllers.Enabled("runner-c")).To(BeTrue())
93+
})
94+
})
95+
96+
Context("Testing pflag package behavior", func() {
97+
It("should keep default settings when no flag is passed", func() {
98+
fs := pflag.NewFlagSet("", pflag.ExitOnError)
99+
controllers := New([]string{"runner-a", Disable("runner-b"), "runner-c"})
100+
fs.Var(controllers, "controllers", "")
101+
102+
Expect(fs.Parse([]string{})).NotTo(HaveOccurred())
103+
Expect(controllers.Enabled("runner-a")).To(BeTrue())
104+
Expect(controllers.Enabled("runner-b")).To(BeFalse())
105+
Expect(controllers.Enabled("runner-c")).To(BeTrue())
106+
})
107+
It("should keep default settings when * is passed", func() {
108+
fs := pflag.NewFlagSet("", pflag.ExitOnError)
109+
controllers := New([]string{"runner-a", Disable("runner-b"), "runner-c"})
110+
fs.Var(controllers, "controllers", "")
111+
112+
Expect(fs.Parse([]string{"--controllers=*"})).NotTo(HaveOccurred())
113+
Expect(controllers.Enabled("runner-a")).To(BeTrue())
114+
Expect(controllers.Enabled("runner-b")).To(BeFalse())
115+
Expect(controllers.Enabled("runner-c")).To(BeTrue())
116+
})
117+
It("should override default settings", func() {
118+
fs := pflag.NewFlagSet("", pflag.ExitOnError)
119+
controllers := New([]string{"runner-a", Disable("runner-b"), "runner-c"})
120+
fs.Var(controllers, "controllers", "")
121+
122+
Expect(fs.Parse([]string{"--controllers=runner-a,-runner-c"})).NotTo(HaveOccurred())
123+
Expect(controllers.Enabled("runner-a")).To(BeTrue())
124+
Expect(controllers.Enabled("runner-b")).To(BeFalse())
125+
Expect(controllers.Enabled("runner-c")).To(BeFalse())
126+
})
127+
It("should override some of default settings", func() {
128+
fs := pflag.NewFlagSet("", pflag.ExitOnError)
129+
controllers := New([]string{"runner-a", Disable("runner-b"), "runner-c"})
130+
fs.Var(controllers, "controllers", "")
131+
132+
Expect(fs.Parse([]string{"--controllers=*,-runner-a"})).NotTo(HaveOccurred())
133+
Expect(controllers.Enabled("runner-a")).To(BeFalse())
134+
Expect(controllers.Enabled("runner-b")).To(BeFalse())
135+
Expect(controllers.Enabled("runner-c")).To(BeTrue())
136+
})
137+
})
138+
})

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/google/addlicense v1.0.0
88
github.com/onsi/ginkgo v1.16.5
99
github.com/onsi/gomega v1.16.0
10+
github.com/spf13/pflag v1.0.5
1011
k8s.io/api v0.22.3
1112
k8s.io/apimachinery v0.22.3
1213
k8s.io/client-go v0.22.3

0 commit comments

Comments
 (0)