<commit_before>// status.go - status defines a Status struct for each node in the cluster,
// providing four attributes (CRole, DBRole, State, UpdateAt) as a way of
// determining each nodes role in the cluster, current state, and the role of
// the pgqsl running inside each (non-monitor) node.
//
// status provides methods for updating the DBRole and State as the clusters
// environment changes due to outages, and also provides methods of retrieving
// information about each node in the cluster or about the cluster as a whole.

package main

import (
	"fmt"
	"net"
	"net/rpc"
	"strconv"
	"time"

	"github.com/nanobox-core/scribble"
)

// Status represents the Status of node in the cluser
type Status struct {
	CRole     string    // the nodes 'role' in the cluster (primary, secondary, monitor)
	DataDir   string    // directory of the postgres database
	DBRole    string    // the 'role' of the running pgsql instance inside the node (master, slave)
	Ip 				string 		//
	PGPort 		int 			//
	State     string    // the current state of the node
	UpdatedAt time.Time // the last time the node state was updated
}

var (
	status *Status
	store  *scribble.Driver
)

// StatusStart
func StatusStart() error {

	// create a new scribble store
	store = scribble.New(conf.StatusDir, log)
	status = &Status{}

	// the current node will attempt to discover who they are in the event of an
	// outage, or be created for the first time
	s, err := Whoami()

	// no record found that matches the current node
	if err != nil {
		log.Warn("[status.StatusStart] 404 Not found: No record found for '%s'\n", conf.Role)

		// create a new record in scribble for the current node
		status = &Status{
			CRole:     conf.Role,
			DataDir:   conf.DataDir,
			DBRole:    "initialized",
			Ip: 		   conf.ClusterIP,
			PGPort:    conf.PGPort,
			State:     "booting",
			UpdatedAt: time.Now(),
		}

		log.Debug("[status.StatusStart] Creating record for '%s'\n", conf.Role)
		save(status)

		// found a record matching the current node
	} else {
		status = s
	}

	log.Debug("[status] Node Status: %+v\n", status)

	// register our Status struct with RPC
	rpc.Register(status)

	log.Info("[status] Starting RPC server...\n")

	// fire up an RPC (tcp) server
	l, err := net.Listen("tcp", ":"+strconv.FormatInt(int64(conf.AdvertisePort+1), 10))
	if err != nil {
		log.Error("[status] Unable to start server!\n%v\n", err)
		return err
	}

	// daemonize the server
	go func(l net.Listener) {

		// accept connections on the RPC server
		for {
			if conn, err := l.Accept(); err != nil {
				log.Error("[status] RPC server - failed to accept connection!\n%s%n", err.Error())
			} else {
				log.Debug("[status] RPC server - new connection established!\n")

				//
				go rpc.ServeConn(conn)
			}
		}
	}(l)

	return nil
}

// Whoami attempts to pull a matching record from scribble for the local node
// returned from memberlist
func Whoami() (*Status, error) {
	log.Debug("[status.Whoami] list.LocalNode() - %+v", list.LocalNode())

	s := &Status{}

	// attempt to pull a record from scribble for the current node
	if err := get(list.LocalNode().Name, s); err != nil {
		return nil, err
	}

	return s, nil
}

// Whois takes a 'role' string and iterates over all the nodes in memberlist looking
// for a matching node. If a matching node is found it then creates an RPC client
// which is used to make an RPC call to the matching node, which returns a Status
// object for that node
func Whois(role string) (*Status, error) {
	log.Debug("[status.Whois] Who is '%s'?", role)

	var conn string

	// find a matching node for the desired 'role'
	for _, m := range list.Members() {
		if m.Name == role {
			conn = fmt.Sprintf("%s:%s", m.Addr, strconv.FormatInt(int64(m.Port+1), 10))
		}
	}

	log.Debug("[status.Whois] connection - %s", conn)

	// create an RPC client that will connect to the matching node
	client, err := rpc.Dial("tcp", conn)
	if err != nil {
		log.Error("[status.Whois] RPC Client unable to dial!\n%s\n", err.Error())
		return nil, err
	}

	s := &Status{}

	// 'ping' the matching node to retrieve its Status
	if err := client.Call("Status.Ping", role, s); err != nil {
		log.Error("[status.Whois] RPC Client unable to call!\n%s\n", err.Error())
		return nil, err
	}

	//
	client.Close()

	return s, nil
}

// Cluster iterates over all the nodes in member list, running a Whois(), and
// storing each corresponding Status into a slice and returning the collection
func Cluster() ([]*Status, error) {
	// log.Info("[status.Cluster]")

	var members = []*Status{}

	// iterate over all nodes in member list
	for _, m := range list.Members() {

		// retrieve each nodes Status
		s, err := Whois(m.Name)
		if err != nil {
			return nil, err
		}

		// append each status into our slice of statuses
		members = append(members, s)
	}

	log.Debug("[status.Cluster] members - %+v", members)

	return members, nil
}

// SetDBRole takes a 'role' string and attempts to set the Status.DBRole, and then
// update the record via scribble
func (s *Status) SetDBRole(role string) {
	log.Debug("[status.SetDBRole] setting role '%s' on node '%s'\n", role, s.CRole)

	s.DBRole = role

	if err := save(s); err != nil {
		log.Fatal("[status.SetDBRole] Failed to save status! %s", err)
		panic(err)
	}

	s.UpdatedAt = time.Now()
}

// SetState takes a 'state' string and attempts to set the Status.State, and then
// update the record via scribble
func (s *Status) SetState(state string) {
	log.Debug("[status.SetState] setting '%s' on '%s'\n", state, s.CRole)

	s.State = state

	if err := save(s); err != nil {
		log.Fatal("[status.SetDBRole] Failed to save status! %s", err)
		panic(err)
	}
}

// Ping makes an RPC call to a desired node (by 'role') attempting to return the
// Status of that node. It will iterate through each node in memberlist until it
// finds a matching node
func (s *Status) Ping(role string, status *Status) error {

	log.Info("[status.Ping] pinging '%s'...", role)

	// iterate through each node in memberlist looking for a node whos name matches
	// the desired 'role'
	for _, m := range list.Members() {
		if m.Name == role {

			// attempt to retrieve that Status of the node from scribble
			if err := get(role, status); err != nil {
				log.Error("[status.Ping] Unable to read '%s'!\n%s\n", role, err.Error())
				return err
			}
		}
	}

	return nil
}

// Demote is used as a way to 'advise' the current node that it needs to demote
func (s *Status) Demote(source string, status *Status) error {
	log.Info("[status.Demote] Advising demote...")

	advice <- "demote"

	return nil
}

// get retrieves a Status from scribble by 'role'
func get(role string, status *Status) error {
	log.Debug("[status.get] Attempting to get node '%s'", role)

	t := scribble.Transaction{Operation: "read", Collection: "cluster", RecordID: role, Container: &status}
	if err := store.Transact(t); err != nil {
		return err
	}

	return nil
}

// save saves a Status to scribble by 'role'
func save(status *Status) error {
	log.Debug("[status.save] Attempting to save node '%s'", status.CRole)

	t := scribble.Transaction{Operation: "write", Collection: "cluster", RecordID: status.CRole, Container: &status}
	if err := store.Transact(t); err != nil {
		return err
	}

	return nil
}
<commit_msg>advertise ip<commit_after>// status.go - status defines a Status struct for each node in the cluster,
// providing four attributes (CRole, DBRole, State, UpdateAt) as a way of
// determining each nodes role in the cluster, current state, and the role of
// the pgqsl running inside each (non-monitor) node.
//
// status provides methods for updating the DBRole and State as the clusters
// environment changes due to outages, and also provides methods of retrieving
// information about each node in the cluster or about the cluster as a whole.

package main

import (
	"fmt"
	"net"
	"net/rpc"
	"strconv"
	"time"

	"github.com/nanobox-core/scribble"
)

// Status represents the Status of node in the cluser
type Status struct {
	CRole     string    // the nodes 'role' in the cluster (primary, secondary, monitor)
	DataDir   string    // directory of the postgres database
	DBRole    string    // the 'role' of the running pgsql instance inside the node (master, slave)
	Ip 				string 		//
	PGPort 		int 			//
	State     string    // the current state of the node
	UpdatedAt time.Time // the last time the node state was updated
}

var (
	status *Status
	store  *scribble.Driver
)

// StatusStart
func StatusStart() error {

	// create a new scribble store
	store = scribble.New(conf.StatusDir, log)
	status = &Status{}

	// the current node will attempt to discover who they are in the event of an
	// outage, or be created for the first time
	s, err := Whoami()

	// no record found that matches the current node
	if err != nil {
		log.Warn("[status.StatusStart] 404 Not found: No record found for '%s'\n", conf.Role)

		// create a new record in scribble for the current node
		status = &Status{
			CRole:     conf.Role,
			DataDir:   conf.DataDir,
			DBRole:    "initialized",
			Ip: 		   conf.AdvertiseIp,
			PGPort:    conf.PGPort,
			State:     "booting",
			UpdatedAt: time.Now(),
		}

		log.Debug("[status.StatusStart] Creating record for '%s'\n", conf.Role)
		save(status)

		// found a record matching the current node
	} else {
		status = s
	}

	log.Debug("[status] Node Status: %+v\n", status)

	// register our Status struct with RPC
	rpc.Register(status)

	log.Info("[status] Starting RPC server...\n")

	// fire up an RPC (tcp) server
	l, err := net.Listen("tcp", ":"+strconv.FormatInt(int64(conf.AdvertisePort+1), 10))
	if err != nil {
		log.Error("[status] Unable to start server!\n%v\n", err)
		return err
	}

	// daemonize the server
	go func(l net.Listener) {

		// accept connections on the RPC server
		for {
			if conn, err := l.Accept(); err != nil {
				log.Error("[status] RPC server - failed to accept connection!\n%s%n", err.Error())
			} else {
				log.Debug("[status] RPC server - new connection established!\n")

				//
				go rpc.ServeConn(conn)
			}
		}
	}(l)

	return nil
}

// Whoami attempts to pull a matching record from scribble for the local node
// returned from memberlist
func Whoami() (*Status, error) {
	log.Debug("[status.Whoami] list.LocalNode() - %+v", list.LocalNode())

	s := &Status{}

	// attempt to pull a record from scribble for the current node
	if err := get(list.LocalNode().Name, s); err != nil {
		return nil, err
	}

	return s, nil
}

// Whois takes a 'role' string and iterates over all the nodes in memberlist looking
// for a matching node. If a matching node is found it then creates an RPC client
// which is used to make an RPC call to the matching node, which returns a Status
// object for that node
func Whois(role string) (*Status, error) {
	log.Debug("[status.Whois] Who is '%s'?", role)

	var conn string

	// find a matching node for the desired 'role'
	for _, m := range list.Members() {
		if m.Name == role {
			conn = fmt.Sprintf("%s:%s", m.Addr, strconv.FormatInt(int64(m.Port+1), 10))
		}
	}

	log.Debug("[status.Whois] connection - %s", conn)

	// create an RPC client that will connect to the matching node
	client, err := rpc.Dial("tcp", conn)
	if err != nil {
		log.Error("[status.Whois] RPC Client unable to dial!\n%s\n", err.Error())
		return nil, err
	}

	s := &Status{}

	// 'ping' the matching node to retrieve its Status
	if err := client.Call("Status.Ping", role, s); err != nil {
		log.Error("[status.Whois] RPC Client unable to call!\n%s\n", err.Error())
		return nil, err
	}

	//
	client.Close()

	return s, nil
}

