<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

const VERSIONS_PREFIX = ".sys.v#."

var GLOBAL_FLAGS *cliFlags

type cliFlags struct {
	dbUsername string
	dbPassword string
	dbHost     string
	dbPort     int
	dbName     string
	noTouchDb  bool
	eosMGMURL  string
	debug      bool
	userPrefix string
	user       string
}

func parseFlags() *cliFlags {
	flags := &cliFlags{}
	flag.StringVar(&flags.dbUsername, "username", "", "The username to connect to the db")
	flag.StringVar(&flags.dbPassword, "password", "", "The password to connect to the db")
	flag.StringVar(&flags.dbHost, "host", "", "The host of the db")
	flag.IntVar(&flags.dbPort, "port", 0, "The port of the db")
	flag.StringVar(&flags.dbName, "dbname", "", "The name of the database")
	flag.BoolVar(&flags.noTouchDb, "notouchdb", false, "With dry run enbaled the changes are not commited to the db")
	flag.StringVar(&flags.eosMGMURL, "eosmgmurl", "root://eospps-slave.cern.ch", "The EOS MGM URL")
	flag.StringVar(&flags.userPrefix, "userprefix", "/eos/scratch/user/", "The path under users reside")
	flag.StringVar(&flags.user, "user", "ourense", "Run the migration just for this user")
	flag.BoolVar(&flags.debug, "debug", false, "Print debug information")
	flag.Parse()
	GLOBAL_FLAGS = flags
	return flags
}

type shareInfo struct {
	ID          int64          `db:"id"`
	ShareType   int            `db:"share_type"`
	ShareWith   sql.NullString `db:"share_with"`
	UIDOwner    string         `db:"uid_owner"`
	Parent      sql.NullInt64  `db:"parent"`
	ItemType    sql.NullString `db:"item_type"`
	ItemSource  sql.NullString `db:"item_source"`
	ItemTarget  sql.NullString `db:"item_target"`
	FileSource  sql.NullInt64  `db:"file_source"`
	FileTarget  sql.NullString `db:"file_target"`
	Permissions string         `db:"permissions"`
	STime       int            `db:"stime"`
	Accepted    int            `db:"accepted"`
	Expiration  time.Time      `db:"expiration"`
	Token       sql.NullString `db:"token"`
	MailSend    int            `db:"mail_send"`
}

type sqlDriver struct {
	db *sqlx.DB
}

func newSQLDriver(flags *cliFlags) (*sqlDriver, error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", flags.dbUsername, flags.dbPassword, flags.dbHost, flags.dbPort, flags.dbName)
	d, err := sqlx.Connect("mysql", dsn)
	if err != nil {
		return nil, err
	}
	return &sqlDriver{db: d}, nil
}
func (d *sqlDriver) getAllShares() ([]shareInfo, error) {
	var entries []shareInfo
	selectStmt := "SELECT id,share_type,item_source,item_target,file_source,file_target from oc_share where share_type=3 and item_type='file' ORDER BY id;"
	if GLOBAL_FLAGS.user != "" {
		selectStmt = fmt.Sprintf("SELECT id,share_type,item_source,item_target,file_source,file_target from oc_share where share_type=3 and item_type='file' and uid_owner='%s' ORDER BY id;", GLOBAL_FLAGS.user)
	}
	err := d.db.Select(&entries, selectStmt)
	if err != nil {
		return nil, err
	}
	return entries, nil
}

type Metadata struct {
	Inode int64
	Path  string
	UID   string
	GID   string
	Size  int64
}

func (d *sqlDriver) executeCMD(cmd *exec.Cmd) (string, string, error) {
	outBuf := &bytes.Buffer{}
	errBuf := &bytes.Buffer{}
	cmd.Stdout = outBuf
	cmd.Stderr = errBuf
	err := cmd.Run()
	if GLOBAL_FLAGS.debug {
		fmt.Printf("DEBUG CMD: %+v ERR:%s", cmd, err)
	}
	return outBuf.String(), errBuf.String(), err
}

// getMetadataFromEos returns the metadata fo the file/folder given the inode
func (d *sqlDriver) getMetadataFromEOS(ID int64) (*Metadata, error) {
	cmd := exec.Command("/usr/bin/eos", "-r", "0", "0", "file", "info", fmt.Sprintf("inode:%d", ID), "-m")
	stdout, _, err := d.executeCMD(cmd)
	if err != nil {
		return nil, err
	}
	return d.parseFileInfo(stdout)
}

// getMetadataFromEosPath returns the metadata fo the file/folder given the eos path
func (d *sqlDriver) getMetadataFromEOSPath(path string) (*Metadata, error) {
	cmd := exec.Command("/usr/bin/eos", "-r", "0", "0", "file", "info", path, "-m")
	stdout, _, err := d.executeCMD(cmd)
	if err != nil {
		return nil, err
	}
	return d.parseFileInfo(stdout)
}

// getVersionsFolderMetadata returns the metadata associated to the versions folder of the file with the inode passed
// if the versions folder does not exists it will try to create it.
func (d *sqlDriver) getVersionsFolderMetadata(fileMeta *Metadata) (*Metadata, error) {
	// obtain the versions folder path from the file path
	dirName := path.Dir(fileMeta.Path)
	baseName := path.Base(fileMeta.Path)
	versionsPath := path.Join(dirName, VERSIONS_PREFIX+baseName)
	versionsMeta, err := d.getMetadataFromEOSPath(versionsPath)
	if err != nil {
		// if versions folder does not exists (eos err code == 2) trigger creation of this folder
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				if status.ExitStatus() == 2 {
					err = d.createVersionsFolder(fileMeta)
					if err != nil {
						return nil, err
					}

					// if we are talking to the slave the creation of the versions folder may not has been replicated, so we retry ever
					maxRetries := 5
					err := fmt.Errorf("Version not created yet")
					var versionsMeta *Metadata

					for maxRetries > 0 && err != nil {
						_versionsMeta, _err := d.getMetadataFromEOSPath(versionsPath)
						err = _err
						versionsMeta = _versionsMeta
						maxRetries--

					}
					if err != nil {
						return nil, err
					}
					return versionsMeta, nil
				}
			}
			return nil, err
		}
		return nil, err
	}
	return versionsMeta, nil
}

func (d *sqlDriver) parseFileInfo(raw string) (*Metadata, error) {
	kv := make(map[string]string)
	partsBySpace := strings.Split(raw, " ") // we have [keylength.file=14 file=/eos/pps/proc/ container=3 ...}
	for _, p := range partsBySpace {
		partsByEqual := strings.Split(p, "=") // we have kv pairs like [ keylength.file 14]
		if len(partsByEqual) == 2 {
			kv[partsByEqual[0]] = partsByEqual[1]
		}
	}
	// fix eos path because the kv pair file=path could contains whitespace and the whitespace is the pair separator. Not very smart :(
	fileLength := kv["keylength.file"]
	fileLengthInt64, err := strconv.ParseInt(fileLength, 10, 64)
	if err != nil {
		return nil, err
	}
	startIndex := int64(14) + int64(len(fileLength)) + 7
	kv["file"] = raw[startIndex : startIndex+fileLengthInt64]

	inodeInt64, err := strconv.ParseInt(kv["ino"], 10, 64)
	if err != nil {
		return nil, err
	}
	sizeInt64, err := strconv.ParseInt(kv["size"], 10, 64)
	m := &Metadata{Inode: inodeInt64, Path: kv["file"], UID: kv["uid"], GID: kv["gid"], Size: sizeInt64}
	return m, nil
}
func (d *sqlDriver) createVersionsFolder(fileMeta *Metadata) error {
	cmd := exec.Command("/usr/bin/eos", "-r", fileMeta.UID, fileMeta.GID, "cp", fileMeta.Path, fileMeta.Path)
	_, stderr, err := d.executeCMD(cmd)
	if err != nil {
		fmt.Fprintln(os.Stderr, stderr)
		return err
	}
	return nil
}
func (d *sqlDriver) updateShareTable(shareInfo *shareInfo, versionsMeta *Metadata) error {
	fmt.Printf("RECORD: %d UPDATE oc_share SET item_source=%s,item_target=%s,file_source=%d,file_target=%s WHERE id=%d\n", shareInfo.ID, fmt.Sprintf("%d", versionsMeta.Inode), "/"+fmt.Sprintf("%d", versionsMeta.Inode), versionsMeta.Inode, "/"+path.Base(versionsMeta.Path), shareInfo.ID)
	if GLOBAL_FLAGS.noTouchDb {
		return nil
	}
	query := "UPDATE oc_share SET item_source=?,item_target=?,file_source=?,file_target=? WHERE id=?"
	stmt, err := d.db.Prepare(query)
	if err != nil {
		return err
	}
	defer stmt.Close()
	result, err := stmt.Exec(fmt.Sprintf("%d", versionsMeta.Inode), "/"+fmt.Sprintf("%d", versionsMeta.Inode), versionsMeta.Inode, "/"+path.Base(versionsMeta.Path), shareInfo.ID)
	if err != nil {
		return err
	}
	numRowsAffected, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if numRowsAffected == 0 || numRowsAffected > 1 {
		return fmt.Errorf("Cannot updated share because share id %d does not exists anymore", shareInfo.ID)
	}
	return nil
}
func main() {
	flags := parseFlags()
	os.Setenv("EOS_MGM_URL", flags.eosMGMURL)
	d, err := newSQLDriver(flags)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	shares, err := d.getAllShares()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Cannot get all shares because ", err)
		os.Exit(1)
	}
	if len(shares) == 0 {
		fmt.Fprintln(os.Stderr, "oc_share table does not contain public share files")
		os.Exit(1)
	}

	const maxConcurrency = 20 // for example
	var throttle = make(chan int, maxConcurrency)

	var wg sync.WaitGroup
	for _, s := range shares {
		throttle <- 1 // whatever number
		wg.Add(1)
		go func(d *sqlDriver, s shareInfo, wg *sync.WaitGroup, throttle chan int) {
			defer wg.Done()
			defer func() {
				<-throttle
			}()
			meta, err := d.getMetadataFromEOS(s.FileSource.Int64)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return
			} else {
				fmt.Printf("RECORD: %d info:file id:%d share_type:%d item_source:%s item_target:%s file_source:%d file_target:%s eospath:%s uid:%s gid:%s\n", s.ID, s.ID, s.ShareType, s.ItemSource.String, s.ItemTarget.String, s.FileSource.Int64, s.FileTarget.String, strconv.Quote(meta.Path), meta.UID, meta.GID)
				parts := strings.Split(path.Clean(meta.Path), "/")
				parentDir := parts[len(parts)-2]
				if strings.HasPrefix(path.Base(meta.Path), VERSIONS_PREFIX) {
					fmt.Printf("RECORD: %d ALREADY POINTS TO THE VERSION FOLDER\n", s.ID)
					return
				}
				if !strings.HasPrefix(meta.Path, GLOBAL_FLAGS.userPrefix) {
					fmt.Printf("RECORD: %d FILE NOT UNDER HOME DIRECTORY\n", s.ID)
					return
				}
				if strings.HasPrefix(parentDir, VERSIONS_PREFIX) {
					fmt.Printf("RECORD: %d POINTS TO A VERSION\n", s.ID)
					versionFolder := path.Dir(meta.Path)
					versionsMeta, err := d.getMetadataFromEOSPath(versionFolder)
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						return
					}
					fmt.Printf("RECORD: %d info:versionfolder id:%d path:%s\n", s.ID, versionsMeta.Inode, versionsMeta.Path)
					err = d.updateShareTable(&s, versionsMeta)
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						return
					}
					return
				}
				versionsMeta, err := d.getVersionsFolderMetadata(meta)
				if err != nil {
					fmt.Fprintln(os.Stderr, err)
					return
				} else {
					fmt.Printf("RECORD: %d info:versionfolder id:%d path:%s\n", s.ID, versionsMeta.Inode, versionsMeta.Path)
					err = d.updateShareTable(&s, versionsMeta)
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						return
					}
				}
			}
		}(d, s, &wg, throttle)
	}
	wg.Wait()
	fmt.Printf("Sucess. Dry run: %t\n", GLOBAL_FLAGS.noTouchDb)
	os.Exit(0)
}
<commit_msg>Remove hardcoded user form -user flag<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

