go-ansible:Go 应用集成 Ansible 命令的封装库,支持多命令执行与工作流编排

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.

分支1Tags40

go-ansible

License: MIT Test Go Report Card Go ReferenceStatic Badge

go-ansible-logo

概述

Go-ansible 是一个用于从 Go 应用直接执行 Ansible 命令(例如 ansible-playbook、ansible-inventory、ansible-galaxy 和 ansible)的 Go 库。它作为 Ansible CLI 的封装,能够将 Ansible 自动化无缝集成到 Go 项目中。

为何使用 go-ansible?

Go-ansible 的诞生,是为了应对管理需要大量额外变量的复杂 Ansible playbook 所带来的挑战。该库不重新实现 Ansible 的逻辑,而是在现有 Ansible 命令之上提供一层抽象,从而简化其在 Go 应用中的执行方式。

最初,这个库是 ansible-playbook 命令的封装,并与 Stevedore 项目紧密耦合。不过,得益于社区成员的贡献、反馈和建议,它已经发展为一个通用型解决方案。如今,go-ansible 支持执行多种命令,包括 ansible-playbook、ansible 和 ansible-galaxy,并且能够对设置和输出管理进行细粒度控制,以适应不同场景。

核心特性

  • Ansible 命令执行:直接从 Go 应用执行 Ansible 命令,包括 ansible-playbook、ansible、ansible-galaxy 和 ansible-inventory。
  • 工作流编排:将多次 Ansible 执行串联为工作流,可以在同一条工作流中按顺序运行命令(例如先安装 roles/collections,再执行 playbooks)。
  • 灵活配置:通过 Go 代码自定义 Ansible 环境变量与设置,支持高级使用场景和定制化工作流。
  • Vault 集成:管理 Ansible Vault 密钥,包括加密变量、密码文件和多个密码来源。
  • 多用途输出管理:支持以多种格式处理命令输出(纯文本、JSON、JSONL),并提供内置与自定义输出处理器和转换器。
  • 可扩展架构:支持通过自定义执行器、输出处理器和转换器进行扩展,以满足复杂需求、高级执行流程以及错误信息增强。

此外,go-ansible 库提供了一组示例,用于演示如何在不同场景中使用该库。建议查看 examples 部分。

目录

快速入门

以下部分将通过一个示例,逐步演示如何使用 go-ansible 配置并执行 Ansible 剧本。同样的方式也适用于其他 Ansible 命令,例如 ansible 或 ansible-inventory。如果你想查看一个完整的示例,了解如何从 Go 中运行 Ansible 剧本,请查看 ansibleplaybook-simple 示例。

注意 在继续之前,请确保已安装 go-ansible 库的最新版本。如果尚未安装,请参见 安装章节 中的说明。

要创建一个启动 ansible-playbook 命令的应用程序,你需要创建一个 AnsiblePlaybookCmd 结构体。该结构体用于生成待执行的 Ansible 命令。然后,你需要使用一个 执行器 执行该命令。在本示例中,你将使用 go-ansible 库提供的 DefaultExecute 执行器。

创建 AnsiblePlaybookCmd 结构体

要执行 ansible-playbook 命令,首先需要定义必要的连接、剧本和提权选项。

首先创建 AnsiblePlaybookOptions 结构体:

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

最后,创建一个 AnsiblePlaybookCmd 结构体,用于生成通过 ansible-playbook 命令执行 playbook site.yml 的命令:

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

定义好 AnsiblePlaybookCmd 后,将命令交给 executor 执行。

创建 DefaultExecute executor

我们将使用 go-ansible 库提供的 DefaultExecute 结构体来执行 ansible-playbook 命令。它需要一个负责生成待执行命令的 Commander。在本示例中,将使用前面定义的 AnsiblePlaybookCmd。

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

定义 DefaultExecute 后,使用以下代码执行 Ansible 命令:

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

管理命令执行的输出

默认情况下,DefaultExecute 会使用 DefaultResults 结构体来管理命令执行的输出。DefaultResults 结构体将输出作为纯文本处理。

 ──
 ── 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

安装

⚠️ 重要提示:

  • master 分支可能包含未发布或预发布的功能。建议使用 发布页 中提供的稳定版本。
  • 主要版本升级(例如从 1.x 升级到 2.x)会引入不兼容变更。升级前请查看 更新日志 以及相应的升级指南:
  • 请更新导入路径以匹配正确的模块版本(例如 2.x 版本使用 github.com/apenella/go-ansible/v2)。

安装最新发布的版本:

go get github.com/apenella/go-ansible/v2

要安装特定版本:

go get github.com/apenella/go-ansible/v2@1.2.3-rc1

升级说明

核心概念

在使用 go-ansible 库之前,你需要理解几个核心概念。这些概念对于有效使用该库,以及理解本文档中提供的示例和用法参考至关重要。

执行器

执行器 是一个负责执行命令并处理执行输出结果的组件。库中内置了 DefaultExecute 执行器,它是执行器的开箱即用实现。如果 DefaultExecute 无法满足你的需求,你也可以创建自定义执行器。 如需了解有关 DefaultExecute 的更多信息,请参阅该章节。