// Cluster iterates over all the nodes in member list, running a Whois(), and
// storing each corresponding Status into a slice and returning the collection
func Cluster() ([]*Status, error) {
	// log.Info("[status.Cluster]")

	var members = []*Status{}

	// iterate over all nodes in member list
	for _, m := range list.Members() {

		// retrieve each nodes Status
		s, err := Whois(m.Name)
		if err != nil {
			return nil, err
		}

		// append each status into our slice of statuses
		members = append(members, s)
	}

	log.Debug("[status.Cluster] members - %+v", members)

	return members, nil
}

// SetDBRole takes a 'role' string and attempts to set the Status.DBRole, and then
// update the record via scribble
func (s *Status) SetDBRole(role string) {
	log.Debug("[status.SetDBRole] setting role '%s' on node '%s'\n", role, s.CRole)

	s.DBRole = role

	if err := save(s); err != nil {
		log.Fatal("[status.SetDBRole] Failed to save status! %s", err)
		panic(err)
	}

	s.UpdatedAt = time.Now()
}

// SetState takes a 'state' string and attempts to set the Status.State, and then
// update the record via scribble
func (s *Status) SetState(state string) {
	log.Debug("[status.SetState] setting '%s' on '%s'\n", state, s.CRole)

	s.State = state

	if err := save(s); err != nil {
		log.Fatal("[status.SetDBRole] Failed to save status! %s", err)
		panic(err)
	}
}

// Ping makes an RPC call to a desired node (by 'role') attempting to return the
// Status of that node. It will iterate through each node in memberlist until it
// finds a matching node
func (s *Status) Ping(role string, status *Status) error {

	log.Info("[status.Ping] pinging '%s'...", role)

	// iterate through each node in memberlist looking for a node whos name matches
	// the desired 'role'
	for _, m := range list.Members() {
		if m.Name == role {

			// attempt to retrieve that Status of the node from scribble
			if err := get(role, status); err != nil {
				log.Error("[status.Ping] Unable to read '%s'!\n%s\n", role, err.Error())
				return err
			}
		}
	}

	return nil
}

// Demote is used as a way to 'advise' the current node that it needs to demote
func (s *Status) Demote(source string, status *Status) error {
	log.Info("[status.Demote] Advising demote...")

	advice <- "demote"

	return nil
}

// get retrieves a Status from scribble by 'role'
func get(role string, status *Status) error {
	log.Debug("[status.get] Attempting to get node '%s'", role)

	t := scribble.Transaction{Operation: "read", Collection: "cluster", RecordID: role, Container: &status}
	if err := store.Transact(t); err != nil {
		return err
	}

	return nil
}

// save saves a Status to scribble by 'role'
func save(status *Status) error {
	log.Debug("[status.save] Attempting to save node '%s'", status.CRole)

	t := scribble.Transaction{Operation: "write", Collection: "cluster", RecordID: status.CRole, Container: &status}
	if err := store.Transact(t); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package manager

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"syscall"

	"github.com/Symantec/Dominator/lib/format"
	proto "github.com/Symantec/Dominator/proto/hypervisor"
)

const (
	procMounts    string = "/proc/mounts"
	sysClassBlock        = "/sys/class/block"
)

type mountInfo struct {
	mountPoint string
	size       uint64
}

func getFreeSpace(dirname string, freeSpaceTable map[string]uint64) (
	uint64, error) {
	if freeSpace, ok := freeSpaceTable[dirname]; ok {
		return freeSpace, nil
	}
	var statbuf syscall.Statfs_t
	if err := syscall.Statfs(dirname, &statbuf); err != nil {
		return 0, fmt.Errorf("error statfsing: %s: %s", dirname, err)
	}
	freeSpace := uint64(statbuf.Bfree * uint64(statbuf.Bsize))
	freeSpaceTable[dirname] = freeSpace
	return freeSpace, nil
}