const VERSIONS_PREFIX = ".sys.v#."

var GLOBAL_FLAGS *cliFlags

type cliFlags struct {
	dbUsername string
	dbPassword string
	dbHost     string
	dbPort     int
	dbName     string
	noTouchDb  bool
	eosMGMURL  string
	debug      bool
	userPrefix string
	user       string
}

func parseFlags() *cliFlags {
	flags := &cliFlags{}
	flag.StringVar(&flags.dbUsername, "username", "", "The username to connect to the db")
	flag.StringVar(&flags.dbPassword, "password", "", "The password to connect to the db")
	flag.StringVar(&flags.dbHost, "host", "", "The host of the db")
	flag.IntVar(&flags.dbPort, "port", 0, "The port of the db")
	flag.StringVar(&flags.dbName, "dbname", "", "The name of the database")
	flag.BoolVar(&flags.noTouchDb, "notouchdb", false, "With dry run enbaled the changes are not commited to the db")
	flag.StringVar(&flags.eosMGMURL, "eosmgmurl", "root://eospps-slave.cern.ch", "The EOS MGM URL")
	flag.StringVar(&flags.userPrefix, "userprefix", "/eos/scratch/user/", "The path under users reside")
	flag.StringVar(&flags.user, "user", "", "Run the migration just for this user")
	flag.BoolVar(&flags.debug, "debug", false, "Print debug information")
	flag.Parse()
	GLOBAL_FLAGS = flags
	return flags
}

type shareInfo struct {
	ID          int64          `db:"id"`
	ShareType   int            `db:"share_type"`
	ShareWith   sql.NullString `db:"share_with"`
	UIDOwner    string         `db:"uid_owner"`
	Parent      sql.NullInt64  `db:"parent"`
	ItemType    sql.NullString `db:"item_type"`
	ItemSource  sql.NullString `db:"item_source"`
	ItemTarget  sql.NullString `db:"item_target"`
	FileSource  sql.NullInt64  `db:"file_source"`
	FileTarget  sql.NullString `db:"file_target"`
	Permissions string         `db:"permissions"`
	STime       int            `db:"stime"`
	Accepted    int            `db:"accepted"`
	Expiration  time.Time      `db:"expiration"`
	Token       sql.NullString `db:"token"`
	MailSend    int            `db:"mail_send"`
}

type sqlDriver struct {
	db *sqlx.DB
}

func newSQLDriver(flags *cliFlags) (*sqlDriver, error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", flags.dbUsername, flags.dbPassword, flags.dbHost, flags.dbPort, flags.dbName)
	d, err := sqlx.Connect("mysql", dsn)
	if err != nil {
		return nil, err
	}
	return &sqlDriver{db: d}, nil
}
func (d *sqlDriver) getAllShares() ([]shareInfo, error) {
	var entries []shareInfo
	selectStmt := "SELECT id,share_type,item_source,item_target,file_source,file_target from oc_share where share_type=3 and item_type='file' ORDER BY id;"
	if GLOBAL_FLAGS.user != "" {
		selectStmt = fmt.Sprintf("SELECT id,share_type,item_source,item_target,file_source,file_target from oc_share where share_type=3 and item_type='file' and uid_owner='%s' ORDER BY id;", GLOBAL_FLAGS.user)
	}
	err := d.db.Select(&entries, selectStmt)
	if err != nil {
		return nil, err
	}
	return entries, nil
}

type Metadata struct {
	Inode int64
	Path  string
	UID   string
	GID   string
	Size  int64
}

func (d *sqlDriver) executeCMD(cmd *exec.Cmd) (string, string, error) {
	outBuf := &bytes.Buffer{}
	errBuf := &bytes.Buffer{}
	cmd.Stdout = outBuf
	cmd.Stderr = errBuf
	err := cmd.Run()
	if GLOBAL_FLAGS.debug {
		fmt.Printf("DEBUG CMD: %+v ERR:%s", cmd, err)
	}
	return outBuf.String(), errBuf.String(), err
}

// getMetadataFromEos returns the metadata fo the file/folder given the inode
func (d *sqlDriver) getMetadataFromEOS(ID int64) (*Metadata, error) {
	cmd := exec.Command("/usr/bin/eos", "-r", "0", "0", "file", "info", fmt.Sprintf("inode:%d", ID), "-m")
	stdout, _, err := d.executeCMD(cmd)
	if err != nil {
		return nil, err
	}
	return d.parseFileInfo(stdout)
}

// getMetadataFromEosPath returns the metadata fo the file/folder given the eos path
func (d *sqlDriver) getMetadataFromEOSPath(path string) (*Metadata, error) {
	cmd := exec.Command("/usr/bin/eos", "-r", "0", "0", "file", "info", path, "-m")
	stdout, _, err := d.executeCMD(cmd)
	if err != nil {
		return nil, err
	}
	return d.parseFileInfo(stdout)
}

// getVersionsFolderMetadata returns the metadata associated to the versions folder of the file with the inode passed
// if the versions folder does not exists it will try to create it.
func (d *sqlDriver) getVersionsFolderMetadata(fileMeta *Metadata) (*Metadata, error) {
	// obtain the versions folder path from the file path
	dirName := path.Dir(fileMeta.Path)
	baseName := path.Base(fileMeta.Path)
	versionsPath := path.Join(dirName, VERSIONS_PREFIX+baseName)
	versionsMeta, err := d.getMetadataFromEOSPath(versionsPath)
	if err != nil {
		// if versions folder does not exists (eos err code == 2) trigger creation of this folder
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				if status.ExitStatus() == 2 {
					err = d.createVersionsFolder(fileMeta)
					if err != nil {
						return nil, err
					}

					// if we are talking to the slave the creation of the versions folder may not has been replicated, so we retry ever
					maxRetries := 5
					err := fmt.Errorf("Version not created yet")
					var versionsMeta *Metadata

					for maxRetries > 0 && err != nil {
						_versionsMeta, _err := d.getMetadataFromEOSPath(versionsPath)
						err = _err
						versionsMeta = _versionsMeta
						maxRetries--

					}
					if err != nil {
						return nil, err
					}
					return versionsMeta, nil
				}
			}
			return nil, err
		}
		return nil, err
	}
	return versionsMeta, nil
}

func (d *sqlDriver) parseFileInfo(raw string) (*Metadata, error) {
	kv := make(map[string]string)
	partsBySpace := strings.Split(raw, " ") // we have [keylength.file=14 file=/eos/pps/proc/ container=3 ...}
	for _, p := range partsBySpace {
		partsByEqual := strings.Split(p, "=") // we have kv pairs like [ keylength.file 14]
		if len(partsByEqual) == 2 {
			kv[partsByEqual[0]] = partsByEqual[1]
		}
	}
	// fix eos path because the kv pair file=path could contains whitespace and the whitespace is the pair separator. Not very smart :(
	fileLength := kv["keylength.file"]
	fileLengthInt64, err := strconv.ParseInt(fileLength, 10, 64)
	if err != nil {
		return nil, err
	}
	startIndex := int64(14) + int64(len(fileLength)) + 7
	kv["file"] = raw[startIndex : startIndex+fileLengthInt64]

	inodeInt64, err := strconv.ParseInt(kv["ino"], 10, 64)
	if err != nil {
		return nil, err
	}
	sizeInt64, err := strconv.ParseInt(kv["size"], 10, 64)
	m := &Metadata{Inode: inodeInt64, Path: kv["file"], UID: kv["uid"], GID: kv["gid"], Size: sizeInt64}
	return m, nil
}
func (d *sqlDriver) createVersionsFolder(fileMeta *Metadata) error {
	cmd := exec.Command("/usr/bin/eos", "-r", fileMeta.UID, fileMeta.GID, "file", "version", fileMeta.Path)
	_, stderr, err := d.executeCMD(cmd)
	if err != nil {
		fmt.Fprintln(os.Stderr, stderr)
		return err
	}
	return nil
}
func (d *sqlDriver) updateShareTable(shareInfo *shareInfo, versionsMeta *Metadata) error {
	fmt.Printf("RECORD: %d UPDATE oc_share SET item_source=%s,item_target=%s,file_source=%d,file_target=%s WHERE id=%d\n", shareInfo.ID, fmt.Sprintf("%d", versionsMeta.Inode), "/"+fmt.Sprintf("%d", versionsMeta.Inode), versionsMeta.Inode, "/"+path.Base(versionsMeta.Path), shareInfo.ID)
	if GLOBAL_FLAGS.noTouchDb {
		return nil
	}
	query := "UPDATE oc_share SET item_source=?,item_target=?,file_source=?,file_target=? WHERE id=?"
	stmt, err := d.db.Prepare(query)
	if err != nil {
		return err
	}
	defer stmt.Close()
	result, err := stmt.Exec(fmt.Sprintf("%d", versionsMeta.Inode), "/"+fmt.Sprintf("%d", versionsMeta.Inode), versionsMeta.Inode, "/"+path.Base(versionsMeta.Path), shareInfo.ID)
	if err != nil {
		return err
	}
	numRowsAffected, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if numRowsAffected == 0 || numRowsAffected > 1 {
		return fmt.Errorf("Cannot updated share because share id %d does not exists anymore", shareInfo.ID)
	}
	return nil
}
func main() {
	flags := parseFlags()
	os.Setenv("EOS_MGM_URL", flags.eosMGMURL)
	d, err := newSQLDriver(flags)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	shares, err := d.getAllShares()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Cannot get all shares because ", err)
		os.Exit(1)
	}
	if len(shares) == 0 {
		fmt.Fprintln(os.Stderr, "oc_share table does not contain public share files")
		os.Exit(1)
	}

	const maxConcurrency = 20 // for example
	var throttle = make(chan int, maxConcurrency)

	var wg sync.WaitGroup
	for _, s := range shares {
		throttle <- 1 // whatever number
		wg.Add(1)
		go func(d *sqlDriver, s shareInfo, wg *sync.WaitGroup, throttle chan int) {
			defer wg.Done()
			defer func() {
				<-throttle
			}()
			meta, err := d.getMetadataFromEOS(s.FileSource.Int64)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return
			} else {
				fmt.Printf("RECORD: %d info:file id:%d share_type:%d item_source:%s item_target:%s file_source:%d file_target:%s eospath:%s uid:%s gid:%s\n", s.ID, s.ID, s.ShareType, s.ItemSource.String, s.ItemTarget.String, s.FileSource.Int64, s.FileTarget.String, strconv.Quote(meta.Path), meta.UID, meta.GID)
				parts := strings.Split(path.Clean(meta.Path), "/")
				parentDir := parts[len(parts)-2]
				if strings.HasPrefix(path.Base(meta.Path), VERSIONS_PREFIX) {
					fmt.Printf("RECORD: %d ALREADY POINTS TO THE VERSION FOLDER\n", s.ID)
					return
				}
				if !strings.HasPrefix(meta.Path, GLOBAL_FLAGS.userPrefix) {
					fmt.Printf("RECORD: %d FILE NOT UNDER HOME DIRECTORY\n", s.ID)
					return
				}
				if strings.HasPrefix(parentDir, VERSIONS_PREFIX) {
					fmt.Printf("RECORD: %d POINTS TO A VERSION\n", s.ID)
					versionFolder := path.Dir(meta.Path)
					versionsMeta, err := d.getMetadataFromEOSPath(versionFolder)
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						return
					}
					fmt.Printf("RECORD: %d info:versionfolder id:%d path:%s\n", s.ID, versionsMeta.Inode, versionsMeta.Path)
					err = d.updateShareTable(&s, versionsMeta)
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						return
					}
					return
				}
				versionsMeta, err := d.getVersionsFolderMetadata(meta)
				if err != nil {
					fmt.Fprintln(os.Stderr, err)
					return
				} else {
					fmt.Printf("RECORD: %d info:versionfolder id:%d path:%s\n", s.ID, versionsMeta.Inode, versionsMeta.Path)
					err = d.updateShareTable(&s, versionsMeta)
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						return
					}
				}
			}
		}(d, s, &wg, throttle)
	}
	wg.Wait()
	fmt.Printf("Sucess. Dry run: %t\n", GLOBAL_FLAGS.noTouchDb)
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"sync"

	"github.com/rodrigocorsi2/mattermail/mmail"
)

