Local Shell 后处理器

原文


类型:shell-local

Local Shell 后处理器在后处理阶段在本地执行脚本。shell-local 提供了一种使用 Packer 输出值和变量自动执行某些任务的便捷方法。

基础样例:

以下样例演示了一个具有完整功能的构建:

HCL2:

source "file" "example" {
    content = "example content"
}

build {
  source "source.file.example" {
    target = "./test_artifact.txt"
  }

  post-processor "shell-local" {
    inline = ["echo foo"]
  }
}

Json:

{
  "builders": [
    {
      "type": "file",
      "name": "example",
      "target": "./test_artifact.txt",
      "content": "example content"
    }
  ],
  "post-processors": [
    {
      "type": "shell-local",
      "inline": ["echo foo"]
    }
  ]
}

配置

下面列出了可用配置选项的参考。

以下参数中必须选择其一设置:

  • command(string)- 该参数是要执行的单个命令。它将被写入临时文件并使用下面的execute_command 调用运行。
  • inline([]string)- 这是要执行的命令数组。这些命令由换行符连接并转换为单个文件,因此它们都在同一上下文中执行。这允许您在一个命令中更改目录,并在下一个命令中使用目录中的某些内容,依此类推。inline 脚本是在机器内完成简单任务的最简单方法。
  • script(string)- 要执行的脚本的路径。该路径可以是绝对路径或相对路径。如果是相对的,就是相对于 Packer 执行时的工作目录。
  • scripts([]string)- 要执行的脚本数组。脚本将按照指定的顺序执行。每个脚本都是独立执行的,因此一个脚本中的变量等状态不会延续到下一个脚本。

以下是可选参数:

  • env (map[string]string) - 在 execute_command 之前注入的键/值对映射。 Packer 默认情况下也会将一些环境变量注入到环境中,这些内容将在后续的部分中介绍。重复的 env 设置会覆盖 environment_vars 设置。
  • environment_vars([]string)- 在 execute_command 之前注入的键/值对数组。格式应为 key=value。 Packer 默认情况下也会将一些环境变量注入到环境中,这些内容将在后续的部分中介绍。
  • env_var_format (string) - 当我们解析您提供的 environment_vars 时,可以用该参数设置一个可以使用的字符串模板,以确保正确设置环境变量。默认情况下,在 Windows 主机上,该参数为 %s=%s &&;在 Unix 上,该参数为 %s='%s'。我们可能不需要更改此格式,但可以在下面查看需要更改的用法示例。
  • execute_command([]string)- 用于执行脚本的命令。默认情况下,在 *nix 系统上这是:

["/bin/sh", "-c", "{{.Vars}} {{.Script}}"]

在 Windows 上,execute_command 默认为:


["cmd", "/V", "/C", "{{.Vars}}", "call", "{{.Script}}"]

该参数将搭配模板引擎使用。有几个可用的变量:

  • Script:要运行的脚本的路径
  • Vars:环境变量的列表(如果已配置)

此外,我们可以使用构建模板功能访问存储在生成的数据中的任何变量。如果您选择设置此参数,请确保数组中的第一个元素是您要使用的 shell 程序(例如,sh/usr/local/bin/zsh,甚至 powershell.exe。除了 shell 命令语言之外的任何语言都没有明确支持,并且可能会被 Packer 中所做的假设所破坏)。值得注意的是,如果您选择尝试对 Powershell 或其他 Windows 命令使用 shell-local,则将无法为您的环境正确设置环境变量。

为了向后兼容,execute_command 将接受字符串而不是字符串数组。如果提供了单个字符串或仅包含一个元素的字符串数组,Packer 将通过将您的 execute_command 添加到到字符串数组 ["sh", "-c"] 尾部来重现过去的行为。例如,如果设置 "execute_command":"foo bar",Packer 最终运行的 execute_command 将是 ["sh","-c","foo bar"]。如果设置为 "execute_command": ["foo", "bar"],则最终的 execute_command 仍为 ["foo", "bar"]

