From 048a53dea2d7382334c81ba9619ddfcb3c342023 Mon Sep 17 00:00:00 2001 From: David Zager Date: Thu, 24 Oct 2019 08:57:45 -0400 Subject: [PATCH 1/2] feat(ansible): Configure verbosity via annotation This makes it possible to create a CR with an annotation, specifically `"ansible.operator-sdk/verbosity"`, to override the verbosity of `ansible-runner` commands for a given CR. - Store the verbosity on `runner` - Update `cmdFuncType` to take verbosity and `*exec.Cmd` to convert integer value of verbosity to string - In `Run()` get verbosity annotation from CR (if it exists) and use that value when executing the `cmdFunc` --- pkg/ansible/runner/runner.go | 48 ++++++++++++++++++++----------- pkg/ansible/runner/runner_test.go | 7 ++--- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/pkg/ansible/runner/runner.go b/pkg/ansible/runner/runner.go index b35164039ec..538d709a5ad 100644 --- a/pkg/ansible/runner/runner.go +++ b/pkg/ansible/runner/runner.go @@ -43,6 +43,11 @@ const ( // particular CR. Setting this to zero will cause all artifact directories to be kept. // Example usage "ansible.operator-sdk/max-runner-artifacts: 100" MaxRunnerArtifactsAnnotation = "ansible.operator-sdk/max-runner-artifacts" + + // AnsibleVerbosityAnnotation - annotation used by a user to specify the verbosity given + // to the ansible-runner command. This will override the value for a particular CR. + // Example usage "ansible.operator-sdk/verbosity: 5" + AnsibleVerbosityAnnotation = "ansible.operator-sdk/verbosity" ) // Runner - a runnable that should take the parameters and name and namespace @@ -59,18 +64,18 @@ func ansibleVerbosityString(verbosity int) string { return "" } -type cmdFuncType func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd +type cmdFuncType func(ident, inputDirPath string, maxArtifacts, verbosity int) *exec.Cmd -func playbookCmdFunc(verbosity, path string) cmdFuncType { - return func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd { - return exec.Command("ansible-runner", verbosity, "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "-p", path, "-i", ident, "run", inputDirPath) +func playbookCmdFunc(path string) cmdFuncType { + return func(ident, inputDirPath string, maxArtifacts, verbosity int) *exec.Cmd { + return exec.Command("ansible-runner", ansibleVerbosityString(verbosity), "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "-p", path, "-i", ident, "run", inputDirPath) } } -func roleCmdFunc(verbosity, path string) cmdFuncType { +func roleCmdFunc(path string) cmdFuncType { rolePath, roleName := filepath.Split(path) - return func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd { - return exec.Command("ansible-runner", verbosity, "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "--role", roleName, "--roles-path", rolePath, "--hosts", "localhost", "-i", ident, "run", inputDirPath) + return func(ident, inputDirPath string, maxArtifacts, verbosity int) *exec.Cmd { + return exec.Command("ansible-runner", ansibleVerbosityString(verbosity), "--rotate-artifacts", fmt.Sprintf("%v", maxArtifacts), "--role", roleName, "--roles-path", rolePath, "--hosts", "localhost", "-i", ident, "run", inputDirPath) } } @@ -84,15 +89,14 @@ func New(watch watches.Watch) (Runner, error) { log.Error(err, "Failed to validate watch") return nil, err } - verbosityString := ansibleVerbosityString(watch.AnsibleVerbosity) switch { case watch.Playbook != "": path = watch.Playbook - cmdFunc = playbookCmdFunc(verbosityString, path) + cmdFunc = playbookCmdFunc(path) case watch.Role != "": path = watch.Role - cmdFunc = roleCmdFunc(verbosityString, path) + cmdFunc = roleCmdFunc(path) } // handle finalizer @@ -100,9 +104,9 @@ func New(watch watches.Watch) (Runner, error) { case watch.Finalizer == nil: finalizerCmdFunc = nil case watch.Finalizer.Playbook != "": - finalizerCmdFunc = playbookCmdFunc(verbosityString, watch.Finalizer.Playbook) + finalizerCmdFunc = playbookCmdFunc(watch.Finalizer.Playbook) case watch.Finalizer.Role != "": - finalizerCmdFunc = roleCmdFunc(verbosityString, watch.Finalizer.Role) + finalizerCmdFunc = roleCmdFunc(watch.Finalizer.Role) default: finalizerCmdFunc = cmdFunc } @@ -115,6 +119,7 @@ func New(watch watches.Watch) (Runner, error) { finalizerCmdFunc: finalizerCmdFunc, GVK: watch.GroupVersionKind, maxRunnerArtifacts: watch.MaxRunnerArtifacts, + ansibleVerbosity: watch.AnsibleVerbosity, }, nil } @@ -123,10 +128,11 @@ type runner struct { Path string // path on disk to a playbook or role depending on what cmdFunc expects GVK schema.GroupVersionKind // GVK being watched that corresponds to the Path Finalizer *watches.Finalizer - cmdFunc func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd // returns a Cmd that runs ansible-runner Vars map[string]interface{} - finalizerCmdFunc func(ident, inputDirPath string, maxArtifacts int) *exec.Cmd + cmdFunc cmdFuncType // returns a Cmd that runs ansible-runner + finalizerCmdFunc cmdFuncType maxRunnerArtifacts int + ansibleVerbosity int } func (r *runner) Run(ident string, u *unstructured.Unstructured, kubeconfig string) (RunResult, error) { @@ -184,13 +190,23 @@ func (r *runner) Run(ident string, u *unstructured.Unstructured, kubeconfig stri } } + verbosity := r.ansibleVerbosity + if av, ok := u.GetAnnotations()[AnsibleVerbosityAnnotation]; ok { + i, err := strconv.Atoi(av) + if err != nil { + log.Info("Invalid ansible verbosity annotation", "err", err, "value", av) + } else { + verbosity = i + } + } + go func() { var dc *exec.Cmd if r.isFinalizerRun(u) { logger.V(1).Info("Resource is marked for deletion, running finalizer", "Finalizer", r.Finalizer.Name) - dc = r.finalizerCmdFunc(ident, inputDir.Path, maxArtifacts) + dc = r.finalizerCmdFunc(ident, inputDir.Path, maxArtifacts, verbosity) } else { - dc = r.cmdFunc(ident, inputDir.Path, maxArtifacts) + dc = r.cmdFunc(ident, inputDir.Path, maxArtifacts, verbosity) } // Append current environment since setting dc.Env to anything other than nil overwrites current env dc.Env = append(dc.Env, os.Environ()...) diff --git a/pkg/ansible/runner/runner_test.go b/pkg/ansible/runner/runner_test.go index 381b13d6323..a60666ca796 100644 --- a/pkg/ansible/runner/runner_test.go +++ b/pkg/ansible/runner/runner_test.go @@ -31,15 +31,14 @@ func checkCmdFunc(t *testing.T, cmdFunc cmdFuncType, playbook, role string, verb inputDirPath := "/test/path" maxArtifacts := 1 var expectedCmd, gotCmd *exec.Cmd - verbosityString := ansibleVerbosityString(verbosity) switch { case playbook != "": - expectedCmd = playbookCmdFunc(verbosityString, playbook)(ident, inputDirPath, maxArtifacts) + expectedCmd = playbookCmdFunc(playbook)(ident, inputDirPath, maxArtifacts, verbosity) case role != "": - expectedCmd = roleCmdFunc(verbosityString, role)(ident, inputDirPath, maxArtifacts) + expectedCmd = roleCmdFunc(role)(ident, inputDirPath, maxArtifacts, verbosity) } - gotCmd = cmdFunc(ident, inputDirPath, maxArtifacts) + gotCmd = cmdFunc(ident, inputDirPath, maxArtifacts, verbosity) if expectedCmd.Path != gotCmd.Path { t.Fatalf("Unexpected cmd path %v expected cmd path %v", gotCmd.Path, expectedCmd.Path) From d01618ef3df256f11e63cddab2edaaee675bdd80 Mon Sep 17 00:00:00 2001 From: David Zager Date: Fri, 8 Nov 2019 11:45:14 -0500 Subject: [PATCH 2/2] Add documentation and changelog --- CHANGELOG.md | 1 + doc/ansible/dev/advanced_options.md | 21 +++++++++++++++++++-- doc/ansible/user-guide.md | 20 +++++++++++++------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c39ef1dcbc..738e68be1b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Added - Support for vars in top level ansible watches. ([#2147](https://github.com/operator-framework/operator-sdk/pull/2147)) +- Support for `"ansible.operator-sdk/verbosity"` annotation on Custom Resources watched by Ansible based operators to override verbosity on an individual resource. ([#2102](https://github.com/operator-framework/operator-sdk/pull/2102)) ### Changed - Upgrade minimal Ansible version in the init projects from `2.4` to `2.6`. ([#2107](https://github.com/operator-framework/operator-sdk/pull/2107)) diff --git a/doc/ansible/dev/advanced_options.md b/doc/ansible/dev/advanced_options.md index 30111894959..8500f289c61 100644 --- a/doc/ansible/dev/advanced_options.md +++ b/doc/ansible/dev/advanced_options.md @@ -120,7 +120,7 @@ here, where higher values mean more output. Acceptable values range from 0 (only the most severe messages are output) to 7 (all debugging messages are output). -There are two ways to configure the verbosity argument to the `ansible-runner` +There are three ways to configure the verbosity argument to the `ansible-runner` command: 1. Operator **authors and admins** can set the Ansible verbosity by including @@ -129,8 +129,11 @@ command: variable in the format `ANSIBLE_VERBOSITY__`. This variable must be all uppercase and all periods (e.g. in the group name) are replaced with underscore. +1. Operator **users, authors, and admins** can set the Ansible verbosity by + setting the `"ansible.operator-sdk/verbosity"` annotation on the Custom + Resource. -### Example +### Examples For demonstration purposes, let us assume that we have a database operator that supports two Kinds -- `MongoDB` and `PostgreSQL` -- in the `db.example.com` @@ -152,3 +155,17 @@ spec in our `deploy/operator.yaml` might look something like: - name: ANSIBLE_VERBOSITY_MONGODB_DB_EXAMPLE_COM value: "4" ``` + +Once the Operator is deployed, the only way to change the verbosity is via the +`"ansible.operator-sdk/verbosity"` annotation. Continuing with our example, our +CR may look like: + +```yaml +apiVersion: "db.example.com/v1" +kind: "PostgreSQL" +metadata: + name: "example-db" + annotations: + "ansible.operator-sdk/verbosity": 5 +spec: {} +``` diff --git a/doc/ansible/user-guide.md b/doc/ansible/user-guide.md index f9dbf5cfc8f..91217548e0b 100644 --- a/doc/ansible/user-guide.md +++ b/doc/ansible/user-guide.md @@ -357,16 +357,22 @@ kubectl logs deployment/memcached-operator -c operator The `ansible` logs contain all of the information about the Ansible run and will make it much easier to debug issues within your Ansible tasks, whereas the `operator` logs will contain much more detailed information about the Ansible Operator's internals and interface with Kubernetes. -### Additional Ansible debug +### Additional Ansible Debug -Occasionally while developing additional debug in the Operator logs is nice to have. To enable Ansible debug output, ie `-vvvv`. -Add the following to the `operator.yaml` manifest. +Occasionally while developing additional debug in the Operator logs is nice to have. +Using the memcached operator as an example, we can simply add the +`"ansible.operator-sdk/verbosity"` annotation to the Custom +Resource with the desired verbosity. ```yaml - env: - ... - - name: ANSIBLE_VERBOSITY - value: "4" +apiVersion: "cache.example.com/v1alpha1" +kind: "Memcached" +metadata: + name: "example-memcached" + annotations: + "ansible.operator-sdk/verbosity": 4 +spec: + size: 4 ``` ### Update the size