const defLinesToPreview = 10

// Version show the current version, changed during the make build
var Version = "3.0-dev"
var configFile string
var help bool
var version bool

func init() {
	flag.StringVar(&configFile, "config", "./config.json", "Sets the file location for config.json")
	flag.StringVar(&configFile, "c", "./config.json", "Sets the file location for config.json")

	flag.BoolVar(&help, "h", false, "Show help")
	flag.BoolVar(&help, "help", false, "Show help")

	flag.BoolVar(&version, "v", false, "Print version")
	flag.BoolVar(&version, "version", false, "Print version")
}

const usage = `mattermail [Options]

MatterMail is an integration service for Mattermost, MatterMail listen an email
box and publish all received emails in a channel or private group in Mattermost

Options:
    -c, --config  Sets the file location for config.json
                  Default: ./config.json
    -h, --help    Show this help
    -v, --version Print current version
`

func loadconfig() []*mmail.Config {
	log.Println("Loading ", configFile)

	file, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Fatal("Could not load: ", err)
	}

	var cfg []*mmail.Config
	err = json.Unmarshal(file, &cfg)

	if err != nil {
		log.Fatal("Could not parse: ", err)
	}

	// Set default value
	for _, c := range cfg {
		if err := c.Valid(); err != nil {
			log.Fatal(err)
		}
	}

	return cfg
}

func main() {

	flag.Parse()

	if help {
		fmt.Println(usage)
		fmt.Println(Version)
		return
	}

	if version {
		fmt.Println(Version)
		return
	}

	cfgs := loadconfig()
	hasconfig := false
	log.Println("MatterMail version:", Version)

	var wg sync.WaitGroup
	for _, cfg := range cfgs {
		if cfg.Disabled {
			continue
		}
		hasconfig = true

		wg.Add(1)
		c := cfg
		go func() {
			mmail.InitMatterMail(c)
			wg.Done()
		}()
	}

	wg.Wait()

	if !hasconfig {
		log.Println(`There is no enabled profile. Check "Disabled" field in config.json`)
	}
}
<commit_msg>temproraily main package name<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"sync"

	"github.com/banzo/mattermail/mmail"
)

const defLinesToPreview = 10

// Version show the current version, changed during the make build
var Version = "3.0-dev"
var configFile string
var help bool
var version bool

func init() {
	flag.StringVar(&configFile, "config", "./config.json", "Sets the file location for config.json")
	flag.StringVar(&configFile, "c", "./config.json", "Sets the file location for config.json")

	flag.BoolVar(&help, "h", false, "Show help")
	flag.BoolVar(&help, "help", false, "Show help")

	flag.BoolVar(&version, "v", false, "Print version")
	flag.BoolVar(&version, "version", false, "Print version")
}

const usage = `mattermail [Options]

MatterMail is an integration service for Mattermost, MatterMail listen an email
box and publish all received emails in a channel or private group in Mattermost

Options:
    -c, --config  Sets the file location for config.json
                  Default: ./config.json
    -h, --help    Show this help
    -v, --version Print current version
`

func loadconfig() []*mmail.Config {
	log.Println("Loading ", configFile)

	file, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Fatal("Could not load: ", err)
	}

	var cfg []*mmail.Config
	err = json.Unmarshal(file, &cfg)

	if err != nil {
		log.Fatal("Could not parse: ", err)
	}

	// Set default value
	for _, c := range cfg {
		if err := c.Valid(); err != nil {
			log.Fatal(err)
		}
	}

	return cfg
}

func main() {

	flag.Parse()

	if help {
		fmt.Println(usage)
		fmt.Println(Version)
		return
	}

	if version {
		fmt.Println(Version)
		return
	}

	cfgs := loadconfig()
	hasconfig := false
	log.Println("MatterMail version:", Version)

	var wg sync.WaitGroup
	for _, cfg := range cfgs {
		if cfg.Disabled {
			continue
		}
		hasconfig = true

		wg.Add(1)
		c := cfg
		go func() {
			mmail.InitMatterMail(c)
			wg.Done()
		}()
	}

	wg.Wait()

	if !hasconfig {
		log.Println(`There is no enabled profile. Check "Disabled" field in config.json`)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"

	"crypto/ecdsa"

	loudp2p "github.com/matiasinsaurralde/loudp2p/core"
	crypto "github.com/matiasinsaurralde/loudp2p/crypto"
)

func main() {
	log.Println("loudp2p starts")

	var err error

	var settings *loudp2p.Settings
	settings = loudp2p.LoadSettings()

	if settings == nil {
		log.Println("No keys present, generating.")

		var privateKey *ecdsa.PrivateKey
		var publicKey *ecdsa.PublicKey
		var privateKeyBytes, publicKeyBytes []byte
		var peerID string

		privateKey, privateKeyBytes, publicKey, publicKeyBytes, peerID, err = crypto.GenerateKeys()

		if err != nil {
			log.Println("Couldn't generate keys!")
			panic(err)
		}

		settings = &loudp2p.Settings{
			PrivateKey:   privateKey,
			PublicKey:    publicKey,
			PrivKeyBytes: privateKeyBytes,
			PubKeyBytes:  publicKeyBytes,
			PeerID:       peerID,
		}
		err = settings.Persist()
		if err != nil {
			log.Println("Couldn't persist settings!")
			panic(err)
		}
	} else {
		log.Println("Using existing keys.")
	}

	log.Println("Peer ID is", settings.PeerID)

	var client loudp2p.Client
	var server loudp2p.Server

	client, err = loudp2p.NewClient(settings)
	if err != nil {
		log.Println("Couldn't initialize client!")
		panic(err)
	}

	server, err = loudp2p.NewServer(settings)
	if err != nil {
		log.Println("Couldn't initialize server!")
		panic(err)
	}

	go client.Start()

	// go client.StartDiscovery()
	// go server.Start()
	for {
	}
	log.Println(1, client, server)
}
<commit_msg>Using a global event handler. Override RPC port.<commit_after>package main

import (
	"log"

	"crypto/ecdsa"

	loudp2p "github.com/matiasinsaurralde/loudp2p/core"
	crypto "github.com/matiasinsaurralde/loudp2p/crypto"
)

func main() {
	log.Println("loudp2p starts")

	var err error

	// Load settings:

	var settings *loudp2p.Settings
	settings = loudp2p.LoadSettings()

	if settings == nil {
		log.Println("No keys present, generating.")

		var privateKey *ecdsa.PrivateKey
		var publicKey *ecdsa.PublicKey
		var privateKeyBytes, publicKeyBytes []byte
		var peerID string

		privateKey, privateKeyBytes, publicKey, publicKeyBytes, peerID, err = crypto.GenerateKeys()

		if err != nil {
			log.Println("Couldn't generate keys!")
			panic(err)
		}

		settings = &loudp2p.Settings{
			PrivateKey:   privateKey,
			PublicKey:    publicKey,
			PrivKeyBytes: privateKeyBytes,
			PubKeyBytes:  publicKeyBytes,
			PeerID:       peerID,
			RPCPort:      loudp2p.DefaultRPCPort,
		}
		err = settings.Persist()
		if err != nil {
			log.Println("Couldn't persist settings!")
			panic(err)
		}
	} else {
		log.Println("Using existing keys.")
	}

	// Attach event system:
	var events loudp2p.EventHandler
	events = loudp2p.NewEventHandler()

	settings.RPCPort = 2016

	log.Println("Peer ID is", settings.PeerID)

	var client loudp2p.Client
	var server loudp2p.Server

	client, err = loudp2p.NewClient(settings, &events)
	if err != nil {
		log.Println("Couldn't initialize client!")
		panic(err)
	}

	server, err = loudp2p.NewServer(settings, &events)
	if err != nil {
		log.Println("Couldn't initialize server!")
		panic(err)
	}

	go client.Start()
	go server.Start()

	for {
	}
	log.Println(1, client, server)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Betalo AB
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"syscall"
	"time"
)

func main() {
	var (
		forceFlag    = flag.Bool("f", false, "Force running the command even after giving up")
		quietFlag    = flag.Bool("q", false, "Set quiet mode")
		timeoutFlag  = flag.Duration("t", 1*time.Minute, "Set timeout duration before giving up")
		verbose1Flag = flag.Bool("v", false, "Set verbose output mode")
		verbose2Flag = flag.Bool("vv", false, "Set more verbose output mode")
	)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: await [options...] <res>... [ -- <cmd>]")
		fmt.Fprintln(os.Stderr, "Await availability of resources.")
		fmt.Fprintln(os.Stderr)
		flag.PrintDefaults()
	}
	flag.Parse()

	var logLevel int
	switch {
	case *quietFlag:
		logLevel = silentLevel
	case *verbose1Flag:
		logLevel = infoLevel
	case *verbose2Flag:
		logLevel = debugLevel
	default:
		logLevel = errorLevel
	}

	logger := NewLogger(logLevel)
	resArgs, cmdArgs := splitArgs(flag.Args())

	ress, err := parseResources(resArgs)
	if err != nil {
		logger.Fatalln("Error: failed to parse resources: %v", err)
	}

	awaiter := &awaiter{
		logger:  logger,
		timeout: *timeoutFlag,
	}

	if err := awaiter.run(ress); err != nil {
		if e, ok := err.(*unavailabilityError); ok {
			logger.Errorf("Resource unavailable: %v", e)
			logger.Errorln("Timeout exceeded")
		} else {
			logger.Fatalln("Error: %v", err)
		}
		if !*forceFlag {
			os.Exit(1)
		}
	} else {
		logger.Infoln("All resources available")
	}

	if len(cmdArgs) > 0 {
		logger.Infof("Runnning command: %v", cmdArgs)
		if err := execCmd(cmdArgs); err != nil {
			logger.Fatalf("Error: failed to execute command: %v", err)
		}
	}
}

func splitArgs(args []string) ([]string, []string) {
	if i := indexOf(args, "--"); i >= 0 {
		return args[0:i], args[i+1:]
	}

	// We haven't seen a resource|command separator ('--'). This can either be
	// because of Go's flag parser removing the separator if no args were given,
	// or because there actually was none given.
	// Fallback to the original (unparsed) flag argument list and see if a
	// separator was given there and if, assume all arguments given are part of
	// the command, i.e. no resources at all were provided.
	if i := indexOf(os.Args, "--"); i >= 0 {
		return []string{}, args
	}

	// We still haven't seen a resource|command separator ('--'). Now finally
	// assume because there actually was none given and use all arguments as
	// resources.
	return args, []string{}
}

func indexOf(l []string, s string) int {
	for i, e := range l {
		if e == "--" {
			return i
		}
	}
	return -1
}

func execCmd(cmdArgs []string) error {
	path, err := exec.LookPath(cmdArgs[0])
	if err != nil {
		return err
	}
	return syscall.Exec(path, cmdArgs, os.Environ())
}
<commit_msg>Realign spacing<commit_after>// Copyright (c) 2016 Betalo AB
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"syscall"
	"time"
)

func main() {
	var (
		forceFlag    = flag.Bool("f", false, "Force running the command even after giving up")
		quietFlag    = flag.Bool("q", false, "Set quiet mode")
		timeoutFlag  = flag.Duration("t", 1*time.Minute, "Set timeout duration before giving up")
		verbose1Flag = flag.Bool("v", false, "Set verbose output mode")
		verbose2Flag = flag.Bool("vv", false, "Set more verbose output mode")
	)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: await [options...] <res>... [ -- <cmd>]")
		fmt.Fprintln(os.Stderr, "Await availability of resources.")
		fmt.Fprintln(os.Stderr)
		flag.PrintDefaults()
	}
	flag.Parse()

	var logLevel int
	switch {
	case *quietFlag:
		logLevel = silentLevel
	case *verbose1Flag:
		logLevel = infoLevel
	case *verbose2Flag:
		logLevel = debugLevel
	default:
		logLevel = errorLevel
	}
	logger := NewLogger(logLevel)

	resArgs, cmdArgs := splitArgs(flag.Args())
	ress, err := parseResources(resArgs)
	if err != nil {
		logger.Fatalln("Error: failed to parse resources: %v", err)
	}

	awaiter := &awaiter{
		logger:  logger,
		timeout: *timeoutFlag,
	}

	if err := awaiter.run(ress); err != nil {
		if e, ok := err.(*unavailabilityError); ok {
			logger.Errorf("Resource unavailable: %v", e)
			logger.Errorln("Timeout exceeded")
		} else {
			logger.Fatalln("Error: %v", err)
		}
		if !*forceFlag {
			os.Exit(1)
		}
	} else {
		logger.Infoln("All resources available")
	}

	if len(cmdArgs) > 0 {
		logger.Infof("Runnning command: %v", cmdArgs)
		if err := execCmd(cmdArgs); err != nil {
			logger.Fatalf("Error: failed to execute command: %v", err)
		}
	}
}