func getMounts() (map[string]string, error) {
	file, err := os.Open(procMounts)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	mounts := make(map[string]string)
	for scanner.Scan() {
		line := scanner.Text()
		var device, mountPoint, fsType, fsOptions, junk string
		nScanned, err := fmt.Sscanf(line, "%s %s %s %s %s",
			&device, &mountPoint, &fsType, &fsOptions, &junk)
		if err != nil {
			return nil, err
		}
		if nScanned < 4 {
			return nil, errors.New(fmt.Sprintf("only read %d values from %s",
				nScanned, line))
		}
		if mountPoint == "/boot" {
			continue
		}
		if !strings.HasPrefix(device, "/dev/") {
			continue
		}
		if target, err := filepath.EvalSymlinks(device); err != nil {
			return nil, err
		} else {
			device = target
		}
		device = device[5:]
		if _, ok := mounts[device]; !ok { // Pick the first mount point.
			mounts[device] = mountPoint
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return mounts, nil
}

func getVolumeDirectories() ([]string, error) {
	mounts, err := getMounts()
	if err != nil {
		return nil, err
	}
	var mountPointsToUse []string
	biggestMounts := make(map[string]mountInfo)
	for device, mountPoint := range mounts {
		if device == "root" { // Ignore this dumb shit.
			continue
		}
		sysDir := filepath.Join(sysClassBlock, device)
		linkTarget, err := os.Readlink(sysDir)
		if err != nil {
			return nil, err
		}
		_, err = os.Stat(filepath.Join(sysDir, "partition"))
		if err != nil {
			if os.IsNotExist(err) { // Not a partition: easy!
				mountPointsToUse = append(mountPointsToUse, mountPoint)
				continue
			}
			return nil, err
		}
		var statbuf syscall.Statfs_t
		if err := syscall.Statfs(mountPoint, &statbuf); err != nil {
			return nil, fmt.Errorf("error statfsing: %s: %s", mountPoint, err)
		}
		size := uint64(statbuf.Blocks * uint64(statbuf.Bsize))
		parentDevice := filepath.Base(filepath.Dir(linkTarget))
		if biggestMount, ok := biggestMounts[parentDevice]; !ok {
			biggestMounts[parentDevice] = mountInfo{mountPoint, size}
		} else if size > biggestMount.size {
			biggestMounts[parentDevice] = mountInfo{mountPoint, size}
		}
	}
	for _, biggestMount := range biggestMounts {
		mountPointsToUse = append(mountPointsToUse, biggestMount.mountPoint)
	}
	var volumeDirectories []string
	for _, mountPoint := range mountPointsToUse {
		volumeDirectories = append(volumeDirectories,
			filepath.Join(mountPoint, "hyper-volumes"))
	}
	sort.Strings(volumeDirectories)
	return volumeDirectories, nil
}

func (m *Manager) findFreeSpace(size uint64, freeSpaceTable map[string]uint64,
	position *int) (string, error) {
	if *position >= len(m.volumeDirectories) {
		*position = 0
	}
	startingPosition := *position
	for {
		freeSpace, err := getFreeSpace(m.volumeDirectories[*position],
			freeSpaceTable)
		if err != nil {
			return "", err
		}
		if size < freeSpace {
			dirname := m.volumeDirectories[*position]
			freeSpaceTable[dirname] -= size
			return dirname, nil
		}
		*position++
		if *position >= len(m.volumeDirectories) {
			*position = 0
		}
		if *position == startingPosition {
			return "", fmt.Errorf("not enough free space for %s volume",
				format.FormatBytes(size))
		}
	}
}

func (m *Manager) getVolumeDirectories(rootSize uint64,
	volumes []proto.Volume, spreadVolumes bool) ([]string, error) {
	sizes := make([]uint64, 1, len(volumes)+1)
	sizes[0] = rootSize
	for _, volume := range volumes {
		if volume.Size > 0 {
			sizes = append(sizes, volume.Size)
		}
	}
	freeSpaceTable := make(map[string]uint64, len(m.volumeDirectories))
	directoriesToUse := make([]string, 0, len(sizes))
	position := 0
	for len(sizes) > 0 {
		dirname, err := m.findFreeSpace(sizes[0], freeSpaceTable, &position)
		if err != nil {
			return nil, err
		}
		directoriesToUse = append(directoriesToUse, dirname)
		sizes = sizes[1:]
		if spreadVolumes {
			position++
		}
	}
	return directoriesToUse, nil
}
<commit_msg>staticcheck fix for hypervisor/manager package.<commit_after>package manager

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"syscall"

	"github.com/Symantec/Dominator/lib/format"
	proto "github.com/Symantec/Dominator/proto/hypervisor"
)

const (
	procMounts    = "/proc/mounts"
	sysClassBlock = "/sys/class/block"
)

type mountInfo struct {
	mountPoint string
	size       uint64
}

func getFreeSpace(dirname string, freeSpaceTable map[string]uint64) (
	uint64, error) {
	if freeSpace, ok := freeSpaceTable[dirname]; ok {
		return freeSpace, nil
	}
	var statbuf syscall.Statfs_t
	if err := syscall.Statfs(dirname, &statbuf); err != nil {
		return 0, fmt.Errorf("error statfsing: %s: %s", dirname, err)
	}
	freeSpace := uint64(statbuf.Bfree * uint64(statbuf.Bsize))
	freeSpaceTable[dirname] = freeSpace
	return freeSpace, nil
}

func getMounts() (map[string]string, error) {
	file, err := os.Open(procMounts)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	mounts := make(map[string]string)
	for scanner.Scan() {
		line := scanner.Text()
		var device, mountPoint, fsType, fsOptions, junk string
		nScanned, err := fmt.Sscanf(line, "%s %s %s %s %s",
			&device, &mountPoint, &fsType, &fsOptions, &junk)
		if err != nil {
			return nil, err
		}
		if nScanned < 4 {
			return nil, errors.New(fmt.Sprintf("only read %d values from %s",
				nScanned, line))
		}
		if mountPoint == "/boot" {
			continue
		}
		if !strings.HasPrefix(device, "/dev/") {
			continue
		}
		if target, err := filepath.EvalSymlinks(device); err != nil {
			return nil, err
		} else {
			device = target
		}
		device = device[5:]
		if _, ok := mounts[device]; !ok { // Pick the first mount point.
			mounts[device] = mountPoint
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return mounts, nil
}

func getVolumeDirectories() ([]string, error) {
	mounts, err := getMounts()
	if err != nil {
		return nil, err
	}
	var mountPointsToUse []string
	biggestMounts := make(map[string]mountInfo)
	for device, mountPoint := range mounts {
		if device == "root" { // Ignore this dumb shit.
			continue
		}
		sysDir := filepath.Join(sysClassBlock, device)
		linkTarget, err := os.Readlink(sysDir)
		if err != nil {
			return nil, err
		}
		_, err = os.Stat(filepath.Join(sysDir, "partition"))
		if err != nil {
			if os.IsNotExist(err) { // Not a partition: easy!
				mountPointsToUse = append(mountPointsToUse, mountPoint)
				continue
			}
			return nil, err
		}
		var statbuf syscall.Statfs_t
		if err := syscall.Statfs(mountPoint, &statbuf); err != nil {
			return nil, fmt.Errorf("error statfsing: %s: %s", mountPoint, err)
		}
		size := uint64(statbuf.Blocks * uint64(statbuf.Bsize))
		parentDevice := filepath.Base(filepath.Dir(linkTarget))
		if biggestMount, ok := biggestMounts[parentDevice]; !ok {
			biggestMounts[parentDevice] = mountInfo{mountPoint, size}
		} else if size > biggestMount.size {
			biggestMounts[parentDevice] = mountInfo{mountPoint, size}
		}
	}
	for _, biggestMount := range biggestMounts {
		mountPointsToUse = append(mountPointsToUse, biggestMount.mountPoint)
	}
	var volumeDirectories []string
	for _, mountPoint := range mountPointsToUse {
		volumeDirectories = append(volumeDirectories,
			filepath.Join(mountPoint, "hyper-volumes"))
	}
	sort.Strings(volumeDirectories)
	return volumeDirectories, nil
}

func (m *Manager) findFreeSpace(size uint64, freeSpaceTable map[string]uint64,
	position *int) (string, error) {
	if *position >= len(m.volumeDirectories) {
		*position = 0
	}
	startingPosition := *position
	for {
		freeSpace, err := getFreeSpace(m.volumeDirectories[*position],
			freeSpaceTable)
		if err != nil {
			return "", err
		}
		if size < freeSpace {
			dirname := m.volumeDirectories[*position]
			freeSpaceTable[dirname] -= size
			return dirname, nil
		}
		*position++
		if *position >= len(m.volumeDirectories) {
			*position = 0
		}
		if *position == startingPosition {
			return "", fmt.Errorf("not enough free space for %s volume",
				format.FormatBytes(size))
		}
	}
}

func (m *Manager) getVolumeDirectories(rootSize uint64,
	volumes []proto.Volume, spreadVolumes bool) ([]string, error) {
	sizes := make([]uint64, 1, len(volumes)+1)
	sizes[0] = rootSize
	for _, volume := range volumes {
		if volume.Size > 0 {
			sizes = append(sizes, volume.Size)
		}
	}
	freeSpaceTable := make(map[string]uint64, len(m.volumeDirectories))
	directoriesToUse := make([]string, 0, len(sizes))
	position := 0
	for len(sizes) > 0 {
		dirname, err := m.findFreeSpace(sizes[0], freeSpaceTable, &position)
		if err != nil {
			return nil, err
		}
		directoriesToUse = append(directoriesToUse, dirname)
		sizes = sizes[1:]
		if spreadVolumes {
			position++
		}
	}
	return directoriesToUse, nil
}
<|endoftext|>


<commit_before>package static_test

import (
	"."
	"fmt"
	"net/http"
)

func ExampleBuild() {
	handler := http.NewServeMux()
	paths := []string{}

	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	paths = append(paths, "/")
	paths = append(paths, "/world")
	paths = append(paths, "/go")

	options := static.DefaultOptions()
	static.Build(options, handler, paths, func(e static.Event) {
		fmt.Println(e)
	})

	// Output:
	// Action: build, Path: /
	// Action: build, Path: /world
	// Action: build, Path: /go
}

func ExampleBuildSingle() {
	handler := http.NewServeMux()

	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	options := static.DefaultOptions()

	var err error

	err = static.BuildSingle(options, handler, "/")
	fmt.Println("Built: /, Error:", err)

	err = static.BuildSingle(options, handler, "/world")
	fmt.Println("Built: /world, Error:", err)

	err = static.BuildSingle(options, handler, "/go")
	fmt.Println("Built: /go, Error:", err)

	// Output:
	// Built: /, Error: <nil>
	// Built: /world, Error: <nil>
	// Built: /go, Error: <nil>
}
<commit_msg>Fixing example tests for consistency.<commit_after>package static_test

import (
	"."
	"fmt"
	"net/http"
)

func ExampleBuild() {
	handler := http.NewServeMux()
	paths := []string{}

	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	paths = append(paths, "/world")

	options := static.DefaultOptions()
	static.Build(options, handler, paths, func(e static.Event) {
		fmt.Println(e)
	})

	// Output:
	// Action: build, Path: /world
}

func ExampleBuildSingle() {
	handler := http.NewServeMux()

	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	options := static.DefaultOptions()
	err := static.BuildSingle(options, handler, "/world")
	fmt.Println("Built: /world, Error:", err)

	// Output:
	// Built: /world, Error: <nil>
}
<|endoftext|>


<commit_before>package htm

import (
	//"fmt"
	"github.com/zacg/testify/assert"
	"sort"
	"testing"
)

func TestPickCellsToLearnOnAvoidDuplicates(t *testing.T) {
	tmp := NewTemporalMemoryParams()
	tmp.MaxNewSynapseCount = 1000
	tm := NewTemporalMemory(tmp)

	connections := tm.Connections
	connections.CreateSegment(0)
	connections.CreateSynapse(0, 23, 0.6)

	winnerCells := []int{233, 144}

	// Ensure that no additional (duplicate) cells were picked
	assert.Equal(t, winnerCells, tm.pickCellsToLearnOn(2, 0, winnerCells, connections))

}

func TestPickCellsToLearnOn(t *testing.T) {
	tmp := NewTemporalMemoryParams()
	tm := NewTemporalMemory(tmp)
	connections := tm.Connections
	connections.CreateSegment(0)

	winnerCells := []int{4, 47, 58, 93}

	result := tm.pickCellsToLearnOn(100, 0, winnerCells, connections)
	sort.Ints(result)
	assert.Equal(t, []int{4, 47, 58, 93}, result)
	assert.Equal(t, []int{}, tm.pickCellsToLearnOn(0, 0, winnerCells, connections))
	assert.Equal(t, []int{4, 58}, tm.pickCellsToLearnOn(2, 0, winnerCells, connections))
}

func TestLeastUsedCell(t *testing.T) {
	tmp := NewTemporalMemoryParams()
	tmp.ColumnDimensions = []int{2}
	tmp.CellsPerColumn = 2

	tm := NewTemporalMemory(tmp)

	connections := tm.Connections
	connections.CreateSegment(0)
	connections.CreateSynapse(0, 3, 0.3)

	for i := 0; i < 100; i++ {
		assert.Equal(t, 1, tm.getLeastUsedCell(0, connections))
	}

}
<commit_msg>add max adapt unit test<commit_after>package htm

import (
	//"fmt"
	"github.com/stretchr/testify/assert"
	"sort"
	"testing"
)

func TestPickCellsToLearnOnAvoidDuplicates(t *testing.T) {
	tmp := NewTemporalMemoryParams()
	tmp.MaxNewSynapseCount = 1000
	tm := NewTemporalMemory(tmp)

	connections := tm.Connections
	connections.CreateSegment(0)
	connections.CreateSynapse(0, 23, 0.6)

	winnerCells := []int{233, 144}

	// Ensure that no additional (duplicate) cells were picked
	assert.Equal(t, winnerCells, tm.pickCellsToLearnOn(2, 0, winnerCells, connections))

}

func TestPickCellsToLearnOn(t *testing.T) {
	tmp := NewTemporalMemoryParams()
	tm := NewTemporalMemory(tmp)
	connections := tm.Connections
	connections.CreateSegment(0)

	winnerCells := []int{4, 47, 58, 93}

	result := tm.pickCellsToLearnOn(100, 0, winnerCells, connections)
	sort.Ints(result)
	assert.Equal(t, []int{4, 47, 58, 93}, result)
	assert.Equal(t, []int{}, tm.pickCellsToLearnOn(0, 0, winnerCells, connections))
	assert.Equal(t, []int{4, 58}, tm.pickCellsToLearnOn(2, 0, winnerCells, connections))
}

func TestAdaptSegmentToMax(t *testing.T) {
	tmp := NewTemporalMemoryParams()
	tm := NewTemporalMemory(tmp)
	connections := tm.Connections
	connections = tm.Connections
	connections.CreateSegment(0)
	connections.CreateSynapse(0, 23, 0.9)

	tm.adaptSegment(0, []int{0}, connections)
	assert.Equal(t, 1.0, connections.DataForSynapse(0).Permanence)

	// Now permanence should be at max
	tm.adaptSegment(0, []int{0}, connections)
	assert.Equal(t, 1.0, connections.DataForSynapse(0).Permanence)

}

func TestLeastUsedCell(t *testing.T) {
	tmp := NewTemporalMemoryParams()
	tmp.ColumnDimensions = []int{2}
	tmp.CellsPerColumn = 2

	tm := NewTemporalMemory(tmp)

	connections := tm.Connections
	connections.CreateSegment(0)
	connections.CreateSynapse(0, 3, 0.3)

	for i := 0; i < 100; i++ {
		assert.Equal(t, 1, tm.getLeastUsedCell(0, connections))
	}

}
<|endoftext|>


<commit_before>package unilog

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"text/template"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/getsentry/raven-go"

	"github.com/stripe/unilog/clevels"
	flag "launchpad.net/gnuflag"
)

// hold the argument passed in with "-statstags"
var statstags string

// A filter to be applied to log lines prior to prefixing them
// with a timestamp and logging them.
type Filter func(string) string

// Unilog represents a unilog process. unilog is intended to be used
// as a standalone application, but is exported as a package to allow
// users to perform compile-time configuration to simplify deployment.
type Unilog struct {
	// Sentry DSN for reporting Unilog errors
	// If this is unset, unilog will not report errors to Sentry
	SentryDSN string
	// StatsdAddress for sending metrics
	// If this is unset, it wlil default to "127.0.0.1:8200" -> TODO: is this what we want?
	StatsdAddress string
	// The email address from which unilog will send mail on
	// errors
	MailTo string
	// The email address to which unilog will email breakages. If
	// either MailTo or MailFrom is unset, unilog will not
	// generate email.
	MailFrom string

	// A series of filters which will be applied to each log line
	// in order
	Filters []Filter

	// The version that unilog will report on the command-line and
	// in error emails. Defaults to the toplevel Version constant.
	Version string
	// The number of log lines to buffer in-memory, in case
	// unilog's disk writer falls behind. Note that when talking
	// to unilog over a pipe, the kernel also maintains an
	// in-kernel pipe buffer, sized 64kb on Linux.
	BufferLines int

	Name    string
	Verbose bool
	Debug   bool

	lines     <-chan string
	errs      <-chan error
	sigReopen <-chan os.Signal
	sigTerm   <-chan os.Signal
	shutdown  chan struct{}
	file      io.WriteCloser
	target    string

	b struct {
		broken bool
		at     time.Time
		count  int
	}
}

func stringFlag(val *string, longname, shortname, init, help string) {
	flag.StringVar(val, longname, init, help)
	flag.StringVar(val, shortname, init, help)
}

func boolFlag(val *bool, longname, shortname string, init bool, help string) {
	flag.BoolVar(val, longname, init, help)
	flag.BoolVar(val, shortname, init, help)
}

func (u *Unilog) fillDefaults() {
	if u.Version == "" {
		u.Version = Version
	}
	if u.BufferLines == 0 {
		u.BufferLines = DefaultBuffer
	}
}

func (u *Unilog) addFlags() {
	stringFlag(&u.Name, "name", "a", "", "Name of logged program")
	boolFlag(&u.Verbose, "verbose", "v", false, "Echo lines to stdout")
	boolFlag(&u.Debug, "debug", "d", false, "Print debug messages")
	flag.StringVar(&u.MailFrom, "mailfrom", u.MailFrom, "Address to send error emails from")
	flag.StringVar(&u.MailTo, "mailto", u.MailTo, "Address to send error emails to")
	flag.StringVar(&u.SentryDSN, "sentrydsn", u.SentryDSN, "Sentry DSN to send errors to")
	flag.StringVar(&u.StatsdAddress, "statsdaddress", "127.0.0.1:8200", "Address to send statsd metrics to")
	flag.StringVar(&clevels.AusterityFile, "austerityfile", clevels.AusterityFile, "(optional) Location of file to read austerity level from")
	stringFlag(&statstags, "statstags", "s", "", `(optional) tags to include with all statsd metrics (e.g. "foo:bar,baz:quz")`)
}

var emailTemplate = template.Must(template.New("email").Parse(`From: {{.From}}
To: {{.To}}
Subject: [unilog] {{.Name}} could not {{.Action}}

Hi there,

This is unilog reporting from {{.Hostname}}. I'm sad to report that
{{.Name}} is having some troubles writing to its log. I got caught up
trying to log a line to {{.Target}}.

To avoid spamming you, I'm going to shut up for an hour. Please fix me.

{{.Error}}
--
Sent from unilog {{.Version}}
`))

const (
	// Version is the Unilog version. Reported in emails and in
	// response to --version on the command line. Can be overriden
	// by the Version field in a Unilog object.
	Version = "0.3"
	// DefaultBuffer is the default size (in lines) of the
	// in-process line buffer
	DefaultBuffer = 1 << 12
)

var Stats *statsd.Client

func readlines(in io.Reader, bufsize int, shutdown chan struct{}) (<-chan string, <-chan error) {
	linec := make(chan string, bufsize)
	errc := make(chan error, 1)

	u := NewUnilogReader(in, shutdown)
	r := bufio.NewReader(u)

	go func() {
		var err error
		var s string

		for err == nil {
			s, err = r.ReadString('\n')
			if s != "" {
				s = strings.TrimRight(s, "\n")
				linec <- s
				if Stats != nil {
					Stats.Count("unilog.bytes", int64(len(s)), nil, .1)
				}
			}
		}

		if err != io.EOF {
			errc <- err
		}
		close(linec)
	}()

	return linec, errc
}

func (u *Unilog) reopen() error {
	if u.target == "-" {
		u.file = os.Stdout
		return nil
	}

	if u.file != nil {
		u.file.Close()
		u.file = nil
	}

	var e error
	if u.file, e = os.OpenFile(u.target, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); e != nil {
		u.file = nil
		return e
	}
	return nil
}

func (u *Unilog) format(line string) string {
	for _, filter := range u.Filters {
		if filter != nil {
			line = filter(line)
		}
	}
	return fmt.Sprintf("[%s] %s\n", time.Now().Format("2006-01-02 15:04:05.000000"), line)
}

func (u *Unilog) logLine(line string) {
	formatted := u.format(line)
	if u.Verbose {
		defer io.WriteString(os.Stdout, formatted)
	}

	var e error
	if u.file == nil {
		e = u.reopen()
	}
	if e != nil {
		u.handleError("reopen_file", e)
		return
	}
	_, e = io.WriteString(u.file, formatted)
	if e != nil {
		u.handleError("write_to_log", e)
	} else {
		u.b.broken = false
	}
}

func (u *Unilog) run() {
	for {
		select {
		case e := <-u.errs:
			if e != nil && e != io.EOF {
				panic(e)
			} else {
				return
			}
		case <-u.sigReopen:
			u.reopen()
		case <-u.sigTerm:
			if u.shutdown != nil {
				close(u.shutdown)
				u.shutdown = nil
			}
		case line, ok := <-u.lines:
			if !ok {
				return
			}
			u.logLine(line)
		}
	}
}

func (u *Unilog) handleError(action string, e error) {
	if !u.b.broken {
		u.b.broken = true
		u.b.at = time.Now()
		u.b.count = 0
	} else if time.Since(u.b.at) > time.Hour {
		u.b.at = time.Now()
		u.b.count = 0
	}

	if u.Debug {
		fmt.Fprintf(os.Stderr, "Could not %s: %s\n", action, e.Error())
	}

	if Stats != nil {
		emsg := fmt.Sprintf("err_action:%s", action)
		Stats.Count("unilog.errors_total", 1, []string{emsg}, 1)
	}

	if u.b.count == 0 && u.SentryDSN != "" {
		hostname, _ := os.Hostname()
		keys := map[string]string{
			"Hostname": hostname,
			"Action":   action,
			"Name":     u.Name,
			"Target":   u.target,
			"Error":    e.Error(),
			"Version":  Version,
		}
		raven.CaptureError(e, keys)
	}

	if u.b.count == 0 && u.MailFrom != "" && u.MailTo != "" {
		message := new(bytes.Buffer)
		hostname, _ := os.Hostname()
		emailTemplate.Execute(message, map[string]string{
			"Hostname": hostname,
			"From":     u.MailFrom,
			"To":       u.MailTo,
			"Action":   action,
			"Name":     u.Name,
			"Target":   u.target,
			"Error":    e.Error(),
			"Version":  Version,
		})
		cmd := exec.Command("sendmail", "-t")
		cmd.Stdin = message
		cmd.Run()
	}

	u.b.count++
}

func (u *Unilog) Main() {
	u.fillDefaults()

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] dstfile\n", os.Args[0])
		flag.PrintDefaults()
	}

	u.addFlags()
	var flagVersion bool
	boolFlag(&flagVersion, "version", "V", false, "Print the version number and exit")

	flag.Parse(true)

	if flagVersion {
		fmt.Printf("This is unilog v%s\n", Version)
		return
	}
	args := flag.Args()
	if len(args) != 1 {
		flag.Usage()
		os.Exit(1)
	}

	reopen := make(chan os.Signal, 2)
	signal.Notify(reopen, syscall.SIGALRM, syscall.SIGHUP)
	u.sigReopen = reopen

	term := make(chan os.Signal, 2)
	signal.Notify(term, syscall.SIGTERM, syscall.SIGINT)
	u.sigTerm = term

	u.shutdown = make(chan struct{})
	u.target = flag.Arg(0)
	u.reopen()

	u.lines, u.errs = readlines(os.Stdin, u.BufferLines, u.shutdown)

	fileName := u.target

	Stats, _ = statsd.New(u.StatsdAddress)

	Stats.Tags = append(Stats.Tags, fmt.Sprintf("file_name:%s", fileName))
	if statstags != "" {
		Stats.Tags = append(Stats.Tags, strings.Split(statstags, ",")...)
	}

	clevels.Stats = Stats

	_ = raven.SetDSN(u.SentryDSN)

	u.run()
}
<commit_msg>Added sigquit<commit_after>package unilog

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"text/template"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/getsentry/raven-go"

	"github.com/stripe/unilog/clevels"
	flag "launchpad.net/gnuflag"
)

