<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/node"
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/version"
	"github.com/spf13/viper"
)

type nodeProcess struct {
	path     string
	errChan  chan error
	exitCode int
	cmd      *exec.Cmd
}

// Returns a new nodeProcess running the binary at [path].
// Returns an error if the command fails to start.
// When the nodeProcess terminates, the returned error (which may be nil)
// is sent on [n.errChan]
func startNode(path string, args []string, printToStdOut bool) (*nodeProcess, error) {
	fmt.Printf("Starting binary at %s with args %s\n", path, args) // TODO remove
	n := &nodeProcess{
		path:    path,
		cmd:     exec.Command(path, args...), // #nosec G204
		errChan: make(chan error, 1),
	}
	if printToStdOut {
		n.cmd.Stdout = os.Stdout
		n.cmd.Stderr = os.Stderr
	}

	// Start the nodeProcess
	if err := n.cmd.Start(); err != nil {
		return nil, err
	}
	go func() {
		// Wait for the nodeProcess to stop.
		// When it does, set the exit code and send the returned error
		// (which may be nil) to [a.errChain]
		if err := n.cmd.Wait(); err != nil {
			if exitError, ok := err.(*exec.ExitError); ok {
				// This code only executes if the exit code is non-zero
				n.exitCode = exitError.ExitCode()
			}
			n.errChan <- err
		}
	}()
	return n, nil
}

func (a *nodeProcess) kill() error {
	if a.cmd.Process == nil {
		return nil
	}
	// Stop printing output from node
	a.cmd.Stdout = ioutil.Discard
	a.cmd.Stderr = ioutil.Discard
	//todo change this to interrupt?
	err := a.cmd.Process.Kill() // todo kill subprocesses
	if err != nil && err != os.ErrProcessDone {
		return fmt.Errorf("failed to kill process: %w", err)
	}
	return nil
}

type binaryManager struct {
	rootPath string
	log      logging.Logger
}

func newBinaryManager(path string, log logging.Logger) *binaryManager {
	return &binaryManager{
		rootPath: path,
		log:      log,
	}
}

// Run two nodes at once: one is a version before the database upgrade and the other after.
// The latter will bootstrap from the former. Its staking port and HTTP port are 2
// greater than the staking/HTTP ports in [v].
// When the new node version is done bootstrapping, both nodes are stopped.
// Returns nil if the new node version successfully bootstrapped.
func (b *binaryManager) runMigration(v *viper.Viper, nodeConfig node.Config) error {
	prevVersionNode, err := b.runPreviousVersion(previousVersion, v)
	if err != nil {
		return fmt.Errorf("couldn't start old version during migration: %w", err)
	}
	defer func() {
		if err := prevVersionNode.kill(); err != nil {
			b.log.Error("error while killing previous version: %w", err)
		}
	}()

	currentVersionNode, err := b.runCurrentVersion(v, true, nodeConfig.NodeID)
	if err != nil {
		return fmt.Errorf("couldn't start current version during migration: %w", err)
	}
	defer func() {
		if err := currentVersionNode.kill(); err != nil {
			b.log.Error("error while killing current version: %w", err)
		}
	}()

	for {
		select {
		case err := <-prevVersionNode.errChan:
			if err != nil {
				return fmt.Errorf("previous version died with exit code %d", prevVersionNode.exitCode)
			}
			if prevVersionNode.exitCode == constants.ExitCodeDoneMigrating {
				return nil
			}
			// TODO restart here
		case err := <-currentVersionNode.errChan:
			if err != nil {
				return fmt.Errorf("current version died with exit code %d", currentVersionNode.exitCode)
			}
		}
	}
}

func (b *binaryManager) runNormal(v *viper.Viper) error {
	node, err := b.runCurrentVersion(v, false, ids.ShortID{})
	if err != nil {
		return fmt.Errorf("couldn't start old version during migration: %w", err)
	}
	return <-node.errChan
}

func (b *binaryManager) runPreviousVersion(prevVersion version.Version, v *viper.Viper) (*nodeProcess, error) {
	binaryPath := getBinaryPath(b.rootPath, prevVersion)
	args := []string{}
	for k, v := range v.AllSettings() {
		if k == "fetch-only" { // TODO replace with const
			continue
		}
		args = append(args, fmt.Sprintf("--%s=%v", k, v))
	}
	return startNode(binaryPath, args, false)
}

func (b *binaryManager) runCurrentVersion(
	v *viper.Viper,
	fetchOnly bool,
	fetchFrom ids.ShortID,
) (*nodeProcess, error) {
	argsMap := v.AllSettings()
	if fetchOnly {
		// TODO use constants for arg names here
		stakingPort, err := strconv.Atoi(argsMap["staking-port"].(string))
		if err != nil {
			return nil, fmt.Errorf("couldn't parse staking port as int: %w", err)
		}
		argsMap["bootstrap-ips"] = fmt.Sprintf("127.0.0.1:%d", stakingPort)
		argsMap["bootstrap-ids"] = fmt.Sprintf("%s%s", constants.NodeIDPrefix, fetchFrom)
		argsMap["staking-port"] = stakingPort + 2

		httpPort, err := strconv.Atoi(argsMap["http-port"].(string))
		if err != nil {
			return nil, fmt.Errorf("couldn't parse staking port as int: %w", err)
		}
		argsMap["http-port"] = httpPort + 2
		argsMap["fetch-only"] = true
	}
	args := []string{}
	for k, v := range argsMap {
		args = append(args, fmt.Sprintf("--%s=%v", k, v))
	}
	binaryPath := getBinaryPath(b.rootPath, currentVersion)
	return startNode(binaryPath, args, true)
}

func getBinaryPath(rootPath string, nodeVersion version.Version) string {
	return fmt.Sprintf(
		"%s/build/avalanchego-%s/avalanchego-inner",
		rootPath,
		nodeVersion,
	)
}
<commit_msg>fix handling when node stops during migration<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/node"
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/version"
	"github.com/spf13/viper"
)

type nodeProcess struct {
	path     string
	errChan  chan error
	exitCode int
	cmd      *exec.Cmd
}

// Returns a new nodeProcess running the binary at [path].
// Returns an error if the command fails to start.
// When the nodeProcess terminates, the returned error (which may be nil)
// is sent on [n.errChan]
func startNode(path string, args []string, printToStdOut bool) (*nodeProcess, error) {
	fmt.Printf("Starting binary at %s with args %s\n", path, args) // TODO remove
	n := &nodeProcess{
		path:    path,
		cmd:     exec.Command(path, args...), // #nosec G204
		errChan: make(chan error, 1),
	}
	if printToStdOut {
		n.cmd.Stdout = os.Stdout
		n.cmd.Stderr = os.Stderr
	}

	// Start the nodeProcess
	if err := n.cmd.Start(); err != nil {
		return nil, err
	}
	go func() {
		// Wait for the nodeProcess to stop.
		// When it does, set the exit code and send the returned error
		// (which may be nil) to [a.errChain]
		if err := n.cmd.Wait(); err != nil {
			if exitError, ok := err.(*exec.ExitError); ok {
				// This code only executes if the exit code is non-zero
				n.exitCode = exitError.ExitCode()
			}
			n.errChan <- err
		}
	}()
	return n, nil
}

func (a *nodeProcess) kill() error {
	if a.cmd.Process == nil {
		return nil
	}
	// Stop printing output from node
	a.cmd.Stdout = ioutil.Discard
	a.cmd.Stderr = ioutil.Discard
	//todo change this to interrupt?
	err := a.cmd.Process.Kill() // todo kill subprocesses
	if err != nil && err != os.ErrProcessDone {
		return fmt.Errorf("failed to kill process: %w", err)
	}
	return nil
}

type binaryManager struct {
	rootPath string
	log      logging.Logger
}

func newBinaryManager(path string, log logging.Logger) *binaryManager {
	return &binaryManager{
		rootPath: path,
		log:      log,
	}
}

// Run two nodes at once: one is a version before the database upgrade and the other after.
// The latter will bootstrap from the former. Its staking port and HTTP port are 2
// greater than the staking/HTTP ports in [v].
// When the new node version is done bootstrapping, both nodes are stopped.
// Returns nil if the new node version successfully bootstrapped.
func (b *binaryManager) runMigration(v *viper.Viper, nodeConfig node.Config) error {
	prevVersionNode, err := b.runPreviousVersion(previousVersion, v)
	if err != nil {
		return fmt.Errorf("couldn't start old version during migration: %w", err)
	}
	defer func() {
		if err := prevVersionNode.kill(); err != nil {
			b.log.Error("error while killing previous version: %w", err)
		}
	}()

	currentVersionNode, err := b.runCurrentVersion(v, true, nodeConfig.NodeID)
	if err != nil {
		return fmt.Errorf("couldn't start current version during migration: %w", err)
	}
	defer func() {
		if err := currentVersionNode.kill(); err != nil {
			b.log.Error("error while killing current version: %w", err)
		}
	}()

	for {
		select {
		case err := <-prevVersionNode.errChan:
			if err != nil {
				return fmt.Errorf("previous version died with exit code %d", prevVersionNode.exitCode)
			} else if prevVersionNode.exitCode != constants.ExitCodeDoneMigrating {
				return fmt.Errorf("expected error code %d (done with migration) but got %d", constants.ExitCodeDoneMigrating, prevVersionNode.exitCode)
			}
			return nil
		case err := <-currentVersionNode.errChan:
			return fmt.Errorf("current version died with exit code %d and error: %w", currentVersionNode.exitCode, err)
		}
	}
}

func (b *binaryManager) runNormal(v *viper.Viper) error {
	node, err := b.runCurrentVersion(v, false, ids.ShortID{})
	if err != nil {
		return fmt.Errorf("couldn't start old version during migration: %w", err)
	}
	return <-node.errChan
}

func (b *binaryManager) runPreviousVersion(prevVersion version.Version, v *viper.Viper) (*nodeProcess, error) {
	binaryPath := getBinaryPath(b.rootPath, prevVersion)
	args := []string{}
	for k, v := range v.AllSettings() {
		if k == "fetch-only" { // TODO replace with const
			continue
		}
		args = append(args, fmt.Sprintf("--%s=%v", k, v))
	}
	return startNode(binaryPath, args, false)
}

func (b *binaryManager) runCurrentVersion(
	v *viper.Viper,
	fetchOnly bool,
	fetchFrom ids.ShortID,
) (*nodeProcess, error) {
	argsMap := v.AllSettings()
	if fetchOnly {
		// TODO use constants for arg names here
		stakingPort, err := strconv.Atoi(argsMap["staking-port"].(string))
		if err != nil {
			return nil, fmt.Errorf("couldn't parse staking port as int: %w", err)
		}
		argsMap["bootstrap-ips"] = fmt.Sprintf("127.0.0.1:%d", stakingPort)
		argsMap["bootstrap-ids"] = fmt.Sprintf("%s%s", constants.NodeIDPrefix, fetchFrom)
		argsMap["staking-port"] = stakingPort + 2

		httpPort, err := strconv.Atoi(argsMap["http-port"].(string))
		if err != nil {
			return nil, fmt.Errorf("couldn't parse staking port as int: %w", err)
		}
		argsMap["http-port"] = httpPort + 2
		argsMap["fetch-only"] = true
	}
	args := []string{}
	for k, v := range argsMap {
		args = append(args, fmt.Sprintf("--%s=%v", k, v))
	}
	binaryPath := getBinaryPath(b.rootPath, currentVersion)
	return startNode(binaryPath, args, true)
}

func getBinaryPath(rootPath string, nodeVersion version.Version) string {
	return fmt.Sprintf(
		"%s/build/avalanchego-%s/avalanchego-inner",
		rootPath,
		nodeVersion,
	)
}
<|endoftext|>


<commit_before>package agent

import (
	"github.com/golang/glog"
	"github.com/romana/core/common"
	"github.com/romana/core/pkg/util/firewall"
	"sync"
)

// agentStore is a backing storage. Agent will likely use
// sqlite which is not very reliable in concurrent access scenario,
// so we are going to guard access with mutex.
type agentStore struct {
	common.DbStore
	mu *sync.Mutex
}

// GetDb implements firewall.FirewallStore
func (agentStore agentStore) GetDb() common.DbStore {
	return agentStore.DbStore
}

// GetMutex implements firewall.FirewallStore
func (agentStore agentStore) GetMutex() *sync.Mutex {
	return agentStore.mu
}

// Entities implements Entities method of
// Service interface.
func (agentStore *agentStore) Entities() []interface{} {
	retval := make([]interface{}, 3)
	retval[0] = new(Route)
	retval[1] = new(NetworkInterface)
	retval[2] = new(firewall.IPtablesRule)
	return retval
}

// NewStore returns initialized agentStore.
func NewStore(config common.ServiceConfig) *agentStore {
	storeConfig := config.ServiceSpecific["store"].(map[string]interface{})
	store := agentStore{
		mu: &sync.Mutex{},
	}
	store.ServiceStore = &store
	store.SetConfig(storeConfig)

	return &store
}