再次强调,上述的设计是为了向后兼容性做的补丁;我们强烈建议您将 execute_command 设置为字符串数组。

  • inline_shebang (string) - 运行 inline 指定的命令时使用的 shebang 值。默认值为 /bin/sh -e。如果您不使用 inline,则此参数无效。重要提示:如果您对此进行自定义,请务必包含类似 -e 标志的内容,否则个别步骤失败不会导致配置器失败。
  • keep_input_artifact (boolean) - 与大多数其他后处理器不同,keep_input_artifact 选项对 shell-local 后处理器没有影响。 Packer 将始终保留 shell-local 的输入制品,因为 shell-local 后处理器仅转发它接收到的制品。如果您的 shell-local 后处理器生成一个或多个您想要替换输入制品的文件,则可以在 shell-local 后处理器运行后使用 artifice 后处理器覆盖输入制品。
  • only_on([]string) - 这是保存了一组 shell-local 将会执行的运行时操作系统的数组。该参数允许我们设置为仅在特定操作系统上执行 shell-local。默认情况下,如果未设置 only_onshell-local 将始终运行。
  • use_linux_pathing (bool) - 这只与 Windows 主机相关。如果您在启用了 Windows Subsystem for Linux 功能的 Windows 环境中运行 Packer,并且想要调用 bash 脚本而不是调用 Cmd 脚本,则需要将此标志设置为 true;它告诉 Packer 使用 Linux 子系统路径来存储脚本,而不是 Windows 路径。 (例如 /mnt/c/path/to/your/file 而不是 C:/path/to/your/file)。请参阅下面的示例以获取有关如何使用此功能的更多指导。如果您不在 Windows 主机上,或者您不打算使用 shell-local 后处理器来运行 bash 脚本,请忽略此参数。如果将此标志设置为 true,则在设置脚本时仍需要提供脚本的标准 Windows 路径。该参数是测试版功能。
  • valid_exit_codes([]int)- 脚本的有效退出代码。默认情况下该值为 0

execute_command 参数详解

对于许多新用户来说,execute_command 令人困惑。然而,它提供了一个重要的功能:定制命令的执行方式。最常见的用例是处理 sudo 密码提示。如果您使用非 POSIX shell(例如 FreeBSD 上的 tcsh),您可能还需要定制化该参数。

Windows 的 Linux 子系统

shell-local 后处理器的设计理念是允许您在本地操作系统的原生 shell 中运行命令。对于 Windows,我们默认假设这是 Cmd。但是,通过修改后处理器配置中的 execute_commanduse_linux_pathing 选项,可以从 shell-local 后处理器将 bash 脚本作为 Windows Linux 子系统的一部分运行。

下面的示例是一个功能齐全的测试配置。

该功能的一个限制是您无法使用 inlinecommand 选项,只能使用 scriptscripts 参数。

请注意,此功能仍处于测试阶段,因为底层 WSL 也仍处于测试阶段。因此会有一些限制。例如,除非 Packer 和您要运行的脚本都位于 C 驱动器上,否则它可能无法工作。

HCL2:


source "null" "example" {
    communicator = "none"
}

build {
    sources = [
        "source.null.example"
    ]

    post-processor "shell-local"{
        environment_vars  = ["PROVISIONERTEST=ProvisionerTest1"]
        execute_command   = ["bash", "-c", "{{.Vars}} {{.Script}}"]
        use_linux_pathing = true
        scripts           = ["C:/Users/me/scripts/example_bash.sh"]
    }
    post-processor "shell-local"{
        environment_vars  = ["PROVISIONERTEST=ProvisionerTest2"]
        execute_command   = ["bash", "-c", "{{.Vars}} {{.Script}}"]
        use_linux_pathing = true
        script            = "C:/Users/me/scripts/example_bash.sh"
    }
}

Json:


{
  "builders": [
    {
      "type": "null",
      "communicator": "none"
    }
  ],
  "post-processors": [
    {
      "type": "shell-local",
      "environment_vars": ["PROVISIONERTEST=ProvisionerTest1"],
      "execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
      "use_linux_pathing": true,
      "scripts": ["C:/Users/me/scripts/example_bash.sh"]
    },
    {
      "type": "shell-local",
      "environment_vars": ["PROVISIONERTEST=ProvisionerTest2"],
      "execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
      "use_linux_pathing": true,
      "script": "C:/Users/me/scripts/example_bash.sh"
    }
  ]
}

