<commit_before>package cluster

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/samalba/dockerclient"
)

const (
	// Force-refresh the state of the node this often.
	stateRefreshPeriod = 30 * time.Second

	// Timeout for requests sent out to the node.
	requestTimeout = 10 * time.Second
)

func NewNode(addr string, overcommitRatio float64) *Node {
	e := &Node{
		Addr:            addr,
		Labels:          make(map[string]string),
		ch:              make(chan bool),
		containers:      make(map[string]*Container),
		healthy:         true,
		overcommitRatio: int64(overcommitRatio * 100),
	}
	return e
}

type Node struct {
	sync.RWMutex

	ID     string
	IP     string
	Addr   string
	Name   string
	Cpus   int64
	Memory int64
	Labels map[string]string

	ch              chan bool
	containers      map[string]*Container
	images          []*dockerclient.Image
	client          dockerclient.Client
	eventHandler    EventHandler
	healthy         bool
	overcommitRatio int64
}

// Connect will initialize a connection to the Docker daemon running on the
// host, gather machine specs (memory, cpu, ...) and monitor state changes.
func (n *Node) Connect(config *tls.Config) error {
	parts := strings.Split(n.Addr, ":")
	addr, err := net.ResolveIPAddr("ip4", parts[0])
	if err != nil {
		return err
	}
	n.IP = addr.IP.String()

	c, err := dockerclient.NewDockerClientTimeout(n.IP+":"+parts[1], config, time.Duration(requestTimeout))
	if err != nil {
		return err
	}

	return n.connectClient(c)
}

func (n *Node) connectClient(client dockerclient.Client) error {
	n.client = client

	// Fetch the engine labels.
	if err := n.updateSpecs(); err != nil {
		n.client = nil
		return err
	}

	// Force a state update before returning.
	if err := n.refreshContainers(); err != nil {
		n.client = nil
		return err
	}
	if err := n.refreshImages(); err != nil {
		n.client = nil
		return err
	}

	// Start the update loop.
	go n.refreshLoop()

	// Start monitoring events from the Node.
	n.client.StartMonitorEvents(n.handler)

	return nil
}

// IsConnected returns true if the engine is connected to a remote docker API
func (n *Node) IsConnected() bool {
	return n.client != nil
}

func (n *Node) IsHealthy() bool {
	return n.healthy
}

// Gather node specs (CPU, memory, constraints, ...).
func (n *Node) updateSpecs() error {
	info, err := n.client.Info()
	if err != nil {
		return err
	}
	// Older versions of Docker don't expose the ID field and are not supported
	// by Swarm.  Catch the error ASAP and refuse to connect.
	if len(info.ID) == 0 {
		return fmt.Errorf("Node %s is running an unsupported version of Docker Engine. Please upgrade.", n.Addr)
	}
	n.ID = info.ID
	n.Name = info.Name
	n.Cpus = info.NCPU
	n.Memory = info.MemTotal
	n.Labels = map[string]string{
		"storagedriver":   info.Driver,
		"executiondriver": info.ExecutionDriver,
		"kernelversion":   info.KernelVersion,
		"operatingsystem": info.OperatingSystem,
	}
	for _, label := range info.Labels {
		kv := strings.SplitN(label, "=", 2)
		n.Labels[kv[0]] = kv[1]
	}
	return nil
}

// Refresh the list of images on the node.
func (n *Node) refreshImages() error {
	images, err := n.client.ListImages()
	if err != nil {
		return err
	}
	n.Lock()
	n.images = images
	n.Unlock()
	return nil
}

// Refresh the list and status of containers running on the node.
func (n *Node) refreshContainers() error {
	containers, err := n.client.ListContainers(true, false, "")
	if err != nil {
		return err
	}

	n.Lock()
	defer n.Unlock()

	merged := make(map[string]*Container)
	for _, c := range containers {
		merged, err = n.updateContainer(c, merged)
		if err != nil {
			log.Errorf("[%s/%s] Unable to update state of %s", n.ID, n.Name, c.Id)
		}
	}

	n.containers = merged

	log.Debugf("[%s/%s] Updated state", n.ID, n.Name)
	return nil
}

// Refresh the status of a container running on the node.
func (n *Node) refreshContainer(ID string) error {
	containers, err := n.client.ListContainers(true, false, fmt.Sprintf("{%q:[%q]}", "id", ID))
	if err != nil {
		return err
	}

	if len(containers) > 1 {
		// We expect one container, if we get more than one, trigger a full refresh.
		return n.refreshContainers()
	}

	n.Lock()
	defer n.Unlock()

	if len(containers) == 0 {
		// The container doesn't exist on the node, remove it.
		delete(n.containers, ID)
		return nil
	}

	_, err = n.updateContainer(containers[0], n.containers)
	return err
}

func (n *Node) ForceRefreshContainer(c dockerclient.Container) error {
	return n.inspectContainer(c, n.containers, true)
}

func (n *Node) inspectContainer(c dockerclient.Container, containers map[string]*Container, lock bool) error {

	container := &Container{
		Container: c,
		Node:      n,
	}

	info, err := n.client.InspectContainer(c.Id)
	if err != nil {
		return err
	}
	container.Info = *info

	// real CpuShares -> nb of CPUs
	container.Info.Config.CpuShares = container.Info.Config.CpuShares / 100.0 * n.Cpus

	if lock {
		n.Lock()
		defer n.Unlock()
	}
	containers[container.Id] = container

	return nil
}

func (n *Node) updateContainer(c dockerclient.Container, containers map[string]*Container) (map[string]*Container, error) {
	if current, exists := n.containers[c.Id]; exists {
		// The container exists. Update its state.
		current.Container = c
		containers[current.Id] = current
	} else {
		// This is a brand new container.
		if err := n.inspectContainer(c, containers, false); err != nil {
			return nil, err
		}
	}
	return containers, nil
}

func (n *Node) refreshContainersAsync() {
	n.ch <- true
}

func (n *Node) refreshLoop() {
	for {
		var err error
		select {
		case <-n.ch:
			err = n.refreshContainers()
		case <-time.After(stateRefreshPeriod):
			err = n.refreshContainers()
		}

		if err == nil {
			err = n.refreshImages()
		}

		if err != nil {
			n.healthy = false
			log.Errorf("[%s/%s] Flagging node as dead. Updated state failed: %v", n.ID, n.Name, err)
		} else {
			if !n.healthy {
				log.Infof("[%s/%s] Node came back to life. Hooray!", n.ID, n.Name)
			}
			n.healthy = true
		}
	}
}

// Return the sum of memory reserved by containers.
func (n *Node) ReservedMemory() int64 {
	var r int64 = 0
	n.RLock()
	for _, c := range n.containers {
		r += c.Info.Config.Memory
	}
	n.RUnlock()
	return r
}

// Return the sum of CPUs reserved by containers.
func (n *Node) ReservedCpus() int64 {
	var r int64 = 0
	n.RLock()
	for _, c := range n.containers {
		r += c.Info.Config.CpuShares
	}
	n.RUnlock()
	return r
}

func (n *Node) UsableMemory() int64 {
	return n.Memory + (n.Memory * n.overcommitRatio / 100)
}

func (n *Node) UsableCpus() int64 {
	return n.Cpus + (n.Cpus * n.overcommitRatio / 100)
}

func (n *Node) Create(config *dockerclient.ContainerConfig, name string, pullImage bool) (*Container, error) {
	var (
		err    error
		id     string
		client = n.client
	)

	newConfig := *config

	// nb of CPUs -> real CpuShares
	newConfig.CpuShares = config.CpuShares * 100 / n.Cpus

	if id, err = client.CreateContainer(&newConfig, name); err != nil {
		// If the error is other than not found, abort immediately.
		if err != dockerclient.ErrNotFound || !pullImage {
			return nil, err
		}
		// Otherwise, try to pull the image...
		if err = n.Pull(config.Image); err != nil {
			return nil, err
		}
		// ...And try again.
		if id, err = client.CreateContainer(&newConfig, name); err != nil {
			return nil, err
		}
	}

	// Register the container immediately while waiting for a state refresh.
	// Force a state refresh to pick up the newly created container.
	n.refreshContainer(id)

	n.RLock()
	defer n.RUnlock()

	return n.containers[id], nil
}

// Destroy and remove a container from the node.
func (n *Node) Destroy(container *Container, force bool) error {
	if err := n.client.RemoveContainer(container.Id, force); err != nil {
		return err
	}

	// Remove the container from the state. Eventually, the state refresh loop
	// will rewrite this.
	n.Lock()
	defer n.Unlock()
	delete(n.containers, container.Id)

	return nil
}

func (n *Node) Pull(image string) error {
	if err := n.client.PullImage(image, nil); err != nil {
		return err
	}
	return nil
}

// Register an event handler.
func (n *Node) Events(h EventHandler) error {
	if n.eventHandler != nil {
		return errors.New("event handler already set")
	}
	n.eventHandler = h
	return nil
}

func (n *Node) Containers() []*Container {
	containers := []*Container{}
	n.RLock()
	for _, container := range n.containers {
		containers = append(containers, container)
	}
	n.RUnlock()
	return containers
}

func (n *Node) Images() []*dockerclient.Image {
	images := []*dockerclient.Image{}
	n.RLock()
	for _, image := range n.images {
		images = append(images, image)
	}
	n.RUnlock()
	return images
}

func (n *Node) String() string {
	return fmt.Sprintf("node %s addr %s", n.ID, n.Addr)
}

func (n *Node) handler(ev *dockerclient.Event, args ...interface{}) {
	// Something changed - refresh our internal state.
	if ev.Status == "pull" {
		n.refreshImages()
	} else {
		n.refreshContainer(ev.Id)
	}

	// If there is no event handler registered, abort right now.
	if n.eventHandler == nil {
		return
	}

	event := &Event{
		Node:  n,
		Event: *ev,
	}

	n.eventHandler.Handle(event)
}

// Inject a container into the internal state.
func (n *Node) AddContainer(container *Container) error {
	n.Lock()
	defer n.Unlock()

	if _, ok := n.containers[container.Id]; ok {
		return errors.New("container already exists")
	}
	n.containers[container.Id] = container
	return nil
}

// Inject an image into the internal state.
func (n *Node) AddImage(image *dockerclient.Image) {
	n.Lock()
	defer n.Unlock()

	n.images = append(n.images, image)
}

// Remove a container from the internal test.
func (n *Node) RemoveContainer(container *Container) error {
	n.Lock()
	defer n.Unlock()

	if _, ok := n.containers[container.Id]; !ok {
		return errors.New("container not found")
	}
	delete(n.containers, container.Id)
	return nil
}

// Wipes the internal container state.
func (n *Node) CleanupContainers() {
	n.Lock()
	n.containers = make(map[string]*Container)
	n.Unlock()
}
<commit_msg>add untag and delete<commit_after>package cluster

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/samalba/dockerclient"
)

const (
	// Force-refresh the state of the node this often.
	stateRefreshPeriod = 30 * time.Second

	// Timeout for requests sent out to the node.
	requestTimeout = 10 * time.Second
)

func NewNode(addr string, overcommitRatio float64) *Node {
	e := &Node{
		Addr:            addr,
		Labels:          make(map[string]string),
		ch:              make(chan bool),
		containers:      make(map[string]*Container),
		healthy:         true,
		overcommitRatio: int64(overcommitRatio * 100),
	}
	return e
}