func splitArgs(args []string) ([]string, []string) {
	if i := indexOf(args, "--"); i >= 0 {
		return args[0:i], args[i+1:]
	}

	// We haven't seen a resource|command separator ('--'). This can either be
	// because of Go's flag parser removing the separator if no args were given,
	// or because there actually was none given.
	// Fallback to the original (unparsed) flag argument list and see if a
	// separator was given there and if, assume all arguments given are part of
	// the command, i.e. no resources at all were provided.
	if i := indexOf(os.Args, "--"); i >= 0 {
		return []string{}, args
	}

	// We still haven't seen a resource|command separator ('--'). Now finally
	// assume because there actually was none given and use all arguments as
	// resources.
	return args, []string{}
}

func indexOf(l []string, s string) int {
	for i, e := range l {
		if e == "--" {
			return i
		}
	}
	return -1
}

func execCmd(cmdArgs []string) error {
	path, err := exec.LookPath(cmdArgs[0])
	if err != nil {
		return err
	}
	return syscall.Exec(path, cmdArgs, os.Environ())
}
<|endoftext|>


<commit_before>//
// Copyright: 2016, mgIT GmbH <office@mgit.at>
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"flag"
	"io/ioutil"
	"log"
	//	"net"
	//	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	etcd "github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

const (
	LOCK_FILE_BASE = "/singleton.mgit.at"
)

// command line flags
var (
	flagNameTemplate   = flag.String("name-template", "", "template for the lockfile name (will get expanded using environment variables)")
	flagInstTemplate   = flag.String("instance-template", "", "template for the instance name (will get expanded using environment variables)")
	flagRequestTimeout = flag.Uint("request-timeout", 5, "timeout in seconds to wait for responses from etcd")
	flagUpdateInterval = flag.Uint("update-interval", 30, "interval in seconds between lock file update requests")
	flagGracePeriod    = flag.Uint("grace-period", 30, "time in seconds to wait for a normal shutdown of the child")
	flagKillBackoff    = flag.Uint("kill-backoff", 5, "")
	flagEtcd           = flag.String("etcd", "http://127.0.0.1:2379", "etcd machines (comma separated list)")
	flagCA             = flag.String("ca", "", "CA certificate")
	flagCert           = flag.String("cert", "", "client certificate")
	flagKey            = flag.String("key", "", "client key")
)

func runChild(cmd string, args []string, signals <-chan os.Signal) (exited chan bool, err error) {
	child := exec.Cmd{}
	child.Path = cmd
	child.Args = args
	child.Stdin = os.Stdin
	child.Stdout = os.Stdout
	child.Stderr = os.Stderr

	if err = child.Start(); err != nil {
		return
	}

	exited = make(chan bool, 1)
	go func() {
		defer func() {
			exited <- true
		}()
		err := child.Wait()
		if err != nil {
			log.Printf("singleton-runner: child exited with: %v", err)
		} else {
			log.Printf("singleton-runner: child exited normally")
		}
	}()

	go func() {
		for sig := range signals {
			child.Process.Signal(sig)
		}
	}()
	return
}

func initETCdClient(machines []string, CA, cert, key string, timeout time.Duration) (*etcd.Client, error) {
	var tlsConfig *tls.Config
	if cert != "" && key != "" && CA != "" {
		cert, err := tls.LoadX509KeyPair(cert, key)
		if err != nil {
			log.Fatal("failed to load client key pair:", err)
		}
		caCert, err := ioutil.ReadFile(CA)
		if err != nil {
			log.Fatal("failed to load CA:", err)
		}
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(caCert)
		tlsConfig = &tls.Config{
			Certificates: []tls.Certificate{cert},
			RootCAs:      caCertPool,
		}
		tlsConfig.BuildNameToCertificate()
	}

	cfg := etcd.Config{
		Endpoints:   machines,
		TLS:         tlsConfig,
		DialTimeout: timeout,
	}

	c, err := etcd.New(cfg)
	if err != nil {
		return nil, err
	}
	return c, err
}

// func IsKeyExists(err error) bool {
// 	if cErr, ok := err.(etcd.Error); ok {
// 		return cErr.Code == etcd.ErrorCodeNodeExist
// 	}
// 	return false
// }

func acquireLock(cli *etcd.Client, lockfile, instanceID string, ttl int64, timeout time.Duration) error {
	// for {
	log.Printf("singleton-runner: trying to acquire lock: %s (TTL: %v)", lockfile, ttl)

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	resp, err := cli.Grant(ctx, ttl)
	cancel()
	if err != nil {
		return err
	}

	log.Printf("singleton-runner: got lease with ID", resp.ID)

	// opts := &etcd.SetOptions{PrevExist: etcd.PrevNoExist, TTL: ttl, Refresh: false}
	// _, err = kvc.Set(ctx, lockfile, instanceID, opts)
	// cancel()
	// if err == nil {
	// 	return // we got the lock!
	// }
	// if !IsKeyExists(err) {
	// 	return // this seems to be a severe problem.. better give up
	// }
	// log.Printf("singleton-runner: lock is already acquired - watching for changes")

	// 	ctx, cancel = context.WithTimeout(context.Background(), ttl)
	// 	watcher := kvc.Watcher(lockfile, nil)
	// WatchLock:
	// 	for {
	// 		resp, err := watcher.Next(ctx)
	// 		if err != nil {
	// 			log.Printf("singleton-runner: watching for events failed: %v", err)
	// 			return err
	// 		}
	// 		switch resp.Action {
	// 		case "expire":
	// 			fallthrough
	// 		case "compareAndDelete":
	// 			fallthrough
	// 		case "delete":
	// 			log.Printf("singleton-runner: delete or expire event - try again!")
	// 			break WatchLock
	// 		case "compareAndSwap":
	// 			return errors.New("locked instance seems to be alive - giving up")
	// 		default:
	// 			log.Printf("singleton-runner: ignoring unknown event '%s'", resp.Action)
	// 		}
	// 	}
	// }
	// return
	return errors.New("not yet implemented")
}

func releaseLock(lease etcd.Lease) {
	// log.Printf("singleton-runner: trying to release lock: %s", lockfile)
	// ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
	// opts := &etcd.DeleteOptions{PrevValue: instanceID}
	// _, err := kvc.Delete(ctx, lockfile, opts)
	// cancel()
	// if err != nil {
	// 	log.Printf("singleton-runner: releasing lock failed: %s", err)
	// }
	// return
}

func updateLock(lease etcd.Lease, timeout time.Duration) (err error) {
	// log.Printf("singleton-runner: trying to update lock: %s (TTL: %v, Timeout: %v)", lockfile, ttl, timeout)

	// ctx, cancel := context.WithTimeout(context.Background(), timeout)
	// opts := &etcd.SetOptions{PrevValue: instanceID, PrevExist: etcd.PrevExist, TTL: ttl, Refresh: false}
	// _, err = kvc.Set(ctx, lockfile, instanceID, opts)
	// cancel()
	// return
	return errors.New("not yet implemented")
}

func main() {
	//
	// **** parse command line
	//
	flag.Parse()

	if *flagNameTemplate == "" {
		log.Fatal("singleton-runner: '-name-template' is empty")
	}
	name := os.ExpandEnv(*flagNameTemplate)
	lockfilePath := filepath.Join(LOCK_FILE_BASE, path.Clean("/"+name))

	if *flagInstTemplate == "" {
		log.Fatal("singleton-runner: '-instance-template' is empty")
	}
	instanceID := os.ExpandEnv(*flagInstTemplate)

	requestTimeout := time.Duration(*flagRequestTimeout) * time.Second
	updateInterval := time.Duration(*flagUpdateInterval) * time.Second
	gracePeriod := time.Duration(*flagGracePeriod) * time.Second
	//killBackoff := time.Duration(*flagKillBackoff) * time.Second
	ttl := int64(*flagRequestTimeout + *flagUpdateInterval + *flagGracePeriod + *flagKillBackoff)

	machines := strings.Split(*flagEtcd, ",")
	CA := *flagCA
	cert := *flagCert
	key := *flagKey

	var cmd string
	args := flag.Args()
	if args := flag.Args(); len(args) > 0 {
		cmd = args[0]
	} else {
		log.Fatal("singleton-runner: please specify a command to run")
	}

	//
	// **** initialization
	//

	cli, err := initETCdClient(machines, CA, cert, key, requestTimeout)
	if err != nil {
		log.Fatal("error connecting to etcd:", err)
	}
	defer cli.Close()

	//
	// **** try to get the lock
	//
	if err = acquireLock(cli, lockfilePath, instanceID, ttl, requestTimeout); err != nil {
		log.Fatal("singleton-runner: unable to acquire lock: ", err)
	}
	log.Printf("singleton-runner: lock acquired successfully! .. starting %q", cmd)
	// defer releaseLock(cli) // should this be done in any case?

	//
	// **** run the child
	//
	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)
	exited, err := runChild(cmd, args, signals)
	if err != nil {
		releaseLock(cli) // remove here if we do this using the defer above
		log.Fatal("singleton-runner: calling child failed:", err)
	}

	//
	// **** update the lock - release lock if child exits
	//
	t := time.NewTicker(updateInterval)
Loop:
	for {
		select {
		case <-t.C:
			if err := updateLock(cli, requestTimeout); err != nil {
				log.Println("singleton-runner: updateting lock failed:", err)
				log.Println("singleton-runner: sending child the TERM signal")
				signals <- syscall.SIGTERM
				break Loop
			}
		case <-exited:
			log.Println("singleton-runner: closing...")
			releaseLock(cli) // remove here if we do this using the defer above
			return
		}
	}

	//
	// **** wait for child to exit - kill it after grace period
	//
	k := time.NewTimer(gracePeriod)
	select {
	case <-k.C:
		log.Println("singleton-runner: grace period elapsed sending child the KILL signal")
		signals <- syscall.SIGKILL
	case <-exited:
		log.Println("singleton-runner: exiting after child stopped")
		releaseLock(cli) // remove here if we do this using the defer above
		return
	}
}
<commit_msg>etcdv3 acquire and release works now updating is still work in progress improved singal handling<commit_after>//
// Copyright: 2016, mgIT GmbH <office@mgit.at>
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"flag"
	"io/ioutil"
	"log"
	//	"net"
	//	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"

	etcd "github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

const (
	LOCK_FILE_PREFIX = "/singleton.mgit.at/"
)

// command line flags
var (
	flagNameTemplate   = flag.String("name-template", "", "template for the lockfile name (will get expanded using environment variables)")
	flagInstTemplate   = flag.String("instance-template", "", "template for the instance name (will get expanded using environment variables)")
	flagRequestTimeout = flag.Uint("request-timeout", 5, "timeout in seconds to wait for responses from etcd")
	flagUpdateInterval = flag.Uint("update-interval", 30, "interval in seconds between lock file update requests")
	flagGracePeriod    = flag.Uint("grace-period", 30, "time in seconds to wait for a normal shutdown of the child")
	flagKillBackoff    = flag.Uint("kill-backoff", 5, "")
	flagEtcd           = flag.String("etcd", "http://127.0.0.1:2379", "etcd machines (comma separated list)")
	flagCA             = flag.String("ca", "", "CA certificate")
	flagCert           = flag.String("cert", "", "client certificate")
	flagKey            = flag.String("key", "", "client key")
)