// hold the argument passed in with "-statstags"
var statstags string

// A filter to be applied to log lines prior to prefixing them
// with a timestamp and logging them.
type Filter func(string) string

// Unilog represents a unilog process. unilog is intended to be used
// as a standalone application, but is exported as a package to allow
// users to perform compile-time configuration to simplify deployment.
type Unilog struct {
	// Sentry DSN for reporting Unilog errors
	// If this is unset, unilog will not report errors to Sentry
	SentryDSN string
	// StatsdAddress for sending metrics
	// If this is unset, it wlil default to "127.0.0.1:8200" -> TODO: is this what we want?
	StatsdAddress string
	// The email address from which unilog will send mail on
	// errors
	MailTo string
	// The email address to which unilog will email breakages. If
	// either MailTo or MailFrom is unset, unilog will not
	// generate email.
	MailFrom string

	// A series of filters which will be applied to each log line
	// in order
	Filters []Filter

	// The version that unilog will report on the command-line and
	// in error emails. Defaults to the toplevel Version constant.
	Version string
	// The number of log lines to buffer in-memory, in case
	// unilog's disk writer falls behind. Note that when talking
	// to unilog over a pipe, the kernel also maintains an
	// in-kernel pipe buffer, sized 64kb on Linux.
	BufferLines int

	Name    string
	Verbose bool
	Debug   bool

	lines     <-chan string
	errs      <-chan error
	sigReopen <-chan os.Signal
	sigTerm   <-chan os.Signal
	sigQuit   <-chan os.Signal
	shutdown  chan struct{}
	file      io.WriteCloser
	target    string

	b struct {
		broken bool
		at     time.Time
		count  int
	}
}

func stringFlag(val *string, longname, shortname, init, help string) {
	flag.StringVar(val, longname, init, help)
	flag.StringVar(val, shortname, init, help)
}

func boolFlag(val *bool, longname, shortname string, init bool, help string) {
	flag.BoolVar(val, longname, init, help)
	flag.BoolVar(val, shortname, init, help)
}

func (u *Unilog) fillDefaults() {
	if u.Version == "" {
		u.Version = Version
	}
	if u.BufferLines == 0 {
		u.BufferLines = DefaultBuffer
	}
}

func (u *Unilog) addFlags() {
	stringFlag(&u.Name, "name", "a", "", "Name of logged program")
	boolFlag(&u.Verbose, "verbose", "v", false, "Echo lines to stdout")
	boolFlag(&u.Debug, "debug", "d", false, "Print debug messages")
	flag.StringVar(&u.MailFrom, "mailfrom", u.MailFrom, "Address to send error emails from")
	flag.StringVar(&u.MailTo, "mailto", u.MailTo, "Address to send error emails to")
	flag.StringVar(&u.SentryDSN, "sentrydsn", u.SentryDSN, "Sentry DSN to send errors to")
	flag.StringVar(&u.StatsdAddress, "statsdaddress", "127.0.0.1:8200", "Address to send statsd metrics to")
	flag.StringVar(&clevels.AusterityFile, "austerityfile", clevels.AusterityFile, "(optional) Location of file to read austerity level from")
	stringFlag(&statstags, "statstags", "s", "", `(optional) tags to include with all statsd metrics (e.g. "foo:bar,baz:quz")`)
}

var emailTemplate = template.Must(template.New("email").Parse(`From: {{.From}}
To: {{.To}}
Subject: [unilog] {{.Name}} could not {{.Action}}

Hi there,

This is unilog reporting from {{.Hostname}}. I'm sad to report that
{{.Name}} is having some troubles writing to its log. I got caught up
trying to log a line to {{.Target}}.

To avoid spamming you, I'm going to shut up for an hour. Please fix me.

{{.Error}}
--
Sent from unilog {{.Version}}
`))

const (
	// Version is the Unilog version. Reported in emails and in
	// response to --version on the command line. Can be overriden
	// by the Version field in a Unilog object.
	Version = "0.3"
	// DefaultBuffer is the default size (in lines) of the
	// in-process line buffer
	DefaultBuffer = 1 << 12
)

var Stats *statsd.Client

func readlines(in io.Reader, bufsize int, shutdown chan struct{}) (<-chan string, <-chan error) {
	linec := make(chan string, bufsize)
	errc := make(chan error, 1)

	u := NewUnilogReader(in, shutdown)
	r := bufio.NewReader(u)

	go func() {
		var err error
		var s string

		for err == nil {
			s, err = r.ReadString('\n')
			if s != "" {
				s = strings.TrimRight(s, "\n")
				linec <- s
				if Stats != nil {
					Stats.Count("unilog.bytes", int64(len(s)), nil, .1)
				}
			}
		}

		if err != io.EOF {
			errc <- err
		}
		close(linec)
	}()

	return linec, errc
}

func (u *Unilog) reopen() error {
	if u.target == "-" {
		u.file = os.Stdout
		return nil
	}

	if u.file != nil {
		u.file.Close()
		u.file = nil
	}

	var e error
	if u.file, e = os.OpenFile(u.target, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); e != nil {
		u.file = nil
		return e
	}
	return nil
}

func (u *Unilog) format(line string) string {
	for _, filter := range u.Filters {
		if filter != nil {
			line = filter(line)
		}
	}
	return fmt.Sprintf("[%s] %s\n", time.Now().Format("2006-01-02 15:04:05.000000"), line)
}

func (u *Unilog) logLine(line string) {
	formatted := u.format(line)
	if u.Verbose {
		defer io.WriteString(os.Stdout, formatted)
	}

	var e error
	if u.file == nil {
		e = u.reopen()
	}
	if e != nil {
		u.handleError("reopen_file", e)
		return
	}
	_, e = io.WriteString(u.file, formatted)
	if e != nil {
		u.handleError("write_to_log", e)
	} else {
		u.b.broken = false
	}
}