// Route is a model to store managed routes
type Route struct {
	ID     uint64 `sql:"AUTO_INCREMENT"`
	IP     string
	Mask   string
	Kind   targetKind
	Spec   string
	Status string
}

// targetKind is a an IP route destination type.
type targetKind string

const (
	device  targetKind = "dev"
	gateway targetKind = "gw"
)

// NetworkInterface is a model to store managed network interfaces.
type NetworkInterface struct {
	ID     uint64 `sql:"AUTO_INCREMENT"`
	Name   string
	Status string
}

// CreateSchemaPostProcess implements CreateSchemaPostProcess method of
// Service interface.
func (agentStore *agentStore) CreateSchemaPostProcess() error {
	return nil
}

func (agentStore *agentStore) deleteNetworkInterface(iface *NetworkInterface) error {
	glog.V(1).Info("Acquiring store mutex for deleteNetworkInterface")
	agentStore.mu.Lock()
	defer func() {
		glog.V(1).Info("Releasing store mutex for deleteNetworkInterface")
		agentStore.mu.Unlock()
	}()
	glog.V(1).Info("Acquired store mutex for deleteNetworkInterface")

	db := agentStore.DbStore.Db
	agentStore.DbStore.Db.Delete(iface)
	err := common.MakeMultiError(db.GetErrors())
	if err != nil {
		return err
	}
	if db.Error != nil {
		return db.Error
	}

	return nil
}

func (agentStore *agentStore) findNetworkInterface(ifaceName string) (*NetworkInterface, error) {
	glog.V(1).Info("Acquiring store mutex for findNetworkInterface")
	agentStore.mu.Lock()
	defer func() {
		glog.V(1).Info("Releasing store mutex for findNetworkInterface")
		agentStore.mu.Unlock()
	}()
	glog.V(1).Info("Acquired store mutex for findNetworkInterface")

	var iface NetworkInterface
	db := agentStore.DbStore.Db
	agentStore.DbStore.Db.Where("name = ?", ifaceName).First(&iface)
	err := common.MakeMultiError(db.GetErrors())
	if err != nil {
		return nil, err
	}
	if db.Error != nil {
		return nil, db.Error
	}
	return &iface, nil
}

func (agentStore *agentStore) addNetworkInterface(iface *NetworkInterface) error {
	glog.V(1).Info("Acquiring store mutex for addNetworkInterface")
	agentStore.mu.Lock()
	defer func() {
		glog.V(1).Info("Releasing store mutex for addNetworkInterface")
		agentStore.mu.Unlock()
	}()
	glog.V(1).Info("Acquired store mutex for addNetworkInterface")

	db := agentStore.DbStore.Db
	agentStore.DbStore.Db.Create(iface)
	if db.Error != nil {
		return db.Error
	}
	agentStore.DbStore.Db.NewRecord(*iface)
	err := common.MakeMultiError(db.GetErrors())
	if err != nil {
		return err
	}
	if db.Error != nil {
		return db.Error
	}
	return nil
}

func (agentStore *agentStore) listNetworkInterfaces() ([]NetworkInterface, error) {
	glog.V(1).Info("Acquiring store mutex for listNetworkInterfaces")
	agentStore.mu.Lock()
	defer func() {
		glog.V(1).Info("Releasing store mutex for listNetworkInterfaces")
		agentStore.mu.Unlock()
	}()
	glog.V(1).Info("Acquired store mutex for listNetworkInterfaces")

	var networkInterfaces []NetworkInterface
	agentStore.DbStore.Db.Find(&networkInterfaces)
	err := common.MakeMultiError(agentStore.DbStore.Db.GetErrors())
	if err != nil {
		return nil, err
	}
	return networkInterfaces, nil
}
<commit_msg>Remove networkInterfaces model, add basic methods for routes model management<commit_after>package agent

import (
	"github.com/golang/glog"
	"github.com/romana/core/common"
	"github.com/romana/core/pkg/util/firewall"
	"sync"
)

// agentStore is a backing storage. Agent will likely use
// sqlite which is not very reliable in concurrent access scenario,
// so we are going to guard access with mutex.
type agentStore struct {
	common.DbStore
	mu *sync.Mutex
}

// GetDb implements firewall.FirewallStore
func (agentStore agentStore) GetDb() common.DbStore {
	return agentStore.DbStore
}

// GetMutex implements firewall.FirewallStore
func (agentStore agentStore) GetMutex() *sync.Mutex {
	return agentStore.mu
}

// Entities implements Entities method of
// Service interface.
func (agentStore *agentStore) Entities() []interface{} {
	retval := make([]interface{}, 2)
	retval[0] = new(Route)
	retval[1] = new(firewall.IPtablesRule)
	return retval
}

// NewStore returns initialized agentStore.
func NewStore(config common.ServiceConfig) *agentStore {
	storeConfig := config.ServiceSpecific["store"].(map[string]interface{})
	store := agentStore{
		mu: &sync.Mutex{},
	}
	store.ServiceStore = &store
	store.SetConfig(storeConfig)

	return &store
}

// Route is a model to store managed routes
type Route struct {
	ID     uint64 `sql:"AUTO_INCREMENT"`
	IP     string
	Mask   string
	Kind   targetKind
	Spec   string
	Status string
}

// targetKind is a an IP route destination type.
type targetKind string

const (
	device  targetKind = "dev"
	gateway targetKind = "gw"
)

// CreateSchemaPostProcess implements CreateSchemaPostProcess method of
// Service interface.
func (agentStore *agentStore) CreateSchemaPostProcess() error {
	return nil
}

func (agentStore *agentStore) deleteRoute(route *Route) error {
	glog.V(1).Info("Acquiring store mutex for deleteRoute")
	agentStore.mu.Lock()
	defer func() {
		glog.V(1).Info("Releasing store mutex for deleteRoute")
		agentStore.mu.Unlock()
	}()
	glog.V(1).Info("Acquired store mutex for deleteRoute")

	db := agentStore.DbStore.Db
	agentStore.DbStore.Db.Delete(route)
	err := common.MakeMultiError(db.GetErrors())
	if err != nil {
		return err
	}
	if db.Error != nil {
		return db.Error
	}

	return nil
}

func (agentStore *agentStore) findRouteByIface(routeIface string) (*Route, error) {
	glog.V(1).Info("Acquiring store mutex for findRoute")
	agentStore.mu.Lock()
	defer func() {
		glog.V(1).Info("Releasing store mutex for findRoute")
		agentStore.mu.Unlock()
	}()
	glog.V(1).Info("Acquired store mutex for findRoute")

	var route Route
	db := agentStore.DbStore.Db
	agentStore.DbStore.Db.Where("ip = ?", routeIface).First(&route)
	err := common.MakeMultiError(db.GetErrors())
	if err != nil {
		return nil, err
	}
	if db.Error != nil {
		return nil, db.Error
	}
	return &route, nil
}

func (agentStore *agentStore) addRoute(route *Route) error {
	glog.V(1).Info("Acquiring store mutex for addRoute")
	agentStore.mu.Lock()
	defer func() {
		glog.V(1).Info("Releasing store mutex for addRoute")
		agentStore.mu.Unlock()
	}()
	glog.V(1).Info("Acquired store mutex for addRoute")

	db := agentStore.DbStore.Db
	agentStore.DbStore.Db.Create(route)
	if db.Error != nil {
		return db.Error
	}
	agentStore.DbStore.Db.NewRecord(*route)
	err := common.MakeMultiError(db.GetErrors())
	if err != nil {
		return err
	}
	if db.Error != nil {
		return db.Error
	}
	return nil
}

