GithubHelp home page GithubHelp logo

apenella / go-ansible Goto Github PK

View Code? Open in Web Editor NEW
771.0 16.0 136.0 587 KB

Go-ansible is a Go package that enables the execution of ansible-playbook or ansible commands directly from Golang applications. It supports a wide range of options for each command, enabling smooth integration of Ansible functionality into your projects.

License: MIT License

Go 99.70% Makefile 0.30%
ansible golang golang-library ansible-playbook

go-ansible's Introduction

go-ansible

License: MIT Test Go Report Card Go Reference

go-ansible-logo

Go-ansible is a Go package that allows executing Ansible commands, such as ansible-playbook, ansible-inventory, or ansible, directly from Golang applications. It offers a variety of options for each command, facilitating seamless integration of Ansible functionality into your projects. It is important to highlight that go-ansible is not an alternative implementation of Ansible, but rather a wrapper around the Ansible commands. Let's dive in and explore the capabilities of go-ansible together.

Important: The master branch may contain unreleased or pre-released features. Be cautious when using that branch in your projects. It is recommended to use the stable releases available in the releases.

Note The latest major version of go-ansible, version 2.x, introduced significant and breaking changes. The first change is that the package name has been changed from github.com/apenella/go-ansible to github.com/apenella/go-ansible/v2. So, you need to update your import paths to use the new module name. To migrate your code from prior version to 2.x, please refer to the upgrade guide for detailed information on how to migrate to version 2.x. The most relevant change is that command structs no longer execute commands. So, AnsiblePlaybookCmd, AnsibleInventoryCmd and AnsibleAdhocCmd do not require an executor anymore. Instead, the executor is responsible for receiving the command to execute and executing it.

Install

Use this command to fetch and install the go-ansible module. You can install the release candidate version by executing the following command:

go get github.com/apenella/go-ansible/[email protected]

You can also install the previous stable version by executing the following command:

go get github.com/apenella/go-ansible

Upgrade to 1.x

If you are currently using a go-ansible version before 1.x, note that there have been significant breaking changes introduced in version 1.0.0 and beyond. Before proceeding with the upgrade, we highly recommend reading the changelog and the upgrade guide carefully. These resources provide detailed information on the changes and steps required for a smooth transition to the new version.

Upgrade to 2.x

Version 2.0.0 introduced notable changes since the major version 1, including several breaking changes. The upgrade guide conveys the necessary information to migrate to version 2.x. Thoroughly read that document and the changelog before upgrading from version 1.x to 2.x.

Concepts

There are a few concepts that you need to understand before using the go-ansible library. These concepts are essential to effectively use the library and to understand the examples and usage references provided in this document.

Executor

An executor is a component that executes commands and handles the results from the execution output. The library includes the DefaultExecute executor, which is a ready-to-go implementation of an executor. If the DefaultExecute does not meet your requirements, you can also create a custom executor. To know more about the DefaultExecute, refer to that section.

Command Generator

A command generator or a commander is responsible for generating the command to be executed. The AnsiblePlaybookCmd and AnsibleAdhocCmd structs are examples of command generators. That concept has been introduced in the major version 2.0.0.

Results Handler

A results handler or a results outputer is responsible for managing the output of the command execution. The library includes two output mechanisms: the DefaultResults and the JSONStdoutCallbackResults structs.

Considerations

Before you proceed further, please take note of the following considerations to ensure optimal usage of the go-ansible library.

Execute go-ansible inside a container

When executing Ansible commands using the go-ansible library inside a container, ensure that the container has configured an init system. The init system is necessary to manage the child processes created by the Ansible commands. If the container does not have an init system, the child processes may not be correctly managed, leading to unexpected behavior such as zombie processes.

You can read more about that in the issue 139 and here.

Disable pseudo-terminal allocation

Ansible commands forces the pseudo-terminal allocation when executed in a terminal. That configuration can cause that the SSH connection leaves zombie processes when the command finished. If you are experiencing this issue, you can disable the pseudo-terminal allocation by setting the -T to the SSH extra arguments, that will disable the pseudo-terminal allocation.

You can read more about that in the issue 139 and here.

Getting Started

This section will guide you through the step-by-step process of using the go-ansible library. Follow these instructions to create an application that utilizes the ansible-playbook utility. The same guidelines can be applied to other Ansible commands, such as the ansible or ansible-inventory command.

Note The following example will guide you through a complete process of creating all the components necessary to execute an ansible-playbook command. For a simpler example utilizing the AnsiblePlaybookExecute struct, please refer to the ansibleplaybook-simple example in the repository.

Before proceeding, ensure you have installed the latest version of the go-ansible library. If not, please refer to the Installation section for instructions.

To create an application that launches the ansible-playbook command you need to create an AnsiblePlaybookCmd struct. This struct generates the Ansible command to be run. Then, you need to execute the command using an executor](#executor). In that guided example, you will use the DefaultExecute executor, an executor provided by the go-ansible library.

Create the AnsiblePlaybookCmd struct

To execute ansible-playbook commands, first, define the necessary connection, playbook, and privilege escalation options.

Start by creating the AnsiblePlaybookOptions struct:

ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
  Become:     true,
  Connection: "local",
  Inventory:  "127.0.0.1,",
}

Finally, create the AnsiblePlaybookCmd struct that generates the command to execute the playbook site.yml using the ansible-playbook command:

playbookCmd := playbook.NewAnsiblePlaybookCmd(
  playbook.WithPlaybooks("site.yml", "site2.yml"),
  playbook.WithPlaybookOptions(ansiblePlaybookOptions),
)

Once the AnsiblePlaybookCmd is defined, provide the command to an executor to run the command.

Create the DefaultExecute executor

We will use the DefaultExecute struct, provided by the go-ansible library, to execute the ansible-playbook command. It requires a Commander responsible for generating the command to be executed. In that example, you will use the AnsiblePlaybookCmd previously defined.

// PlaybookCmd is the Commander responsible for generating the command to execute
exec := execute.NewDefaultExecute(
  execute.WithCmd(playbookCmd),
)

Once you have defined the DefaultExecute, execute the Ansible command using the following code:

err := exec.Execute(context.Background())
if err != nil {
  // Manage the error
}

Manage the output of the command execution

By default, the DefaultExecute uses the DefaultResults struct to manage the output of the command execution. The DefaultResults struct handles the output as plain text.

 ──
 ── PLAY [all] *********************************************************************
 ──
 ── TASK [Gathering Facts] *********************************************************
 ── ok: [127.0.0.1]
 ──
 ── TASK [simple-ansibleplaybook] **************************************************
 ── ok: [127.0.0.1] => {
 ──     "msg": "Your are running 'simple-ansibleplaybook' example"
 ── }
 ──
 ── PLAY RECAP *********************************************************************
 ── 127.0.0.1                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
 ──
 ── Playbook run took 0 days, 0 hours, 0 minutes, 0 seconds

Usage Reference

The Usage Reference section provides an overview of the different packages and their main resources available in the go-ansible library. Here you will find the details to effectively use the library to execute Ansible commands, such as ansible-playbook and ansible. For detailed information on the library's packages, structs, methods, and functions, please refer to the complete reference available here.

Adhoc package

This section provides an overview of the adhoc package in the go-ansible library, outlining its key components and functionalities.

The github.com/apenella/go-ansible/v2/pkg/adhoc package facilitates the generation of Ansible ad-hoc commands. It does not execute the commands directly, but instead provides the necessary structs to generate the command to be executed by an executor. The adhoc package includes the following essential structs for executing ad-hoc commands:

AnsibleAdhocCmd struct

The AnsibleAdhocCmd struct enables the generation of Ansible ad-hoc commands. It implements the Commander interface, so its method Command returns an array of strings that represents the command to be executed. An executor can use it to create the command to be executed.