func runChild(cmd string, args []string) (child exec.Cmd, exited chan bool, err error) {
	child.Path = cmd
	child.Args = args
	child.Stdin = os.Stdin
	child.Stdout = os.Stdout
	child.Stderr = os.Stderr

	if err = child.Start(); err != nil {
		return
	}

	exited = make(chan bool, 1)
	go func() {
		defer func() {
			exited <- true
		}()
		err := child.Wait()
		if err != nil {
			log.Printf("singleton-runner: child exited with: %v", err)
		} else {
			log.Printf("singleton-runner: child exited normally")
		}
	}()

	return
}

func initETCdClient(machines []string, CA, cert, key string, timeout time.Duration) (*etcd.Client, error) {
	var tlsConfig *tls.Config
	if cert != "" && key != "" && CA != "" {
		cert, err := tls.LoadX509KeyPair(cert, key)
		if err != nil {
			log.Fatal("failed to load client key pair:", err)
		}
		caCert, err := ioutil.ReadFile(CA)
		if err != nil {
			log.Fatal("failed to load CA:", err)
		}
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(caCert)
		tlsConfig = &tls.Config{
			Certificates: []tls.Certificate{cert},
			RootCAs:      caCertPool,
		}
		tlsConfig.BuildNameToCertificate()
	}

	cfg := etcd.Config{
		Endpoints:   machines,
		TLS:         tlsConfig,
		DialTimeout: timeout,
	}

	c, err := etcd.New(cfg)
	if err != nil {
		return nil, err
	}
	return c, err
}

func acquireLock(cli *etcd.Client, lockfile, instanceID string, ttl int64, timeout time.Duration) (etcd.LeaseID, <-chan *etcd.LeaseKeepAliveResponse, error) {
	// for {
	log.Printf("singleton-runner: trying to acquire lock: %s (TTL: %v)", lockfile, ttl)

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	resp, err := cli.Grant(ctx, ttl)
	if err != nil {
		return etcd.NoLease, nil, err
	}

	log.Printf("singleton-runner: got lease with ID: %v", resp.ID)

	respTxn, err := cli.Txn(ctx).
		If(etcd.Compare(etcd.Version(lockfile), "=", 0)).
		Then(etcd.OpPut(lockfile, instanceID, etcd.WithLease(resp.ID))).Commit()
	if err != nil {
		return etcd.NoLease, nil, err
	}
	if respTxn.Succeeded {
		ch, err := cli.KeepAlive(ctx, resp.ID)
		if err != nil {
			return etcd.NoLease, nil, err
		}

		return resp.ID, ch, nil
	}

	return etcd.NoLease, nil, errors.New("lock already acquired .. watching is not yet implemented")
	//  log.Printf("singleton-runner: lock is already acquired - watching for changes")
	// 	ctx, cancel = context.WithTimeout(context.Background(), ttl)
	// 	watcher := kvc.Watcher(lockfile, nil)
	// WatchLock:
	// 	for {
	// 		resp, err := watcher.Next(ctx)
	// 		if err != nil {
	// 			log.Printf("singleton-runner: watching for events failed: %v", err)
	// 			return err
	// 		}
	// 		switch resp.Action {
	// 		case "expire":
	// 			fallthrough
	// 		case "compareAndDelete":
	// 			fallthrough
	// 		case "delete":
	// 			log.Printf("singleton-runner: delete or expire event - try again!")
	// 			break WatchLock
	// 		case "compareAndSwap":
	// 			return errors.New("locked instance seems to be alive - giving up")
	// 		default:
	// 			log.Printf("singleton-runner: ignoring unknown event '%s'", resp.Action)
	// 		}
	// 	}
	// }
	// return
}

func releaseLock(lease etcd.Lease, id etcd.LeaseID) {
	log.Printf("singleton-runner: trying to release lock")
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
	_, err := lease.Revoke(ctx, id)
	cancel()
	if err != nil {
		log.Printf("singleton-runner: releasing lock failed: %s", err)
	}
	return
}

func main() {
	//
	// **** parse command line
	//
	flag.Parse()

	if *flagNameTemplate == "" {
		log.Fatal("singleton-runner: '-name-template' is empty")
	}
	name := os.ExpandEnv(*flagNameTemplate)
	lockfilePath := LOCK_FILE_PREFIX + name

	if *flagInstTemplate == "" {
		log.Fatal("singleton-runner: '-instance-template' is empty")
	}
	instanceID := os.ExpandEnv(*flagInstTemplate)

	requestTimeout := time.Duration(*flagRequestTimeout) * time.Second
	//updateInterval := time.Duration(*flagUpdateInterval) * time.Second
	gracePeriod := time.Duration(*flagGracePeriod) * time.Second
	killBackoff := time.Duration(*flagKillBackoff) * time.Second
	ttl := int64(*flagRequestTimeout + *flagUpdateInterval + *flagGracePeriod + *flagKillBackoff)

	machines := strings.Split(*flagEtcd, ",")
	CA := *flagCA
	cert := *flagCert
	key := *flagKey

	var cmd string
	args := flag.Args()
	if args := flag.Args(); len(args) > 0 {
		cmd = args[0]
	} else {
		log.Fatal("singleton-runner: please specify a command to run")
	}

	//
	// **** initialization
	//

	cli, err := initETCdClient(machines, CA, cert, key, requestTimeout)
	if err != nil {
		log.Fatal("error connecting to etcd:", err)
	}
	defer cli.Close()

	//
	// **** try to get the lock
	//
	//	leaseID, kac, err := acquireLock(cli, lockfilePath, instanceID, ttl, requestTimeout)
	leaseID, _, err := acquireLock(cli, lockfilePath, instanceID, ttl, requestTimeout)
	if err != nil {
		log.Fatal("singleton-runner: unable to acquire lock: ", err)
	}
	log.Printf("singleton-runner: lock acquired successfully! .. starting %q", cmd)
	// defer releaseLock(cli, leaseID) // should this be done in any case?

	//
	// **** run the child
	//
	child, exited, err := runChild(cmd, args)
	if err != nil {
		releaseLock(cli, leaseID) // remove here if we do this using the defer above
		log.Fatal("singleton-runner: calling child failed:", err)
	}

	//
	// **** update the lock - release lock if child exits
	//
	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)
Loop:
	for {
		select {
		// case resp, ok := <-kac:
		// 	if !ok {
		// 		log.Println("singleton-runner: keep alive reponse channel is closed!")
		//		child.Process.Signal(syscall.SIGKILL)
		// 		signals <- syscall.SIGTERM
		// 		//				break Loop
		// 	}
		// 	log.Println("singleton-runner: got keep alive response:", resp)
		// TODO: if TTL expired:
		// 		signals <- syscall.SIGTERM
		// 		break Loop
		case <-exited:
			log.Println("singleton-runner: closing...")
			releaseLock(cli, leaseID) // remove here if we do this using the defer above
			return
		case sig := <-signals:
			child.Process.Signal(sig)
			if sig == syscall.SIGTERM || sig == syscall.SIGINT {
				log.Printf("singleton-runner: got signal %v ... waiting %v for child to terminate", sig, gracePeriod)
				break Loop
			}
		}
	}

	//
	// **** wait for child to exit - kill it after grace period
	//
	g := time.NewTimer(gracePeriod)
	k := time.NewTimer(gracePeriod + killBackoff)
	for {
		select {
		case <-g.C:
			log.Println("singleton-runner: grace period elapsed sending child the KILL signal")
			child.Process.Signal(syscall.SIGKILL)
		case <-k.C:
			log.Println("singleton-runner: child is still running ... something is very wrong!!!")
			return
		case <-exited:
			log.Println("singleton-runner: releasing lock after child stopped")
			releaseLock(cli, leaseID) // remove here if we do this using the defer above
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"strings"
	"sync"

	"code.google.com/p/go.crypto/ssh"
	"code.google.com/p/gopass"

	"github.com/sudharsh/henchman/lib"
)

func currentUsername() *user.User {
	u, err := user.Current()
	if err != nil {
		panic("Couldn't get current username: " + err.Error())
	}
	return u
}

func defaultKeyFile() string {
	u := currentUsername()
	return path.Join(u.HomeDir, ".ssh", "id_rsa")
}

func parseExtraArgs(args string) map[string]string {
	extraArgs := make(map[string]string)
	if args == "" {
		return extraArgs
	}
	for _, a := range strings.Split(args, " ") {
		kv := strings.Split(a, "=")
		extraArgs[kv[0]] = kv[1]
	}
	return extraArgs
}

func main() {
	username := flag.String("user", currentUsername().Username, "User to run as")
	usePassword := flag.Bool("password", false, "Use password authentication")
	keyfile := flag.String("private-keyfile", defaultKeyFile(), "Path to the keyfile")
	extraArgs := flag.String("args", "", "Extra arguments for the plan")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [args] <plan>\n\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	planFile := flag.Arg(0)

	if planFile == "" {
		flag.Usage()
		os.Exit(1)
	}

	if *username == "" {
		fmt.Fprintf(os.Stderr, "Missing username")
		os.Exit(1)
	}

	var sshAuth ssh.AuthMethod
	var err error

	if *usePassword {
		var password string
		if password, err = gopass.GetPass("Password:"); err != nil {
			log.Fatalf("Couldn't get password: " + err.Error())
			os.Exit(1)
		}
		sshAuth, err = henchman.PasswordAuth(password)
	} else {
		sshAuth, err = henchman.ClientKeyAuth(*keyfile)
	}
	if err != nil {
		log.Fatalf("SSH Auth prep failed: " + err.Error())
	}
	config := &ssh.ClientConfig{
		User: *username,
		Auth: []ssh.AuthMethod{sshAuth},
	}

	planBuf, err := ioutil.ReadFile(planFile)
	if err != nil {
		log.Fatalf("Error reading plan - %s\n", planFile)
		os.Exit(1)
	}

	plan, err := henchman.NewPlan(planBuf, parseExtraArgs(*extraArgs))
	if err != nil {
		log.Fatalf("Couldn't read the plan: %s", err)
		os.Exit(1)
	}

	wg := new(sync.WaitGroup)
	machines := henchman.Machines(plan.Hosts, config)
	for _, _machine := range machines {
		machine := _machine
		wg.Add(1)
		go func() {
			defer wg.Done()
			for _, task := range plan.Tasks {
				task.Prepare(plan.Vars)
				status := machine.RunTask(&task)
				plan.SaveStatus(&task, status)
			}
		}()
	}
	wg.Wait()
	plan.PrintReport()
}
<commit_msg>Add modules path in cli args.<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"strings"
	"sync"

	"code.google.com/p/go.crypto/ssh"
	"code.google.com/p/gopass"

	"github.com/sudharsh/henchman/lib"
)

func currentUsername() *user.User {
	u, err := user.Current()
	if err != nil {
		panic("Couldn't get current username: " + err.Error())
	}
	return u
}

func defaultKeyFile() string {
	u := currentUsername()
	return path.Join(u.HomeDir, ".ssh", "id_rsa")
}

func parseExtraArgs(args string) map[string]string {
	extraArgs := make(map[string]string)
	if args == "" {
		return extraArgs
	}
	for _, a := range strings.Split(args, " ") {
		kv := strings.Split(a, "=")
		extraArgs[kv[0]] = kv[1]
	}
	return extraArgs
}

func validateModulesPath() (string, error) {
	_modulesDir := os.Getenv("HENCHMAN_MODULES_PATH")
	if _modulesDir == "" {
		cwd, _ := os.Getwd()
		_modulesDir = path.Join(cwd, "modules")
	}
	modulesDir := flag.String("modules", _modulesDir, "Path to the modules")
	_, err := os.Stat(*modulesDir)
	return *modulesDir, err
}

