Skip to content

Commit d94ad3e

Browse files
djzagerasmacdo
authored andcommitted
feat(ansible): make ansible verbosity configurable (#2087)
* feat(ansible): make ansible verbosity configurable Make it possible to configure the verbosity of `ansible-runner` command invocations via command line flags. The default value should be 2 to avoid unexpected changes in behavior. Also, like `maxWorkers` the `ansibleVerbosity` can be overridden per GVK with an environment variable of the form `ANSIBLE_VERBOSITY_$KIND_$GROUP`. This PR: - Adds new command line flag `ansible-verbosity` for Ansible based operators - Add internal `cmdFuncType` to runner pkg for use when constructing command functions * feat(ansible): address documentation comments - word choice in advanced_options - refer to correct environment key in getAnsibleVerbosity() * Ansible verbosity change goes with unreleased
1 parent 424a61d commit d94ad3e

File tree

9 files changed

+348
-88
lines changed

9 files changed

+348
-88
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Added
44

55
- Added `Operator Version: X.Y.Z` information in the operator logs.([#1953](https://github.com/operator-framework/operator-sdk/pull/1953))
6+
- Make Ansible verbosity configurable via the `ansible-verbosity` flag. ([#2087](https://github.com/operator-framework/operator-sdk/pull/2087))
67

78
### Changed
89

doc/ansible/dev/advanced_options.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Some features can be overridden per resource via an annotation on that CR. The o
1111

1212
| Feature | Yaml Key | Description| Annotation for override | default | Documentation |
1313
|---------|----------|------------|-------------------------|---------|---------------|
14-
| Reconcile Period | `reconcilePeriod` | time between reconcile runs for a particular CR | ansbile.operator-sdk/reconcile-period | 1m | |
14+
| Reconcile Period | `reconcilePeriod` | time between reconcile runs for a particular CR | ansible.operator-sdk/reconcile-period | 1m | |
1515
| Manage Status | `manageStatus` | Allows the ansible operator to manage the conditions section of each resource's status section. | | true | |
1616
| Watching Dependent Resources | `watchDependentResources` | Allows the ansible operator to dynamically watch resources that are created by ansible | | true | [dependent_watches.md](dependent_watches.md) |
1717
| Watching Cluster-Scoped Resources | `watchClusterScopedResources` | Allows the ansible operator to watch cluster-scoped resources that are created by ansible | | false | |
@@ -111,3 +111,44 @@ From this data, we can see that the environment variable will be
111111
- name: WORKER_MEMCACHED_CACHE_EXAMPLE_COM
112112
value: "6"
113113
```
114+
115+
## Ansible Verbosity
116+
117+
Setting the verbosity at which `ansible-runner` is run controls how verbose the
118+
output of `ansible-playbook` will be. The normal rules for verbosity apply
119+
here, where higher values mean more output. Acceptable values range from 0
120+
(only the most severe messages are output) to 7 (all debugging messages are
121+
output).
122+
123+
There are two ways to configure the verbosity argument to the `ansible-runner`
124+
command:
125+
126+
1. Operator **authors and admins** can set the Ansible verbosity by including
127+
extra args to the operator container in the operator deployment.
128+
1. Operator **admins** can set Ansible verbosity by setting an environment
129+
variable in the format `ANSIBLE_VERBOSITY_<kind>_<group>`. This variable must
130+
be all uppercase and all periods (e.g. in the group name) are replaced with
131+
underscore.
132+
133+
### Example
134+
135+
For demonstration purposes, let us assume that we have a database operator that
136+
supports two Kinds -- `MongoDB` and `PostgreSQL` -- in the `db.example.com`
137+
Group. We have only recently implemented the support for the `MongoDB` Kind so
138+
we want reconciles for this Kind to be more verbose. Our operator container's
139+
spec in our `deploy/operator.yaml` might look something like:
140+
141+
```yaml
142+
- name: operator
143+
image: "quay.io/example/database-operator:v1.0.0"
144+
imagePullPolicy: "Always"
145+
args:
146+
# This value applies to all GVKs specified in watches.yaml
147+
# that are not overriden by environment variables.
148+
- "--ansible-verbosity"
149+
- "1"
150+
env:
151+
# Override the verbosity for the MongoDB kind
152+
- name: ANSIBLE_VERBOSITY_MONGODB_DB_EXAMPLE_COM
153+
value: "4"
154+
```

pkg/ansible/flags/flag.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ import (
2525
// AnsibleOperatorFlags - Options to be used by an ansible operator
2626
type AnsibleOperatorFlags struct {
2727
watch.WatchFlags
28-
InjectOwnerRef bool
29-
MaxWorkers int
28+
InjectOwnerRef bool
29+
MaxWorkers int
30+
AnsibleVerbosity int
3031
}
3132

3233
// AddTo - Add the ansible operator flags to the the flagset
@@ -47,5 +48,12 @@ func AddTo(flagSet *pflag.FlagSet, helpTextPrefix ...string) *AnsibleOperatorFla
4748
"Maximum number of workers to use. Overridden by environment variable."),
4849
" "),
4950
)
51+
flagSet.IntVar(&aof.AnsibleVerbosity,
52+
"ansible-verbosity",
53+
2,
54+
strings.Join(append(helpTextPrefix,
55+
"Ansible verbosity. Overridden by environment variable."),
56+
" "),
57+
)
5058
return aof
5159
}

pkg/ansible/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func Run(flags *aoflags.AnsibleOperatorFlags) error {
8989

9090
var gvks []schema.GroupVersionKind
9191
cMap := controllermap.NewControllerMap()
92-
watches, err := watches.Load(flags.WatchesFile, flags.MaxWorkers)
92+
watches, err := watches.Load(flags.WatchesFile, flags.MaxWorkers, flags.AnsibleVerbosity)
9393
if err != nil {
9494
log.Error(err, "Failed to load watches.")
9595
return err

pkg/ansible/runner/runner.go

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -52,62 +52,65 @@ type Runner interface {
5252
GetFinalizer() (string, bool)
5353
}
5454

55+
func ansibleVerbosityString(verbosity int) string {
56+
if verbosity > 0 {
57+
return fmt.Sprintf("-%v", strings.Repeat("v", verbosity))
58+
}
59+
return ""
60+
}
61+
62+
type cmdFuncType func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd
63+
64+
func playbookCmdFunc(verbosity, path string) cmdFuncType {
65+
return func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd {
66+
return exec.Command("ansible-runner", verbosity, "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "-p", path, "-i", ident, "run", inputDirPath)
67+
}
68+
}
69+
70+
func roleCmdFunc(verbosity, path string) cmdFuncType {
71+
rolePath, roleName := filepath.Split(path)
72+
return func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd {
73+
return exec.Command("ansible-runner", verbosity, "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "--role", roleName, "--roles-path", rolePath, "--hosts", "localhost", "-i", ident, "run", inputDirPath)
74+
}
75+
}
76+
5577
// New - creates a Runner from a Watch struct
5678
func New(watch watches.Watch) (Runner, error) {
57-
// handle role or playbook
5879
var path string
59-
var cmdFunc func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd
80+
var cmdFunc, finalizerCmdFunc cmdFuncType
6081

6182
err := watch.Validate()
6283
if err != nil {
6384
log.Error(err, "Failed to validate watch")
6485
return nil, err
6586
}
87+
verbosityString := ansibleVerbosityString(watch.AnsibleVerbosity)
6688

6789
switch {
6890
case watch.Playbook != "":
6991
path = watch.Playbook
70-
cmdFunc = func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd {
71-
return exec.Command("ansible-runner", "-vv", "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "-p", path, "-i", ident, "run", inputDirPath)
72-
}
92+
cmdFunc = playbookCmdFunc(verbosityString, path)
7393
case watch.Role != "":
7494
path = watch.Role
75-
cmdFunc = func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd {
76-
rolePath, roleName := filepath.Split(path)
77-
return exec.Command("ansible-runner", "-vv", "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "--role", roleName, "--roles-path", rolePath, "--hosts", "localhost", "-i", ident, "run", inputDirPath)
78-
}
79-
default:
80-
return nil, fmt.Errorf("must specify Role or Path")
95+
cmdFunc = roleCmdFunc(verbosityString, path)
8196
}
8297

8398
// handle finalizer
84-
var finalizer *watches.Finalizer
85-
var finalizerCmdFunc func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd
8699
switch {
87100
case watch.Finalizer == nil:
88-
finalizer = nil
89101
finalizerCmdFunc = nil
90102
case watch.Finalizer.Playbook != "":
91-
finalizer = watch.Finalizer
92-
finalizerCmdFunc = func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd {
93-
return exec.Command("ansible-runner", "-vv", "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "-p", finalizer.Playbook, "-i", ident, "run", inputDirPath)
94-
}
103+
finalizerCmdFunc = playbookCmdFunc(verbosityString, watch.Finalizer.Playbook)
95104
case watch.Finalizer.Role != "":
96-
finalizer = watch.Finalizer
97-
finalizerCmdFunc = func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd {
98-
path := strings.TrimRight(finalizer.Role, "/")
99-
rolePath, roleName := filepath.Split(path)
100-
return exec.Command("ansible-runner", "-vv", "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "--role", roleName, "--roles-path", rolePath, "--hosts", "localhost", "-i", ident, "run", inputDirPath)
101-
}
105+
finalizerCmdFunc = roleCmdFunc(verbosityString, watch.Finalizer.Role)
102106
case len(watch.Finalizer.Vars) != 0:
103-
finalizer = watch.Finalizer
104107
finalizerCmdFunc = cmdFunc
105108
}
106109

107110
return &runner{
108111
Path: path,
109112
cmdFunc: cmdFunc,
110-
Finalizer: finalizer,
113+
Finalizer: watch.Finalizer,
111114
finalizerCmdFunc: finalizerCmdFunc,
112115
GVK: watch.GroupVersionKind,
113116
maxRunnerArtifacts: watch.MaxRunnerArtifacts,

pkg/ansible/runner/runner_test.go

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package runner
1616

1717
import (
18-
"fmt"
1918
"os"
2019
"os/exec"
2120
"path/filepath"
@@ -27,32 +26,20 @@ import (
2726
"github.com/operator-framework/operator-sdk/pkg/ansible/watches"
2827
)
2928

30-
func playbookCmd(path string, ident string, inputDirPath string, maxArtifacts int) *exec.Cmd {
31-
return exec.Command("ansible-runner", "-vv", "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "-p", path, "-i", ident, "run", inputDirPath)
32-
}
33-
34-
func roleCmd(path string, ident string, inputDirPath string, maxArtifacts int) *exec.Cmd {
35-
rolePath, roleName := filepath.Split(path)
36-
return exec.Command("ansible-runner", "-vv", "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "--role", roleName, "--roles-path", rolePath, "--hosts", "localhost", "-i", ident, "run", inputDirPath)
37-
}
38-
39-
type cmdFuncType func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd
40-
41-
func checkCmdFunc(t *testing.T, cmdFunc cmdFuncType, role, playbook string, watchesMaxArtifacts, runnerMaxArtifacts int) {
29+
func checkCmdFunc(t *testing.T, cmdFunc cmdFuncType, playbook, role string, verbosity int) {
4230
ident := "test"
4331
inputDirPath := "/test/path"
44-
var path string
32+
maxArtifacts := 1
4533
var expectedCmd, gotCmd *exec.Cmd
34+
verbosityString := ansibleVerbosityString(verbosity)
4635
switch {
4736
case playbook != "":
48-
path = playbook
49-
expectedCmd = playbookCmd(path, ident, inputDirPath, watchesMaxArtifacts)
37+
expectedCmd = playbookCmdFunc(verbosityString, playbook)(ident, inputDirPath, maxArtifacts)
5038
case role != "":
51-
path = role
52-
expectedCmd = roleCmd(path, ident, inputDirPath, watchesMaxArtifacts)
39+
expectedCmd = roleCmdFunc(verbosityString, role)(ident, inputDirPath, maxArtifacts)
5340
}
5441

55-
gotCmd = cmdFunc(ident, inputDirPath, runnerMaxArtifacts)
42+
gotCmd = cmdFunc(ident, inputDirPath, maxArtifacts)
5643

5744
if expectedCmd.Path != gotCmd.Path {
5845
t.Fatalf("Unexpected cmd path %v expected cmd path %v", gotCmd.Path, expectedCmd.Path)
@@ -171,7 +158,7 @@ func TestNew(t *testing.T) {
171158
}
172159

173160
// Check the cmdFunc
174-
checkCmdFunc(t, testRunnerStruct.cmdFunc, testWatch.Role, testWatch.Playbook, testWatch.MaxRunnerArtifacts, testRunnerStruct.maxRunnerArtifacts)
161+
checkCmdFunc(t, testRunnerStruct.cmdFunc, testWatch.Playbook, testWatch.Role, testWatch.AnsibleVerbosity)
175162

176163
// Check finalizer
177164
if testRunnerStruct.Finalizer != testWatch.Finalizer {
@@ -184,12 +171,32 @@ func TestNew(t *testing.T) {
184171
}
185172

186173
if len(testWatch.Finalizer.Vars) == 0 {
187-
checkCmdFunc(t, testRunnerStruct.finalizerCmdFunc, testWatch.Finalizer.Role, testWatch.Finalizer.Playbook, testWatch.MaxRunnerArtifacts, testRunnerStruct.maxRunnerArtifacts)
174+
checkCmdFunc(t, testRunnerStruct.cmdFunc, testWatch.Finalizer.Playbook, testWatch.Finalizer.Role, testWatch.AnsibleVerbosity)
188175
} else {
189176
// when finalizer vars is set the finalizerCmdFunc should be the same as the cmdFunc
190-
checkCmdFunc(t, testRunnerStruct.finalizerCmdFunc, testWatch.Role, testWatch.Playbook, testWatch.MaxRunnerArtifacts, testRunnerStruct.maxRunnerArtifacts)
177+
checkCmdFunc(t, testRunnerStruct.finalizerCmdFunc, testWatch.Playbook, testWatch.Role, testWatch.AnsibleVerbosity)
191178
}
192179
}
193180
})
194181
}
195182
}
183+
184+
func TestAnsibleVerbosityString(t *testing.T) {
185+
testCases := []struct {
186+
verbosity int
187+
expectedString string
188+
}{
189+
{verbosity: -1, expectedString: ""},
190+
{verbosity: 0, expectedString: ""},
191+
{verbosity: 1, expectedString: "-v"},
192+
{verbosity: 2, expectedString: "-vv"},
193+
{verbosity: 7, expectedString: "-vvvvvvv"},
194+
}
195+
196+
for _, tc := range testCases {
197+
gotString := ansibleVerbosityString(tc.verbosity)
198+
if tc.expectedString != gotString {
199+
t.Fatalf("Unexpected string %v expected %v", gotString, tc.expectedString)
200+
}
201+
}
202+
}

pkg/ansible/watches/testdata/valid.yaml.tmpl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,16 @@
6868
group: app.example.com
6969
kind: MaxWorkersEnv
7070
role: {{ .ValidRole }}
71+
- version: v1alpha1
72+
group: app.example.com
73+
kind: AnsibleVerbosityDefault
74+
role: {{ .ValidRole }}
75+
- version: v1alpha1
76+
group: app.example.com
77+
kind: AnsibleVerbosityIgnored
78+
role: {{ .ValidRole }}
79+
ansibleVerbosity: 5
80+
- version: v1alpha1
81+
group: app.example.com
82+
kind: AnsibleVerbosityEnv
83+
role: {{ .ValidRole }}

pkg/ansible/watches/watches.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ type Watch struct {
4848
Finalizer *Finalizer `yaml:"finalizer"`
4949

5050
// Not configurable via watches.yaml
51-
MaxWorkers int `yaml:"maxWorkers"`
51+
MaxWorkers int `yaml:"maxWorkers"`
52+
AnsibleVerbosity int `yaml:"ansibleVerbosity"`
5253
}
5354

5455
// Finalizer - Expose finalizer to be used by a user.
@@ -68,7 +69,8 @@ var (
6869
watchClusterScopedResourcesDefault = false
6970

7071
// these are overridden by cmdline flags
71-
maxWorkersDefault = 1
72+
maxWorkersDefault = 1
73+
ansibleVerbosityDefault = 2
7274
)
7375

7476
// UnmarshalYAML - implements the yaml.Unmarshaler interface for Watch.
@@ -130,6 +132,7 @@ func (w *Watch) UnmarshalYAML(unmarshal func(interface{}) error) error {
130132
w.WatchDependentResources = tmp.WatchDependentResources
131133
w.WatchClusterScopedResources = tmp.WatchClusterScopedResources
132134
w.Finalizer = tmp.Finalizer
135+
w.AnsibleVerbosity = getAnsibleVerbosity(gvk, ansibleVerbosityDefault)
133136

134137
return nil
135138
}
@@ -176,12 +179,14 @@ func New(gvk schema.GroupVersionKind, role, playbook string, finalizer *Finalize
176179
WatchDependentResources: watchDependentResourcesDefault,
177180
WatchClusterScopedResources: watchClusterScopedResourcesDefault,
178181
Finalizer: finalizer,
182+
AnsibleVerbosity: ansibleVerbosityDefault,
179183
}
180184
}
181185

182186
// Load - loads a slice of Watches from the watches file from the CLI
183-
func Load(path string, maxWorkers int) ([]Watch, error) {
187+
func Load(path string, maxWorkers, ansibleVerbosity int) ([]Watch, error) {
184188
maxWorkersDefault = maxWorkers
189+
ansibleVerbosityDefault = ansibleVerbosity
185190
b, err := ioutil.ReadFile(path)
186191
if err != nil {
187192
log.Error(err, "Failed to get config file")
@@ -251,7 +256,7 @@ func verifyAnsiblePath(playbook string, role string) error {
251256
}
252257

253258
// if the WORKER_* environment variable is set, use that value.
254-
// Otherwise, use the value from the CLI. This is definitely
259+
// Otherwise, use defValue. This is definitely
255260
// counter-intuitive but it allows the operator admin adjust the
256261
// number of workers based on their cluster resources. While the
257262
// author may use the CLI option to specify a suggested
@@ -276,3 +281,30 @@ func getMaxWorkers(gvk schema.GroupVersionKind, defValue int) int {
276281
}
277282
return maxWorkers
278283
}
284+
285+
// if the ANSIBLE_VERBOSITY_* environment variable is set, use that value.
286+
// Otherwise, use defValue.
287+
func getAnsibleVerbosity(gvk schema.GroupVersionKind, defValue int) int {
288+
envVar := strings.ToUpper(strings.Replace(
289+
fmt.Sprintf("ANSIBLE_VERBOSITY_%s_%s", gvk.Kind, gvk.Group),
290+
".",
291+
"_",
292+
-1,
293+
))
294+
ansibleVerbosity, err := strconv.Atoi(os.Getenv(envVar))
295+
if err != nil {
296+
log.Info("Failed to parse %v from environment. Using default %v", envVar, defValue)
297+
return defValue
298+
}
299+
300+
// Use default value when value doesn't make sense
301+
if ansibleVerbosity < 0 {
302+
log.Info("Value %v not valid. Using default %v", ansibleVerbosity, defValue)
303+
return defValue
304+
}
305+
if ansibleVerbosity > 7 {
306+
log.Info("Value %v not valid. Using default %v", ansibleVerbosity, defValue)
307+
return defValue
308+
}
309+
return ansibleVerbosity
310+
}

0 commit comments

Comments
 (0)