func (u *Unilog) run() {
	for {
		select {
		case e := <-u.errs:
			if e != nil && e != io.EOF {
				panic(e)
			} else {
				return
			}
		case <-u.sigReopen:
			u.reopen()
		case <-u.sigTerm:
			if u.shutdown != nil {
				close(u.shutdown)
				u.shutdown = nil
			}
		case line, ok := <-u.lines:
			if !ok {
				return
			}
			u.logLine(line)
		}
	}
}

func (u *Unilog) handleError(action string, e error) {
	if !u.b.broken {
		u.b.broken = true
		u.b.at = time.Now()
		u.b.count = 0
	} else if time.Since(u.b.at) > time.Hour {
		u.b.at = time.Now()
		u.b.count = 0
	}

	if u.Debug {
		fmt.Fprintf(os.Stderr, "Could not %s: %s\n", action, e.Error())
	}

	if Stats != nil {
		emsg := fmt.Sprintf("err_action:%s", action)
		Stats.Count("unilog.errors_total", 1, []string{emsg}, 1)
	}

	if u.b.count == 0 && u.SentryDSN != "" {
		hostname, _ := os.Hostname()
		keys := map[string]string{
			"Hostname": hostname,
			"Action":   action,
			"Name":     u.Name,
			"Target":   u.target,
			"Error":    e.Error(),
			"Version":  Version,
		}
		raven.CaptureError(e, keys)
	}

	if u.b.count == 0 && u.MailFrom != "" && u.MailTo != "" {
		message := new(bytes.Buffer)
		hostname, _ := os.Hostname()
		emailTemplate.Execute(message, map[string]string{
			"Hostname": hostname,
			"From":     u.MailFrom,
			"To":       u.MailTo,
			"Action":   action,
			"Name":     u.Name,
			"Target":   u.target,
			"Error":    e.Error(),
			"Version":  Version,
		})
		cmd := exec.Command("sendmail", "-t")
		cmd.Stdin = message
		cmd.Run()
	}

	u.b.count++
}

func (u *Unilog) Main() {
	u.fillDefaults()

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] dstfile\n", os.Args[0])
		flag.PrintDefaults()
	}

	u.addFlags()
	var flagVersion bool
	boolFlag(&flagVersion, "version", "V", false, "Print the version number and exit")

	flag.Parse(true)

	if flagVersion {
		fmt.Printf("This is unilog v%s\n", Version)
		return
	}
	args := flag.Args()
	if len(args) != 1 {
		flag.Usage()
		os.Exit(1)
	}

	reopen := make(chan os.Signal, 2)
	signal.Notify(reopen, syscall.SIGALRM, syscall.SIGHUP)
	u.sigReopen = reopen

	term := make(chan os.Signal, 2)
	signal.Notify(term, syscall.SIGTERM, syscall.SIGINT)
	u.sigTerm = term

	quit := make(chan os.Signal, 2)
	signal.Notify(quit, syscall.SIGQUIT)
	u.sigQuit = quit

	u.shutdown = make(chan struct{})
	u.target = flag.Arg(0)
	u.reopen()

	u.lines, u.errs = readlines(os.Stdin, u.BufferLines, u.shutdown)

	fileName := u.target

	Stats, _ = statsd.New(u.StatsdAddress)

	Stats.Tags = append(Stats.Tags, fmt.Sprintf("file_name:%s", fileName))
	if statstags != "" {
		Stats.Tags = append(Stats.Tags, strings.Split(statstags, ",")...)
	}

	clevels.Stats = Stats

	_ = raven.SetDSN(u.SentryDSN)

	u.run()
}
<|endoftext|>


<commit_before>// +build linux darwin freebsd

// package fuse/readonly implements a fuse filesystem to access files
// stored inside of ipfs.
package readonly

import (
	"io/ioutil"
	"os"

	fuse "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
	fs "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"

	core "github.com/jbenet/go-ipfs/core"
	mdag "github.com/jbenet/go-ipfs/merkledag"
	path "github.com/jbenet/go-ipfs/path"
	uio "github.com/jbenet/go-ipfs/unixfs/io"
	ftpb "github.com/jbenet/go-ipfs/unixfs/pb"
	u "github.com/jbenet/go-ipfs/util"
)

var log = u.Logger("ipfs")

// FileSystem is the readonly Ipfs Fuse Filesystem.
type FileSystem struct {
	Ipfs *core.IpfsNode
}

// NewFileSystem constructs new fs using given core.IpfsNode instance.
func NewFileSystem(ipfs *core.IpfsNode) *FileSystem {
	return &FileSystem{Ipfs: ipfs}
}

// Root constructs the Root of the filesystem, a Root object.
func (f FileSystem) Root() (fs.Node, fuse.Error) {
	return &Root{Ipfs: f.Ipfs}, nil
}

// Root is the root object of the filesystem tree.
type Root struct {
	Ipfs *core.IpfsNode
}

// Attr returns file attributes.
func (*Root) Attr() fuse.Attr {
	return fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x
}

// Lookup performs a lookup under this node.
func (s *Root) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debugf("Root Lookup: '%s'", name)
	switch name {
	case "mach_kernel", ".hidden", "._.":
		// Just quiet some log noise on OS X.
		return nil, fuse.ENOENT
	}

	nd, err := s.Ipfs.Resolver.ResolvePath(path.Path(name))
	if err != nil {
		// todo: make this error more versatile.
		return nil, fuse.ENOENT
	}

	return &Node{Ipfs: s.Ipfs, Nd: nd}, nil
}

// ReadDir reads a particular directory. Disallowed for root.
func (*Root) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
	log.Debug("Read Root.")
	return nil, fuse.EPERM
}

// Node is the core object representing a filesystem tree node.
type Node struct {
	Ipfs   *core.IpfsNode
	Nd     *mdag.Node
	fd     *uio.DagReader
	cached *ftpb.Data
}

func (s *Node) loadData() error {
	s.cached = new(ftpb.Data)
	return proto.Unmarshal(s.Nd.Data, s.cached)
}

// Attr returns the attributes of a given node.
func (s *Node) Attr() fuse.Attr {
	log.Debug("Node attr.")
	if s.cached == nil {
		s.loadData()
	}
	switch s.cached.GetType() {
	case ftpb.Data_Directory:
		return fuse.Attr{Mode: os.ModeDir | 0555}
	case ftpb.Data_File:
		size := s.cached.GetFilesize()
		return fuse.Attr{
			Mode:   0444,
			Size:   uint64(size),
			Blocks: uint64(len(s.Nd.Links)),
		}
	case ftpb.Data_Raw:
		return fuse.Attr{
			Mode:   0444,
			Size:   uint64(len(s.cached.GetData())),
			Blocks: uint64(len(s.Nd.Links)),
		}

	default:
		log.Error("Invalid data type.")
		return fuse.Attr{}
	}
}

// Lookup performs a lookup under this node.
func (s *Node) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debugf("Lookup '%s'", name)
	nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
	if err != nil {
		// todo: make this error more versatile.
		return nil, fuse.ENOENT
	}

	return &Node{Ipfs: s.Ipfs, Nd: nd}, nil
}

// ReadDir reads the link structure as directory entries
func (s *Node) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
	log.Debug("Node ReadDir")
	entries := make([]fuse.Dirent, len(s.Nd.Links))
	for i, link := range s.Nd.Links {
		n := link.Name
		if len(n) == 0 {
			n = link.Hash.B58String()
		}
		entries[i] = fuse.Dirent{Name: n, Type: fuse.DT_File}
	}

	if len(entries) > 0 {
		return entries, nil
	}
	return nil, fuse.ENOENT
}

// ReadAll reads the object data as file data
func (s *Node) ReadAll(intr fs.Intr) ([]byte, fuse.Error) {
	log.Debug("Read node.")
	r, err := uio.NewDagReader(context.TODO(), s.Nd, s.Ipfs.DAG)
	if err != nil {
		return nil, err
	}
	// this is a terrible function... 'ReadAll'?
	// what if i have a 6TB file? GG RAM.
	return ioutil.ReadAll(r)
}
<commit_msg>fuse/ipfs: seeking<commit_after>// +build linux darwin freebsd

// package fuse/readonly implements a fuse filesystem to access files
// stored inside of ipfs.
package readonly

import (
	"io"
	"os"

	fuse "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
	fs "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"

	core "github.com/jbenet/go-ipfs/core"
	mdag "github.com/jbenet/go-ipfs/merkledag"
	path "github.com/jbenet/go-ipfs/path"
	eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog"
	uio "github.com/jbenet/go-ipfs/unixfs/io"
	ftpb "github.com/jbenet/go-ipfs/unixfs/pb"
	lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
)

var log = eventlog.Logger("fuse/ipfs")

// FileSystem is the readonly Ipfs Fuse Filesystem.
type FileSystem struct {
	Ipfs *core.IpfsNode
}

// NewFileSystem constructs new fs using given core.IpfsNode instance.
func NewFileSystem(ipfs *core.IpfsNode) *FileSystem {
	return &FileSystem{Ipfs: ipfs}
}

// Root constructs the Root of the filesystem, a Root object.
func (f FileSystem) Root() (fs.Node, fuse.Error) {
	return &Root{Ipfs: f.Ipfs}, nil
}

// Root is the root object of the filesystem tree.
type Root struct {
	Ipfs *core.IpfsNode
}

// Attr returns file attributes.
func (*Root) Attr() fuse.Attr {
	return fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x
}

// Lookup performs a lookup under this node.
func (s *Root) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debugf("Root Lookup: '%s'", name)
	switch name {
	case "mach_kernel", ".hidden", "._.":
		// Just quiet some log noise on OS X.
		return nil, fuse.ENOENT
	}

	nd, err := s.Ipfs.Resolver.ResolvePath(path.Path(name))
	if err != nil {
		// todo: make this error more versatile.
		return nil, fuse.ENOENT
	}

	return &Node{Ipfs: s.Ipfs, Nd: nd}, nil
}

// ReadDir reads a particular directory. Disallowed for root.
func (*Root) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
	log.Debug("Read Root.")
	return nil, fuse.EPERM
}

// Node is the core object representing a filesystem tree node.
type Node struct {
	Ipfs   *core.IpfsNode
	Nd     *mdag.Node
	fd     *uio.DagReader
	cached *ftpb.Data
}

func (s *Node) loadData() error {
	s.cached = new(ftpb.Data)
	return proto.Unmarshal(s.Nd.Data, s.cached)
}

// Attr returns the attributes of a given node.
func (s *Node) Attr() fuse.Attr {
	log.Debug("Node attr.")
	if s.cached == nil {
		s.loadData()
	}
	switch s.cached.GetType() {
	case ftpb.Data_Directory:
		return fuse.Attr{Mode: os.ModeDir | 0555}
	case ftpb.Data_File:
		size := s.cached.GetFilesize()
		return fuse.Attr{
			Mode:   0444,
			Size:   uint64(size),
			Blocks: uint64(len(s.Nd.Links)),
		}
	case ftpb.Data_Raw:
		return fuse.Attr{
			Mode:   0444,
			Size:   uint64(len(s.cached.GetData())),
			Blocks: uint64(len(s.Nd.Links)),
		}

	default:
		log.Error("Invalid data type.")
		return fuse.Attr{}
	}
}