The package provides the NewAnsibleAdhocCmd function to create a new instance of the AnsibleAdhocCmd struct. The function accepts a list of options to customize the ad-hoc command. The following functions are available:

  • WithAdhocOptions(options *AnsibleAdhocOptions) AdhocOptionsFunc: Set the ad-hoc options for the command.
  • WithBinary(binary string) AdhocOptionsFunc: Set the binary for the ad-hoc command.
  • WithPattern(pattern string) AdhocOptionsFunc: Set the pattern for the ad-hoc command.

The following code snippet demonstrates how to use the AnsibleAdhocCmd struct to generate an ad-hoc command:

ansibleAdhocOptions := &adhoc.AnsibleAdhocOptions{
  Inventory:  "127.0.0.1,",
  ModuleName: "debug",
  Args:       "msg={{ arg }}",
  ExtraVars:  map[string]interface{}{
      "arg": "value",
  }
}

adhocCmd := adhoc.NewAnsibleAdhocCmd(
  adhoc.WithPattern("all"),
  adhoc.WithAdhocOptions(ansibleAdhocOptions),
)

// Generate the command to be executed
cmd, err := adhocCmd.Command()
if err != nil {
  // Manage the error
}

AnsibleAdhocExecute struct

The AnsibleAdhocExecute struct serves as a streamlined executor for running ansible command. It encapsulates the setup process for both the command generator and executor. This executor is particularly useful when no additional configuration or customization is required.

The following methods are available to set attributes for the AnsibleAdhocCmd struct:

  • WithBinary(binary string) *AnsibleAdhocExecute: The method sets the Binary attribute.
  • WithAdhocOptions(options *AnsibleAdhocOptions) *AnsibleAdhocExecute: The method sets the AdhocOptions attribute.

Here is an example of launching an ansible command using AnsibleAdhocExecute:

err := adhoc.NewAnsibleAdhocExecute("all").
  WithAdhocOptions(ansibleAdhocOptions).
  Execute(context.TODO())

if err != nil {
  // Manage the error
}

AnsibleAdhocOptions struct

With AnsibleAdhocOptions struct, you can define parameters described in Ansible's manual page's Options section. On the same struct, you can define the connection options and privilage escalation options.

Execute package

The execute package, available at github.com/apenella/go-ansible/v2/pkg/execute, provides the DefaultExecute, a ready-to-use executor. Additionally, the package defines some interfaces for managing the command execution and customizing the behavior of the executor.

Find below the main resources available in the execute package:

Executor interface

The Executor interface defines components responsible for executing external commands. The DefaultExecute struct implements this interface. Below is the definition of the Executor interface:

type Executor interface {
  Execute(ctx context.Context) error
}

Commander interface

The Commander interface defines components responsible for generating the command to be executed. The AnsiblePlaybookCmd and AnsibleAdhocCmd structs implement this interface. Below is the definition of the Commander interface:

type Commander interface {
  Command() ([]string, error)
  String() string
}

ErrorEnricher interface

The ErrorEnricher interface defines components responsible for enriching the error message. The AnsiblePlaybookErrorEnrich struct implements this interface. The DefaultExecute struct uses this interface to enrich the error message. Below is the definition of the ErrorEnricher interface:

type ErrorEnricher interface {
  Enrich(err error) error
}

Executabler interface

The Executabler interface defines a component required by DefaultExecute to execute commands. Through the Executabler interface, you can customize the execution of commands according to your requirements.

Below is the definition of the Executabler interface:

type Executabler interface {
  Command(name string, arg ...string) exec.Cmder
  CommandContext(ctx context.Context, name string, arg ...string) exec.Cmder
}

DefaultExecute struct

The DefaultExecute executor is a component provided by the go-ansible library for managing the commands execution. It offers flexibility and customization options to suit various use cases. Think of the DefaultExecute executor as a pipeline that handles command execution. It consists of three main stages, each managed by a different component:

  • Commander: Generates the command to be executed.
  • Executabler: Executes the command.
  • ResultsOutputer: Manages the output of the command execution.

By default, the DefaultExecute executor uses the OsExec struct as the Executabler for executing commands, a wrapper around the os/exec package. It also uses the DefaultResults struct as the ResultsOutputer for managing the output of the command execution. However, you can customize these components to tailor the execution process to your needs.

The following functions can be provided when creating a new instance of the DefaultExecute to customize its behavior. All of them are available in the github.com/apenella/go-ansible/v2/pkg/execute package:

  • WithCmd(cmd Commander) ExecuteOptions: Set the component responsible for generating the command.
  • WithCmdRunDir(cmdRunDir string) ExecuteOptions: Define the directory where the command will be executed.
  • WithEnvVars(vars map[string]string) ExecuteOptions: Set environment variables for command execution.
  • WithErrorEnricher(errEnricher ErrorEnricher) ExecuteOptions: Define the component responsible for enriching the error message.
  • WithExecutable(executable Executabler) ExecuteOptions: Define the component responsible for executing the command.
  • WithOutput(output result.ResultsOutputer) ExecuteOptions: Specify the component responsible for managing command output.
  • WithTransformers(trans ...transformer.TransformerFunc) ExecuteOptions: Add transformers to modify command output.
  • WithWrite(w io.Writer) ExecuteOptions: Set the writer for command output.
  • WithWriteError(w io.Writer) ExecuteOptions: Set the writer for command error output.

The snippet below shows how to customize the DefaultExecute executor using the ExecuteOptions functions:

// PlaybookCmd is the Commander responsible for generating the command to execute
playbookCmd := playbook.NewAnsiblePlaybookCmd(
  playbook.WithPlaybooks("site.yml"),
  playbook.WithPlaybookOptions(ansiblePlaybookOptions),
)

// MyExecutabler is an hypothetical implementation of the Executabler interface
executabler := &myExecutabler{}

// MyOutputer is an hypothetical implementation of the ResultsOutputer interface
output := &myOutputer{}

// Exec is an instance of the DefaultExecute executor
exec := execute.NewDefaultExecute(
  execute.WithCmd(playbookCmd),
  execute.WithExecutable(executabler),
  execute.WithOutput(output),
)

// Execute the ansible-playbook command
err := exec.Execute(context.Background())
if err != nil {
  // Manage the error
}

For more examples and practical use cases, refer to the examples directory in the go-ansible repository.

Defining a Custom Executor

If DefaultExecute does not meet your requirements or expectations, you have the option to implement a custom executor. Below is an example of a custom executor that demonstrates how to integrate it with the AnsiblePlaybookCmd or AnsibleAdhocCmd structs to execute the playbook with your desired behavior:

type MyExecutor struct {
  Prefix string
  Cmd    Commander
}

func (e *MyExecutor) Execute(ctx context.Context) error {
  // That's a dummy work
  fmt.Println(fmt.Sprintf("[%s] %s\n", e.Prefix, "I am a lazy executor and I am doing nothing"))
  return nil
}

The next code snippet demonstrates how to execute the ansible-playbook command using the custom executor:

// Define the command to execute
playbookCmd := playbook.NewAnsiblePlaybookCmd(
  playbook.WithPlaybooks("site.yml"),
  playbook.WithPlaybookOptions(ansiblePlaybookOptions),
)

// Define an instance for the new executor and set the options
exec := &MyExecutor{
  Prefix: "go-ansible example",
  Cmd:    playbookCmd,
}

err := exec.Execute(context.Background())
if err != nil {
  // Manage the error
}

When you run the playbook using the custom executor, the output will be:

 [go-ansible example] I am a lazy executor and I am doing nothing

Customizing the Execution

The go-ansible library offers a range of options to configure and customize the execution of the Ansible commands. These customization capabilities were introduced in version 2.0.0, when the executor became the central component in the execution process.

In the following sections, we will explore the components available for customizing the execution process:

Configuration package

The github.com/apenella/go-ansible/v2/pkg/execute/configuration package provides components for configuring the Ansible settings during command execution. In the following sections, we will explore the available elements for customizing the execution process.

ExecutorEnvVarSetter interface

The ExecutorEnvVarSetter interface extends the Executor interface with the capability of setting environment variables for the command execution. The DefaultExecute struct implements this interface. Below is the definition of the ExecutorEnvVarSetter interface:

type ExecutorEnvVarSetter interface {
  execute.Executor
  AddEnvVar(key, value string)
}
Ansible Configuration functions

The github.com/apenella/go-ansible/v2/pkg/execute/configuration package provides a set of functions for configuring Ansible settings during command execution. Each function corresponds to a configuration setting available in Ansible's reference guide. The functions follow a consistent naming convention: With<setting name> or Without<setting name>, where <setting name> is the name of the Ansible setting to be configured.

AnsibleWithConfigurationSettingsExecute struct

The AnsibleWithConfigurationSettingsExecute struct serves as a decorator over an ExecutorEnvVarSetter, enabling configuration of Ansible settings for execution. When instantiating a new AnsibleWithConfigurationSettingsExecute, you must provide an ExecutorEnvVarSetter and a list of functions for configuring Ansible settings. Here you can see an example of how to use the AnsibleWithConfigurationSettingsExecute struct to configure Ansible settings for execution:

ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
  Connection: "local",
  Inventory:  "127.0.0.1,",
}

playbookCmd := playbook.NewAnsiblePlaybookCmd(
  playbook.WithPlaybooks("site.yml"),
  playbook.WithPlaybookOptions(ansiblePlaybookOptions),
)

exec := configuration.NewAnsibleWithConfigurationSettingsExecute(
  execute.NewDefaultExecute(
    execute.WithCmd(playbookCmd),
  ),
  configuration.WithAnsibleForceColor(),
  configuration.WithAnsibleForks(10),
  configuration.WithAnsibleHome("/path/to/ansible/home"),
  configuration.WithAnsibleHostKeyChecking(),
  configuration.WithoutAnsibleActionWarnings(),
)

err := exec.Execute(context.Background())
if err != nil {
  // Manage the error
}
Measure package

The go-ansible library offers a convenient mechanism for measuring the execution time of Ansible commands through the github.com/apenella/go-ansible/v2/pkg/execute/measure package. This package includes the ExecutorTimeMeasurement struct, which acts as a decorator over an Executor to track the time taken for command execution.

To illustrate, consider the following code snippet, which demonstrates how to use the ExecutorTimeMeasurement struct to measure the time it takes to execute the ansible-playbook command:

ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
  Connection: "local",
  Inventory:  "127.0.0.1,",
}

playbookCmd := playbook.NewAnsiblePlaybookCmd(
  playbook.WithPlaybooks("site.yml"),
  playbook.WithPlaybookOptions(ansiblePlaybookOptions),
)

executorTimeMeasurement := measure.NewExecutorTimeMeasurement(
  execute.NewDefaultExecute(
    execute.WithCmd(playbookCmd),
  ),
)

err := executorTimeMeasurement.Execute(context.Background())
if err != nil {
  // Manage the error
}

fmt.Println("Duration: ", exec.Duration().String())

For a complete example showcasing how to use measurement, refer to the ansibleplaybook-time-measurement example in the go-ansible repository.

Result package

The github.com/apenella/go-ansible/v2/pkg/execute/result package provides a set of components and subpackages to manage the output of Ansible commands. The following sections describe the available elements.

ResultsOutputer interface

The ResultsOutputer interface in the github.com/apenella/go-ansible/v2/pkg/execute/result package defines a component responsible for managing the output of command execution within the go-ansible library. Both the DefaultResults and JSONStdoutCallbackResults structs implement this interface. Below is the definition of the ResultsOutputer interface:

type ResultsOutputer interface {
  Print(ctx context.Context, reader io.Reader, writer io.Writer, options ...OptionsFunc) error
}
DefaultResults struct

The DefaultResults struct, located in the github.com/apenella/go-ansible/v2/pkg/execute/result/default package, serves as the default output manager for command execution within the go-ansible library. It implements the ResultsOutputer interface, providing functionality to handle command output as plain text.

The DefaultResults struct reads the execution output line by line and applies a set of transformers to each line. A transformer allows you to enrich or modify of the output according to your specific requirements. You can specify the transformers during the instantiation of a new DefaultResults instance or when configuring the DefaultExecute executor. Here you have an example of how to specify them when creating a new instance of DefaultExecute:

exec := execute.NewDefaultExecute(
  execute.WithCmd(playbook),
  execute.WithTransformers(
    transformer.Prepend("Go-ansible example"),
    transformer.LogFormat(transformer.DefaultLogFormatLayout, transformer.Now),
  ),
)
JSONStdoutCallbackResults struct

The JSONStdoutCallbackResults struct, located in the github.com/apenella/go-ansible/v2/pkg/execute/result/json package, is designed to handle the output of command execution when using the JSON stdout callback method. It implements the ResultsOutputer interface, providing functionality to parse and manipulate JSON-formatted output from Ansible commands.

The package also includes the ParseJSONResultsStream function, which decodes the JSON output into an AnsiblePlaybookJSONResults data structure. This structure can be further manipulated to format the JSON output according to specific requirements. The expected JSON schema from Ansible output is defined in the json.py file within the Ansible repository.

When creating a new instance of JSONStdoutCallbackResults, you can specify a list of transformers to be applied to the output. These transformers enrich or update the output as needed. By default, the JSONStdoutCallbackResults struct applies the IgnoreMessage transformer to ignore any non-JSON lines defined in the skipPatterns array.

Here you have the skipPatterns definition within the JSONStdoutCallbackResults code:

skipPatterns := []string{
    // This pattern skips timer's callback whitelist output
    "^[\\s\\t]*Playbook run took [0-9]+ days, [0-9]+ hours, [0-9]+ minutes, [0-9]+ seconds$",
  }

The following code snippet demonstrates how to use the JSONStdoutCallbackResults struct to manage the output of command execution:

executorTimeMeasurement := stdoutcallback.NewJSONStdoutCallbackExecute(
  execute.NewDefaultExecute(
    execute.WithCmd(playbookCmd),
    // The default executor writes the output to the buffer to be parsed by the JSONStdoutCallbackResults
    execute.WithWrite(io.Writer(buff)),
  ),
)

err = exec.Execute(context.TODO())
if err != nil {
  // Manage the error
}

// Parse the JSON output from the buffer
res, err = results.ParseJSONResultsStream(io.Reader(buff))
if err != nil {
  // Manage the error
}

fmt.Println(res.String())

For a detailed example showcasing how to use measurement, refer to the ansibleplaybook-json-stdout example in the go-ansible repository.

Transformer functions

In go-ansible, transformer functions are essential components that enrich or update the output received from the executor, allowing users to customize the output according to their specific requirements. Each transformer function follows the signature defined by the TransformerFunc type:

// TransformerFunc is used to enrich or update messages before to be printed out
type TransformerFunc func(string) string

When the output is received from the executor, it undergoes processing line by line, with each line being passed through the available transformers. The github.com/apenella/go-ansible/v2/pkg/execute/result/transformer package provides a set of ready-to-use transformers, and users can also create custom transformers as needed.

Here you have the transformer functions available in the github.com/apenella/go-ansible/v2/pkg/execute/result/transformer package:

  • Prepend: Adds a specified prefix string to each output line.
transformer.Prepend("Prefix: ")
  • Append: Adds a specified suffix string to each output line.
transformer.Append(" [suffix]")
  • LogFormat: Prepends each output line with a date-time prefix.
transformer.LogFormat(transformer.DefaultLogFormatLayout, transformer.Now)
  • IgnoreMessage: Filters out output lines based on specified patterns. It uses the regexp.MatchString function to match the output lines with the specified patterns.
skipPatterns := []string{"regexp-pattern1", "regexp-pattern2"}
transformer.IgnoreMessage(skipPatterns...)
Stdoutcallback package

The github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback package in the go-ansible library facilitates the management of Ansible's stdout callback method. Configuring the stdout callback method typically involves two steps:

  • Setting the ANSIBLE_STDOUT_CALLBACK environment variable to specify the desired stdout callback plugin name.
  • Defining the method responsible for handling the results output from the command execution.