func (agentStore *agentStore) listRoutes() ([]Route, error) {
	glog.V(1).Info("Acquiring store mutex for listRoutes")
	agentStore.mu.Lock()
	defer func() {
		glog.V(1).Info("Releasing store mutex for listRoutes")
		agentStore.mu.Unlock()
	}()
	glog.V(1).Info("Acquired store mutex for listRoutes")

	var networkInterfaces []Route
	agentStore.DbStore.Db.Find(&networkInterfaces)
	err := common.MakeMultiError(agentStore.DbStore.Db.GetErrors())
	if err != nil {
		return nil, err
	}
	return networkInterfaces, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/APTrust/bagman/bagman"
	"github.com/APTrust/bagman/dpn"
	"github.com/APTrust/bagman/workers"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// We want to mark these two APTrust bags
// for ingest to DPN, so they'll go into
// the work queue when we run our tests.
var APTrustBags = []string {
	"test.edu/ncsu.1840.16-1028",
	"test.edu/test.edu.bag2",
}
var testBagUuid = "00000000-0000-0000-0000-000000000001"
var goodBagPath = fmt.Sprintf("dpn/testdata/%s.tar", testBagUuid)
var testBagSize = uint64(268800)
var testBagDigest = "f9f39a1602cde405042dd8b4859c6a3e2c04092a76eaab858ae28e48403ccba4"
var adminTestToken = "0000000000000000000000000000000000000000"
var FaberCollege = "9a000000-0000-4000-a000-000000000002"

// dpn_test_setup.go sets up some test data on our local DPN REST cluster
// so that we can run some end-to-end replication tests. Make sure you're
// running the local DPN cluster before you run this. You can run the cluster
// by running DPN-REST/dpnode/run_cluster.sh. This app will set up some bags
// and replication requests in the local cluster, and will make sure that
// the bag files exist in a location we can copy from using rsync.
// This app runs as part of bagman/scripts/dpn_local_test.sh.
func main() {
	testUtil := NewTestUtil()
	err := testUtil.MakeTestDirs()
	if err != nil {
		testUtil.ProcUtil.MessageLog.Fatal(err)
	}
	err = testUtil.MakeTestData()
	if err != nil {
		testUtil.ProcUtil.MessageLog.Fatal(err)
	}
	err = testUtil.MarkAPTrustBagsForDPN()
	if err != nil {
		testUtil.ProcUtil.MessageLog.Fatal(err)
	}
}

type TestUtil struct {
	ProcUtil             *bagman.ProcessUtil
	DPNConfig            *dpn.DPNConfig
	LocalRestClient      *dpn.DPNRestClient
	RemoteClients        map[string]*dpn.DPNRestClient
	RemoteAdminClients   map[string]*dpn.DPNRestClient
}

func NewTestUtil() (*TestUtil) {
	procUtil := workers.CreateProcUtil()
	procUtil.MessageLog.Info("Starting data setup for local integration test")
	dpnConfig, err := dpn.LoadConfig("dpn/dpn_config.json", "test")
	if err != nil {
		procUtil.MessageLog.Fatal(err.Error())
	}

	// Create a local REST client to talk to our local APTrust DPN REST server
	localClient, err := dpn.NewDPNRestClient(
		dpnConfig.RestClient.LocalServiceURL,
		dpnConfig.RestClient.LocalAPIRoot,
		dpnConfig.RestClient.LocalAuthToken,
		dpnConfig.LocalNode,
		dpnConfig,
		procUtil.MessageLog)
	if err != nil {
		procUtil.MessageLog.Fatal(err.Error())
	}

	// Create clients to talk to remote DPN REST servers.
	// Actually, these are servers in the local cluster that
	// impersonate remote nodes. All of these admin clients
	// need to use the admin API token. We need these admin
	// clients to do admin-only work, such as creating bags
	// and transfer requests.
	remoteClients, err := dpn.GetRemoteClients(localClient, dpnConfig,
		procUtil.MessageLog)
	adminConfig := *dpnConfig
	remoteAdminClients, err := dpn.GetRemoteClients(localClient,
		&adminConfig, procUtil.MessageLog)

	if err != nil {
		panic(err)
	}

	return &TestUtil{
		ProcUtil: procUtil,
		DPNConfig: dpnConfig,
		LocalRestClient: localClient,
		RemoteClients: remoteClients,
		RemoteAdminClients: remoteAdminClients,
	}
}

func (testUtil *TestUtil) MakeTestDirs() (error) {
	err := os.MkdirAll(testUtil.ProcUtil.Config.DPNStagingDirectory, 0755)
	if err != nil {
		return err
	}
	testUserDir := filepath.Join(testUtil.ProcUtil.Config.DPNHomeDirectory,
		"integration_test")
	err = os.MkdirAll(testUserDir, 0755)
	return err
}

func (testUtil *TestUtil) MakeTestData() (error) {
	count := 0
	for node, _ := range testUtil.DPNConfig.RemoteNodeURLs {
		count += 1

		// Create a symlink from dpn_home/integration_test/<uuid>.tar
		// to our known good bag in dpn/testdata/000...1.tar
		bagUuid := fmt.Sprintf("00000000-0000-4000-a000-00000000000%d", count)
		linkPath, err := testUtil.CreateSymLink(bagUuid)
		if err != nil {
			return err
		} else {
			testUtil.ProcUtil.MessageLog.Info("Created symlink at %s", linkPath)
		}

		// Create an entry for this bag on the remote node.
		bag, err := testUtil.CreateBag(bagUuid, node)
		if err != nil {
			return err
		} else {
			testUtil.ProcUtil.MessageLog.Info("Created bag %s on %s",
				bag.UUID, bag.AdminNode)
		}

		// Create a transfer record for this bag on the remote node.
		xfer, err := testUtil.CreateReplicationRequest(bag, linkPath)
		if err != nil {
			return err
		} else {
			testUtil.ProcUtil.MessageLog.Info(
				"Created replication request %s on %s",
				xfer.ReplicationId, bag.AdminNode)
		}
	}
	return nil
}

func (testUtil *TestUtil) CreateSymLink(bagUuid string) (string, error) {
	sourceFile, err := bagman.RelativeToAbsPath(goodBagPath)
	if err != nil {
		return "", err
	}
	linkPath := filepath.Join(testUtil.ProcUtil.Config.DPNHomeDirectory,
		"integration_test", bagUuid + ".tar")
	if bagman.FileExists(linkPath) {
		return linkPath, nil
	}
	err = os.Symlink(sourceFile, linkPath)
	if err != nil {
		return "", err
	}
	return linkPath, err
}

func (testUtil *TestUtil) CreateBag(bagUuid, node string) (*dpn.DPNBag, error) {
	bag, err := testUtil.RemoteClients[node].DPNBagGet(bagUuid)
	if err == nil && bag != nil {
		// Bag already exists. No need to recreate it.
		return bag, err
	}
	bag = &dpn.DPNBag{
		UUID: bagUuid,
		LocalId: fmt.Sprintf("integration-test-%s-1000", node),
		Size: testBagSize,
		FirstVersionUUID: bagUuid,
		Version: 1,
		IngestNode: node,
		AdminNode: node,
		BagType: "D",
		Rights: make([]string, 0),
		Interpretive: make([]string, 0),
		ReplicatingNodes: make([]string, 0),
		Member: FaberCollege,
		Fixities: &dpn.DPNFixity{
			Sha256: testBagDigest,
		},
	}
	// You have to be node admin to create a bag, so use the admin client.
	return testUtil.RemoteAdminClients[node].DPNBagCreate(bag)
}

func (testUtil *TestUtil) CreateReplicationRequest(bag *dpn.DPNBag, linkPath string) (*dpn.DPNReplicationTransfer, error) {
	xfer := &dpn.DPNReplicationTransfer{
		FromNode: bag.AdminNode,
		ToNode: testUtil.DPNConfig.LocalNode,
		BagId: bag.UUID,
		ReplicationId: strings.Replace(bag.UUID, "4000", "4444", 1),
		FixityAlgorithm: "sha256",
		Status: "requested",
		Protocol: "rsync",
		Link: linkPath,
	}
	// You have to be node admin to create the transfer request,
	// so use the admin client.
	return testUtil.RemoteAdminClients[bag.AdminNode].ReplicationTransferCreate(xfer)
}

// Mark two existing APTrust bags for ingest into DPN
func (testUtil *TestUtil) MarkAPTrustBagsForDPN() (error) {
	for _, identifier := range APTrustBags {
		processedItem, err := testUtil.GetLatestStatusFor(identifier)
		if err != nil {
			return err
		}
		// Clear out the id, so the Fluctus client will create a new
		// ProcessedItem record that says this bag has a pending DPN
		// ingest request.
		processedItem.Id = 0
		processedItem.Date = time.Now()
		processedItem.GenericFileIdentifier = ""
		processedItem.Note = "Requested item be sent to DPN"
		processedItem.User = ""
		processedItem.Action = "DPN"
		processedItem.Status = "Pending"
		processedItem.Stage = "Requested"
		processedItem.Retry = true
		err = testUtil.ProcUtil.FluctusClient.UpdateProcessedItem(processedItem)
		if err != nil {
			return err
		}
		testUtil.ProcUtil.MessageLog.Debug("Created DPN ingest request for bag %s", identifier)
	}
	return nil
}

func (testUtil *TestUtil) GetLatestStatusFor(objectIdentifier string) (*bagman.ProcessStatus, error) {
	testUtil.ProcUtil.MessageLog.Debug(
		"Looking up latest status for bag %s",
		objectIdentifier)
	ps := &bagman.ProcessStatus{
		ObjectIdentifier: objectIdentifier,
		Action: "Ingest",
		Status: "Success",
	}
	statusRecords, err := testUtil.ProcUtil.FluctusClient.ProcessStatusSearch(ps, false, false)
	if err != nil {
		return nil, err
	}
	if len(statusRecords) == 0 {
		return nil, fmt.Errorf("No ingest records found for %s", objectIdentifier)
	}
	var latestStatus *bagman.ProcessStatus
	latestTimestamp, _ := time.Parse(time.RFC3339, "1999-01-01T12:00:00+00:00")
	for i := range statusRecords {
		if statusRecords[i].Date.After(latestTimestamp) {
			latestTimestamp = statusRecords[i].Date
			latestStatus = statusRecords[i]
		}
	}
	return latestStatus, nil
}
<commit_msg>Add timestamp to test bags, or integration tests fail<commit_after>package main

import (
	"fmt"
	"github.com/APTrust/bagman/bagman"
	"github.com/APTrust/bagman/dpn"
	"github.com/APTrust/bagman/workers"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// We want to mark these two APTrust bags
// for ingest to DPN, so they'll go into
// the work queue when we run our tests.
var APTrustBags = []string {
	"test.edu/ncsu.1840.16-1028",
	"test.edu/test.edu.bag2",
}
var testBagUuid = "00000000-0000-0000-0000-000000000001"
var goodBagPath = fmt.Sprintf("dpn/testdata/%s.tar", testBagUuid)
var testBagSize = uint64(268800)
var testBagDigest = "f9f39a1602cde405042dd8b4859c6a3e2c04092a76eaab858ae28e48403ccba4"
var adminTestToken = "0000000000000000000000000000000000000000"
var FaberCollege = "9a000000-0000-4000-a000-000000000002"

// dpn_test_setup.go sets up some test data on our local DPN REST cluster
// so that we can run some end-to-end replication tests. Make sure you're
// running the local DPN cluster before you run this. You can run the cluster
// by running DPN-REST/dpnode/run_cluster.sh. This app will set up some bags
// and replication requests in the local cluster, and will make sure that
// the bag files exist in a location we can copy from using rsync.
// This app runs as part of bagman/scripts/dpn_local_test.sh.
func main() {
	testUtil := NewTestUtil()
	err := testUtil.MakeTestDirs()
	if err != nil {
		testUtil.ProcUtil.MessageLog.Fatal(err)
	}
	err = testUtil.MakeTestData()
	if err != nil {
		testUtil.ProcUtil.MessageLog.Fatal(err)
	}
	err = testUtil.MarkAPTrustBagsForDPN()
	if err != nil {
		testUtil.ProcUtil.MessageLog.Fatal(err)
	}
}

type TestUtil struct {
	ProcUtil             *bagman.ProcessUtil
	DPNConfig            *dpn.DPNConfig
	LocalRestClient      *dpn.DPNRestClient
	RemoteClients        map[string]*dpn.DPNRestClient
	RemoteAdminClients   map[string]*dpn.DPNRestClient
}

func NewTestUtil() (*TestUtil) {
	procUtil := workers.CreateProcUtil()
	procUtil.MessageLog.Info("Starting data setup for local integration test")
	dpnConfig, err := dpn.LoadConfig("dpn/dpn_config.json", "test")
	if err != nil {
		procUtil.MessageLog.Fatal(err.Error())
	}

	// Create a local REST client to talk to our local APTrust DPN REST server
	localClient, err := dpn.NewDPNRestClient(
		dpnConfig.RestClient.LocalServiceURL,
		dpnConfig.RestClient.LocalAPIRoot,
		dpnConfig.RestClient.LocalAuthToken,
		dpnConfig.LocalNode,
		dpnConfig,
		procUtil.MessageLog)
	if err != nil {
		procUtil.MessageLog.Fatal(err.Error())
	}

	// Create clients to talk to remote DPN REST servers.
	// Actually, these are servers in the local cluster that
	// impersonate remote nodes. All of these admin clients
	// need to use the admin API token. We need these admin
	// clients to do admin-only work, such as creating bags
	// and transfer requests.
	remoteClients, err := dpn.GetRemoteClients(localClient, dpnConfig,
		procUtil.MessageLog)
	adminConfig := *dpnConfig
	remoteAdminClients, err := dpn.GetRemoteClients(localClient,
		&adminConfig, procUtil.MessageLog)

	if err != nil {
		panic(err)
	}

	return &TestUtil{
		ProcUtil: procUtil,
		DPNConfig: dpnConfig,
		LocalRestClient: localClient,
		RemoteClients: remoteClients,
		RemoteAdminClients: remoteAdminClients,
	}
}

func (testUtil *TestUtil) MakeTestDirs() (error) {
	err := os.MkdirAll(testUtil.ProcUtil.Config.DPNStagingDirectory, 0755)
	if err != nil {
		return err
	}
	testUserDir := filepath.Join(testUtil.ProcUtil.Config.DPNHomeDirectory,
		"integration_test")
	err = os.MkdirAll(testUserDir, 0755)
	return err
}

func (testUtil *TestUtil) MakeTestData() (error) {
	count := 0
	for node, _ := range testUtil.DPNConfig.RemoteNodeURLs {
		count += 1

		// Create a symlink from dpn_home/integration_test/<uuid>.tar
		// to our known good bag in dpn/testdata/000...1.tar
		bagUuid := fmt.Sprintf("00000000-0000-4000-a000-00000000000%d", count)
		linkPath, err := testUtil.CreateSymLink(bagUuid)
		if err != nil {
			return err
		} else {
			testUtil.ProcUtil.MessageLog.Info("Created symlink at %s", linkPath)
		}

		// Create an entry for this bag on the remote node.
		bag, err := testUtil.CreateBag(bagUuid, node)
		if err != nil {
			return err
		} else {
			testUtil.ProcUtil.MessageLog.Info("Created bag %s on %s",
				bag.UUID, bag.AdminNode)
		}

		// Create a transfer record for this bag on the remote node.
		xfer, err := testUtil.CreateReplicationRequest(bag, linkPath)
		if err != nil {
			return err
		} else {
			testUtil.ProcUtil.MessageLog.Info(
				"Created replication request %s on %s",
				xfer.ReplicationId, bag.AdminNode)
		}
	}
	return nil
}

func (testUtil *TestUtil) CreateSymLink(bagUuid string) (string, error) {
	sourceFile, err := bagman.RelativeToAbsPath(goodBagPath)
	if err != nil {
		return "", err
	}
	linkPath := filepath.Join(testUtil.ProcUtil.Config.DPNHomeDirectory,
		"integration_test", bagUuid + ".tar")
	if bagman.FileExists(linkPath) {
		return linkPath, nil
	}
	err = os.Symlink(sourceFile, linkPath)
	if err != nil {
		return "", err
	}
	return linkPath, err
}

func (testUtil *TestUtil) CreateBag(bagUuid, node string) (*dpn.DPNBag, error) {
	bag, err := testUtil.RemoteClients[node].DPNBagGet(bagUuid)
	if err == nil && bag != nil {
		// Bag already exists. No need to recreate it.
		return bag, err
	}
	utcNow := time.Now().UTC()
	bag = &dpn.DPNBag{
		UUID: bagUuid,
		LocalId: fmt.Sprintf("integration-test-%s-1000", node),
		Size: testBagSize,
		FirstVersionUUID: bagUuid,
		Version: 1,
		IngestNode: node,
		AdminNode: node,
		BagType: "D",
		Rights: make([]string, 0),
		Interpretive: make([]string, 0),
		ReplicatingNodes: make([]string, 0),
		Member: FaberCollege,
		Fixities: &dpn.DPNFixity{
			Sha256: testBagDigest,
		},
		CreatedAt: utcNow,
		UpdatedAt: utcNow,
	}
	// You have to be node admin to create a bag, so use the admin client.
	return testUtil.RemoteAdminClients[node].DPNBagCreate(bag)
}

func (testUtil *TestUtil) CreateReplicationRequest(bag *dpn.DPNBag, linkPath string) (*dpn.DPNReplicationTransfer, error) {
	xfer := &dpn.DPNReplicationTransfer{
		FromNode: bag.AdminNode,
		ToNode: testUtil.DPNConfig.LocalNode,
		BagId: bag.UUID,
		ReplicationId: strings.Replace(bag.UUID, "4000", "4444", 1),
		FixityAlgorithm: "sha256",
		Status: "requested",
		Protocol: "rsync",
		Link: linkPath,
	}
	// You have to be node admin to create the transfer request,
	// so use the admin client.
	return testUtil.RemoteAdminClients[bag.AdminNode].ReplicationTransferCreate(xfer)
}

// Mark two existing APTrust bags for ingest into DPN
func (testUtil *TestUtil) MarkAPTrustBagsForDPN() (error) {
	for _, identifier := range APTrustBags {
		processedItem, err := testUtil.GetLatestStatusFor(identifier)
		if err != nil {
			return err
		}
		// Clear out the id, so the Fluctus client will create a new
		// ProcessedItem record that says this bag has a pending DPN
		// ingest request.
		processedItem.Id = 0
		processedItem.Date = time.Now()
		processedItem.GenericFileIdentifier = ""
		processedItem.Note = "Requested item be sent to DPN"
		processedItem.User = ""
		processedItem.Action = "DPN"
		processedItem.Status = "Pending"
		processedItem.Stage = "Requested"
		processedItem.Retry = true
		err = testUtil.ProcUtil.FluctusClient.UpdateProcessedItem(processedItem)
		if err != nil {
			return err
		}
		testUtil.ProcUtil.MessageLog.Debug("Created DPN ingest request for bag %s", identifier)
	}
	return nil
}

func (testUtil *TestUtil) GetLatestStatusFor(objectIdentifier string) (*bagman.ProcessStatus, error) {
	testUtil.ProcUtil.MessageLog.Debug(
		"Looking up latest status for bag %s",
		objectIdentifier)
	ps := &bagman.ProcessStatus{
		ObjectIdentifier: objectIdentifier,
		Action: "Ingest",
		Status: "Success",
	}
	statusRecords, err := testUtil.ProcUtil.FluctusClient.ProcessStatusSearch(ps, false, false)
	if err != nil {
		return nil, err
	}
	if len(statusRecords) == 0 {
		return nil, fmt.Errorf("No ingest records found for %s", objectIdentifier)
	}
	var latestStatus *bagman.ProcessStatus
	latestTimestamp, _ := time.Parse(time.RFC3339, "1999-01-01T12:00:00+00:00")
	for i := range statusRecords {
		if statusRecords[i].Date.After(latestTimestamp) {
			latestTimestamp = statusRecords[i].Date
			latestStatus = statusRecords[i]
		}
	}
	return latestStatus, nil
}
<|endoftext|>


<commit_before>package alias

import (
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"strings"

	"text/template"

	"io/ioutil"

	log "github.com/Sirupsen/logrus"
)

// Alias is a struct for managing a single Drush Alias
type Alias struct {
	name     string
	path     string
	uri      string
	template string
}

func (a *Alias) Template() string {
	return a.template
}

func (a *Alias) SetTemplate(template string) {
	a.template = template
}

// NewAlias instantiates an Alias struct
func NewAlias(name, path, alias string) *Alias {
	alias = strings.Replace(alias, "@", "", -1)
	Command := exec.Command("drush", "sa", "@"+alias)
	CommandOut, _ := Command.CombinedOutput()
	if strings.Contains(string(CommandOut), "Could not find the alias") {
		log.Warnln(string(CommandOut))
		return &Alias{}
	} else {
		return &Alias{name, path, alias, ""}
	}
}

// SetName sets the name field for an alias struct
func (Alias *Alias) SetName(value string) {
	Alias.name = value
}

// GetName gets the name field for an alias struct
func (Alias *Alias) GetName() string {
	return Alias.name
}

// SetURI sets the uri field for an alias struct
func (Alias *Alias) SetURI(value string) {
	Alias.uri = value
}

// GetURI gets the uri field for an alias struct
func (Alias *Alias) GetURI() string {
	return Alias.uri
}

// SetPath sets the path field for an alias struct
func (Alias *Alias) SetPath(value string) {
	Alias.path = value
}

// GetPath gets the path field for an alias struct
func (Alias *Alias) GetPath() string {
	return Alias.path
}

// Install an alias from an alias struct
func (Alias *Alias) Install() {
	Root := fmt.Sprintf("%v/%v.latest/docroot", Alias.GetPath(), Alias.GetURI())
	data := map[string]string{
		"Name":   Alias.GetName(),
		"Root":   Root,
		"Alias":  Alias.GetURI(),
		"Domain": Alias.GetURI(),
	}
	usr, _ := user.Current()
	filedir := usr.HomeDir + "/.drush"
	filename := Alias.GetURI() + ".alias.drushrc.php"
	fullpath := filedir + "/" + filename

	t := template.New("alias")
	if Alias.template == "" {
		defaultTemplate := fmt.Sprintf("%v/src/github.com/fubarhouse/golang-drush/cmd/yoink/templates/alias.gotpl", os.Getenv("GOPATH"))
		defaultData, _ := ioutil.ReadFile(defaultTemplate)
		t.Parse(string(defaultData))
	} else {
		defaultData, _ := ioutil.ReadFile(Alias.template)
		t.Parse(string(defaultData))
	}

	os.Remove(fullpath)
	file, _ := os.Create(fullpath)
	tplErr := t.Execute(file, data)

	if tplErr == nil {
		log.Infof("Successfully templated alias to file %v", fullpath)
	} else {
		log.Warnf("Error templating alias to file %v", fullpath)
	}
}

// Uninstall un-installs an alias from an alias struct
func (Alias *Alias) Uninstall() {
	usr, _ := user.Current()
	filedir := usr.HomeDir + "/.drush"
	filename := Alias.GetURI() + ".alias.drushrc.php"
	fullpath := filedir + "/" + filename
	_, statErr := os.Stat(fullpath)
	if statErr == nil {
		err := os.Remove(fullpath)
		if err != nil {
			log.Warnln("Could not remove alias file", fullpath)
		} else {
			log.Infoln("Removed alias file", fullpath)
		}
	} else {
		log.Warnln("Alias file was not found.", fullpath)
	}

}

// Reinstall re-installs an alias from an alias struct
func (Alias *Alias) Reinstall() {
	Alias.Uninstall()
	Alias.Install()

}

// GetStatus returns the installation status of an alias struct
func (Alias *Alias) GetStatus() bool {
	_, err := os.Stat(getHome() + "/.drush/" + Alias.GetURI() + ".alias.drushrc.php")
	if err != nil {
		Command := exec.Command("drush", "sa")
		CommandOut, _ := Command.CombinedOutput()
		if strings.Contains(string(CommandOut), Alias.GetName()) {
			return true
		} else {
			return false
		}
	}
	return true
}

// PrintStatus prints the installation status of an alias struct
func (Alias *Alias) PrintStatus() {
	_, err := os.Stat(getHome() + "/.drush/" + Alias.GetURI() + ".alias.drushrc.php")
	if err != nil {
		fmt.Println("false")
	} else {
		fmt.Println("true")
	}
}

// getHome returns the user home directory.
// Performs some validation in the process.
func getHome() string {
	usr, err := user.Current()
	if err != nil {
		log.Fatalln(err)
		os.Exit(1)
	}
	return usr.HomeDir
}
<commit_msg>Add template logic to alias.go<commit_after>package alias

import (
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"strings"

	"text/template"

	"io/ioutil"

	log "github.com/Sirupsen/logrus"
)

// Alias is a struct for managing a single Drush Alias
type Alias struct {
	name     string
	path     string
	uri      string
	template string
}

func (a *Alias) Template() string {
	return a.template
}

func (a *Alias) SetTemplate(template string) {
	a.template = template
}

// NewAlias instantiates an Alias struct
func NewAlias(name, path, alias string) *Alias {
	alias = strings.Replace(alias, "@", "", -1)
	Command := exec.Command("drush", "sa", "@"+alias)
	CommandOut, _ := Command.CombinedOutput()
	if strings.Contains(string(CommandOut), "Could not find the alias") {
		log.Warnln(string(CommandOut))
		return &Alias{}
	} else {
		return &Alias{name, path, alias, ""}
	}
}

// SetName sets the name field for an alias struct
func (Alias *Alias) SetName(value string) {
	Alias.name = value
}

// GetName gets the name field for an alias struct
func (Alias *Alias) GetName() string {
	return Alias.name
}

// SetURI sets the uri field for an alias struct
func (Alias *Alias) SetURI(value string) {
	Alias.uri = value
}

// GetURI gets the uri field for an alias struct
func (Alias *Alias) GetURI() string {
	return Alias.uri
}

// SetPath sets the path field for an alias struct
func (Alias *Alias) SetPath(value string) {
	Alias.path = value
}

// GetPath gets the path field for an alias struct
func (Alias *Alias) GetPath() string {
	return Alias.path
}

// Install an alias from an alias struct
func (Alias *Alias) Install() {
	Root := fmt.Sprintf("%v/%v.latest/docroot", Alias.GetPath(), Alias.GetURI())
	data := map[string]string{
		"Name":   Alias.GetName(),
		"Root":   Root,
		"Alias":  Alias.GetURI(),
		"Domain": Alias.GetURI(),
	}
	usr, _ := user.Current()
	filedir := usr.HomeDir + "/.drush"
	filename := Alias.GetURI() + ".alias.drushrc.php"
	fullpath := filedir + "/" + filename

	defaultTemplate := ""
	if Alias.template == "" {
		defaultTemplate = fmt.Sprintf("%v/src/github.com/fubarhouse/golang-drush/cmd/yoink/templates/alias.gotpl", os.Getenv("GOPATH"))
	} else {
		defaultTemplate = Alias.template
	}

	t := template.New("alias")
	if _, err := os.Stat(defaultTemplate); err != nil {
		log.Warnln("default drush alias template could not be found, source files do not exist.")
	} else {
		log.Infof("Found template %v for usage", defaultTemplate)
		defaultData, _ := ioutil.ReadFile(defaultTemplate)
		t.Parse(string(defaultData))
	}

	os.Remove(fullpath)
	file, _ := os.Create(fullpath)
	tplErr := t.Execute(file, data)

	if tplErr == nil {
		log.Infof("Successfully templated alias to file %v", fullpath)
	} else {
		log.Warnf("Error templating alias to file %v", fullpath)
	}
}

// Uninstall un-installs an alias from an alias struct
func (Alias *Alias) Uninstall() {
	usr, _ := user.Current()
	filedir := usr.HomeDir + "/.drush"
	filename := Alias.GetURI() + ".alias.drushrc.php"
	fullpath := filedir + "/" + filename
	_, statErr := os.Stat(fullpath)
	if statErr == nil {
		err := os.Remove(fullpath)
		if err != nil {
			log.Warnln("Could not remove alias file", fullpath)
		} else {
			log.Infoln("Removed alias file", fullpath)
		}
	} else {
		log.Warnln("Alias file was not found.", fullpath)
	}

}

// Reinstall re-installs an alias from an alias struct
func (Alias *Alias) Reinstall() {
	Alias.Uninstall()
	Alias.Install()

}

// GetStatus returns the installation status of an alias struct
func (Alias *Alias) GetStatus() bool {
	_, err := os.Stat(getHome() + "/.drush/" + Alias.GetURI() + ".alias.drushrc.php")
	if err != nil {
		Command := exec.Command("drush", "sa")
		CommandOut, _ := Command.CombinedOutput()
		if strings.Contains(string(CommandOut), Alias.GetName()) {
			return true
		} else {
			return false
		}
	}
	return true
}

// PrintStatus prints the installation status of an alias struct
func (Alias *Alias) PrintStatus() {
	_, err := os.Stat(getHome() + "/.drush/" + Alias.GetURI() + ".alias.drushrc.php")
	if err != nil {
		fmt.Println("false")
	} else {
		fmt.Println("true")
	}
}

// getHome returns the user home directory.
// Performs some validation in the process.
func getHome() string {
	usr, err := user.Current()
	if err != nil {
		log.Fatalln(err)
		os.Exit(1)
	}
	return usr.HomeDir
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package tests_test

import (
	"flag"
	"strings"
	"net/http"
	"io/ioutil"
	"regexp"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"kubevirt.io/kubevirt/tests"
)

func search_for_pattern(yaml_segment string, pattern string) bool {
	r, _ := regexp.Compile(pattern)
	for _, line := range strings.Split(yaml_segment, "\n") {
		if r.MatchString(line) {
			return true
		}
	}
	return false
}

var _ = Describe("Common templates", func() {
	flag.Parse()

	BeforeEach(func() {
		tests.BeforeTestCleanup()
	})

	Context("Testing generated templates", func() {

		// CNV-1069
		It("Check if template valid for UI", func() {

			// Getting common_templates
			// TODO: replace downloading common-templates with getting common-templates from RPM
			ct_yml_url := "https://github.com/kubevirt/common-templates/releases/download/v0.3.1/common-templates-v0.3.1.yaml"
			response, err := http.Get(ct_yml_url)
			Expect(err).NotTo(HaveOccurred())
			defer response.Body.Close()
			data, err := ioutil.ReadAll(response.Body)
			common_templates_yaml := string(data)

			for _, yaml_segment := range strings.Split(common_templates_yaml, "\n---\n") {

				if !search_for_pattern(yaml_segment, "^Kind: Template$") {
					continue
				}

				By("Checking that template contains required lables")
				Expect(search_for_pattern(yaml_segment, "\\s+os.template.cnv.io/[a-z0-9\\.]+:\\s\"true\"$")).To(BeTrue(), "Template should have os label")
				Expect(search_for_pattern(yaml_segment, "\\s+workload.template.cnv.io/[a-z]+:\\s\"true\"$")).To(BeTrue(), "Template should have workload label")
				Expect(search_for_pattern(yaml_segment, "\\s+flavor.template.cnv.io/[a-z]+:\\s\"true\"$")).To(BeTrue(), "Template should have flavor label")
				Expect(search_for_pattern(yaml_segment, "\\s+template.cnv.io/type:\\s\"base\"$")).To(BeTrue(), "Template should have type base")

			}
		})
	})
})

<commit_msg>rebase & implement determination of the manifest-s latest version<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package tests_test

import (
	"flag"
	"strings"
	"net/http"
	"io/ioutil"
	"regexp"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"kubevirt.io/kubevirt/tests"
        "os/exec"

)

func search_for_pattern(yaml_segment string, pattern string) bool {
	r, _ := regexp.Compile(pattern)
	for _, line := range strings.Split(yaml_segment, "\n") {
		if r.MatchString(line) {
			return true
		}
	}
	return false
}

var _ = Describe("Common templates", func() {
	flag.Parse()

	BeforeEach(func() {
		tests.BeforeTestCleanup()
	})

	Context("Testing generated templates", func() {

		// CNV-1069
		It("Check if template valid for UI", func() {

			// Getting common_templates
			// TODO: replace downloading common-templates with getting common-templates from RPM

                        ct_yml_url_byte, err := exec.Command("/bin/bash", "-c", "curl -s https://api.github.com/repos/kubevirt/common-templates/releases/latest | grep browser_download_url | cut -d '\"' -f 4").Output()
                        ct_yml_url :=  string(ct_yml_url_byte)
                        Expect(err).ToNot(HaveOccurred())
			response, err := http.Get(ct_yml_url)
			Expect(err).NotTo(HaveOccurred())
			defer response.Body.Close()
			data, err := ioutil.ReadAll(response.Body)
			common_templates_yaml := string(data)

			for _, yaml_segment := range strings.Split(common_templates_yaml, "\n---\n") {

				if !search_for_pattern(yaml_segment, "^Kind: Template$") {
					continue
				}

				By("Checking that template contains required lables")
				Expect(search_for_pattern(yaml_segment, "\\s+os.template.cnv.io/[a-z0-9\\.]+:\\s\"true\"$")).To(BeTrue(), "Template should have os label")
				Expect(search_for_pattern(yaml_segment, "\\s+workload.template.cnv.io/[a-z]+:\\s\"true\"$")).To(BeTrue(), "Template should have workload label")
				Expect(search_for_pattern(yaml_segment, "\\s+flavor.template.cnv.io/[a-z]+:\\s\"true\"$")).To(BeTrue(), "Template should have flavor label")
				Expect(search_for_pattern(yaml_segment, "\\s+template.cnv.io/type:\\s\"base\"$")).To(BeTrue(), "Template should have type base")

			}
		})
	})
})

<|endoftext|>


<commit_before>package kk

import (
	"github.com/kkserver/kk-direct/direct"
	"github.com/kkserver/kk-lib/kk/app"
	"github.com/kkserver/kk-lib/kk/app/client"
	"github.com/kkserver/kk-lib/kk/dynamic"
	"time"
)

var AppKeys = []string{"app"}
var ResultKeys = []string{"result"}

type Direct struct {
	direct.Direct
}

func (D *Direct) Exec(ctx direct.IContext) error {

	options := D.Options()

	v := ctx.Get(AppKeys)

	if v != nil {

		a, ok := v.(app.IApp)

		if ok {

			task := client.RequestTask{}

			task.Name = options.Name()
			task.Timeout = time.Duration(dynamic.IntValue(dynamic.Get(options, "timeout"), 1)) * time.Second

			v, ok = options["options"]

			if ok {
				task.Request = direct.ReflectValue(D.App(), ctx, v)
			} else {
				task.Request = map[interface{}]interface{}{}
			}

			err := app.Handle(a, &task)

			if err != nil {
				return D.Fail(ctx, err)
			}

			errno := dynamic.IntValue(dynamic.Get(task.Result, "errno"), 0)
			errmsg := dynamic.StringValue(dynamic.Get(task.Result, "errmsg"), "")

			if errno != 0 {
				return D.Fail(ctx, direct.NewError(errno, errmsg))
			}

			ctx.Set(ResultKeys, task.Result)

			return D.Done(ctx, "done")
		}
	}

	return D.Fail(ctx, direct.NewError(direct.ERROR_UNKNOWN, "Not Found kk app"))
}
<commit_msg>fix bug<commit_after>package kk

import (
	"github.com/kkserver/kk-direct/direct"
	"github.com/kkserver/kk-lib/kk/app"
	"github.com/kkserver/kk-lib/kk/app/client"
	"github.com/kkserver/kk-lib/kk/dynamic"
	"log"
	"time"
)

var AppKeys = []string{"app"}
var ResultKeys = []string{"result"}

type Direct struct {
	direct.Direct
}

func (D *Direct) Exec(ctx direct.IContext) error {

	options := D.Options()

	v := ctx.Get(AppKeys)

	if v != nil {

		a, ok := v.(app.IApp)

		if ok {

			task := client.RequestTask{}

			task.Name = options.Name()
			task.Timeout = time.Duration(dynamic.IntValue(dynamic.Get(options, "timeout"), 1)) * time.Second

			v, ok = options["options"]

			if ok {
				task.Request = direct.ReflectValue(D.App(), ctx, v)
			} else {
				task.Request = map[interface{}]interface{}{}
			}

			log.Println("kk", task)

			err := app.Handle(a, &task)

			log.Println("kk", task, err)

			if err != nil {
				return D.Fail(ctx, err)
			}

			errno := dynamic.IntValue(dynamic.Get(task.Result, "errno"), 0)
			errmsg := dynamic.StringValue(dynamic.Get(task.Result, "errmsg"), "")

			if errno != 0 {
				return D.Fail(ctx, direct.NewError(errno, errmsg))
			}

			ctx.Set(ResultKeys, task.Result)

			return D.Done(ctx, "done")
		}
	}

	return D.Fail(ctx, direct.NewError(direct.ERROR_UNKNOWN, "Not Found kk app"))
}
<|endoftext|>


<commit_before>package vizzini_test

import (
	"github.com/cloudfoundry-incubator/bbs/models"
	. "github.com/cloudfoundry-incubator/vizzini/matchers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("DiskLimits", func() {
	var lrp *models.DesiredLRP
	BeforeEach(func() {
		lrp = DesiredLRPWithGuid(guid)
	})

	Describe("with a preloaded rootfs, the disk limit is applied to the COW layer", func() {
		Context("when the disk limit exceeds the contents to be copied in", func() {
			It("should not crash, but should start succesfully", func() {
				lrp.DiskMb = 64
				Ω(bbsClient.DesireLRP(lrp)).Should(Succeed())
				Eventually(ActualGetter(guid, 0)).Should(BeActualLRPWithState(guid, 0, models.ActualLRPStateRunning))
			})
		})

		Context("when the disk limit is less than the contents to be copied in", func() {
			It("should crash", func() {
				lrp.DiskMb = 4
				Ω(bbsClient.DesireLRP(lrp)).Should(Succeed())
				Eventually(ActualGetter(guid, 0)).Should(BeActualLRPThatHasCrashed(guid, 0))

				//getting all the way helps ensure the tests don't spuriously fail
				//when we delete the DesiredLRP if the application is in the middle of restarting it looks like we need to wiat for a convergence
				//loop to eventually clean it up.  This is likely a bug, though it's not crticial.
				Eventually(ActualGetter(guid, 0), ConvergerInterval).Should(BeActualLRPWithStateAndCrashCount(guid, 0, models.ActualLRPStateCrashed, 3))
			})
		})
	})

	Describe("{DOCKER} with a docker-image rootfs", func() {
		BeforeEach(func() {
			lrp.RootFs = "docker:///onsi/grace-busybox"
			lrp.Setup = nil //note: we copy nothing in, the docker image on its own should cause this failure
			lrp.Action = models.WrapAction(&models.RunAction{
				Path: "/grace",
				User: "root",
				Env:  []*models.EnvironmentVariable{{Name: "PORT", Value: "8080"}},
			})
			lrp.Monitor = nil
		})

		Context("when the disk limit exceeds the size of the docker image", func() {
			It("should not crash, but should start succesfully", func() {
				lrp.DiskMb = 64
				Ω(bbsClient.DesireLRP(lrp)).Should(Succeed())
				Eventually(ActualGetter(guid, 0)).Should(BeActualLRPWithState(guid, 0, models.ActualLRPStateRunning))
			})
		})

		// TODO: re-enable once Garden story https://www.pivotaltracker.com/story/show/106183900 lands in a final release
		// Context("when the disk limit is less than the size of the docker image", func() {
		// 	It("should crash", func() {
		// 		lrp.DiskMb = 4
		// 		Ω(bbsClient.DesireLRP(lrp)).Should(Succeed())
		// 		Eventually(ActualGetter(guid, 0)).Should(BeActualLRPThatHasCrashed(guid, 0))

		// 		//getting all the way helps ensure the tests don't spuriously fail
		// 		//when we delete the DesiredLRP if the application is in the middle of restarting it looks like we need to wiat for a convergence
		// 		//loop to eventually clean it up.  This is likely a bug, though it's not crticial.
		// 		Eventually(ActualGetter(guid, 0), ConvergerInterval).Should(BeActualLRPWithStateAndCrashCount(guid, 0, models.ActualLRPStateCrashed, 3))
		// 	})
		// })
	})
})
<commit_msg>Revert "Temporarily disable test for docker image size failure"<commit_after>package vizzini_test

import (
	"github.com/cloudfoundry-incubator/bbs/models"
	. "github.com/cloudfoundry-incubator/vizzini/matchers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("DiskLimits", func() {
	var lrp *models.DesiredLRP
	BeforeEach(func() {
		lrp = DesiredLRPWithGuid(guid)
	})

	Describe("with a preloaded rootfs, the disk limit is applied to the COW layer", func() {
		Context("when the disk limit exceeds the contents to be copied in", func() {
			It("should not crash, but should start succesfully", func() {
				lrp.DiskMb = 64
				Ω(bbsClient.DesireLRP(lrp)).Should(Succeed())
				Eventually(ActualGetter(guid, 0)).Should(BeActualLRPWithState(guid, 0, models.ActualLRPStateRunning))
			})
		})

		Context("when the disk limit is less than the contents to be copied in", func() {
			It("should crash", func() {
				lrp.DiskMb = 4
				Ω(bbsClient.DesireLRP(lrp)).Should(Succeed())
				Eventually(ActualGetter(guid, 0)).Should(BeActualLRPThatHasCrashed(guid, 0))

				//getting all the way helps ensure the tests don't spuriously fail
				//when we delete the DesiredLRP if the application is in the middle of restarting it looks like we need to wiat for a convergence
				//loop to eventually clean it up.  This is likely a bug, though it's not crticial.
				Eventually(ActualGetter(guid, 0), ConvergerInterval).Should(BeActualLRPWithStateAndCrashCount(guid, 0, models.ActualLRPStateCrashed, 3))
			})
		})
	})

	Describe("{DOCKER} with a docker-image rootfs", func() {
		BeforeEach(func() {
			lrp.RootFs = "docker:///onsi/grace-busybox"
			lrp.Setup = nil //note: we copy nothing in, the docker image on its own should cause this failure
			lrp.Action = models.WrapAction(&models.RunAction{
				Path: "/grace",
				User: "root",
				Env:  []*models.EnvironmentVariable{{Name: "PORT", Value: "8080"}},
			})
			lrp.Monitor = nil
		})

		Context("when the disk limit exceeds the size of the docker image", func() {
			It("should not crash, but should start succesfully", func() {
				lrp.DiskMb = 64
				Ω(bbsClient.DesireLRP(lrp)).Should(Succeed())
				Eventually(ActualGetter(guid, 0)).Should(BeActualLRPWithState(guid, 0, models.ActualLRPStateRunning))
			})
		})

		Context("when the disk limit is less than the size of the docker image", func() {
			It("should crash", func() {
				lrp.DiskMb = 4
				Ω(bbsClient.DesireLRP(lrp)).Should(Succeed())
				Eventually(ActualGetter(guid, 0)).Should(BeActualLRPThatHasCrashed(guid, 0))

				//getting all the way helps ensure the tests don't spuriously fail
				//when we delete the DesiredLRP if the application is in the middle of restarting it looks like we need to wiat for a convergence
				//loop to eventually clean it up.  This is likely a bug, though it's not crticial.
				Eventually(ActualGetter(guid, 0), ConvergerInterval).Should(BeActualLRPWithStateAndCrashCount(guid, 0, models.ActualLRPStateCrashed, 3))
			})
		})
	})
})
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 dot produces DOT graphs from Beam graph representations.
package dot

import (
	"fmt"
	"io"
	"text/template"

	"github.com/apache/beam/sdks/go/pkg/beam/core/graph"
)

var (
	header = `
digraph execution_plan {
  label="execution_plan"
  labeljust="l";
  fontname="Ubuntu";
  fontsize="13";
  bgcolor="lightgray";
  style="solid";
  penwidth="0.5";
	concentrate="true";

	// Node definition used for multiedge
  node [shape="square" style="filled" fillcolor="honeydew" fontname="Ubuntu" penwidth="1.0" margin="0.05,0.0.05"];

	bgcolor="#e6ecfa";
`

	nodeText = `  "{{.Name}}" [ shape="ellipse" fillcolor = "lightblue" label="{{.Label}}"]
`
	edgeText = `  "{{.From}}" -> "{{.To}}"
`
	footer = `
}
`
	nodeTmpl = template.Must(template.New("node").Parse(nodeText))
	edgeTmpl = template.Must(template.New("edge").Parse(edgeText))
)

type nodeLinks struct {
	From *graph.Outbound
	To   *graph.Inbound
	Node *graph.Node
}

func (n *nodeLinks) String() string {
	status := ""
	if n.Node == nil {
		status = "UNKNOWN NODE\n"
	}

	if n.From != nil && n.To != nil {
		return fmt.Sprintf("%sModel: %d: %v\nAs: %d: %v", status, n.To.From.ID(), n.To.Type, n.From.To.ID(), n.From.Type)
	}

	if n.To != nil {
		return fmt.Sprintf("%sModel: %d: %v", status, n.To.From.ID(), n.To.Type)
	}

	if n.From != nil {
		return fmt.Sprintf("%sAs: %d %v", status, n.From.To.ID(), n.From.Type)
	}

	return fmt.Sprintf("%sUNCONNECTED NODE", status)
}

// Render produces a DOT-compatible representation of the graph into the supplied io.Writer
func Render(edges []*graph.MultiEdge, nodes []*graph.Node, w io.Writer) error {
	uniqNodes := make(map[*graph.Node]*nodeLinks)

	// Create a lookup structure for all the created nodes. This helps identify
	// unconnected nodes (which shouldn't happen in practice).
	for _, node := range nodes {
		uniqNodes[node] = &nodeLinks{Node: node, From: nil, To: nil}
	}

	// Extract the node connectivity from the graph
	for _, edge := range edges {
		for _, ib := range edge.Input {
			uniqNodes[ib.From].To = ib
		}
		for _, ob := range edge.Output {
			uniqNodes[ob.To].From = ob
		}
	}

	// Render the graph elements: nodes and the edges
	w.Write([]byte(header))
	for node := range uniqNodes {
		err := nodeTmpl.Execute(w, struct{ Name, Label string }{node.String(), uniqNodes[node].String()})
		if err != nil {
			return err
		}
	}

	for _, edge := range edges {
		e := fmt.Sprintf("%d: %s", edge.ID(), edge.Op)
		for _, ib := range edge.Input {
			err := edgeTmpl.Execute(w, struct{ From, To string }{ib.From.String(), e})
			if err != nil {
				return fmt.Errorf("render DOT failed: %v", err)
			}
		}
		for _, ob := range edge.Output {
			uniqNodes[ob.To].From = ob
			err := edgeTmpl.Execute(w, struct{ From, To string }{e, ob.To.String()})
			if err != nil {
				return fmt.Errorf("render DOT failed: %v", err)
			}
		}
	}
	w.Write([]byte(footer))
	return nil
}
<commit_msg>Improve rendering of DOT diagrams.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 dot produces DOT graphs from Beam graph representations.
package dot

import (
	"fmt"
	"io"
	"text/template"

	"github.com/apache/beam/sdks/go/pkg/beam/core/graph"
)

var (
	header = `
digraph execution_plan {
  label="execution_plan"
  labeljust="l";
  fontname="Ubuntu";
  fontsize="13";
  bgcolor="lightgray";
  style="solid";
  penwidth="0.5";
	concentrate="true";

	// Node definition used for multiedge
  node [shape="rectangle" style="filled" fillcolor="honeydew" fontname="Ubuntu" penwidth="1.0" margin="0.05,0.0.05"];

	bgcolor="#e6ecfa";
`

	nodeText = `  "{{.Name}}" [ shape="ellipse" fillcolor = "lightblue" label="{{.Label}}"]
`
	edgeText = `  "{{.From}}" -> "{{.To}}"
`
	footer = `
}
`
	nodeTmpl = template.Must(template.New("node").Parse(nodeText))
	edgeTmpl = template.Must(template.New("edge").Parse(edgeText))
)

type nodeLinks struct {
	From *graph.Outbound
	To   *graph.Inbound
	Node *graph.Node
}

func (n *nodeLinks) String() string {
	status := ""
	if n.Node == nil {
		status = "UNKNOWN NODE\n"
	}

	if n.From != nil && n.To != nil {
		return fmt.Sprintf("%sModel: %d: %v\nAs: %d: %v", status, n.To.From.ID(), n.To.Type, n.From.To.ID(), n.From.Type)
	}

	if n.To != nil {
		return fmt.Sprintf("%sModel: %d: %v", status, n.To.From.ID(), n.To.Type)
	}

	if n.From != nil {
		return fmt.Sprintf("%sAs: %d %v", status, n.From.To.ID(), n.From.Type)
	}

	return fmt.Sprintf("%sUNCONNECTED NODE", status)
}

// Render produces a DOT-compatible representation of the graph into the supplied io.Writer
func Render(edges []*graph.MultiEdge, nodes []*graph.Node, w io.Writer) error {
	uniqNodes := make(map[*graph.Node]*nodeLinks)

	// Create a lookup structure for all the created nodes. This helps identify
	// unconnected nodes (which shouldn't happen in practice).
	for _, node := range nodes {
		uniqNodes[node] = &nodeLinks{Node: node, From: nil, To: nil}
	}

	// Extract the node connectivity from the graph
	for _, edge := range edges {
		for _, ib := range edge.Input {
			uniqNodes[ib.From].To = ib
		}
		for _, ob := range edge.Output {
			uniqNodes[ob.To].From = ob
		}
	}

	// Render the graph elements: nodes and the edges
	w.Write([]byte(header))
	for node := range uniqNodes {
		err := nodeTmpl.Execute(w, struct{ Name, Label string }{node.String(), uniqNodes[node].String()})
		if err != nil {
			return err
		}
	}

	for _, edge := range edges {
		e := fmt.Sprintf("%d: %s", edge.ID(), edge.Op)
		for _, ib := range edge.Input {
			err := edgeTmpl.Execute(w, struct{ From, To string }{ib.From.String(), e})
			if err != nil {
				return fmt.Errorf("render DOT failed: %v", err)
			}
		}
		for _, ob := range edge.Output {
			uniqNodes[ob.To].From = ob
			err := edgeTmpl.Execute(w, struct{ From, To string }{e, ob.To.String()})
			if err != nil {
				return fmt.Errorf("render DOT failed: %v", err)
			}
		}
	}
	w.Write([]byte(footer))
	return nil
}
<|endoftext|>


<commit_before>package webhook

import (
	"sync/atomic"
	"time"
)

const (
	DEFAULT_EXPIRATION_DURATION time.Duration = time.Second * 300
)

// W is the structure that represents the Webhook listener
// data we share.
//
// (Note to Wes: this follows the golang naming conventions.  webhook.Webhook "stutters",
// and this type is really the central type of this package.  Calling it a single letter is the norm.
// This could also go in the server package, in which case I'd change the name to Webhook, since
// service.Webhook works better.  See https://blog.golang.org/package-names)
type W struct {
	// Configuration for message delivery
	Config struct {
		// The URL to deliver messages to.
		URL string `json:"url"`

		// The content-type to set the messages to (unless specified by WRP).
		ContentType string `json:"content_type"`

		// The secret to use for the SHA1 HMAC.
		// Optional, set to "" to disable behavior.
		Secret string `json:"secret,omitempty"`
	} `json:"config"`

	// The URL to notify when we cut off a client due to overflow.
	// Optional, set to "" to disable behavior
	FailureURL string `json:"failure_url"`

	// The list of regular expressions to match event type against.
	Events []string `json:"events"`

	// Matcher type contains values to match against the metadata.
	Matcher struct {
		// The list of regular expressions to match device id type against.
		DeviceId []string `json:"device_id"`
	} `json:"matcher,omitempty"`

	// The specified duration for this hook to live
	Duration time.Duration `json:"duration"`

	// The absolute time when this hook is to be disabled
	Until time.Time `json:"until"`

	// The address that performed the registration
	Address string `json:"registered_from_address"`
}

// ID creates the canonical string identifing a WebhookListener
func (w *W) ID() string {
	return w.Config.URL
}

// durationValidator performs a check on a W.Duration value.
// if found to be invalid it is set to the DEFAULT_EXPIRATION_DURATION
func (w *W) DurationValidator() {
	if w.Duration < 0 || w.Duration > DEFAULT_EXPIRATION_DURATION {
		w.Duration = DEFAULT_EXPIRATION_DURATION
	}
}

// List is a read-only random access interface to a set of W's
// We don't necessarily need an implementation of just this interface alone.
type List interface {
	Len() int
	Get(int) *W
}

// UpdatableList is mutable list that can be updated en masse
type UpdatableList interface {
	List

	// Update performs a bulk update of this webhooks known to this list
	Update([]W)

	// Filter atomically filters the elements of this list
	Filter(func([]W) []W)
}

type updatableList struct {
	value atomic.Value
}

func (ul *updatableList) set(list []W) {
	ul.value.Store(list)
}

func (ul *updatableList) Len() int {
	if list, ok := ul.value.Load().([]W); ok {
		return len(list)
	}

	return 0
}

func (ul *updatableList) Get(index int) *W {
	if list, ok := ul.value.Load().([]W); ok {
		return &list[index]
	}

	// TODO: design choice.  may want to panic here, to mimic
	// the behavior of the golang runtime for slices.  Alternatively,
	// could return a second parameter that is an error (consistentHash does that).
	return nil
}

func (ul *updatableList) Update(newItems []W) {
	for _, newItem := range newItems {
		found := false
		var items []*W
		for i := 0; i < ul.Len(); i++ {
			items = append(items, ul.Get(i))
		}

		newItem.DurationValidator()
		newItem.Until = time.Now().Add(newItem.Duration)

		// update item
		for i := 0; i < len(items) && !found; i++ {
			if items[i].ID() == newItem.ID() {
				found = true

				items[i].Matcher = newItem.Matcher
				items[i].Events = newItem.Events
				items[i].Config.ContentType = newItem.Config.ContentType
				items[i].Config.Secret = newItem.Config.Secret
			}
		}

		// add item
		if !found {
			items = append(items, &newItem)
		}

		var itemsCopy []W
		for _, i := range items {
			itemsCopy = append(itemsCopy, *i)
		}

		// store items
		ul.set(itemsCopy)
	}
}

func (ul *updatableList) Filter(filter func([]W) []W) {
	if list, ok := ul.value.Load().([]W); ok {
		copyOf := make([]W, len(list))
		for i, w := range list {
			copyOf[i] = w
		}

		ul.set(filter(copyOf))
	}
}

// NewList just creates an UpdatableList.  Don't forget:
// NewList(nil) is valid!
func NewList(initial []W) UpdatableList {
	ul := &updatableList{}
	ul.Update(initial)
	return ul
}
<commit_msg>fixes webhook registration if duration is 0 will set to 300s<commit_after>package webhook

import (
	"sync/atomic"
	"time"
)

const (
	DEFAULT_EXPIRATION_DURATION time.Duration = time.Second * 300
)

// W is the structure that represents the Webhook listener
// data we share.
//
// (Note to Wes: this follows the golang naming conventions.  webhook.Webhook "stutters",
// and this type is really the central type of this package.  Calling it a single letter is the norm.
// This could also go in the server package, in which case I'd change the name to Webhook, since
// service.Webhook works better.  See https://blog.golang.org/package-names)
type W struct {
	// Configuration for message delivery
	Config struct {
		// The URL to deliver messages to.
		URL string `json:"url"`

		// The content-type to set the messages to (unless specified by WRP).
		ContentType string `json:"content_type"`

		// The secret to use for the SHA1 HMAC.
		// Optional, set to "" to disable behavior.
		Secret string `json:"secret,omitempty"`
	} `json:"config"`

	// The URL to notify when we cut off a client due to overflow.
	// Optional, set to "" to disable behavior
	FailureURL string `json:"failure_url"`

	// The list of regular expressions to match event type against.
	Events []string `json:"events"`

	// Matcher type contains values to match against the metadata.
	Matcher struct {
		// The list of regular expressions to match device id type against.
		DeviceId []string `json:"device_id"`
	} `json:"matcher,omitempty"`

	// The specified duration for this hook to live
	Duration time.Duration `json:"duration"`

	// The absolute time when this hook is to be disabled
	Until time.Time `json:"until"`

	// The address that performed the registration
	Address string `json:"registered_from_address"`
}

// ID creates the canonical string identifing a WebhookListener
func (w *W) ID() string {
	return w.Config.URL
}

// durationValidator performs a check on a W.Duration value.
// if found to be invalid it is set to the DEFAULT_EXPIRATION_DURATION
func (w *W) DurationValidator() {
	if w.Duration <= 0 || w.Duration > DEFAULT_EXPIRATION_DURATION {
		w.Duration = DEFAULT_EXPIRATION_DURATION
	}
}

// List is a read-only random access interface to a set of W's
// We don't necessarily need an implementation of just this interface alone.
type List interface {
	Len() int
	Get(int) *W
}

// UpdatableList is mutable list that can be updated en masse
type UpdatableList interface {
	List

	// Update performs a bulk update of this webhooks known to this list
	Update([]W)

	// Filter atomically filters the elements of this list
	Filter(func([]W) []W)
}

type updatableList struct {
	value atomic.Value
}

func (ul *updatableList) set(list []W) {
	ul.value.Store(list)
}

func (ul *updatableList) Len() int {
	if list, ok := ul.value.Load().([]W); ok {
		return len(list)
	}

	return 0
}

func (ul *updatableList) Get(index int) *W {
	if list, ok := ul.value.Load().([]W); ok {
		return &list[index]
	}

	// TODO: design choice.  may want to panic here, to mimic
	// the behavior of the golang runtime for slices.  Alternatively,
	// could return a second parameter that is an error (consistentHash does that).
	return nil
}

func (ul *updatableList) Update(newItems []W) {
	for _, newItem := range newItems {
		found := false
		var items []*W
		for i := 0; i < ul.Len(); i++ {
			items = append(items, ul.Get(i))
		}

		newItem.DurationValidator()
		newItem.Until = time.Now().Add(newItem.Duration)

		// update item
		for i := 0; i < len(items) && !found; i++ {
			if items[i].ID() == newItem.ID() {
				found = true

				items[i].Matcher = newItem.Matcher
				items[i].Events = newItem.Events
				items[i].Config.ContentType = newItem.Config.ContentType
				items[i].Config.Secret = newItem.Config.Secret
			}
		}

		// add item
		if !found {
			items = append(items, &newItem)
		}

		var itemsCopy []W
		for _, i := range items {
			itemsCopy = append(itemsCopy, *i)
		}

		// store items
		ul.set(itemsCopy)
	}
}

func (ul *updatableList) Filter(filter func([]W) []W) {
	if list, ok := ul.value.Load().([]W); ok {
		copyOf := make([]W, len(list))
		for i, w := range list {
			copyOf[i] = w
		}

		ul.set(filter(copyOf))
	}
}

// NewList just creates an UpdatableList.  Don't forget:
// NewList(nil) is valid!
func NewList(initial []W) UpdatableList {
	ul := &updatableList{}
	ul.Update(initial)
	return ul
}
<|endoftext|>


<commit_before>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package app

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/api/auth"
	"github.com/globocom/tsuru/api/bind"
	"github.com/globocom/tsuru/api/service"
	"github.com/globocom/tsuru/db"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/repository"
	"io"
	"labix.org/v2/mgo/bson"
	"launchpad.net/goyaml"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"
	"time"
)

