From d09a9ab0c7d94f4f938195367fe3dc59165707de Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Mon, 17 Aug 2015 01:26:03 -0700 Subject: [PATCH] Implemented internal plugins - Internal plugins are compiled into the same packer binary and invoked through the plugin command - Search paths allow disk-based plugins to override and should function as normal - This should allow for a 94% space savings vs statically compiling all the plugins as separate binaries.. approximately 24mb vs 431mb --- command/plugin.go | 147 ++++++++++++++++++++++++++++++++++++++++++++++ commands.go | 6 ++ config.go | 55 ++++++++++++++++- 3 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 command/plugin.go diff --git a/command/plugin.go b/command/plugin.go new file mode 100644 index 000000000..76e97484e --- /dev/null +++ b/command/plugin.go @@ -0,0 +1,147 @@ +package command + +import ( + "fmt" + "log" + "os" + "strings" + + "github.com/mitchellh/packer/builder/amazon/chroot" + "github.com/mitchellh/packer/builder/amazon/ebs" + "github.com/mitchellh/packer/builder/amazon/instance" + "github.com/mitchellh/packer/builder/digitalocean" + "github.com/mitchellh/packer/builder/docker" + filebuilder "github.com/mitchellh/packer/builder/file" + "github.com/mitchellh/packer/builder/googlecompute" + "github.com/mitchellh/packer/builder/null" + "github.com/mitchellh/packer/builder/openstack" + parallelsiso "github.com/mitchellh/packer/builder/parallels/iso" + parallelspvm "github.com/mitchellh/packer/builder/parallels/pvm" + "github.com/mitchellh/packer/builder/qemu" + virtualboxiso "github.com/mitchellh/packer/builder/virtualbox/iso" + virtualboxovf "github.com/mitchellh/packer/builder/virtualbox/ovf" + vmwareiso "github.com/mitchellh/packer/builder/vmware/iso" + vmwarevmx "github.com/mitchellh/packer/builder/vmware/vmx" + "github.com/mitchellh/packer/packer" + "github.com/mitchellh/packer/packer/plugin" + "github.com/mitchellh/packer/provisioner/ansible-local" + "github.com/mitchellh/packer/provisioner/chef-client" + "github.com/mitchellh/packer/provisioner/chef-solo" + fileprovisioner "github.com/mitchellh/packer/provisioner/file" + "github.com/mitchellh/packer/provisioner/powershell" + "github.com/mitchellh/packer/provisioner/puppet-masterless" + "github.com/mitchellh/packer/provisioner/puppet-server" + "github.com/mitchellh/packer/provisioner/salt-masterless" + "github.com/mitchellh/packer/provisioner/shell" + shelllocal "github.com/mitchellh/packer/provisioner/shell-local" + "github.com/mitchellh/packer/provisioner/windows-restart" + windowsshell "github.com/mitchellh/packer/provisioner/windows-shell" +) + +type PluginCommand struct { + Meta +} + +var Builders = map[string]packer.Builder{ + "amazon-chroot": new(chroot.Builder), + "amazon-ebs": new(ebs.Builder), + "amazon-instance": new(instance.Builder), + "digitalocean": new(digitalocean.Builder), + "docker": new(docker.Builder), + "file": new(filebuilder.Builder), + "googlecompute": new(googlecompute.Builder), + "null": new(null.Builder), + "openstack": new(openstack.Builder), + "parallels-iso": new(parallelsiso.Builder), + "parallels-pvm": new(parallelspvm.Builder), + "qemu": new(qemu.Builder), + "virtualbox-iso": new(virtualboxiso.Builder), + "virtualbox-ovf": new(virtualboxovf.Builder), + "vmware-iso": new(vmwareiso.Builder), + "vmware-vmx": new(vmwarevmx.Builder), +} + +var Provisioners = map[string]packer.Provisioner{ + "ansible-local": new(ansiblelocal.Provisioner), + "chef-client": new(chefclient.Provisioner), + "chef-solo": new(chefsolo.Provisioner), + "file": new(fileprovisioner.Provisioner), + "powershell": new(powershell.Provisioner), + "puppet-masterless": new(puppetmasterless.Provisioner), + "puppet-server": new(puppetserver.Provisioner), + "salt-masterless": new(saltmasterless.Provisioner), + "shell": new(shell.Provisioner), + "shell-local": new(shelllocal.Provisioner), + "windows-restart": new(restart.Provisioner), + "windows-shell": new(windowsshell.Provisioner), +} + +var PostProcessors = map[string]packer.PostProcessor{} + +func (c *PluginCommand) Run(args []string) int { + // This is an internal call so we're not going to do much error checking. + // If there's a problem we'll usually just crash. + log.Printf("args: %#v", args) + if len(args) != 1 { + c.Ui.Error("Wrong number of args") + os.Exit(1) + } + + // Plugin should be called like "packer-builder-amazon-ebs" so we'll take it + // apart. + parts := strings.Split(args[0], "-") + pluginType := parts[1] + pluginName := "" + // Post-processor is split so we'll so some magic here. We could use a + // regexp but this is simpler. + if pluginType == "post" { + pluginType = strings.Join(parts[1:2], "-") + pluginName = strings.Join(parts[3:], "-") + } else { + pluginName = strings.Join(parts[2:], "-") + } + + server, err := plugin.Server() + if err != nil { + panic(err) + } + + if pluginType == "builder" { + builder, found := Builders[pluginName] + if !found { + c.Ui.Error(fmt.Sprintf("Could not load builder: %s", pluginName)) + } + server.RegisterBuilder(builder) + } else if pluginType == "provisioner" { + provisioner, found := Provisioners[pluginName] + if !found { + c.Ui.Error(fmt.Sprintf("Could not load provisioner: %s", pluginName)) + } + server.RegisterProvisioner(provisioner) + } else if pluginType == "post-processor" { + postProcessor, found := PostProcessors[pluginName] + if !found { + c.Ui.Error(fmt.Sprintf("Could not load post-processor: %s", pluginName)) + } + server.RegisterPostProcessor(postProcessor) + } + + server.Serve() + + return 0 +} + +func (*PluginCommand) Help() string { + helpText := ` +Usage: packer plugin PLUGIN + + Runs an internally-compiled version of a plugin from the packer binary. Note + that this is an internal command and you should not call it yourself. +` + + return strings.TrimSpace(helpText) +} + +func (c *PluginCommand) Synopsis() string { + return "call an internal plugin" +} diff --git a/commands.go b/commands.go index 510250721..21ed30df9 100644 --- a/commands.go +++ b/commands.go @@ -59,6 +59,12 @@ func init() { CheckFunc: commandVersionCheck, }, nil }, + + "plugin": func() (cli.Command, error) { + return &command.PluginCommand{ + Meta: *CommandMeta, + }, nil + }, } } diff --git a/config.go b/config.go index efb4e7d31..004315489 100644 --- a/config.go +++ b/config.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "io" "log" "os/exec" @@ -10,6 +11,7 @@ import ( "strings" "github.com/mitchellh/osext" + "github.com/mitchellh/packer/command" "github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer/plugin" ) @@ -73,11 +75,17 @@ func (c *config) Discover() error { } } - // Last, look in the CWD. + // Next, look in the CWD. if err := c.discover("."); err != nil { return err } + // Finally, try to use an internal plugin. Note that this will not Override + // any previously-loaded plugins. + if err := c.discoverInternal(); err != nil { + return err + } + return nil } @@ -196,6 +204,41 @@ func (c *config) discoverSingle(glob string, m *map[string]string) error { return nil } +func (c *config) discoverInternal() error { + // Get the packer binary path + packerPath, err := osext.Executable() + if err != nil { + log.Printf("[ERR] Error loading exe directory: %s", err) + return err + } + + for builder := range command.Builders { + _, found := (c.Builders)[builder] + if !found { + log.Printf("Using internal plugin for %s", builder) + (c.Builders)[builder] = fmt.Sprintf("%s-PACKERSPACE-plugin-PACKERSPACE-packer-builder-%s", packerPath, builder) + } + } + + for provisioner := range command.Provisioners { + _, found := (c.Provisioners)[provisioner] + if !found { + log.Printf("Using internal plugin for %s", provisioner) + (c.Provisioners)[provisioner] = fmt.Sprintf("%s-PACKERSPACE-plugin-PACKERSPACE-packer-provisioner-%s", packerPath, provisioner) + } + } + + for postProcessor := range command.PostProcessors { + _, found := (c.PostProcessors)[postProcessor] + if !found { + log.Printf("Using internal plugin for %s", postProcessor) + (c.PostProcessors)[postProcessor] = fmt.Sprintf("%s-PACKERSPACE-plugin-PACKERSPACE-packer-post-processor-%s", packerPath, postProcessor) + } + } + + return nil +} + func (c *config) pluginClient(path string) *plugin.Client { originalPath := path @@ -214,6 +257,14 @@ func (c *config) pluginClient(path string) *plugin.Client { } } + // Check for special case using `packer plugin PLUGIN` + args := []string{} + if strings.Contains(path, "-PACKERSPACE-") { + parts := strings.Split(path, "-PACKERSPACE-") + path = parts[0] + args = parts[1:] + } + // If everything failed, just use the original path and let the error // bubble through. if path == "" { @@ -222,7 +273,7 @@ func (c *config) pluginClient(path string) *plugin.Client { log.Printf("Creating plugin client for path: %s", path) var config plugin.ClientConfig - config.Cmd = exec.Command(path) + config.Cmd = exec.Command(path, args...) config.Managed = true config.MinPort = c.PluginMinPort config.MaxPort = c.PluginMaxPort