To streamline the configuration process, the go-ansible library provides a collection of structs dedicated to setting each stdout callback method, along with the ExecutorStdoutCallbackSetter interface.

The following sections detail the available components for configuring the stdout callback method:

ExecutorStdoutCallbackSetter interface

The ExecutorStdoutCallbackSetter interface extends the capabilities of the Executor interface by enabling the setting of environment variables and specifying the results output mechanism for command execution. The DefaultExecute struct implements this interface, providing flexibility in configuring the stdout callback method.

Below is the definition of the ExecutorStdoutCallbackSetter interface:

type ExecutorStdoutCallbackSetter interface {
  execute.Executor
  AddEnvVar(key, value string)
  WithOutput(output result.ResultsOutputer)
}
ExecutorQuietStdoutCallbackSetter interface

The ExecutorQuietStdoutCallbackSetter interface in the github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback package extends the ExecutorStdoutCallbackSetter interface with the capability to remove the verbosity of the command execution output. The DefaultExecute struct implements this interface, allowing you to silence the output of the command execution.

That interface is required by the JSONStdoutCallbackExecute struct to remove the verbosity of the command execution output.

The next code snippet shows the definition of the ExecutorQuietStdoutCallbackSetter interface:

type ExecutorQuietStdoutCallbackSetter interface {
  ExecutorStdoutCallbackSetter
  Quiet()
}
Stdout Callback Execute structs

The github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback package provides a collection of structs designed to simplify the configuration of stdout callback methods for Ansible command execution. These structs act as decorators over an ExecutorStdoutCallbackSetter, allowing seamless integration of different stdout callback plugins with command execution.

Each stdout callback method in Ansible corresponds to a specific struct in go-ansible, making it easy to select and configure the desired method. Here are the available structs:

  • DebugStdoutCallbackExecute
  • DefaultStdoutCallbackExecute
  • DenseStdoutCallbackExecute
  • JSONStdoutCallbackExecute
  • MinimalStdoutCallbackExecute
  • NullStdoutCallbackExecute
  • OnelineStdoutCallbackExecute
  • StderrStdoutCallbackExecute
  • TimerStdoutCallbackExecute
  • YamlStdoutCallbackExecute

For example, to configure the JSON stdout callback method for command execution, you can use the JSONStdoutCallbackExecute struct as shown in the following code snippet:

execJson := stdoutcallback.NewJSONStdoutCallbackExecute(
  execute.NewDefaultExecute(
    execute.WithCmd(playbookCmd),
  )
)

err := execJson.Execute(context.Background())
if err != nil {
  // Manage the error
}
Workflow package

The github.com/apenella/go-ansible/v2/pkg/execute/workflow package provides an executor that allows you to run a sequence of executors.

WorkflowExecute struct

The WorkflowExecute struct is responsible for managing the execution of a sequence of executors. It allows you to define a workflow for running Ansible commands. Additionally, the WorkflowExecute implements the Executor interface, enabling you to apply some decorators such as the ExecutorTimeMeasurement to the workflow.

By default, when an executor execution in the sequence fails, the WorkflowExecute stops the execution of the remaining executors. However, you can customize this behaviour by setting the ContinueOnError attribute to true. In that case, if one executor fails, the WorkflowExecute continues with the remaining executors and raises an error at the end of the execution.

The WorkflowExecute struct provides the following methods to setup the execution process:

  • AppendExecutor(exec Executor) *WorkflowExecute: Appends an executor to the sequence.
  • Execute(ctx context.Context) error: Executes the sequence of executors.
  • WithContinueOnError() *WorkflowExecute: Sets the ContinueOnError attribute to true.

Here is an example of how to use the WorkflowExecute struct to run a sequence of executors:

exec1 := playbook.NewAnsiblePlaybookExecute("first.yml").
    WithPlaybookOptions(ansiblePlaybookOptions)

exec2 := playbook.NewAnsiblePlaybookExecute("second.yml").
    WithPlaybookOptions(ansiblePlaybookOptions)

err := workflow.NewWorkflowExecute(exec1, exec2).
    WithContinueOnError().
    Execute(context.TODO())

if err != nil {
  // Manage the error
}

Galaxy package

The go-ansible library provides you with the ability to interact with the Ansible Galaxy command-line tool. To do that it includes the following package:

Galaxy Collection Install package

The github.com/apenella/go-ansible/v2/pkg/galaxy/collection/install package allows you to install collections from the Ansible Galaxy using the ansible-galaxy command. The package provides the following structs and functions:

AnsibleGalaxyCollectionInstallCmd struct

The AnsibleGalaxyCollectionInstallCmd struct enables the generation of ansible-galaxy commands to install collections. It implements the Commander interface, so its method Command returns an array of strings that represents the command to be executed. An executor can use it to create the command to be executed.

The package provides the NewAnsibleGalaxyCollectionInstallCmd function to create a new instance of the AnsibleGalaxyCollectionInstallCmd struct. The function accepts a list of options to customize the ansible-galaxy command. The following functions are available:

  • WithBinary(binary string) AnsibleGalaxyCollectionInstallOptionsFunc: Set the binary for the ansible-galaxy command.
  • WithCollectionInstallOptions(options *AnsibleGalaxyCollectionInstallOptions) AnsibleGalaxyCollectionInstallOptionsFunc: Set the collection install options for the command.
  • WithCollectionNames(collectionNames ...string) AnsibleGalaxyCollectionInstallOptionsFunc: Set the collection names for the ansible-galaxy command.
AnsibleGalaxyCollectionInstallOptions struct

The AnsibleGalaxyCollectionInstallOptions struct includes parameters described in the Options section of the Ansible Galaxy manual page. It defines the behavior of the Ansible Galaxy collection installation operations and specifies where to find the configuration settings.

Galaxy Role Install package

The github.com/apenella/go-ansible/v2/pkg/galaxy/role/install package allows you to install roles from the Ansible Galaxy using the ansible-galaxy command. The package provides the following structs and functions:

AnsibleGalaxyRoleInstallCmd struct

The AnsibleGalaxyRoleInstallCmd struct enables the generation of ansible-galaxy commands to install roles. It implements the Commander interface, so its method Command returns an array of strings that represents the command to be executed. An executor can use it to create the command to be executed.

The package provides the NewAnsibleGalaxyRoleInstallCmd function to create a new instance of the AnsibleGalaxyRoleInstallCmd struct. The function accepts a list of options to customize the ansible-galaxy command. The following functions are available:

  • WithBinary(binary string) AnsibleGalaxyRoleInstallOptionsFunc: Set the binary for the ansible-galaxy command.
  • WithGalaxyRoleInstallOptions(options *AnsibleGalaxyRoleInstallOptions) AnsibleGalaxyRoleInstallOptionsFunc: Set the role install options for the command.
  • WithRoleNames(roleNames ...string) AnsibleGalaxyRoleInstallOptionsFunc: Set the role names for the ansible-galaxy command.
AnsibleGalaxyRoleInstallOptions struct

The AnsibleGalaxyRoleInstallOptions struct includes parameters described in the Options section of the Ansible Galaxy manual page. It defines the behavior of the Ansible Galaxy role installation operations and specifies where to find the configuration settings.

Inventory package

The information provided in this section gives an overview of the Inventory package in go-ansible.

The github.com/apenella/go-ansible/v2/pkg/inventory package provides the functionality to execute ansible-inventory. To perform these tasks, you can use the following inventory structs:

AnsibleInventoryCmd struct

The AnsibleInventoryCmd struct enables the generation of ansible-inventory commands. It implements the Commander interface, so its method Command returns an array of strings that represents the command to be executed. An executor can use it to create the command to be executed.

