github.com/jbronn/packer@v0.1.6-0.20140120165540-8a1364dbd817/config.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"github.com/mitchellh/osext"
     6  	"github.com/mitchellh/packer/packer"
     7  	"github.com/mitchellh/packer/packer/plugin"
     8  	"io"
     9  	"log"
    10  	"os/exec"
    11  	"path/filepath"
    12  )
    13  
    14  // This is the default, built-in configuration that ships with
    15  // Packer.
    16  const defaultConfig = `
    17  {
    18  	"plugin_min_port": 10000,
    19  	"plugin_max_port": 25000,
    20  
    21  	"builders": {
    22  		"amazon-ebs": "packer-builder-amazon-ebs",
    23  		"amazon-chroot": "packer-builder-amazon-chroot",
    24  		"amazon-instance": "packer-builder-amazon-instance",
    25  		"digitalocean": "packer-builder-digitalocean",
    26  		"docker": "packer-builder-docker",
    27  		"googlecompute": "packer-builder-googlecompute",
    28  		"openstack": "packer-builder-openstack",
    29  		"qemu": "packer-builder-qemu",
    30  		"virtualbox-iso": "packer-builder-virtualbox-iso",
    31  		"virtualbox-ovf": "packer-builder-virtualbox-ovf",
    32  		"vmware-iso": "packer-builder-vmware-iso",
    33  		"vmware-vmx": "packer-builder-vmware-vmx"
    34  	},
    35  
    36  	"commands": {
    37  		"build": "packer-command-build",
    38  		"fix": "packer-command-fix",
    39  		"inspect": "packer-command-inspect",
    40  		"validate": "packer-command-validate"
    41  	},
    42  
    43  	"post-processors": {
    44  		"vagrant": "packer-post-processor-vagrant",
    45  		"vsphere": "packer-post-processor-vsphere",
    46      "docker-push": "packer-post-processor-docker-push",
    47      "docker-import": "packer-post-processor-docker-import"
    48  	},
    49  
    50  	"provisioners": {
    51  		"ansible-local": "packer-provisioner-ansible-local",
    52  		"chef-solo": "packer-provisioner-chef-solo",
    53  		"file": "packer-provisioner-file",
    54  		"puppet-masterless": "packer-provisioner-puppet-masterless",
    55  		"shell": "packer-provisioner-shell",
    56  		"salt-masterless": "packer-provisioner-salt-masterless"
    57  	}
    58  }
    59  `
    60  
    61  type config struct {
    62  	PluginMinPort uint
    63  	PluginMaxPort uint
    64  
    65  	Builders       map[string]string
    66  	Commands       map[string]string
    67  	PostProcessors map[string]string `json:"post-processors"`
    68  	Provisioners   map[string]string
    69  }
    70  
    71  // Decodes configuration in JSON format from the given io.Reader into
    72  // the config object pointed to.
    73  func decodeConfig(r io.Reader, c *config) error {
    74  	decoder := json.NewDecoder(r)
    75  	return decoder.Decode(c)
    76  }
    77  
    78  // Returns an array of defined command names.
    79  func (c *config) CommandNames() (result []string) {
    80  	result = make([]string, 0, len(c.Commands))
    81  	for name := range c.Commands {
    82  		result = append(result, name)
    83  	}
    84  	return
    85  }
    86  
    87  // This is a proper packer.BuilderFunc that can be used to load packer.Builder
    88  // implementations from the defined plugins.
    89  func (c *config) LoadBuilder(name string) (packer.Builder, error) {
    90  	log.Printf("Loading builder: %s\n", name)
    91  	bin, ok := c.Builders[name]
    92  	if !ok {
    93  		log.Printf("Builder not found: %s\n", name)
    94  		return nil, nil
    95  	}
    96  
    97  	return c.pluginClient(bin).Builder()
    98  }
    99  
   100  // This is a proper packer.CommandFunc that can be used to load packer.Command
   101  // implementations from the defined plugins.
   102  func (c *config) LoadCommand(name string) (packer.Command, error) {
   103  	log.Printf("Loading command: %s\n", name)
   104  	bin, ok := c.Commands[name]
   105  	if !ok {
   106  		log.Printf("Command not found: %s\n", name)
   107  		return nil, nil
   108  	}
   109  
   110  	return c.pluginClient(bin).Command()
   111  }
   112  
   113  // This is a proper implementation of packer.HookFunc that can be used
   114  // to load packer.Hook implementations from the defined plugins.
   115  func (c *config) LoadHook(name string) (packer.Hook, error) {
   116  	log.Printf("Loading hook: %s\n", name)
   117  	return c.pluginClient(name).Hook()
   118  }
   119  
   120  // This is a proper packer.PostProcessorFunc that can be used to load
   121  // packer.PostProcessor implementations from defined plugins.
   122  func (c *config) LoadPostProcessor(name string) (packer.PostProcessor, error) {
   123  	log.Printf("Loading post-processor: %s", name)
   124  	bin, ok := c.PostProcessors[name]
   125  	if !ok {
   126  		log.Printf("Post-processor not found: %s", name)
   127  		return nil, nil
   128  	}
   129  
   130  	return c.pluginClient(bin).PostProcessor()
   131  }
   132  
   133  // This is a proper packer.ProvisionerFunc that can be used to load
   134  // packer.Provisioner implementations from defined plugins.
   135  func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) {
   136  	log.Printf("Loading provisioner: %s\n", name)
   137  	bin, ok := c.Provisioners[name]
   138  	if !ok {
   139  		log.Printf("Provisioner not found: %s\n", name)
   140  		return nil, nil
   141  	}
   142  
   143  	return c.pluginClient(bin).Provisioner()
   144  }
   145  
   146  func (c *config) pluginClient(path string) *plugin.Client {
   147  	originalPath := path
   148  
   149  	// First attempt to find the executable by consulting the PATH.
   150  	path, err := exec.LookPath(path)
   151  	if err != nil {
   152  		// If that doesn't work, look for it in the same directory
   153  		// as the `packer` executable (us).
   154  		log.Printf("Plugin could not be found. Checking same directory as executable.")
   155  		exePath, err := osext.Executable()
   156  		if err != nil {
   157  			log.Printf("Couldn't get current exe path: %s", err)
   158  		} else {
   159  			log.Printf("Current exe path: %s", exePath)
   160  			path = filepath.Join(filepath.Dir(exePath), filepath.Base(originalPath))
   161  		}
   162  	}
   163  
   164  	// If everything failed, just use the original path and let the error
   165  	// bubble through.
   166  	if path == "" {
   167  		path = originalPath
   168  	}
   169  
   170  	log.Printf("Creating plugin client for path: %s", path)
   171  	var config plugin.ClientConfig
   172  	config.Cmd = exec.Command(path)
   173  	config.Managed = true
   174  	config.MinPort = c.PluginMinPort
   175  	config.MaxPort = c.PluginMaxPort
   176  	return plugin.NewClient(&config)
   177  }