Skip to content

Commit

Permalink
Merge pull request hashicorp#2854 from mitchellh/f-integrated-plugins…
Browse files Browse the repository at this point in the history
…-rebase

Implemented internal plugins
  • Loading branch information
cbednarski committed Oct 22, 2015
2 parents f197cf7 + a143f1e commit 8e63ce1
Show file tree
Hide file tree
Showing 8 changed files with 637 additions and 13 deletions.
181 changes: 181 additions & 0 deletions command/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
//
// This file is automatically generated by scripts/generate-plugins.go -- Do not edit!
//

package command

import (
"fmt"
"log"
"regexp"
"strings"

"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/packer/plugin"

amazonchrootbuilder "github.com/mitchellh/packer/builder/amazon/chroot"
amazonebsbuilder "github.com/mitchellh/packer/builder/amazon/ebs"
amazoninstancebuilder "github.com/mitchellh/packer/builder/amazon/instance"
ansiblelocalprovisioner "github.com/mitchellh/packer/provisioner/ansible-local"
artificepostprocessor "github.com/mitchellh/packer/post-processor/artifice"
atlaspostprocessor "github.com/mitchellh/packer/post-processor/atlas"
chefclientprovisioner "github.com/mitchellh/packer/provisioner/chef-client"
chefsoloprovisioner "github.com/mitchellh/packer/provisioner/chef-solo"
compresspostprocessor "github.com/mitchellh/packer/post-processor/compress"
digitaloceanbuilder "github.com/mitchellh/packer/builder/digitalocean"
dockerbuilder "github.com/mitchellh/packer/builder/docker"
dockerimportpostprocessor "github.com/mitchellh/packer/post-processor/docker-import"
dockerpushpostprocessor "github.com/mitchellh/packer/post-processor/docker-push"
dockersavepostprocessor "github.com/mitchellh/packer/post-processor/docker-save"
dockertagpostprocessor "github.com/mitchellh/packer/post-processor/docker-tag"
filebuilder "github.com/mitchellh/packer/builder/file"
fileprovisioner "github.com/mitchellh/packer/provisioner/file"
googlecomputebuilder "github.com/mitchellh/packer/builder/googlecompute"
nullbuilder "github.com/mitchellh/packer/builder/null"
openstackbuilder "github.com/mitchellh/packer/builder/openstack"
parallelsisobuilder "github.com/mitchellh/packer/builder/parallels/iso"
parallelspvmbuilder "github.com/mitchellh/packer/builder/parallels/pvm"
powershellprovisioner "github.com/mitchellh/packer/provisioner/powershell"
puppetmasterlessprovisioner "github.com/mitchellh/packer/provisioner/puppet-masterless"
puppetserverprovisioner "github.com/mitchellh/packer/provisioner/puppet-server"
qemubuilder "github.com/mitchellh/packer/builder/qemu"
saltmasterlessprovisioner "github.com/mitchellh/packer/provisioner/salt-masterless"
shelllocalprovisioner "github.com/mitchellh/packer/provisioner/shell-local"
shellprovisioner "github.com/mitchellh/packer/provisioner/shell"
vagrantcloudpostprocessor "github.com/mitchellh/packer/post-processor/vagrant-cloud"
vagrantpostprocessor "github.com/mitchellh/packer/post-processor/vagrant"
virtualboxisobuilder "github.com/mitchellh/packer/builder/virtualbox/iso"
virtualboxovfbuilder "github.com/mitchellh/packer/builder/virtualbox/ovf"
vmwareisobuilder "github.com/mitchellh/packer/builder/vmware/iso"
vmwarevmxbuilder "github.com/mitchellh/packer/builder/vmware/vmx"
vspherepostprocessor "github.com/mitchellh/packer/post-processor/vsphere"
windowsrestartprovisioner "github.com/mitchellh/packer/provisioner/windows-restart"
windowsshellprovisioner "github.com/mitchellh/packer/provisioner/windows-shell"

)

type PluginCommand struct {
Meta
}