// Lookup performs a lookup under this node.
func (s *Node) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Debugf("Lookup '%s'", name)
	nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
	if err != nil {
		// todo: make this error more versatile.
		return nil, fuse.ENOENT
	}

	return &Node{Ipfs: s.Ipfs, Nd: nd}, nil
}

// ReadDir reads the link structure as directory entries
func (s *Node) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
	log.Debug("Node ReadDir")
	entries := make([]fuse.Dirent, len(s.Nd.Links))
	for i, link := range s.Nd.Links {
		n := link.Name
		if len(n) == 0 {
			n = link.Hash.B58String()
		}
		entries[i] = fuse.Dirent{Name: n, Type: fuse.DT_File}
	}

	if len(entries) > 0 {
		return entries, nil
	}
	return nil, fuse.ENOENT
}

func (s *Node) Read(req *fuse.ReadRequest, resp *fuse.ReadResponse, intr fs.Intr) fuse.Error {
	// intr will be closed by fuse if the request is cancelled. turn this into a context.
	ctx, cancel := context.WithCancel(context.TODO())
	defer cancel() // make sure all operations we started close.

	// we wait on intr and cancel our context if it closes.
	go func() {
		select {
		case <-intr: // closed by fuse
			cancel() // cancel our context
		case <-ctx.Done():
		}
	}()

	k, err := s.Nd.Key()
	if err != nil {
		return err
	}

	// setup our logging event
	lm := make(lgbl.DeferredMap)
	lm["key"] = func() interface{} { return k.Pretty() }
	lm["req_offset"] = req.Offset
	lm["req_size"] = req.Size
	defer log.EventBegin(ctx, "fuseRead", lm).Done()

	r, err := uio.NewDagReader(ctx, s.Nd, s.Ipfs.DAG)
	if err != nil {
		return err
	}
	o, err := r.Seek(req.Offset, os.SEEK_SET)
	lm["req_offset"] = o
	if err != nil {
		return err
	}
	n, err := io.ReadFull(r, resp.Data[:req.Size])
	resp.Data = resp.Data[:n]
	lm["req_size"] = n
	return err // may be non-nil / not succeeded
}

// // ReadAll reads the object data as file data
// func (s *Node) ReadAll(intr fs.Intr) ([]byte, fuse.Error) {
// 	// this is a terrible function... 'ReadAll'?
// 	// what if i have a 6TB file? GG RAM.
// 	return ioutil.ReadAll(r)
// }
// GG RAM alright... -jbenet
<|endoftext|>


<commit_before>package main

import (
	"encoding/hex"
	"fmt"
	"github.com/GeertJohan/go.rice"
	"github.com/davecgh/go-spew/spew"
	"log"
	"net/http"
	"os"
	"text/template"
)

func main() {
	box, err := rice.FindBox("example-files")
	if err != nil {
		log.Fatalf("error opening rice.Box: %s\n", err)
	}
	// spew.Dump(box)

	contentString, err := box.String("file.txt")
	if err != nil {
		log.Fatalf("could not read file contents as string: %s\n", err)
	}
	log.Printf("Read some file contents as string:\n%s\n", contentString)

	contentBytes, err := box.Bytes("file.txt")
	if err != nil {
		log.Fatalf("could not read file contents as byteSlice: %s\n", err)
	}
	log.Printf("Read some file contents as byteSlice:\n%s\n", hex.Dump(contentBytes))

	file, err := box.Open("file.txt")
	if err != nil {
		log.Fatalf("could not open file: %s\n", err)
	}
	spew.Dump(file)
	// debianFile, err := box.Open("debian-7.3.0-amd64-i386-netinst.iso")
	// if err != nil {
	// 	log.Fatalf("error opening file debian-7.3.0-amd64-i386-netinst.iso: %v", err)
	// }
	// info, err := debianFile.Stat()
	// if err != nil {
	// 	log.Fatalf("error doing stat for debian file: %v", err)
	// }
	// log.Printf("debian file was last modified at %v\n", info.ModTime())
	// log.Printf("debian file is %d bytes large\n", info.Size())

	// find/create a rice.Box
	templateBox, err := rice.FindBox("example-templates")
	if err != nil {
		log.Fatal(err)
	}
	// get file contents as string
	templateString, err := templateBox.String("message.tmpl")
	if err != nil {
		log.Fatal(err)
	}
	// parse and execute the template
	tmplMessage, err := template.New("message").Parse(templateString)
	if err != nil {
		log.Fatal(err)
	}
	tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})

	http.Handle("/", http.FileServer(box.HTTPBox()))
	go http.ListenAndServe(":8080", nil)
	fmt.Printf("Serving files on :8080, press ctrl-C to exit")
	select {}
}
<commit_msg>Add rice.Config to example Check error in example's http fileserver<commit_after>package main

import (
	"encoding/hex"
	"fmt"
	"github.com/GeertJohan/go.rice"
	"github.com/davecgh/go-spew/spew"
	"log"
	"net/http"
	"os"
	"text/template"
)

func main() {
	conf := rice.Config{
		LocateOrder: []rice.LocateMethod{rice.LocateEmbedded, rice.LocateAppended, rice.LocateFS},
	}
	box, err := conf.FindBox("example-files")
	if err != nil {
		log.Fatalf("error opening rice.Box: %s\n", err)
	}
	// spew.Dump(box)

	contentString, err := box.String("file.txt")
	if err != nil {
		log.Fatalf("could not read file contents as string: %s\n", err)
	}
	log.Printf("Read some file contents as string:\n%s\n", contentString)

	contentBytes, err := box.Bytes("file.txt")
	if err != nil {
		log.Fatalf("could not read file contents as byteSlice: %s\n", err)
	}
	log.Printf("Read some file contents as byteSlice:\n%s\n", hex.Dump(contentBytes))

	file, err := box.Open("file.txt")
	if err != nil {
		log.Fatalf("could not open file: %s\n", err)
	}
	spew.Dump(file)

	// find/create a rice.Box
	templateBox, err := rice.FindBox("example-templates")
	if err != nil {
		log.Fatal(err)
	}
	// get file contents as string
	templateString, err := templateBox.String("message.tmpl")
	if err != nil {
		log.Fatal(err)
	}
	// parse and execute the template
	tmplMessage, err := template.New("message").Parse(templateString)
	if err != nil {
		log.Fatal(err)
	}
	tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})

	http.Handle("/", http.FileServer(box.HTTPBox()))
	go func() {
		fmt.Println("Serving files on :8080, press ctrl-C to exit")
		err := http.ListenAndServe(":8080", nil)
		if err != nil {
			log.Fatalf("error serving files: %v", err)
		}
	}()
	select {}
}
<|endoftext|>


<commit_before>package wellington

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"testing"

	"github.com/wellington/go-libsass"
)

func TestCompileStdin_imports(t *testing.T) {

	in := bytes.NewBufferString(`@import "compass";
@import "compass/utilities/sprite/base";

`)
	ctx := &libsass.Context{}
	InitializeContext(ctx)
	ctx.Imports.Init()

	var buf bytes.Buffer
	err := ctx.Compile(in, &buf)
	if err != nil {
		t.Fatal(err)
	}

	out := buf.String()

	if e := ``; e != out {
		t.Fatalf("mismatch expected:\n%s\nwas:\n%s\n", e, out)
	}

}

func TestLoadAndBuild(t *testing.T) {
	oo := os.Stdout

	r, w, _ := os.Pipe()
	os.Stdout = w
	err := LoadAndBuild("test/sass/file.scss", &BuildArgs{}, NewPartialMap())
	if err != nil {
		t.Error(err)
	}
	outC := make(chan string)
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	w.Close()
	os.Stdout = oo
	out := <-outC

	e := `div {
  color: black; }
Rebuilt: test/sass/file.scss
`
	if e != out {
		t.Errorf("got:\n%s\nwanted:\n%s", out, e)
	}
}

func TestLandB_error(t *testing.T) {

	oo := os.Stdout
	var w *os.File
	defer w.Close()
	os.Stdout = w
	err := LoadAndBuild("test/sass/error.scss", &BuildArgs{}, NewPartialMap())
	qs := fmt.Sprintf("%q", err.Error())

	if !strings.Contains(qs, `error.scss:1\nInvalid CSS after \"div {\\a\": expected \"}\", was \"\"`) {
		t.Fatalf("Error contains invalid text:\n%s", qs)
	}
	os.Stdout = oo
}

func TestLandB_updateFile(t *testing.T) {
	s := "file.scss"
	ren := updateFileOutputType(s)
	if e := "file.css"; e != ren {
		t.Errorf("got: %s wanted: %s", ren, e)
	}
}

func TestLoadAndBuild_args(t *testing.T) {
	oo := os.Stdout

	r, w, _ := os.Pipe()
	os.Stdout = w
	err := LoadAndBuild("test/sass/file.scss",
		&BuildArgs{
			BuildDir: "test/build",
			Includes: "test",
		},
		NewPartialMap(),
	)
	if err != nil {
		t.Error(err)
	}
	outC := make(chan string)
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	w.Close()
	os.Stdout = oo
	out := <-outC

	e := `Rebuilt: test/sass/file.scss
`
	if e != out {
		t.Errorf("got:\n%s\nwanted:\n%s", out, e)
	}
}

func TestLoadAndBuild_comply(t *testing.T) {
	stdout := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w
	err := LoadAndBuild("test/compass/top.scss",
		&BuildArgs{
			BuildDir: "test/build",
			Includes: "test",
		},
		NewPartialMap(),
	)
	if err != nil {
		t.Error(err)
	}
	outC := make(chan string)
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	w.Close()
	os.Stdout = stdout
	out := <-outC

	e := `Rebuilt: test/compass/top.scss
`
	if e != out {
		t.Errorf("got:\n%s\nwanted:\n%s", out, e)
	}
}
<commit_msg>update test for libsass changes<commit_after>package wellington

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"testing"

	"github.com/wellington/go-libsass"
)

func TestCompileStdin_imports(t *testing.T) {

	in := bytes.NewBufferString(`@import "compass";
@import "compass/utilities/sprite/base";

`)
	ctx := &libsass.Context{}
	InitializeContext(ctx)
	ctx.Imports.Init()

	var buf bytes.Buffer
	err := ctx.Compile(in, &buf)
	if err != nil {
		t.Fatal(err)
	}

	out := buf.String()

	if e := ``; e != out {
		t.Fatalf("mismatch expected:\n%s\nwas:\n%s\n", e, out)
	}

}

func TestLoadAndBuild(t *testing.T) {
	oo := os.Stdout

	r, w, _ := os.Pipe()
	os.Stdout = w
	err := LoadAndBuild("test/sass/file.scss", &BuildArgs{}, NewPartialMap())
	if err != nil {
		t.Error(err)
	}
	outC := make(chan string)
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	w.Close()
	os.Stdout = oo
	out := <-outC

	e := `div {
  color: black; }
Rebuilt: test/sass/file.scss
`
	if e != out {
		t.Errorf("got:\n%s\nwanted:\n%s", out, e)
	}
}

func TestLandB_error(t *testing.T) {

	oo := os.Stdout
	var w *os.File
	defer func() {
		w.Close()
		os.Stdout = oo
	}()
	os.Stdout = w
	err := LoadAndBuild("test/sass/error.scss", &BuildArgs{}, NewPartialMap())
	qs := fmt.Sprintf("%q", err.Error())

	e := `Invalid CSS after \"div {\": expected \"}\", was \"\"`
	if !strings.Contains(qs, e) {
		t.Fatalf("Error contains invalid text:\n%s", qs)
	}
}

func TestLandB_updateFile(t *testing.T) {
	s := "file.scss"
	ren := updateFileOutputType(s)
	if e := "file.css"; e != ren {
		t.Errorf("got: %s wanted: %s", ren, e)
	}
}