The package provides the NewAnsibleInventoryCmd function to create a new instance of the AnsibleInventoryCmd struct. The function accepts a list of options to customize the ansible-inventory command. The following functions are available:

  • WithBinary(binary string) InventoryOptionsFunc: Set the binary for the ansible-inventory command.
  • WithInventoryOptions(options *AnsibleInventoryOptions) InventoryOptionsFunc: Set the inventory options for the command.
  • WithPattern(pattern string) InventoryOptionsFunc: Set the pattern for the ansible-inventory command.

Note Unlike other Ansible commands, the ansible-inventory command does not provide privilege escalation or connection options, aligning with the functionality of the command itself.

AnsibleInventoryExecute struct

The AnsibleInventoryExecute struct serves as a streamlined executor for running ansible-inventory commands. It encapsulates the setup process for both the command generator and executor. This executor is particularly useful when no additional configuration or customization is required.

The following methods are available to set attributes for the AnsibleInventoryCmd struct:

  • WithBinary(binary string) *AnsibleInventoryExecute: The method sets the Binary attribute.
  • WithInventoryOptions(options *AnsibleAdhocOptions) *AnsibleInventoryExecute: The method sets the InventoryOptions attribute.
  • WithPattern(pattern string) *AnsibleInventoryExecute: The method sets the Pattern attribute.

Here is an example of launching an ansible-inventory command using AnsibleInventoryExecute:

err := inventory.NewAnsibleInventoryExecute().
  WithInventoryOptions(&ansibleInventoryOptions).
  WithPattern("all").
  Execute(context.TODO())

if err != nil {
  // Manage the error
}

AnsibleInventoryOptions struct

The AnsibleInventoryOptions struct includes parameters described in the Options section of the Ansible manual page. It defines the behavior of the Ansible inventory operations and specifies where to find the configuration settings.

Playbook package

This section provides an overview of the playbook package in the go-ansible library. Here are described its main components and functionalities.

The github.com/apenella/go-ansible/v2/pkg/playbook package facilitates the generation of ansible-playbook commands. It does not execute the commands directly, but instead provides the necessary structs to generate the command to be executed by an executor. The playbook package includes the following essential structs for executing ad-hoc commands:

AnsiblePlaybookCmd struct

The AnsiblePlaybookCmd struct enables the generation of ansible-playbook commands. It implements the Commander interface, so its method Command returns an array of strings that represents the command to be executed. An executor can use it to create the command to be executed.

The package provides the NewAnsiblePlaybookCmd function to create a new instance of the AnsiblePlaybookCmd struct. The function accepts a list of options to customize the ansible-playbook command. The following functions are available:

  • WithBinary(binary string) PlaybookOptionsFunc: Set the binary for the ansible-playbook command.
  • WithPlaybookOptions(options *AnsiblePlaybookOptions) PlaybookOptionsFunc: Set the playbook options for the command.
  • WithPlaybooks(playbooks ...string) PlaybookOptionsFunc: Set the playbooks for the ansible-playbook command.

Next is an example of how to use the AnsiblePlaybookCmd struct to generate an ansible-playbook command:

ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
  Connection: "local",
  Become:     true,
  Inventory:  "127.0.0.1,",
}

playbookCmd := playbook.NewAnsiblePlaybookCmd(
  playbook.WithPlaybooks("site.yml"),
  playbook.WithPlaybookOptions(ansiblePlaybookOptions),
)

// Generate the command to be executed
cmd, err := playbookCmd.Command()
if err != nil {
  // Manage the error
}

AnsiblePlaybookErrorEnrich struct

The AnsiblePlaybookErrorEnrich struct, that implements the ErrorEnricher interface, is responsible for enriching the error message when executing an ansible-playbook command. Based on the exit code of the command execution, the AnsiblePlaybookErrorEnrich struct appends additional information to the error message. This additional information includes the exit code, the command that was executed, and the error message.

AnsiblePlaybookExecute struct

The AnsiblePlaybookExecute struct serves as a streamlined executor for running ansible-playbook command. It encapsulates the setup process for both the command generator and executor. Additionally, it provides the ability to enrich the error message when an error occurs during command execution. This executor is particularly useful when no additional configuration or customization is required.

The following methods are available to set attributes for the AnsiblePlaybookCmd struct:

  • WithBinary(binary string) *AnsiblePlaybookExecute: The method sets the Binary attribute.
  • WithPlaybookOptions(options *AnsiblePlaybookOptions) *AnsiblePlaybookExecute: The method sets the PlaybookOptions attribute.

Here is an example of launching an ansible-playbook command using AnsiblePlaybookExecute:

err := playbook.NewAnsiblePlaybookExecute("site.yml", "site2.yml").
  WithPlaybookOptions(ansiblePlaybookOptions).
  Execute(context.TODO())

if err != nil {
  // Manage the error
}

AnsiblePlaybookOptions struct

With AnsiblePlaybookOptions struct, you can define parameters described in Ansible's manual page's Options section. It also allows you to define the connection options and privilege escalation options.

Vault package

The github.com/apenella/go-ansible/v2/pkg/vault package provides functionality to encrypt variables. It introduces the VariableVaulter struct, which is responsible for creating a VaultVariableValue from the value that you need to encrypt.

The VaultVariableValue can return the instantiated variable in JSON format.

To perform the encryption, the vault package relies on an Encrypter interface implementation.

type Encrypter interface {
  Encrypt(plainText string) (string, error)
}

The encryption functionality is implemented in the encrypt package, which is described in the following section.

Encrypt

The github.com/apenella/go-ansible/v2/pkg/vault/encrypt package is responsible for encrypting variables. It implements the Encrypter interface defined in the github.com/apenella/go-ansible/v2/pkg/vault package.

Currently, the package provides the EncryptString struct, which supports the encryption of string variables. It utilizes the github.com/sosedoff/ansible-vault-go library for encryption.

To use the EncryptString struct, you need to instantiate it with a password reader. The password reader is responsible for providing the password used for encryption and it should implement the PasswordReader interface:

type PasswordReader interface {
  Read() (string, error)
}

Here's an example of how to instantiate the EncryptString struct:

encrypt := NewEncryptString(
  WithReader(
    text.NewReadPasswordFromText(
      text.WithText("secret"),
    ),
  ),
)

In this example, the text.NewReadPasswordFromText function is used to create a password reader that reads the password from a text source. The WithText option is used to specify the actual password value.

Password

The go-ansible library provides a set of packages that can be used as PasswordReader to read the password for encryption. The following sections describe these packages and how they can be used.

Envvars

The github.com/apenella/go-ansible/v2/pkg/vault/password/envvars package allows you to read the password from an environment variable. To use this package, you need to use the NewReadPasswordFromEnvVar function and provide the name of the environment variable where the password is stored using the WithEnvVar option:

reader := NewReadPasswordFromEnvVar(
  WithEnvVar("VAULT_PASSWORD"),
)

In this example, the VAULT_PASSWORD environment variable is specified as the source of the password. The NewReadPasswordFromEnvVar function creates a password reader that reads the password from the specified environment variable.

Using the envvars package, you can conveniently read the password from an environment variable and use it for encryption.

File

The github.com/apenella/go-ansible/v2/pkg/vault/password/file package allows you to read the password from a file, using the afero file system abstraction.

To use this package, you need to instantiate the NewReadPasswordFromFile function and provide the necessary options. The WithFs option is used to specify the file system, and the WithFile option is used to specify the path to the password file.

If you don't explicitly define a file system, the package uses the default file system, which is the OsFs from the github.com/spf13/afero package. The OsFs represents the file system of your host machine.

Therefore, if you don't provide a specific file system using the WithFs option when instantiating the password reader, the file package will automatically use the OsFs as the file system to read the password from a file.

Here's an example without specifying the file system:

reader := NewReadPasswordFromFile(
  WithFile("/password"),
)

In this case, the OsFs will be used to access the /password file on your host file system.

Resolve

The github.com/apenella/go-ansible/v2/pkg/vault/password/resolve package provides a mechanism to resolve the password by exploring multiple PasswordReader implementations. It returns the first password obtained from any of the PasswordReader instances.