var Builders = map[string]packer.Builder{
"amazon-chroot": new(amazonchrootbuilder.Builder),
"amazon-ebs": new(amazonebsbuilder.Builder),
"amazon-instance": new(amazoninstancebuilder.Builder),
"digitalocean": new(digitaloceanbuilder.Builder),
"docker": new(dockerbuilder.Builder),
"file": new(filebuilder.Builder),
"googlecompute": new(googlecomputebuilder.Builder),
"null": new(nullbuilder.Builder),
"openstack": new(openstackbuilder.Builder),
"parallels-iso": new(parallelsisobuilder.Builder),
"parallels-pvm": new(parallelspvmbuilder.Builder),
"qemu": new(qemubuilder.Builder),
"virtualbox-iso": new(virtualboxisobuilder.Builder),
"virtualbox-ovf": new(virtualboxovfbuilder.Builder),
"vmware-iso": new(vmwareisobuilder.Builder),
"vmware-vmx": new(vmwarevmxbuilder.Builder),
}


var Provisioners = map[string]packer.Provisioner{
"ansible-local": new(ansiblelocalprovisioner.Provisioner),
"chef-client": new(chefclientprovisioner.Provisioner),
"chef-solo": new(chefsoloprovisioner.Provisioner),
"file": new(fileprovisioner.Provisioner),
"powershell": new(powershellprovisioner.Provisioner),
"puppet-masterless": new(puppetmasterlessprovisioner.Provisioner),
"puppet-server": new(puppetserverprovisioner.Provisioner),
"salt-masterless": new(saltmasterlessprovisioner.Provisioner),
"shell": new(shellprovisioner.Provisioner),
"shell-local": new(shelllocalprovisioner.Provisioner),
"windows-restart": new(windowsrestartprovisioner.Provisioner),
"windows-shell": new(windowsshellprovisioner.Provisioner),
}


var PostProcessors = map[string]packer.PostProcessor{
"artifice": new(artificepostprocessor.PostProcessor),
"atlas": new(atlaspostprocessor.PostProcessor),
"compress": new(compresspostprocessor.PostProcessor),
"docker-import": new(dockerimportpostprocessor.PostProcessor),
"docker-push": new(dockerpushpostprocessor.PostProcessor),
"docker-save": new(dockersavepostprocessor.PostProcessor),
"docker-tag": new(dockertagpostprocessor.PostProcessor),
"vagrant": new(vagrantpostprocessor.PostProcessor),
"vagrant-cloud": new(vagrantcloudpostprocessor.PostProcessor),
"vsphere": new(vspherepostprocessor.PostProcessor),
}


var pluginRegexp = regexp.MustCompile("packer-(builder|post-processor|provisioner)-(.+)")

func (c *PluginCommand) Run(args []string) int {
// This is an internal call (users should not call this directly) so we're
// not going to do much input validation. If there's a problem we'll often
// just crash. Error handling should be added to facilitate debugging.
log.Printf("args: %#v", args)
if len(args) != 1 {
c.Ui.Error("Wrong number of args")
return 1
}

// Plugin will match something like "packer-builder-amazon-ebs"
parts := pluginRegexp.FindStringSubmatch(args[0])
if len(parts) != 3 {
c.Ui.Error(fmt.Sprintf("Error parsing plugin argument [DEBUG]: %#v", parts))
return 1
}
pluginType := parts[1] // capture group 1 (builder|post-processor|provisioner)
pluginName := parts[2] // capture group 2 (.+)

server, err := plugin.Server()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error starting plugin server: %s", err))
return 1
}

