<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) *Node {
	e := &Node{
		Addr:       addr,
		Labels:     make(map[string]string),
		ch:         make(chan bool),
		containers: make(map[string]*Container),
		healthy:    true,
	}
	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
	client       dockerclient.Client
	eventHandler EventHandler
	healthy      bool
}

// 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 {
	c, err := dockerclient.NewDockerClientTimeout(n.Addr, config, time.Duration(requestTimeout))
	if err != nil {
		return err
	}

	addr, err := net.ResolveIPAddr("ip4", strings.Split(c.URL.Host, ":")[0])
	if err != nil {
		return err
	}
	n.IP = addr.IP.String()

	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
	}

	// 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 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.Container = c
	container.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 {
			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) 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
}

func (n *Node) ListImages() ([]string, error) {
	images, err := n.client.ListImages()
	if err != nil {
		return nil, err
	}

	out := []string{}

	for _, i := range images {
		for _, t := range i.RepoTags {
			out = append(out, t)
		}
	}

	return out, 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) 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.
	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
}

// 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>More canonical Container creation in inspectContainer<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) *Node {
	e := &Node{
		Addr:       addr,
		Labels:     make(map[string]string),
		ch:         make(chan bool),
		containers: make(map[string]*Container),
		healthy:    true,
	}
	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
	client       dockerclient.Client
	eventHandler EventHandler
	healthy      bool
}

// 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 {
	c, err := dockerclient.NewDockerClientTimeout(n.Addr, config, time.Duration(requestTimeout))
	if err != nil {
		return err
	}

	addr, err := net.ResolveIPAddr("ip4", strings.Split(c.URL.Host, ":")[0])
	if err != nil {
		return err
	}
	n.IP = addr.IP.String()

	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
	}

	// 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 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 {
			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) 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
}

func (n *Node) ListImages() ([]string, error) {
	images, err := n.client.ListImages()
	if err != nil {
		return nil, err
	}

	out := []string{}

	for _, i := range images {
		for _, t := range i.RepoTags {
			out = append(out, t)
		}
	}

	return out, 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) 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.
	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
}

// 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 (
	"fmt"
	"log"
	"net"
	"os"
	"strings"

	"github.com/n0stack/n0core/datastore/etcd"
	"github.com/n0stack/n0core/provisioning/node"
	pprovisioning "github.com/n0stack/proto.go/provisioning/v0"

	"github.com/urfave/cli"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
)

func main() {
	app := cli.NewApp()
	app.Name = "n0core API"
	// app.Usage = ""
	app.Version = "0.1.0" // CIで取るようにする

	// command action
	app.Commands = []cli.Command{
		{
			Name:  "serve",
			Usage: "Join to API and serve some daemons.",
			Action: func(c *cli.Context) error {
				var err error
				e, err := etcd.NewEtcdDatastore("node", strings.Split(c.String("etcd-endpoints"), ","))
				if err != nil {
					return err
				}

				lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", c.String("bind-address"), c.Int("bind-port")))
				if err != nil {
					return err
				}

				s := grpc.NewServer()

				// starterをsliceでとったほうがいいかもしれない
				n, err := node.CreateNodeAPI(e, c.String("memberlist-starter"))
				if err != nil {
					return err
					fmt.Printf("%v", err.Error())

				}

				pprovisioning.RegisterNodeServiceServer(s, n)
				reflection.Register(s)

				log.Printf("[INFO] Starting API")
				if err := s.Serve(lis); err != nil {
					return err
				}

				return nil
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name: "etcd-endpoints",
				},
				cli.StringFlag{
					// interfaceからも取れるようにしたい
					Name:  "bind-address",
					Value: "0.0.0.0",
				},
				cli.IntFlag{
					Name:  "bind-port",
					Value: 20180,
				},
			},
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatalf("Failed to start process, err:%v", err.Error())
	}
}
<commit_msg>close etcd connection<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"strings"

	"github.com/n0stack/n0core/datastore/etcd"
	"github.com/n0stack/n0core/provisioning/node"
	pprovisioning "github.com/n0stack/proto.go/provisioning/v0"

	"github.com/urfave/cli"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
)