命令生成器

命令生成器 或 命令器 负责生成要执行的命令。AnsiblePlaybookCmd 和 AnsibleAdhocCmd 结构体是命令生成器的示例。该概念是在 2.0.0 主版本中引入的。

结果处理器

结果处理器 或 结果输出器 负责管理命令执行的输出。库中包含两种输出机制:DefaultResults 和 JSONStdoutCallbackResults 结构体。

注意事项

在继续之前,请注意以下事项,以确保最佳地使用 go-ansible 库。

在容器内执行 go-ansible

在容器内使用 go-ansible 库执行 Ansible 命令时,请确保容器已配置初始化系统。初始化系统用于管理由 Ansible 命令创建的子进程。如果容器没有初始化系统,子进程可能无法被正确管理,从而导致僵尸进程等异常行为。

关于这一点,你可以在问题 139 以及相关的 Ansible GitHub issue 评论 中阅读更多内容。

禁用伪终端分配

在终端中执行时,Ansible 命令会强制进行伪终端分配。该配置可能导致命令结束后 SSH 连接留下僵尸进程。如果你遇到此问题,可以在 SSH 额外参数中设置 -T,以禁用伪终端分配。

你可以在议题 139 和 此 Google Groups 讨论 中了解更多信息。

使用参考

使用参考部分概述了 go-ansible 库中可用的不同包及其主要资源。在这里,你可以找到有效使用该库执行 Ansible 命令(如 ansible-playbook 和 ansible)的详细信息。 如需了解该库的包、结构体、方法和函数的详细信息,请参阅 go-ansible GoDoc 参考。

Adhoc 包

本节概述了 go-ansible 库中的 adhoc 包,并介绍其主要组件和功能。

github.com/apenella/go-ansible/v2/pkg/adhoc 包用于生成 Ansible ad-hoc 命令。它不直接执行命令,而是提供生成命令所需的结构体,由执行器执行。adhoc 包包含执行 ad-hoc 命令所需的以下关键结构体:

AnsibleAdhocCmd 结构体

AnsibleAdhocCmd 结构体用于生成 Ansible ad-hoc 命令。它实现了 Commander 接口,因此其方法 Command 返回一个字符串数组,表示要执行的命令。执行器可以使用该数组创建要执行的命令。

该包提供 NewAnsibleAdhocCmd 函数,用于创建 AnsibleAdhocCmd 结构体的新实例。该函数接受一个选项列表,用于自定义 ad-hoc 命令。可用函数如下:

  • WithAdhocOptions(options *AnsibleAdhocOptions) AdhocOptionsFunc:设置命令的 ad-hoc 选项。
  • WithBinary(binary string) AdhocOptionsFunc:设置 ad-hoc 命令的二进制文件。
  • WithPattern(pattern string) AdhocOptionsFunc:设置 ad-hoc 命令的主机模式。

以下代码片段演示如何使用 AnsibleAdhocCmd 结构体生成 ad-hoc 命令:

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 结构体

AnsibleAdhocExecute 结构体是一个用于执行 ansible 命令的精简 执行器。它将 命令生成器 与 执行器 的初始化过程封装在一起。当无需额外配置或定制时,该 执行器 尤其实用。

以下方法可用于设置 AnsibleAdhocCmd 结构体的属性:

  • WithBinary(binary string) *AnsibleAdhocExecute:用于设置 Binary 属性。
  • WithAdhocOptions(options *AnsibleAdhocOptions) *AnsibleAdhocExecute:用于设置 AdhocOptions 属性。

下面是一个使用 AnsibleAdhocExecute 执行 ansible 命令的示例:

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

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

AnsibleAdhocOptions 结构体

使用 AnsibleAdhocOptions 结构体,可以定义 Ansible 手册页中 Options 一节所述的参数。在同一结构体中,还可以定义连接选项和权限提升选项。

execute 包

execute 包位于 github.com/apenella/go-ansible/v2/pkg/execute,提供了开箱即用的 DefaultExecute,即可用的 执行器。此外,该包还定义了一些接口,用于管理命令执行并自定义 执行器 的行为。

以下是 execute 包中可用的主要资源:

Executor 接口

Executor 接口定义了负责执行外部命令的组件。DefaultExecute 结构体实现了该接口。以下是 Executor 接口的定义:

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

Commander 接口

Commander 接口定义了负责生成待执行命令的组件。AnsiblePlaybookCmd 和 AnsibleAdhocCmd 结构体实现了该接口。以下是 Commander 接口的定义:

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

ErrorEnricher 接口

ErrorEnricher 接口定义负责增强错误信息的组件。AnsiblePlaybookErrorEnrich 结构体实现了该接口。 DefaultExecute 结构体使用该接口增强错误信息。以下是 ErrorEnricher 接口的定义:

type ErrorEnricher interface {
  Enrich(err error) error
}

Executabler 接口