const confSep = "========"

type App struct {
	Env       map[string]bind.EnvVar
	Framework string
	Logs      []applog
	Name      string
	State     string
	Units     []Unit
	Teams     []string
}

func (a *App) MarshalJSON() ([]byte, error) {
	result := make(map[string]interface{})
	result["Name"] = a.Name
	result["State"] = a.State
	result["Framework"] = a.Framework
	result["Teams"] = a.Teams
	result["Units"] = a.Units
	result["Repository"] = repository.GetUrl(a.Name)
	return json.Marshal(&result)
}

type applog struct {
	Date    time.Time
	Message string
}

type conf struct {
	PreRestart []string `yaml:"pre-restart"`
	PosRestart []string `yaml:"pos-restart"`
}

func (a *App) Get() error {
	return db.Session.Apps().Find(bson.M{"name": a.Name}).One(a)
}

// createApp creates a new app.
//
// Creating a new app is a process composed of two steps:
//
//       1. Saves the app in the database
//       2. Deploys juju charm
func createApp(a *App) error {
	a.State = "pending"
	err := db.Session.Apps().Insert(a)
	if err != nil {
		return err
	}
	return deploy(a)
}

// Deploys an app.
func deploy(a *App) error {
	a.log(fmt.Sprintf("creating app %s", a.Name))
	cmd := exec.Command("juju", "deploy", "--repository=/home/charms", "local:"+a.Framework, a.Name)
	log.Printf("deploying %s with name %s", a.Framework, a.Name)
	out, err := cmd.CombinedOutput()
	outStr := string(out)
	a.log(outStr)
	log.Printf("executing %s", outStr)
	if err != nil {
		a.log(fmt.Sprintf("juju finished with exit status: %s", err))
		db.Session.Apps().Remove(bson.M{"name": a.Name})
		return errors.New(outStr)
	}
	return nil
}

