Merge pull request #8616 from hashicorp/packer-plugin-path
add PACKER_PLUGIN_PATH for plugin discovery
This commit is contained in:
commit
817957fe4f
20
config.go
20
config.go
|
@ -130,7 +130,7 @@ func (c *config) Discover() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// First, look in the same directory as the executable.
|
||||
// Next, look in the same directory as the executable.
|
||||
exePath, err := osext.Executable()
|
||||
if err != nil {
|
||||
log.Printf("[ERR] Error loading exe directory: %s", err)
|
||||
|
@ -140,7 +140,7 @@ func (c *config) Discover() error {
|
|||
}
|
||||
}
|
||||
|
||||
// Next, look in the plugins directory.
|
||||
// Next, look in the default plugins directory inside the configdir/.packer.d/plugins.
|
||||
dir, err := packer.ConfigDir()
|
||||
if err != nil {
|
||||
log.Printf("[ERR] Error loading config directory: %s", err)
|
||||
|
@ -155,6 +155,22 @@ func (c *config) Discover() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Check whether there is a custom Plugin directory defined. This gets
|
||||
// absolute preference.
|
||||
if packerPluginPath := os.Getenv("PACKER_PLUGIN_PATH"); packerPluginPath != "" {
|
||||
sep := ":"
|
||||
if runtime.GOOS == "windows" {
|
||||
// on windows, PATH is semicolon-separated
|
||||
sep = ";"
|
||||
}
|
||||
plugPaths := strings.Split(packerPluginPath, sep)
|
||||
for _, plugPath := range plugPaths {
|
||||
if err := c.discoverExternalComponents(plugPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, try to use an internal plugin. Note that this will not override
|
||||
// any previously-loaded plugins.
|
||||
if err := c.discoverInternalComponents(); err != nil {
|
||||
|
|
145
config_test.go
145
config_test.go
|
@ -12,8 +12,107 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/packer/plugin"
|
||||
)
|
||||
|
||||
func newConfig() config {
|
||||
var conf config
|
||||
conf.PluginMinPort = 10000
|
||||
conf.PluginMaxPort = 25000
|
||||
conf.Builders = packer.MapOfBuilder{}
|
||||
conf.PostProcessors = packer.MapOfPostProcessor{}
|
||||
conf.Provisioners = packer.MapOfProvisioner{}
|
||||
|
||||
return conf
|
||||
}
|
||||
func TestDiscoverReturnsIfMagicCookieSet(t *testing.T) {
|
||||
config := newConfig()
|
||||
|
||||
os.Setenv(plugin.MagicCookieKey, plugin.MagicCookieValue)
|
||||
defer os.Unsetenv(plugin.MagicCookieKey)
|
||||
|
||||
err := config.Discover()
|
||||
if err != nil {
|
||||
t.Fatalf("Should not have errored: %s", err)
|
||||
}
|
||||
|
||||
if len(config.Builders) != 0 {
|
||||
t.Fatalf("Should not have tried to find builders")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvVarPackerPluginPath(t *testing.T) {
|
||||
// Create a temporary directory to store plugins in
|
||||
dir, _, cleanUpFunc, err := generateFakePlugins("custom_plugin_dir",
|
||||
[]string{"packer-provisioner-partyparrot"})
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating fake custom plugins: %s", err)
|
||||
}
|
||||
|
||||
defer cleanUpFunc()
|
||||
|
||||
// Add temp dir to path.
|
||||
os.Setenv("PACKER_PLUGIN_PATH", dir)
|
||||
defer os.Unsetenv("PACKER_PLUGIN_PATH")
|
||||
|
||||
config := newConfig()
|
||||
|
||||
err = config.Discover()
|
||||
if err != nil {
|
||||
t.Fatalf("Should not have errored: %s", err)
|
||||
}
|
||||
|
||||
if len(config.Provisioners) == 0 {
|
||||
t.Fatalf("Should have found partyparrot provisioner")
|
||||
}
|
||||
if _, ok := config.Provisioners["partyparrot"]; !ok {
|
||||
t.Fatalf("Should have found partyparrot provisioner.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvVarPackerPluginPath_MultiplePaths(t *testing.T) {
|
||||
// Create a temporary directory to store plugins in
|
||||
dir, _, cleanUpFunc, err := generateFakePlugins("custom_plugin_dir",
|
||||
[]string{"packer-provisioner-partyparrot"})
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating fake custom plugins: %s", err)
|
||||
}
|
||||
|
||||
defer cleanUpFunc()
|
||||
|
||||
pathsep := ":"
|
||||
if runtime.GOOS == "windows" {
|
||||
pathsep = ";"
|
||||
}
|
||||
|
||||
// Create a second dir to look in that will be empty
|
||||
decoyDir, err := ioutil.TempDir("", "decoy")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a temporary test dir.")
|
||||
}
|
||||
defer os.Remove(decoyDir)
|
||||
|
||||
pluginPath := dir + pathsep + decoyDir
|
||||
|
||||
// Add temp dir to path.
|
||||
os.Setenv("PACKER_PLUGIN_PATH", pluginPath)
|
||||
defer os.Unsetenv("PACKER_PLUGIN_PATH")
|
||||
|
||||
config := newConfig()
|
||||
|
||||
err = config.Discover()
|
||||
if err != nil {
|
||||
t.Fatalf("Should not have errored: %s", err)
|
||||
}
|
||||
|
||||
if len(config.Provisioners) == 0 {
|
||||
t.Fatalf("Should have found partyparrot provisioner")
|
||||
}
|
||||
if _, ok := config.Provisioners["partyparrot"]; !ok {
|
||||
t.Fatalf("Should have found partyparrot provisioner.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeConfig(t *testing.T) {
|
||||
|
||||
packerConfig := `
|
||||
|
@ -145,18 +244,13 @@ func TestLoadSingleComponent(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
/* generateFakePackerConfigData creates a collection of mock plugins along with a basic packerconfig.
|
||||
The return packerConfigData is a valid packerconfig file that can be used for configuring external plugins, cleanUpFunc is a function that should be called for cleaning up any generated mock data.
|
||||
This function will only clean up if there is an error, on successful runs the caller
|
||||
is responsible for cleaning up the data via cleanUpFunc().
|
||||
*/
|
||||
func generateFakePackerConfigData() (packerConfigData string, cleanUpFunc func(), err error) {
|
||||
dir, err := ioutil.TempDir("", "random-testdata")
|
||||
func generateFakePlugins(dirname string, pluginNames []string) (string, []string, func(), error) {
|
||||
dir, err := ioutil.TempDir("", dirname)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to create temporary test directory: %v", err)
|
||||
return "", nil, nil, fmt.Errorf("failed to create temporary test directory: %v", err)
|
||||
}
|
||||
|
||||
cleanUpFunc = func() {
|
||||
cleanUpFunc := func() {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
|
@ -165,19 +259,36 @@ func generateFakePackerConfigData() (packerConfigData string, cleanUpFunc func()
|
|||
suffix = ".exe"
|
||||
}
|
||||
|
||||
plugins := [...]string{
|
||||
filepath.Join(dir, "packer-builder-cloud-xyz"+suffix),
|
||||
filepath.Join(dir, "packer-provisioner-super-shell"+suffix),
|
||||
filepath.Join(dir, "packer-post-processor-noop"+suffix),
|
||||
}
|
||||
for _, plugin := range plugins {
|
||||
_, err := os.Create(plugin)
|
||||
plugins := make([]string, len(pluginNames))
|
||||
for i, plugin := range pluginNames {
|
||||
plug := filepath.Join(dir, plugin+suffix)
|
||||
plugins[i] = plug
|
||||
_, err := os.Create(plug)
|
||||
if err != nil {
|
||||
cleanUpFunc()
|
||||
return "", nil, fmt.Errorf("failed to create temporary plugin file (%s): %v", plugin, err)
|
||||
return "", nil, nil, fmt.Errorf("failed to create temporary plugin file (%s): %v", plug, err)
|
||||
}
|
||||
}
|
||||
|
||||
return dir, plugins, cleanUpFunc, nil
|
||||
}
|
||||
|
||||
/* generateFakePackerConfigData creates a collection of mock plugins along with a basic packerconfig.
|
||||
The return packerConfigData is a valid packerconfig file that can be used for configuring external plugins, cleanUpFunc is a function that should be called for cleaning up any generated mock data.
|
||||
This function will only clean up if there is an error, on successful runs the caller
|
||||
is responsible for cleaning up the data via cleanUpFunc().
|
||||
*/
|
||||
func generateFakePackerConfigData() (packerConfigData string, cleanUpFunc func(), err error) {
|
||||
_, plugins, cleanUpFunc, err := generateFakePlugins("random-testdata",
|
||||
[]string{"packer-builder-cloud-xyz",
|
||||
"packer-provisioner-super-shell",
|
||||
"packer-post-processor-noop"})
|
||||
|
||||
if err != nil {
|
||||
cleanUpFunc()
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
packerConfigData = fmt.Sprintf(`
|
||||
{
|
||||
"PluginMinPort": 10,
|
||||
|
|
|
@ -51,6 +51,7 @@ Once the plugin is named properly, Packer automatically discovers plugins in
|
|||
the following directories in the given order. If a conflicting plugin is found
|
||||
later, it will take precedence over one found earlier.
|
||||
|
||||
|
||||
1. The directory where `packer` is, or the executable directory.
|
||||
|
||||
2. The `$HOME/.packer.d/plugins` directory, if `$HOME` is defined (unix)
|
||||
|
@ -62,6 +63,14 @@ later, it will take precedence over one found earlier.
|
|||
|
||||
5. The current working directory.
|
||||
|
||||
6. The directory defined in the env var `PACKER_PLUGIN_PATH`. There can be more
|
||||
than one directory defined; for example, `~/custom-dir-1:~/custom-dir-2`.
|
||||
Separate directories in the PATH string using a colon (`:`) on posix systems and
|
||||
a semicolon (`;`) on windows systems. The above example path would be able to
|
||||
find a provisioner named `packer-provisioner-foo` in either
|
||||
`~/custom-dir-1/packer-provisioner-foo` or
|
||||
`~/custom-dir-2/packer-provisioner-foo`.
|
||||
|
||||
The valid types for plugins are:
|
||||
|
||||
- `builder` - Plugins responsible for building images for a specific
|
||||
|
|
|
@ -39,6 +39,14 @@ each can be found below:
|
|||
connections on your local host. The default is 10,000. See the [core
|
||||
configuration page](/docs/other/core-configuration.html).
|
||||
|
||||
- `PACKER_PLUGIN_PATH` - a PATH variable for finding third-party packer
|
||||
plugins. For example: `~/custom-dir-1:~/custom-dir-2`.
|
||||
Separate directories in the PATH string using a colon (`:`) on posix systems and
|
||||
a semicolon (`;`) on windows systems. The above example path would be able to
|
||||
find a provisioner named `packer-provisioner-foo` in either
|
||||
`~/custom-dir-1/packer-provisioner-foo` or
|
||||
`~/custom-dir-2/packer-provisioner-foo`.
|
||||
|
||||
- `CHECKPOINT_DISABLE` - When Packer is invoked it sometimes calls out to
|
||||
[checkpoint.hashicorp.com](https://checkpoint.hashicorp.com/) to look for
|
||||
new versions of Packer. If you want to disable this for security or privacy
|
||||
|
|
Loading…
Reference in New Issue