Executabler 接口定义了 DefaultExecute 执行命令所需的组件。通过 Executabler 接口,可以根据需求自定义命令的执行方式。

以下是 Executabler 接口的定义:

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

DefaultExecute 结构体

DefaultExecute 执行器是由 go-ansible 库提供的组件,用于管理命令的执行。它提供了灵活性和自定义选项,以适应各种使用场景。 可以把 DefaultExecute 执行器理解为一条负责处理命令执行的流水线。它由三个主要阶段组成,每个阶段分别由不同组件负责管理:

  • Commander:生成要执行的命令。
  • Executabler:执行命令。
  • ResultsOutputer:管理命令执行的输出。

默认情况下,DefaultExecute 执行器使用 OsExec 结构体作为 Executabler 来执行命令,它是对 os/exec 包的封装。同时,它使用 DefaultResults 结构体作为 ResultsOutputer 来管理命令执行的输出。不过,你可以自定义这些组件,以便根据需求定制执行过程。

在创建新的 DefaultExecute 实例时,可以提供以下函数来自定义其行为。它们均可在 github.com/apenella/go-ansible/v2/pkg/execute 包中使用:

  • WithCmd(cmd Commander) ExecuteOptions:设置负责生成命令的组件。
  • WithCmdRunDir(cmdRunDir string) ExecuteOptions:定义命令执行所在的目录。
  • WithEnvVars(vars map[string]string) ExecuteOptions:为命令执行设置环境变量。
  • WithErrorEnricher(errEnricher ErrorEnricher) ExecuteOptions:定义负责丰富错误信息的组件。
  • WithExecutable(executable Executabler) ExecuteOptions:定义负责执行命令的组件。
  • WithOutput(output result.ResultsOutputer) ExecuteOptions:指定负责管理命令输出的组件。
  • WithStdin(r io.Reader) ExecuteOptions:设置命令输入的读取器。
  • WithTransformers(trans ...transformer.TransformerFunc) ExecuteOptions:添加转换器以修改命令输出。
  • WithWrite(w io.Writer) ExecuteOptions:设置命令输出的写入器。
  • WithWriteError(w io.Writer) ExecuteOptions:设置命令错误输出的写入器。

下面的代码片段展示了如何使用 ExecuteOptions 函数来自定义 DefaultExecute 执行器:

// 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.WithErrorEnrich(playbook.NewAnsiblePlaybookErrorEnrich()),
  execute.WithExecutable(executabler),
  execute.WithOutput(output),
)

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

如需更多示例与实用场景,请参考 go-ansible 仓库中的 examples 目录。

定义自定义 Executor

如果 DefaultExecute 不能满足你的需求或预期,你可以实现一个自定义 executor。以下示例展示了一个自定义 executor,说明如何将其与 AnsiblePlaybookCmd 或 AnsibleAdhocCmd 结构体集成,以按所需行为执行剧本:

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
}

下一个代码片段展示了如何使用自定义执行器来执行 ansible-playbook 命令:

// 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
}

当你使用自定义执行器运行 playbook 时,输出如下:

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

自定义执行方式

go-ansible 库提供了多种选项,用于配置和自定义 Ansible 命令的执行过程。这些自定义能力在 2.0.0 版本 中引入,当时 Executor 成为执行过程的核心组件。

在接下来的章节中,我们将介绍可用于自定义执行过程的组件:

配置包

github.com/apenella/go-ansible/v2/pkg/execute/configuration 包提供了用于在命令执行期间配置 Ansible 设置的组件。在接下来的章节中,我们将介绍可用于自定义执行过程的可用元素。

ExecutorEnvVarSetter 接口

ExecutorEnvVarSetter 接口扩展了 Executor 接口,增加了为命令执行设置环境变量的能力。DefaultExecute 结构体实现了该接口。以下是 ExecutorEnvVarSetter 接口的定义:

type ExecutorEnvVarSetter interface {
  execute.Executor
  AddEnvVar(key, value string)
}
Ansible 配置函数

github.com/apenella/go-ansible/v2/pkg/execute/configuration 包提供了一组用于在命令执行期间配置 Ansible 设置的函数。每个函数对应 Ansible 参考指南 中可用的一个配置项。这些函数遵循一致的命名约定:With<setting name> 或 Without<setting name>,其中 <setting name> 是要配置的 Ansible 设置名称。

AnsibleWithConfigurationSettingsExecute 结构体

AnsibleWithConfigurationSettingsExecute 结构体作为 ExecutorEnvVarSetter 的装饰器,用于配置执行过程中的 Ansible 设置。在实例化新的 AnsibleWithConfigurationSettingsExecute 时,必须提供一个 ExecutorEnvVarSetter 以及一组用于配置 Ansible 设置的函数。 下面展示如何使用 AnsibleWithConfigurationSettingsExecute 结构体为执行配置 Ansible 设置:

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
}
Exec 包

github.com/apenella/go-ansible/v2/pkg/execute/exec 包对外部命令的执行进行了抽象,并作为 os/exec 包的封装。它包含以下组件:

Cmder 接口

Cmder 接口定义了 Cmd 结构体中可用的方法,这些方法对应于 os/exec.Cmd 结构体中的方法。Cmder 接口对外部命令的执行进行了抽象,便于使用其他组件来自定义执行过程并管理命令输出。以下是 Cmder 接口的定义:

type Cmder interface {
  CombinedOutput() ([]byte, error)
  Environ() []string
  Output() ([]byte, error)
  Run() error
  Start() error
  StderrPipe() (io.ReadCloser, error)
  StdinPipe() (io.WriteCloser, error)
  StdoutPipe() (io.ReadCloser, error)
  String() string
  Wait() error
}
Cmd 结构体

Cmd 结构体是 os/exec.Cmd 结构体的封装。OsExec 结构体使用它来执行外部命令。

OsExec 结构体

OsExec 结构体复现了 os/exec 包中 Command 和 CommandContext 函数的行为。不同之处在于,这些函数不再返回 *os/exec.Cmd 结构体,而是返回 Cmder 接口。

这种抽象便于在执行外部命令时引入更多组件、定制执行流程并管理命令输出。该抽象的另一优点是允许在测试中模拟命令执行。

Measure 包

go-ansible 库通过 github.com/apenella/go-ansible/v2/pkg/execute/measure 包提供了一种便捷机制,用于测量 Ansible 命令的执行时间。该包包含 ExecutorTimeMeasurement 结构体,它作为 Executor 的装饰器,用于跟踪命令执行所花费的时间。

为便于说明,请参考以下代码片段,其中演示了如何使用 ExecutorTimeMeasurement 结构体来测量执行 ansible-playbook 命令所需的时间:

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())

若需查看展示如何使用测量功能的完整示例,请参阅 go-ansible 仓库中的 ansibleplaybook-time-measurement 示例。

Result 包

github.com/apenella/go-ansible/v2/pkg/execute/result 包提供了一组组件和子包,用于管理 Ansible 命令的输出。以下部分将介绍可用的元素。

ResultsOutputer 接口

github.com/apenella/go-ansible/v2/pkg/execute/result 包中的 ResultsOutputer 接口定义了一个负责管理 go-ansible 库中命令执行输出的组件。DefaultResults 和 JSONStdoutCallbackResults 两个结构体均实现了该接口。以下是 ResultsOutputer 接口的定义:

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

DefaultResults 结构体位于 github.com/apenella/go-ansible/v2/pkg/execute/result/default 包中,在 go-ansible 库内作为命令执行的默认输出管理器。它实现了 ResultsOutputer 接口,提供以纯文本形式处理命令输出的功能。

DefaultResults 结构体会逐行读取执行输出,并对每一行应用一组 转换器。转换器 允许你根据具体需求丰富或修改输出。你可以在实例化新的 DefaultResults 时,或配置 DefaultExecute 执行器时指定 转换器。下面是创建 DefaultExecute 新实例时指定它们的示例:

exec := execute.NewDefaultExecute(
  execute.WithCmd(playbook),
  execute.WithTransformers(
    transformer.Prepend("Go-ansible example"),
    transformer.LogFormat(transformer.DefaultLogFormatLayout, transformer.Now),
  ),
)
JSONLEventStdoutCallbackResults 结构体

JSONLEventStdoutCallbackResults 结构体位于 github.com/apenella/go-ansible/v2/pkg/execute/result/json 包中,用于处理使用 ansible.posix.jsonl stdout 回调方式时的命令执行输出。它实现了 ResultsOutputer 接口,提供解析与处理来自 Ansible 命令的 JSONL 格式 输出的能力。

当使用 AnsiblePosixJsonlStdoutCallbackExecute 执行器时,输出由 JSONLEventStdoutCallbackResults 结构体管理。还可以将 AnsiblePosixJsonlStdoutCallbackExecute 配置为应用一组 transformers 来更新输出。这些 transformers 会按需丰富或更新输出内容。

github.com/apenella/go-ansible/v2/pkg/execute/result/json 提供了 AnsiblePlaybookJSONLEventResults 结构体,它表示来自 ansible.posix.jsonl 的 JSON 事件输出。你可以使用这个结构体来管理事件。

你可以参考 ansibleplaybook-posix-jsonl-stdout 示例,了解如何使用 JSONLEventStdoutCallbackResults 结构体。
对于更高级的使用场景,例如将事件持久化到数据库并应用 transformer,请查看 ansibleplaybook-posix-jsonl-stdout-persistence 示例。

JSONStdoutCallbackResults 结构体

JSONStdoutCallbackResults 结构体位于 github.com/apenella/go-ansible/v2/pkg/execute/result/json 包中,用于处理使用 JSON stdout 回调方式时的命令执行输出。它实现了 ResultsOutputer 接口,提供解析与处理来自 Ansible 命令的 JSON 格式 输出的能力。

当使用 JSONStdoutCallbackExecute 执行器时,输出由 JSONStdoutCallbackResults 结构体管理。