switch pluginType {
case "builder":
builder, found := Builders[pluginName]
if !found {
c.Ui.Error(fmt.Sprintf("Could not load builder: %s", pluginName))
return 1
}
server.RegisterBuilder(builder)
case "provisioner":
provisioner, found := Provisioners[pluginName]
if !found {
c.Ui.Error(fmt.Sprintf("Could not load provisioner: %s", pluginName))
return 1
}
server.RegisterProvisioner(provisioner)
case "post-processor":
postProcessor, found := PostProcessors[pluginName]
if !found {
c.Ui.Error(fmt.Sprintf("Could not load post-processor: %s", pluginName))
return 1
}
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: this is an internal command and you should not call it yourself.
`

return strings.TrimSpace(helpText)
}

func (c *PluginCommand) Synopsis() string {
return "internal plugin command"
}
2 changes: 2 additions & 0 deletions command/version.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package command

//go:generate go run ../scripts/generate-plugins.go

import (
"bytes"
"fmt"
Expand Down
6 changes: 6 additions & 0 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ func init() {
CheckFunc: commandVersionCheck,
}, nil
},

"plugin": func() (cli.Command, error) {
return &command.PluginCommand{
Meta: *CommandMeta,
}, nil
},
}
}

Expand Down
64 changes: 62 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"encoding/json"
"fmt"
"io"
"log"
"os/exec"
Expand All @@ -10,10 +11,15 @@ import (
"strings"

"github.com/mitchellh/osext"
"github.com/mitchellh/packer/command"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/packer/plugin"
)

// PACKERSPACE is used to represent the spaces that separate args for a command
// without being confused with spaces in the path to the command itself.
const PACKERSPACE = "-PACKERSPACE-"

type config struct {
DisableCheckpoint bool `json:"disable_checkpoint"`
DisableCheckpointSignature bool `json:"disable_checkpoint_signature"`
Expand Down Expand Up @@ -73,11 +79,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
}

Expand Down Expand Up @@ -196,6 +208,46 @@ 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%splugin%spacker-builder-%s",
packerPath, PACKERSPACE, PACKERSPACE, 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%splugin%spacker-provisioner-%s",
packerPath, PACKERSPACE, PACKERSPACE, 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%splugin%spacker-post-processor-%s",
packerPath, PACKERSPACE, PACKERSPACE, postProcessor)
}
}

return nil
}

func (c *config) pluginClient(path string) *plugin.Client {
originalPath := path

Expand All @@ -214,6 +266,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 == "" {
Expand All @@ -222,7 +282,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
Expand Down
23 changes: 22 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func wrappedMain() int {
cli := &cli.CLI{
Args: args,
Commands: Commands,
HelpFunc: cli.BasicHelpFunc("packer"),
HelpFunc: excludeHelpFunc(Commands, []string{"plugin"}),
HelpWriter: os.Stdout,
Version: Version,
}
Expand All @@ -195,6 +195,27 @@ func wrappedMain() int {
return exitCode
}

// excludeHelpFunc filters commands we don't want to show from the list of
// commands displayed in packer's help text.
func excludeHelpFunc(commands map[string]cli.CommandFactory, exclude []string) cli.HelpFunc {
// Make search slice into a map so we can use use the `if found` idiom
// instead of a nested loop.
var excludes = make(map[string]interface{}, len(exclude))
for _, item := range exclude {
excludes[item] = nil
}

// Create filtered list of commands
helpCommands := []string{}
for command := range commands {
if _, found := excludes[command]; !found {
helpCommands = append(helpCommands, command)
}
}

return cli.FilteredHelpFunc(helpCommands, cli.BasicHelpFunc("packer"))
}

// extractMachineReadable checks the args for the machine readable
// flag and returns whether or not it is on. It modifies the args
// to remove this flag.
Expand Down
27 changes: 27 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,36 @@ package main
import (
"math/rand"
"reflect"
"strings"
"testing"

"github.com/mitchellh/cli"
"github.com/mitchellh/packer/command"
)

func TestExcludeHelpFunc(t *testing.T) {
commands := map[string]cli.CommandFactory{
"build": func() (cli.Command, error) {
return &command.BuildCommand{
Meta: command.Meta{},
}, nil
},

"fix": func() (cli.Command, error) {
return &command.FixCommand{
Meta: command.Meta{},
}, nil
},
}

helpFunc := excludeHelpFunc(commands, []string{"fix"})
helpText := helpFunc(commands)

if strings.Contains(helpText, "fix") {
t.Fatalf("Found fix in help text even though we excluded it: \n\n%s\n\n", helpText)
}
}

func TestExtractMachineReadable(t *testing.T) {
var args, expected, result []string
var mr bool
Expand Down
Loading

0 comments on commit 8e63ce1

Please sign in to comment.