func main() {
	app := cli.NewApp()
	app.Name = "n0core API"
	// app.Usage = ""
	app.Version = "0.1.0" // CIで取るようにする

	app.Commands = []cli.Command{
		{
			Name:  "serve",
			Usage: "Join to API and serve some daemons.",
			Action: func(c *cli.Context) error {
				var err error
				e, err := etcd.NewEtcdDatastore("node", strings.Split(c.String("etcd-endpoints"), ","))
				if err != nil {
					return err
				}
				defer e.Close()

				lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", c.String("bind-address"), c.Int("bind-port")))
				if err != nil {
					return err
				}

				s := grpc.NewServer()

				// starterをsliceでとったほうがいいかもしれない
				n, err := node.CreateNodeAPI(e, c.String("memberlist-starter"))
				if err != nil {
					return err
				}

				pprovisioning.RegisterNodeServiceServer(s, n)
				reflection.Register(s)

				log.Printf("[INFO] Starting API")
				return s.Serve(lis)
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name: "etcd-endpoints",
				},
				cli.StringFlag{
					// interfaceからも取れるようにしたい
					Name:  "bind-address",
					Value: "0.0.0.0",
				},
				cli.IntFlag{
					Name:  "bind-port",
					Value: 20180,
				},
			},
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatalf("Failed to start process, err:%v", err.Error())
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/bernardolins/vandame/cluster"
	"github.com/bernardolins/vandame/coreos"
	"github.com/bernardolins/vandame/file"
	"github.com/bernardolins/vandame/metadata"
	"github.com/spf13/cobra"
	"os"
	"text/template"
)

// This function is called to insert
func init() {
	generateCommand := NewGenerateCommand()
	VandameCommand.AddCommand(generateCommand.command)
}

// GenerateCommand object structure.
type GenerateCommand struct {
	command   *cobra.Command
	input     string
	templates string
	output    string
}

// The generate command constructor. Returns a GenerateCommand object containing flag values and a cobra.Command object.
func NewGenerateCommand() *GenerateCommand {
	generate := new(GenerateCommand)

	generate.command = &cobra.Command{
		Use: "generate",
		Run: func(cmd *cobra.Command, args []string) {
			generate.run()
		},
	}

	generate.command.Flags().StringVarP(&generate.input, "input", "f", "./", "Path to input file")
	generate.command.Flags().StringVarP(&generate.templates, "templates", "t", "./", "Path to templates directory")
	generate.command.Flags().StringVarP(&generate.output, "output", "o", "./", "Path to output cloud-config")

	return generate
}

// Runs the generate command
func (generate *GenerateCommand) run() {
	inputFile := file.Load(generate.input)
	config := metadata.Build(inputFile)

	for _, node := range config.GetClusterNodes() {
		coreos := coreos.Config(node.GetNodeName(), config)
		member := cluster.MemberConfig(node, coreos)

		dirname := node.GetNodeName()
		filename := node.GetNodeName() + "-cloud-config.yaml"

		outputFile := file.CreateFileAndDir(dirname, filename)
		generate.executeTemplates(outputFile, member)
	}
}

// Helper file to load templates. May be extracted to another module.
func (generate *GenerateCommand) executeTemplates(output *os.File, config *cluster.Member) {
	templateFiles := file.Ls(generate.templates)

	for _, f := range templateFiles {
		t := template.New(f)
		t, _ = t.ParseFiles(generate.templates + f)
		t.Execute(output, config)
	}
}
<commit_msg>Closing output file after using it<commit_after>package cmd

import (
	"github.com/bernardolins/vandame/cluster"
	"github.com/bernardolins/vandame/coreos"
	"github.com/bernardolins/vandame/file"
	"github.com/bernardolins/vandame/metadata"
	"github.com/spf13/cobra"
	"os"
	"text/template"
)

// This function is called to insert
func init() {
	generateCommand := NewGenerateCommand()
	VandameCommand.AddCommand(generateCommand.command)
}

// GenerateCommand object structure.
type GenerateCommand struct {
	command   *cobra.Command
	input     string
	templates string
	output    string
}

// The generate command constructor. Returns a GenerateCommand object containing flag values and a cobra.Command object.
func NewGenerateCommand() *GenerateCommand {
	generate := new(GenerateCommand)

	generate.command = &cobra.Command{
		Use: "generate",
		Run: func(cmd *cobra.Command, args []string) {
			generate.run()
		},
	}

	generate.command.Flags().StringVarP(&generate.input, "input", "f", "./", "Path to input file")
	generate.command.Flags().StringVarP(&generate.templates, "templates", "t", "./", "Path to templates directory")
	generate.command.Flags().StringVarP(&generate.output, "output", "o", "./", "Path to output cloud-config")

	return generate
}

// Runs the generate command
func (generate *GenerateCommand) run() {
	inputFile := file.Load(generate.input)
	config := metadata.Build(inputFile)

	for _, node := range config.GetClusterNodes() {
		coreos := coreos.Config(node.GetNodeName(), config)
		member := cluster.MemberConfig(node, *coreos)

		dirname := node.GetNodeName()
		filename := node.GetNodeName() + "-cloud-config.yaml"

		outputFile := file.CreateFileAndDir(dirname, filename)
		defer outputFile.Close()
		generate.executeTemplates(outputFile, member)
	}
}

// Helper file to load templates. May be extracted to another module.
func (generate *GenerateCommand) executeTemplates(output *os.File, config *cluster.Member) {
	templateFiles := file.Ls(generate.templates)

	for _, f := range templateFiles {
		t := template.New(f)
		t, _ = t.ParseFiles(generate.templates + f)
		t.Execute(output, config)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/ecc1/medtronic"
)

func showInternal(v interface{}) {
	fmt.Printf("%+v\n", v)
}

func showJSON(v interface{}) {
	b, err := json.MarshalIndent(v, "", "  ")
	if err != nil {
		fmt.Println(err)
		fmt.Println(v)
		return
	}
	fmt.Println(string(b))
}

func showOpenAPS(v interface{}) {
	showJSON(OpenAPSJSON(v))
}

// OpenAPSJSON converts v into a value that the json package
// will marshal in a format compatible with openaps.
func OpenAPSJSON(v interface{}) interface{} {
	switch r := v.(type) {
	case medtronic.BasalRateSchedule:
		return convertBasalRateSchedule(r)
	case medtronic.BatteryInfo:
		return convertBatteryInfo(r)
	case medtronic.CarbRatioSchedule:
		return convertCarbRatioSchedule(r)
	case medtronic.CarbUnitsType:
		return carbU(r)
	case medtronic.GlucoseTargetSchedule:
		return convertGlucoseTargetSchedule(r)
	case medtronic.GlucoseUnitsType:
		return glucoseU(r)
	case medtronic.InsulinSensitivitySchedule:
		return convertInsulinSensitivitySchedule(r)
	case medtronic.SettingsInfo:
		return convertSettingsInfo(r)
	case medtronic.StatusInfo:
		return convertStatusInfo(r)
	case medtronic.TempBasalInfo:
		return convertTempBasalInfo(r)
	default:
		return v
	}
}

func hours(d time.Duration) int {
	return int((d + 30*time.Minute) / time.Hour)
}

func minutes(d time.Duration) int {
	return int((d + 30*time.Second) / time.Minute)
}

func carbU(u medtronic.CarbUnitsType) string {
	switch u {
	case medtronic.Grams:
		return "grams"
	case medtronic.Exchanges:
		return "exchanges"
	default:
		log.Panicf("unknown carb unit %d", u)
	}
	panic("unreachable")
}

func glucoseU(u medtronic.GlucoseUnitsType) string {
	switch u {
	case medtronic.MgPerDeciLiter:
		return "mg/dL"
	case medtronic.MMolPerLiter:
		return "mmol/L"
	default:
		log.Panicf("unknown glucose unit %d", u)
	}
	panic("unreachable")
}

// AltBasalRate is an openaps-compatible form of BasalRate.
type AltBasalRate struct {
	Index   int               `json:"i"`
	Start   string            `json:"start"`
	Minutes int               `json:"minutes"`
	Rate    medtronic.Insulin `json:"rate"`
}

func convertBasalRateSchedule(r medtronic.BasalRateSchedule) interface{} {
	sched := make([]AltBasalRate, len(r))
	for i, s := range r {
		sched[i] = AltBasalRate{
			Index:   i,
			Start:   s.Start.String() + ":00",
			Minutes: minutes(time.Duration(s.Start)),
			Rate:    s.Rate,
		}
	}
	return sched
}

// AltCarbRatio is an openaps-compatible form of CarbRatio.
type AltCarbRatio struct {
	X      int     `json:"x"`
	Index  int     `json:"i"`
	Start  string  `json:"start"`
	Offset int     `json:"offset"`
	Ratio  float64 `json:"ratio"`
}

func convertCarbRatioSchedule(r medtronic.CarbRatioSchedule) interface{} {
	sched := make([]AltCarbRatio, len(r))
	u := r[0].Units
	for i, s := range r {
		if s.Units != u {
			log.Fatalf("unexpected carb units type %v", s.Units)
		}
		m := minutes(time.Duration(s.Start))
		ratio := float64(s.Ratio) / 10
		if u == medtronic.Exchanges {
			ratio /= 100
		}
		sched[i] = AltCarbRatio{
			X:      i,
			Index:  m / 30,
			Start:  s.Start.String() + ":00",
			Offset: m,
			Ratio:  ratio,
		}
	}
	return struct {
		First    int            `json:"first"`
		Units    string         `json:"units"`
		Schedule []AltCarbRatio `json:"schedule"`
	}{
		First:    int(u),
		Units:    carbU(u),
		Schedule: sched,
	}
}

// AltGlucoseTarget is an openaps-compatible form of GlucoseTarget.
type AltGlucoseTarget struct {
	X      int     `json:"x"`
	Index  int     `json:"i"`
	Start  string  `json:"start"`
	Offset int     `json:"offset"`
	Low    float64 `json:"low"`
	High   float64 `json:"high"`
}

func convertGlucoseTargetSchedule(r medtronic.GlucoseTargetSchedule) interface{} {
	sched := make([]AltGlucoseTarget, len(r))
	u := r[0].Units
	for i, s := range r {
		if s.Units != u {
			log.Fatalf("unexpected glucose units type %v", s.Units)
		}
		m := minutes(time.Duration(s.Start))
		low := float64(s.Low)
		high := float64(s.High)
		if u == medtronic.MMolPerLiter {
			low /= 1000
			high /= 1000
		}
		sched[i] = AltGlucoseTarget{
			X:      i,
			Index:  m / 30,
			Start:  s.Start.String() + ":00",
			Offset: m,
			Low:    low,
			High:   high,
		}
	}
	return struct {
		First    int                `json:"first"`
		Units    string             `json:"units"`
		Schedule []AltGlucoseTarget `json:"targets"`
	}{
		First:    int(u),
		Units:    glucoseU(u),
		Schedule: sched,
	}
}

// AltInsulinSensitivity is an openaps-compatible form of InsulinSensitivity.
type AltInsulinSensitivity struct {
	X           int     `json:"x"`
	Index       int     `json:"i"`
	Start       string  `json:"start"`
	Offset      int     `json:"offset"`
	Sensitivity float64 `json:"sensitivity"`
}

func convertInsulinSensitivitySchedule(r medtronic.InsulinSensitivitySchedule) interface{} {
	sched := make([]AltInsulinSensitivity, len(r))
	u := r[0].Units
	for i, s := range r {
		if s.Units != u {
			log.Fatalf("unexpected carb units type %v", s.Units)
		}
		m := minutes(time.Duration(s.Start))
		sens := float64(s.Sensitivity)
		if u == medtronic.MMolPerLiter {
			sens /= 1000
		}
		sched[i] = AltInsulinSensitivity{
			X:           i,
			Index:       m / 30,
			Start:       s.Start.String() + ":00",
			Offset:      m,
			Sensitivity: sens,
		}
	}
	return struct {
		First    int                     `json:"first"`
		Units    string                  `json:"units"`
		Schedule []AltInsulinSensitivity `json:"sensitivities"`
	}{
		First:    int(u),
		Units:    glucoseU(u),
		Schedule: sched,
	}
}

func convertBatteryInfo(r medtronic.BatteryInfo) interface{} {
	status := "normal"
	if r.LowBattery {
		status = "low"
	}
	return struct {
		Voltage medtronic.Voltage `json:"voltage"`
		Status  string            `json:"string"`
	}{
		Voltage: r.Voltage,
		Status:  status,
	}
}

func convertSettingsInfo(r medtronic.SettingsInfo) interface{} {
	return struct {
		AutoOffHours         int               `json:"auto_off_duration_hrs"`
		InsulinActionHours   int               `json:"insulin_action_curve"`
		InsulinConcentration int               `json:"insulinConcentration"`
		MaxBolus             medtronic.Insulin `json:"maxBolus"`
		MaxBasal             medtronic.Insulin `json:"maxBasal"`
		RFEnabled            bool              `json:"rf_enable"`
		SelectedPattern      int               `json:"selected_pattern"`
	}{
		AutoOffHours:         hours(r.AutoOff),
		InsulinActionHours:   hours(r.InsulinAction),
		InsulinConcentration: r.InsulinConcentration,
		MaxBolus:             r.MaxBolus,
		MaxBasal:             r.MaxBasal,
		RFEnabled:            r.RFEnabled,
		SelectedPattern:      r.SelectedPattern,
	}
}

func convertStatusInfo(r medtronic.StatusInfo) interface{} {
	status := "normal"
	if r.Code != 0x03 {
		status = "error"
	}
	return struct {
		Status    string `json:"status"`
		Bolusing  bool   `json:"bolusing"`
		Suspended bool   `json:"suspended"`
	}{
		Status:    status,
		Bolusing:  r.Bolusing,
		Suspended: r.Suspended,
	}
}

func convertTempBasalInfo(r medtronic.TempBasalInfo) interface{} {
	return struct {
		Duration int               `json:"duration"`
		Rate     medtronic.Insulin `json:"rate"`
		Temp     string            `json:"temp"`
	}{
		Duration: minutes(r.Duration),
		Rate:     *r.Rate,
		Temp:     strings.ToLower(r.Type.String()),
	}
}
<commit_msg>Handle both absolute and percent temp basal info<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/ecc1/medtronic"
)

func showInternal(v interface{}) {
	fmt.Printf("%+v\n", v)
}

func showJSON(v interface{}) {
	b, err := json.MarshalIndent(v, "", "  ")
	if err != nil {
		fmt.Println(err)
		fmt.Println(v)
		return
	}
	fmt.Println(string(b))
}

func showOpenAPS(v interface{}) {
	showJSON(OpenAPSJSON(v))
}

// OpenAPSJSON converts v into a value that the json package
// will marshal in a format compatible with openaps.
func OpenAPSJSON(v interface{}) interface{} {
	switch r := v.(type) {
	case medtronic.BasalRateSchedule:
		return convertBasalRateSchedule(r)
	case medtronic.BatteryInfo:
		return convertBatteryInfo(r)
	case medtronic.CarbRatioSchedule:
		return convertCarbRatioSchedule(r)
	case medtronic.CarbUnitsType:
		return carbU(r)
	case medtronic.GlucoseTargetSchedule:
		return convertGlucoseTargetSchedule(r)
	case medtronic.GlucoseUnitsType:
		return glucoseU(r)
	case medtronic.InsulinSensitivitySchedule:
		return convertInsulinSensitivitySchedule(r)
	case medtronic.SettingsInfo:
		return convertSettingsInfo(r)
	case medtronic.StatusInfo:
		return convertStatusInfo(r)
	case medtronic.TempBasalInfo:
		return convertTempBasalInfo(r)
	default:
		return v
	}
}

func hours(d time.Duration) int {
	return int((d + 30*time.Minute) / time.Hour)
}

func minutes(d time.Duration) int {
	return int((d + 30*time.Second) / time.Minute)
}

func carbU(u medtronic.CarbUnitsType) string {
	switch u {
	case medtronic.Grams:
		return "grams"
	case medtronic.Exchanges:
		return "exchanges"
	default:
		log.Panicf("unknown carb unit %d", u)
	}
	panic("unreachable")
}

func glucoseU(u medtronic.GlucoseUnitsType) string {
	switch u {
	case medtronic.MgPerDeciLiter:
		return "mg/dL"
	case medtronic.MMolPerLiter:
		return "mmol/L"
	default:
		log.Panicf("unknown glucose unit %d", u)
	}
	panic("unreachable")
}

// AltBasalRate is an openaps-compatible form of BasalRate.
type AltBasalRate struct {
	Index   int               `json:"i"`
	Start   string            `json:"start"`
	Minutes int               `json:"minutes"`
	Rate    medtronic.Insulin `json:"rate"`
}

func convertBasalRateSchedule(r medtronic.BasalRateSchedule) interface{} {
	sched := make([]AltBasalRate, len(r))
	for i, s := range r {
		sched[i] = AltBasalRate{
			Index:   i,
			Start:   s.Start.String() + ":00",
			Minutes: minutes(time.Duration(s.Start)),
			Rate:    s.Rate,
		}
	}
	return sched
}

// AltCarbRatio is an openaps-compatible form of CarbRatio.
type AltCarbRatio struct {
	X      int     `json:"x"`
	Index  int     `json:"i"`
	Start  string  `json:"start"`
	Offset int     `json:"offset"`
	Ratio  float64 `json:"ratio"`
}

func convertCarbRatioSchedule(r medtronic.CarbRatioSchedule) interface{} {
	sched := make([]AltCarbRatio, len(r))
	u := r[0].Units
	for i, s := range r {
		if s.Units != u {
			log.Fatalf("unexpected carb units type %v", s.Units)
		}
		m := minutes(time.Duration(s.Start))
		ratio := float64(s.Ratio) / 10
		if u == medtronic.Exchanges {
			ratio /= 100
		}
		sched[i] = AltCarbRatio{
			X:      i,
			Index:  m / 30,
			Start:  s.Start.String() + ":00",
			Offset: m,
			Ratio:  ratio,
		}
	}
	return struct {
		First    int            `json:"first"`
		Units    string         `json:"units"`
		Schedule []AltCarbRatio `json:"schedule"`
	}{
		First:    int(u),
		Units:    carbU(u),
		Schedule: sched,
	}
}

// AltGlucoseTarget is an openaps-compatible form of GlucoseTarget.
type AltGlucoseTarget struct {
	X      int     `json:"x"`
	Index  int     `json:"i"`
	Start  string  `json:"start"`
	Offset int     `json:"offset"`
	Low    float64 `json:"low"`
	High   float64 `json:"high"`
}

func convertGlucoseTargetSchedule(r medtronic.GlucoseTargetSchedule) interface{} {
	sched := make([]AltGlucoseTarget, len(r))
	u := r[0].Units
	for i, s := range r {
		if s.Units != u {
			log.Fatalf("unexpected glucose units type %v", s.Units)
		}
		m := minutes(time.Duration(s.Start))
		low := float64(s.Low)
		high := float64(s.High)
		if u == medtronic.MMolPerLiter {
			low /= 1000
			high /= 1000
		}
		sched[i] = AltGlucoseTarget{
			X:      i,
			Index:  m / 30,
			Start:  s.Start.String() + ":00",
			Offset: m,
			Low:    low,
			High:   high,
		}
	}
	return struct {
		First    int                `json:"first"`
		Units    string             `json:"units"`
		Schedule []AltGlucoseTarget `json:"targets"`
	}{
		First:    int(u),
		Units:    glucoseU(u),
		Schedule: sched,
	}
}

// AltInsulinSensitivity is an openaps-compatible form of InsulinSensitivity.
type AltInsulinSensitivity struct {
	X           int     `json:"x"`
	Index       int     `json:"i"`
	Start       string  `json:"start"`
	Offset      int     `json:"offset"`
	Sensitivity float64 `json:"sensitivity"`
}

func convertInsulinSensitivitySchedule(r medtronic.InsulinSensitivitySchedule) interface{} {
	sched := make([]AltInsulinSensitivity, len(r))
	u := r[0].Units
	for i, s := range r {
		if s.Units != u {
			log.Fatalf("unexpected carb units type %v", s.Units)
		}
		m := minutes(time.Duration(s.Start))
		sens := float64(s.Sensitivity)
		if u == medtronic.MMolPerLiter {
			sens /= 1000
		}
		sched[i] = AltInsulinSensitivity{
			X:           i,
			Index:       m / 30,
			Start:       s.Start.String() + ":00",
			Offset:      m,
			Sensitivity: sens,
		}
	}
	return struct {
		First    int                     `json:"first"`
		Units    string                  `json:"units"`
		Schedule []AltInsulinSensitivity `json:"sensitivities"`
	}{
		First:    int(u),
		Units:    glucoseU(u),
		Schedule: sched,
	}
}

func convertBatteryInfo(r medtronic.BatteryInfo) interface{} {
	status := "normal"
	if r.LowBattery {
		status = "low"
	}
	return struct {
		Voltage medtronic.Voltage `json:"voltage"`
		Status  string            `json:"string"`
	}{
		Voltage: r.Voltage,
		Status:  status,
	}
}

func convertSettingsInfo(r medtronic.SettingsInfo) interface{} {
	return struct {
		AutoOffHours         int               `json:"auto_off_duration_hrs"`
		InsulinActionHours   int               `json:"insulin_action_curve"`
		InsulinConcentration int               `json:"insulinConcentration"`
		MaxBolus             medtronic.Insulin `json:"maxBolus"`
		MaxBasal             medtronic.Insulin `json:"maxBasal"`
		RFEnabled            bool              `json:"rf_enable"`
		SelectedPattern      int               `json:"selected_pattern"`
	}{
		AutoOffHours:         hours(r.AutoOff),
		InsulinActionHours:   hours(r.InsulinAction),
		InsulinConcentration: r.InsulinConcentration,
		MaxBolus:             r.MaxBolus,
		MaxBasal:             r.MaxBasal,
		RFEnabled:            r.RFEnabled,
		SelectedPattern:      r.SelectedPattern,
	}
}

func convertStatusInfo(r medtronic.StatusInfo) interface{} {
	status := "normal"
	if r.Code != 0x03 {
		status = "error"
	}
	return struct {
		Status    string `json:"status"`
		Bolusing  bool   `json:"bolusing"`
		Suspended bool   `json:"suspended"`
	}{
		Status:    status,
		Bolusing:  r.Bolusing,
		Suspended: r.Suspended,
	}
}

func convertTempBasalInfo(r medtronic.TempBasalInfo) interface{} {
	t := struct {
		Duration int         `json:"duration"`
		Temp     string      `json:"temp"`
		Rate     interface{} `json:"rate"`
	}{
		Duration: minutes(r.Duration),
		Temp:     strings.ToLower(r.Type.String()),
	}
	if r.Rate != nil {
		t.Rate = *r.Rate
	} else if r.Percent != nil {
		t.Rate = *r.Percent
	}
	return t
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/signal"

	. "github.com/polydawn/go-errcat"
	"github.com/polydawn/refmt"
	"github.com/polydawn/refmt/json"
	"gopkg.in/alecthomas/kingpin.v2"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
)

func main() {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	go CancelOnInterrupt(cancel)
	exitCode := Main(ctx, os.Args, os.Stdin, os.Stdout, os.Stderr)
	os.Exit(exitCode)
}

// Blocks until a sigint is received, then calls cancel.
func CancelOnInterrupt(cancel context.CancelFunc) {
	signalChan := make(chan os.Signal)
	signal.Notify(signalChan, os.Interrupt)
	<-signalChan
	cancel()
	close(signalChan)
}

// Holder type which makes it easier for us to inspect
//  the args parser result in test code before running logic.
type behavior struct {
	parsedArgs interface{}
	action     func() error
}

type format string

const (
	format_Dumb = "dumb"
	format_Json = "json"
)

func Main(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
	bhv := Parse(ctx, args, stdin, stdout, stderr)
	err := bhv.action()
	return rio.ExitCodeForError(err)
}

func Parse(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) behavior {
	// CLI boilerplate.
	app := kingpin.New("rio", "Repeatable I/O.")
	app.HelpFlag.Short('h')
	app.UsageWriter(stderr)
	app.ErrorWriter(stderr)
	app.Terminate(func(int) {})

	// Output control helper.
	//  Declared early because we reference it in action thunks;
	//  however its format field may not end up set until much lower in the file.
	oc := &outputController{"", stdout, stderr}

	// Args struct defs and flag declarations.
	bhvs := map[string]*behavior{}
	baseArgs := struct {
		Format string
	}{}
	app.Flag("format", "Output api format").
		Default(format_Dumb).
		EnumVar(&baseArgs.Format,
			format_Dumb, format_Json)
	{
		cmd := app.Command("pack", "Pack a Fileset into a Ware.")
		args := struct {
			PackType            string             // Pack type
			Path                string             // Pack target path, abs or rel
			Filters             api.FilesetFilters // Filters for pack
			TargetWarehouseAddr string             // Warehouse address to push to
		}{}
		cmd.Arg("pack", "Pack type").
			Required().
			StringVar(&args.PackType)
		cmd.Arg("path", "Target path").
			Required().
			StringVar(&args.Path)
		cmd.Flag("target", "Warehouse in which to place the ware").
			StringVar(&args.TargetWarehouseAddr)
		cmd.Flag("uid", "Set UID filter [keep, <int>]").
			StringVar(&args.Filters.Uid)
		cmd.Flag("gid", "Set GID filter [keep, <int>]").
			StringVar(&args.Filters.Gid)
		cmd.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]. Will be set to a date if not specified.").
			StringVar(&args.Filters.Mtime)
		cmd.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
			Default("keep").
			EnumVar(&args.Filters.Sticky,
				"keep", "zero")
		bhvs[cmd.FullCommand()] = &behavior{&args, func() error {
			packFunc, err := demuxPackTool(args.PackType)
			if err != nil {
				return err
			}
			resultWareID, err := packFunc(
				ctx,
				api.PackType(args.PackType),
				args.Path,
				args.Filters,
				api.WarehouseAddr(args.TargetWarehouseAddr),
				oc.WireMonitor(rio.Monitor{}),
			)
			if err != nil {
				return err
			}
			oc.EmitResult(resultWareID, nil)
			return nil
		}}
	}
	{
		cmd := app.Command("unpack", "Unpack a Ware into a Fileset on your local filesystem.")
		args := struct {
			WareID               string             // Ware id string "<kind>:<hash>"
			Path                 string             // Unpack target path, may be abs or rel
			Filters              api.FilesetFilters // Filters for unpack
			PlacementMode        string             // Placement mode enum
			SourcesWarehouseAddr []string           // Warehouse address to fetch from
		}{}
		cmd.Arg("path", "Target path").
			Required().
			StringVar(&args.Path)
		cmd.Arg("ware", "Ware ID").
			Required().
			StringVar(&args.WareID)
		cmd.Flag("placer", "Placement mode to use [copy, direct, mount, none]").
			EnumVar(&args.PlacementMode,
				string(rio.Placement_Copy), string(rio.Placement_Direct), string(rio.Placement_Mount), string(rio.Placement_None))
		cmd.Flag("source", "Warehouses from which to fetch the ware").
			StringsVar(&args.SourcesWarehouseAddr)
		cmd.Flag("uid", "Set UID filter [keep, mine, <int>]").
			Default("mine").
			StringVar(&args.Filters.Uid)
		cmd.Flag("gid", "Set GID filter [keep, mine, <int>]").
			Default("mine").
			StringVar(&args.Filters.Gid)
		cmd.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]").
			Default("keep").
			StringVar(&args.Filters.Mtime)
		cmd.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
			Default("zero").
			EnumVar(&args.Filters.Sticky,
				"keep", "zero")
		bhvs[cmd.FullCommand()] = &behavior{&args, func() error {
			wareID, err := api.ParseWareID(args.WareID)
			if err != nil {
				return err
			}
			unpackFunc, err := demuxUnpackTool(string(wareID.Type))
			if err != nil {
				return err
			}
			resultWareID, err := unpackFunc(
				ctx,
				wareID,
				args.Path,
				args.Filters,
				rio.PlacementMode(args.PlacementMode),
				convertWarehouseSlice(args.SourcesWarehouseAddr),
				oc.WireMonitor(rio.Monitor{}),
			)
			if err != nil {
				return err
			}
			oc.EmitResult(resultWareID, nil)
			return nil
		}}
	}
	{
		cmd := app.Command("scan", "Scan some existing data stream see if it's a known packed format, and compute its WareID if so.  (Mostly used for importing tars from the interweb.)")
		args := struct {
			PackType            string             // Pack type
			Filters             api.FilesetFilters // Filters for pack
			SourceWarehouseAddr string             // Warehouse address of data to scan
		}{}
		cmd.Arg("pack", "Pack type").
			Required().
			StringVar(&args.PackType)
		cmd.Flag("source", "Address to of the data to scan.").
			StringVar(&args.SourceWarehouseAddr)
		cmd.Flag("uid", "Set UID filter [keep, <int>]").
			StringVar(&args.Filters.Uid)
		cmd.Flag("gid", "Set GID filter [keep, <int>]").
			StringVar(&args.Filters.Gid)
		cmd.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]. Will be set to a date if not specified.").
			StringVar(&args.Filters.Mtime)
		cmd.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
			Default("keep").
			EnumVar(&args.Filters.Sticky,
				"keep", "zero")
		bhvs[cmd.FullCommand()] = &behavior{&args, func() error {
			scanFunc, err := demuxScanTool(string(args.PackType))
			if err != nil {
				return err
			}
			resultWareID, err := scanFunc(
				ctx,
				api.PackType(args.PackType),
				args.Filters,
				rio.Placement_Direct,
				api.WarehouseAddr(args.SourceWarehouseAddr),
				oc.WireMonitor(rio.Monitor{}),
			)
			if err != nil {
				return err
			}
			oc.EmitResult(resultWareID, nil)
			return nil
		}}
	}
	// Okay now let's be clear: actually all of these behaviors should, end of day,
	//  actually send their errors through our output control.
	//  We still also return it, both so you can write tests around this
	//  method as a whole, and so the top of the program can choose an exit code!
	for _, bhv := range bhvs {
		_action := bhv.action
		bhv.action = func() error {
			err := _action()
			if err != nil {
				oc.EmitResult(api.WareID{}, err)
			}
			return err
		}
	}

	// Parse!
	parsedCmdStr, err := app.Parse(args[1:])
	oc.format = format(baseArgs.Format)
	if err != nil {
		return behavior{
			parsedArgs: err,
			action: func() error {
				err := Errorf(rio.ErrUsage, "error parsing args: %s", err)
				oc.EmitResult(api.WareID{}, err)
				return err
			},
		}
	}
	// Return behavior named by the command and subcommand strings.
	if bhv, ok := bhvs[parsedCmdStr]; ok {
		return *bhv
	}
	panic("unreachable, cli parser must error on unknown commands")
}

