diff --git a/packer/plugin.go b/packer/plugin.go index 69f0b631c..dd3e6d317 100644 --- a/packer/plugin.go +++ b/packer/plugin.go @@ -85,7 +85,8 @@ func (c *PluginConfig) Discover() error { } if len(c.KnownPluginFolders) == 0 { - c.KnownPluginFolders = PluginFolders() + //PluginFolders should match the call in github.com/hahicorp/packer/main.go#loadConfig + c.KnownPluginFolders = PluginFolders(".") } // TODO after JSON is deprecated remove support for legacy component plugins. diff --git a/packer/plugin_discover_test.go b/packer/plugin_discover_test.go index 91e8ba6b3..fec52607d 100644 --- a/packer/plugin_discover_test.go +++ b/packer/plugin_discover_test.go @@ -154,6 +154,233 @@ func TestDiscoverDatasource(t *testing.T) { } } +func TestMultiPlugin_describe(t *testing.T) { + createMockPlugins(t, mockPlugins) + pluginDir := os.Getenv("PACKER_PLUGIN_PATH") + defer os.RemoveAll(pluginDir) + c := PluginConfig{} + err := c.Discover() + if err != nil { + t.Fatalf("error discovering plugins; %s", err.Error()) + } + + for mockPluginName, plugin := range mockPlugins { + for mockBuilderName := range plugin.Builders { + expectedBuilderName := mockPluginName + "-" + mockBuilderName + + if !c.Builders.Has(expectedBuilderName) { + t.Fatalf("expected to find builder %q", expectedBuilderName) + } + } + for mockProvisionerName := range plugin.Provisioners { + expectedProvisionerName := mockPluginName + "-" + mockProvisionerName + if !c.Provisioners.Has(expectedProvisionerName) { + t.Fatalf("expected to find builder %q", expectedProvisionerName) + } + } + for mockPostProcessorName := range plugin.PostProcessors { + expectedPostProcessorName := mockPluginName + "-" + mockPostProcessorName + if !c.PostProcessors.Has(expectedPostProcessorName) { + t.Fatalf("expected to find post-processor %q", expectedPostProcessorName) + } + } + for mockDatasourceName := range plugin.Datasources { + expectedDatasourceName := mockPluginName + "-" + mockDatasourceName + if !c.DataSources.Has(expectedDatasourceName) { + t.Fatalf("expected to find datasource %q", expectedDatasourceName) + } + } + } +} + +func TestMultiPlugin_describe_installed(t *testing.T) { + createMockInstalledPlugins(t, mockInstalledPlugins, createMockChecksumFile) + pluginDir := os.Getenv("PACKER_PLUGIN_PATH") + defer os.RemoveAll(pluginDir) + + c := PluginConfig{} + err := c.Discover() + if err != nil { + t.Fatalf("error discovering plugins; %s", err.Error()) + } + + for mockPluginName, plugin := range mockInstalledPlugins { + mockPluginName = strings.Split(mockPluginName, "_")[0] + for mockBuilderName := range plugin.Builders { + expectedBuilderName := mockPluginName + "-" + mockBuilderName + if !c.Builders.Has(expectedBuilderName) { + t.Fatalf("expected to find builder %q", expectedBuilderName) + } + } + for mockProvisionerName := range plugin.Provisioners { + expectedProvisionerName := mockPluginName + "-" + mockProvisionerName + if !c.Provisioners.Has(expectedProvisionerName) { + t.Fatalf("expected to find builder %q", expectedProvisionerName) + } + } + for mockPostProcessorName := range plugin.PostProcessors { + expectedPostProcessorName := mockPluginName + "-" + mockPostProcessorName + if !c.PostProcessors.Has(expectedPostProcessorName) { + t.Fatalf("expected to find post-processor %q", expectedPostProcessorName) + } + } + for mockDatasourceName := range plugin.Datasources { + expectedDatasourceName := mockPluginName + "-" + mockDatasourceName + if !c.DataSources.Has(expectedDatasourceName) { + t.Fatalf("expected to find datasource %q", expectedDatasourceName) + } + } + } +} + +func TestMultiPlugin_describe_installed_for_invalid(t *testing.T) { + tc := []struct { + desc string + installedPluginsMock map[string]pluginsdk.Set + createMockFn func(*testing.T, map[string]pluginsdk.Set) + }{ + { + desc: "Incorrectly named plugins", + installedPluginsMock: invalidInstalledPluginsMock, + createMockFn: func(t *testing.T, mocks map[string]pluginsdk.Set) { + createMockInstalledPlugins(t, mocks, createMockChecksumFile) + }, + }, + { + desc: "Plugins missing checksums", + installedPluginsMock: mockInstalledPlugins, + createMockFn: func(t *testing.T, mocks map[string]pluginsdk.Set) { + createMockInstalledPlugins(t, mocks) + }, + }, + } + + for _, tt := range tc { + t.Run(tt.desc, func(t *testing.T) { + tt.createMockFn(t, tt.installedPluginsMock) + pluginDir := os.Getenv("PACKER_PLUGIN_PATH") + defer os.RemoveAll(pluginDir) + + c := PluginConfig{} + err := c.Discover() + if err != nil { + t.Fatalf("error discovering plugins; %s", err.Error()) + } + if c.Builders.Has("feather") { + t.Fatalf("expected to not find builder %q", "feather") + } + for mockPluginName, plugin := range tt.installedPluginsMock { + mockPluginName = strings.Split(mockPluginName, "_")[0] + for mockBuilderName := range plugin.Builders { + expectedBuilderName := mockPluginName + "-" + mockBuilderName + if c.Builders.Has(expectedBuilderName) { + t.Fatalf("expected to not find builder %q", expectedBuilderName) + } + } + for mockProvisionerName := range plugin.Provisioners { + expectedProvisionerName := mockPluginName + "-" + mockProvisionerName + if c.Provisioners.Has(expectedProvisionerName) { + t.Fatalf("expected to not find builder %q", expectedProvisionerName) + } + } + for mockPostProcessorName := range plugin.PostProcessors { + expectedPostProcessorName := mockPluginName + "-" + mockPostProcessorName + if c.PostProcessors.Has(expectedPostProcessorName) { + t.Fatalf("expected to not find post-processor %q", expectedPostProcessorName) + } + } + for mockDatasourceName := range plugin.Datasources { + expectedDatasourceName := mockPluginName + "-" + mockDatasourceName + if c.DataSources.Has(expectedDatasourceName) { + t.Fatalf("expected to not find datasource %q", expectedDatasourceName) + } + } + } + }) + } +} + +func TestMultiPlugin_defaultName(t *testing.T) { + createMockPlugins(t, defaultNameMock) + pluginDir := os.Getenv("PACKER_PLUGIN_PATH") + defer os.RemoveAll(pluginDir) + + c := PluginConfig{} + err := c.Discover() + if err != nil { + t.Fatalf("error discovering plugins; %s ; mocks are %#v", err.Error(), defaultNameMock) + } + + expectedBuilderNames := []string{"foo-bar", "foo-baz", "foo"} + for _, mockBuilderName := range expectedBuilderNames { + if !c.Builders.Has(mockBuilderName) { + t.Fatalf("expected to find builder %q; builders is %#v", mockBuilderName, c.Builders) + } + } +} + +// no T.Parallel using os.Chdir +func TestMultiPlugin_CWD(t *testing.T) { + createMockPlugins(t, defaultNameMock) + pluginDir := os.Getenv("PACKER_PLUGIN_PATH") + defer os.RemoveAll(pluginDir) + // Unset PACKER_PLUGIN_PATH to test CWD loading + os.Unsetenv("PACKER_PLUGIN_PATH") + if err := os.Chdir(pluginDir); err != nil { + t.Fatalf("failed to change directory to test loading from CWD: %s", err) + } + c := PluginConfig{} + err := c.Discover() + if err != nil { + t.Fatalf("error discovering plugins; %s ; mocks are %#v", err.Error(), defaultNameMock) + } + expectedBuilderNames := []string{"foo-bar", "foo-baz", "foo"} + for _, mockBuilderName := range expectedBuilderNames { + if !c.Builders.Has(mockBuilderName) { + t.Fatalf("expected to find builder %q; builders is %#v", mockBuilderName, c.Builders) + } + } +} + +func TestMultiPlugin_IgnoreChecksumFile(t *testing.T) { + createMockPlugins(t, defaultNameMock) + pluginDir := os.Getenv("PACKER_PLUGIN_PATH") + defer os.RemoveAll(pluginDir) + + csFile, err := generateMockChecksumFile(filepath.Join(pluginDir, "packer-plugin-foo")) + if err != nil { + t.Fatal(err.Error()) + } + // Copy plugin contents into checksum file to validate that it is not only skipped but that it never gets loaded + if err := os.Rename(filepath.Join(pluginDir, "packer-plugin-foo"), csFile); err != nil { + t.Fatalf("failed to rename plugin bin file to checkfum file needed for test: %s", err) + } + + c := PluginConfig{} + err = c.Discover() + if err != nil { + t.Fatalf("error discovering plugins; %s ; mocks are %#v", err.Error(), defaultNameMock) + } + expectedBuilderNames := []string{"foo-bar", "foo-baz", "foo"} + for _, mockBuilderName := range expectedBuilderNames { + if c.Builders.Has(mockBuilderName) { + t.Fatalf("expected to not find builder %q; builders is %#v", mockBuilderName, c.Builders) + } + } +} + +func TestMultiPlugin_defaultName_each_plugin_type(t *testing.T) { + createMockPlugins(t, doubleDefaultMock) + pluginDir := os.Getenv("PACKER_PLUGIN_PATH") + defer os.RemoveAll(pluginDir) + + c := PluginConfig{} + err := c.Discover() + if err != nil { + t.Fatal("Should not have error because pluginsdk.DEFAULT_NAME is used twice but only once per plugin type.") + } +} + func generateFakePlugins(dirname string, pluginNames []string) (string, []string, func(), error) { dir, err := os.MkdirTemp("", dirname) if err != nil { @@ -296,6 +523,15 @@ func createMockPlugins(t *testing.T, plugins map[string]pluginsdk.Set) { } func createMockChecksumFile(t testing.TB, filePath string) { + t.Helper() + cs, err := generateMockChecksumFile(filePath) + if err != nil { + t.Fatalf(err.Error()) + } + t.Logf("created fake plugin checksum file %s", cs) +} + +func generateMockChecksumFile(filePath string) (string, error) { cs := plugingetter.Checksummer{ Type: "sha256", Hash: sha256.New(), @@ -303,19 +539,20 @@ func createMockChecksumFile(t testing.TB, filePath string) { f, err := os.Open(filePath) if err != nil { - t.Fatalf("failed to open fake plugin binary: %v", err) + return "", fmt.Errorf("failed to open fake plugin binary: %v", err) } defer f.Close() sum, err := cs.Sum(f) if err != nil { - t.Fatalf("failed to checksum fake plugin binary: %v", err) + return "", fmt.Errorf("failed to checksum fake plugin binary: %v", err) } - t.Logf("creating fake plugin checksum file %s with contents %x", filePath+cs.FileExt(), string(sum)) - if err := os.WriteFile(filePath+cs.FileExt(), []byte(fmt.Sprintf("%x", sum)), os.ModePerm); err != nil { - t.Fatalf("failed to write checksum fake plugin binary: %v", err) + sumfile := filePath + cs.FileExt() + if err := os.WriteFile(sumfile, []byte(fmt.Sprintf("%x", sum)), os.ModePerm); err != nil { + return "", fmt.Errorf("failed to write checksum fake plugin binary: %v", err) } + return sumfile, nil } func createMockInstalledPlugins(t *testing.T, plugins map[string]pluginsdk.Set, opts ...func(tb testing.TB, filePath string)) { @@ -461,180 +698,3 @@ var ( }, } ) - -func Test_multiplugin_describe(t *testing.T) { - createMockPlugins(t, mockPlugins) - pluginDir := os.Getenv("PACKER_PLUGIN_PATH") - defer os.RemoveAll(pluginDir) - c := PluginConfig{} - err := c.Discover() - if err != nil { - t.Fatalf("error discovering plugins; %s", err.Error()) - } - - for mockPluginName, plugin := range mockPlugins { - for mockBuilderName := range plugin.Builders { - expectedBuilderName := mockPluginName + "-" + mockBuilderName - - if !c.Builders.Has(expectedBuilderName) { - t.Fatalf("expected to find builder %q", expectedBuilderName) - } - } - for mockProvisionerName := range plugin.Provisioners { - expectedProvisionerName := mockPluginName + "-" + mockProvisionerName - if !c.Provisioners.Has(expectedProvisionerName) { - t.Fatalf("expected to find builder %q", expectedProvisionerName) - } - } - for mockPostProcessorName := range plugin.PostProcessors { - expectedPostProcessorName := mockPluginName + "-" + mockPostProcessorName - if !c.PostProcessors.Has(expectedPostProcessorName) { - t.Fatalf("expected to find post-processor %q", expectedPostProcessorName) - } - } - for mockDatasourceName := range plugin.Datasources { - expectedDatasourceName := mockPluginName + "-" + mockDatasourceName - if !c.DataSources.Has(expectedDatasourceName) { - t.Fatalf("expected to find datasource %q", expectedDatasourceName) - } - } - } -} - -func Test_multiplugin_describe_installed(t *testing.T) { - createMockInstalledPlugins(t, mockInstalledPlugins, createMockChecksumFile) - pluginDir := os.Getenv("PACKER_PLUGIN_PATH") - defer os.RemoveAll(pluginDir) - - c := PluginConfig{} - err := c.Discover() - if err != nil { - t.Fatalf("error discovering plugins; %s", err.Error()) - } - - for mockPluginName, plugin := range mockInstalledPlugins { - mockPluginName = strings.Split(mockPluginName, "_")[0] - for mockBuilderName := range plugin.Builders { - expectedBuilderName := mockPluginName + "-" + mockBuilderName - if !c.Builders.Has(expectedBuilderName) { - t.Fatalf("expected to find builder %q", expectedBuilderName) - } - } - for mockProvisionerName := range plugin.Provisioners { - expectedProvisionerName := mockPluginName + "-" + mockProvisionerName - if !c.Provisioners.Has(expectedProvisionerName) { - t.Fatalf("expected to find builder %q", expectedProvisionerName) - } - } - for mockPostProcessorName := range plugin.PostProcessors { - expectedPostProcessorName := mockPluginName + "-" + mockPostProcessorName - if !c.PostProcessors.Has(expectedPostProcessorName) { - t.Fatalf("expected to find post-processor %q", expectedPostProcessorName) - } - } - for mockDatasourceName := range plugin.Datasources { - expectedDatasourceName := mockPluginName + "-" + mockDatasourceName - if !c.DataSources.Has(expectedDatasourceName) { - t.Fatalf("expected to find datasource %q", expectedDatasourceName) - } - } - } -} - -func Test_multiplugin_describe_installed_for_invalid(t *testing.T) { - tc := []struct { - desc string - installedPluginsMock map[string]pluginsdk.Set - createMockFn func(*testing.T, map[string]pluginsdk.Set) - }{ - { - desc: "Incorrectly named plugins", - installedPluginsMock: invalidInstalledPluginsMock, - createMockFn: func(t *testing.T, mocks map[string]pluginsdk.Set) { - createMockInstalledPlugins(t, mocks, createMockChecksumFile) - }, - }, - { - desc: "Plugins missing checksums", - installedPluginsMock: mockInstalledPlugins, - createMockFn: func(t *testing.T, mocks map[string]pluginsdk.Set) { - createMockInstalledPlugins(t, mocks) - }, - }, - } - - for _, tt := range tc { - t.Run(tt.desc, func(t *testing.T) { - tt.createMockFn(t, tt.installedPluginsMock) - pluginDir := os.Getenv("PACKER_PLUGIN_PATH") - defer os.RemoveAll(pluginDir) - - c := PluginConfig{} - err := c.Discover() - if err != nil { - t.Fatalf("error discovering plugins; %s", err.Error()) - } - if c.Builders.Has("feather") { - t.Fatalf("expected to not find builder %q", "feather") - } - for mockPluginName, plugin := range tt.installedPluginsMock { - mockPluginName = strings.Split(mockPluginName, "_")[0] - for mockBuilderName := range plugin.Builders { - expectedBuilderName := mockPluginName + "-" + mockBuilderName - if c.Builders.Has(expectedBuilderName) { - t.Fatalf("expected to not find builder %q", expectedBuilderName) - } - } - for mockProvisionerName := range plugin.Provisioners { - expectedProvisionerName := mockPluginName + "-" + mockProvisionerName - if c.Provisioners.Has(expectedProvisionerName) { - t.Fatalf("expected to not find builder %q", expectedProvisionerName) - } - } - for mockPostProcessorName := range plugin.PostProcessors { - expectedPostProcessorName := mockPluginName + "-" + mockPostProcessorName - if c.PostProcessors.Has(expectedPostProcessorName) { - t.Fatalf("expected to not find post-processor %q", expectedPostProcessorName) - } - } - for mockDatasourceName := range plugin.Datasources { - expectedDatasourceName := mockPluginName + "-" + mockDatasourceName - if c.DataSources.Has(expectedDatasourceName) { - t.Fatalf("expected to not find datasource %q", expectedDatasourceName) - } - } - } - }) - } -} - -func Test_multiplugin_defaultName(t *testing.T) { - createMockPlugins(t, defaultNameMock) - pluginDir := os.Getenv("PACKER_PLUGIN_PATH") - defer os.RemoveAll(pluginDir) - - c := PluginConfig{} - err := c.Discover() - if err != nil { - t.Fatalf("error discovering plugins; %s ; mocks are %#v", err.Error(), defaultNameMock) - } - - expectedBuilderNames := []string{"foo-bar", "foo-baz", "foo"} - for _, mockBuilderName := range expectedBuilderNames { - if !c.Builders.Has(mockBuilderName) { - t.Fatalf("expected to find builder %q; builders is %#v", mockBuilderName, c.Builders) - } - } -} - -func Test_only_one_multiplugin_defaultName_each_plugin_type(t *testing.T) { - createMockPlugins(t, doubleDefaultMock) - pluginDir := os.Getenv("PACKER_PLUGIN_PATH") - defer os.RemoveAll(pluginDir) - - c := PluginConfig{} - err := c.Discover() - if err != nil { - t.Fatal("Should not have error because pluginsdk.DEFAULT_NAME is used twice but only once per plugin type.") - } -}