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