type outputController struct {
	format         format
	stdout, stderr io.Writer
}

func (oc outputController) EmitResult(wareID api.WareID, err error) {
	result := &rio.Event_Result{}
	result.WareID = wareID
	result.SetError(err)
	evt := rio.Event{Result: result}
	switch oc.format {
	case "", format_Dumb:
		if err != nil {
			fmt.Fprintln(oc.stderr, err)
		} else {
			fmt.Fprintln(oc.stdout, wareID)
		}
	case format_Json:
		if err != nil {
			fmt.Fprintln(oc.stderr, err)
		}
		marshaller := refmt.NewMarshallerAtlased(json.EncodeOptions{}, oc.stdout, rio.Atlas)
		err := marshaller.Marshal(evt)
		if err != nil {
			panic(err)
		}
	default:
		panic(fmt.Errorf("rio: invalid format %s", oc.format))
	}
}

func (oc outputController) WireMonitor(m rio.Monitor) rio.Monitor {
	ch := make(chan rio.Event)
	m.Chan = ch
	switch oc.format {
	case "", format_Dumb:
		go func() {
			for evt := range ch {
				switch {
				case evt.Log != nil:
					fmt.Fprintf(oc.stderr, "log: lvl=%s msg=%s\n", evt.Log.Level, evt.Log.Msg)
				case evt.Progress != nil:
					// pass... for now
				case evt.Result != nil:
					// pass
				}
			}
		}()
	case format_Json:
		marshaller := refmt.NewMarshallerAtlased(json.EncodeOptions{}, oc.stdout, rio.Atlas)
		go func() {
			for evt := range ch {
				err := marshaller.Marshal(evt)
				if err != nil {
					panic(err)
				}
			}
		}()
	default:
		panic(fmt.Errorf("rio: invalid format %s", oc.format))
	}
	return m
}