type Node struct {
	sync.RWMutex

	ID     string
	IP     string
	Addr   string
	Name   string
	Cpus   int64
	Memory int64
	Labels map[string]string

	ch              chan bool
	containers      map[string]*Container
	images          []*dockerclient.Image
	client          dockerclient.Client
	eventHandler    EventHandler
	healthy         bool
	overcommitRatio int64
}

// Connect will initialize a connection to the Docker daemon running on the
// host, gather machine specs (memory, cpu, ...) and monitor state changes.
func (n *Node) Connect(config *tls.Config) error {
	parts := strings.Split(n.Addr, ":")
	addr, err := net.ResolveIPAddr("ip4", parts[0])
	if err != nil {
		return err
	}
	n.IP = addr.IP.String()

	c, err := dockerclient.NewDockerClientTimeout(n.IP+":"+parts[1], config, time.Duration(requestTimeout))
	if err != nil {
		return err
	}

	return n.connectClient(c)
}

func (n *Node) connectClient(client dockerclient.Client) error {
	n.client = client

	// Fetch the engine labels.
	if err := n.updateSpecs(); err != nil {
		n.client = nil
		return err
	}

	// Force a state update before returning.
	if err := n.refreshContainers(); err != nil {
		n.client = nil
		return err
	}
	if err := n.refreshImages(); err != nil {
		n.client = nil
		return err
	}

	// Start the update loop.
	go n.refreshLoop()

	// Start monitoring events from the Node.
	n.client.StartMonitorEvents(n.handler)

	return nil
}

// IsConnected returns true if the engine is connected to a remote docker API
func (n *Node) IsConnected() bool {
	return n.client != nil
}

func (n *Node) IsHealthy() bool {
	return n.healthy
}

// Gather node specs (CPU, memory, constraints, ...).
func (n *Node) updateSpecs() error {
	info, err := n.client.Info()
	if err != nil {
		return err
	}
	// Older versions of Docker don't expose the ID field and are not supported
	// by Swarm.  Catch the error ASAP and refuse to connect.
	if len(info.ID) == 0 {
		return fmt.Errorf("Node %s is running an unsupported version of Docker Engine. Please upgrade.", n.Addr)
	}
	n.ID = info.ID
	n.Name = info.Name
	n.Cpus = info.NCPU
	n.Memory = info.MemTotal
	n.Labels = map[string]string{
		"storagedriver":   info.Driver,
		"executiondriver": info.ExecutionDriver,
		"kernelversion":   info.KernelVersion,
		"operatingsystem": info.OperatingSystem,
	}
	for _, label := range info.Labels {
		kv := strings.SplitN(label, "=", 2)
		n.Labels[kv[0]] = kv[1]
	}
	return nil
}

// Refresh the list of images on the node.
func (n *Node) refreshImages() error {
	images, err := n.client.ListImages()
	if err != nil {
		return err
	}
	n.Lock()
	n.images = images
	n.Unlock()
	return nil
}

// Refresh the list and status of containers running on the node.
func (n *Node) refreshContainers() error {
	containers, err := n.client.ListContainers(true, false, "")
	if err != nil {
		return err
	}

	n.Lock()
	defer n.Unlock()

	merged := make(map[string]*Container)
	for _, c := range containers {
		merged, err = n.updateContainer(c, merged)
		if err != nil {
			log.Errorf("[%s/%s] Unable to update state of %s", n.ID, n.Name, c.Id)
		}
	}

	n.containers = merged

	log.Debugf("[%s/%s] Updated state", n.ID, n.Name)
	return nil
}

// Refresh the status of a container running on the node.
func (n *Node) refreshContainer(ID string) error {
	containers, err := n.client.ListContainers(true, false, fmt.Sprintf("{%q:[%q]}", "id", ID))
	if err != nil {
		return err
	}

	if len(containers) > 1 {
		// We expect one container, if we get more than one, trigger a full refresh.
		return n.refreshContainers()
	}

	n.Lock()
	defer n.Unlock()

	if len(containers) == 0 {
		// The container doesn't exist on the node, remove it.
		delete(n.containers, ID)
		return nil
	}

	_, err = n.updateContainer(containers[0], n.containers)
	return err
}

func (n *Node) ForceRefreshContainer(c dockerclient.Container) error {
	return n.inspectContainer(c, n.containers, true)
}

func (n *Node) inspectContainer(c dockerclient.Container, containers map[string]*Container, lock bool) error {

	container := &Container{
		Container: c,
		Node:      n,
	}

	info, err := n.client.InspectContainer(c.Id)
	if err != nil {
		return err
	}
	container.Info = *info

	// real CpuShares -> nb of CPUs
	container.Info.Config.CpuShares = container.Info.Config.CpuShares / 100.0 * n.Cpus

	if lock {
		n.Lock()
		defer n.Unlock()
	}
	containers[container.Id] = container

	return nil
}

func (n *Node) updateContainer(c dockerclient.Container, containers map[string]*Container) (map[string]*Container, error) {
	if current, exists := n.containers[c.Id]; exists {
		// The container exists. Update its state.
		current.Container = c
		containers[current.Id] = current
	} else {
		// This is a brand new container.
		if err := n.inspectContainer(c, containers, false); err != nil {
			return nil, err
		}
	}
	return containers, nil
}

func (n *Node) refreshContainersAsync() {
	n.ch <- true
}

func (n *Node) refreshLoop() {
	for {
		var err error
		select {
		case <-n.ch:
			err = n.refreshContainers()
		case <-time.After(stateRefreshPeriod):
			err = n.refreshContainers()
		}

		if err == nil {
			err = n.refreshImages()
		}

		if err != nil {
			n.healthy = false
			log.Errorf("[%s/%s] Flagging node as dead. Updated state failed: %v", n.ID, n.Name, err)
		} else {
			if !n.healthy {
				log.Infof("[%s/%s] Node came back to life. Hooray!", n.ID, n.Name)
			}
			n.healthy = true
		}
	}
}

// Return the sum of memory reserved by containers.
func (n *Node) ReservedMemory() int64 {
	var r int64 = 0
	n.RLock()
	for _, c := range n.containers {
		r += c.Info.Config.Memory
	}
	n.RUnlock()
	return r
}

// Return the sum of CPUs reserved by containers.
func (n *Node) ReservedCpus() int64 {
	var r int64 = 0
	n.RLock()
	for _, c := range n.containers {
		r += c.Info.Config.CpuShares
	}
	n.RUnlock()
	return r
}

func (n *Node) UsableMemory() int64 {
	return n.Memory + (n.Memory * n.overcommitRatio / 100)
}

func (n *Node) UsableCpus() int64 {
	return n.Cpus + (n.Cpus * n.overcommitRatio / 100)
}

func (n *Node) Create(config *dockerclient.ContainerConfig, name string, pullImage bool) (*Container, error) {
	var (
		err    error
		id     string
		client = n.client
	)

	newConfig := *config

	// nb of CPUs -> real CpuShares
	newConfig.CpuShares = config.CpuShares * 100 / n.Cpus

	if id, err = client.CreateContainer(&newConfig, name); err != nil {
		// If the error is other than not found, abort immediately.
		if err != dockerclient.ErrNotFound || !pullImage {
			return nil, err
		}
		// Otherwise, try to pull the image...
		if err = n.Pull(config.Image); err != nil {
			return nil, err
		}
		// ...And try again.
		if id, err = client.CreateContainer(&newConfig, name); err != nil {
			return nil, err
		}
	}

	// Register the container immediately while waiting for a state refresh.
	// Force a state refresh to pick up the newly created container.
	n.refreshContainer(id)

	n.RLock()
	defer n.RUnlock()

	return n.containers[id], nil
}

// Destroy and remove a container from the node.
func (n *Node) Destroy(container *Container, force bool) error {
	if err := n.client.RemoveContainer(container.Id, force); err != nil {
		return err
	}

	// Remove the container from the state. Eventually, the state refresh loop
	// will rewrite this.
	n.Lock()
	defer n.Unlock()
	delete(n.containers, container.Id)

	return nil
}

func (n *Node) Pull(image string) error {
	if err := n.client.PullImage(image, nil); err != nil {
		return err
	}
	return nil
}

// Register an event handler.
func (n *Node) Events(h EventHandler) error {
	if n.eventHandler != nil {
		return errors.New("event handler already set")
	}
	n.eventHandler = h
	return nil
}

func (n *Node) Containers() []*Container {
	containers := []*Container{}
	n.RLock()
	for _, container := range n.containers {
		containers = append(containers, container)
	}
	n.RUnlock()
	return containers
}

func (n *Node) Images() []*dockerclient.Image {
	images := []*dockerclient.Image{}
	n.RLock()
	for _, image := range n.images {
		images = append(images, image)
	}
	n.RUnlock()
	return images
}

func (n *Node) String() string {
	return fmt.Sprintf("node %s addr %s", n.ID, n.Addr)
}

func (n *Node) handler(ev *dockerclient.Event, args ...interface{}) {
	// Something changed - refresh our internal state.
	if ev.Status == "pull" || ev.Status == "untag" || ev.Status == "delete" {
		n.refreshImages()
	} else {
		n.refreshContainer(ev.Id)
	}

	// If there is no event handler registered, abort right now.
	if n.eventHandler == nil {
		return
	}

	event := &Event{
		Node:  n,
		Event: *ev,
	}

	n.eventHandler.Handle(event)
}

// Inject a container into the internal state.
func (n *Node) AddContainer(container *Container) error {
	n.Lock()
	defer n.Unlock()

	if _, ok := n.containers[container.Id]; ok {
		return errors.New("container already exists")
	}
	n.containers[container.Id] = container
	return nil
}

// Inject an image into the internal state.
func (n *Node) AddImage(image *dockerclient.Image) {
	n.Lock()
	defer n.Unlock()

	n.images = append(n.images, image)
}

// Remove a container from the internal test.
func (n *Node) RemoveContainer(container *Container) error {
	n.Lock()
	defer n.Unlock()

	if _, ok := n.containers[container.Id]; !ok {
		return errors.New("container not found")
	}
	delete(n.containers, container.Id)
	return nil
}

// Wipes the internal container state.
func (n *Node) CleanupContainers() {
	n.Lock()
	n.containers = make(map[string]*Container)
	n.Unlock()
}
<|endoftext|>


<commit_before>package main

import (
    "flag"
    "fmt"
    "os"
    "regexp"
    "strings"

    customCar    "github.com/jtyr/gbt/pkg/cars/custom"
    dirCar       "github.com/jtyr/gbt/pkg/cars/dir"
    exectimeCar  "github.com/jtyr/gbt/pkg/cars/exectime"
    gitCar       "github.com/jtyr/gbt/pkg/cars/git"
    hostnameCar  "github.com/jtyr/gbt/pkg/cars/hostname"
    kubectlCar   "github.com/jtyr/gbt/pkg/cars/kubectl"
    osCar        "github.com/jtyr/gbt/pkg/cars/os"
    pyvirtenvCar "github.com/jtyr/gbt/pkg/cars/pyvirtenv"
    signCar      "github.com/jtyr/gbt/pkg/cars/sign"
    statusCar    "github.com/jtyr/gbt/pkg/cars/status"
    timeCar      "github.com/jtyr/gbt/pkg/cars/time"

    "github.com/jtyr/gbt/pkg/core/car"
    "github.com/jtyr/gbt/pkg/core/utils"
)