func main() {
	username := flag.String("user", currentUsername().Username, "User to run as")
	usePassword := flag.Bool("password", false, "Use password authentication")
	keyfile := flag.String("private-keyfile", defaultKeyFile(), "Path to the keyfile")
	extraArgs := flag.String("args", "", "Extra arguments for the plan")

	modulesDir, err := validateModulesPath()
	if err != nil {
		log.Fatalf("Couldn't stat modules path '%s'\n", modulesDir)
	}

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [args] <plan>\n\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	planFile := flag.Arg(0)
	if planFile == "" {
		flag.Usage()
		os.Exit(1)
	}

	if *username == "" {
		fmt.Fprintf(os.Stderr, "Missing username")
		os.Exit(1)
	}
	var sshAuth ssh.AuthMethod
	if *usePassword {
		var password string
		if password, err = gopass.GetPass("Password:"); err != nil {
			log.Fatalf("Couldn't get password: " + err.Error())
			os.Exit(1)
		}
		sshAuth, err = henchman.PasswordAuth(password)
	} else {
		sshAuth, err = henchman.ClientKeyAuth(*keyfile)
	}
	if err != nil {
		log.Fatalf("SSH Auth prep failed: " + err.Error())
	}
	config := &ssh.ClientConfig{
		User: *username,
		Auth: []ssh.AuthMethod{sshAuth},
	}

	planBuf, err := ioutil.ReadFile(planFile)
	if err != nil {
		log.Fatalf("Error reading plan - %s\n", planFile)
		os.Exit(1)
	}

	var plan *henchman.Plan
	plan, err = henchman.NewPlan(planBuf, parseExtraArgs(*extraArgs))
	if err != nil {
		log.Fatalf("Couldn't read the plan: %s", err)
		os.Exit(1)
	}

	wg := new(sync.WaitGroup)
	machines := henchman.Machines(plan.Hosts, config)
	for _, _machine := range machines {
		machine := _machine
		wg.Add(1)
		go func() {
			defer wg.Done()
			for _, task := range plan.Tasks {
				task.Prepare(plan.Vars)
				status := machine.RunTask(&task)
				plan.SaveStatus(&task, status)
			}
		}()
	}
	wg.Wait()
	plan.PrintReport()
}
<|endoftext|>


<commit_before>package main

import (
    "io"
    "os"
    "flag"
    "time"
    "strings"

    "github.com/op/go-logging"

    "golang.org/x/net/context"
    "github.com/coreos/etcd/client"
    "github.com/hashicorp/consul/api"
    "github.com/samuel/go-zookeeper/zk")

var (
    storeType = flag.String("storeType", "", "zookeeper, consul or etcd.")
    storeConnection = flag.String("storeConnection", "", "Key-value store connection string.")
    rootPath = flag.String("rootPath", "", "Scheduled workflow key-value store root path.")

    workflowPath = flag.String("workflowPath", "/opt/vamp/workflow", "Path to workflow files.")

    logo = flag.Bool("logo", true, "Show logo.")
    help = flag.Bool("help", false, "Print usage.")

    logger = createLogger()
)

func printLogo(version string) string {
    return `
██╗   ██╗ █████╗ ███╗   ███╗██████╗
██║   ██║██╔══██╗████╗ ████║██╔══██╗
██║   ██║███████║██╔████╔██║██████╔╝
╚██╗ ██╔╝██╔══██║██║╚██╔╝██║██╔═══╝
 ╚████╔╝ ██║  ██║██║ ╚═╝ ██║██║
  ╚═══╝  ╚═╝  ╚═╝╚═╝     ╚═╝╚═╝
                       workflow agent
                       version ` + version + `
                       by magnetic.io
                                      `
}

func createLogger() *logging.Logger {
    var logger = logging.MustGetLogger("vamp-workflow-agent")
    var backend = logging.NewLogBackend(io.Writer(os.Stdout), "", 0)
    backendFormatter := logging.NewBackendFormatter(backend, logging.MustStringFormatter(
        "%{color}%{time:15:04:05.000} %{shortpkg:.4s} %{level:.4s} ==> %{message} %{color:reset}",
    ))
    logging.SetBackend(backendFormatter)
    return logger
}

func main() {

    flag.Parse()

    if *logo {
        logger.Notice(printLogo("0.9.0"))
    }

    if *help {
        flag.Usage()
        return
    }

    if len(*storeType) == 0 {
        logger.Panic("Key-value store type not speciffed.")
        return
    }

    if len(*rootPath) == 0 {
        logger.Panic("Key-value store root key path not speciffed.")
        return
    }

    if len(*storeConnection) == 0 {
        logger.Panic("Key-value store connection not speciffed.")
        return
    }

    logger.Notice("Starting Vamp Workflow Agent")

    logger.Info("Key-value store type          : %s", *storeType)
    logger.Info("Key-value store connection    : %s", *storeConnection)
    logger.Info("Key-value store root key path : %s", *rootPath)
    logger.Info("Workflow file path            : %s", *workflowPath)

    workflow := *rootPath + "/workflow"
    logger.Info("Reading workflow from         : %s", workflow)

    response, err := readFromKeyValueStore(workflow)

    if err != nil {
        logger.Panic("Can't read the workflow: ", err)
        return
    }

    logger.Info("Value: %s", response)
}

func readFromKeyValueStore(path string) ([]byte, error) {
    if *storeType == "etcd" {
        return readFromEtcd(path)
    } else if *storeType == "consul" {
        return readFromConsul(path)
    } else if *storeType == "zookeeper" {
        return readFromZooKeeper(path)
    } else {
        logger.Panic("Key-value store type not supported: ", *storeType)
        return nil, nil
    }
}

func readFromEtcd(path string) ([]byte, error) {

    logger.Info("Initializing etcd connection: %s", *storeConnection)

    servers := strings.Split(*storeConnection, ",")
    cfg := client.Config{
        Endpoints:               servers,
        Transport:               client.DefaultTransport,
        HeaderTimeoutPerRequest: 5 * time.Second,
    }
    c, err := client.New(cfg)
    if err != nil {
        logger.Fatal(err)
        return nil, err
    }
    api := client.NewKeysAPI(c)

    logger.Info("etcd getting '%s' key value.", path)
    response, err := api.Get(context.Background(), path, nil)
    if err != nil {
        return nil, err
    } else {
        return []byte(response.Node.Value), nil
    }
}

func readFromConsul(path string) ([]byte, error) {

    logger.Info("Initializing Consul connection: %s", *storeConnection)

    conf := api.DefaultConfig()
    conf.Address = *storeConnection
    client, err := api.NewClient(conf)

    if err != nil {
        return nil, err
    }

    kv := client.KV()

    logger.Info("Consul getting '%s' key value.", path)
    pair, _, err := kv.Get(strings.TrimPrefix(path, "/"), nil)
    if err != nil {
        return nil, err
    }

    return pair.Value, nil
}

func readFromZooKeeper(path string) ([]byte, error) {

    logger.Info("Initializing ZooKeeper connection: %s", *storeConnection)

    servers := strings.Split(*storeConnection, ",")
    connection, _, err := zk.Connect(servers, 10 * time.Second)
    if err != nil {
        return nil, err
    }

    logger.Info("ZooKeeper getting '%s' key value.", path)
    response, _, err := connection.Get(path)
    if err != nil {
        return nil, err
    }

    return response, nil
}
<commit_msg>writing to workflow.js<commit_after>package main

import (
    "io"
    "os"
    "flag"
    "time"
    "strings"
    "io/ioutil"

    "github.com/op/go-logging"

    "golang.org/x/net/context"
    "github.com/coreos/etcd/client"
    "github.com/hashicorp/consul/api"
    "github.com/samuel/go-zookeeper/zk"
)

var (
    storeType = flag.String("storeType", "", "zookeeper, consul or etcd.")
    storeConnection = flag.String("storeConnection", "", "Key-value store connection string.")
    rootPath = flag.String("rootPath", "", "Scheduled workflow key-value store root path.")

    workflowPath = flag.String("workflowPath", "/opt/vamp/workflow", "Path to workflow files.")

    logo = flag.Bool("logo", true, "Show logo.")
    help = flag.Bool("help", false, "Print usage.")

    logger = createLogger()
)

func printLogo(version string) string {
    return `
██╗   ██╗ █████╗ ███╗   ███╗██████╗
██║   ██║██╔══██╗████╗ ████║██╔══██╗
██║   ██║███████║██╔████╔██║██████╔╝
╚██╗ ██╔╝██╔══██║██║╚██╔╝██║██╔═══╝
 ╚████╔╝ ██║  ██║██║ ╚═╝ ██║██║
  ╚═══╝  ╚═╝  ╚═╝╚═╝     ╚═╝╚═╝
                       workflow agent
                       version ` + version + `
                       by magnetic.io
                                      `
}

func createLogger() *logging.Logger {
    var logger = logging.MustGetLogger("vamp-workflow-agent")
    var backend = logging.NewLogBackend(io.Writer(os.Stdout), "", 0)
    backendFormatter := logging.NewBackendFormatter(backend, logging.MustStringFormatter(
        "%{color}%{time:15:04:05.000} %{shortpkg:.4s} %{level:.4s} ==> %{message} %{color:reset}",
    ))
    logging.SetBackend(backendFormatter)
    return logger
}

func main() {

    flag.Parse()

    if *logo {
        logger.Notice(printLogo("0.9.0"))
    }

    if *help {
        flag.Usage()
        return
    }

    if len(*storeType) == 0 {
        logger.Panic("Key-value store type not speciffed.")
        return
    }

    if len(*rootPath) == 0 {
        logger.Panic("Key-value store root key path not speciffed.")
        return
    }

    if len(*storeConnection) == 0 {
        logger.Panic("Key-value store connection not speciffed.")
        return
    }

    logger.Notice("Starting Vamp Workflow Agent")

    logger.Info("Key-value store type          : %s", *storeType)
    logger.Info("Key-value store connection    : %s", *storeConnection)
    logger.Info("Key-value store root key path : %s", *rootPath)
    logger.Info("Workflow file path            : %s", *workflowPath)

    workflowKey := *rootPath + "/workflow"
    logger.Info("Reading workflow from         : %s", workflowKey)

    content, err := readFromKeyValueStore(workflowKey)

    if err != nil {
        logger.Panic("Can't read the workflow: ", err)
        return
    }

    workflowFile := *workflowPath + "/workflow.js"
    logger.Info("Writing workflow to the file  : %s", workflowFile)
    err = ioutil.WriteFile(workflowFile, content, 0644)

    if err != nil {
        logger.Panic("Can't write to the workflow file: ", err)
        return
    }
}

func readFromKeyValueStore(path string) ([]byte, error) {
    if *storeType == "etcd" {
        return readFromEtcd(path)
    } else if *storeType == "consul" {
        return readFromConsul(path)
    } else if *storeType == "zookeeper" {
        return readFromZooKeeper(path)
    } else {
        logger.Panic("Key-value store type not supported: ", *storeType)
        return nil, nil
    }
}

func readFromEtcd(path string) ([]byte, error) {

    logger.Info("Initializing etcd connection: %s", *storeConnection)

    servers := strings.Split(*storeConnection, ",")
    cfg := client.Config{
        Endpoints:               servers,
        Transport:               client.DefaultTransport,
        HeaderTimeoutPerRequest: 5 * time.Second,
    }
    c, err := client.New(cfg)
    if err != nil {
        logger.Fatal(err)
        return nil, err
    }
    api := client.NewKeysAPI(c)

    logger.Info("etcd getting '%s' key value.", path)
    response, err := api.Get(context.Background(), path, nil)
    if err != nil {
        return nil, err
    } else {
        return []byte(response.Node.Value), nil
    }
}

func readFromConsul(path string) ([]byte, error) {

    logger.Info("Initializing Consul connection: %s", *storeConnection)

    conf := api.DefaultConfig()
    conf.Address = *storeConnection
    client, err := api.NewClient(conf)

    if err != nil {
        return nil, err
    }

    kv := client.KV()

    logger.Info("Consul getting '%s' key value.", path)
    pair, _, err := kv.Get(strings.TrimPrefix(path, "/"), nil)
    if err != nil {
        return nil, err
    }

    return pair.Value, nil
}

func readFromZooKeeper(path string) ([]byte, error) {

    logger.Info("Initializing ZooKeeper connection: %s", *storeConnection)

    servers := strings.Split(*storeConnection, ",")
    connection, _, err := zk.Connect(servers, 10 * time.Second)
    if err != nil {
        return nil, err
    }

    logger.Info("ZooKeeper getting '%s' key value.", path)
    response, _, err := connection.Get(path)
    if err != nil {
        return nil, err
    }

    return response, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/VojtechVitek/go-trello"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
	"github.com/pelletier/go-toml"
)