func convertWarehouseSlice(slice []string) []api.WarehouseAddr {
	result := make([]api.WarehouseAddr, len(slice))
	for idx, item := range slice {
		result[idx] = api.WarehouseAddr(item)
	}
	return result
}
<commit_msg>cmd: correct flow control for monitor forwarding.<commit_after>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/signal"
	"sync"

	. "github.com/polydawn/go-errcat"
	"github.com/polydawn/refmt"
	"github.com/polydawn/refmt/json"
	"gopkg.in/alecthomas/kingpin.v2"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
)

func main() {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	go CancelOnInterrupt(cancel)
	exitCode := Main(ctx, os.Args, os.Stdin, os.Stdout, os.Stderr)
	os.Exit(exitCode)
}

// Blocks until a sigint is received, then calls cancel.
func CancelOnInterrupt(cancel context.CancelFunc) {
	signalChan := make(chan os.Signal)
	signal.Notify(signalChan, os.Interrupt)
	<-signalChan
	cancel()
}

// Holder type which makes it easier for us to inspect
//  the args parser result in test code before running logic.
type behavior struct {
	parsedArgs interface{}
	action     func() error
}

type format string

const (
	format_Dumb = "dumb"
	format_Json = "json"
)

func Main(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
	bhv := Parse(ctx, args, stdin, stdout, stderr)
	err := bhv.action()
	return rio.ExitCodeForError(err)
}

func Parse(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) behavior {
	// CLI boilerplate.
	app := kingpin.New("rio", "Repeatable I/O.")
	app.HelpFlag.Short('h')
	app.UsageWriter(stderr)
	app.ErrorWriter(stderr)
	app.Terminate(func(int) {})

	// Output control helper.
	//  Declared early because we reference it in action thunks;
	//  however its format field may not end up set until much lower in the file.
	oc := &outputController{"", stdout, stderr, nil, sync.WaitGroup{}}

	// Args struct defs and flag declarations.
	bhvs := map[string]*behavior{}
	baseArgs := struct {
		Format string
	}{}
	app.Flag("format", "Output api format").
		Default(format_Dumb).
		EnumVar(&baseArgs.Format,
			format_Dumb, format_Json)
	{
		cmd := app.Command("pack", "Pack a Fileset into a Ware.")
		args := struct {
			PackType            string             // Pack type
			Path                string             // Pack target path, abs or rel
			Filters             api.FilesetFilters // Filters for pack
			TargetWarehouseAddr string             // Warehouse address to push to
		}{}
		cmd.Arg("pack", "Pack type").
			Required().
			StringVar(&args.PackType)
		cmd.Arg("path", "Target path").
			Required().
			StringVar(&args.Path)
		cmd.Flag("target", "Warehouse in which to place the ware").
			StringVar(&args.TargetWarehouseAddr)
		cmd.Flag("uid", "Set UID filter [keep, <int>]").
			StringVar(&args.Filters.Uid)
		cmd.Flag("gid", "Set GID filter [keep, <int>]").
			StringVar(&args.Filters.Gid)
		cmd.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]. Will be set to a date if not specified.").
			StringVar(&args.Filters.Mtime)
		cmd.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
			Default("keep").
			EnumVar(&args.Filters.Sticky,
				"keep", "zero")
		bhvs[cmd.FullCommand()] = &behavior{&args, func() error {
			packFunc, err := demuxPackTool(args.PackType)
			if err != nil {
				return err
			}
			resultWareID, err := packFunc(
				ctx,
				api.PackType(args.PackType),
				args.Path,
				args.Filters,
				api.WarehouseAddr(args.TargetWarehouseAddr),
				oc.WireMonitor(ctx, rio.Monitor{}),
			)
			if err != nil {
				return err
			}
			oc.EmitResult(resultWareID, nil)
			return nil
		}}
	}
	{
		cmd := app.Command("unpack", "Unpack a Ware into a Fileset on your local filesystem.")
		args := struct {
			WareID               string             // Ware id string "<kind>:<hash>"
			Path                 string             // Unpack target path, may be abs or rel
			Filters              api.FilesetFilters // Filters for unpack
			PlacementMode        string             // Placement mode enum
			SourcesWarehouseAddr []string           // Warehouse address to fetch from
		}{}
		cmd.Arg("path", "Target path").
			Required().
			StringVar(&args.Path)
		cmd.Arg("ware", "Ware ID").
			Required().
			StringVar(&args.WareID)
		cmd.Flag("placer", "Placement mode to use [copy, direct, mount, none]").
			EnumVar(&args.PlacementMode,
				string(rio.Placement_Copy), string(rio.Placement_Direct), string(rio.Placement_Mount), string(rio.Placement_None))
		cmd.Flag("source", "Warehouses from which to fetch the ware").
			StringsVar(&args.SourcesWarehouseAddr)
		cmd.Flag("uid", "Set UID filter [keep, mine, <int>]").
			Default("mine").
			StringVar(&args.Filters.Uid)
		cmd.Flag("gid", "Set GID filter [keep, mine, <int>]").
			Default("mine").
			StringVar(&args.Filters.Gid)
		cmd.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]").
			Default("keep").
			StringVar(&args.Filters.Mtime)
		cmd.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
			Default("zero").
			EnumVar(&args.Filters.Sticky,
				"keep", "zero")
		bhvs[cmd.FullCommand()] = &behavior{&args, func() error {
			wareID, err := api.ParseWareID(args.WareID)
			if err != nil {
				return err
			}
			unpackFunc, err := demuxUnpackTool(string(wareID.Type))
			if err != nil {
				return err
			}
			resultWareID, err := unpackFunc(
				ctx,
				wareID,
				args.Path,
				args.Filters,
				rio.PlacementMode(args.PlacementMode),
				convertWarehouseSlice(args.SourcesWarehouseAddr),
				oc.WireMonitor(ctx, rio.Monitor{}),
			)
			if err != nil {
				return err
			}
			oc.EmitResult(resultWareID, nil)
			return nil
		}}
	}
	{
		cmd := app.Command("scan", "Scan some existing data stream see if it's a known packed format, and compute its WareID if so.  (Mostly used for importing tars from the interweb.)")
		args := struct {
			PackType            string             // Pack type
			Filters             api.FilesetFilters // Filters for pack
			SourceWarehouseAddr string             // Warehouse address of data to scan
		}{}
		cmd.Arg("pack", "Pack type").
			Required().
			StringVar(&args.PackType)
		cmd.Flag("source", "Address to of the data to scan.").
			StringVar(&args.SourceWarehouseAddr)
		cmd.Flag("uid", "Set UID filter [keep, <int>]").
			StringVar(&args.Filters.Uid)
		cmd.Flag("gid", "Set GID filter [keep, <int>]").
			StringVar(&args.Filters.Gid)
		cmd.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]. Will be set to a date if not specified.").
			StringVar(&args.Filters.Mtime)
		cmd.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
			Default("keep").
			EnumVar(&args.Filters.Sticky,
				"keep", "zero")
		bhvs[cmd.FullCommand()] = &behavior{&args, func() error {
			scanFunc, err := demuxScanTool(string(args.PackType))
			if err != nil {
				return err
			}
			resultWareID, err := scanFunc(
				ctx,
				api.PackType(args.PackType),
				args.Filters,
				rio.Placement_Direct,
				api.WarehouseAddr(args.SourceWarehouseAddr),
				oc.WireMonitor(ctx, rio.Monitor{}),
			)
			if err != nil {
				return err
			}
			oc.EmitResult(resultWareID, nil)
			return nil
		}}
	}
	// Okay now let's be clear: actually all of these behaviors should, end of day,
	//  actually send their errors through our output control.
	//  We still also return it, both so you can write tests around this
	//  method as a whole, and so the top of the program can choose an exit code!
	for _, bhv := range bhvs {
		_action := bhv.action
		bhv.action = func() error {
			err := _action()
			if err != nil {
				oc.EmitResult(api.WareID{}, err)
			}
			return err
		}
	}

	// Parse!
	parsedCmdStr, err := app.Parse(args[1:])
	oc.format = format(baseArgs.Format)
	if err != nil {
		return behavior{
			parsedArgs: err,
			action: func() error {
				err := Errorf(rio.ErrUsage, "error parsing args: %s", err)
				oc.EmitResult(api.WareID{}, err)
				return err
			},
		}
	}
	// Return behavior named by the command and subcommand strings.
	if bhv, ok := bhvs[parsedCmdStr]; ok {
		return *bhv
	}
	panic("unreachable, cli parser must error on unknown commands")
}