// Cars interface for methods from the core.car package.
type Cars interface {
    Init()
    Format() string
    SetParamStr(string, string)
    GetColor(string, bool) string
    DecorateElement(element, bg, fg, fm, text string) string
    GetModel() map[string]car.ModelElement
    GetDisplay() bool
    GetSep() string
    GetWrap() bool
}

const version = "1.2.0"

func printCars(cars []Cars, right bool) {
    prevBg := "\000"
    prevDisplay := true
    fakeCar := car.Car{}
    defaultSeparator := utils.GetEnv("GBT_SEPARATOR", "\ue0b0")

    if right {
        defaultSeparator = utils.GetEnv("GBT_RSEPARATOR", "\ue0b2")
    }

    if ! right && utils.GetEnv("GBT_BEGINNING_TEXT", "") != "" {
        myPrint(
            fakeCar.DecorateElement(
                "",
                fakeCar.GetColor(utils.GetEnv("GBT_BEGINNING_BG", "default"), false),
                fakeCar.GetColor(utils.GetEnv("GBT_BEGINNING_FG", "default"), true),
                fakeCar.GetColor(utils.GetEnv("GBT_BEGINNING_FM", "none"), false),
                utils.GetEnv("GBT_BEGINNING_TEXT", "")))
    }

    for _, c := range cars {
        c.Init()

        cModel := c.GetModel()
        cDisplay := c.GetDisplay()
        cSep := c.GetSep()
        cWrap := c.GetWrap()

        separator := defaultSeparator

        if cSep != "\000" {
            separator = cSep
        }

        if cDisplay {
            myPrint(fakeCar.GetColor("RESETALL", false))

            if prevBg != "\000" && prevDisplay {
                bg := c.GetColor(cModel["root"].Bg, false)
                fg := c.GetColor(cModel["root"].Bg, true)

                if cWrap {
                    bg = c.GetColor("default", false)
                    fg = c.GetColor("default", true)
                }

                if right {
                    myPrint(
                        c.DecorateElement(
                            "",
                            c.GetColor(prevBg, false),
                            fg,
                            "",
                            separator))
                } else {
                    myPrint(
                        c.DecorateElement(
                            "",
                            bg,
                            c.GetColor(prevBg, true),
                            "",
                            separator))
                }

                if cWrap {
                    myPrint("\n")
                }
            }

            prevBg = cModel["root"].Bg
            prevDisplay = cDisplay

            myPrint(c.Format())
        }
    }

    myPrint(fakeCar.GetColor("RESETALL", false))
}

// For the test
var myPrint = func(s string) {
    fmt.Print(s)
}
var printDefaults = flag.PrintDefaults
var exit = os.Exit
var argsHelp, argsVersion, argsRight bool

func main() {
    if len(flag.Args()) == 0 {
        flag.BoolVar(&argsHelp, "help", false, "show this help message and exit")
        flag.BoolVar(&argsVersion, "version", false, "show version and exit")
        flag.BoolVar(&argsRight, "right", false, "compose right hand site prompt")
        flag.Parse()
    }

    if argsHelp {
        fmt.Printf("Usage of %s:\n", os.Args[0])
        printDefaults()
        exit(0)
    }

    if argsVersion {
        myPrint(fmt.Sprintf("GBT v%s\n", version))
        exit(0)
    }

    carsStr := strings.ToLower(utils.GetEnv("GBT_CARS", "status, os, hostname, dir, git, sign"))

    if argsRight {
        carsStr = strings.ToLower(utils.GetEnv("GBT_RCARS", "time"))
    }

    reCarSplit := regexp.MustCompile(`\s*,\s*`)
    carsNames := reCarSplit.Split(carsStr, -1)
    carsFactory := map[string]Cars{
        "custom":    &customCar.Car{},
        "dir":       &dirCar.Car{},
        "exectime":  &exectimeCar.Car{},
        "git":       &gitCar.Car{},
        "hostname":  &hostnameCar.Car{},
        "kubectl":   &kubectlCar.Car{},
        "os":        &osCar.Car{},
        "pyvirtenv": &pyvirtenvCar.Car{},
        "sign":      &signCar.Car{},
        "status":    &statusCar.Car{},
        "time":      &timeCar.Car{},
    }
    cars := []Cars{}

    for _, cn := range carsNames {
        custom := "\000"

        if len(cn) >= 6 && cn[:6] == "custom" {
            custom = cn[6:]
            cn = cn[:6]
        }

        if val, ok := carsFactory[cn]; ok {
            if cn == "status" && len(flag.Args()) > 0 {
                val.SetParamStr("args", flag.Args()[0])
            } else if custom != "\000" {
                val = &customCar.Car{}
                val.SetParamStr("name", custom)
            }

            cars = append(cars, val)
        }
    }

    printCars(cars, argsRight)
}
<commit_msg>Changing version to 2.0.0<commit_after>package main

import (
    "flag"
    "fmt"
    "os"
    "regexp"
    "strings"

    customCar    "github.com/jtyr/gbt/pkg/cars/custom"
    dirCar       "github.com/jtyr/gbt/pkg/cars/dir"
    exectimeCar  "github.com/jtyr/gbt/pkg/cars/exectime"
    gitCar       "github.com/jtyr/gbt/pkg/cars/git"
    hostnameCar  "github.com/jtyr/gbt/pkg/cars/hostname"
    kubectlCar   "github.com/jtyr/gbt/pkg/cars/kubectl"
    osCar        "github.com/jtyr/gbt/pkg/cars/os"
    pyvirtenvCar "github.com/jtyr/gbt/pkg/cars/pyvirtenv"
    signCar      "github.com/jtyr/gbt/pkg/cars/sign"
    statusCar    "github.com/jtyr/gbt/pkg/cars/status"
    timeCar      "github.com/jtyr/gbt/pkg/cars/time"

    "github.com/jtyr/gbt/pkg/core/car"
    "github.com/jtyr/gbt/pkg/core/utils"
)

// Cars interface for methods from the core.car package.
type Cars interface {
    Init()
    Format() string
    SetParamStr(string, string)
    GetColor(string, bool) string
    DecorateElement(element, bg, fg, fm, text string) string
    GetModel() map[string]car.ModelElement
    GetDisplay() bool
    GetSep() string
    GetWrap() bool
}

const version = "2.0.0"

func printCars(cars []Cars, right bool) {
    prevBg := "\000"
    prevDisplay := true
    fakeCar := car.Car{}
    defaultSeparator := utils.GetEnv("GBT_SEPARATOR", "\ue0b0")

    if right {
        defaultSeparator = utils.GetEnv("GBT_RSEPARATOR", "\ue0b2")
    }

    if ! right && utils.GetEnv("GBT_BEGINNING_TEXT", "") != "" {
        myPrint(
            fakeCar.DecorateElement(
                "",
                fakeCar.GetColor(utils.GetEnv("GBT_BEGINNING_BG", "default"), false),
                fakeCar.GetColor(utils.GetEnv("GBT_BEGINNING_FG", "default"), true),
                fakeCar.GetColor(utils.GetEnv("GBT_BEGINNING_FM", "none"), false),
                utils.GetEnv("GBT_BEGINNING_TEXT", "")))
    }

    for _, c := range cars {
        c.Init()

        cModel := c.GetModel()
        cDisplay := c.GetDisplay()
        cSep := c.GetSep()
        cWrap := c.GetWrap()

        separator := defaultSeparator

        if cSep != "\000" {
            separator = cSep
        }

        if cDisplay {
            myPrint(fakeCar.GetColor("RESETALL", false))

            if prevBg != "\000" && prevDisplay {
                bg := c.GetColor(cModel["root"].Bg, false)
                fg := c.GetColor(cModel["root"].Bg, true)

                if cWrap {
                    bg = c.GetColor("default", false)
                    fg = c.GetColor("default", true)
                }

                if right {
                    myPrint(
                        c.DecorateElement(
                            "",
                            c.GetColor(prevBg, false),
                            fg,
                            "",
                            separator))
                } else {
                    myPrint(
                        c.DecorateElement(
                            "",
                            bg,
                            c.GetColor(prevBg, true),
                            "",
                            separator))
                }

                if cWrap {
                    myPrint("\n")
                }
            }

            prevBg = cModel["root"].Bg
            prevDisplay = cDisplay

            myPrint(c.Format())
        }
    }

    myPrint(fakeCar.GetColor("RESETALL", false))
}

// For the test
var myPrint = func(s string) {
    fmt.Print(s)
}
var printDefaults = flag.PrintDefaults
var exit = os.Exit
var argsHelp, argsVersion, argsRight bool

func main() {
    if len(flag.Args()) == 0 {
        flag.BoolVar(&argsHelp, "help", false, "show this help message and exit")
        flag.BoolVar(&argsVersion, "version", false, "show version and exit")
        flag.BoolVar(&argsRight, "right", false, "compose right hand site prompt")
        flag.Parse()
    }

    if argsHelp {
        fmt.Printf("Usage of %s:\n", os.Args[0])
        printDefaults()
        exit(0)
    }

    if argsVersion {
        myPrint(fmt.Sprintf("GBT v%s\n", version))
        exit(0)
    }

    carsStr := strings.ToLower(utils.GetEnv("GBT_CARS", "status, os, hostname, dir, git, sign"))

    if argsRight {
        carsStr = strings.ToLower(utils.GetEnv("GBT_RCARS", "time"))
    }

    reCarSplit := regexp.MustCompile(`\s*,\s*`)
    carsNames := reCarSplit.Split(carsStr, -1)
    carsFactory := map[string]Cars{
        "custom":    &customCar.Car{},
        "dir":       &dirCar.Car{},
        "exectime":  &exectimeCar.Car{},
        "git":       &gitCar.Car{},
        "hostname":  &hostnameCar.Car{},
        "kubectl":   &kubectlCar.Car{},
        "os":        &osCar.Car{},
        "pyvirtenv": &pyvirtenvCar.Car{},
        "sign":      &signCar.Car{},
        "status":    &statusCar.Car{},
        "time":      &timeCar.Car{},
    }
    cars := []Cars{}

    for _, cn := range carsNames {
        custom := "\000"

        if len(cn) >= 6 && cn[:6] == "custom" {
            custom = cn[6:]
            cn = cn[:6]
        }

        if val, ok := carsFactory[cn]; ok {
            if cn == "status" && len(flag.Args()) > 0 {
                val.SetParamStr("args", flag.Args()[0])
            } else if custom != "\000" {
                val = &customCar.Car{}
                val.SetParamStr("name", custom)
            }

            cars = append(cars, val)
        }
    }

    printCars(cars, argsRight)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/disorganizer/brig"
	"github.com/disorganizer/brig/brigd/client"
	"github.com/disorganizer/brig/brigd/server"
	"github.com/disorganizer/brig/util/colors"
	"github.com/dustin/go-humanize"
)

