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