type outputController struct {
	format         format
	stdout, stderr io.Writer
	monChan        chan rio.Event // set up when calling WireMonitor
	monWg          sync.WaitGroup
}

func (oc *outputController) EmitResult(wareID api.WareID, err error) {
	if oc.monChan != nil {
		close(oc.monChan)
	}
	oc.monWg.Wait()
	result := &rio.Event_Result{}
	result.WareID = wareID
	result.SetError(err)
	evt := rio.Event{Result: result}
	switch oc.format {
	case "", format_Dumb:
		if err != nil {
			fmt.Fprintln(oc.stderr, err)
		} else {
			fmt.Fprintln(oc.stdout, wareID)
		}
	case format_Json:
		if err != nil {
			fmt.Fprintln(oc.stderr, err)
		}
		marshaller := refmt.NewMarshallerAtlased(json.EncodeOptions{}, oc.stdout, rio.Atlas)
		err := marshaller.Marshal(evt)
		if err != nil {
			panic(err)
		}
	default:
		panic(fmt.Errorf("rio: invalid format %s", oc.format))
	}
}

func (oc *outputController) WireMonitor(ctx context.Context, m rio.Monitor) rio.Monitor {
	oc.monChan = make(chan rio.Event)
	oc.monWg.Add(1)
	m.Chan = oc.monChan
	switch oc.format {
	case "", format_Dumb:
		go func() {
			defer oc.monWg.Done()
			for {
				select {
				case evt, ok := <-oc.monChan:
					if !ok {
						return
					}
					switch {
					case evt.Log != nil:
						fmt.Fprintf(oc.stderr, "log: lvl=%s msg=%s\n", evt.Log.Level, evt.Log.Msg)
					case evt.Progress != nil:
						// pass... for now
					case evt.Result != nil:
						// pass
					}
				case <-ctx.Done():
					return
				}
			}
		}()
	case format_Json:
		marshaller := refmt.NewMarshallerAtlased(json.EncodeOptions{}, oc.stdout, rio.Atlas)
		go func() {
			defer oc.monWg.Done()
			for {
				select {
				case evt, ok := <-oc.monChan:
					if !ok {
						return
					}
					err := marshaller.Marshal(evt)
					if err != nil {
						panic(err)
					}
				case <-ctx.Done():
					return
				}
			}
		}()
	default:
		panic(fmt.Errorf("rio: invalid format %s", oc.format))
	}
	return m
}

func convertWarehouseSlice(slice []string) []api.WarehouseAddr {
	result := make([]api.WarehouseAddr, len(slice))
	for idx, item := range slice {
		result[idx] = api.WarehouseAddr(item)
	}
	return result
}
<|endoftext|>


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

package main

import (
	"github.com/globocom/tsuru/cmd"
	_ "github.com/globocom/tsuru/provision/docker"
	_ "github.com/globocom/tsuru/provision/juju"
	"os"
)

func buildManager() *cmd.Manager {
	m := cmd.NewManager("tsr", "0.1.0", "", os.Stdout, os.Stderr, os.Stdin)
	m.Register(&apiCmd{})
	m.Register(&collectorCmd{})
	m.Register(&tokenCmd{})
	return m
}

func main() {
	m := buildManager()
	m.Run(os.Args[1:])
}
<commit_msg>cmd/tsr: use tsrCommand in main<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/globocom/tsuru/cmd"
	_ "github.com/globocom/tsuru/provision/docker"
	_ "github.com/globocom/tsuru/provision/juju"
	"os"
)

func buildManager() *cmd.Manager {
	m := cmd.NewManager("tsr", "0.1.0", "", os.Stdout, os.Stderr, os.Stdin)
	m.Register(&tsrCommand{Command: &apiCmd{}})
	m.Register(&tsrCommand{Command: &collectorCmd{}})
	m.Register(&tsrCommand{Command: tokenCmd{}})
	return m
}

func main() {
	m := buildManager()
	m.Run(os.Args[1:])
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"syscall"
)

const (
	/*
	 * IOCTL commands --- we will commandeer 0x4C ('L')
	 */
	LOOP_SET_CAPACITY = 0x4C07
	LOOP_CHANGE_FD    = 0x4C06
	LOOP_GET_STATUS64 = 0x4C05
	LOOP_SET_STATUS64 = 0x4C04
	LOOP_GET_STATUS   = 0x4C03
	LOOP_SET_STATUS   = 0x4C02
	LOOP_CLR_FD       = 0x4C01
	LOOP_SET_FD       = 0x4C00
	LO_NAME_SIZE      = 64
	LO_KEY_SIZE       = 32
	/* /dev/loop-control interface */
	LOOP_CTL_ADD      = 0x4C80
	LOOP_CTL_REMOVE   = 0x4C81
	LOOP_CTL_GET_FREE = 0x4C82
	SYS_ioctl         = 16
)

//http://distro.ibiblio.org/tinycorelinux/5.x/x86_64/tcz/
//The .dep is the name + .dep

var (
	l                  = log.New(os.Stdout, "tcz: ", 0)
	host               = flag.String("h", "tinycorelinux.net", "Host name for packages")
	version            = flag.String("v", "5.x", "tinycore version")
	arch               = flag.String("a", "x86_64", "tinycore architecture")
	port               = flag.String("p", "80", "Host port")
	debugPrint         = flag.Bool("d", false, "Enable debug prints")
	debug              = func(f string, s ...interface{}) {}
	tczServerDir       string
	tczLocalPackageDir string
)

// consider making this a goroutine which pushes the string down the channel.
func findloop() (name string, err error) {
	cfd, err := syscall.Open("/dev/loop-control", syscall.O_RDWR, 0)
	if err != nil {
		log.Fatalf("/dev/loop-control: %v", err)
	}
	defer syscall.Close(cfd)
	a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(cfd), LOOP_CTL_GET_FREE, 0)
	if errno != 0 {
		log.Fatalf("ioctl: %v\n", err)
	}
	log.Printf("a %v b %v err %v\n", a, b, err)
	name = fmt.Sprintf("/dev/loop%d", a)
	return name, nil
}

func clonetree(tree string) error {
	debug("Clone tree %v", tree)
	lt := len(tree)
	err := filepath.Walk(tree, func(path string, fi os.FileInfo, err error) error {

		debug("Clone tree with path %s fi %v", path, fi)
		if fi.IsDir() {
			debug("Walking, dir %v\n", path)
			if path[lt:] == "" {
				return nil
			}
			if err := os.MkdirAll(path[lt:], 0700); err != nil {
				debug("Mkdir of %s failed: %v", path[lt:], err)
				// TODO: EEXIST should not be an error. Ignore
				// err for now. FIXME.
				//return err
			}
			return nil
		}
		// all else gets a symlink.

		// If the link exists
		if target, err := os.Readlink(path[lt:]); err == nil {
			// Confirm that it points to the same path to be symlinked
			if target == path {
				return nil
			}

			// If it does not, return error because tcz packages are inconsistent
			return fmt.Errorf("symlink: need %q -> %q, but %q -> %q is already there", path, path[lt:], path, target)
		}

		debug("Need to symlink %v to %v\n", path, path[lt:])

		if err := os.Symlink(path, path[lt:]); err != nil {
			return fmt.Errorf("Symlink: %v", err)
		}

		return nil
	})
	if err != nil {
		l.Fatalf("Clone tree: %v", err)
	}
	return nil
}

func fetch(p string) error {
	fullpath := filepath.Join(tczLocalPackageDir, p)
	packageName := filepath.Join(tczServerDir, p)
	if _, err := os.Stat(fullpath); err != nil {
		cmd := fmt.Sprintf("http://%s:%s/%s", *host, *port, packageName)
		debug("Fetch %v\n", cmd)

		resp, err := http.Get(cmd)
		if err != nil {
			l.Fatalf("Get of %v failed: %v\n", cmd, err)
		}
		defer resp.Body.Close()

		if resp.Status != "200 OK" {
			debug("%v Not OK! %v\n", cmd, resp.Status)
			return syscall.ENOENT
		}

		debug("resp %v err %v\n", resp, err)
		// we have the whole tcz in resp.Body.
		// First, save it to /tcz/name
		f, err := os.Create(fullpath)
		if err != nil {
			l.Fatalf("Create of :%v: failed: %v\n", fullpath, err)
		} else {
			debug("created %v f %v\n", fullpath, f)
		}

		if c, err := io.Copy(f, resp.Body); err != nil {
			l.Fatal(err)
		} else {
			/* OK, these are compressed tars ... */
			debug("c %v err %v\n", c, err)
		}
		f.Close()
	}
	return nil
}