func (a *App) unbind() error {
	var instances []service.ServiceInstance
	err := db.Session.ServiceInstances().Find(bson.M{"apps": bson.M{"$in": []string{a.Name}}}).All(&instances)
	if err != nil {
		return err
	}
	var msg string
	var addMsg = func(instanceName string, reason error) {
		if msg == "" {
			msg = "Failed to unbind the following instances:\n"
		}
		msg += fmt.Sprintf("- %s (%s)", instanceName, reason.Error())
	}
	for _, instance := range instances {
		err = instance.Unbind(a)
		if err != nil {
			addMsg(instance.Name, err)
		}
	}
	if msg != "" {
		return errors.New(msg)
	}
	return nil
}

func (a *App) destroy() error {
	out, err := a.unit().destroy()
	msg := string(out)
	log.Print(msg)
	if err != nil {
		return errors.New(msg)
	}
	err = a.unbind()
	if err != nil {
		return err
	}
	return db.Session.Apps().Remove(bson.M{"name": a.Name})
}

func (a *App) AddUnit(u *Unit) {
	for i, unt := range a.Units {
		if unt.Machine == u.Machine {
			a.Units[i] = *u
			return
		}
	}
	a.Units = append(a.Units, *u)
}

func (a *App) find(team *auth.Team) (int, bool) {
	pos := sort.Search(len(a.Teams), func(i int) bool {
		return a.Teams[i] >= team.Name
	})
	return pos, pos < len(a.Teams) && a.Teams[pos] == team.Name
}