const brigLogo = `
          _____                   _____                   _____                   _____
         /\    \                 /\    \                 /\    \                 /\    \
        /::\    \               /::\    \               /::\    \               /::\    \
       /::::\    \             /::::\    \              \:::\    \             /::::\    \
      /::::::\    \           /::::::\    \              \:::\    \           /::::::\    \
     /:::/\:::\    \         /:::/\:::\    \              \:::\    \         /:::/\:::\    \
    /:::/__\:::\    \       /:::/__\:::\    \              \:::\    \       /:::/  \:::\    \
   /::::\   \:::\    \     /::::\   \:::\    \             /::::\    \     /:::/    \:::\    \
  /::::::\   \:::\    \   /::::::\   \:::\    \   ____    /::::::\    \   /:::/    / \:::\    \
 /:::/\:::\   \:::\ ___\ /:::/\:::\   \:::\____\ /\   \  /:::/\:::\    \ /:::/    /   \:::\ ___\
/:::/__\:::\   \:::|    /:::/  \:::\   \:::|    /::\   \/:::/  \:::\____/:::/____/  ___\:::|    |
\:::\   \:::\  /:::|____\::/   |::::\  /:::|____\:::\  /:::/    \::/    \:::\    \ /\  /:::|____|
 \:::\   \:::\/:::/    / \/____|:::::\/:::/    / \:::\/:::/    / \/____/ \:::\    /::\ \::/    /
  \:::\   \::::::/    /        |:::::::::/    /   \::::::/    /           \:::\   \:::\ \/____/
   \:::\   \::::/    /         |::|\::::/    /     \::::/____/             \:::\   \:::\____\
    \:::\  /:::/    /          |::| \::/____/       \:::\    \              \:::\  /:::/    /
     \:::\/:::/    /           |::|  ~|              \:::\    \              \:::\/:::/    /
      \::::::/    /            |::|   |               \:::\    \              \::::::/    /
       \::::/    /             \::|   |                \:::\____\              \::::/    /
        \::/____/               \:|   |                 \::/    /               \::/____/
         ~~                      \|___|                  \/____/
`

func handleVersion(ctx *cli.Context) error {
	fmt.Println(brig.VersionString())
	return nil
}

func handleOpen(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleClose(ctx *cli.Context, ctl *client.Client) error {
	return handleDaemonQuit(ctx, ctl)
}

func handleDaemonPing(ctx *cli.Context, ctl *client.Client) error {
	for i := 0; i < 100; i++ {
		before := time.Now()
		symbol := colors.Colorize("✔", colors.Green)

		if err := ctl.Ping(); err != nil {
			symbol = colors.Colorize("✘", colors.Red)
		}

		delay := time.Since(before)
		fmt.Printf("#%02d %s ➔ %s: %s (%v)\n",
			i+1,
			ctl.LocalAddr().String(),
			ctl.RemoteAddr().String(),
			symbol,
			delay,
		)

		time.Sleep(1 * time.Second)
	}

	return nil
}

func handleDaemonQuit(ctx *cli.Context, ctl *client.Client) error {
	if err := ctl.Quit(); err != nil {
		return ExitCode{
			DaemonNotResponding,
			fmt.Sprintf("brigd not responding: %v", err),
		}
	}

	return nil
}

func handleDaemonLaunch(ctx *cli.Context) error {
	brigPath := os.Getenv("BRIG_PATH")
	if brigPath == "" {
		// TODO: Check parent directories to see if we're in some
		//       brig repository.
		brigPath = "."
	}

	server, err := server.BootServer(brigPath, guessPort())
	if err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("Failed to boot brigd: %v", err),
		}
	}

	defer server.Close()

	if err := server.Serve(); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("Failed to serve: %v", err),
		}
	}

	return nil
}

func doMount(ctx *cli.Context, ctl *client.Client, mount bool) error {
	return nil
}

func handleMount(ctx *cli.Context, ctl *client.Client) error {
	return doMount(ctx, ctl, !ctx.Bool("unmount"))
}

func handleUnmount(ctx *cli.Context, ctl *client.Client) error {
	return doMount(ctx, ctl, false)
}

func handleInit(ctx *cli.Context, ctl *client.Client) error {
	// Accumulate args:
	owner := ctx.Args().First()
	folder := guessRepoFolder()
	backend := ctx.String("backend")

	if err := ctl.Init(folder, owner, backend); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("init failed: %v", err)}
	}

	fmt.Println(brigLogo)
	return nil
}

func handleConfigList(cli *cli.Context, ctl *client.Client) error {
	all, err := ctl.ConfigAll()
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("config list: %v", err)}
	}

	// Display the output nicely sorted:
	keys := []string{}
	for key := range all {
		keys = append(keys, key)
	}

	sort.Strings(keys)

	for _, key := range keys {
		fmt.Printf(
			"%s: %s\n",
			colors.Colorize(key, colors.Green),
			all[key],
		)
	}
	return nil
}

func handleConfigGet(ctx *cli.Context, ctl *client.Client) error {
	key := ctx.Args().Get(0)
	val, err := ctl.ConfigGet(key)
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("config get: %v", err)}
	}

	fmt.Printf(
		"%s: %s\n",
		colors.Colorize(key, colors.Green),
		val,
	)

	return nil
}

func handleConfigSet(ctx *cli.Context, ctl *client.Client) error {
	key := ctx.Args().Get(0)
	val := ctx.Args().Get(1)
	if err := ctl.ConfigSet(key, val); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("config set: %v", err)}
	}

	return nil
}

func handleStage(ctx *cli.Context, ctl *client.Client) error {
	localPath := ctx.Args().Get(0)

	repoPath := filepath.Base(localPath)
	if len(ctx.Args()) > 1 {
		repoPath = ctx.Args().Get(1)
	}

	if err := ctl.Stage(localPath, repoPath); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("stage: %v", err),
		}
	}
	return nil
}

func handleCat(ctx *cli.Context, ctl *client.Client) error {
	stream, err := ctl.Cat(ctx.Args().First())
	if err != nil {
		// TODO: Make those exit codes a wrapper function.
		return ExitCode{
			UnknownError,
			fmt.Sprintf("cat: %v", err),
		}
	}

	defer stream.Close()

	if _, err := io.Copy(os.Stdout, stream); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("cat: %v", err),
		}
	}

	return nil
}

func handleRm(ctx *cli.Context, ctl *client.Client) error {
	path := ctx.Args().First()

	if err := ctl.Remove(path); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("rm: %v", err),
		}
	}

	return nil
}

func handleMv(ctx *cli.Context, ctl *client.Client) error {
	srcPath := ctx.Args().Get(0)
	dstPath := ctx.Args().Get(0)

	if err := ctl.Move(srcPath, dstPath); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("mv: %v", err),
		}
	}

	return nil
}

func handleHistory(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleOffline(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleIsOnline(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleOnline(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleList(ctx *cli.Context, ctl *client.Client) error {
	maxDepth := ctx.Int("depth")
	if ctx.Bool("recursive") {
		maxDepth = -1
	}

	root := "/"
	if ctx.Args().Present() {
		root = ctx.Args().First()
	}

	entries, err := ctl.List(root, maxDepth)
	if err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("ls: %v", err),
		}
	}

	for _, entry := range entries {
		fmt.Printf(
			"%6s %8s  %s\n",
			humanize.Bytes(entry.Size),
			entry.ModTime.Format(time.Stamp),
			entry.Path,
		)
	}

	return nil
}

func handleTree(ctx *cli.Context, ctl *client.Client) error {
	entries, err := ctl.List("/", -1)
	if err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("ls: %v", err),
		}
	}

	return showTree(entries, -1)
}

func handleMkdir(ctx *cli.Context, ctl *client.Client) error {
	path := ctx.Args().First()
	createParents := ctx.Bool("parents")

	if err := ctl.Mkdir(path, createParents); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("mkdir: %v", err)}
	}

	return nil
}

func handleStatus(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleCommit(ctx *cli.Context, ctl *client.Client) error {
	msg := ""
	if ctx.Args().Present() {
		msg = ctx.Args().First()
	} else {
		msg = fmt.Sprintf("Manual commit")
	}

	if err := ctl.MakeCommit(msg); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("commit: %v", err)}
	}

	return nil
}

func handleTag(ctx *cli.Context, ctl *client.Client) error {
	if ctx.Bool("delete") {
		name := ctx.Args().Get(0)

		if err := ctl.Untag(name); err != nil {
			return ExitCode{
				UnknownError,
				fmt.Sprintf("untag: %v", err),
			}
		}
	} else {
		if len(ctx.Args()) < 2 {
			return ExitCode{BadArgs, "tag needs at least two arguments"}
		}

		rev := ctx.Args().Get(0)
		name := ctx.Args().Get(1)

		if err := ctl.Tag(rev, name); err != nil {
			return ExitCode{
				UnknownError,
				fmt.Sprintf("tag: %v", err),
			}
		}
	}

	return nil
}

func handleLog(ctx *cli.Context, ctl *client.Client) error {
	entries, err := ctl.Log()
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("commit: %v", err)}
	}

	for idx, entry := range entries {
		tags := ""
		if len(entry.Tags) > 0 {
			tags = fmt.Sprintf(" (%s)", strings.Join(entry.Tags, ", "))
		}

		msg := entry.Msg
		if msg == "" {
			msg = colors.Colorize("*", colors.Red)
		}

		entry.Hash.ShortB58()

		fmt.Printf(
			"%2d: %s %s %s%s\n",
			idx,
			colors.Colorize(entry.Hash.ShortB58(), colors.Green),
			colors.Colorize(entry.Date.Format(time.Stamp), colors.Yellow),
			msg,
			colors.Colorize(tags, colors.Cyan),
		)
	}

	return nil
}

func handleDiff(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handlePin(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleUnpin(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleSync(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleReset(ctx *cli.Context, ctl *client.Client) error {
	return nil
}
<commit_msg>Make config get only display the actual value<commit_after>package cmd

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/disorganizer/brig"
	"github.com/disorganizer/brig/brigd/client"
	"github.com/disorganizer/brig/brigd/server"
	"github.com/disorganizer/brig/util/colors"
	"github.com/dustin/go-humanize"
)

const brigLogo = `
          _____                   _____                   _____                   _____
         /\    \                 /\    \                 /\    \                 /\    \
        /::\    \               /::\    \               /::\    \               /::\    \
       /::::\    \             /::::\    \              \:::\    \             /::::\    \
      /::::::\    \           /::::::\    \              \:::\    \           /::::::\    \
     /:::/\:::\    \         /:::/\:::\    \              \:::\    \         /:::/\:::\    \
    /:::/__\:::\    \       /:::/__\:::\    \              \:::\    \       /:::/  \:::\    \
   /::::\   \:::\    \     /::::\   \:::\    \             /::::\    \     /:::/    \:::\    \
  /::::::\   \:::\    \   /::::::\   \:::\    \   ____    /::::::\    \   /:::/    / \:::\    \
 /:::/\:::\   \:::\ ___\ /:::/\:::\   \:::\____\ /\   \  /:::/\:::\    \ /:::/    /   \:::\ ___\
/:::/__\:::\   \:::|    /:::/  \:::\   \:::|    /::\   \/:::/  \:::\____/:::/____/  ___\:::|    |
\:::\   \:::\  /:::|____\::/   |::::\  /:::|____\:::\  /:::/    \::/    \:::\    \ /\  /:::|____|
 \:::\   \:::\/:::/    / \/____|:::::\/:::/    / \:::\/:::/    / \/____/ \:::\    /::\ \::/    /
  \:::\   \::::::/    /        |:::::::::/    /   \::::::/    /           \:::\   \:::\ \/____/
   \:::\   \::::/    /         |::|\::::/    /     \::::/____/             \:::\   \:::\____\
    \:::\  /:::/    /          |::| \::/____/       \:::\    \              \:::\  /:::/    /
     \:::\/:::/    /           |::|  ~|              \:::\    \              \:::\/:::/    /
      \::::::/    /            |::|   |               \:::\    \              \::::::/    /
       \::::/    /             \::|   |                \:::\____\              \::::/    /
        \::/____/               \:|   |                 \::/    /               \::/____/
         ~~                      \|___|                  \/____/
`

func handleVersion(ctx *cli.Context) error {
	fmt.Println(brig.VersionString())
	return nil
}

func handleOpen(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleClose(ctx *cli.Context, ctl *client.Client) error {
	return handleDaemonQuit(ctx, ctl)
}

func handleDaemonPing(ctx *cli.Context, ctl *client.Client) error {
	for i := 0; i < 100; i++ {
		before := time.Now()
		symbol := colors.Colorize("✔", colors.Green)

		if err := ctl.Ping(); err != nil {
			symbol = colors.Colorize("✘", colors.Red)
		}

		delay := time.Since(before)
		fmt.Printf("#%02d %s ➔ %s: %s (%v)\n",
			i+1,
			ctl.LocalAddr().String(),
			ctl.RemoteAddr().String(),
			symbol,
			delay,
		)

		time.Sleep(1 * time.Second)
	}

	return nil
}

func handleDaemonQuit(ctx *cli.Context, ctl *client.Client) error {
	if err := ctl.Quit(); err != nil {
		return ExitCode{
			DaemonNotResponding,
			fmt.Sprintf("brigd not responding: %v", err),
		}
	}

	return nil
}

func handleDaemonLaunch(ctx *cli.Context) error {
	brigPath := os.Getenv("BRIG_PATH")
	if brigPath == "" {
		// TODO: Check parent directories to see if we're in some
		//       brig repository.
		brigPath = "."
	}

	server, err := server.BootServer(brigPath, guessPort())
	if err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("Failed to boot brigd: %v", err),
		}
	}

	defer server.Close()

	if err := server.Serve(); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("Failed to serve: %v", err),
		}
	}

	return nil
}