该包还包含 ParseJSONResultsStream 函数,用于将 JSON 输出解码为 AnsiblePlaybookJSONResults 数据结构。你可以使用这个数据结构,按照特定要求对 JSON 输出进行格式化。来自 Ansible 输出的预期 JSON schema 定义在 Ansible 仓库中的 json.py 文件中。

创建 JSONStdoutCallbackResults 的新实例时,你可以指定一组 transformers,将其应用于输出。这些 transformers 会按需丰富或更新输出。默认情况下,JSONStdoutCallbackResults 结构体会应用 IgnoreMessage transformer,以忽略 skipPatterns 数组中定义的任何 non-JSON 行。

下面是 JSONStdoutCallbackResults 代码中 skipPatterns 的定义:

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$",
  }

以下代码片段演示了如何使用 JSONStdoutCallbackResults 结构体来管理命令执行的输出:

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())

有关如何使用 measurement 的详细示例,请参阅 go-ansible 仓库中的 ansibleplaybook-json-stdout 示例。

转换器函数

在 go-ansible 中,转换器函数是关键组件,用于丰富或更新来自 executor 的输出,使用户能够根据自身需求自定义输出。每个转换器函数都遵循 TransformerFunc 类型所定义的签名:

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

当从 executor 接收到输出时,输出会逐行进行处理,每一行都会经过可用的转换器处理。github.com/apenella/go-ansible/v2/pkg/execute/result/transformer 包提供了一组开箱即用的转换器,用户也可根据需要创建自定义转换器。

以下是 github.com/apenella/go-ansible/v2/pkg/execute/result/transformer 包中可用的转换器函数:

  • Prepend:为每一行输出添加指定的前缀字符串。
transformer.Prepend("Prefix: ")
  • Append: 为每行输出追加指定的后缀字符串。
transformer.Append(" [suffix]")
  • LogFormat:在每行输出前添加日期时间前缀。
transformer.LogFormat(transformer.DefaultLogFormatLayout, transformer.Now)
  • IgnoreMessage:基于指定模式过滤输出行。该功能通过 regexp.MatchString 函数,将输出行与指定模式进行匹配。
skipPatterns := []string{"regexp-pattern1", "regexp-pattern2"}
transformer.IgnoreMessage(skipPatterns...)
Stdoutcallback 包

go-ansible 库中的 github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback 包用于管理 Ansible 的 stdout 回调方法。配置 stdout 回调方法通常包括两个步骤:

  • 设置 ANSIBLE_STDOUT_CALLBACK 环境变量,以指定所需的 stdout 回调插件名称。
  • 定义用于处理命令执行结果输出的方法。

为简化配置流程,go-ansible 库提供了一系列结构体,专门用于设置每种 stdout 回调方法,并提供了 ExecutorStdoutCallbackSetter 接口。

以下章节将详细说明可用于配置 stdout 回调方法的组件:

ExecutorStdoutCallbackSetter 接口

ExecutorStdoutCallbackSetter 接口在 Executor 接口的能力基础上进行了扩展,支持设置环境变量,并指定命令执行的结果输出机制。DefaultExecute 结构体实现了该接口,为配置 stdout 回调方法提供了灵活性。

以下是 ExecutorStdoutCallbackSetter 接口的定义:

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

ExecutorQuietStdoutCallbackSetter 接口位于 github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback 包中,它扩展了 ExecutorStdoutCallbackSetter 接口,并提供了移除命令执行输出冗长度的能力。DefaultExecute 结构体实现了该接口,可让您使命令执行输出保持静默。

该接口是 JSONStdoutCallbackExecute 结构体所要求的,用于移除命令执行输出的冗长度。

以下代码片段展示了 ExecutorQuietStdoutCallbackSetter 接口的定义:

type ExecutorQuietStdoutCallbackSetter interface {
  ExecutorStdoutCallbackSetter
  Quiet()
}
Stdout Callback 执行结构体

github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback 包提供了一组结构体,用于简化 Ansible 命令执行时的 stdout callback 配置。这些结构体作为 ExecutorStdoutCallbackSetter 的装饰器,便于将不同的 stdout callback 插件与命令执行无缝集成。

Ansible 中的每个 stdout callback 都对应 go-ansible 中的一个特定结构体,便于选择和配置所需方式。以下是可用的结构体:

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

例如,要为命令执行配置 JSON stdout callback,可以使用 JSONStdoutCallbackExecute 结构体,如下面的代码片段所示:

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

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

github.com/apenella/go-ansible/v2/pkg/execute/workflow 包提供了一个执行器,允许你依次运行多个执行器。

WorkflowExecute struct

WorkflowExecute struct 负责管理多个执行器的依次执行。它允许你定义一个用于运行 Ansible 命令的工作流。此外,WorkflowExecute 实现了 Executor 接口,使你能够为该工作流应用一些装饰器,例如 ExecutorTimeMeasurement。

