github.com/masahide/goansible@v0.0.0-20160116054156-01eac649e9f2/goansible.go (about)

     1  package goansible
     2  
     3  /*
     4  import (
     5  	"bufio"
     6  	"bytes"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  )
    15  
    16  var cUpdateScript = []byte(`#!/bin/bash
    17  
    18  cd .goansible
    19  
    20  REL=$TACHYON_RELEASE
    21  BIN=goansible-$TACHYON_OS-$TACHYON_ARCH
    22  
    23  if test -f goansible; then
    24    CUR=$(< release)
    25    if test "$REL" != "$CUR"; then
    26      echo "Detected goansible of old release ($CUR), removing."
    27      rm goansible
    28    fi
    29  fi
    30  
    31  if which curl > /dev/null; then
    32    DL="curl -O"
    33  elif which wget > /dev/null; then
    34    DL="wget"
    35  else
    36    echo "No curl or wget, unable to pull a release"
    37    exit 1
    38  fi
    39  
    40  if ! test -f goansible; then
    41    echo "Downloading $REL/$BIN..."
    42  
    43    $DL https://s3-us-west-2.amazonaws.com/goansible.vektra.io/$REL/sums
    44    if which gpg > /dev/null; then
    45      gpg --keyserver keys.gnupg.net --recv-key A408199F &
    46      $DL https://s3-us-west-2.amazonaws.com/goansible.vektra.io/$REL/sums.asc &
    47    fi
    48  
    49    $DL https://s3-us-west-2.amazonaws.com/goansible.vektra.io/$REL/$BIN
    50  
    51    wait
    52  
    53    if which gpg > /dev/null; then
    54      if ! gpg --verify sums.asc; then
    55        echo "Signature verification failed! Aborting!"
    56        exit 1
    57      fi
    58    fi
    59  
    60    mv $BIN $BIN.gz
    61  
    62    # If gunzip fails, it's because the file isn't gzip'd, so we
    63    # assume it's already in the correct format.
    64    if ! gunzip $BIN.gz; then
    65      mv $BIN.gz $BIN
    66    fi
    67  
    68    if which shasum > /dev/null; then
    69      if ! (grep $BIN sums | shasum -c); then
    70        echo "Sum verification failed!"
    71        exit 1
    72      fi
    73    else
    74      echo "No shasum available to verify files"
    75    fi
    76  
    77    echo $REL > release
    78  
    79    chmod a+x $BIN
    80    ln -s $BIN goansible
    81  fi
    82  `)
    83  
    84  func normalizeArch(arch string) string {
    85  	switch arch {
    86  	case "x86_64":
    87  		return "amd64"
    88  	default:
    89  		return arch
    90  	}
    91  }
    92  
    93  type Tachyon struct {
    94  	Target      string `goansible:"target"`
    95  	Debug       bool   `goansible:"debug"`
    96  	Output      bool   `goansible:"output"`
    97  	Clean       bool   `goansible:"clean"`
    98  	Dev         bool   `goansible:"dev"`
    99  	Playbook    string `goansible:"playbook"`
   100  	Release     string `goansible:"release"`
   101  	NoJSON      bool   `goansible:"no_json"`
   102  	InstallOnly bool   `goansible:"install_only"`
   103  }
   104  
   105  func (t *Tachyon) ShowOutput() bool { return t.Output }
   106  func (t *Tachyon) ShowDebug() bool  { return t.Debug }
   107  
   108  func (t *Tachyon) Run(env *CommandEnv) (*Result, error) {
   109  	if t.Release == "" {
   110  		t.Release = Release
   111  	}
   112  
   113  	ssh := NewSSH(t.Target)
   114  	ssh.Debug = t.Debug
   115  
   116  	defer ssh.Cleanup()
   117  
   118  	// err := ssh.Start()
   119  	// if err != nil {
   120  	// return nil, fmt.Errorf("Error starting persistent SSH connection: %s\n", err)
   121  	// }
   122  
   123  	var bootstrap string
   124  
   125  	if t.Clean {
   126  		bootstrap = "rm -rf .goansible && mkdir -p .goansible"
   127  	} else {
   128  		bootstrap = "mkdir -p .goansible"
   129  	}
   130  
   131  	out, err := ssh.RunAndCapture(bootstrap + " && uname && uname -m")
   132  	if err != nil {
   133  		return nil, fmt.Errorf("Error creating remote .goansible dir: %s (%s)", err, string(out))
   134  	}
   135  
   136  	tos, arch, ok := split2(string(out), "\n")
   137  	if !ok {
   138  		return nil, fmt.Errorf("Unable to figure out os and arch of remote machine\n")
   139  	}
   140  
   141  	tos = strings.ToLower(tos)
   142  	arch = normalizeArch(strings.TrimSpace(arch))
   143  
   144  	binary := fmt.Sprintf("goansible-%s-%s", tos, arch)
   145  
   146  	if t.Dev {
   147  		env.Progress("Copying development goansible...")
   148  
   149  		path := filepath.Dir(Arg0)
   150  
   151  		err = ssh.CopyToHost(filepath.Join(path, binary), ".goansible/"+binary+".new")
   152  		if err != nil {
   153  			return nil, fmt.Errorf("Error copying goansible to vagrant: %s\n", err)
   154  		}
   155  
   156  		ssh.Run(fmt.Sprintf("cd .goansible && mv %[1]s.new %[1]s && ln -fs %[1]s goansible", binary))
   157  	} else {
   158  		env.Progress("Updating goansible release...")
   159  
   160  		c := ssh.Command("cat > .goansible/update && chmod a+x .goansible/update")
   161  
   162  		c.Stdout = os.Stdout
   163  		c.Stdin = bytes.NewReader(cUpdateScript)
   164  		err = c.Run()
   165  		if err != nil {
   166  			return nil, fmt.Errorf("Error updating, well, the updater: %s\n", err)
   167  		}
   168  
   169  		cmd := fmt.Sprintf("TACHYON_RELEASE=%s TACHYON_OS=%s TACHYON_ARCH=%s ./.goansible/update", t.Release, tos, arch)
   170  		err = ssh.Run(cmd)
   171  		if err != nil {
   172  			return nil, fmt.Errorf("Error running updater: %s", err)
   173  		}
   174  	}
   175  
   176  	if t.InstallOnly {
   177  		res := NewResult(true)
   178  		res.Add("target", t.Target)
   179  		res.Add("install_only", true)
   180  
   181  		return res, nil
   182  	}
   183  
   184  	var src string
   185  
   186  	var main string
   187  
   188  	fi, err := os.Stat(t.Playbook)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	if fi.IsDir() {
   194  		src, err = filepath.Abs(t.Playbook)
   195  		if err != nil {
   196  			return nil, fmt.Errorf("Unable to resolve %s: %s", t.Playbook, err)
   197  		}
   198  		main = "site.yml"
   199  	} else {
   200  		abs, err := filepath.Abs(t.Playbook)
   201  		if err != nil {
   202  			return nil, fmt.Errorf("Unable to resolve %s: %s", t.Playbook, err)
   203  		}
   204  
   205  		main = filepath.Base(abs)
   206  		src = filepath.Dir(abs)
   207  	}
   208  
   209  	src += "/"
   210  
   211  	env.Progress("Syncing playbook...")
   212  
   213  	c := exec.Command("rsync", "-av", "-e", ssh.RsyncCommand(), src, ssh.Host+":.goansible/playbook")
   214  
   215  	if t.Debug {
   216  		c.Stdout = os.Stdout
   217  	}
   218  
   219  	err = c.Run()
   220  
   221  	if err != nil {
   222  		return nil, fmt.Errorf("Error copying playbook to vagrant: %s\n", err)
   223  	}
   224  
   225  	env.Progress("Running playbook...")
   226  
   227  	var format string
   228  
   229  	if !t.NoJSON {
   230  		format = "--json"
   231  	}
   232  
   233  	startCmd := fmt.Sprintf("cd .goansible && sudo ./goansible %s playbook/%s", format, main)
   234  
   235  	c = ssh.Command(startCmd)
   236  
   237  	if t.Debug {
   238  		fmt.Fprintf(os.Stderr, "Run: %#v\n", c.Args)
   239  	}
   240  
   241  	stream, err := c.StdoutPipe()
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	c.Stderr = os.Stderr
   247  
   248  	c.Start()
   249  
   250  	input := bufio.NewReader(stream)
   251  
   252  	for {
   253  		str, err := input.ReadString('\n')
   254  		if err != nil {
   255  			break
   256  		}
   257  
   258  		sz, err := strconv.Atoi(strings.TrimSpace(str))
   259  		if err != nil {
   260  			break
   261  		}
   262  
   263  		data := make([]byte, sz)
   264  
   265  		_, err = input.Read(data)
   266  		if err != nil {
   267  			break
   268  		}
   269  
   270  		_, err = input.ReadByte()
   271  		if err != nil {
   272  			break
   273  		}
   274  
   275  		env.progress.JSONProgress(data)
   276  	}
   277  
   278  	if err != nil {
   279  		if err != io.EOF {
   280  			fmt.Printf("error: %s\n", err)
   281  		}
   282  	}
   283  
   284  	err = c.Wait()
   285  	if err != nil {
   286  		return nil, fmt.Errorf("Error running playbook remotely: %s", err)
   287  	}
   288  
   289  	res := NewResult(true)
   290  	res.Add("target", t.Target)
   291  	res.Add("playbook", t.Playbook)
   292  
   293  	return res, nil
   294  }
   295  
   296  func init() {
   297  	RegisterCommand("goansible", &Tachyon{})
   298  }
   299  */