func (a *App) grant(team *auth.Team) error {
	pos, found := a.find(team)
	if found {
		return errors.New("This team already has access to this app")
	}
	a.Teams = append(a.Teams, "")
	tmp := a.Teams[pos]
	for i := pos; i < len(a.Teams)-1; i++ {
		a.Teams[i+1], tmp = tmp, a.Teams[i]
	}
	a.Teams[pos] = team.Name
	return nil
}

func (a *App) revoke(team *auth.Team) error {
	index, found := a.find(team)
	if !found {
		return errors.New("This team does not have access to this app")
	}
	copy(a.Teams[index:], a.Teams[index+1:])
	a.Teams = a.Teams[:len(a.Teams)-1]
	return nil
}

func (a *App) teams() []auth.Team {
	var teams []auth.Team
	db.Session.Teams().Find(bson.M{"_id": bson.M{"$in": a.Teams}}).All(&teams)
	return teams
}

func (a *App) setTeams(teams []auth.Team) {
	a.Teams = make([]string, len(teams))
	for i, team := range teams {
		a.Teams[i] = team.Name
	}
	sort.Strings(a.Teams)
}

func (a *App) setEnv(env bind.EnvVar) {
	if a.Env == nil {
		a.Env = make(map[string]bind.EnvVar)
	}
	a.Env[env.Name] = env
	a.log(fmt.Sprintf("setting env %s with value %s", env.Name, env.Value))
}