func doMount(ctx *cli.Context, ctl *client.Client, mount bool) error {
	return nil
}

func handleMount(ctx *cli.Context, ctl *client.Client) error {
	return doMount(ctx, ctl, !ctx.Bool("unmount"))
}

func handleUnmount(ctx *cli.Context, ctl *client.Client) error {
	return doMount(ctx, ctl, false)
}

func handleInit(ctx *cli.Context, ctl *client.Client) error {
	// Accumulate args:
	owner := ctx.Args().First()
	folder := guessRepoFolder()
	backend := ctx.String("backend")

	if err := ctl.Init(folder, owner, backend); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("init failed: %v", err)}
	}

	fmt.Println(brigLogo)
	return nil
}

func handleConfigList(cli *cli.Context, ctl *client.Client) error {
	all, err := ctl.ConfigAll()
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("config list: %v", err)}
	}

	// Display the output nicely sorted:
	keys := []string{}
	for key := range all {
		keys = append(keys, key)
	}

	sort.Strings(keys)

	for _, key := range keys {
		fmt.Printf(
			"%s: %s\n",
			colors.Colorize(key, colors.Green),
			all[key],
		)
	}
	return nil
}

func handleConfigGet(ctx *cli.Context, ctl *client.Client) error {
	key := ctx.Args().Get(0)
	val, err := ctl.ConfigGet(key)
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("config get: %v", err)}
	}

	fmt.Println(val)
	return nil
}

func handleConfigSet(ctx *cli.Context, ctl *client.Client) error {
	key := ctx.Args().Get(0)
	val := ctx.Args().Get(1)
	if err := ctl.ConfigSet(key, val); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("config set: %v", err)}
	}

	return nil
}

func handleStage(ctx *cli.Context, ctl *client.Client) error {
	localPath := ctx.Args().Get(0)

	repoPath := filepath.Base(localPath)
	if len(ctx.Args()) > 1 {
		repoPath = ctx.Args().Get(1)
	}

	if err := ctl.Stage(localPath, repoPath); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("stage: %v", err),
		}
	}
	return nil
}

func handleCat(ctx *cli.Context, ctl *client.Client) error {
	stream, err := ctl.Cat(ctx.Args().First())
	if err != nil {
		// TODO: Make those exit codes a wrapper function.
		return ExitCode{
			UnknownError,
			fmt.Sprintf("cat: %v", err),
		}
	}

	defer stream.Close()

	if _, err := io.Copy(os.Stdout, stream); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("cat: %v", err),
		}
	}

	return nil
}

func handleRm(ctx *cli.Context, ctl *client.Client) error {
	path := ctx.Args().First()

	if err := ctl.Remove(path); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("rm: %v", err),
		}
	}

	return nil
}

func handleMv(ctx *cli.Context, ctl *client.Client) error {
	srcPath := ctx.Args().Get(0)
	dstPath := ctx.Args().Get(0)

	if err := ctl.Move(srcPath, dstPath); err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("mv: %v", err),
		}
	}

	return nil
}

func handleHistory(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleOffline(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleIsOnline(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleOnline(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleList(ctx *cli.Context, ctl *client.Client) error {
	maxDepth := ctx.Int("depth")
	if ctx.Bool("recursive") {
		maxDepth = -1
	}

	root := "/"
	if ctx.Args().Present() {
		root = ctx.Args().First()
	}

	entries, err := ctl.List(root, maxDepth)
	if err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("ls: %v", err),
		}
	}

	for _, entry := range entries {
		fmt.Printf(
			"%6s %8s  %s\n",
			humanize.Bytes(entry.Size),
			entry.ModTime.Format(time.Stamp),
			entry.Path,
		)
	}

	return nil
}

func handleTree(ctx *cli.Context, ctl *client.Client) error {
	entries, err := ctl.List("/", -1)
	if err != nil {
		return ExitCode{
			UnknownError,
			fmt.Sprintf("ls: %v", err),
		}
	}

	return showTree(entries, -1)
}

func handleMkdir(ctx *cli.Context, ctl *client.Client) error {
	path := ctx.Args().First()
	createParents := ctx.Bool("parents")

	if err := ctl.Mkdir(path, createParents); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("mkdir: %v", err)}
	}

	return nil
}

func handleStatus(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleCommit(ctx *cli.Context, ctl *client.Client) error {
	msg := ""
	if ctx.Args().Present() {
		msg = ctx.Args().First()
	} else {
		msg = fmt.Sprintf("Manual commit")
	}

	if err := ctl.MakeCommit(msg); err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("commit: %v", err)}
	}

	return nil
}

func handleTag(ctx *cli.Context, ctl *client.Client) error {
	if ctx.Bool("delete") {
		name := ctx.Args().Get(0)

		if err := ctl.Untag(name); err != nil {
			return ExitCode{
				UnknownError,
				fmt.Sprintf("untag: %v", err),
			}
		}
	} else {
		if len(ctx.Args()) < 2 {
			return ExitCode{BadArgs, "tag needs at least two arguments"}
		}

		rev := ctx.Args().Get(0)
		name := ctx.Args().Get(1)

		if err := ctl.Tag(rev, name); err != nil {
			return ExitCode{
				UnknownError,
				fmt.Sprintf("tag: %v", err),
			}
		}
	}

	return nil
}

func handleLog(ctx *cli.Context, ctl *client.Client) error {
	entries, err := ctl.Log()
	if err != nil {
		return ExitCode{UnknownError, fmt.Sprintf("commit: %v", err)}
	}

	for idx, entry := range entries {
		tags := ""
		if len(entry.Tags) > 0 {
			tags = fmt.Sprintf(" (%s)", strings.Join(entry.Tags, ", "))
		}

		msg := entry.Msg
		if msg == "" {
			msg = colors.Colorize("*", colors.Red)
		}

		entry.Hash.ShortB58()

		fmt.Printf(
			"%2d: %s %s %s%s\n",
			idx,
			colors.Colorize(entry.Hash.ShortB58(), colors.Green),
			colors.Colorize(entry.Date.Format(time.Stamp), colors.Yellow),
			msg,
			colors.Colorize(tags, colors.Cyan),
		)
	}

	return nil
}