默认情况下,当序列中某个 executor 的执行失败时,WorkflowExecute 将停止执行剩余的执行器。不过,你可以通过将 ContinueOnError 属性设置为 true 来更改该行为。在这种情况下,如果某个执行器失败,WorkflowExecute 将继续执行剩余的执行器,并在执行结束时返回错误。

WorkflowExecute struct 提供了以下方法来配置执行流程:

  • AppendExecutor(exec Executor) *WorkflowExecute:向序列中追加一个执行器。
  • Execute(ctx context.Context) error:依次执行该序列中的执行器。
  • WithContinueOnError() *WorkflowExecute:将 ContinueOnError 属性设置为 true。

以下是如何使用 WorkflowExecute struct 依次运行多个执行器的示例:

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 包

go-ansible 库使你能够与 Ansible Galaxy 命令行工具进行交互。为此,它包含以下包:

Galaxy 集合安装包

github.com/apenella/go-ansible/v2/pkg/galaxy/collection/install 包允许你使用 ansible-galaxy 命令从 Ansible Galaxy 安装集合。该包提供以下结构体和函数:

AnsibleGalaxyCollectionInstallCmd 结构体

AnsibleGalaxyCollectionInstallCmd 结构体用于生成安装集合的 ansible-galaxy 命令。它实现了 Commander 接口,因此其 Command 方法返回表示待执行命令的字符串数组。执行器可以使用它来创建待执行命令。

该包提供 NewAnsibleGalaxyCollectionInstallCmd 函数,用于创建 AnsibleGalaxyCollectionInstallCmd 结构体的新实例。该函数接受一组选项,以自定义 ansible-galaxy 命令。可用函数如下:

  • WithBinary(binary string) AnsibleGalaxyCollectionInstallOptionsFunc:设置 ansible-galaxy 命令的可执行文件。
  • WithCollectionInstallOptions(options *AnsibleGalaxyCollectionInstallOptions) AnsibleGalaxyCollectionInstallOptionsFunc:设置命令的集合安装选项。
  • WithCollectionNames(collectionNames ...string) AnsibleGalaxyCollectionInstallOptionsFunc:设置 ansible-galaxy 命令的集合名称。
AnsibleGalaxyCollectionInstallOptions 结构体

AnsibleGalaxyCollectionInstallOptions 结构体包含 Ansible Galaxy 手册页 Options 部分中描述的参数。它定义了 Ansible Galaxy 集合安装操作的行为,并指定查找配置设置的位置。

Galaxy 角色安装包

github.com/apenella/go-ansible/v2/pkg/galaxy/role/install 包允许你使用 ansible-galaxy 命令从 Ansible Galaxy 安装角色。该包提供以下结构体和函数:

AnsibleGalaxyRoleInstallCmd 结构体

AnsibleGalaxyRoleInstallCmd 结构体用于生成安装角色的 ansible-galaxy 命令。它实现了 Commander 接口,因此其 Command 方法返回表示待执行命令的字符串数组。执行器可以使用它来创建待执行命令。

该包提供 NewAnsibleGalaxyRoleInstallCmd 函数,用于创建 AnsibleGalaxyRoleInstallCmd 结构体的新实例。该函数接受一组选项,以自定义 ansible-galaxy 命令。可用函数如下:

  • WithBinary(binary string) AnsibleGalaxyRoleInstallOptionsFunc:设置 ansible-galaxy 命令的可执行文件。
  • WithGalaxyRoleInstallOptions(options *AnsibleGalaxyRoleInstallOptions) AnsibleGalaxyRoleInstallOptionsFunc:设置命令的角色安装选项。
  • WithRoleNames(roleNames ...string) AnsibleGalaxyRoleInstallOptionsFunc:设置 ansible-galaxy 命令的角色名称。
AnsibleGalaxyRoleInstallOptions 结构体

AnsibleGalaxyRoleInstallOptions 结构体包含 Ansible Galaxy 手册页 Options 部分中描述的参数。它定义了 Ansible Galaxy 角色安装操作的行为,并指定查找配置设置的位置。

Inventory 包

本节提供的信息概述了 go-ansible 中的 Inventory 包。

github.com/apenella/go-ansible/v2/pkg/inventory 包提供了执行 ansible-inventory 的功能。要完成这些任务,可以使用以下清单结构体:

AnsibleInventoryCmd 结构体

AnsibleInventoryCmd 结构体用于生成 ansible-inventory 命令。它实现了 Commander 接口,因此其 Command 方法返回一个字符串数组,用于表示要执行的命令。执行器可以使用它来创建要执行的命令。

该包提供 NewAnsibleInventoryCmd 函数,用于创建 AnsibleInventoryCmd 结构体的新实例。该函数接受一个选项列表,用于自定义 ansible-inventory 命令。可用的函数如下:

  • WithBinary(binary string) InventoryOptionsFunc:设置 ansible-inventory 命令的二进制文件。
  • WithInventoryOptions(options *AnsibleInventoryOptions) InventoryOptionsFunc:设置命令的清单选项。
  • WithPattern(pattern string) InventoryOptionsFunc:设置 ansible-inventory 命令的模式。