func (a *App) getEnv(name string) (bind.EnvVar, error) {
	var (
		env bind.EnvVar
		err error
		ok  bool
	)
	if env, ok = a.Env[name]; !ok {
		err = errors.New("Environment variable not declared for this app.")
	}
	return env, err
}

func (a *App) InstanceEnv(name string) map[string]bind.EnvVar {
	envs := make(map[string]bind.EnvVar)
	for k, env := range a.Env {
		if env.InstanceName == name {
			envs[k] = bind.EnvVar(env)
		}
	}
	return envs
}

func deployHookAbsPath(p string) (string, error) {
	repoPath, err := config.GetString("git:unit-repo")
	if err != nil {
		return "", nil
	}
	cmdArgs := strings.Fields(p)
	abs := path.Join(repoPath, cmdArgs[0])
	_, err = os.Stat(abs)
	if os.IsNotExist(err) {
		return p, nil
	}
	cmdArgs[0] = abs
	return strings.Join(cmdArgs, " "), nil
}

// Returns app.conf located at app's git repository
func (a *App) conf() (conf, error) {
	var c conf
	uRepo, err := repository.GetPath()
	if err != nil {
		a.log(fmt.Sprintf("Got error while getting repository path: %s", err.Error()))
		return c, err
	}
	cPath := path.Join(uRepo, "app.conf")
	cmd := fmt.Sprintf(`echo "%s";cat %s`, confSep, cPath)
	o, err := a.unit().Command(nil, nil, cmd)
	if err != nil {
		a.log(fmt.Sprintf("Got error while executing command: %s... Skipping hooks execution", err.Error()))
		return c, nil
	}
	data := strings.Split(string(o), confSep)[1]
	err = goyaml.Unmarshal([]byte(data), &c)
	if err != nil {
		a.log(fmt.Sprintf("Got error while parsing yaml: %s", err.Error()))
		return c, err
	}
	return c, nil
}

func (a *App) runHook(cmds []string, kind string) ([]byte, error) {
	var (
		buf bytes.Buffer
		err error
	)
	a.log(fmt.Sprintf("Executing %s hook...", kind))
	for _, cmd := range cmds {
		p, err := deployHookAbsPath(cmd)
		if err != nil {
			a.log(fmt.Sprintf("Error obtaining absolute path to hook: %s.", err))
			continue
		}
		err = a.run(p, &buf)
		if err != nil {
			return nil, err
		}
		a.log(fmt.Sprintf("Output of %s (%s hook): %s", p, kind, buf.Bytes()))
	}
	return buf.Bytes(), err
}

/*
* preRestart is responsible for running user's pre-restart script.
* The path to this script can be found at the app.conf file, at the root of user's app repository.
 */
func (a *App) preRestart(c conf) ([]byte, error) {
	if !a.hasRestartHooks(c) {
		a.log("app.conf file does not exists or is in the right place. Skipping pre-restart hook...")
		return []byte(nil), nil
	}
	if len(c.PreRestart) == 0 {
		a.log("pre-restart hook section in app conf does not exists... Skipping pre-restart hook...")
		return []byte(nil), nil
	}
	return a.runHook(c.PreRestart, "pre-restart")
}

/*
* posRestart is responsible for running user's pos-restart script.
* The path to this script can be found at the app.conf file, at the root of user's app repository.
 */
func (a *App) posRestart(c conf) ([]byte, error) {
	if !a.hasRestartHooks(c) {
		a.log("app.conf file does not exists or is in the right place. Skipping pos-restart hook...")
		return []byte(nil), nil
	}
	if len(c.PosRestart) == 0 {
		a.log("pos-restart hook section in app conf does not exists... Skipping pos-restart hook...")
		return []byte(nil), nil
	}
	return a.runHook(c.PosRestart, "pos-restart")
}

func (a *App) hasRestartHooks(c conf) bool {
	return len(c.PreRestart) > 0 || len(c.PosRestart) > 0
}

func (a *App) run(cmd string, w io.Writer) error {
	a.log(fmt.Sprintf("running '%s'", cmd))
	cmd = fmt.Sprintf("[ -f /home/application/apprc ] && source /home/application/apprc; [ -d /home/application/current ] && cd /home/application/current; %s", cmd)
	out, err := a.unit().Command(w, w, cmd)
	a.log(string(out))
	return err
}