func handleDiff(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handlePin(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleUnpin(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleSync(ctx *cli.Context, ctl *client.Client) error {
	return nil
}

func handleReset(ctx *cli.Context, ctl *client.Client) error {
	return nil
}
<|endoftext|>


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

package main

import (
	"bytes"
	"expvar"
	"flag"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"path"
	"runtime"
	"runtime/pprof"
	"sort"
	"strings"
	"time"

	log "github.com/couchbase/clog"

	"github.com/couchbaselabs/cbgt"
	"github.com/couchbaselabs/cbgt/cmd"
	"github.com/couchbaselabs/cbgt/rebalance"
)

var VERSION = "v0.0.0"

var expvars = expvar.NewMap("stats")

func main() {
	flag.Parse()

	if flags.Help {
		flag.Usage()
		os.Exit(2)
	}

	if flags.Version {
		fmt.Printf("%s main: %s, data: %s\n",
			path.Base(os.Args[0]), VERSION, cbgt.VERSION)
		os.Exit(0)
	}

	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	log.Printf("main: %s started (%s/%s)",
		os.Args[0], VERSION, cbgt.VERSION)

	rand.Seed(time.Now().UTC().UnixNano())

	go dumpOnSignalForPlatform()

	MainWelcome(flagAliases)

	nodesToRemove := []string(nil)
	if len(flags.RemoveNodes) > 0 {
		nodesToRemove = strings.Split(flags.RemoveNodes, ",")
	}

	bindHttp := "NO-BIND-HTTP"
	register := "unchanged"
	dataDir := "NO-DATA-DIR"

	// If cfg is down, we error, leaving it to some user-supplied
	// outside watchdog to backoff and restart/retry.
	cfg, err := cmd.MainCfg("mcp", flags.CfgConnect,
		bindHttp, register, dataDir)
	if err != nil {
		if err == cmd.ErrorBindHttp {
			log.Fatalf("%v", err)
			return
		}
		log.Fatalf("main: could not start cfg, cfgConnect: %s, err: %v\n"+
			"  Please check that your -cfg/-cfgConnect parameter (%q)\n"+
			"  is correct and/or that your configuration provider\n"+
			"  is available.",
			flags.CfgConnect, err, flags.CfgConnect)
		return
	}

	r, err := rebalance.StartRebalance(cbgt.VERSION, cfg, flags.Server,
		nodesToRemove,
		rebalance.RebalanceOptions{
			DryRun:  flags.DryRun,
			Verbose: flags.Verbose,
		})
	if err != nil {
		log.Fatalf("main: StartRebalance, err: %v", err)
		return
	}

	reportProgress(r)

	r.Stop()

	log.Printf("main: done")
}

// ------------------------------------------------------------

type ProgressEntry struct {
	stateOp     rebalance.StateOp
	currUUIDSeq cbgt.UUIDSeq
	wantUUIDSeq cbgt.UUIDSeq
}

func reportProgress(r *rebalance.Rebalancer) {
	maxNodeLen := 0
	maxPIndexLen := 0

	seenNodes := map[string]bool{}
	seenNodesSorted := []string(nil)

	// Map of pindex -> (source) partition -> node -> *ProgressEntry
	progressEntries := map[string]map[string]map[string]*ProgressEntry{}

	inflightPIndexes := map[string]bool{}
	inflightPIndexesSorted := []string(nil)

	writeNodeEntry := func(b *bytes.Buffer, s string) {
		b.Write([]byte(s))

		for i := 3; i < maxNodeLen; i++ {
			b.WriteByte(' ')
		}
	}

	updateProgressEntry := func(pindex, sourcePartition, node string,
		cb func(*ProgressEntry)) {
		if !seenNodes[node] {
			seenNodes[node] = true
			seenNodesSorted = append(seenNodesSorted, node)
			sort.Strings(seenNodesSorted)

			if maxNodeLen < len(node) {
				maxNodeLen = len(node)
			}
		}

		if maxPIndexLen < len(pindex) {
			maxPIndexLen = len(pindex)
		}

		sourcePartitions, exists := progressEntries[pindex]
		if !exists || sourcePartitions == nil {
			sourcePartitions = map[string]map[string]*ProgressEntry{}
			progressEntries[pindex] = sourcePartitions
		}

		nodes, exists := sourcePartitions[sourcePartition]
		if !exists || nodes == nil {
			nodes = map[string]*ProgressEntry{}
			sourcePartitions[sourcePartition] = nodes
		}

		progressEntry, exists := nodes[node]
		if !exists || progressEntry == nil {
			progressEntry = &ProgressEntry{}
			nodes[node] = progressEntry
		}

		cb(progressEntry)

		// TODO: Check UUID matches, too.

		if progressEntry.wantUUIDSeq.Seq >
			progressEntry.currUUIDSeq.Seq {
			if !inflightPIndexes[pindex] {
				inflightPIndexes[pindex] = true
				inflightPIndexesSorted =
					append(inflightPIndexesSorted, pindex)

				sort.Strings(inflightPIndexesSorted)
			}
		} else if false { // TODO: Shrink inflightPIndexes.
			if progressEntry.wantUUIDSeq.Seq > 0 &&
				progressEntry.currUUIDSeq.Seq > 0 &&
				inflightPIndexes[pindex] {
				delete(inflightPIndexes, pindex)

				inflightPIndexesSorted =
					make([]string, 0, len(inflightPIndexesSorted))
				for inflightPIndex := range inflightPIndexes {
					inflightPIndexesSorted =
						append(inflightPIndexesSorted, inflightPIndex)
				}

				sort.Strings(inflightPIndexesSorted)
			}
		}
	}

	for progress := range r.ProgressCh() {
		if progress.Index == "" {
			r.Log("main: progress: %+v", progress)
			continue
		}

		if progress.Error != nil {
			r.Log("main: error, progress: %+v", progress)
			continue
		}

		r.Visit(func(
			currStates rebalance.CurrStates,
			currSeqs rebalance.CurrSeqs,
			wantSeqs rebalance.WantSeqs) {
			for _, pindexes := range currStates {
				for pindex, nodes := range pindexes {
					for node, stateOp := range nodes {
						updateProgressEntry(pindex, "", node,
							func(pe *ProgressEntry) {
								pe.stateOp = stateOp
							})
					}
				}
			}

			for pindex, sourcePartitions := range currSeqs {
				for sourcePartition, nodes := range sourcePartitions {
					for node, currUUIDSeq := range nodes {
						updateProgressEntry(pindex,
							sourcePartition, node,
							func(pe *ProgressEntry) {
								pe.currUUIDSeq = currUUIDSeq
							})
					}
				}
			}

			for pindex, sourcePartitions := range wantSeqs {
				for sourcePartition, nodes := range sourcePartitions {
					for node, wantUUIDSeq := range nodes {
						updateProgressEntry(pindex,
							sourcePartition, node,
							func(pe *ProgressEntry) {
								pe.wantUUIDSeq = wantUUIDSeq
							})
					}
				}
			}

			// ----------------------------------------

			var b bytes.Buffer

			for i := 0; i < maxPIndexLen; i++ {
				b.WriteByte(' ')
			}
			b.WriteByte(' ')

			for i, seenNode := range seenNodesSorted {
				if i > 0 {
					b.WriteByte(' ')
				}
				b.Write([]byte(seenNode))
			}
			b.WriteByte('\n')

			for _, inflightPIndex := range inflightPIndexesSorted {
				b.Write([]byte("                    "))
				b.Write([]byte(inflightPIndex))

				for _, seenNode := range seenNodesSorted {
					b.WriteByte(' ')

					sourcePartitions, exists :=
						progressEntries[inflightPIndex]
					if !exists || sourcePartitions == nil {
						writeNodeEntry(&b, "...")
						continue
					}

					nodes, exists := sourcePartitions[""]
					if !exists || nodes == nil {
						writeNodeEntry(&b, "...")
						continue
					}

					pe, exists := nodes[seenNode]
					if !exists || pe == nil {
						writeNodeEntry(&b, "...")
						continue
					}

					if pe.stateOp.Op == "" {
						writeNodeEntry(&b, "___")
					} else {
						writeNodeEntry(&b, pe.stateOp.Op)
					}
				}

				b.WriteByte('\n')
			}

			r.Log("%s", b.String())
		})
	}
}

// ------------------------------------------------------------

func MainWelcome(flagAliases map[string][]string) {
	flag.VisitAll(func(f *flag.Flag) {
		if flagAliases[f.Name] != nil {
			log.Printf("  -%s=%q\n", f.Name, f.Value)
		}
	})
	log.Printf("  GOMAXPROCS=%d", runtime.GOMAXPROCS(-1))
}

func dumpOnSignal(signals ...os.Signal) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, signals...)
	for _ = range c {
		log.Printf("dump: goroutine...")
		pprof.Lookup("goroutine").WriteTo(os.Stderr, 1)
		log.Printf("dump: heap...")
		pprof.Lookup("heap").WriteTo(os.Stderr, 1)
	}
}
<commit_msg>attempt to print percent complete in rebalanceProgress<commit_after>//  Copyright (c) 2015 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package main

import (
	"bytes"
	"expvar"
	"flag"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"path"
	"runtime"
	"runtime/pprof"
	"sort"
	"strings"
	"time"

	log "github.com/couchbase/clog"

	"github.com/couchbaselabs/cbgt"
	"github.com/couchbaselabs/cbgt/cmd"
	"github.com/couchbaselabs/cbgt/rebalance"
)

var VERSION = "v0.0.0"

var expvars = expvar.NewMap("stats")

func main() {
	flag.Parse()

	if flags.Help {
		flag.Usage()
		os.Exit(2)
	}

	if flags.Version {
		fmt.Printf("%s main: %s, data: %s\n",
			path.Base(os.Args[0]), VERSION, cbgt.VERSION)
		os.Exit(0)
	}

	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	log.Printf("main: %s started (%s/%s)",
		os.Args[0], VERSION, cbgt.VERSION)

	rand.Seed(time.Now().UTC().UnixNano())

	go dumpOnSignalForPlatform()

	MainWelcome(flagAliases)

	nodesToRemove := []string(nil)
	if len(flags.RemoveNodes) > 0 {
		nodesToRemove = strings.Split(flags.RemoveNodes, ",")
	}

	bindHttp := "NO-BIND-HTTP"
	register := "unchanged"
	dataDir := "NO-DATA-DIR"

	// If cfg is down, we error, leaving it to some user-supplied
	// outside watchdog to backoff and restart/retry.
	cfg, err := cmd.MainCfg("mcp", flags.CfgConnect,
		bindHttp, register, dataDir)
	if err != nil {
		if err == cmd.ErrorBindHttp {
			log.Fatalf("%v", err)
			return
		}
		log.Fatalf("main: could not start cfg, cfgConnect: %s, err: %v\n"+
			"  Please check that your -cfg/-cfgConnect parameter (%q)\n"+
			"  is correct and/or that your configuration provider\n"+
			"  is available.",
			flags.CfgConnect, err, flags.CfgConnect)
		return
	}

	r, err := rebalance.StartRebalance(cbgt.VERSION, cfg, flags.Server,
		nodesToRemove,
		rebalance.RebalanceOptions{
			DryRun:  flags.DryRun,
			Verbose: flags.Verbose,
		})
	if err != nil {
		log.Fatalf("main: StartRebalance, err: %v", err)
		return
	}

	reportProgress(r)

	r.Stop()

	log.Printf("main: done")
}

// ------------------------------------------------------------

type ProgressEntry struct {
	stateOp     rebalance.StateOp
	initUUIDSeq cbgt.UUIDSeq
	currUUIDSeq cbgt.UUIDSeq
	wantUUIDSeq cbgt.UUIDSeq
}

func reportProgress(r *rebalance.Rebalancer) {
	maxNodeLen := 0
	maxPIndexLen := 0

	seenNodes := map[string]bool{}
	seenNodesSorted := []string(nil)

	// Map of pindex -> (source) partition -> node -> *ProgressEntry
	progressEntries := map[string]map[string]map[string]*ProgressEntry{}

	inflightPIndexes := map[string]bool{}
	inflightPIndexesSorted := []string(nil)

	opMap := map[string]string{
		"":    ".",
		"add": "+",
		"del": "-",
	}

	writeNodeEntry := func(b *bytes.Buffer, s string,
		pe *ProgressEntry) {
		b.Write([]byte(s))

		start := 1
		if pe != nil {
			d := pe.wantUUIDSeq.Seq - pe.initUUIDSeq.Seq
			if d != 0 {
				pct :=
					float32(pe.currUUIDSeq.Seq-pe.initUUIDSeq.Seq) /
						float32(d)

				n, _ := fmt.Fprintf(b, " %.2f", pct)
				start = start + n
			}
		}

		for i := start; i < maxNodeLen; i++ {
			b.WriteByte(' ')
		}
	}

	updateProgressEntry := func(pindex, sourcePartition, node string,
		cb func(*ProgressEntry)) {
		if !seenNodes[node] {
			seenNodes[node] = true
			seenNodesSorted = append(seenNodesSorted, node)
			sort.Strings(seenNodesSorted)

			if maxNodeLen < len(node) {
				maxNodeLen = len(node)
			}
		}

		if maxPIndexLen < len(pindex) {
			maxPIndexLen = len(pindex)
		}

		sourcePartitions, exists := progressEntries[pindex]
		if !exists || sourcePartitions == nil {
			sourcePartitions = map[string]map[string]*ProgressEntry{}
			progressEntries[pindex] = sourcePartitions
		}

		nodes, exists := sourcePartitions[sourcePartition]
		if !exists || nodes == nil {
			nodes = map[string]*ProgressEntry{}
			sourcePartitions[sourcePartition] = nodes
		}

		progressEntry, exists := nodes[node]
		if !exists || progressEntry == nil {
			progressEntry = &ProgressEntry{}
			nodes[node] = progressEntry
		}

		cb(progressEntry)

		// TODO: Check UUID matches, too.

		if progressEntry.wantUUIDSeq.Seq >
			progressEntry.currUUIDSeq.Seq {
			if !inflightPIndexes[pindex] {
				inflightPIndexes[pindex] = true
				inflightPIndexesSorted =
					append(inflightPIndexesSorted, pindex)

				sort.Strings(inflightPIndexesSorted)
			}
		} else if false { // TODO: Shrink inflightPIndexes.
			if progressEntry.wantUUIDSeq.Seq > 0 &&
				progressEntry.currUUIDSeq.Seq > 0 &&
				inflightPIndexes[pindex] {
				delete(inflightPIndexes, pindex)

				inflightPIndexesSorted =
					make([]string, 0, len(inflightPIndexesSorted))
				for inflightPIndex := range inflightPIndexes {
					inflightPIndexesSorted =
						append(inflightPIndexesSorted, inflightPIndex)
				}

				sort.Strings(inflightPIndexesSorted)
			}
		}
	}

	for progress := range r.ProgressCh() {
		if progress.Index == "" {
			r.Log("main: progress: %+v", progress)
			continue
		}

		if progress.Error != nil {
			r.Log("main: error, progress: %+v", progress)
			continue
		}

		r.Visit(func(
			currStates rebalance.CurrStates,
			currSeqs rebalance.CurrSeqs,
			wantSeqs rebalance.WantSeqs) {
			for _, pindexes := range currStates {
				for pindex, nodes := range pindexes {
					for node, stateOp := range nodes {
						updateProgressEntry(pindex, "", node,
							func(pe *ProgressEntry) {
								pe.stateOp = stateOp
							})
					}
				}
			}

			for pindex, sourcePartitions := range currSeqs {
				for sourcePartition, nodes := range sourcePartitions {
					for node, currUUIDSeq := range nodes {
						updateProgressEntry(pindex,
							sourcePartition, node,
							func(pe *ProgressEntry) {
								pe.currUUIDSeq = currUUIDSeq

								if pe.initUUIDSeq.UUID == "" {
									pe.initUUIDSeq = currUUIDSeq
								}
							})
					}
				}
			}

			for pindex, sourcePartitions := range wantSeqs {
				for sourcePartition, nodes := range sourcePartitions {
					for node, wantUUIDSeq := range nodes {
						updateProgressEntry(pindex,
							sourcePartition, node,
							func(pe *ProgressEntry) {
								pe.wantUUIDSeq = wantUUIDSeq
							})
					}
				}
			}

			// ----------------------------------------

			var b bytes.Buffer

			for i := 0; i < maxPIndexLen; i++ {
				b.WriteByte(' ')
			}
			b.WriteByte(' ')

			for i, seenNode := range seenNodesSorted {
				if i > 0 {
					b.WriteByte(' ')
				}
				b.Write([]byte(seenNode))
			}
			b.WriteByte('\n')

			for _, inflightPIndex := range inflightPIndexesSorted {
				b.Write([]byte("                    "))
				b.Write([]byte(inflightPIndex))

				for _, seenNode := range seenNodesSorted {
					b.WriteByte(' ')

					sourcePartitions, exists :=
						progressEntries[inflightPIndex]
					if !exists || sourcePartitions == nil {
						writeNodeEntry(&b, ".", nil)
						continue
					}

					nodes, exists := sourcePartitions[""]
					if !exists || nodes == nil {
						writeNodeEntry(&b, ".", nil)
						continue
					}

					pe, exists := nodes[seenNode]
					if !exists || pe == nil {
						writeNodeEntry(&b, ".", nil)
						continue
					}

					writeNodeEntry(&b, opMap[pe.stateOp.Op], pe)
				}

				b.WriteByte('\n')
			}

			r.Log("%s", b.String())
		})
	}
}

// ------------------------------------------------------------

func MainWelcome(flagAliases map[string][]string) {
	flag.VisitAll(func(f *flag.Flag) {
		if flagAliases[f.Name] != nil {
			log.Printf("  -%s=%q\n", f.Name, f.Value)
		}
	})
	log.Printf("  GOMAXPROCS=%d", runtime.GOMAXPROCS(-1))
}

func dumpOnSignal(signals ...os.Signal) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, signals...)
	for _ = range c {
		log.Printf("dump: goroutine...")
		pprof.Lookup("goroutine").WriteTo(os.Stderr, 1)
		log.Printf("dump: heap...")
		pprof.Lookup("heap").WriteTo(os.Stderr, 1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	configv1 "github.com/openshift/api/config/v1"
	configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
	clusteroperatorv1helpers "github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers"
	operatorv1helpers "github.com/openshift/library-go/pkg/operator/v1helpers"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
	olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	defaultWakeupInterval = 5 * time.Minute
	defaultOperatorName   = "operator-lifecycle-manager"
)

// config flags defined globally so that they appear on the test binary as well
var (
	kubeConfigPath = flag.String(
		"kubeconfig", "", "absolute path to the kubeconfig file")

	wakeupInterval = flag.Duration(
		"interval", defaultWakeupInterval, "wake up interval")

	watchedNamespaces = flag.String(
		"watchedNamespaces", "", "comma separated list of namespaces for olm operator to watch. "+
			"If not set, or set to the empty string (e.g. `-watchedNamespaces=\"\"`), "+
			"olm operator will watch all namespaces in the cluster.")

	writeStatusName = flag.String(
		"writeStatusName", defaultOperatorName, "ClusterOperator name in which to write status, set to \"\" to disable.")

	debug = flag.Bool(
		"debug", false, "use debug log level")

	version = flag.Bool("version", false, "displays olm version")
)

func init() {
	metrics.RegisterOLM()
}

// main function - entrypoint to OLM operator
func main() {
	stopCh := signals.SetupSignalHandler()

	// Parse the command-line flags.
	flag.Parse()

	// Check if version flag was set
	if *version {
		fmt.Print(olmversion.String())

		// Exit early
		os.Exit(0)
	}

	// `namespaces` will always contain at least one entry: if `*watchedNamespaces` is
	// the empty string, the resulting array will be `[]string{""}`.
	namespaces := strings.Split(*watchedNamespaces, ",")
	for _, ns := range namespaces {
		if ns == v1.NamespaceAll {
			namespaces = []string{v1.NamespaceAll}
			break
		}
	}

	// Create a client for OLM
	crClient, err := client.NewClient(*kubeConfigPath)
	if err != nil {
		log.Fatalf("error configuring client: %s", err.Error())
	}

	logger := log.New()

	// Set log level to debug if `debug` flag set
	if *debug {
		logger.SetLevel(log.DebugLevel)
	}
	logger.Infof("log level %s", logger.Level)

	opClient := operatorclient.NewClientFromConfig(*kubeConfigPath, logger)

	// create a config client for operator status
	config, err := clientcmd.BuildConfigFromFlags("", *kubeConfigPath)
	if err != nil {
		log.Fatalf("error configuring client: %s", err.Error())
	}
	configClient, err := configv1client.NewForConfig(config)
	if err != nil {
		log.Fatalf("error configuring client: %s", err.Error())
	}

	// Create a new instance of the operator.
	operator, err := olm.NewOperator(logger, crClient, opClient, &install.StrategyResolver{}, *wakeupInterval, namespaces)

	if err != nil {
		log.Fatalf("error configuring operator: %s", err.Error())
	}

	// Serve a health check.
	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	})
	go http.ListenAndServe(":8080", nil)

	http.Handle("/metrics", promhttp.Handler())
	go http.ListenAndServe(":8081", nil)

	ready, done := operator.Run(stopCh)
	<-ready

	if *writeStatusName != "" {
		existing, err := configClient.ClusterOperators().Get(*writeStatusName, metav1.GetOptions{})
		if meta.IsNoMatchError(err) {
			log.Infof("ClusterOperator api not present, skipping update")
		} else if k8serrors.IsNotFound(err) {
			log.Info("Existing cluster operator not found, creating")
			created, err := configClient.ClusterOperators().Create(&configv1.ClusterOperator{
				ObjectMeta: metav1.ObjectMeta{
					Name: *writeStatusName,
				},
			})
			if err != nil {
				log.Fatalf("ClusterOperator create failed: %v\n", err)
			}

			created.Status = configv1.ClusterOperatorStatus{
				Conditions: []configv1.ClusterOperatorStatusCondition{
					configv1.ClusterOperatorStatusCondition{
						Type:               configv1.OperatorProgressing,
						Status:             configv1.ConditionFalse,
						Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
						LastTransitionTime: metav1.Now(),
					},
					configv1.ClusterOperatorStatusCondition{
						Type:               configv1.OperatorFailing,
						Status:             configv1.ConditionFalse,
						Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
						LastTransitionTime: metav1.Now(),
					},
					configv1.ClusterOperatorStatusCondition{
						Type:               configv1.OperatorAvailable,
						Status:             configv1.ConditionTrue,
						Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
						LastTransitionTime: metav1.Now(),
					},
				},
				Versions: []configv1.OperandVersion{{
					Name:    "operator",
					Version: olmversion.Full(),
				}},
			}
			_, err = configClient.ClusterOperators().UpdateStatus(created)
			if err != nil {
				log.Fatalf("ClusterOperator update status failed: %v", err)
			}
		} else if err != nil {
			log.Fatalf("ClusterOperators get failed: %v", err)
		} else {
			clusteroperatorv1helpers.SetStatusCondition(&existing.Status.Conditions, configv1.ClusterOperatorStatusCondition{
				Type:               configv1.OperatorProgressing,
				Status:             configv1.ConditionFalse,
				Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
				LastTransitionTime: metav1.Now(),
			})
			clusteroperatorv1helpers.SetStatusCondition(&existing.Status.Conditions, configv1.ClusterOperatorStatusCondition{
				Type:               configv1.OperatorFailing,
				Status:             configv1.ConditionFalse,
				Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
				LastTransitionTime: metav1.Now(),
			})
			clusteroperatorv1helpers.SetStatusCondition(&existing.Status.Conditions, configv1.ClusterOperatorStatusCondition{
				Type:               configv1.OperatorAvailable,
				Status:             configv1.ConditionTrue,
				Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
				LastTransitionTime: metav1.Now(),
			})

			olmOperandVersion := configv1.OperandVersion{Name: "operator", Version: olmversion.Full()}
			// look for operator version, even though in OLM's case should only be one
			for _, item := range existing.Status.Versions {
				if item.Name == "operator" && item != olmOperandVersion {
					// if a cluster wide upgrade has occurred, hopefully any existing operator statuses have been deleted
					log.Infof("Updating version from %v to %v\n", item.Version, olmversion.Full())
				}
			}
			operatorv1helpers.SetOperandVersion(&existing.Status.Versions, olmOperandVersion)
			_, err = configClient.ClusterOperators().UpdateStatus(existing)
			if err != nil {
				log.Fatalf("ClusterOperator update status failed: %v", err)
			}
		}
	}

	<-done
}
<commit_msg>fix(olm): properly detect cluster operator API<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	configv1 "github.com/openshift/api/config/v1"
	configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
	clusteroperatorv1helpers "github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers"
	operatorv1helpers "github.com/openshift/library-go/pkg/operator/v1helpers"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/signals"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
	olmversion "github.com/operator-framework/operator-lifecycle-manager/pkg/version"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	defaultWakeupInterval = 5 * time.Minute
	defaultOperatorName   = ""
)