注意 与其他 Ansible 命令不同,ansible-inventory 命令不提供权限提升或连接选项,这与命令本身的功能保持一致。

AnsibleInventoryExecute 结构体

AnsibleInventoryExecute 结构体是用于运行 ansible-inventory 命令的精简执行器。它封装了命令生成器和_执行器_的设置过程。当不需要额外配置或自定义时,该_执行器_尤其有用。

以下方法可用于设置 AnsibleInventoryCmd 结构体的属性:

  • WithBinary(binary string) *AnsibleInventoryExecute:该方法用于设置 Binary 属性。
  • WithInventoryOptions(options *AnsibleAdhocOptions) *AnsibleInventoryExecute:该方法用于设置 InventoryOptions 属性。
  • WithPattern(pattern string) *AnsibleInventoryExecute:该方法用于设置 Pattern 属性。

下面是一个使用 AnsibleInventoryExecute 启动 ansible-inventory 命令的示例:

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

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

AnsibleInventoryOptions 结构体

AnsibleInventoryOptions 结构体包含 Ansible 手册页 Options 部分中所述的参数。它定义了 Ansible 清单操作的行为,并指定了查找配置设置的位置。

Playbook 包

本节概述 go-ansible 库中的 playbook 包。这里描述了其主要组件和功能。

github.com/apenella/go-ansible/v2/pkg/playbook 包用于生成 ansible-playbook 命令。它不会直接执行命令,而是提供必要的结构体,用于生成供执行器执行的命令。playbook 包包含以下用于执行 ad-hoc 命令的关键结构体:

AnsiblePlaybookCmd 结构体

AnsiblePlaybookCmd 结构体用于生成 ansible-playbook 命令。它实现了 Commander 接口,因此其方法 Command 会返回一个字符串数组,表示要执行的命令。执行器可以使用它来创建要执行的命令。

该包提供了 NewAnsiblePlaybookCmd 函数,用于创建 AnsiblePlaybookCmd 结构体的新实例。该函数接受一组选项,以自定义 ansible-playbook 命令。可用函数如下:

  • WithBinary(binary string) PlaybookOptionsFunc:设置 ansible-playbook 命令的二进制文件。
  • WithPlaybookOptions(options *AnsiblePlaybookOptions) PlaybookOptionsFunc:设置命令的 playbook 选项。
  • WithPlaybooks(playbooks ...string) PlaybookOptionsFunc:为 ansible-playbook 命令设置 playbook。

下面是一个示例,展示如何使用 AnsiblePlaybookCmd 结构体生成 ansible-playbook 命令:

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 结构体

实现 ErrorEnricher 接口的 AnsiblePlaybookErrorEnrich 结构体,负责在执行 ansible-playbook 命令时丰富错误信息。根据命令执行的退出码,AnsiblePlaybookErrorEnrich 结构体会在错误信息中追加额外内容。这些额外内容包括退出码、所执行的命令以及错误信息。

AnsiblePlaybookExecute 结构体

AnsiblePlaybookExecute 结构体是用于运行 ansible-playbook 命令的精简 执行器。它封装了 命令生成器 和 执行器 的配置过程。此外,它还可以在命令执行发生错误时丰富错误信息。 当无需额外配置或自定义时,该 执行器 尤其有用。

以下方法可用于设置 AnsiblePlaybookCmd 结构体的属性:

  • WithBinary(binary string) *AnsiblePlaybookExecute:该方法用于设置 Binary 属性。
  • WithPlaybookOptions(options *AnsiblePlaybookOptions) *AnsiblePlaybookExecute:该方法用于设置 PlaybookOptions 属性。

以下是使用 AnsiblePlaybookExecute 启动 ansible-playbook 命令的示例:

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

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

AnsiblePlaybookOptions struct

借助 AnsiblePlaybookOptions struct,你可以定义 Ansible 手册页 Options 部分中列出的参数,并支持配置连接选项与权限提升选项。

Vault package

github.com/apenella/go-ansible/v2/pkg/vault package 提供加密变量的能力。其中引入了 VariableVaulter struct,用于根据待加密的值创建 VaultVariableValue。

VaultVariableValue 支持以 JSON 格式返回实例化后的变量。

执行加密时,vault package 会依赖 Encrypter interface 的实现。

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

加密功能实现在 encrypt 包中,该包在以下章节中介绍。

加密

github.com/apenella/go-ansible/v2/pkg/vault/encrypt 包负责加密变量。它实现了在 github.com/apenella/go-ansible/v2/pkg/vault 包中定义的 Encrypter 接口。

目前,该包提供 EncryptString 结构体,用于支持字符串变量的加密。它使用 github.com/sosedoff/ansible-vault-go 库执行加密。

若要使用 EncryptString 结构体,需要基于密码读取器对其进行实例化。密码读取器负责提供加密所用的密码,并且应实现 PasswordReader 接口:

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

下面是一个实例化 EncryptString 结构体的示例:

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