默认的环境变量

除了能够使用 environment_vars 参数指定自定义环境变量之外,配置程序还自动设定某些常用的环境变量:

  • PACKER_BUILD_NAME 设置为 Packer 正在运行的构建的名称。在 Packer 进行多个构建并且您希望将它们与常见的配置脚本稍微区分开来时,该参数将非常有用。
  • PACKER_BUILDER_TYPE 是用于创建运行脚本的计算机的构建器的类型。如果您只想在使用某些构建器上运行脚本的某些部分,该参数将非常有用。

编写安全的脚本

无论您使用 inline 参数,还是使用 scriptscripts 参数,了解 shell-local 后处理器的工作原理,对以安全、轻松地运行脚本非常重要。这种理解将在此过程中为您节省大量时间。

每个构建器运行一次

您传递给 shell-local 的脚本将在每个构建器中运行一次。这意味着,如果您有 amazon-ebs 构建器和 docker 构建器,您的脚本将运行两次。如果您有 3 个构建器,它将运行 3 次,每个构建器运行一次。

与构建制品的互动

为了与构建制品交互,您可能需要使用 manifest 后处理器。该后处理器将在每个构建器运行后将构建器生成的文件列表写入一个 json 文件。

例如,如果您想将 file 构建器中的文件打包到 tarball 中,您可以这样写:

Json:

{
  "builders": [
    {
      "content": "Lorem ipsum dolor sit amet",
      "target": "dummy_artifact",
      "type": "file"
    }
  ],
  "post-processors": [
    [
      {
        "output": "manifest.json",
        "strip_path": true,
        "type": "manifest"
      },
      {
        "inline": [
          "jq \".builds[].files[].name\" manifest.json | xargs tar cfz artifacts.tgz"
        ],
        "type": "shell-local"
      }
    ]
  ]
}

HCL2:

source "file" "example" {
    content = "Lorem ipsum dolor sit amet"
    target  = "dummy_artifact.txt"
}
build {
  sources = [
    "source.file.example"
  ]
  post-processor "manifest" {
    output     = "manifest.json"
    strip_path = true
  }

  post-processor "shell-local" {
    inline = [
        "jq \".builds[].files[].name\" manifest.json | xargs tar cfz artifacts.tgz"
    ]
  }
}

该例子使用 jq 工具从清单文件中提取所有文件名并将它们传递给 tar。

总是显式退出

任何后处理器失败都会触发 Packer 构建的停止,所有临时制品都会被清除。

对于 shell 脚本,这意味着脚本必须以代码 0 退出。您必须非常小心,在需要时显式地以 exit 0 命令退出。

使用示例

Windows 主机:

以下是一个在 Windows 主机上运行 .cmd 的示例:

HCL2:

post-processor "shell-local" {
  environment_vars = ["SHELLLOCALTEST=ShellTest1"]
  scripts          = ["./scripts/test_cmd.cmd"]
}

Json:

{
  "type": "shell-local",
  "environment_vars": ["SHELLLOCALTEST=ShellTest1"],
  "scripts": ["./scripts/test_cmd.cmd"]
}

test_cmd.cmd 的内容:

echo %SHELLLOCALTEST%

在 Windows 上运行 inline 命令的示例,需要自定义参数 tempfile_extension

HCL2:

post-processor "shell-local" {
  environment_vars   = ["SHELLLOCALTEST=ShellTest2"],
  tempfile_extension = ".cmd",
  inline             = ["echo %SHELLLOCALTEST%"]
}

Json:

{
  "type": "shell-local",
  "environment_vars": ["SHELLLOCALTEST=ShellTest2"],
  "tempfile_extension": ".cmd",
  "inline": ["echo %SHELLLOCALTEST%"]
}