// config flags defined globally so that they appear on the test binary as well
var (
	kubeConfigPath = flag.String(
		"kubeconfig", "", "absolute path to the kubeconfig file")

	wakeupInterval = flag.Duration(
		"interval", defaultWakeupInterval, "wake up interval")

	watchedNamespaces = flag.String(
		"watchedNamespaces", "", "comma separated list of namespaces for olm operator to watch. "+
			"If not set, or set to the empty string (e.g. `-watchedNamespaces=\"\"`), "+
			"olm operator will watch all namespaces in the cluster.")

	writeStatusName = flag.String(
		"writeStatusName", defaultOperatorName, "ClusterOperator name in which to write status, set to \"\" to disable.")

	debug = flag.Bool(
		"debug", false, "use debug log level")

	version = flag.Bool("version", false, "displays olm version")
)

func init() {
	metrics.RegisterOLM()
}

// main function - entrypoint to OLM operator
func main() {
	stopCh := signals.SetupSignalHandler()

	// Parse the command-line flags.
	flag.Parse()

	// Check if version flag was set
	if *version {
		fmt.Print(olmversion.String())

		// Exit early
		os.Exit(0)
	}

	// `namespaces` will always contain at least one entry: if `*watchedNamespaces` is
	// the empty string, the resulting array will be `[]string{""}`.
	namespaces := strings.Split(*watchedNamespaces, ",")
	for _, ns := range namespaces {
		if ns == v1.NamespaceAll {
			namespaces = []string{v1.NamespaceAll}
			break
		}
	}

	// Create a client for OLM
	crClient, err := client.NewClient(*kubeConfigPath)
	if err != nil {
		log.Fatalf("error configuring client: %s", err.Error())
	}

	logger := log.New()

	// Set log level to debug if `debug` flag set
	if *debug {
		logger.SetLevel(log.DebugLevel)
	}
	logger.Infof("log level %s", logger.Level)

	opClient := operatorclient.NewClientFromConfig(*kubeConfigPath, logger)

	// create a config client for operator status
	config, err := clientcmd.BuildConfigFromFlags("", *kubeConfigPath)
	if err != nil {
		log.Fatalf("error configuring client: %s", err.Error())
	}
	configClient, err := configv1client.NewForConfig(config)
	if err != nil {
		log.Fatalf("error configuring client: %s", err.Error())
	}

	// Create a new instance of the operator.
	operator, err := olm.NewOperator(logger, crClient, opClient, &install.StrategyResolver{}, *wakeupInterval, namespaces)

	if err != nil {
		log.Fatalf("error configuring operator: %s", err.Error())
	}

	// Serve a health check.
	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	})
	go http.ListenAndServe(":8080", nil)

	http.Handle("/metrics", promhttp.Handler())
	go http.ListenAndServe(":8081", nil)

	ready, done := operator.Run(stopCh)
	<-ready

	if *writeStatusName != "" {
		opStatusGV := schema.GroupVersion{
			Group:   "config.openshift.io",
			Version: "v1",
		}
		err := discovery.ServerSupportsVersion(opClient.KubernetesInterface().Discovery(), opStatusGV)
		if err != nil {
			log.Infof("ClusterOperator api not present, skipping update (%v)", err)
		} else {
			existing, err := configClient.ClusterOperators().Get(*writeStatusName, metav1.GetOptions{})
			if k8serrors.IsNotFound(err) {
				log.Info("Existing operator status not found, creating")
				created, err := configClient.ClusterOperators().Create(&configv1.ClusterOperator{
					ObjectMeta: metav1.ObjectMeta{
						Name: *writeStatusName,
					},
				})
				if err != nil {
					log.Fatalf("ClusterOperator create failed: %v\n", err)
				}

				created.Status = configv1.ClusterOperatorStatus{
					Conditions: []configv1.ClusterOperatorStatusCondition{
						configv1.ClusterOperatorStatusCondition{
							Type:               configv1.OperatorProgressing,
							Status:             configv1.ConditionFalse,
							Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
							LastTransitionTime: metav1.Now(),
						},
						configv1.ClusterOperatorStatusCondition{
							Type:               configv1.OperatorFailing,
							Status:             configv1.ConditionFalse,
							Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
							LastTransitionTime: metav1.Now(),
						},
						configv1.ClusterOperatorStatusCondition{
							Type:               configv1.OperatorAvailable,
							Status:             configv1.ConditionTrue,
							Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
							LastTransitionTime: metav1.Now(),
						},
					},
					Versions: []configv1.OperandVersion{{
						Name:    "operator",
						Version: olmversion.Full(),
					}},
				}
				_, err = configClient.ClusterOperators().UpdateStatus(created)
				if err != nil {
					log.Fatalf("ClusterOperator update status failed: %v", err)
				}
			} else if err != nil {
				log.Fatalf("ClusterOperators get failed: %v", err)
			} else {
				clusteroperatorv1helpers.SetStatusCondition(&existing.Status.Conditions, configv1.ClusterOperatorStatusCondition{
					Type:               configv1.OperatorProgressing,
					Status:             configv1.ConditionFalse,
					Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
					LastTransitionTime: metav1.Now(),
				})
				clusteroperatorv1helpers.SetStatusCondition(&existing.Status.Conditions, configv1.ClusterOperatorStatusCondition{
					Type:               configv1.OperatorFailing,
					Status:             configv1.ConditionFalse,
					Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
					LastTransitionTime: metav1.Now(),
				})
				clusteroperatorv1helpers.SetStatusCondition(&existing.Status.Conditions, configv1.ClusterOperatorStatusCondition{
					Type:               configv1.OperatorAvailable,
					Status:             configv1.ConditionTrue,
					Message:            fmt.Sprintf("Done deploying %s.", olmversion.OLMVersion),
					LastTransitionTime: metav1.Now(),
				})

				olmOperandVersion := configv1.OperandVersion{Name: "operator", Version: olmversion.Full()}
				// look for operator version, even though in OLM's case should only be one
				for _, item := range existing.Status.Versions {
					if item.Name == "operator" && item != olmOperandVersion {
						// if a cluster wide upgrade has occurred, hopefully any existing operator statuses have been deleted
						log.Infof("Updating version from %v to %v\n", item.Version, olmversion.Full())
					}
				}
				operatorv1helpers.SetOperandVersion(&existing.Status.Versions, olmOperandVersion)
				_, err = configClient.ClusterOperators().UpdateStatus(existing)
				if err != nil {
					log.Fatalf("ClusterOperator update status failed: %v", err)
				}
			}
		}
	}

	<-done
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/voxelbrain/goptions"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
)