// restart runs the restart hook for the app
// and returns your output.
func restart(a *App, w io.Writer) ([]byte, error) {
	u := a.unit()
	a.log("executting hook to restarting")
	if w != nil {
		_, err := w.Write([]byte("\n ---> Restarting your app\n"))
		if err != nil {
			return nil, err
		}
	}
	out, err := u.executeHook(w, w, "restart")
	if err != nil {
		return out, err
	}
	a.log(string(out))
	return out, nil
}

//installDeps runs the dependencies hook for the app
//and returns your output.
func installDeps(a *App, stdout, stderr io.Writer) ([]byte, error) {
	u := a.unit()
	a.log("executting hook dependencies")
	out, err := u.executeHook(stdout, stderr, "dependencies")
	a.log(string(out))
	if err != nil {
		return out, err
	}
	return out, nil
}

func (a *App) unit() *Unit {
	if len(a.Units) > 0 {
		unit := a.Units[0]
		unit.app = a
		return &unit
	}
	return &Unit{app: a}
}

func (a *App) GetUnits() []bind.Unit {
	var units []bind.Unit
	for _, u := range a.Units {
		u.app = a
		units = append(units, &u)
	}
	return units
}

func (a *App) GetName() string {
	return a.Name
}

func (a *App) SetEnvs(envs []bind.EnvVar, publicOnly bool) error {
	e := make([]bind.EnvVar, len(envs))
	for i, env := range envs {
		e[i] = bind.EnvVar(env)
	}
	return setEnvsToApp(a, e, publicOnly)
}

func (a *App) UnsetEnvs(envs []string, publicOnly bool) error {
	return unsetEnvFromApp(a, envs, publicOnly)
}

func (a *App) log(message string) error {
	log.Printf(message)
	l := applog{Date: time.Now(), Message: message}
	a.Logs = append(a.Logs, l)
	return db.Session.Apps().Update(bson.M{"name": a.Name}, a)
}
<commit_msg>api/app: printing all output once<commit_after>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package app

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/api/auth"
	"github.com/globocom/tsuru/api/bind"
	"github.com/globocom/tsuru/api/service"
	"github.com/globocom/tsuru/db"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/repository"
	"io"
	"labix.org/v2/mgo/bson"
	"launchpad.net/goyaml"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"
	"time"
)

const confSep = "========"

type App struct {
	Env       map[string]bind.EnvVar
	Framework string
	Logs      []applog
	Name      string
	State     string
	Units     []Unit
	Teams     []string
}

func (a *App) MarshalJSON() ([]byte, error) {
	result := make(map[string]interface{})
	result["Name"] = a.Name
	result["State"] = a.State
	result["Framework"] = a.Framework
	result["Teams"] = a.Teams
	result["Units"] = a.Units
	result["Repository"] = repository.GetUrl(a.Name)
	return json.Marshal(&result)
}

type applog struct {
	Date    time.Time
	Message string
}

type conf struct {
	PreRestart []string `yaml:"pre-restart"`
	PosRestart []string `yaml:"pos-restart"`
}

func (a *App) Get() error {
	return db.Session.Apps().Find(bson.M{"name": a.Name}).One(a)
}

// createApp creates a new app.
//
// Creating a new app is a process composed of two steps:
//
//       1. Saves the app in the database
//       2. Deploys juju charm
func createApp(a *App) error {
	a.State = "pending"
	err := db.Session.Apps().Insert(a)
	if err != nil {
		return err
	}
	return deploy(a)
}

// Deploys an app.
func deploy(a *App) error {
	a.log(fmt.Sprintf("creating app %s", a.Name))
	cmd := exec.Command("juju", "deploy", "--repository=/home/charms", "local:"+a.Framework, a.Name)
	log.Printf("deploying %s with name %s", a.Framework, a.Name)
	out, err := cmd.CombinedOutput()
	outStr := string(out)
	a.log(outStr)
	log.Printf("executing %s", outStr)
	if err != nil {
		a.log(fmt.Sprintf("juju finished with exit status: %s", err))
		db.Session.Apps().Remove(bson.M{"name": a.Name})
		return errors.New(outStr)
	}
	return nil
}

func (a *App) unbind() error {
	var instances []service.ServiceInstance
	err := db.Session.ServiceInstances().Find(bson.M{"apps": bson.M{"$in": []string{a.Name}}}).All(&instances)
	if err != nil {
		return err
	}
	var msg string
	var addMsg = func(instanceName string, reason error) {
		if msg == "" {
			msg = "Failed to unbind the following instances:\n"
		}
		msg += fmt.Sprintf("- %s (%s)", instanceName, reason.Error())
	}
	for _, instance := range instances {
		err = instance.Unbind(a)
		if err != nil {
			addMsg(instance.Name, err)
		}
	}
	if msg != "" {
		return errors.New(msg)
	}
	return nil
}

func (a *App) destroy() error {
	out, err := a.unit().destroy()
	msg := string(out)
	log.Print(msg)
	if err != nil {
		return errors.New(msg)
	}
	err = a.unbind()
	if err != nil {
		return err
	}
	return db.Session.Apps().Remove(bson.M{"name": a.Name})
}

func (a *App) AddUnit(u *Unit) {
	for i, unt := range a.Units {
		if unt.Machine == u.Machine {
			a.Units[i] = *u
			return
		}
	}
	a.Units = append(a.Units, *u)
}

func (a *App) find(team *auth.Team) (int, bool) {
	pos := sort.Search(len(a.Teams), func(i int) bool {
		return a.Teams[i] >= team.Name
	})
	return pos, pos < len(a.Teams) && a.Teams[pos] == team.Name
}

func (a *App) grant(team *auth.Team) error {
	pos, found := a.find(team)
	if found {
		return errors.New("This team already has access to this app")
	}
	a.Teams = append(a.Teams, "")
	tmp := a.Teams[pos]
	for i := pos; i < len(a.Teams)-1; i++ {
		a.Teams[i+1], tmp = tmp, a.Teams[i]
	}
	a.Teams[pos] = team.Name
	return nil
}

func (a *App) revoke(team *auth.Team) error {
	index, found := a.find(team)
	if !found {
		return errors.New("This team does not have access to this app")
	}
	copy(a.Teams[index:], a.Teams[index+1:])
	a.Teams = a.Teams[:len(a.Teams)-1]
	return nil
}

func (a *App) teams() []auth.Team {
	var teams []auth.Team
	db.Session.Teams().Find(bson.M{"_id": bson.M{"$in": a.Teams}}).All(&teams)
	return teams
}

func (a *App) setTeams(teams []auth.Team) {
	a.Teams = make([]string, len(teams))
	for i, team := range teams {
		a.Teams[i] = team.Name
	}
	sort.Strings(a.Teams)
}

func (a *App) setEnv(env bind.EnvVar) {
	if a.Env == nil {
		a.Env = make(map[string]bind.EnvVar)
	}
	a.Env[env.Name] = env
	a.log(fmt.Sprintf("setting env %s with value %s", env.Name, env.Value))
}

func (a *App) getEnv(name string) (bind.EnvVar, error) {
	var (
		env bind.EnvVar
		err error
		ok  bool
	)
	if env, ok = a.Env[name]; !ok {
		err = errors.New("Environment variable not declared for this app.")
	}
	return env, err
}

func (a *App) InstanceEnv(name string) map[string]bind.EnvVar {
	envs := make(map[string]bind.EnvVar)
	for k, env := range a.Env {
		if env.InstanceName == name {
			envs[k] = bind.EnvVar(env)
		}
	}
	return envs
}

func deployHookAbsPath(p string) (string, error) {
	repoPath, err := config.GetString("git:unit-repo")
	if err != nil {
		return "", nil
	}
	cmdArgs := strings.Fields(p)
	abs := path.Join(repoPath, cmdArgs[0])
	_, err = os.Stat(abs)
	if os.IsNotExist(err) {
		return p, nil
	}
	cmdArgs[0] = abs
	return strings.Join(cmdArgs, " "), nil
}

// Returns app.conf located at app's git repository
func (a *App) conf() (conf, error) {
	var c conf
	uRepo, err := repository.GetPath()
	if err != nil {
		a.log(fmt.Sprintf("Got error while getting repository path: %s", err.Error()))
		return c, err
	}
	cPath := path.Join(uRepo, "app.conf")
	cmd := fmt.Sprintf(`echo "%s";cat %s`, confSep, cPath)
	o, err := a.unit().Command(nil, nil, cmd)
	if err != nil {
		a.log(fmt.Sprintf("Got error while executing command: %s... Skipping hooks execution", err.Error()))
		return c, nil
	}
	data := strings.Split(string(o), confSep)[1]
	err = goyaml.Unmarshal([]byte(data), &c)
	if err != nil {
		a.log(fmt.Sprintf("Got error while parsing yaml: %s", err.Error()))
		return c, err
	}
	return c, nil
}

func (a *App) runHook(cmds []string, kind string) ([]byte, error) {
	var (
		buf bytes.Buffer
		err error
	)
	a.log(fmt.Sprintf("Executing %s hook...", kind))
	for _, cmd := range cmds {
		p, err := deployHookAbsPath(cmd)
		if err != nil {
			a.log(fmt.Sprintf("Error obtaining absolute path to hook: %s.", err))
			continue
		}
		err = a.run(p, &buf)
		if err != nil {
			return nil, err
		}
	}
	a.log(fmt.Sprintf("Output of %s hooks: %s", kind, buf.Bytes()))
	return buf.Bytes(), err
}

/*
* preRestart is responsible for running user's pre-restart script.
* The path to this script can be found at the app.conf file, at the root of user's app repository.
 */
func (a *App) preRestart(c conf) ([]byte, error) {
	if !a.hasRestartHooks(c) {
		a.log("app.conf file does not exists or is in the right place. Skipping pre-restart hook...")
		return []byte(nil), nil
	}
	if len(c.PreRestart) == 0 {
		a.log("pre-restart hook section in app conf does not exists... Skipping pre-restart hook...")
		return []byte(nil), nil
	}
	return a.runHook(c.PreRestart, "pre-restart")
}

/*
* posRestart is responsible for running user's pos-restart script.
* The path to this script can be found at the app.conf file, at the root of user's app repository.
 */
func (a *App) posRestart(c conf) ([]byte, error) {
	if !a.hasRestartHooks(c) {
		a.log("app.conf file does not exists or is in the right place. Skipping pos-restart hook...")
		return []byte(nil), nil
	}
	if len(c.PosRestart) == 0 {
		a.log("pos-restart hook section in app conf does not exists... Skipping pos-restart hook...")
		return []byte(nil), nil
	}
	return a.runHook(c.PosRestart, "pos-restart")
}

func (a *App) hasRestartHooks(c conf) bool {
	return len(c.PreRestart) > 0 || len(c.PosRestart) > 0
}

func (a *App) run(cmd string, w io.Writer) error {
	a.log(fmt.Sprintf("running '%s'", cmd))
	cmd = fmt.Sprintf("[ -f /home/application/apprc ] && source /home/application/apprc; [ -d /home/application/current ] && cd /home/application/current; %s", cmd)
	out, err := a.unit().Command(w, w, cmd)
	a.log(string(out))
	return err
}

// restart runs the restart hook for the app
// and returns your output.
func restart(a *App, w io.Writer) ([]byte, error) {
	u := a.unit()
	a.log("executting hook to restarting")
	if w != nil {
		_, err := w.Write([]byte("\n ---> Restarting your app\n"))
		if err != nil {
			return nil, err
		}
	}
	out, err := u.executeHook(w, w, "restart")
	if err != nil {
		return out, err
	}
	a.log(string(out))
	return out, nil
}

//installDeps runs the dependencies hook for the app
//and returns your output.
func installDeps(a *App, stdout, stderr io.Writer) ([]byte, error) {
	u := a.unit()
	a.log("executting hook dependencies")
	out, err := u.executeHook(stdout, stderr, "dependencies")
	a.log(string(out))
	if err != nil {
		return out, err
	}
	return out, nil
}

func (a *App) unit() *Unit {
	if len(a.Units) > 0 {
		unit := a.Units[0]
		unit.app = a
		return &unit
	}
	return &Unit{app: a}
}

func (a *App) GetUnits() []bind.Unit {
	var units []bind.Unit
	for _, u := range a.Units {
		u.app = a
		units = append(units, &u)
	}
	return units
}

func (a *App) GetName() string {
	return a.Name
}

func (a *App) SetEnvs(envs []bind.EnvVar, publicOnly bool) error {
	e := make([]bind.EnvVar, len(envs))
	for i, env := range envs {
		e[i] = bind.EnvVar(env)
	}
	return setEnvsToApp(a, e, publicOnly)
}

func (a *App) UnsetEnvs(envs []string, publicOnly bool) error {
	return unsetEnvFromApp(a, envs, publicOnly)
}

func (a *App) log(message string) error {
	log.Printf(message)
	l := applog{Date: time.Now(), Message: message}
	a.Logs = append(a.Logs, l)
	return db.Session.Apps().Update(bson.M{"name": a.Name}, a)
}
<|endoftext|>