To use this package, you need to create a NewReadPasswordResolve instance and provide a list of PasswordReader implementations as arguments to the WithReader option:

reader := NewReadPasswordResolve(
  WithReader(
    envvars.NewReadPasswordFromEnvVar(
      envvars.WithEnvVar("VAULT_PASSWORD"),
    ),
    file.NewReadPasswordFromFile(
      file.WithFs(testFs),
      file.WithFile("/password"),
    ),
  ),
)

In this example, the ReadPasswordResolve instance is created with two PasswordReader implementations: one that reads the password from an environment variable (envvars.NewReadPasswordFromEnvVar), and another that reads the password from a file (file.NewReadPasswordFromFile).

The ReadPasswordResolve will attempt to obtain the password from each PasswordReader in the provided order. The first successful password read will be returned. It returns an error when no password is achieved.

Using the resolve package, you can explore multiple PasswordReader implementations to resolve the password for encryption.

Text

The github.com/apenella/go-ansible/v2/pkg/vault/password/text package provides functionality to read the password from a text source.

To use this package, you need to instantiate the NewReadPasswordFromText function and provide the password as a text value using the WithText option:

reader := NewReadPasswordFromText(
  WithText("ThatIsAPassword"),
)

In this example, the password is directly specified as the text value "ThatIsAPassword" using the WithText option.

Examples

The go-ansible library includes a variety of examples that demonstrate how to use the library in different scenarios. These examples can be found in the examples directory of the go-ansible repository.

The examples cover various use cases and provide practical demonstrations of utilizing different features and functionalities offered by go-ansible. They serve as a valuable resource to understand and learn how to integrate go-ansible into your applications.

Feel free to explore the examples directory to gain insights and ideas on how to leverage the go-ansible library in your projects.

Here you have a list of examples:

Development Reference

This section provides a reference guide for developing and contributing to the go-ansible library.

Development Environment

To set up a development environment for the go-ansible library, you need to have the following tools installed on your system:

  • Docker Compose. The version used for development is docker-compose version v2.26.1.
  • Docker. The version used for development is Docker version 26.0.1.
  • Go. The version used for development is 1.22.
  • make utility. The version used for development is GNU Make 4.3. It is used to wrap the continuous integration and development processes, such us testing or linting.

Testing

To run the tests, you can use the following command:

make test

Static Analysis

To run the static analysis tools, you can use the following command:

make static-analysis

Contributing

Thank you for your interest in contributing to go-ansible! All contributions are welcome, whether they are bug reports, feature requests, or code contributions. Please read the contributor's guide here to learn more about how to contribute.

Code Of Conduct

The go-ansible project is committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, disability, ethnicity, religion, or similar personal characteristics.

We expect all contributors, users, and community members to follow this code of conduct. This includes all interactions within the go-ansible community, whether online, in person, or otherwise.

Please to know more about the code of conduct refer here.

License

The go-ansible library is available under MIT license.

go-ansible's People

Contributors

abergmeier avatar alexandrevilain avatar apenella avatar asafalima avatar chungeun-choi avatar dependabot[bot] avatar edo-aleix-penella avatar eugenebad avatar febrianrendak avatar gciavarrini avatar jgengo avatar lovexayah avatar lucabodd avatar rvben avatar sestegra avatar svasilevski avatar zhangguanzhang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-ansible's Issues

help with remote options

how can i connect to remote host to run ansible-playbook,the options in the code are local,can somebody share a remote config?

If it is possible to add a list variable as extra-vars

ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'

in the example of go-ansible:

ansiblePlaybookOptions.AddExtraVar("app_name", app_name)

The value of the extra-vars must be string?
If it is possible to add a list variable as extra-vars?
Thank you very much

Typo "resutls"

Both in `docs/upgrade_guide_to_1.0.0.md` and in `pkg/stdoutcallback/results/JSONResults.go` there is the typo "resutls".

Is their a mechanism for hostvars?

Im building my inventory dynamically, but I want to use host variables, ie assume the following traditional inventory:

Can this library implement host variables that would have the same effect as this inventory file?

all:
  vars:
    new_kernel_version: kernel-3.10.0-1160.36.2.el7.x86_64
  children:
    cluster:
      hosts:
        host1:
          ansible_host: host1.example.com
          rack: rack1
          platform: xyz

Ansible playbook with wrong IP hang up

I launch ansible playbook using go-ansible with wrong IP address (real IP is 192.168.2.10, i specify 192.168.2.12 - no host with such IP address). It hangup for more than 600 seconds. Sometimes 1 hour timeout, before i get an error that host is unreachable. No specific settings in code, just run playbook with specific inventory. If i sepcify wrong ssh port (23 instead of 22) - i will get an error in 10-15 seconds.

If i run ansible playbook command directly - i'll get an error very fast.

AnsibleAdhocCmd return error when args has spaces

When I use AnsibleAdhocCmd w/ Args('ping x.x.x.x -c 10'), I see this error. This command works in shell. The root cause is there are spaces in teh args. Any solution?

*errors.Error(&errors.Error{context:"(DefaultExecute::Execute)", message:"Error during command execution: ansible-playbook error: one or more host failed\n\nCommand executed: /usr/bin/ansible all --args 'ping x.x.x.x -c 10' --inventory x.x.x.x, --module-name command --private-key ~/.ssh/gpc_rack_id_rsa\n\nexit status 2", wrappedErrors:[]error(nil)})

warning messages are included on json data when is used json stdout_callback