在这个示例中,text.NewReadPasswordFromText 函数用于创建一个密码读取器,该读取器从文本来源读取密码。WithText 选项用于指定实际的密码值。

密码

go-ansible 库提供了一组可作为 PasswordReader 使用的包,用于读取加密所需的密码。以下部分介绍了这些包及其使用方式。

环境变量

github.com/apenella/go-ansible/v2/pkg/vault/password/envvars 包允许你从环境变量中读取密码。要使用该包,你需要使用 NewReadPasswordFromEnvVar 函数,并通过 WithEnvVar 选项提供存储密码的环境变量名称:

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

在这个示例中,VAULT_PASSWORD 环境变量被指定为密码来源。NewReadPasswordFromEnvVar 函数会创建一个密码读取器,用于从指定环境变量中读取密码。

使用 envvars 包,你可以方便地从环境变量中读取密码,并将其用于加密。

文件

github.com/apenella/go-ansible/v2/pkg/vault/password/file 包允许你从文件中读取密码,并使用 afero 文件系统抽象。

要使用该包,你需要使用 NewReadPasswordFromFile 函数创建密码读取器,并提供所需选项。WithFs 选项用于指定文件系统,WithFile 选项用于指定密码文件的路径。

如果你没有显式定义文件系统,该包会使用默认文件系统,即来自 github.com/spf13/afero 包的 OsFs。OsFs 表示你主机的文件系统。

因此,如果你没有通过 WithFs 选项为密码读取器提供特定文件系统,该文件包会自动使用 OsFs 作为文件系统,从文件中读取密码。

下面是一个未指定文件系统的示例:

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

在这种情况下,将使用 OsFs 访问宿主机文件系统上的 /password 文件。

Resolve

github.com/apenella/go-ansible/v2/pkg/vault/password/resolve 包提供了一套密码解析机制,通过依次尝试多个 PasswordReader 实现来获取密码。它会返回从任一 PasswordReader 实例获取到的第一个密码。

使用该包时,你需要创建一个 NewReadPasswordResolve 实例,并将一组 PasswordReader 实现作为参数传递给 WithReader 选项:

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

本例中,ReadPasswordResolve 实例由两个 PasswordReader 实现创建:一个从环境变量读取密码(envvars.NewReadPasswordFromEnvVar),另一个从文件读取密码(file.NewReadPasswordFromFile)。

ReadPasswordResolve 会按照提供的顺序,依次尝试从每个 PasswordReader 获取密码。首次成功读取的密码将被返回。如果未能获取到任何密码,则返回错误。

使用 resolve 包,您可以尝试多种 PasswordReader 实现,以解析用于加密的密码。

文本

github.com/apenella/go-ansible/v2/pkg/vault/password/text 包提供了从文本来源读取密码的功能。

要使用该包,需要调用 NewReadPasswordFromText 函数,并使用 WithText 选项以文本值形式提供密码:

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

在此示例中,密码通过 WithText 选项直接指定为文本值 "ThatIsAPassword"。

示例

go-ansible 库包含多种示例,展示如何在不同场景中使用该库。这些示例位于 go-ansible 仓库的 examples 目录中。

这些示例涵盖多种用例,并对 go-ansible 提供的不同特性和功能进行了实用演示。它们是理解并学习如何在应用程序中集成 go-ansible 的宝贵资源。

欢迎探索 examples 目录,获取有关如何在项目中利用 go-ansible 库的见解和思路。

以下是示例列表:

开发参考

本节为开发和贡献 go-ansible 库提供指南。

开发环境

为 go-ansible 库搭建开发环境,需要在系统上安装以下工具:

  • Docker Compose。开发使用的版本为 docker-compose version v2.34.0。
  • Docker。开发使用的版本为 Docker version 28.0.4。
  • Go。开发使用的版本为 1.23。
  • make 工具。开发使用的版本为 GNU Make 4.3。它用于封装持续集成与开发流程,例如测试或代码检查。

测试

要运行测试,可以使用以下命令:

make test

静态分析

若要运行静态分析工具,可使用以下命令:

make static-analysis

参与贡献

感谢您对 go-ansible 项目的关注与贡献!无论是问题报告、功能建议,还是代码贡献,我们都欢迎。请阅读贡献者指南,了解更多参与贡献的方式。

行为准则

go-ansible 项目致力于向所有人提供友好、安全且包容的环境,无论其性别、性取向、残障状况、种族、宗教信仰或类似个人特征如何。

我们期望所有贡献者、用户和社区成员遵守本行为准则。这涵盖 go-ansible 社区中的全部互动,无论线上、线下或其他形式。

如需更多信息,请参阅go-ansible 行为准则。

许可证

go-ansible 库基于 MIT 许可证提供。

项目介绍

Go-ansible 是一个 Go 语言包,它允许直接从 Golang 应用程序执行 ansible-playbook 或 ansible 命令。该包支持针对每个命令的广泛选项,使得 Ansible 功能可以顺畅地融入到您的项目中。【此简介由AI生成】

定制我的领域
161.12 K146访问 GitHub