// deps is ALL the packages we need fetched or not
// this may even let us work with parallel tcz, ALMOST
func installPackage(tczName string, deps map[string]bool) error {
	debug("installPackage: %v %v\n", tczName, deps)
	depName := tczName + ".dep"
	if err := fetch(tczName); err != nil {
		l.Fatal(err)
	}
	deps[tczName] = true
	debug("Fetched %v\n", tczName)
	// now fetch dependencies if any.
	if err := fetch(depName); err == nil {
		debug("Fetched dep ok!\n")
	} else {
		debug("No dep file found\n")
		if err := ioutil.WriteFile(filepath.Join(tczLocalPackageDir, depName), []byte{}, os.FileMode(0444)); err != nil {
			debug("Tried to write Blank file %v, failed %v\n", depName, err)
		}
		return nil
	}
	// read deps file
	deplist, err := ioutil.ReadFile(filepath.Join(tczLocalPackageDir, depName))
	if err != nil {
		l.Fatalf("Fetched dep file %v but can't read it? %v", depName, err)
	}
	debug("deplist for %v is :%v:\n", depName, deplist)
	for _, v := range strings.Split(string(deplist), "\n") {
		// split("name\n") gets you a 2-element array with second
		// element the empty string
		if len(v) == 0 {
			break
		}
		debug("FOR %v get package %v\n", tczName, v)
		if deps[v] {
			continue
		}
		if err := installPackage(v, deps); err != nil {
			return err
		}
	}
	return nil

}

func setupPackages(tczName string, deps map[string]bool) error {
	debug("setupPackages: @ %v deps %v\n", tczName, deps)
	for v := range deps {
		cmdName := strings.Split(v, ".")[0]
		packagePath := filepath.Join("/tmp/tcloop", cmdName)
		if err := os.MkdirAll(packagePath, 0700); err != nil {
			l.Fatal(err)
		}

		loopname, err := findloop()
		if err != nil {
			l.Fatal(err)
		}
		debug("findloop gets %v err %v\n", loopname, err)
		pkgpath := filepath.Join(tczLocalPackageDir, v)
		ffd, err := syscall.Open(pkgpath, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatalf("%v: %v\n", pkgpath, err)
		}
		lfd, err := syscall.Open(loopname, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatalf("%v: %v\n", loopname, err)
		}
		debug("ffd %v lfd %v\n", ffd, lfd)
		a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(lfd), LOOP_SET_FD, uintptr(ffd))
		if errno != 0 {
			l.Fatalf("loop set fd ioctl: pkgpath :%v:, loop :%v:, %v, %v, %v\n", pkgpath, loopname, a, b, errno)
		}
		/* now mount it. The convention is the mount is in /tmp/tcloop/packagename */
		if err := syscall.Mount(loopname, packagePath, "squashfs", syscall.MS_MGC_VAL|syscall.MS_RDONLY, ""); err != nil {
			l.Fatalf("Mount :%s: on :%s: %v\n", loopname, packagePath, err)
		}
		err = clonetree(packagePath)
		if err != nil {
			l.Fatalf("clonetree:  %v\n", err)
		}
	}
	return nil

}

func main() {
	flag.Parse()
	needPackages := make(map[string]bool)
	tczServerDir = filepath.Join("/", *version, *arch, "tcz")
	tczLocalPackageDir = filepath.Join("/tcz", tczServerDir)
	if *debugPrint {
		debug = l.Printf
	}
	if len(os.Args) < 2 {
		os.Exit(1)
	}

	cmdName := flag.Args()[0]
	tczName := cmdName + ".tcz"

	if err := os.MkdirAll(tczLocalPackageDir, 0700); err != nil {
		l.Fatal(err)
	}

	if err := os.MkdirAll("/tmp/tcloop", 0700); err != nil {
		l.Fatal(err)
	}

	if err := installPackage(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
	debug("After installpackages: needPackages %v\n", needPackages)

	if err := setupPackages(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
}
<commit_msg>Add debug flag<commit_after>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"syscall"
)

const (
	/*
	 * IOCTL commands --- we will commandeer 0x4C ('L')
	 */
	LOOP_SET_CAPACITY = 0x4C07
	LOOP_CHANGE_FD    = 0x4C06
	LOOP_GET_STATUS64 = 0x4C05
	LOOP_SET_STATUS64 = 0x4C04
	LOOP_GET_STATUS   = 0x4C03
	LOOP_SET_STATUS   = 0x4C02
	LOOP_CLR_FD       = 0x4C01
	LOOP_SET_FD       = 0x4C00
	LO_NAME_SIZE      = 64
	LO_KEY_SIZE       = 32
	/* /dev/loop-control interface */
	LOOP_CTL_ADD      = 0x4C80
	LOOP_CTL_REMOVE   = 0x4C81
	LOOP_CTL_GET_FREE = 0x4C82
	SYS_ioctl         = 16
)

//http://distro.ibiblio.org/tinycorelinux/5.x/x86_64/tcz/
//The .dep is the name + .dep

var (
	l                  = log.New(os.Stdout, "tcz: ", 0)
	host               = flag.String("h", "tinycorelinux.net", "Host name for packages")
	version            = flag.String("v", "5.x", "tinycore version")
	arch               = flag.String("a", "x86_64", "tinycore architecture")
	port               = flag.String("p", "80", "Host port")
	debugPrint         = flag.Bool("d", false, "Enable debug prints")
	debug              = func(f string, s ...interface{}) {}
	tczServerDir       string
	tczLocalPackageDir string
)

// consider making this a goroutine which pushes the string down the channel.
func findloop() (name string, err error) {
	cfd, err := syscall.Open("/dev/loop-control", syscall.O_RDWR, 0)
	if err != nil {
		log.Fatalf("/dev/loop-control: %v", err)
	}
	defer syscall.Close(cfd)
	a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(cfd), LOOP_CTL_GET_FREE, 0)
	if errno != 0 {
		log.Fatalf("ioctl: %v\n", err)
	}
	log.Printf("a %v b %v err %v\n", a, b, err)
	name = fmt.Sprintf("/dev/loop%d", a)
	return name, nil
}

func clonetree(tree string) error {
	debug("Clone tree %v", tree)
	lt := len(tree)
	err := filepath.Walk(tree, func(path string, fi os.FileInfo, err error) error {

		debug("Clone tree with path %s fi %v", path, fi)
		if fi.IsDir() {
			debug("Walking, dir %v\n", path)
			if path[lt:] == "" {
				return nil
			}
			if err := os.MkdirAll(path[lt:], 0700); err != nil {
				debug("Mkdir of %s failed: %v", path[lt:], err)
				// TODO: EEXIST should not be an error. Ignore
				// err for now. FIXME.
				//return err
			}
			return nil
		}
		// all else gets a symlink.

		// If the link exists
		if target, err := os.Readlink(path[lt:]); err == nil {
			// Confirm that it points to the same path to be symlinked
			if target == path {
				return nil
			}

			// If it does not, return error because tcz packages are inconsistent
			return fmt.Errorf("symlink: need %q -> %q, but %q -> %q is already there", path, path[lt:], path, target)
		}

		debug("Need to symlink %v to %v\n", path, path[lt:])

		if err := os.Symlink(path, path[lt:]); err != nil {
			return fmt.Errorf("Symlink: %v", err)
		}

		return nil
	})
	if err != nil {
		l.Fatalf("Clone tree: %v", err)
	}
	return nil
}

func fetch(p string) error {
	fullpath := filepath.Join(tczLocalPackageDir, p)
	packageName := filepath.Join(tczServerDir, p)
	if _, err := os.Stat(fullpath); err != nil {
		cmd := fmt.Sprintf("http://%s:%s/%s", *host, *port, packageName)
		debug("Fetch %v\n", cmd)

		resp, err := http.Get(cmd)
		if err != nil {
			l.Fatalf("Get of %v failed: %v\n", cmd, err)
		}
		defer resp.Body.Close()

		if resp.Status != "200 OK" {
			debug("%v Not OK! %v\n", cmd, resp.Status)
			return syscall.ENOENT
		}

		debug("resp %v err %v\n", resp, err)
		// we have the whole tcz in resp.Body.
		// First, save it to /tcz/name
		f, err := os.Create(fullpath)
		if err != nil {
			l.Fatalf("Create of :%v: failed: %v\n", fullpath, err)
		} else {
			debug("created %v f %v\n", fullpath, f)
		}

		if c, err := io.Copy(f, resp.Body); err != nil {
			l.Fatal(err)
		} else {
			/* OK, these are compressed tars ... */
			debug("c %v err %v\n", c, err)
		}
		f.Close()
	}
	return nil
}

// deps is ALL the packages we need fetched or not
// this may even let us work with parallel tcz, ALMOST
func installPackage(tczName string, deps map[string]bool) error {
	debug("installPackage: %v %v\n", tczName, deps)
	depName := tczName + ".dep"
	if err := fetch(tczName); err != nil {
		l.Fatal(err)
	}
	deps[tczName] = true
	debug("Fetched %v\n", tczName)
	// now fetch dependencies if any.
	if err := fetch(depName); err == nil {
		debug("Fetched dep ok!\n")
	} else {
		debug("No dep file found\n")
		if err := ioutil.WriteFile(filepath.Join(tczLocalPackageDir, depName), []byte{}, os.FileMode(0444)); err != nil {
			debug("Tried to write Blank file %v, failed %v\n", depName, err)
		}
		return nil
	}
	// read deps file
	deplist, err := ioutil.ReadFile(filepath.Join(tczLocalPackageDir, depName))
	if err != nil {
		l.Fatalf("Fetched dep file %v but can't read it? %v", depName, err)
	}
	debug("deplist for %v is :%v:\n", depName, deplist)
	for _, v := range strings.Split(string(deplist), "\n") {
		// split("name\n") gets you a 2-element array with second
		// element the empty string
		if len(v) == 0 {
			break
		}
		debug("FOR %v get package %v\n", tczName, v)
		if deps[v] {
			continue
		}
		if err := installPackage(v, deps); err != nil {
			return err
		}
	}
	return nil

}

func setupPackages(tczName string, deps map[string]bool) error {
	debug("setupPackages: @ %v deps %v\n", tczName, deps)
	for v := range deps {
		cmdName := strings.Split(v, ".")[0]
		packagePath := filepath.Join("/tmp/tcloop", cmdName)
		if err := os.MkdirAll(packagePath, 0700); err != nil {
			l.Fatal(err)
		}

		loopname, err := findloop()
		if err != nil {
			l.Fatal(err)
		}
		debug("findloop gets %v err %v\n", loopname, err)
		pkgpath := filepath.Join(tczLocalPackageDir, v)
		ffd, err := syscall.Open(pkgpath, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatalf("%v: %v\n", pkgpath, err)
		}
		lfd, err := syscall.Open(loopname, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatalf("%v: %v\n", loopname, err)
		}
		debug("ffd %v lfd %v\n", ffd, lfd)
		a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(lfd), LOOP_SET_FD, uintptr(ffd))
		if errno != 0 {
			l.Fatalf("loop set fd ioctl: pkgpath :%v:, loop :%v:, %v, %v, %v\n", pkgpath, loopname, a, b, errno)
		}
		/* now mount it. The convention is the mount is in /tmp/tcloop/packagename */
		if err := syscall.Mount(loopname, packagePath, "squashfs", syscall.MS_MGC_VAL|syscall.MS_RDONLY, ""); err != nil {
			l.Fatalf("Mount :%s: on :%s: %v\n", loopname, packagePath, err)
		}
		err = clonetree(packagePath)
		if err != nil {
			l.Fatalf("clonetree:  %v\n", err)
		}
	}
	return nil

}