func setColor(c string) {
	if c == "red" {
		color.Set(color.FgRed)
	} else if c == "yellow" {
		color.Set(color.FgYellow)
	} else if c == "green" {
		color.Set(color.FgGreen)
	} else if c == "purple" {
		color.Set(color.FgMagenta)
	}
}

func main() {
	appKey := os.Getenv("TRELLO_APP_KEY")
	token := os.Getenv("TRELLO_TOKEN")
	username := os.Getenv("TRELLO_USER")

	if appKey == "" || token == "" || username == "" {
		home, err := homedir.Dir()
		if err != nil {
			log.Fatal(err)
		}

		config, err := toml.LoadFile(home + "/.trelloclirc")
		if err != nil {
			log.Fatal(err)
		}

		appKey = config.Get("app_key").(string)
		token = config.Get("token").(string)
		username = config.Get("username").(string)
	}

	trello, err := trello.NewAuthClient(appKey, &token)
	if err != nil {
		log.Fatal(err)
	}

	user, err := trello.Member(username)
	if err != nil {
		log.Fatal(err)
	}

	boards, err := user.Boards()
	if err != nil {
		log.Fatal(err)
	}

	for _, board := range boards {
		if board.Closed {
			continue
		}

		fmt.Printf("* %v (%v)\n", board.Name, board.ShortUrl)

		lists, err := board.Lists()
		if err != nil {
			log.Fatal(err)
		}

		for _, list := range lists {
			cards, _ := list.Cards()
			if len(cards) > 0 {
				fmt.Println("  *", list.Name)

				for _, card := range cards {
					for _, label := range card.Labels {
						setColor(label.Color)
						break
					}

					if card.Due != "" {
						t, _ := time.Parse(time.RFC3339Nano, card.Due)
						fmt.Printf("    * %s %s\n", t.Format("[2006-01-02]"), card.Name)
					} else {
						fmt.Printf("    * %s\n", card.Name)
					}

					color.Unset()
				}
			}
		}
	}
}
<commit_msg>Change print format more like Markdown<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/VojtechVitek/go-trello"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
	"github.com/pelletier/go-toml"
)

func setColor(c string) {
	if c == "red" {
		color.Set(color.FgRed)
	} else if c == "yellow" {
		color.Set(color.FgYellow)
	} else if c == "green" {
		color.Set(color.FgGreen)
	} else if c == "purple" {
		color.Set(color.FgMagenta)
	}
}

func main() {
	appKey := os.Getenv("TRELLO_APP_KEY")
	token := os.Getenv("TRELLO_TOKEN")
	username := os.Getenv("TRELLO_USER")

	if appKey == "" || token == "" || username == "" {
		home, err := homedir.Dir()
		if err != nil {
			log.Fatal(err)
		}

		config, err := toml.LoadFile(home + "/.trelloclirc")
		if err != nil {
			log.Fatal(err)
		}

		appKey = config.Get("app_key").(string)
		token = config.Get("token").(string)
		username = config.Get("username").(string)
	}

	trello, err := trello.NewAuthClient(appKey, &token)
	if err != nil {
		log.Fatal(err)
	}

	user, err := trello.Member(username)
	if err != nil {
		log.Fatal(err)
	}

	boards, err := user.Boards()
	if err != nil {
		log.Fatal(err)
	}

	for _, board := range boards {
		if board.Closed {
			continue
		}

		fmt.Printf("# %v (%v)\n", board.Name, board.ShortUrl)
		fmt.Println()

		lists, err := board.Lists()
		if err != nil {
			log.Fatal(err)
		}

		for _, list := range lists {
			cards, _ := list.Cards()
			if len(cards) > 0 {
				fmt.Println("*", list.Name)

				for _, card := range cards {
					for _, label := range card.Labels {
						setColor(label.Color)
						break
					}

					if card.Due != "" {
						t, _ := time.Parse(time.RFC3339Nano, card.Due)
						fmt.Printf("  * %s %s\n", t.Format("[2006-01-02]"), card.Name)
					} else {
						fmt.Printf("  * %s\n", card.Name)
					}

					color.Unset()
				}
			}
		}

		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/phayes/checkstyle"
)

var (
	regexGithub    = regexp.MustCompile("^g")
	regexBitbucket = regexp.MustCompile("^b")
	regexGitURI    = regexp.MustCompile("((git|ssh|http(s)?)|(git@[\\w\\.]+))(:(//)?)([\\w\\.@\\:/\\-~]+)(\\.git)(/)?")
)

var (
	cstyle                                                   = checkstyle.New()
	homeEnv                                                  = os.Getenv("HOME")
	gopathEnv                                                = os.Getenv("GOPATH")
	scrutinizerProjectEnv                                    = os.Getenv("SCRUTINIZER_PROJECT")
	projectFull, projectDomain, projectOwner, projectProject string
)

func main() {
	// Set up environment variables
	if gopathEnv == "" {
		gopathEnv = homeEnv + "/go"
	}

	// Set up project
	if len(scrutinizerProjectEnv) == 0 {
		log.Fatal("Not running without scrutinizer environment. SCRUTINIZER_PROJECT environment variable not found")
	}

	projectFull = regexGithub.ReplaceAllString(scrutinizerProjectEnv, "github.com")
	projectFull = regexBitbucket.ReplaceAllString(projectFull, "bitbucket.com")

	projectParts := strings.Split(projectFull, "/")
	if len(projectParts) != 3 {
		log.Fatal("Malformed SCRUTINIZER_PROJECT environment variable.")
	}
	projectDomain = projectParts[0]
	projectOwner = projectParts[1]
	projectProject = projectParts[2]

	// Install all dependencies
	cmd := exec.Command("go", "get", "-t", "./...")
	output, err := cmd.Output()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if ok && len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Println(output)
		log.Fatal("go get -t ./...", err)
	}

	// Run metalinter
	metalinter()

	// Run tests and coverage
	testAndCoverage()
}

func metalinter() {
	goMetaLinterCmd := gopathEnv + "/bin/gometalinter"

	// Install gometalinter -- no-op if already installed
	cmd := exec.Command("go", "get", "github.com/alecthomas/gometalinter")
	_, err := cmd.Output()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if ok && len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal("go get github.com/alecthomas/gometalinter", err)
	}

	// Install all gometalinter dependencies -- no-op if already installed
	cmd = exec.Command(goMetaLinterCmd, "--install")
	_, err = cmd.Output()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if ok && len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal(goMetaLinterCmd, "--install", err)
	}

	// Configure the metalinter
	if _, err := os.Stat("go-scrutinize.config"); os.IsNotExist(err) {
		cmd = exec.Command(goMetaLinterCmd, "./...", "--checkstyle")
	} else {
		cmd = exec.Command(goMetaLinterCmd, "./...", "--checkstyle", "--config=go-scrutinize.config")
	}

	// Run the metalinter -- note that will return non-zero exit status
	out, err := cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
	}

	// Write the output from the metalinter
	ioutil.WriteFile("checkstyle_report.xml", out, os.ModePerm)
}

func testAndCoverage() {
	goConvCmd := gopathEnv + "/bin/gocov"
	goConvXMLCmd := gopathEnv + "/bin/gocov-xml"

	// Install the coverage tool covov
	cmd := exec.Command("go", "get", "github.com/axw/gocov/...")
	_, err := cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal("go", "get", "github.com/axw/gocov/...", err)
	}

	// Install the coverage file translation tool gocov-xml
	cmd = exec.Command("go", "get", "github.com/AlekSi/gocov-xml")
	_, err = cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal("go", "get", "github.com/AlekSi/gocov-xml", err)
	}

	// Run tests with coverage
	cmd = exec.Command(goConvCmd, "test", "./...", "-race")
	gocovout, err := cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal(goConvCmd, "test", "./...", "-race", err)
	}

	// Convert to clover format
	cmd = exec.Command(goConvXMLCmd)
	cmd.Stdin = bytes.NewReader(gocovout)
	xmlout, err := cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal(goConvXMLCmd, err)
	}

	// Rewrite all filenames to use /home/scrutinizer/build paths
	coveragexml := strings.Replace(string(xmlout), gopathEnv+"/src/"+projectFull, "/home/scrutinizer/build", 0)

	// Write the output from the metalinter
	ioutil.WriteFile("coverage.xml", []byte(coveragexml), os.ModePerm)

}
<commit_msg>Pipe stderr for running tests<commit_after>package main

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/phayes/checkstyle"
)

var (
	regexGithub    = regexp.MustCompile("^g")
	regexBitbucket = regexp.MustCompile("^b")
	regexGitURI    = regexp.MustCompile("((git|ssh|http(s)?)|(git@[\\w\\.]+))(:(//)?)([\\w\\.@\\:/\\-~]+)(\\.git)(/)?")
)

var (
	cstyle                                                   = checkstyle.New()
	homeEnv                                                  = os.Getenv("HOME")
	gopathEnv                                                = os.Getenv("GOPATH")
	scrutinizerProjectEnv                                    = os.Getenv("SCRUTINIZER_PROJECT")
	projectFull, projectDomain, projectOwner, projectProject string
)

func main() {
	// Set up environment variables
	if gopathEnv == "" {
		gopathEnv = homeEnv + "/go"
	}

	// Set up project
	if len(scrutinizerProjectEnv) == 0 {
		log.Fatal("Not running without scrutinizer environment. SCRUTINIZER_PROJECT environment variable not found")
	}

	projectFull = regexGithub.ReplaceAllString(scrutinizerProjectEnv, "github.com")
	projectFull = regexBitbucket.ReplaceAllString(projectFull, "bitbucket.com")

	projectParts := strings.Split(projectFull, "/")
	if len(projectParts) != 3 {
		log.Fatal("Malformed SCRUTINIZER_PROJECT environment variable.")
	}
	projectDomain = projectParts[0]
	projectOwner = projectParts[1]
	projectProject = projectParts[2]

	// Install all dependencies
	cmd := exec.Command("go", "get", "-t", "./...")
	output, err := cmd.Output()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if ok && len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Println(output)
		log.Fatal("go get -t ./...", err)
	}

	// Run metalinter
	metalinter()

	// Run tests and coverage
	testAndCoverage()
}

func metalinter() {
	goMetaLinterCmd := gopathEnv + "/bin/gometalinter"

	// Install gometalinter -- no-op if already installed
	cmd := exec.Command("go", "get", "github.com/alecthomas/gometalinter")
	_, err := cmd.Output()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if ok && len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal("go get github.com/alecthomas/gometalinter", err)
	}

	// Install all gometalinter dependencies -- no-op if already installed
	cmd = exec.Command(goMetaLinterCmd, "--install")
	_, err = cmd.Output()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if ok && len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal(goMetaLinterCmd, "--install", err)
	}

	// Configure the metalinter
	if _, err := os.Stat("go-scrutinize.config"); os.IsNotExist(err) {
		cmd = exec.Command(goMetaLinterCmd, "./...", "--checkstyle")
	} else {
		cmd = exec.Command(goMetaLinterCmd, "./...", "--checkstyle", "--config=go-scrutinize.config")
	}

	// Run the metalinter -- note that will return non-zero exit status
	out, err := cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
	}

	// Write the output from the metalinter
	ioutil.WriteFile("checkstyle_report.xml", out, os.ModePerm)
}

func testAndCoverage() {
	goConvCmd := gopathEnv + "/bin/gocov"
	goConvXMLCmd := gopathEnv + "/bin/gocov-xml"

	// Install the coverage tool covov
	cmd := exec.Command("go", "get", "github.com/axw/gocov/...")
	_, err := cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal("go", "get", "github.com/axw/gocov/...", err)
	}

	// Install the coverage file translation tool gocov-xml
	cmd = exec.Command("go", "get", "github.com/AlekSi/gocov-xml")
	_, err = cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal("go", "get", "github.com/AlekSi/gocov-xml", err)
	}

	// Run tests with coverage
	cmd = exec.Command(goConvCmd, "test", "./...", "-race")
	cmd.Stderr = os.Stderr // pipe stderr directly
	gocovout, err := cmd.Output()
	if err != nil {
		log.Fatal(goConvCmd, "test", "./...", "-race", err)
	}

	// Convert to clover format
	cmd = exec.Command(goConvXMLCmd)
	cmd.Stdin = bytes.NewReader(gocovout)
	xmlout, err := cmd.Output()
	if err != nil {
		exitErr := err.(*exec.ExitError)
		if len(exitErr.Stderr) != 0 {
			log.Println(string(exitErr.Stderr))
		}
		log.Fatal(goConvXMLCmd, err)
	}

	// Rewrite all filenames to use /home/scrutinizer/build paths
	coveragexml := strings.Replace(string(xmlout), gopathEnv+"/src/"+projectFull, "/home/scrutinizer/build", 0)

	// Write the output from the metalinter
	ioutil.WriteFile("coverage.xml", []byte(coveragexml), os.ModePerm)

}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Betalo AB
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"syscall"
	"time"
)