func TestLoadAndBuild_args(t *testing.T) {
	oo := os.Stdout

	r, w, _ := os.Pipe()
	os.Stdout = w
	err := LoadAndBuild("test/sass/file.scss",
		&BuildArgs{
			BuildDir: "test/build",
			Includes: "test",
		},
		NewPartialMap(),
	)
	if err != nil {
		t.Error(err)
	}
	outC := make(chan string)
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	w.Close()
	os.Stdout = oo
	out := <-outC

	e := `Rebuilt: test/sass/file.scss
`
	if e != out {
		t.Errorf("got:\n%s\nwanted:\n%s", out, e)
	}
}

func TestLoadAndBuild_comply(t *testing.T) {
	stdout := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w
	err := LoadAndBuild("test/compass/top.scss",
		&BuildArgs{
			BuildDir: "test/build",
			Includes: "test",
		},
		NewPartialMap(),
	)
	if err != nil {
		t.Error(err)
	}
	outC := make(chan string)
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	w.Close()
	os.Stdout = stdout
	out := <-outC

	e := `Rebuilt: test/compass/top.scss
`
	if e != out {
		t.Errorf("got:\n%s\nwanted:\n%s", out, e)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"../service"
	"fmt"
	"os"
	"runtime"
	"time"
)

func main() {
	commands := make(chan service.Command)
	responses := make(chan service.Response)
	events := make(chan service.Event)
	svc, err := service.NewService([]string{"sleep", "10"})
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	go svc.Run(commands, events)
	go func() {
		commands <- service.Command{service.Start, responses}
		time.Sleep(5 * time.Second)
		commands <- service.Command{service.Restart, responses}
		time.Sleep(5 * time.Second)
		commands <- service.Command{service.Stop, responses}
		time.Sleep(5 * time.Second)
		commands <- service.Command{service.Start, responses}
		time.Sleep(15 * time.Second)
		commands <- service.Command{service.Shutdown, responses}
	}()

loop:
	for {
		select {
		case event := <-events:
			fmt.Printf("service is %s\n", event.State)
		case response := <-responses:
			if response.Success() {
				fmt.Printf("command %s succeeded\n", response.Name)
				if response.Name == service.Shutdown {
					break loop
				}
			} else {
				fmt.Printf("command %s failed: %s\n", response.Name, response.Error)
			}
		default:
			runtime.Gosched()
		}
	}
}
<commit_msg>Print errors in example.<commit_after>package main

import (
	"../service"
	"fmt"
	"os"
	"runtime"
	"time"
)

func main() {
	commands := make(chan service.Command)
	responses := make(chan service.Response)
	events := make(chan service.Event)
	svc, err := service.NewService([]string{"sleep", "10"})
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	go svc.Run(commands, events)
	go func() {
		commands <- service.Command{service.Start, responses}
		time.Sleep(5 * time.Second)
		commands <- service.Command{service.Restart, responses}
		time.Sleep(5 * time.Second)
		commands <- service.Command{service.Stop, responses}
		time.Sleep(5 * time.Second)
		commands <- service.Command{service.Start, responses}
		time.Sleep(15 * time.Second)
		commands <- service.Command{service.Shutdown, responses}
	}()

loop:
	for {
		select {
		case event := <-events:
			if event.Error == nil {
				fmt.Printf("service is %s\n", event.State)
			} else {
				fmt.Printf("service is %s: %s\n", event.State, event.Error)
			}
		case response := <-responses:
			if response.Success() {
				fmt.Printf("command %s succeeded\n", response.Name)
				if response.Name == service.Shutdown {
					break loop
				}
			} else {
				fmt.Printf("command %s failed: %s\n", response.Name, response.Error)
			}
		default:
			runtime.Gosched()
		}
	}
}
<|endoftext|>


<commit_before>package common

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"go.skia.org/infra/fuzzer/go/config"
	"go.skia.org/infra/go/buildskia"
	"go.skia.org/infra/go/fileutil"
	"go.skia.org/infra/go/git/gitinfo"
	"go.skia.org/infra/go/sklog"
)

// BuildClangHarness builds the test harness for fuzzing using clang, pulling it from the executable
// cache if possible.  It returns the path to the executable (which should be copied somewhere else)
// and any error.
func BuildClangHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s clang harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", config.Common.ClangPath),
		fmt.Sprintf("cxx=%q", config.Common.ClangPlusPlusPath),
	}
	return buildOrGetCachedHarness(ctx, "clang", buildType, isClean, buildArgs)
}

// BuildASANHarness builds the test harness for fuzzing using clang and AddressSanitizer, pulling it
// from the executable cache if possible.  It returns the path to the executable (which should be
// copied somewhere else) and any error.
func BuildASANHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s ASAN harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", config.Common.ClangPath),
		fmt.Sprintf("cxx=%q", config.Common.ClangPlusPlusPath),
		`sanitize="address"`, // No UBSAN, to avoid noise.
	}
	return buildOrGetCachedHarness(ctx, "asan", buildType, isClean, buildArgs)
}

// BuildFuzzingHarness builds the test harness for fuzzing using afl-instrumented clang, pulling it
// from the executable cache if possible.  It returns the path to the executable (which should be
// copied somewhere else) and any error.
func BuildFuzzingHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s fuzzing harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", filepath.Join(config.Generator.AflRoot, "afl-clang-fast")),
		fmt.Sprintf("cxx=%q", filepath.Join(config.Generator.AflRoot, "afl-clang-fast++")),
	}

	return buildOrGetCachedHarness(ctx, "afl-instrumented", buildType, isClean, buildArgs)
}

// buildOrGetCachedHarness first looks into the ExecutableCache for a already built binary.  If it
// cannot find one, it triggers a build and puts it in the cache.  The cache is structured like:
// [ExecutableCachePath]/[skia-hash]/[buildType]/[buildname]
// It returns the path to the executable (which should be copied somewhere else) and any error.
// buildName is a human friendly name for this build type. buildType is Release, Debug, etc,
// buildName and buildType work together to identify a unique build (in the eyes of the cache, at
// least).  isClean is whether the build output directory should be cleared before making a new
// build.  buildArgs are the arguments passed to GN.
func buildOrGetCachedHarness(ctx context.Context, buildName string, buildType buildskia.ReleaseType, isClean bool, buildArgs []string) (string, error) {
	if buildType == buildskia.RELEASE_BUILD {
		buildArgs = append(buildArgs, "is_debug=false")
	}
	// This makes crashing because we ran out of memory or because someone called SK_ABORT turn
	// into an exit(1), so we don't count it as a "crash".
	buildArgs = append(buildArgs, `extra_cflags=["-DIS_FUZZING", "-DIS_FUZZING_AFL"]`)
	// System freetype has many MSAN-like bugs, which can throw off our fuzzer. Build our own
	// (newer) freetype to minimize these.
	buildArgs = append(buildArgs, "skia_use_system_freetype2=false")

	d := filepath.Join(config.Common.SkiaRoot, "skia")
	gi, err := gitinfo.NewGitInfo(ctx, d, false, false)
	if err != nil {
		return "", fmt.Errorf("Could not locate git info about Skia Root %s: %s", d, err)
	}
	hashes := gi.LastN(ctx, 1)
	if len(hashes) != 1 {
		return "", fmt.Errorf("Could not get last git hash, instead got %q", hashes)
	}

	cache := filepath.Join(config.Common.ExecutableCachePath, hashes[0], string(buildType))
	cache, err = fileutil.EnsureDirExists(cache)
	if err != nil {
		return "", fmt.Errorf("Could not create cache dir %s: %s", cache, err)
	}

	cachedFile := filepath.Join(cache, buildName)
	if info, err := os.Stat(cachedFile); err != nil {
		if os.IsNotExist(err) {
			sklog.Infof("Did not find %s %s build for revision %s in cache.  Going to build it.", buildName, buildType, hashes[0])
			if builtExePath, err := buildHarness(ctx, buildType, isClean, buildArgs); err != nil {
				return "", fmt.Errorf("There was a problem building: %s", err)
			} else {
				return cachedFile, fileutil.CopyExecutable(builtExePath, cachedFile)
			}
		}
		// If it's not, Something is bad, and we should error
		return "", fmt.Errorf("There was something unexpectedly wrong with the cached executable: %s", err)
	} else if info.IsDir() {
		return "", fmt.Errorf("The cached executable %s was actually a directory.  This should not be the case", cachedFile)
	} else {
		sklog.Infof("Found %s %s build for revision %s in cache", buildName, buildType, hashes[0])
		return cachedFile, nil
	}
}

// buildHarnesGNs builds the test harness for fuzzing. It activates Skia's GN command, which creates
// the build (ninja) files for a Clang build. Then, it uses buildskia.GNNinjaBuild to execute the
// build. It returns the path to the executable (which should be copied somewhere else) and
// any error. buildType is Release, Debug, etc, isClean is whether the build output directory should
// be cleared before making a new build. buildArgs are the arguments that should be passed into GN.
func buildHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool, buildArgs []string) (string, error) {
	// clean previous build if specified

	buildLocation := filepath.Join(config.Common.SkiaRoot, "skia", "out", string(buildType))
	if isClean {
		if err := os.RemoveAll(buildLocation); err != nil {
			return "", fmt.Errorf("Could not clear out %s before building: %s", buildLocation, err)
		}
	}

	if err := buildskia.GNGen(ctx, config.Common.SkiaRoot, config.Common.DepotToolsPath, string(buildType), buildArgs); err != nil {
		return "", fmt.Errorf("Failed GN: %s", err)
	}

	builtExe := filepath.Join(buildLocation, TEST_HARNESS_NAME)

	_, err := buildskia.GNNinjaBuild(ctx, config.Common.SkiaRoot, config.Common.DepotToolsPath, string(buildType), TEST_HARNESS_NAME, config.Common.VerboseBuilds)
	return builtExe, err
}
<commit_msg>Update flag to what actually landed<commit_after>package common

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"go.skia.org/infra/fuzzer/go/config"
	"go.skia.org/infra/go/buildskia"
	"go.skia.org/infra/go/fileutil"
	"go.skia.org/infra/go/git/gitinfo"
	"go.skia.org/infra/go/sklog"
)

// BuildClangHarness builds the test harness for fuzzing using clang, pulling it from the executable
// cache if possible.  It returns the path to the executable (which should be copied somewhere else)
// and any error.
func BuildClangHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s clang harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", config.Common.ClangPath),
		fmt.Sprintf("cxx=%q", config.Common.ClangPlusPlusPath),
	}
	return buildOrGetCachedHarness(ctx, "clang", buildType, isClean, buildArgs)
}

// BuildASANHarness builds the test harness for fuzzing using clang and AddressSanitizer, pulling it
// from the executable cache if possible.  It returns the path to the executable (which should be
// copied somewhere else) and any error.
func BuildASANHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s ASAN harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", config.Common.ClangPath),
		fmt.Sprintf("cxx=%q", config.Common.ClangPlusPlusPath),
		`sanitize="address"`, // No UBSAN, to avoid noise.
	}
	return buildOrGetCachedHarness(ctx, "asan", buildType, isClean, buildArgs)
}

// BuildFuzzingHarness builds the test harness for fuzzing using afl-instrumented clang, pulling it
// from the executable cache if possible.  It returns the path to the executable (which should be
// copied somewhere else) and any error.
func BuildFuzzingHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s fuzzing harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", filepath.Join(config.Generator.AflRoot, "afl-clang-fast")),
		fmt.Sprintf("cxx=%q", filepath.Join(config.Generator.AflRoot, "afl-clang-fast++")),
	}

	return buildOrGetCachedHarness(ctx, "afl-instrumented", buildType, isClean, buildArgs)
}