func debug(f string, s ...interface{}) {}

func main() {
	flag.Parse()
	needPackages := make(map[string]bool)
	tczServerDir = filepath.Join("/", *version, *arch, "tcz")
	tczLocalPackageDir = filepath.Join("/tcz", tczServerDir)
	if *debugPrint {
		debug = l.Printf
	}
	if len(os.Args) < 2 {
		os.Exit(1)
	}

	cmdName := flag.Args()[0]
	tczName := cmdName + ".tcz"

	if err := os.MkdirAll(tczLocalPackageDir, 0700); err != nil {
		l.Fatal(err)
	}

	if err := os.MkdirAll("/tmp/tcloop", 0700); err != nil {
		l.Fatal(err)
	}

	if err := installPackage(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
	debug("After installpackages: needPackages %v\n", needPackages)

	if err := setupPackages(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
}
<|endoftext|>


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

// Package safefile implements safe "atomic" saving of files.
//
// Instead of truncating and overwriting the destination file, it creates a
// temporary file in the same directory, writes to it, and then renames the
// temporary file to the original name when calling Commit.
//
// Example:
//
//  f, err := safefile.Create("/home/ken/report.txt")
//  if err != nil {
//  	// ...
//  }
//  // Created temporary file /home/ken/133a7876287381fa-0.tmp
//
//  defer f.Close()
//
//  _, err = io.WriteString(f, "Hello world")
//  if err != nil {
//  	// ...
//  }
//  // Wrote "Hello world" to /home/ken/133a7876287381fa-0.tmp
//
//  err = f.Commit()
//  if err != nil {
//      // ...
//  }
//  // Renamed /home/ken/133a7876287381fa-0.tmp to /home/ken/report.txt
//
package safefile

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

type File struct {
	*os.File
	origName  string
	closeFunc func(*File) error
}

func makeTempName(origname string, counter int) (tempname string, err error) {
	origname = filepath.Clean(origname)
	if len(origname) == 0 || origname[len(origname)-1] == filepath.Separator {
		return "", os.ErrInvalid
	}
	return filepath.Join(filepath.Dir(origname), fmt.Sprintf("%x-%d.tmp", time.Now().UnixNano(), counter)), nil
}

// Create creates a file in the same directory as filename
func Create(filename string, perm os.FileMode) (*File, error) {
	counter := 0
	for {
		tempname, err := makeTempName(filename, counter)
		if err != nil {
			return nil, err
		}
		f, err := os.OpenFile(tempname, os.O_RDWR|os.O_CREATE, perm)
		if err != nil {
			if os.IsExist(err) {
				counter++
				continue
			}
			return nil, err
		}
		return &File{
			File:      f,
			origName:  filename,
			closeFunc: closeUncommitted,
		}, nil
	}
}

// OrigName returns the original filename given to Create.
func (f *File) OrigName() string {
	return f.origName
}

// Close closes temporary file and removes it.
// If the file has been committed, Close is noop.
func (f *File) Close() error {
	return f.closeFunc(f)
}

func closeUncommitted(f *File) error {
	err0 := f.File.Close()
	err1 := os.Remove(f.Name())
	f.closeFunc = closeAgainError
	if err0 != nil {
		return err0
	}
	return err1
}

func closeAfterFailedRename(f *File) error {
	// just remove temporary file.
	f.closeFunc = closeAgainError
	return os.Remove(f.Name())
}

func closeCommitted(f *File) error {
	// noop
	return nil
}

func closeAgainError(f *File) error {
	return os.ErrInvalid
}

// Commit safely closes the file by syncing temporary file,
// closing it and renaming to the original file name.
//
// In case of success, the temporary file is closed and
// no longer exists on disk. It is safe to call Close on
// after Commit: the operation will do nothing.
//
// In case of error, the temporary file is still opened
// and exists on disk; it must be closed by callers by
// calling Close or by trying to commit again.
func (f *File) Commit() error {
	// Sync to disk.
	err := f.Sync()
	if err != nil {
		return err
	}
	// Close underlying os.File.
	err = f.File.Close()
	if err != nil {
		return err
	}
	// Rename.
	err = os.Rename(f.Name(), f.origName)
	if err != nil {
		f.closeFunc = closeAfterFailedRename
		return err
	}
	f.closeFunc = closeCommitted
	return nil
}

// WriteFile is a safe analog of ioutil.WriteFile.
func WriteFile(filename string, data []byte, perm os.FileMode) error {
	f, err := Create(filename, perm)
	if err != nil {
		return err
	}
	defer f.Close()
	n, err := f.Write(data)
	if err != nil {
		return err
	}
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
		return err
	}
	return f.Commit()
}
<commit_msg>Fix documentation.<commit_after>// Copyright 2013 Dmitry Chestnykh. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package safefile implements safe "atomic" saving of files.
//
// Instead of truncating and overwriting the destination file, it creates a
// temporary file in the same directory, writes to it, and then renames the
// temporary file to the original name when calling Commit.
//
// Example:
//
//  f, err := safefile.Create("/home/ken/report.txt")
//  if err != nil {
//  	// ...
//  }
//  // Created temporary file /home/ken/133a7876287381fa-0.tmp
//
//  defer f.Close()
//
//  _, err = io.WriteString(f, "Hello world")
//  if err != nil {
//  	// ...
//  }
//  // Wrote "Hello world" to /home/ken/133a7876287381fa-0.tmp
//
//  err = f.Commit()
//  if err != nil {
//      // ...
//  }
//  // Renamed /home/ken/133a7876287381fa-0.tmp to /home/ken/report.txt
//
package safefile

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

type File struct {
	*os.File
	origName  string
	closeFunc func(*File) error
}

func makeTempName(origname string, counter int) (tempname string, err error) {
	origname = filepath.Clean(origname)
	if len(origname) == 0 || origname[len(origname)-1] == filepath.Separator {
		return "", os.ErrInvalid
	}
	return filepath.Join(filepath.Dir(origname), fmt.Sprintf("%x-%d.tmp", time.Now().UnixNano(), counter)), nil
}

// Create creates a temporary file in the same directory as filename,
// which will be renamed to the given filename when calling Commit.
func Create(filename string, perm os.FileMode) (*File, error) {
	counter := 0
	for {
		tempname, err := makeTempName(filename, counter)
		if err != nil {
			return nil, err
		}
		f, err := os.OpenFile(tempname, os.O_RDWR|os.O_CREATE, perm)
		if err != nil {
			if os.IsExist(err) {
				counter++
				continue
			}
			return nil, err
		}
		return &File{
			File:      f,
			origName:  filename,
			closeFunc: closeUncommitted,
		}, nil
	}
}

// OrigName returns the original filename given to Create.
func (f *File) OrigName() string {
	return f.origName
}

// Close closes temporary file and removes it.
// If the file has been committed, Close is no-op.
func (f *File) Close() error {
	return f.closeFunc(f)
}

func closeUncommitted(f *File) error {
	err0 := f.File.Close()
	err1 := os.Remove(f.Name())
	f.closeFunc = closeAgainError
	if err0 != nil {
		return err0
	}
	return err1
}

func closeAfterFailedRename(f *File) error {
	// just remove temporary file.
	f.closeFunc = closeAgainError
	return os.Remove(f.Name())
}

func closeCommitted(f *File) error {
	// noop
	return nil
}

func closeAgainError(f *File) error {
	return os.ErrInvalid
}

// Commit safely commits data into the original file by syncing temporary
// file to disk, closing it and renaming to the original file name.
//
// In case of success, the temporary file is closed and no longer exists
// on disk. It is safe to call Close after Commit: the operation will do
// nothing.
//
// In case of error, the temporary file is still opened and exists on disk;
// it must be closed by callers by calling Close or by trying to commit again.
func (f *File) Commit() error {
	// Sync to disk.
	err := f.Sync()
	if err != nil {
		return err
	}
	// Close underlying os.File.
	err = f.File.Close()
	if err != nil {
		return err
	}
	// Rename.
	err = os.Rename(f.Name(), f.origName)
	if err != nil {
		f.closeFunc = closeAfterFailedRename
		return err
	}
	f.closeFunc = closeCommitted
	return nil
}

// WriteFile is a safe analog of ioutil.WriteFile.
func WriteFile(filename string, data []byte, perm os.FileMode) error {
	f, err := Create(filename, perm)
	if err != nil {
		return err
	}
	defer f.Close()
	n, err := f.Write(data)
	if err != nil {
		return err
	}
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
		return err
	}
	return f.Commit()
}
<|endoftext|>


<commit_before>package time

// Schedule represents a schedule of an application on a platform.
type Schedule struct {
	Cores uint
	Tasks uint
	Span  float64

	Mapping []uint
	Order   []uint
	Start   []float64
	Finish  []float64
}
<commit_msg>Add Duration to Schedule<commit_after>package time

// Schedule represents a schedule of an application on a platform.
type Schedule struct {
	Cores uint
	Tasks uint
	Span  float64

	Mapping []uint
	Order   []uint
	Start   []float64
	Finish  []float64
}

// Duration computes the difference between Finish and Start.
func (self *Schedule) Duration() []float64 {
	time := make([]float64, len(self.Start))
	for i := range time {
		time[i] = self.Finish[i] - self.Start[i]
	}
	return time
}
<|endoftext|>


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

package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"strconv"
	"text/tabwriter"

	"github.com/romana/core/common"
	"github.com/romana/core/romana/util"

	cli "github.com/spf13/cobra"
	config "github.com/spf13/viper"
)

// hostCmd represents the host commands
var hostCmd = &cli.Command{
	Use:   "host [add|show|list|remove]",
	Short: "Add, Remove or Show hosts for romana services.",
	Long: `Add, Remove or Show hosts for romana services.

host requires a subcommand, e.g. ` + "`romana host add`." + `

For more information, please check http://romana.io
`,
}

func init() {
	hostCmd.AddCommand(hostAddCmd)
	hostCmd.AddCommand(hostShowCmd)
	hostCmd.AddCommand(hostListCmd)
	hostCmd.AddCommand(hostRemoveCmd)
}

var hostAddCmd = &cli.Command{
	Use:          "add [hostname][hostip][romana cidr][(optional)agent port]",
	Short:        "Add a new host.",
	Long:         `Add a new host.`,
	RunE:         hostAdd,
	SilenceUsage: true,
}

var hostShowCmd = &cli.Command{
	Use:          "show [hostname1][hostname2]...",
	Short:        "Show details for a specific host.",
	Long:         `Show details for a specific host.`,
	RunE:         hostShow,
	SilenceUsage: true,
}

var hostListCmd = &cli.Command{
	Use:          "list",
	Short:        "List all hosts.",
	Long:         `List all hosts.`,
	RunE:         hostList,
	SilenceUsage: true,
}