Which ansible version are you using?
ansible 2.10.5
config file = ??/.ansible.cfg
configured module search path = ['??/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = ??/.local/lib/python3.8/site-packages/ansible
executable location = ??/.local/bin/ansible
python version = 3.8.5 (default, Jul 28 2020, 12:59:40) [GCC 9.3.0]

Which go version are you using?
go version go1.15.6 linux/amd64

Which go-ansbile version are you using?
0.7.0

What did you see?
When is run a playbook using the json stdout_callbak, and there is a warning message on the output (with warning messages I mean those messages that start with [WARNING]), that message is included on json data and then the parsing done by JSONParse fails.

exit status 4                                                                                  
panic: Unmarshall error                     
        invalid character '[' after top-level value

On the other hand, some warning messages returned by ansible-playbook in one line on terminal are seen as multiple line messages by go-ansible

Thats an example:

  • Shell output:
$ ANSIBLE_STDOUT_CALLBACK=json ansible-playbook  --inventory 127.0.0.1,  --user apenella site.yml
[WARNING]: Unhandled error in Python interpreter discovery for host 127.0.0.1: Failed to connect to the host via ssh: ssh: connect to host 127.0.0.1 port 22: Connection refused
...
  • go-ansible output:
-- #[WARNING]: Unhandled error in Python interpreter discovery for host 127.0.0.1:#
-- #Failed to connect to the host via ssh: ssh: connect to host 127.0.0.1 port 22:#
-- #Connection refused#

How to reproduce the issue?
To reproduce the issue I used simple-ansibleplaybook-json example.
I forced a connection error updating ansiblePlaybookConnectionOptions definition as is shown below:

	ansiblePlaybookConnectionOptions := &ansibler.AnsiblePlaybookConnectionOptions{
	//	Connection: "local",
		User:       "apenella",
	}

You could see the scanned text by go-ansible updating ansiblePlaybookJSONResults as is shown below:

	for scanner.Scan() {
		line := scanner.Text()
		if !skipLine(line) {
                        // println for debuggin purpose
                        fmt.Println("-- #" + line + "#")
			fmt.Fprintf(w, "%s", line)
		}
	}

The issue is related to #23

Help with StdoutCallbacks

Im writing a large automation that will be running several playbooks and many adhoc commands. Is there a way to get stdout/stderr from ansible commands using the shell/command modules in json or some structured output that can be easily parse?

I can only seem to get output like this:

msg="running ansible command: ansible all  --args uname -r --extra-vars '{"ansible_sudo_pass":"mysecret"}' --forks 5 --inventory myhost,  --timeout 30  --become-method sudo --become-user root"
msg="── myhost | CHANGED | rc=0 >>"
msg="── 3.10.0-1160.31.1.el7.x86_64"
── myhost | CHANGED | rc=0 >>
── 3.10.0-1160.31.1.el7.x86_64

My transformer:

func parseKernelVersion() results.TransformerFunc {
    return func(s string) string {
        log.Debugf("%s", s)
        return s
    }
}

Then how I set it up:

    command.Exec = execute.NewDefaultExecute(execute.WithTransformers(parseKernelVersion()))
    // I've tried setting this to "" and few other options
    command.StdoutCallback = "oneline"
    //command.StdoutCallback = "json"

Is there a way to get stdout/stderr back in a json structure of some sort here?

about ansible.cfg

while i worked on this project,i find it doens't run ansible-playbook as i think,i change ansible.cfg log_path,but the go-ansible's log doesn't record ansible's output there.
i read the code but don't find any config about log,does the execute.WithWrite work?
here's code
file,err:= os.OpenFile("xayah.log",os.O_RDWR | os.O_CREATE | os.O_APPEND,0766)

playbook := &playbook.AnsiblePlaybookCmd{
	Playbooks:         []string{"xayah.yml"},
	ConnectionOptions: ansiblePlaybookConnectionOptions,
	Options:           ansiblePlaybookOptions,
	Exec: execute.NewDefaultExecute(
		execute.WithTransformers(
			results.Prepend("Go-ansible example"),
		),
		execute.WithWrite(file),
	),
	StdoutCallback: "json",
}

just find that does't work.

Typos in README.md

There are a number of typos in the README file.
lastest > latest
recieves > receives
ansbile > ansible

Execute ansible-vault command

Enhancement request
Execute ansible-vault command using go-ansible.
edit subcommand could be excluded.

usage: ansible-vault [-h] [--version] [-v] {create,decrypt,edit,view,encrypt,encrypt_string,rekey} ...

encryption/decryption utility for Ansible data files

positional arguments:
  {create,decrypt,edit,view,encrypt,encrypt_string,rekey}
    create              Create new vault encrypted file
    decrypt             Decrypt vault encrypted file
    edit                Edit vault encrypted file
    view                View vault encrypted file
    encrypt             Encrypt YAML file
    encrypt_string      Encrypt a string
    rekey               Re-key a vault encrypted file

optional arguments:
  --version             show program's version number, config file location, configured module search path, module location, executable location and exit
  -h, --help            show this help message and exit
  -v, --verbose         verbose mode (-vvv for more, -vvvv to enable connection debugging)

Async callback

Right now the only way to have result while launching a playbook is to launch ansible-playbook with de default callback. But this doesn't allow the use of result on the fly.

All other callback are synchronous, the execution need finish to get result (ex: json callback)

I went through the Ansible community callback plugins but none would enable async. Though it was been done for specific use case like AWX or ARA.

Would you be open to the idea of adding such feature ? It would require writing a custom Ansible callback plugin

How to get ansible log as JSON?

I've read doc but i did not understand, how to get ansible log as JSON?

	playbook := &ansibler.AnsiblePlaybookCmd{
		Playbook: fmt.Sprintf(playbookPath, "test"),
		Options:  ansiblePlaybookOptions,
		Writer:   &AnsibleLog{},
		StdoutCallback: "json",
	}

code below make a panic

panic: send on closed channel

goroutine 7 [running]:
github.com/apenella/go-ansible/execute.(*DefaultExecute).Execute.func1(0xc0001c2080, 0x0, 0x0, 0x16e5b00, 0xc0000106b0, 0xc000028840, 0xc0000287e0)
        /Users/dikkini/go/pkg/mod/github.com/apenella/[email protected]/execute/defaultExecute.go:92 +0x158
created by github.com/apenella/go-ansible/execute.(*DefaultExecute).Execute
        /Users/dikkini/go/pkg/mod/github.com/apenella/[email protected]/execute/defaultExecute.go:82 +0x508
Exiting.

Also, there is a misstake in example https://github.com/apenella/go-ansible/blob/master/examples/simple-ansibleplaybook-json/simple-ansibleplaybook-json.go#L41

Error: Cannot use 'buff.Bytes()' (type []byte) as type *bufio.Reader

results.JSONParse error

Multiple types of plays.tasks.hosts.msg

panic: Unmarshall error json: cannot unmarshal array into Go struct field AnsiblePlaybookJSONResultsPlayTaskHostsItem.plays.tasks.hosts.msg of type string

{
    "custom_stats": {},
    "global_custom_stats": {},
    "plays": [
        {
            "play": {
                "duration": {
                    "end": "2021-08-03T03:48:07.438294Z",
                    "start": "2021-08-03T03:48:06.634238Z"
                },
                "id": "000c29ae-db4d-69ff-80c5-000000000009",
                "name": "Welcome"
            },
            "tasks": [
                { 
                    "hosts": { 
                        "localhost": {
                            "_ansible_no_log": false,
                            "_ansible_verbose_always": true,
                            "action": "debug",
                            "changed": false,
                            "msg": [
                                "*****************************************************************",
                                " ",
                                "*****************************************************************"
                            ]  
                        }
                    }
}

How to connection other nodes?

ansiblePlaybookConnectionOptions := &ansibler.AnsiblePlaybookConnectionOptions{
Connection: "local",
}

when i chnage Connection: local to Connection:<my node ip>

ansiblePlaybookConnectionOptions := &ansibler.AnsiblePlaybookConnectionOptions{
Connection: "192.168.1.176",
}

Additional context:

Go-ansible example => fatal: [192.168.1.176]: FAILED! => {"msg": "the connection plugin '192.168.1.176' was not found"}
Go-ansible example => to retry, use: --limit @/root/go/src/github.com/apenella/go-ansible/examples/simple-ansibleplaybook/site.retry
Go-ansible example =>
Go-ansible example => PLAY RECAP

Execute ansible-galaxy commands

Enhacement request
Execute ansible-galaxy commands using go-ansible package.

  • Main command options:
usage: ansible-galaxy [-h] [--version] [-v] TYPE ...

Perform various Role and Collection related operations.

positional arguments:
  TYPE
    collection   Manage an Ansible Galaxy collection.
    role         Manage an Ansible Galaxy role.

optional arguments:
  --version      show program's version number, config file location, configured module search path, module location, executable location and exit
  -h, --help     show this help message and exit
  -v, --verbose  verbose mode (-vvv for more, -vvvv to enable connection debugging)
  • Collection subcommand options:
usage: ansible-galaxy collection [-h] COLLECTION_ACTION ...

positional arguments:
  COLLECTION_ACTION
    download         Download collections and their dependencies as a tarball for an offline install.
    init             Initialize new collection with the base structure of a collection.
    build            Build an Ansible collection artifact that can be publish to Ansible Galaxy.
    publish          Publish a collection artifact to Ansible Galaxy.
    install          Install collection(s) from file(s), URL(s) or Ansible Galaxy
    list             Show the name and version of each collection installed in the collections_path.
    verify           Compare checksums with the collection(s) found on the server and the installed copy. This does not verify dependencies.

optional arguments:
  -h, --help         show this help message and exit
  • Role subcommand options:
usage: ansible-galaxy role [-h] ROLE_ACTION ...

positional arguments:
  ROLE_ACTION
    init       Initialize new role with the base structure of a role.
    remove     Delete roles from roles_path.
    delete     Removes the role from Galaxy. It does not remove or alter the actual GitHub repository.
    list       Show the name and version of each role installed in the roles_path.
    search     Search the Galaxy database by tags, platforms, author and multiple keywords.
    import     Import a role into a galaxy server
    setup      Manage the integration between Galaxy and the given source.
    info       View more details about a specific role.
    install    Install role(s) from file(s), URL(s) or Ansible Galaxy

optional arguments:
  -h, --help   show this help message and exit

How to pass ansible environment variables for the ansible playbook?

Hi @apenella, I am trying to understand how I can set the env variables for running Ansible playbook. I tried setting the env variable "ANSIBLE_LOG_PATH" by doing os.Setenv("ANSIBLE_LOG_PATH", "test.log") before triggering the following command. The ansible does not seem to log even with the env set. Could you please help me with this?

The code snippet is as follows

        os.Setenv("DEFAULT_LOG_PATH", "test.log")  
	ansibleConnectionOption := &playbookOptions.AnsibleConnectionOptions{
		Connection: "local",
	}

	ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
		ExtraVars: extraVars,
	}

	playbook := &playbook.AnsiblePlaybookCmd{
		Playbooks:         []string{playFile},
		ConnectionOptions: ansibleConnectionOption,
		Options:           ansiblePlaybookOptions,
	}

	return playbook.Run(context.Background())```

Problem with unreachable host

Hi @apenella
When I excute with command----(1.1.1.1 is unreachable host for test)
/opt/homebrew/bin/ansible-playbook --extra-vars '{"host_name":"10.200.75.152,10.200.75.136,1.1.1.1"}' --inventory 10.200.75.136,10.200.75.152,1.1.1.1 --user root nstall-nginx.yml
is ok.
but with go-ansible i got a panic:
panic: Error during command execution: ansible-playbook error: parser error
tips:when i del 1.1.1.1, it just ok with go-ansible

code:

func ansiblePlaybookTask(_, f, inventory string, jsonvars map[string]interface{}){
ansiblePlaybookConnectionOptions := &options.AnsibleConnectionOptions{
//Connection: "local",
User: "root",
}

ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
Inventory: inventory,
//Inventory: "/root/inventory",
ExtraVars: jsonvars,
}

playbook := &playbook.AnsiblePlaybookCmd{
Playbooks: []string{f},
ConnectionOptions: ansiblePlaybookConnectionOptions,
Options: ansiblePlaybookOptions,
StdoutCallback: "json",
}

err := playbook.Run(context.TODO())
if err != nil {
panic(err)
}
}

thanks!

feature: support interactive input from stdin

I have a playbook file which requires user input in order to progress. Currently, when invoking an interactive playbook from go-ansible default values for user input are taken.

How do you think this might be supported ? I was thinking of piping the stdin of go-ansible to ansible's stdin

ExtraVars not enclosed in quotes

The ansible command that is being generated when adding extra vars argument is

ansible-playbook --extra-vars {"EC2IP":"127.0.0.1","ansible_password":"123456"} --inventory 10.10.10.120, --user root change-settings.yml

This is not working as extra-vars json should be enclosed in qoutes like

ansible-playbook --extra-vars '{"EC2IP":"127.0.0.1","ansible_password":"123456"}' --inventory 10.10.10.120, --user root change-settings.yml

Ansible command

Hi

I am looking to run the following command . Just wondering if its possible using the go-ansible , Any pointers how I can run following command

ansible-playbook -i hosts.yml platform.all

Add virtualenv option

Hello,

Right now it's only possible to launch with the default ansible-playbook binary.

As you may know managing Ansible modules directly through default pip installed on the system is a nightmare (I have already broken machine myself ;). The best practice we have is to always set it up through virtualenv and I think it's pretty conventional.

I am aware that it would work when the virtualenv is already loaded, but it would be nice to provide a way to overwrite the default binary. Similar to how ansible provide the ansible_python_interpreter var

I can submit a PR

adhoc command StdoutCallback as json raise err.

I have some code as following:


func CheckAssetsConnectivityManual(ctx context.Context, adminUser, password, hosts string) {

	buff := new(bytes.Buffer)

	ansibleConnectionOptions := &options.AnsibleConnectionOptions{
		User: adminUser,
	}

	ansibleAdhocOptions := &adhoc.AnsibleAdhocOptions{
		Inventory:  hosts,
		ModuleName: "ping",
		//ModuleName: "setup",
	}

	// install sshpass
	ansibleAdhocOptions.AddExtraVar("ansible_password", password)

	executor := execute.NewDefaultExecute(
		execute.WithWrite(io.Writer(buff)),
	)

	adhoc := &adhoc.AnsibleAdhocCmd{
		Pattern:           "all",
		Options:           ansibleAdhocOptions,
		ConnectionOptions: ansibleConnectionOptions,
		Exec:              executor,
		StdoutCallback:    "json",
	}

	err := adhoc.Run(ctx)
	if err != nil {
		log.Println(err.Error())
	}
	log.Println(" ------ ===== buff: ", string(buff.Bytes()))
	res, err := results.JSONParse(buff.Bytes())
	if err != nil {
		log.Println(err.Error())
	}
	log.Println(" ---- res : ", res.String())

}


res, err := results.JSONParse(buff.Bytes())

panic.

exec: "ansible-playbook": executable file not found in %PATH%

windows vscode use

panic: Binary file 'ansible-playbook' does not exists
        exec: "ansible-playbook": executable file not found in %PATH%

goroutine 1 [running]:

linux all normal

windows application how to set this individual PATH 。ansible-playbook file create ok

thanks

How to define variable for inventory and extra-vars.??

I am using go func will which pass the hostname and dbname to the ansiblePlaybookOptions but i am trying to use that variable getting below error.

 ansiblePlaybookOptions := &ansibler.AnsiblePlaybookOptions{
		Inventory: "%s",host
		ExtraVars: map[string]interface{}{
			"dbname":    "%s",dbname

		},
	}

syntax error: unexpected newline, expecting comma or }

ssh port change

I have modified the port of Ansible SSH and now use Go-Ansible to run Ansible PlayBook. How can I run it properly。

How to kill the process

DefaultExecute Execute function don't save exec.Command object, I can't call cmd.Process.Kill() to kill the ansible-playbook process.

Support multiple playbooks using results.JSONParse

When executing multiple playbooks in playbook.AnsiblePlaybookCmd, the JSON parsing doesn't work.

For instance, I tested with examples/json-stdout-ansibleplaybook/json-stdout-ansibleplaybook.go.

--- a/examples/json-stdout-ansibleplaybook/json-stdout-ansibleplaybook.go
+++ b/examples/json-stdout-ansibleplaybook/json-stdout-ansibleplaybook.go
@@ -33,7 +33,7 @@ func main() {
        )
 
        playbook := &playbook.AnsiblePlaybookCmd{
-               Playbooks:         []string{"site.yml"},
+               Playbooks:         []string{"site.yml", "site.yml"},
                Exec:              execute,
                ConnectionOptions: ansiblePlaybookConnectionOptions,
                Options:           ansiblePlaybookOptions,

The result is following.

panic: Unmarshall error
        invalid character '{' after top-level value

goroutine 1 [running]:
main.main()
        /workspace/go-ansible/examples/json-stdout-ansibleplaybook/json-stdout-ansibleplaybook.go:50 +0x2bd
exit status 2

playbook.AnsiblePlaybookCmd uses Playbooks fields as an array of playbook's name, so I suggest to have an array of AnsiblePlaybookJSONResults as results.JSONParse output.

Can you construct a playbook fully with code?

Right now my playbooks are local yaml files. I was curious if you can completely construct a playbook in code with this library and completely removed the dependence on static files?

Duration + Json parsing

if i make:

execute := execute.NewDefaultExecute(
	execute.WithWrite(io.Writer(buff)),
	execute.WithShowDuration(),
)

then:

StdoutCallback:    	"json",
Exec: 			execute,

then:

err := playbook.Run(context.TODO())
res, err = results.JSONParse(buff.Bytes())

it will be error:

panic: Unmarshall error
	invalid character 'D' after top-level value

goroutine 1 [running]:

Because of line in the end of output: "Duration: 5.348349791s"

Sorry, if it's my mistake! Amazing rep!

Add Ansible ad-hoc

Hi,

Would it make sense to add the ad hoc module in this repo?

I really like how this repo enable to use playbook with go and I would like to use the mechanism for ad-hoc command. I'm wondering if I should create a new repo or if is something we could add here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.