使用 WSL 在 Windows 上运行 bash 命令的示例,需要自定义参数 use_linux_pathingexecute_command

HCL2:


post-processor "shell-local" {
  environment_vars  = ["SHELLLOCALTEST=ShellTest3"],
  execute_command   = ["bash", "-c", "{{.Vars}} {{.Script}}"]
  use_linux_pathing = true
  script            = "./scripts/example_bash.sh"
}

Json:


{
  "type": "shell-local",
  "environment_vars": ["SHELLLOCALTEST=ShellTest3"],
  "execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
  "use_linux_pathing": true,
  "script": "./scripts/example_bash.sh"
}

example_bash.sh 文件的内容:

#!/bin/bash
echo $SHELLLOCALTEST

在 Windows 上运行 PowerShell 脚本的示例,需要自定义参数 env_var_formatexecute_command

HCL2:


post-processor "shell-local" {
  environment_vars = ["SHELLLOCALTEST=ShellTest4"]
  execute_command  = ["powershell.exe", "{{.Vars}} {{.Script}}"]
  env_var_format   = "$env:%s=\"%s\"; "
  script           = "./scripts/example_ps.ps1"
}

Json:


{
  "type": "shell-local",
  "environment_vars": ["SHELLLOCALTEST=ShellTest4"],
  "execute_command": ["powershell.exe", "{{.Vars}} {{.Script}}"],
  "env_var_format": "$env:%s=\"%s\"; ",
  "script": "./scripts/example_ps.ps1"
}

在 Windows 上通过 inline 参数运行 PowerShell 脚本的示例,需要自定义参数 env_var_formattempfile_extensionexecute_command

HCL2:


post-processor "shell-local" {
  tempfile_extension = ".ps1"
  environment_vars   = ["SHELLLOCALTEST=ShellTest5"]
  execute_command    = ["powershell.exe", "{{.Vars}} {{.Script}}"]
  env_var_format     = "$env:%s=\"%s\"; "
  inline             = ["write-output $env:SHELLLOCALTEST"]
}

Json:


{
  "type": "shell-local",
  "tempfile_extension": ".ps1",
  "environment_vars": ["SHELLLOCALTEST=ShellTest5"],
  "execute_command": ["powershell.exe", "{{.Vars}} {{.Script}}"],
  "env_var_format": "$env:%s=\"%s\"; ",
  "inline": ["write-output $env:SHELLLOCALTEST"]
}

Unix 主机

在 Unix 主机上运行一段脚本的例子:

HCL2:

post-processor "shell-local" {
  environment_vars = ["PROVISIONERTEST=ProvisionerTest1"]
  scripts = ["./scripts/example_bash.sh"]
}

Json:

{
  "type": "shell-local",
  "environment_vars": ["PROVISIONERTEST=ProvisionerTest1"],
  "scripts": ["./scripts/example_bash.sh"]
}

在 Unix 上通过 inline 参数运行 bash 脚本的例子:

HCL2:

post-processor "shell-local" {
  environment_vars = ["PROVISIONERTEST=ProvisionerTest2"]
  inline           = ["echo hello", "echo $PROVISIONERTEST"]
}

Json:

{
  "type": "shell-local",
  "environment_vars": ["PROVISIONERTEST=ProvisionerTest2"],
  "inline": ["echo hello", "echo $PROVISIONERTEST"]
}

在 Unix 上运行一段 Python 脚本的例子:

HCL2:


post-processor "shell-local" {
  script           = "hello.py"
  environment_vars = ["HELLO_USER=packeruser"]
  execute_command  = [
    "/bin/sh",
    "-c",
    "{{.Vars}} /usr/local/bin/python {{.Script}}"
  ]
}

Json:


{
  "type": "shell-local",
  "script": "hello.py",
  "environment_vars": ["HELLO_USER=packeruser"],
  "execute_command": [
    "/bin/sh",
    "-c",
    "{{.Vars}} /usr/local/bin/python {{.Script}}"
  ]
}

hello.py 文件的内容:

import os

print('Hello, %s!' % os.getenv("HELLO_USER"))

results matching ""

    No results matching ""