var hostRemoveCmd = &cli.Command{
	Use:          "remove [hostname|hostip]",
	Short:        "Remove a host.",
	Long:         `Remove a host.`,
	RunE:         hostRemove,
	SilenceUsage: true,
}

func hostAdd(cmd *cli.Command, args []string) error {
	if len(args) < 3 || len(args) > 4 {
		return util.UsageError(cmd,
			fmt.Sprintf("expected 3 or 4 arguments, saw %d: %s", len(args), args))
	}

	hostname := args[0]
	hostip := args[1]
	romanacidr := args[2]
	var agentport uint64
	if len(args) == 4 {
		var err error
		agentPortInt, err := strconv.Atoi(args[3])
		if err != nil {
			return util.UsageError(cmd,
				fmt.Sprintf("Agent Port number error, saw %s", args[3]))
		}
		agentport = uint64(agentPortInt)
	}

	client, err := common.NewRestClient(common.GetDefaultRestClientConfig(rootURL))
	if err != nil {
		return err
	}

	topologyURL, err := client.GetServiceUrl("topology")
	if err != nil {
		return err
	}

	index := common.IndexResponse{}
	err = client.Get(topologyURL, &index)
	if err != nil {
		return err
	}

	host := common.Host{
		Name:      hostname,
		Ip:        hostip,
		RomanaIp:  romanacidr,
		AgentPort: agentport,
	}
	fmt.Printf("Host (%v) added successfully.\n", host)

	data := common.Host{}
	err = client.Post(topologyURL+"/hosts", host, &data)
	if err != nil {
		fmt.Printf("Error adding host (%s).\n", hostname)
		return err
	}

	fmt.Printf("Host (%s) added successfully.\n", hostname)
	return nil
}

func hostShow(cmd *cli.Command, args []string) error {
	if len(args) < 1 {
		return util.UsageError(cmd,
			fmt.Sprintf("expected at-least 1 argument, saw none"))
	}

	client, err := common.NewRestClient(common.GetDefaultRestClientConfig(rootURL))
	if err != nil {
		return err
	}

	topologyURL, err := client.GetServiceUrl("topology")
	if err != nil {
		return err
	}

	index := common.IndexResponse{}
	err = client.Get(topologyURL, &index)
	if err != nil {
		return err
	}

	hostURL := index.Links.FindByRel("host-list")
	data := []common.Host{}
	hosts := []common.Host{}
	err = client.Get(hostURL, &data)
	if err != nil {
		return err
	}

	for _, h := range data {
		for _, n := range args {
			if h.Name == n {
				hosts = append(hosts, h)
			}
		}
	}

	if config.GetString("Format") == "json" {
		body, err := json.MarshalIndent(hosts, "", "\t")
		if err != nil {
			return err
		}
		fmt.Println(string(body))
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 0, '\t', 0)
		fmt.Println("Host List")
		fmt.Fprintln(w, "Id\t",
			"Host Name\t",
			"Host IP\t",
			"Romana CIDR\t",
			"Agent Port\t")
		for _, host := range hosts {
			fmt.Fprintln(w, host.ID, "\t",
				host.Name, "\t",
				host.Ip, "\t",
				host.RomanaIp, "\t",
				host.AgentPort, "\t")
		}
		w.Flush()
	}

	return nil
}

func hostList(cmd *cli.Command, args []string) error {
	client, err := common.NewRestClient(common.GetDefaultRestClientConfig(rootURL))
	if err != nil {
		return err
	}

	topologyURL, err := client.GetServiceUrl("topology")
	if err != nil {
		return err
	}

	index := common.IndexResponse{}
	err = client.Get(topologyURL, &index)
	if err != nil {
		return err
	}

	hostURL := index.Links.FindByRel("host-list")
	hosts := []common.Host{}
	err = client.Get(hostURL, &hosts)
	if err != nil {
		return err
	}

	if config.GetString("Format") == "json" {
		body, err := json.MarshalIndent(hosts, "", "\t")
		if err != nil {
			return err
		}
		fmt.Println(string(body))
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 0, '\t', 0)
		fmt.Println("Host List")
		fmt.Fprintln(w, "Id\t",
			"Host Name\t",
			"Host IP\t",
			"Romana CIDR\t",
			"Agent Port\t")
		for _, host := range hosts {
			fmt.Fprintln(w, host.ID, "\t",
				host.Name, "\t",
				host.Ip, "\t",
				host.RomanaIp, "\t",
				host.AgentPort, "\t")
		}
		w.Flush()
	}

	return nil
}

func hostRemove(cmd *cli.Command, args []string) error {
	fmt.Println("Unimplemented: Remove a host.")
	return nil
}
<commit_msg>romana: add support for making romana cidr optional.<commit_after>// Copyright (c) 2016 Pani Networks
// All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"strconv"
	"text/tabwriter"

	"github.com/romana/core/common"
	"github.com/romana/core/romana/util"

	cli "github.com/spf13/cobra"
	config "github.com/spf13/viper"
)

// hostCmd represents the host commands
var hostCmd = &cli.Command{
	Use:   "host [add|show|list|remove]",
	Short: "Add, Remove or Show hosts for romana services.",
	Long: `Add, Remove or Show hosts for romana services.

host requires a subcommand, e.g. ` + "`romana host add`." + `

For more information, please check http://romana.io
`,
}

func init() {
	hostCmd.AddCommand(hostAddCmd)
	hostCmd.AddCommand(hostShowCmd)
	hostCmd.AddCommand(hostListCmd)
	hostCmd.AddCommand(hostRemoveCmd)
}

var hostAddCmd = &cli.Command{
	Use:          "add [hostname][hostip][(optional)romana cidr][(optional)agent port]",
	Short:        "Add a new host.",
	Long:         `Add a new host.`,
	RunE:         hostAdd,
	SilenceUsage: true,
}

var hostShowCmd = &cli.Command{
	Use:          "show [hostname1][hostname2]...",
	Short:        "Show details for a specific host.",
	Long:         `Show details for a specific host.`,
	RunE:         hostShow,
	SilenceUsage: true,
}

var hostListCmd = &cli.Command{
	Use:          "list",
	Short:        "List all hosts.",
	Long:         `List all hosts.`,
	RunE:         hostList,
	SilenceUsage: true,
}

var hostRemoveCmd = &cli.Command{
	Use:          "remove [hostname|hostip]",
	Short:        "Remove a host.",
	Long:         `Remove a host.`,
	RunE:         hostRemove,
	SilenceUsage: true,
}

func hostAdd(cmd *cli.Command, args []string) error {
	if len(args) < 2 || len(args) > 4 {
		return util.UsageError(cmd,
			fmt.Sprintf("expected 2, 3 or 4 arguments, saw %d: %s", len(args), args))
	}

	hostname := args[0]
	hostip := args[1]
	var romanacidr string
	if len(args) >= 3 {
		romanacidr = args[2]
	}
	var agentport uint64
	if len(args) == 4 {
		var err error
		agentPortInt, err := strconv.Atoi(args[3])
		if err != nil {
			return util.UsageError(cmd,
				fmt.Sprintf("Agent Port number error, saw %s", args[3]))
		}
		agentport = uint64(agentPortInt)
	}

	client, err := common.NewRestClient(common.GetDefaultRestClientConfig(rootURL))
	if err != nil {
		return err
	}

	topologyURL, err := client.GetServiceUrl("topology")
	if err != nil {
		return err
	}

	index := common.IndexResponse{}
	err = client.Get(topologyURL, &index)
	if err != nil {
		return err
	}

	host := common.Host{
		Name:      hostname,
		Ip:        hostip,
		RomanaIp:  romanacidr,
		AgentPort: agentport,
	}
	fmt.Printf("Host (%v) added successfully.\n", host)

	data := common.Host{}
	err = client.Post(topologyURL+"/hosts", host, &data)
	if err != nil {
		fmt.Printf("Error adding host (%s).\n", hostname)
		return err
	}

	fmt.Printf("Host (%s) added successfully.\n", hostname)
	return nil
}

func hostShow(cmd *cli.Command, args []string) error {
	if len(args) < 1 {
		return util.UsageError(cmd,
			fmt.Sprintf("expected at-least 1 argument, saw none"))
	}

	client, err := common.NewRestClient(common.GetDefaultRestClientConfig(rootURL))
	if err != nil {
		return err
	}

	topologyURL, err := client.GetServiceUrl("topology")
	if err != nil {
		return err
	}

	index := common.IndexResponse{}
	err = client.Get(topologyURL, &index)
	if err != nil {
		return err
	}

	hostURL := index.Links.FindByRel("host-list")
	data := []common.Host{}
	hosts := []common.Host{}
	err = client.Get(hostURL, &data)
	if err != nil {
		return err
	}

	for _, h := range data {
		for _, n := range args {
			if h.Name == n {
				hosts = append(hosts, h)
			}
		}
	}

	if config.GetString("Format") == "json" {
		body, err := json.MarshalIndent(hosts, "", "\t")
		if err != nil {
			return err
		}
		fmt.Println(string(body))
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 0, '\t', 0)
		fmt.Println("Host List")
		fmt.Fprintln(w, "Id\t",
			"Host Name\t",
			"Host IP\t",
			"Romana CIDR\t",
			"Agent Port\t")
		for _, host := range hosts {
			fmt.Fprintln(w, host.ID, "\t",
				host.Name, "\t",
				host.Ip, "\t",
				host.RomanaIp, "\t",
				host.AgentPort, "\t")
		}
		w.Flush()
	}

	return nil
}

func hostList(cmd *cli.Command, args []string) error {
	client, err := common.NewRestClient(common.GetDefaultRestClientConfig(rootURL))
	if err != nil {
		return err
	}

	topologyURL, err := client.GetServiceUrl("topology")
	if err != nil {
		return err
	}

	index := common.IndexResponse{}
	err = client.Get(topologyURL, &index)
	if err != nil {
		return err
	}

	hostURL := index.Links.FindByRel("host-list")
	hosts := []common.Host{}
	err = client.Get(hostURL, &hosts)
	if err != nil {
		return err
	}

	if config.GetString("Format") == "json" {
		body, err := json.MarshalIndent(hosts, "", "\t")
		if err != nil {
			return err
		}
		fmt.Println(string(body))
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 0, '\t', 0)
		fmt.Println("Host List")
		fmt.Fprintln(w, "Id\t",
			"Host Name\t",
			"Host IP\t",
			"Romana CIDR\t",
			"Agent Port\t")
		for _, host := range hosts {
			fmt.Fprintln(w, host.ID, "\t",
				host.Name, "\t",
				host.Ip, "\t",
				host.RomanaIp, "\t",
				host.AgentPort, "\t")
		}
		w.Flush()
	}

	return nil
}

func hostRemove(cmd *cli.Command, args []string) error {
	fmt.Println("Unimplemented: Remove a host.")
	return nil
}
<|endoftext|>