github.com/daniellockard/packer@v0.7.6-0.20141210173435-5a9390934716/config.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"log"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/mitchellh/osext"
    12  	"github.com/mitchellh/packer/packer"
    13  	"github.com/mitchellh/packer/packer/plugin"
    14  )
    15  
    16  // EnvConfig is the global EnvironmentConfig we use to initialize the CLI.
    17  var EnvConfig packer.EnvironmentConfig
    18  
    19  type config struct {
    20  	DisableCheckpoint          bool `json:"disable_checkpoint"`
    21  	DisableCheckpointSignature bool `json:"disable_checkpoint_signature"`
    22  	PluginMinPort              uint
    23  	PluginMaxPort              uint
    24  
    25  	Builders       map[string]string
    26  	PostProcessors map[string]string `json:"post-processors"`
    27  	Provisioners   map[string]string
    28  }
    29  
    30  // ConfigFile returns the default path to the configuration file. On
    31  // Unix-like systems this is the ".packerconfig" file in the home directory.
    32  // On Windows, this is the "packer.config" file in the application data
    33  // directory.
    34  func ConfigFile() (string, error) {
    35  	return configFile()
    36  }
    37  
    38  // ConfigDir returns the configuration directory for Packer.
    39  func ConfigDir() (string, error) {
    40  	return configDir()
    41  }
    42  
    43  // Decodes configuration in JSON format from the given io.Reader into
    44  // the config object pointed to.
    45  func decodeConfig(r io.Reader, c *config) error {
    46  	decoder := json.NewDecoder(r)
    47  	return decoder.Decode(c)
    48  }
    49  
    50  // Discover discovers plugins.
    51  //
    52  // This looks in the directory of the executable and the CWD, in that
    53  // order for priority.
    54  func (c *config) Discover() error {
    55  	// Next, look in the same directory as the executable. Any conflicts
    56  	// will overwrite those found in our current directory.
    57  	exePath, err := osext.Executable()
    58  	if err != nil {
    59  		log.Printf("[ERR] Error loading exe directory: %s", err)
    60  	} else {
    61  		if err := c.discover(filepath.Dir(exePath)); err != nil {
    62  			return err
    63  		}
    64  	}
    65  
    66  	// Look in the plugins directory
    67  	dir, err := ConfigDir()
    68  	if err != nil {
    69  		log.Printf("[ERR] Error loading config directory: %s", err)
    70  	} else {
    71  		if err := c.discover(filepath.Join(dir, "plugins")); err != nil {
    72  			return err
    73  		}
    74  	}
    75  
    76  	// Look in the cwd.
    77  	if err := c.discover("."); err != nil {
    78  		return err
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // This is a proper packer.BuilderFunc that can be used to load packer.Builder
    85  // implementations from the defined plugins.
    86  func (c *config) LoadBuilder(name string) (packer.Builder, error) {
    87  	log.Printf("Loading builder: %s\n", name)
    88  	bin, ok := c.Builders[name]
    89  	if !ok {
    90  		log.Printf("Builder not found: %s\n", name)
    91  		return nil, nil
    92  	}
    93  
    94  	return c.pluginClient(bin).Builder()
    95  }
    96  
    97  // This is a proper implementation of packer.HookFunc that can be used
    98  // to load packer.Hook implementations from the defined plugins.
    99  func (c *config) LoadHook(name string) (packer.Hook, error) {
   100  	log.Printf("Loading hook: %s\n", name)
   101  	return c.pluginClient(name).Hook()
   102  }
   103  
   104  // This is a proper packer.PostProcessorFunc that can be used to load
   105  // packer.PostProcessor implementations from defined plugins.
   106  func (c *config) LoadPostProcessor(name string) (packer.PostProcessor, error) {
   107  	log.Printf("Loading post-processor: %s", name)
   108  	bin, ok := c.PostProcessors[name]
   109  	if !ok {
   110  		log.Printf("Post-processor not found: %s", name)
   111  		return nil, nil
   112  	}
   113  
   114  	return c.pluginClient(bin).PostProcessor()
   115  }
   116  
   117  // This is a proper packer.ProvisionerFunc that can be used to load
   118  // packer.Provisioner implementations from defined plugins.
   119  func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) {
   120  	log.Printf("Loading provisioner: %s\n", name)
   121  	bin, ok := c.Provisioners[name]
   122  	if !ok {
   123  		log.Printf("Provisioner not found: %s\n", name)
   124  		return nil, nil
   125  	}
   126  
   127  	return c.pluginClient(bin).Provisioner()
   128  }
   129  
   130  func (c *config) discover(path string) error {
   131  	var err error
   132  
   133  	if !filepath.IsAbs(path) {
   134  		path, err = filepath.Abs(path)
   135  		if err != nil {
   136  			return err
   137  		}
   138  	}
   139  
   140  	err = c.discoverSingle(
   141  		filepath.Join(path, "packer-builder-*"), &c.Builders)
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	err = c.discoverSingle(
   147  		filepath.Join(path, "packer-post-processor-*"), &c.PostProcessors)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	err = c.discoverSingle(
   153  		filepath.Join(path, "packer-provisioner-*"), &c.Provisioners)
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	return nil
   159  }
   160  
   161  func (c *config) discoverSingle(glob string, m *map[string]string) error {
   162  	matches, err := filepath.Glob(glob)
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	if *m == nil {
   168  		*m = make(map[string]string)
   169  	}
   170  
   171  	prefix := filepath.Base(glob)
   172  	prefix = prefix[:strings.Index(prefix, "*")]
   173  	for _, match := range matches {
   174  		file := filepath.Base(match)
   175  
   176  		// If the filename has a ".", trim up to there
   177  		if idx := strings.Index(file, "."); idx >= 0 {
   178  			file = file[:idx]
   179  		}
   180  
   181  		// Look for foo-bar-baz. The plugin name is "baz"
   182  		plugin := file[len(prefix):]
   183  		log.Printf("[DEBUG] Discoverd plugin: %s = %s", plugin, match)
   184  		(*m)[plugin] = match
   185  	}
   186  
   187  	return nil
   188  }
   189  
   190  func (c *config) pluginClient(path string) *plugin.Client {
   191  	originalPath := path
   192  
   193  	// First attempt to find the executable by consulting the PATH.
   194  	path, err := exec.LookPath(path)
   195  	if err != nil {
   196  		// If that doesn't work, look for it in the same directory
   197  		// as the `packer` executable (us).
   198  		log.Printf("Plugin could not be found. Checking same directory as executable.")
   199  		exePath, err := osext.Executable()
   200  		if err != nil {
   201  			log.Printf("Couldn't get current exe path: %s", err)
   202  		} else {
   203  			log.Printf("Current exe path: %s", exePath)
   204  			path = filepath.Join(filepath.Dir(exePath), filepath.Base(originalPath))
   205  		}
   206  	}
   207  
   208  	// If everything failed, just use the original path and let the error
   209  	// bubble through.
   210  	if path == "" {
   211  		path = originalPath
   212  	}
   213  
   214  	log.Printf("Creating plugin client for path: %s", path)
   215  	var config plugin.ClientConfig
   216  	config.Cmd = exec.Command(path)
   217  	config.Managed = true
   218  	config.MinPort = c.PluginMinPort
   219  	config.MaxPort = c.PluginMaxPort
   220  	return plugin.NewClient(&config)
   221  }