github.com/phobos182/packer@v0.2.3-0.20130819023704-c84d2aeffc68/provisioner/salt-masterless/provisioner.go (about)

     1  // This package implements a provisioner for Packer that executes a
     2  // saltstack highstate within the remote machine
     3  package saltmasterless
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"github.com/mitchellh/packer/common"
     9  	"github.com/mitchellh/packer/packer"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  )
    14  
    15  const DefaultTempConfigDir = "/tmp/salt"
    16  
    17  type Config struct {
    18  	common.PackerConfig `mapstructure:",squash"`
    19  
    20  	// If true, run the salt-bootstrap script
    21  	SkipBootstrap bool   `mapstructure:"skip_bootstrap"`
    22  	BootstrapArgs string `mapstructure:"bootstrap_args"`
    23  
    24  	// Local path to the salt state tree
    25  	LocalStateTree string `mapstructure:"local_state_tree"`
    26  
    27  	// Where files will be copied before moving to the /srv/salt directory
    28  	TempConfigDir string `mapstructure:"temp_config_dir"`
    29  
    30  	tpl *packer.ConfigTemplate
    31  }
    32  
    33  type Provisioner struct {
    34  	config Config
    35  }
    36  
    37  func (p *Provisioner) Prepare(raws ...interface{}) error {
    38  	md, err := common.DecodeConfig(&p.config, raws...)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	p.config.tpl, err = packer.NewConfigTemplate()
    44  	if err != nil {
    45  		return err
    46  	}
    47  	p.config.tpl.UserVars = p.config.PackerUserVars
    48  
    49  	if p.config.TempConfigDir == "" {
    50  		p.config.TempConfigDir = DefaultTempConfigDir
    51  	}
    52  
    53  	// Accumulate any errors
    54  	errs := common.CheckUnusedConfig(md)
    55  
    56  	templates := map[string]*string{
    57  		"bootstrap_args":   &p.config.BootstrapArgs,
    58  		"local_state_tree": &p.config.LocalStateTree,
    59  		"temp_config_dir":  &p.config.TempConfigDir,
    60  	}
    61  
    62  	for n, ptr := range templates {
    63  		var err error
    64  		*ptr, err = p.config.tpl.Process(*ptr, nil)
    65  		if err != nil {
    66  			errs = packer.MultiErrorAppend(
    67  				errs, fmt.Errorf("Error processing %s: %s", n, err))
    68  		}
    69  	}
    70  
    71  	if p.config.LocalStateTree != "" {
    72  		if _, err := os.Stat(p.config.LocalStateTree); err != nil {
    73  			errs = packer.MultiErrorAppend(errs,
    74  				errors.New("local_state_tree must exist and be accessible"))
    75  		}
    76  	}
    77  
    78  	if errs != nil && len(errs.Errors) > 0 {
    79  		return errs
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
    86  	var err error
    87  
    88  	ui.Say("Provisioning with Salt...")
    89  	if !p.config.SkipBootstrap {
    90  		cmd := &packer.RemoteCmd{
    91  			Command: fmt.Sprintf("wget -O - http://bootstrap.saltstack.org | sudo sh -s %s", p.config.BootstrapArgs),
    92  		}
    93  		ui.Message(fmt.Sprintf("Installing Salt with command %s", cmd))
    94  		if err = cmd.StartWithUi(comm, ui); err != nil {
    95  			return fmt.Errorf("Unable to install Salt: %d", err)
    96  		}
    97  	}
    98  
    99  	ui.Message(fmt.Sprintf("Creating remote directory: %s", p.config.TempConfigDir))
   100  	cmd := &packer.RemoteCmd{Command: fmt.Sprintf("mkdir -p %s", p.config.TempConfigDir)}
   101  	if err = cmd.StartWithUi(comm, ui); err != nil || cmd.ExitStatus != 0 {
   102  		if err == nil {
   103  			err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus)
   104  		}
   105  
   106  		return fmt.Errorf("Error creating remote salt state directory: %s", err)
   107  	}
   108  
   109  	ui.Message(fmt.Sprintf("Uploading local state tree: %s", p.config.LocalStateTree))
   110  	if err = UploadLocalDirectory(p.config.LocalStateTree, p.config.TempConfigDir, comm, ui); err != nil {
   111  		return fmt.Errorf("Error uploading local state tree to remote: %s", err)
   112  	}
   113  
   114  	ui.Message(fmt.Sprintf("Moving %s to /srv/salt", p.config.TempConfigDir))
   115  	cmd = &packer.RemoteCmd{Command: fmt.Sprintf("sudo mv %s /srv/salt", p.config.TempConfigDir)}
   116  	if err = cmd.StartWithUi(comm, ui); err != nil || cmd.ExitStatus != 0 {
   117  		if err == nil {
   118  			err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus)
   119  		}
   120  
   121  		return fmt.Errorf("Unable to move %s to /srv/salt: %d", p.config.TempConfigDir, err)
   122  	}
   123  
   124  	ui.Message("Running highstate")
   125  	cmd = &packer.RemoteCmd{Command: "sudo salt-call --local state.highstate -l info"}
   126  	if err = cmd.StartWithUi(comm, ui); err != nil || cmd.ExitStatus != 0 {
   127  		if err == nil {
   128  			err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus)
   129  		}
   130  
   131  		return fmt.Errorf("Error executing highstate: %s", err)
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communicator, ui packer.Ui) (err error) {
   138  	visitPath := func(localPath string, f os.FileInfo, err error) (err2 error) {
   139  		localRelPath := strings.Replace(localPath, localDir, "", 1)
   140  		localRelPath = strings.Replace(localRelPath, "\\", "/", -1)
   141  		remotePath := fmt.Sprintf("%s%s", remoteDir, localRelPath)
   142  		if f.IsDir() && f.Name() == ".git" {
   143  			return filepath.SkipDir
   144  		}
   145  		if f.IsDir() {
   146  			// Make remote directory
   147  			cmd := &packer.RemoteCmd{Command: fmt.Sprintf("mkdir -p %s", remotePath)}
   148  			if err = cmd.StartWithUi(comm, ui); err != nil {
   149  				return err
   150  			}
   151  		} else {
   152  			// Upload file to existing directory
   153  			file, err := os.Open(localPath)
   154  			if err != nil {
   155  				return fmt.Errorf("Error opening file: %s", err)
   156  			}
   157  			defer file.Close()
   158  
   159  			ui.Message(fmt.Sprintf("Uploading file %s: %s", localPath, remotePath))
   160  			if err = comm.Upload(remotePath, file); err != nil {
   161  				return fmt.Errorf("Error uploading file: %s", err)
   162  			}
   163  		}
   164  		return
   165  	}
   166  
   167  	err = filepath.Walk(localDir, visitPath)
   168  	if err != nil {
   169  		return fmt.Errorf("Error uploading local directory %s: %s", localDir, err)
   170  	}
   171  
   172  	return nil
   173  }