func main() {
	var (
		forceFlag   = flag.Bool("f", false, "Force running the command even after giving up")
		timeoutFlag = flag.Duration("t", 1*time.Minute, "Timeout duration before giving up")
		verboseFlag = flag.Bool("v", false, "Set verbose output")
		quietFlag   = flag.Bool("q", false, "Set quiet mode")
	)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: await [options...] <res>... [ -- <cmd>]")
		fmt.Fprintln(os.Stderr, "Await availability of resources.")
		fmt.Fprintln(os.Stderr)
		flag.PrintDefaults()
	}
	flag.Parse()

	var logLevel int
	switch {
	case *quietFlag:
		logLevel = silentLevel
	case *verboseFlag:
		logLevel = infoLevel
	default:
		logLevel = errorLevel
	}

	logger := NewLogger(logLevel)
	resArgs, cmdArgs := splitArgs(flag.Args())

	ress, err := parseResources(resArgs)
	if err != nil {
		logger.Fatalln("Error: failed to parse resources: %v", err)
	}

	awaiter := &awaiter{
		logger:  logger,
		timeout: *timeoutFlag,
	}

	if err := awaiter.run(ress); err != nil {
		if e, ok := err.(*unavailabilityError); ok {
			logger.Infof("Resource unavailable: %v", e)
			logger.Infoln("Timeout exceeded")
		} else {
			logger.Fatalln("Error: %v", err)
		}
		if !*forceFlag {
			os.Exit(1)
		}
	} else {
		logger.Infoln("All resources available")
	}

	if len(cmdArgs) > 0 {
		logger.Infof("Runnning command: %v", cmdArgs)
		if err := execCmd(cmdArgs); err != nil {
			logger.Fatalf("Error: failed to execute command: %v", err)
		}
	}
}

func splitArgs(args []string) ([]string, []string) {
	if i := indexOf(args, "--"); i >= 0 {
		return args[0:i], args[i+1:]
	}

	// We haven't seen a resource|command separator ('--'). This can either be
	// because of Go's flag parser removing the separator if no args were given,
	// or because there actually was none given.
	// Fallback to the original (unparsed) flag argument list and see if a
	// separator was given there and if, assume all arguments given are part of
	// the command, i.e. no resources at all were provided.
	if i := indexOf(os.Args, "--"); i >= 0 {
		return []string{}, args
	}

	// We still haven't seen a resource|command separator ('--'). Now finally
	// assume because there actually was none given and use all arguments as
	// resources.
	return args, []string{}
}

func indexOf(l []string, s string) int {
	for i, e := range l {
		if e == "--" {
			return i
		}
	}
	return -1
}

func execCmd(cmdArgs []string) error {
	path, err := exec.LookPath(cmdArgs[0])
	if err != nil {
		return err
	}
	return syscall.Exec(path, cmdArgs, os.Environ())
}
<commit_msg>Add flag to set output mode to extra verbose<commit_after>// Copyright (c) 2016 Betalo AB
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"syscall"
	"time"
)

func main() {
	var (
		forceFlag    = flag.Bool("f", false, "Force running the command even after giving up")
		timeoutFlag  = flag.Duration("t", 1*time.Minute, "Timeout duration before giving up")
		verbose1Flag = flag.Bool("v", false, "Set verbose output mode")
		verbose2Flag = flag.Bool("vv", false, "Set more verbose output mode")
		quietFlag    = flag.Bool("q", false, "Set quiet mode")
	)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: await [options...] <res>... [ -- <cmd>]")
		fmt.Fprintln(os.Stderr, "Await availability of resources.")
		fmt.Fprintln(os.Stderr)
		flag.PrintDefaults()
	}
	flag.Parse()

	var logLevel int
	switch {
	case *quietFlag:
		logLevel = silentLevel
	case *verbose1Flag:
		logLevel = infoLevel
	case *verbose2Flag:
		logLevel = debugLevel
	default:
		logLevel = errorLevel
	}

	logger := NewLogger(logLevel)
	resArgs, cmdArgs := splitArgs(flag.Args())

	ress, err := parseResources(resArgs)
	if err != nil {
		logger.Fatalln("Error: failed to parse resources: %v", err)
	}

	awaiter := &awaiter{
		logger:  logger,
		timeout: *timeoutFlag,
	}

	if err := awaiter.run(ress); err != nil {
		if e, ok := err.(*unavailabilityError); ok {
			logger.Infof("Resource unavailable: %v", e)
			logger.Infoln("Timeout exceeded")
		} else {
			logger.Fatalln("Error: %v", err)
		}
		if !*forceFlag {
			os.Exit(1)
		}
	} else {
		logger.Infoln("All resources available")
	}

	if len(cmdArgs) > 0 {
		logger.Infof("Runnning command: %v", cmdArgs)
		if err := execCmd(cmdArgs); err != nil {
			logger.Fatalf("Error: failed to execute command: %v", err)
		}
	}
}

func splitArgs(args []string) ([]string, []string) {
	if i := indexOf(args, "--"); i >= 0 {
		return args[0:i], args[i+1:]
	}

	// We haven't seen a resource|command separator ('--'). This can either be
	// because of Go's flag parser removing the separator if no args were given,
	// or because there actually was none given.
	// Fallback to the original (unparsed) flag argument list and see if a
	// separator was given there and if, assume all arguments given are part of
	// the command, i.e. no resources at all were provided.
	if i := indexOf(os.Args, "--"); i >= 0 {
		return []string{}, args
	}

	// We still haven't seen a resource|command separator ('--'). Now finally
	// assume because there actually was none given and use all arguments as
	// resources.
	return args, []string{}
}

func indexOf(l []string, s string) int {
	for i, e := range l {
		if e == "--" {
			return i
		}
	}
	return -1
}

func execCmd(cmdArgs []string) error {
	path, err := exec.LookPath(cmdArgs[0])
	if err != nil {
		return err
	}
	return syscall.Exec(path, cmdArgs, os.Environ())
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/gorilla/websocket"
	"github.com/olorin/nagiosplugin"
	"log"
	"net/url"
)

var addr = flag.String("addr", "echo.websocket.org", "http service address")

func main() {
	flag.Parse()

	u := url.URL{Scheme: "wss", Host: *addr, Path: "/"}

	// Initialize the check - this will return an UNKNOWN result
	// until more results are added.
	check := nagiosplugin.NewCheck()
	// If we exit early or panic() we'll still output a result.
	defer check.Finish()

	// Connect to the server
	log.Printf("Connecting to %s", u.String())
	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		check.Criticalf("Cannot connect to websocket")
		log.Fatal("dial:", err)
	} else {
		log.Println("connected")
	}
	defer c.Close()

	// close the connection
	log.Println("Requesting connection closure")
	err = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
	if err != nil {
		log.Println("write close:", err)
		check.Criticalf("Error while closing websocket")
		return
	} else {
		check.AddResult(nagiosplugin.OK, "Websocket server is working correctly")
	}

}
<commit_msg>added flags for all URL components (scheme+path)<commit_after>package main

import (
	"flag"
	"github.com/gorilla/websocket"
	"github.com/olorin/nagiosplugin"
	"log"
	"net/url"
)

var scheme = flag.String("scheme", "wss", "Websocket scheme (default to secure)")
var addr = flag.String("addr", "echo.websocket.org", "http service address")
var path = flag.String("path", "/", "URI path")

func main() {
	flag.Parse()

	u := url.URL{Scheme: *scheme, Host: *addr, Path: *path}

	// Initialize the check - this will return an UNKNOWN result
	// until more results are added.
	check := nagiosplugin.NewCheck()
	// If we exit early or panic() we'll still output a result.
	defer check.Finish()

	// Connect to the server
	log.Printf("Connecting to %s", u.String())
	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		check.Criticalf("Cannot connect to websocket")
		log.Fatal("dial:", err)
	} else {
		log.Println("connected")
	}
	defer c.Close()

	// close the connection
	log.Println("Requesting connection closure")
	err = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
	if err != nil {
		log.Println("write close:", err)
		check.Criticalf("Error while closing websocket")
		return
	} else {
		check.AddResult(nagiosplugin.OK, "Websocket server is working correctly")
	}

}
<|endoftext|>


<commit_before>package main

import (
	"log"

	r "github.com/dancannon/gorethink"
	"github.com/david-torres/duelyst-casual/controllers"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/standard"
	middleware "github.com/labstack/echo/middleware"
	"github.com/spf13/viper"
)

// Config allows config values can be accessed
var Config = viper.New()

func init() {
	// read in configurations
	Config.SetConfigName("config")
	Config.AddConfigPath("$GOPATH/src/github.com/david-torres/duelyst-casual/configs")
	err := Config.ReadInConfig()
	if err != nil {
		log.Fatalln(err.Error())
	}
}

func main() {
	// init the web server
	e := echo.New()

	// init app-wide middleware
	e.Pre(middleware.RemoveTrailingSlash())
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	e.Use(middleware.Gzip())

	// security measures
	e.Use(middleware.BodyLimit("2M")) // sets maximum request body size
	e.Use(middleware.CSRF())          // default protection against session riding
	e.Use(middleware.Secure())        // default protection against XSS, content sniffing, clickjacking, and other infections

	// init static assets
	e.Static("/assets", "assets")

	// init db
	session, err := r.Connect(r.ConnectOpts{
		Address:  Config.GetString("db.host"),
		Database: Config.GetString("db.database"),
	})

	if err != nil {
		// db is down, die
		log.Fatalln(err.Error())
	}

	// routes
	e.File("/", "public/index.html")
	e.GET("/ws", standard.WrapHandler(controllers.Socket(session)))

	// start the server
	e.Run(standard.New(":3000"))
}
<commit_msg>create db and tables if needed<commit_after>package main

import (
	"log"

	r "github.com/dancannon/gorethink"
	"github.com/david-torres/duelyst-casual/controllers"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/standard"
	middleware "github.com/labstack/echo/middleware"
	"github.com/spf13/viper"
)

// Config allows config values can be accessed
var Config = viper.New()

func init() {
	// read in configurations
	Config.SetConfigName("config")
	Config.AddConfigPath("$GOPATH/src/github.com/david-torres/duelyst-casual/configs")
	err := Config.ReadInConfig()
	if err != nil {
		log.Fatalln(err.Error())
	}
}

func main() {
	// init the web server
	e := echo.New()

	// init app-wide middleware
	e.Pre(middleware.RemoveTrailingSlash())
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	e.Use(middleware.Gzip())

	// security measures
	e.Use(middleware.BodyLimit("2M")) // sets maximum request body size
	e.Use(middleware.CSRF())          // default protection against session riding
	e.Use(middleware.Secure())        // default protection against XSS, content sniffing, clickjacking, and other infections

	// init static assets
	e.Static("/assets", "assets")

	// init db
	host := Config.GetString("db.host")
	database := Config.GetString("db.database")

	session, err := r.Connect(r.ConnectOpts{
		Address:  host,
		Database: database,
	})

	result, err := r.DBList().Contains(database).Run(session)
	if err != nil {
		log.Fatalf("error checking if database exists %s", err)
	}

	var dbExists bool
	result.One(&dbExists)

	if !dbExists {
		log.Println("Creating db and tables")
		r.DBCreate(database).RunWrite(session)
		r.TableCreate("games").RunWrite(session)
	}

	// routes
	e.File("/", "public/index.html")
	e.GET("/ws", standard.WrapHandler(controllers.Socket(session)))

	// start the server
	e.Run(standard.New(":3000"))
}
<|endoftext|>