// buildOrGetCachedHarness first looks into the ExecutableCache for a already built binary.  If it
// cannot find one, it triggers a build and puts it in the cache.  The cache is structured like:
// [ExecutableCachePath]/[skia-hash]/[buildType]/[buildname]
// It returns the path to the executable (which should be copied somewhere else) and any error.
// buildName is a human friendly name for this build type. buildType is Release, Debug, etc,
// buildName and buildType work together to identify a unique build (in the eyes of the cache, at
// least).  isClean is whether the build output directory should be cleared before making a new
// build.  buildArgs are the arguments passed to GN.
func buildOrGetCachedHarness(ctx context.Context, buildName string, buildType buildskia.ReleaseType, isClean bool, buildArgs []string) (string, error) {
	if buildType == buildskia.RELEASE_BUILD {
		buildArgs = append(buildArgs, "is_debug=false")
	}
	// This makes crashing because we ran out of memory or because someone called SK_ABORT turn
	// into an exit(1), so we don't count it as a "crash".
	buildArgs = append(buildArgs, `extra_cflags=["-DIS_FUZZING", "-DIS_FUZZING_WITH_AFL"]`)
	// System freetype has many MSAN-like bugs, which can throw off our fuzzer. Build our own
	// (newer) freetype to minimize these.
	buildArgs = append(buildArgs, "skia_use_system_freetype2=false")

	d := filepath.Join(config.Common.SkiaRoot, "skia")
	gi, err := gitinfo.NewGitInfo(ctx, d, false, false)
	if err != nil {
		return "", fmt.Errorf("Could not locate git info about Skia Root %s: %s", d, err)
	}
	hashes := gi.LastN(ctx, 1)
	if len(hashes) != 1 {
		return "", fmt.Errorf("Could not get last git hash, instead got %q", hashes)
	}

	cache := filepath.Join(config.Common.ExecutableCachePath, hashes[0], string(buildType))
	cache, err = fileutil.EnsureDirExists(cache)
	if err != nil {
		return "", fmt.Errorf("Could not create cache dir %s: %s", cache, err)
	}

	cachedFile := filepath.Join(cache, buildName)
	if info, err := os.Stat(cachedFile); err != nil {
		if os.IsNotExist(err) {
			sklog.Infof("Did not find %s %s build for revision %s in cache.  Going to build it.", buildName, buildType, hashes[0])
			if builtExePath, err := buildHarness(ctx, buildType, isClean, buildArgs); err != nil {
				return "", fmt.Errorf("There was a problem building: %s", err)
			} else {
				return cachedFile, fileutil.CopyExecutable(builtExePath, cachedFile)
			}
		}
		// If it's not, Something is bad, and we should error
		return "", fmt.Errorf("There was something unexpectedly wrong with the cached executable: %s", err)
	} else if info.IsDir() {
		return "", fmt.Errorf("The cached executable %s was actually a directory.  This should not be the case", cachedFile)
	} else {
		sklog.Infof("Found %s %s build for revision %s in cache", buildName, buildType, hashes[0])
		return cachedFile, nil
	}
}

// buildHarnesGNs builds the test harness for fuzzing. It activates Skia's GN command, which creates
// the build (ninja) files for a Clang build. Then, it uses buildskia.GNNinjaBuild to execute the
// build. It returns the path to the executable (which should be copied somewhere else) and
// any error. buildType is Release, Debug, etc, isClean is whether the build output directory should
// be cleared before making a new build. buildArgs are the arguments that should be passed into GN.
func buildHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool, buildArgs []string) (string, error) {
	// clean previous build if specified

	buildLocation := filepath.Join(config.Common.SkiaRoot, "skia", "out", string(buildType))
	if isClean {
		if err := os.RemoveAll(buildLocation); err != nil {
			return "", fmt.Errorf("Could not clear out %s before building: %s", buildLocation, err)
		}
	}

	if err := buildskia.GNGen(ctx, config.Common.SkiaRoot, config.Common.DepotToolsPath, string(buildType), buildArgs); err != nil {
		return "", fmt.Errorf("Failed GN: %s", err)
	}

	builtExe := filepath.Join(buildLocation, TEST_HARNESS_NAME)

	_, err := buildskia.GNNinjaBuild(ctx, config.Common.SkiaRoot, config.Common.DepotToolsPath, string(buildType), TEST_HARNESS_NAME, config.Common.VerboseBuilds)
	return builtExe, err
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"syscall"
	"time"

	"github.com/dickeyxxx/golock"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = "master"
		}
		Update(channel)
	},
}

var binPath = filepath.Join(AppDir, "heroku-cli")
var updateLockPath = filepath.Join(AppDir, "updating.lock")
var autoupdateFile = filepath.Join(AppDir, "autoupdate")

func init() {
	if runtime.GOOS == "windows" {
		binPath = binPath + ".exe"
	}
}

// Update updates the CLI and plugins
func Update(channel string) {
	Logln("updating")
	if err := golock.Lock(updateLockPath); err != nil {
		panic(err)
	}
	defer golock.Unlock(updateLockPath)
	done := make(chan bool)
	go func() {
		updateCLI(channel)
		updatePlugins()
		done <- true
	}()
	select {
	case <-time.After(time.Second * 120):
		Errln("Timed out while updating")
		Logln("Timed out while updating")
	case <-done:
		touchAutoupdateFile()
	}
	Logln("done updating")
}

func updatePlugins() {
	Logln("updating plugins")
	b, _ := node.UpdatePackages()
	if len(b) > 0 {
		Logln("clearing plugins cache")
		ClearPluginCache()
		WritePluginCache(GetPlugins())
	}
}

func updateCLI(channel string) {
	manifest := getUpdateManifest(channel)
	if manifest.Version == Version && manifest.Channel == Channel {
		return
	}
	if !updatable() {
		Errf("Out of date: You are running %s but %s is out.\n", Version, manifest.Version)
		return
	}
	Logln("updating from %s to %s (%s)", Version, manifest.Version, manifest.Channel)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	update(build.URL, build.Sha1)
	reexecBin()
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded(t string) bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	if t == "soft" {
		return time.Since(f.ModTime()) > 1*time.Hour
	}
	return time.Since(f.ModTime()) > 168*time.Hour
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) manifest {
	res, err := http.Get("https://d1gvo455cekpjp.cloudfront.net/" + channel + "/manifest.json")
	if err != nil {
		panic(err)
	}
	var m manifest
	json.NewDecoder(res.Body).Decode(&m)
	return m
}

func updatable() bool {
	path, err := filepath.Abs(os.Args[0])
	if err != nil {
		Errln(err)
	}
	return path == binPath
}

func update(url, sha1 string) {
	// on windows we can't remove an existing file or remove the running binary
	// so we download the file to binName.new
	// move the running binary to binName.old (deleting any existing file first)
	// rename the downloaded file to binName
	if err := downloadBin(binPath+".new", url); err != nil {
		panic(err)
	}
	if fileSha1(binPath+".new") != sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath + ".old")
	if err := os.Rename(binPath, binPath+".old"); err != nil {
		panic(err)
	}
	if err := os.Rename(binPath+".new", binPath); err != nil {
		panic(err)
	}
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	client := &http.Client{}
	req, err := http.NewRequest("GET", url+".gz", nil)
	if err != nil {
		return err
	}
	req.Header.Add("Accept-Encoding", "gzip")
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	uncompressed, err := gzip.NewReader(resp.Body)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, uncompressed)
	if err != nil {
		return err
	}
	return out.Close()
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

func reexecBin() {
	if runtime.GOOS == "windows" {
		cmd := exec.Command(binPath, os.Args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Run()
		os.Exit(0)
	} else {
		if err := syscall.Exec(binPath, os.Args, os.Environ()); err != nil {
			panic(err)
		}
		os.Exit(99) // should never happen
	}
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	if err := exec.Command("./heroku-cli", "update").Start(); err != nil {
		panic(err)
	}
}
<commit_msg>small refactor<commit_after>package main

import (
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"syscall"
	"time"

	"github.com/dickeyxxx/golock"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = "master"
		}
		Update(channel)
	},
}

var binPath = filepath.Join(AppDir, "heroku-cli")
var updateLockPath = filepath.Join(AppDir, "updating.lock")
var autoupdateFile = filepath.Join(AppDir, "autoupdate")

func init() {
	if runtime.GOOS == "windows" {
		binPath = binPath + ".exe"
	}
}

// Update updates the CLI and plugins
func Update(channel string) {
	Logln("updating")
	if err := golock.Lock(updateLockPath); err != nil {
		panic(err)
	}
	defer golock.Unlock(updateLockPath)
	done := make(chan bool)
	go func() {
		updateCLI(channel)
		updatePlugins()
		done <- true
	}()
	select {
	case <-time.After(time.Second * 120):
		Errln("Timed out while updating")
		Logln("Timed out while updating")
	case <-done:
		touchAutoupdateFile()
	}
	Logln("done updating")
}

func updatePlugins() {
	Logln("updating plugins")
	b, _ := node.UpdatePackages()
	if len(b) > 0 {
		Logln("clearing plugins cache")
		ClearPluginCache()
		WritePluginCache(GetPlugins())
	}
}

func updateCLI(channel string) {
	manifest := getUpdateManifest(channel)
	if manifest.Version == Version && manifest.Channel == Channel {
		return
	}
	if !updatable() {
		Errf("Out of date: You are running %s but %s is out.\n", Version, manifest.Version)
		return
	}
	Logln("updating from %s to %s (%s)", Version, manifest.Version, manifest.Channel)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	// on windows we can't remove an existing file or remove the running binary
	// so we download the file to binName.new
	// move the running binary to binName.old (deleting any existing file first)
	// rename the downloaded file to binName
	if err := downloadBin(binPath+".new", build.URL); err != nil {
		panic(err)
	}
	if fileSha1(binPath+".new") != build.Sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath + ".old")
	if err := os.Rename(binPath, binPath+".old"); err != nil {
		panic(err)
	}
	if err := os.Rename(binPath+".new", binPath); err != nil {
		panic(err)
	}
	reexecBin()
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded(t string) bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	if t == "soft" {
		return time.Since(f.ModTime()) > 1*time.Hour
	}
	return time.Since(f.ModTime()) > 168*time.Hour
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) manifest {
	res, err := http.Get("https://d1gvo455cekpjp.cloudfront.net/" + channel + "/manifest.json")
	if err != nil {
		panic(err)
	}
	var m manifest
	json.NewDecoder(res.Body).Decode(&m)
	return m
}

func updatable() bool {
	path, err := filepath.Abs(os.Args[0])
	if err != nil {
		Errln(err)
	}
	return path == binPath
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	client := &http.Client{}
	req, err := http.NewRequest("GET", url+".gz", nil)
	if err != nil {
		return err
	}
	req.Header.Add("Accept-Encoding", "gzip")
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	uncompressed, err := gzip.NewReader(resp.Body)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, uncompressed)
	if err != nil {
		return err
	}
	return out.Close()
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

func reexecBin() {
	if runtime.GOOS == "windows" {
		cmd := exec.Command(binPath, os.Args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Run()
		os.Exit(0)
	} else {
		if err := syscall.Exec(binPath, os.Args, os.Environ()); err != nil {
			panic(err)
		}
		os.Exit(99) // should never happen
	}
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	if err := exec.Command("./heroku-cli", "update").Start(); err != nil {
		panic(err)
	}
}
<|endoftext|>