const (
	VERSION = "0.3.1"
)
var (
	options = struct {
		Fatal bool	 `goptions:"-f, --fatal, description='Do not continue on error'"`
		Input string `goptions:"-i, --input, description='Read input from file instead of stdin'"`
		Format string `goptions:"-f, --format, obligatory, description='Name for the formatter'"`
		goptions.Help
	}{
		Format: "csv",
		Fatal: false,
		Input: "",
	}
)

func main() {
	err := goptions.Parse(&options)
	if err != nil {
		goptions.PrintHelp()
		fmt.Println("Formatters:")
		for name, format := range Formats {
			fmt.Printf("\t\"%s\": %s\n", name, format.Description)
		}
		fmt.Printf("Version %s\n", VERSION)
		return
	}

	format, ok := Formats[options.Format]
	if !ok {
		log.Fatalf("Unknown format %s", options.Format)
	}

	f, err := format.Compiler(formatString())
	if err != nil {
		log.Fatalf("Template invalid: %s", err)
	}

	dec := json.NewDecoder(os.Stdin)
	logFn := NewLogFn(options.Fatal)
	for {
		var input interface{}
		err := dec.Decode(&input)
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalf("Could not decode input: %s", err)
			continue
		}
		err = f.Execute(os.Stdout, input)
		if err != nil {
			logFn("Could not apply input to template: %s", err)
			continue
		}
		io.WriteString(os.Stdout, "\n")
	}
}

type LogFn func(format string, v ...interface{})

func NewLogFn(fatal bool) LogFn {
	if fatal {
		return func(format string, v ...interface{}) {
			log.Fatalf(format, v...)
		}
	}
	return func(format string, v ...interface{}) {
		log.Printf(format, v...)
	}
}

func formatString() string {
	if len(options.Input) > 0 {
		d, e := ioutil.ReadFile(options.Input)
		if e != nil {
			log.Fatalf("Could not read file \"%s\": %s", options.Input, e)
		}
		return string(d)
	}
	// return flag.Arg(0)
	return ""
}
<commit_msg>Add new flags for less ambiguity<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/voxelbrain/goptions"
	"io"
	"io/ioutil"
	"log"
	"os"
)

const (
	VERSION = "0.3.1"
)

var (
	options = struct {
		Continue      bool   `goptions:"-c, --continue, description='Continue on error'"`
		FormatFile    string `goptions:"-r, --format-file, description='Read format from file', mutexgroup='input'"`
		FormatString  string `goptions:"-s, --format-string, description='Format string', mutexgroup='input'"`
		Format        string `goptions:"-f, --format, obligatory, description='Name for the formatter'"`
		goptions.Help `goptions:"-h, --help, description='Show this help'`
	}{
		Format: "csv",
	}
)

func main() {
	err := goptions.Parse(&options)
	if err != nil || (len(options.FormatFile) <= 0 && len(options.FormatString) <= 0) {
		if err == nil {
			err = fmt.Errorf("One of --format-file and --format-string must be specified")
		}
		fmt.Printf("Error: %s\n", err)
		goptions.PrintHelp()
		fmt.Println("Formatters:")
		for name, format := range Formats {
			fmt.Printf("\t\"%s\": %s\n", name, format.Description)
		}
		fmt.Printf("Version %s\n", VERSION)
		return
	}

	format, ok := Formats[options.Format]
	if !ok {
		log.Fatalf("Unknown format %s", options.Format)
	}

	f, err := format.Compiler(formatString())
	if err != nil {
		log.Fatalf("Template invalid: %s", err)
	}

	dec := json.NewDecoder(os.Stdin)
	logFn := NewLogFn(options.Continue)
	for {
		var input interface{}
		err := dec.Decode(&input)
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalf("Could not decode input: %s", err)
			continue
		}
		err = f.Execute(os.Stdout, input)
		if err != nil {
			logFn("Could not apply input to template: %s", err)
			continue
		}
		io.WriteString(os.Stdout, "\n")
	}
}

type LogFn func(format string, v ...interface{})

func NewLogFn(c bool) LogFn {
	if !c {
		return func(format string, v ...interface{}) {
			log.Fatalf(format, v...)
		}
	}
	return func(format string, v ...interface{}) {
		log.Printf(format, v...)
	}
}

func formatString() string {
	if len(options.FormatFile) > 0 {
		d, e := ioutil.ReadFile(options.FormatFile)
		if e != nil {
			log.Fatalf("Could not read file \"%s\": %s", options.FormatFile, e)
		}
		return string(d)
	}
	if len(options.FormatString) > 0 {
		return options.FormatString
	}
	panic("Invalid execution path")
}
<|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"
	"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.Debug("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)
	nodes, 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: nodes[len(nodes)-1]}, 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["fs"] = "ipfs"
	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["res_offset"] = o
	if err != nil {
		return err
	}
	n, err := io.ReadFull(r, resp.Data[:req.Size])
	resp.Data = resp.Data[:n]
	lm["res_size"] = n
	return err // may be non-nil / not succeeded
}
<commit_msg>fix fuse reading on linux<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.Debug("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)
	nodes, 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: nodes[len(nodes)-1]}, 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["fs"] = "ipfs"
	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["res_offset"] = o
	if err != nil {
		return err
	}
	n, err := r.Read(resp.Data[:req.Size])
	if err != nil && err != io.EOF {
		return err
	}
	resp.Data = resp.Data[:n]
	lm["res_size"] = n
	return nil // may be non-nil / not succeeded
}
<|endoftext|>