<commit_before>// Copyright 2014 Rob Bassi. 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 (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"log"
	"math"
	"math/rand"
	"net"
	"net/url"
	"strconv"
	"time"
)

const (
	connectMinResponseLength = 16
	announceMinResponseLength = 20
	connectBufferSize = 150
	announceBufferSize = 20000
)

type UdpTracker struct {
	*tracker
	connectionId  uint64
	transactionId uint32
	serverAddr    *net.UDPAddr
	conn          *net.UDPConn
}

type connectRequest struct {
	connectionId  uint64
	action        uint32
	transactionId uint32
}

type connectResponse struct {
	action        uint32
	transactionId uint32
	connectionId  uint64
}

type errorResponse struct {
	action        uint32
	transactionId uint32
	message       string
}

type announceRequest struct {
	connectionId  uint64
	action        uint32
	transactionId uint32
	infoHash      []byte
	peerId        []byte
	downloaded    uint64
	left          uint64
	uploaded      uint64
	event         uint32
	ipAddr        uint32
	key           uint32
	numWant       int32
	port          uint16
}

type announceResponse struct {
	action        uint32
	transactionId uint32
	interval      uint32
	leechers      uint32
	seeders       uint32
	peers         []PeerTuple
}

func (r *connectRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r.connectionId)
	err = binary.Write(buf, binary.BigEndian, r.action)
	err = binary.Write(buf, binary.BigEndian, r.transactionId)
	return buf.Bytes(), err
}

func (r *connectResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data[4:])
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(buf, binary.BigEndian, &r.transactionId)
	err = binary.Read(buf, binary.BigEndian, &r.connectionId)
	return err
}

func (r *connectResponse) MarshalBinary() ([]byte, error) {
	var b bytes.Buffer
	fmt.Fprintln(&b, r.connectionId, r.action, r.transactionId)
	return b.Bytes(), nil
}

func (r *errorResponse) UnmarshalBinary(data []byte) error {
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(bytes.NewReader(data[4:]), binary.BigEndian, &r.transactionId)
	r.message = string(data[8:])
	return err
}

func (r *announceRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r.connectionId)
	err = binary.Write(buf, binary.BigEndian, r.action)
	err = binary.Write(buf, binary.BigEndian, r.transactionId)
	err = binary.Write(buf, binary.BigEndian, r.infoHash)
	err = binary.Write(buf, binary.BigEndian, r.peerId)
	err = binary.Write(buf, binary.BigEndian, r.downloaded)
	err = binary.Write(buf, binary.BigEndian, r.left)
	err = binary.Write(buf, binary.BigEndian, r.uploaded)
	err = binary.Write(buf, binary.BigEndian, r.event)
	err = binary.Write(buf, binary.BigEndian, r.ipAddr)
	err = binary.Write(buf, binary.BigEndian, r.key)
	err = binary.Write(buf, binary.BigEndian, r.numWant)
	err = binary.Write(buf, binary.BigEndian, r.port)
	return buf.Bytes(), err
}

func (r *announceResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data[4:])
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(buf, binary.BigEndian, &r.transactionId)
	err = binary.Read(buf, binary.BigEndian, &r.interval)
	err = binary.Read(buf, binary.BigEndian, &r.leechers)
	err = binary.Read(buf, binary.BigEndian, &r.seeders)

	if len(data) > announceMinResponseLength {
		peerBytes := data[announceMinResponseLength:]
		peers := len(peerBytes) / 6

		ipLen := 4
		portLen := 2
		for i := 0; i < peers; i++ {
			ipOffset := i*6
			portOffset := ipOffset + ipLen
			peer := PeerTuple{}
			ipBytes := peerBytes[ipOffset : ipOffset + ipLen]
			peer.IP = net.IPv4(ipBytes[0], ipBytes[1], ipBytes[2], ipBytes[3])
			binary.Read(bytes.NewReader(peerBytes[portOffset : portOffset + portLen]), binary.BigEndian, &peer.Port)
			r.peers = append(r.peers, peer)
		}
	}

	return err
}

func NewUdpTracker(key string, chans trackerPeerChans, port uint16, infoHash []byte, announce *url.URL) *UdpTracker {
	return &UdpTracker{&tracker{key: key, peerChans: chans, port: port, infoHash: infoHash, announceURL: announce}, 0, 0, &net.UDPAddr{}, &net.UDPConn{}}
}

func (tr *UdpTracker) Announce(event int) {
	err := tr.connect()
	if err != nil {
		log.Printf("Tracker : Could not connect to tracker %s: \n", tr.announceURL.String(), err)
		return
	}

	key, _ := strconv.ParseUint(tr.key, 16, 4)
	announce := &announceRequest{
		connectionId:  tr.connectionId,
		action:        1,
		transactionId: tr.transactionId,
		infoHash:      tr.infoHash,
		peerId:        PeerID[:],
		downloaded:    uint64(tr.stats.Downloaded),
		left:          uint64(tr.stats.Left),
		uploaded:      uint64(tr.stats.Uploaded),
		event:         uint32(event),
		ipAddr:        0,
		key:           uint32(key),
		numWant:       -1,
		port:          6881,
	}
	announceBytes, _ := announce.MarshalBinary()

	buff := make([]byte, announceBufferSize)
	length := tr.request(announceBytes, buff)

	if length >= announceMinResponseLength {
		var response announceResponse
		response.UnmarshalBinary(buff[:length])

		if event != Stopped {
			if response.interval != 0 {
				nextAnnounce := time.Second * time.Duration(response.interval)
				log.Println("Tracker : Announce : Scheduling next announce in", nextAnnounce)
				tr.timer = time.After(nextAnnounce)
			}

			for _, peer := range response.peers {
				tr.peerChans.peers <- peer
			}
		}
	}
}

func (tr *UdpTracker) connect() error {
	log.Printf("Tracker : Connect (%v)", tr.announceURL)

	connectReq := connectRequest{connectionId: 0x41727101980, action: 0, transactionId: tr.transactionId}
	connectBytes, _ := connectReq.MarshalBinary()

	buff := make([]byte, connectBufferSize)
	length := tr.request(connectBytes, buff)

	if length >= connectMinResponseLength {
		var response connectResponse
		response.UnmarshalBinary(buff[:length])

		if response.action == 3 || tr.transactionId != response.transactionId {
			error := errorResponse{}
			error.UnmarshalBinary(buff)
			return errors.New("Response Error: " + error.message)
		}

		tr.connectionId = response.connectionId
		return nil
	}

	return errors.New("Invalid connect response length")
}

func (tr *UdpTracker) Run() {
	log.Printf("Tracker : Run : Started (%s)\n", tr.announceURL)
	defer log.Printf("Tracker : Run : Completed (%s)\n", tr.announceURL)

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

	serverAddr, err := net.ResolveUDPAddr("udp", tr.announceURL.Host)
	if err != nil {
		log.Println("Tracker : Could not resolve tracker host!")
		return
	}

	conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 0})
	if err != nil {
		log.Println("Tracker : Could not bind to any port")
		return
	}

	tr.transactionId = rand.Uint32()
	tr.serverAddr = serverAddr
	tr.conn = conn
	tr.Announce(Started)

	for {
		select {
		case <-tr.quit:
			log.Println("Tracker : Stop : Stopping")
			tr.Announce(Stopped)
			return
		case <-tr.completedCh:
			go tr.Announce(Completed)
		case <-tr.timer:
			log.Printf("Tracker : Run : Interval Timer Expired (%s)\n", tr.announceURL)
			go tr.Announce(Interval)
		case stats := <-tr.peerChans.stats:
			log.Println("read from stats", stats)
		}
	}
}

// Send a udp packet to the tracker, fill in the dest buffer with the response
func (tr *UdpTracker) request(payload []byte, dest []byte) int {
	n := 0
	totalAttempts := 0

	// notify the sender when a response is recieved
	recvChan := make(chan bool)

	// keep sending the packet and wait for the specifed time
	// before trying again, limit n to 8
	go func() {

		// initial send
		tr.conn.WriteTo(payload, tr.serverAddr)
	Listen:
		for {
			// timeout: 15 * 2 ^ n (0-8)
			timeout := time.Second * time.Duration(15 * int(math.Pow(2.0, float64(n))))
			timer := time.After(timeout)
			select {
			case <-recvChan:
				break Listen
			case <-timer:
				tr.conn.WriteTo(payload, tr.serverAddr)
				totalAttempts++
				if n < 8 { n++ }
			}
		}
	}()

	// read until some data is received
	var err error
	length := 0
	for length == 0 {
		length, _, err = tr.conn.ReadFrom(dest)

		// not sure what to do here?
		if err != nil {
			log.Println("Tracker : udp packet read error")
		}
	}

	recvChan <- true
	return length
}
<commit_msg>Refactored binary.Read/Writes for better error handling<commit_after>// Copyright 2014 Rob Bassi. 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 (
	"bytes"
	"encoding/binary"
	"errors"
	"log"
	"math"
	"math/rand"
	"net"
	"net/url"
	"strconv"
	"time"
)

const (
	connectMinResponseLength = 16
	announceMinResponseLength = 20
	connectBufferSize = 150
	announceBufferSize = 20000
)

type UdpTracker struct {
	*tracker
	ConnectionId  uint64
	TransactionId uint32
	ServerAddr    *net.UDPAddr
	Conn          *net.UDPConn
}

type connectRequest struct {
	ConnectionId  uint64
	Action        uint32
	TransactionId uint32
}

type connectResponse struct {
	Action        uint32
	TransactionId uint32
	ConnectionId  uint64
}

type errorResponse struct {
	Action        uint32
	TransactionId uint32
	Message       string
}

type announceRequest struct {
	ConnectionId  uint64
	Action        uint32
	TransactionId uint32
	InfoHash      [20]byte
	PeerId        [20]byte
	Downloaded    uint64
	Left          uint64
	Uploaded      uint64
	Event         uint32
	IpAddr        uint32
	Key           uint32
	NumWant       int32
	Port          uint16
}

type announceResponse struct {
	Action        uint32
	TransactionId uint32
	Interval      uint32
	Leechers      uint32
	Seeders       uint32
	Peers         []PeerTuple
}

func (r *connectRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r)
	return buf.Bytes(), err
}

func (r *connectResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data)
	err := binary.Read(buf, binary.BigEndian, r)
	return err
}

func (r *errorResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(append([]byte{0,0,0,data[0]}, data[4:]...))
	err := binary.Read(buf, binary.BigEndian, &r.Action)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.TransactionId)
	if err != nil {
		return err
	}

	r.Message = string(data[8:])
	return err
}

func (r *announceRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r)
	return buf.Bytes(), err
}

func (r *announceResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data)

	err := binary.Read(buf, binary.BigEndian, &r.Action)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.TransactionId)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Interval)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Leechers)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Seeders)
	if err != nil {
		return err
	}

	if len(data) > announceMinResponseLength {
		peerBytes := bytes.NewReader(data[announceMinResponseLength:])
		peers := (len(data) - announceMinResponseLength) / 6

		for i := 0; i < peers; i++ {
			var peer PeerTuple
			var ipBuf [4]byte

			err = binary.Read(peerBytes, binary.BigEndian, &ipBuf)
			if err != nil {
				return err
			}

			err = binary.Read(peerBytes, binary.BigEndian, &peer.Port)
			if err != nil {
				return err
			}

			peer.IP = net.IPv4(ipBuf[0], ipBuf[1], ipBuf[2], ipBuf[3])
			r.Peers = append(r.Peers, peer)
		}
	}
	return nil
}

func NewUdpTracker(key string, chans trackerPeerChans, port uint16, infoHash []byte, announce *url.URL) *UdpTracker {
	return &UdpTracker{&tracker{key: key, peerChans: chans, port: port, infoHash: infoHash, announceURL: announce}, 0, 0, &net.UDPAddr{}, &net.UDPConn{}}
}

func (tr *UdpTracker) Announce(event int) {
	err := tr.connect()
	if err != nil {
		log.Printf("Tracker : Could not connect to tracker %s: \n", tr.announceURL.String(), err)
		return
	}

	key, _ := strconv.ParseUint(tr.key, 16, 4)
	announce := &announceRequest{
		ConnectionId:  tr.ConnectionId,
		Action:        1,
		TransactionId: tr.TransactionId,
		PeerId: PeerID,
		Downloaded:    uint64(tr.stats.Downloaded),
		Left:          uint64(tr.stats.Left),
		Uploaded:      uint64(tr.stats.Uploaded),
		Event:         uint32(event),
		IpAddr:        0,
		Key:           uint32(key),
		NumWant:       -1,
		Port:          6881,
	}
	copy(announce.InfoHash[:20], tr.infoHash)
	announceBytes, err := announce.MarshalBinary()
	if err != nil {
		log.Println("Tracker : Announce : Invalid response")
		return
	}

	buff := make([]byte, announceBufferSize)
	length := tr.request(announceBytes, buff)

	if length >= announceMinResponseLength {
		var response announceResponse
		response.UnmarshalBinary(buff[:length])

		if event != Stopped {
			if response.Interval != 0 {
				nextAnnounce := time.Second * time.Duration(response.Interval)
				log.Println("Tracker : Announce : Scheduling next announce in", nextAnnounce)
				tr.timer = time.After(nextAnnounce)
			}

			for _, peer := range response.Peers {
				tr.peerChans.peers <- peer
			}
		}
	}
}

func (tr *UdpTracker) connect() error {
	log.Printf("Tracker : Connect (%v)", tr.announceURL)

	connectReq := connectRequest{ConnectionId: 0x41727101980, Action: 0, TransactionId: tr.TransactionId}
	connectBytes, _ := connectReq.MarshalBinary()

	buff := make([]byte, connectBufferSize)
	length := tr.request(connectBytes, buff)

	if length >= connectMinResponseLength {
		var response connectResponse
		err := response.UnmarshalBinary(buff[:length])
		if err != nil {
			log.Println("Tracker : Connect : Invalid response")
			return err
		}

		if response.Action == 3 || tr.TransactionId != response.TransactionId {
			error := errorResponse{}
			err := error.UnmarshalBinary(buff)
			if err != nil {
				return errors.New(error.Message)
			} else {
				return err
			}
		}

		tr.ConnectionId = response.ConnectionId
		return nil
	}

	return errors.New("Invalid connect response length")
}

func (tr *UdpTracker) Run() {
	log.Printf("Tracker : Run : Started (%s)\n", tr.announceURL)
	defer log.Printf("Tracker : Run : Completed (%s)\n", tr.announceURL)

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

	serverAddr, err := net.ResolveUDPAddr("udp", tr.announceURL.Host)
	if err != nil {
		log.Println("Tracker : Could not resolve tracker host!")
		return
	}

	conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 0})
	if err != nil {
		log.Println("Tracker : Could not bind to any port")
		return
	}

	tr.TransactionId = rand.Uint32()
	tr.ServerAddr = serverAddr
	tr.Conn = conn
	tr.Announce(Started)

	for {
		select {
		case <-tr.quit:
			log.Println("Tracker : Stop : Stopping")
			tr.Announce(Stopped)
			return
		case <-tr.completedCh:
			go tr.Announce(Completed)
		case <-tr.timer:
			log.Printf("Tracker : Run : Interval Timer Expired (%s)\n", tr.announceURL)
			go tr.Announce(Interval)
		case stats := <-tr.peerChans.stats:
			log.Println("read from stats", stats)
		}
	}
}

// Send a udp packet to the tracker, fill in the dest buffer with the response
func (tr *UdpTracker) request(payload []byte, dest []byte) int {
	n := 0
	totalAttempts := 0

	// notify the sender when a response is recieved
	recvChan := make(chan bool)

	// keep sending the packet and wait for the specifed time
	// before trying again, limit n to 8
	go func() {

		// initial send
		tr.Conn.WriteTo(payload, tr.ServerAddr)
	Listen:
		for {
			// timeout: 15 * 2 ^ n (0-8)
			timeout := time.Second * time.Duration(15 * int(math.Pow(2.0, float64(n))))
			timer := time.After(timeout)
			select {
			case <-recvChan:
				break Listen
			case <-timer:
				tr.Conn.WriteTo(payload, tr.ServerAddr)
				totalAttempts++
				if n < 8 { n++ }
			}
		}
	}()

	// read until some data is received
	var err error
	length := 0
	for length == 0 {
		length, _, err = tr.Conn.ReadFrom(dest)

		// not sure what to do here?
		if err != nil {
			log.Println("Tracker : udp packet read error")
		}
	}

	recvChan <- true
	return length
}
<|endoftext|>


<commit_before>package swarm

import (
	"fmt"
	"sort"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/units"
	"github.com/docker/swarm/cluster"
	"github.com/docker/swarm/discovery"
	"github.com/docker/swarm/scheduler"
	"github.com/docker/swarm/scheduler/node"
	"github.com/docker/swarm/state"
	"github.com/samalba/dockerclient"
)

// Cluster is exported
type Cluster struct {
	sync.RWMutex

	eventHandler cluster.EventHandler
	engines      map[string]*cluster.Engine
	scheduler    *scheduler.Scheduler
	options      *cluster.Options
	store        *state.Store
}

// NewCluster is exported
func NewCluster(scheduler *scheduler.Scheduler, store *state.Store, eventhandler cluster.EventHandler, options *cluster.Options) cluster.Cluster {
	log.WithFields(log.Fields{"name": "swarm"}).Debug("Initializing cluster")

	cluster := &Cluster{
		eventHandler: eventhandler,
		engines:      make(map[string]*cluster.Engine),
		scheduler:    scheduler,
		options:      options,
		store:        store,
	}

	// get the list of entries from the discovery service
	go func() {
		d, err := discovery.New(options.Discovery, options.Heartbeat)
		if err != nil {
			log.Fatal(err)
		}

		entries, err := d.Fetch()
		if err != nil {
			log.Fatal(err)

		}
		cluster.newEntries(entries)

		go d.Watch(cluster.newEntries)
	}()

	return cluster
}

// Handle callbacks for the events
func (c *Cluster) Handle(e *cluster.Event) error {
	if err := c.eventHandler.Handle(e); err != nil {
		log.Error(err)
	}
	return nil
}

// CreateContainer aka schedule a brand new container into the cluster.
func (c *Cluster) CreateContainer(config *dockerclient.ContainerConfig, name string) (*cluster.Container, error) {
	c.scheduler.Lock()
	defer c.scheduler.Unlock()

	n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config)
	if err != nil {
		return nil, err
	}

	if nn, ok := c.engines[n.ID]; ok {
		container, err := nn.Create(config, name, true)
		if err != nil {
			return nil, err
		}

		st := &state.RequestedState{
			ID:     container.Id,
			Name:   name,
			Config: config,
		}
		return container, c.store.Add(container.Id, st)
	}

	return nil, nil
}

// RemoveContainer aka Remove a container from the cluster. Containers should
// always be destroyed through the scheduler to guarantee atomicity.
func (c *Cluster) RemoveContainer(container *cluster.Container, force bool) error {
	c.scheduler.Lock()
	defer c.scheduler.Unlock()

	if err := container.Engine.Destroy(container, force); err != nil {
		return err
	}

	if err := c.store.Remove(container.Id); err != nil {
		if err == state.ErrNotFound {
			log.Debugf("Container %s not found in the store", container.Id)
			return nil
		}
		return err
	}
	return nil
}

// Entries are Docker Engines
func (c *Cluster) newEntries(entries []*discovery.Entry) {
	for _, entry := range entries {
		go func(m *discovery.Entry) {
			if !c.hasEngine(m.String()) {
				engine := cluster.NewEngine(m.String(), c.options.OvercommitRatio)
				if err := engine.Connect(c.options.TLSConfig); err != nil {
					log.Error(err)
					return
				}
				c.Lock()

				if old, exists := c.engines[engine.ID]; exists {
					c.Unlock()
					if old.IP != engine.IP {
						log.Errorf("ID duplicated. %s shared by %s and %s", engine.ID, old.IP, engine.IP)
					} else {
						log.Errorf("node %q is already registered", engine.ID)
					}
					return
				}
				c.engines[engine.ID] = engine
				if err := engine.Events(c); err != nil {
					log.Error(err)
					c.Unlock()
					return
				}
				c.Unlock()

			}
		}(entry)
	}
}

func (c *Cluster) hasEngine(addr string) bool {
	for _, engine := range c.engines {
		if engine.Addr == addr {
			return true
		}
	}
	return false
}

// Images returns all the images in the cluster.
func (c *Cluster) Images() []*cluster.Image {
	c.RLock()
	defer c.RUnlock()

	out := []*cluster.Image{}
	for _, n := range c.engines {
		out = append(out, n.Images()...)
	}

	return out
}

// Image returns an image with IDOrName in the cluster
func (c *Cluster) Image(IDOrName string) *cluster.Image {
	// Abort immediately if the name is empty.
	if len(IDOrName) == 0 {
		return nil
	}

	c.RLock()
	defer c.RUnlock()
	for _, n := range c.engines {
		if image := n.Image(IDOrName); image != nil {
			return image
		}
	}

	return nil
}

// RemoveImage removes an image from the cluster
func (c *Cluster) RemoveImage(image *cluster.Image) ([]*dockerclient.ImageDelete, error) {
	c.Lock()
	defer c.Unlock()
	return image.Engine.RemoveImage(image)
}

// Pull is exported
func (c *Cluster) Pull(name string, callback func(what, status string)) {
	size := len(c.engines)
	done := make(chan bool, size)
	for _, n := range c.engines {
		go func(nn *cluster.Engine) {
			if callback != nil {
				callback(nn.Name, "")
			}
			err := nn.Pull(name)
			if callback != nil {
				if err != nil {
					callback(nn.Name, err.Error())
				} else {
					callback(nn.Name, "downloaded")
				}
			}
			done <- true
		}(n)
	}
	for i := 0; i < size; i++ {
		<-done
	}
}

// Containers returns all the containers in the cluster.
func (c *Cluster) Containers() []*cluster.Container {
	c.RLock()
	defer c.RUnlock()

	out := []*cluster.Container{}
	for _, n := range c.engines {
		out = append(out, n.Containers()...)
	}

	return out
}

// Container returns the container with IDOrName in the cluster
func (c *Cluster) Container(IDOrName string) *cluster.Container {
	// Abort immediately if the name is empty.
	if len(IDOrName) == 0 {
		return nil
	}

	c.RLock()
	defer c.RUnlock()
	for _, n := range c.engines {
		if container := n.Container(IDOrName); container != nil {
			return container
		}
	}

	return nil
}

// listNodes returns all the engines in the cluster.
func (c *Cluster) listNodes() []*node.Node {
	c.RLock()
	defer c.RUnlock()

	out := []*node.Node{}
	for _, n := range c.engines {
		out = append(out, node.NewNode(n))
	}

	return out
}

// listEngines returns all the engines in the cluster.
func (c *Cluster) listEngines() []*cluster.Engine {
	c.RLock()
	defer c.RUnlock()

	out := []*cluster.Engine{}
	for _, n := range c.engines {
		out = append(out, n)
	}
	return out
}

// Info is exported
func (c *Cluster) Info() [][2]string {
	info := [][2]string{
		{"\bStrategy", c.scheduler.Strategy()},
		{"\bFilters", c.scheduler.Filters()},
		{"\bNodes", fmt.Sprintf("%d", len(c.engines))},
	}

	engines := c.listEngines()
	sort.Sort(cluster.EngineSorter(engines))

	for _, engine := range engines {
		info = append(info, [2]string{engine.Name, engine.Addr})
		info = append(info, [2]string{" └ Containers", fmt.Sprintf("%d", len(engine.Containers()))})
		info = append(info, [2]string{" └ Reserved CPUs", fmt.Sprintf("%d / %d", engine.UsedCpus(), engine.TotalCpus())})
		info = append(info, [2]string{" └ Reserved Memory", fmt.Sprintf("%s / %s", units.BytesSize(float64(engine.UsedMemory())), units.BytesSize(float64(engine.TotalMemory())))})
	}

	return info
}
<commit_msg>Improve Error message to show node's Name and IP<commit_after>package swarm

import (
	"fmt"
	"sort"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/units"
	"github.com/docker/swarm/cluster"
	"github.com/docker/swarm/discovery"
	"github.com/docker/swarm/scheduler"
	"github.com/docker/swarm/scheduler/node"
	"github.com/docker/swarm/state"
	"github.com/samalba/dockerclient"
)

// Cluster is exported
type Cluster struct {
	sync.RWMutex

	eventHandler cluster.EventHandler
	engines      map[string]*cluster.Engine
	scheduler    *scheduler.Scheduler
	options      *cluster.Options
	store        *state.Store
}

// NewCluster is exported
func NewCluster(scheduler *scheduler.Scheduler, store *state.Store, eventhandler cluster.EventHandler, options *cluster.Options) cluster.Cluster {
	log.WithFields(log.Fields{"name": "swarm"}).Debug("Initializing cluster")

	cluster := &Cluster{
		eventHandler: eventhandler,
		engines:      make(map[string]*cluster.Engine),
		scheduler:    scheduler,
		options:      options,
		store:        store,
	}

	// get the list of entries from the discovery service
	go func() {
		d, err := discovery.New(options.Discovery, options.Heartbeat)
		if err != nil {
			log.Fatal(err)
		}

		entries, err := d.Fetch()
		if err != nil {
			log.Fatal(err)

		}
		cluster.newEntries(entries)

		go d.Watch(cluster.newEntries)
	}()

	return cluster
}

// Handle callbacks for the events
func (c *Cluster) Handle(e *cluster.Event) error {
	if err := c.eventHandler.Handle(e); err != nil {
		log.Error(err)
	}
	return nil
}

// CreateContainer aka schedule a brand new container into the cluster.
func (c *Cluster) CreateContainer(config *dockerclient.ContainerConfig, name string) (*cluster.Container, error) {
	c.scheduler.Lock()
	defer c.scheduler.Unlock()

	n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config)
	if err != nil {
		return nil, err
	}

	if nn, ok := c.engines[n.ID]; ok {
		container, err := nn.Create(config, name, true)
		if err != nil {
			return nil, err
		}

		st := &state.RequestedState{
			ID:     container.Id,
			Name:   name,
			Config: config,
		}
		return container, c.store.Add(container.Id, st)
	}

	return nil, nil
}

// RemoveContainer aka Remove a container from the cluster. Containers should
// always be destroyed through the scheduler to guarantee atomicity.
func (c *Cluster) RemoveContainer(container *cluster.Container, force bool) error {
	c.scheduler.Lock()
	defer c.scheduler.Unlock()

	if err := container.Engine.Destroy(container, force); err != nil {
		return err
	}

	if err := c.store.Remove(container.Id); err != nil {
		if err == state.ErrNotFound {
			log.Debugf("Container %s not found in the store", container.Id)
			return nil
		}
		return err
	}
	return nil
}

// Entries are Docker Engines
func (c *Cluster) newEntries(entries []*discovery.Entry) {
	for _, entry := range entries {
		go func(m *discovery.Entry) {
			if !c.hasEngine(m.String()) {
				engine := cluster.NewEngine(m.String(), c.options.OvercommitRatio)
				if err := engine.Connect(c.options.TLSConfig); err != nil {
					log.Error(err)
					return
				}
				c.Lock()

				if old, exists := c.engines[engine.ID]; exists {
					c.Unlock()
					if old.IP != engine.IP {
						log.Errorf("ID duplicated. %s shared by %s and %s", engine.ID, old.IP, engine.IP)
					} else {
						log.Errorf("node %q with IP %q is  already registered", engine.Name, engine.IP)
					}
					return
				}
				c.engines[engine.ID] = engine
				if err := engine.Events(c); err != nil {
					log.Error(err)
					c.Unlock()
					return
				}
				c.Unlock()

			}
		}(entry)
	}
}

func (c *Cluster) hasEngine(addr string) bool {
	for _, engine := range c.engines {
		if engine.Addr == addr {
			return true
		}
	}
	return false
}

// Images returns all the images in the cluster.
func (c *Cluster) Images() []*cluster.Image {
	c.RLock()
	defer c.RUnlock()

	out := []*cluster.Image{}
	for _, n := range c.engines {
		out = append(out, n.Images()...)
	}

	return out
}

// Image returns an image with IDOrName in the cluster
func (c *Cluster) Image(IDOrName string) *cluster.Image {
	// Abort immediately if the name is empty.
	if len(IDOrName) == 0 {
		return nil
	}

	c.RLock()
	defer c.RUnlock()
	for _, n := range c.engines {
		if image := n.Image(IDOrName); image != nil {
			return image
		}
	}

	return nil
}

// RemoveImage removes an image from the cluster
func (c *Cluster) RemoveImage(image *cluster.Image) ([]*dockerclient.ImageDelete, error) {
	c.Lock()
	defer c.Unlock()
	return image.Engine.RemoveImage(image)
}

// Pull is exported
func (c *Cluster) Pull(name string, callback func(what, status string)) {
	size := len(c.engines)
	done := make(chan bool, size)
	for _, n := range c.engines {
		go func(nn *cluster.Engine) {
			if callback != nil {
				callback(nn.Name, "")
			}
			err := nn.Pull(name)
			if callback != nil {
				if err != nil {
					callback(nn.Name, err.Error())
				} else {
					callback(nn.Name, "downloaded")
				}
			}
			done <- true
		}(n)
	}
	for i := 0; i < size; i++ {
		<-done
	}
}

// Containers returns all the containers in the cluster.
func (c *Cluster) Containers() []*cluster.Container {
	c.RLock()
	defer c.RUnlock()

	out := []*cluster.Container{}
	for _, n := range c.engines {
		out = append(out, n.Containers()...)
	}

	return out
}

// Container returns the container with IDOrName in the cluster
func (c *Cluster) Container(IDOrName string) *cluster.Container {
	// Abort immediately if the name is empty.
	if len(IDOrName) == 0 {
		return nil
	}

	c.RLock()
	defer c.RUnlock()
	for _, n := range c.engines {
		if container := n.Container(IDOrName); container != nil {
			return container
		}
	}

	return nil
}

// listNodes returns all the engines in the cluster.
func (c *Cluster) listNodes() []*node.Node {
	c.RLock()
	defer c.RUnlock()

	out := []*node.Node{}
	for _, n := range c.engines {
		out = append(out, node.NewNode(n))
	}

	return out
}

// listEngines returns all the engines in the cluster.
func (c *Cluster) listEngines() []*cluster.Engine {
	c.RLock()
	defer c.RUnlock()

	out := []*cluster.Engine{}
	for _, n := range c.engines {
		out = append(out, n)
	}
	return out
}

// Info is exported
func (c *Cluster) Info() [][2]string {
	info := [][2]string{
		{"\bStrategy", c.scheduler.Strategy()},
		{"\bFilters", c.scheduler.Filters()},
		{"\bNodes", fmt.Sprintf("%d", len(c.engines))},
	}

	engines := c.listEngines()
	sort.Sort(cluster.EngineSorter(engines))

	for _, engine := range engines {
		info = append(info, [2]string{engine.Name, engine.Addr})
		info = append(info, [2]string{" └ Containers", fmt.Sprintf("%d", len(engine.Containers()))})
		info = append(info, [2]string{" └ Reserved CPUs", fmt.Sprintf("%d / %d", engine.UsedCpus(), engine.TotalCpus())})
		info = append(info, [2]string{" └ Reserved Memory", fmt.Sprintf("%s / %s", units.BytesSize(float64(engine.UsedMemory())), units.BytesSize(float64(engine.TotalMemory())))})
	}

	return info
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom_test

import (
	. "github.com/FactomProject/factom"
	"testing"

	"encoding/json"
	"time"
)

//func TestNewTransaction(t *testing.T) {
//	if err := NewTransaction("b"); err != nil {
//		t.Error(err)
//	}
//	if txs, err := ListTransactions(); err != nil {
//		t.Error(err)
//	} else {
//		for _, v := range txs {
//			t.Log(v)
//		}
//	}
//}

func TestJSONTransactions(t *testing.T) {
	tx1 := mkdummytx()
	t.Log("Transaction:", tx1)
	p, err := json.Marshal(tx1)
	if err != nil {
		t.Error(err)
	}
	t.Log("JSON transaction:", string(p))

	tx2 := new(Transaction)
	if err := json.Unmarshal(p, tx2); err != nil {
		t.Error(err)
	}
	t.Log("Unmarshaled:", tx2)
}

func mkdummytx() *Transaction {
	tx := &Transaction{
		BlockHeight: 42,
		Name:        "dummy",
		Timestamp: func() time.Time {
			t, _ := time.Parse("2006-Jan-02 15:04", "1988-Jan-02 10:00")
			return t
		}(),
		TotalInputs:    13,
		TotalOutputs:   12,
		TotalECOutputs: 1,
	}
	return tx
}
<commit_msg>added transaction tests and dummy wallet api<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom_test

import (
	. "github.com/FactomProject/factom"
	"testing"

	"encoding/json"
	"time"

	"github.com/FactomProject/factom/wallet"
	"github.com/FactomProject/factom/wallet/wsapi"
)

//func TestNewTransaction(t *testing.T) {
//	if err := NewTransaction("b"); err != nil {
//		t.Error(err)
//	}
//	if txs, err := ListTransactions(); err != nil {
//		t.Error(err)
//	} else {
//		for _, v := range txs {
//			t.Log(v)
//		}
//	}
//}

func TestJSONTransactions(t *testing.T) {
	tx1 := mkdummytx()
	t.Log("Transaction:", tx1)
	p, err := json.Marshal(tx1)
	if err != nil {
		t.Error(err)
	}
	t.Log("JSON transaction:", string(p))

	tx2 := new(Transaction)
	if err := json.Unmarshal(p, tx2); err != nil {
		t.Error(err)
	}
	t.Log("Unmarshaled:", tx2)
}

func mkdummytx() *Transaction {
	tx := &Transaction{
		BlockHeight: 42,
		Name:        "dummy",
		Timestamp: func() time.Time {
			t, _ := time.Parse("2006-Jan-02 15:04", "1988-Jan-02 10:00")
			return t
		}(),
		TotalInputs:    13,
		TotalOutputs:   12,
		TotalECOutputs: 1,
	}
	return tx
}

func TestTransactions(t *testing.T) {
	fctWallet, err := wallet.NewOrOpenBoltDBWallet("/tmp/testingwallet.bolt")
	if err != nil {
		t.Error(err)
	}

	txdb, err := wallet.NewTXBoltDB("/tmp/testingtxdb.bolt")
	if err != nil {
		t.Error(err)
	} else {
		fctWallet.AddTXDB(txdb)
	}

	 RpcConfig = &RPCConfig{
		WalletTLSEnable:   false,
		WalletTLSKeyFile:  "",
		WalletTLSCertFile: "",
		WalletRPCUser:     "",
		WalletRPCPassword: "",
		WalletServer:      "localhost:8089",
	}

	go wsapi.Start(fctWallet, ":8089", *RpcConfig)
	defer wsapi.Stop()

	if txs, err := ListTransactionsTmp(); err != nil {
		t.Error(err)
	} else if len(txs) > 0 {
		t.Error("Unexpected transactions returned from the wallet:", txs)
	}

	tx1, err := NewTransaction("tx1")
	if err != nil {
		t.Error(err)
	}
	if tx1 == nil {
		t.Error("No transaction was returned")
	}

	if txs, err := ListTransactionsTmp(); err != nil {
		t.Error(err)
	} else if len(txs) < 1 {
		t.Error("Temporary transaction was not saved in the wallet")
	}
}
<|endoftext|>


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

package cmd

import (
	"fmt"
	"net/url"
	"sort"
	"strings"
	"time"

	humanize "github.com/dustin/go-humanize"
	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/mc/pkg/colorjson"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/minio/pkg/madmin"
)

var adminInfoServer = cli.Command{
	Name:   "server",
	Usage:  "display MinIO server information",
	Action: mainAdminServerInfo,
	Before: setGlobalsFromContext,
	Flags:  globalFlags,
	CustomHelpTemplate: `NAME:
  {{.HelpName}} - {{.Usage}}

USAGE:
  {{.HelpName}} TARGET

FLAGS:
  {{range .VisibleFlags}}{{.}}
  {{end}}
EXAMPLES:
  1. Get server information of the 'play' MinIO server.
	   $ {{.HelpName}} play/

`,
}

// backendType - indicates the type of backend storage
type backendType string

const (
	fsType      = backendType("FS")
	erasureType = backendType("Erasure")
)

// fsBackend contains specific FS storage information
type fsBackend struct {
	Type backendType `json:"backendType"`
}

// xlBackend contains specific erasure storage information
type xlBackend struct {
	Type         backendType `json:"backendType"`
	OnlineDisks  int         `json:"onlineDisks"`
	OfflineDisks int         `json:"offlineDisks"`
	// Data disks for currently configured Standard storage class.
	StandardSCData int `json:"standardSCData"`
	// Parity disks for currently configured Standard storage class.
	StandardSCParity int `json:"standardSCParity"`
	// Data disks for currently configured Reduced Redundancy storage class.
	RRSCData int `json:"rrSCData"`
	// Parity disks for currently configured Reduced Redundancy storage class.
	RRSCParity int `json:"rrSCParity"`

	// List of all disk status.
	Sets [][]madmin.DriveInfo `json:"sets"`
}

// backendStatus represents the overall information of all backend storage types
type backendStatus struct {
	Used    uint64      `json:"used"`
	Backend interface{} `json:"backend"`
}

// ServerInfo holds the whole server information that will be
// returned by ServerInfo API.
type ServerInfo struct {
	StorageInfo backendStatus             `json:"storage"`
	ConnStats   madmin.ServerConnStats    `json:"network"`
	Properties  madmin.ServerProperties   `json:"server"`
	CPULoad     madmin.ServerCPULoadInfo  `json:"cpu,omitempty"`
	MemUsage    madmin.ServerMemUsageInfo `json:"mem,omitempty"`
}

// infoMessage container to hold service status information.
type infoMessage struct {
	Status  string `json:"status"`
	Service string `json:"service"`
	Addr    string `json:"address"`
	Err     string `json:"error"`
	*ServerInfo
}

// String colorized service status message.
func (u infoMessage) String() (msg string) {
	dot := "●"

	// When service is offline
	if u.Service == "off" {
		msg += fmt.Sprintf("%s  %s\n", console.Colorize("InfoFail", dot), console.Colorize("PrintB", u.Addr))
		msg += fmt.Sprintf("   Uptime: %s\n", console.Colorize("InfoFail", "offline"))
		return
	}

	// Print error if any and exit
	if u.Err != "" {
		msg += fmt.Sprintf("%s  %s\n", console.Colorize("InfoFail", dot), console.Colorize("PrintB", u.Addr))
		msg += fmt.Sprintf("   Uptime: %s\n", console.Colorize("InfoFail", "offline"))
		e := u.Err
		if strings.Trim(e, " ") == "rpc: retry error" {
			e = "unreachable"
		}
		msg += fmt.Sprintf("    Error: %s", console.Colorize("InfoFail", e))
		return
	}

	// Print server title
	msg += fmt.Sprintf("%s  %s\n", console.Colorize("Info", dot), console.Colorize("PrintB", u.Addr))

	// Uptime
	msg += fmt.Sprintf("   Uptime: %s\n", console.Colorize("Info",
		humanize.RelTime(time.Now(), time.Now().Add(-u.ServerInfo.Properties.Uptime), "", "")))

	// Version
	version := u.ServerInfo.Properties.Version
	if u.ServerInfo.Properties.Version == "DEVELOPMENT.GOGET" {
		version = "<development>"
	}
	msg += fmt.Sprintf("   Version: %s\n", version)
	// Region
	if u.ServerInfo.Properties.Region != "" {
		msg += fmt.Sprintf("   Region: %s\n", u.ServerInfo.Properties.Region)
	}
	// ARNs
	sqsARNs := ""
	for _, v := range u.ServerInfo.Properties.SQSARN {
		sqsARNs += fmt.Sprintf("%s ", v)
	}
	if sqsARNs != "" {
		msg += fmt.Sprintf(" SQS ARNs: %s\n", sqsARNs)
	}

	// Incoming/outgoing
	msg += fmt.Sprintf("   Storage: Used %s", humanize.IBytes(u.StorageInfo.Used))
	if v, ok := u.ServerInfo.StorageInfo.Backend.(xlBackend); ok {
		upBackends := 0
		downBackends := 0
		for _, set := range v.Sets {
			for _, s := range set {
				if strings.Contains(s.Endpoint, u.Addr) || s.Endpoint[0] == '/' || s.Endpoint[0] == '.' {
					if s.State == "ok" {
						upBackends++
					} else {
						downBackends++
					}
				}
			}
		}
		upBackendsString := fmt.Sprintf("%d", upBackends)
		if downBackends != 0 {
			upBackendsString = console.Colorize("InfoFail", fmt.Sprintf("%d", upBackends))
		}
		msg += fmt.Sprintf("\n   Drives: %s/%d %s\n", upBackendsString,
			upBackends+downBackends, console.Colorize("Info", "OK"))
		msg += "\n"

		//CPU section
		msg += fmt.Sprintf("%s        min        avg      max\n", console.Colorize("Info", "   CPU"))
		for i := range u.CPULoad.Load {
			msg += fmt.Sprintf("   current    %.2f%%      %.2f%%    %.2f%%\n", u.CPULoad.Load[i].Min, u.CPULoad.Load[i].Avg, u.CPULoad.Load[i].Max)
			if len(u.CPULoad.HistoricLoad) > i {
				msg += fmt.Sprintf("   historic   %.2f%%      %.2f%%    %.2f%%\n", u.CPULoad.HistoricLoad[i].Min, u.CPULoad.HistoricLoad[i].Avg, u.CPULoad.HistoricLoad[i].Max)
			}
			msg += "\n"
		}

		// Mem section
		msg += fmt.Sprintf("%s        usage\n", console.Colorize("Info", "   MEM"))
		for i := range u.MemUsage.Usage {
			msg += fmt.Sprintf("   current    %s\n", humanize.IBytes(u.MemUsage.Usage[i].Mem))
			if len(u.MemUsage.HistoricUsage) > i {
				msg += fmt.Sprintf("   historic   %s\n", humanize.IBytes(u.MemUsage.HistoricUsage[i].Mem))
			}
			msg += "\n"
		}

	}
	return
}

// JSON jsonified service status message.
func (u infoMessage) JSON() string {
	u.Status = "success"
	statusJSONBytes, e := json.MarshalIndent(u, "", " ")
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")

	return string(statusJSONBytes)
}

// checkAdminInfoSyntax - validate all the passed arguments
func checkAdminServerInfoSyntax(ctx *cli.Context) {
	if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 {
		cli.ShowCommandHelpAndExit(ctx, "server", 1) // last argument is exit code
	}
}

func mainAdminServerInfo(ctx *cli.Context) error {
	checkAdminServerInfoSyntax(ctx)

	console.SetColor("Info", color.New(color.FgGreen, color.Bold))
	console.SetColor("InfoDegraded", color.New(color.FgYellow, color.Bold))
	console.SetColor("InfoFail", color.New(color.FgRed, color.Bold))

	// Get the alias parameter from cli
	args := ctx.Args()
	aliasedURL := args.Get(0)

	// Create a new MinIO Admin Client
	client, err := newAdminClient(aliasedURL)
	fatalIf(err, "Cannot get a configured admin connection.")

	printOfflineErrorMessage := func(err error) {
		errMsg := ""
		if err != nil {
			errMsg = err.Error()
		}
		printMsg(infoMessage{
			Addr:    aliasedURL,
			Service: "off",
			Err:     errMsg,
		})
	}

	processErr := func(e error) error {
		switch e.(type) {
		case *json.SyntaxError:
			printOfflineErrorMessage(e)
			return e
		case *url.Error:
			printOfflineErrorMessage(e)
			return e
		default:
			// If the error is not nil and unrecognized, just print it and exit
			fatalIf(probe.NewError(e), "Cannot get service status.")
		}
		return nil
	}

	// Fetch info of all servers (cluster or single server)
	serversInfo, e := client.ServerInfo()
	if err := processErr(e); err != nil {
		// exit immediately if error encountered
		return nil
	}
	// Fetch info of all CPU loads (all MinIO server instances)
	cpuLoads, e := client.ServerCPULoadInfo()

	if err := processErr(e); err != nil {
		// exit immediately if error encountered
		return nil
	}
	memUsages, e := client.ServerMemUsageInfo()
	if err := processErr(e); err != nil {
		// exit immediately if error encountered
		return nil
	}

	infoMessages := []infoMessage{}

	for i, serverInfo := range serversInfo {
		cpuLoad := cpuLoads[i]
		memUsage := memUsages[i]

		// Print the error if exists and jump to the next server
		if serverInfo.Error != "" {

			infoMessages = append(infoMessages, infoMessage{
				Service: "on",
				Addr:    serverInfo.Addr,
				Err:     serverInfo.Error,
			})
			continue
		}

		// Construct the backend status
		storageInfo := backendStatus{
			Used: serverInfo.Data.StorageInfo.Used,
		}

		if serverInfo.Data.StorageInfo.Backend.Type == madmin.Erasure {
			storageInfo.Backend = xlBackend{
				Type:             erasureType,
				OnlineDisks:      serverInfo.Data.StorageInfo.Backend.OnlineDisks,
				OfflineDisks:     serverInfo.Data.StorageInfo.Backend.OfflineDisks,
				StandardSCData:   serverInfo.Data.StorageInfo.Backend.StandardSCData,
				StandardSCParity: serverInfo.Data.StorageInfo.Backend.StandardSCParity,
				RRSCData:         serverInfo.Data.StorageInfo.Backend.RRSCData,
				RRSCParity:       serverInfo.Data.StorageInfo.Backend.RRSCParity,
				Sets:             serverInfo.Data.StorageInfo.Backend.Sets,
			}
		} else {
			storageInfo.Backend = fsBackend{
				Type: fsType,
			}
		}

		infoMessages = append(infoMessages, (infoMessage{
			Service: "on",
			Addr:    serverInfo.Addr,
			Err:     serverInfo.Error,
			ServerInfo: &ServerInfo{
				StorageInfo: storageInfo,
				ConnStats:   serverInfo.Data.ConnStats,
				Properties:  serverInfo.Data.Properties,
				CPULoad:     cpuLoad,
				MemUsage:    memUsage,
			},
		}))

	}

	sort.Stable(&sortInfoWrapper{infoMessages})

	for _, s := range infoMessages {
		printMsg(s)
	}

	return nil
}

type sortInfoWrapper struct {
	infos []infoMessage
}

func (s *sortInfoWrapper) Len() int {
	return len(s.infos)
}

func (s *sortInfoWrapper) Swap(i, j int) {
	if s.infos != nil {
		s.infos[i], s.infos[j] = s.infos[j], s.infos[i]
	}
}

func (s *sortInfoWrapper) Less(i, j int) bool {
	if s.infos != nil {
		return s.infos[i].Addr < s.infos[j].Addr
	}
	return false

}
<commit_msg>Add support for Total Space and Available Space on mc admin info (#2872)<commit_after>/*
 * MinIO Client (C) 2016-2019 MinIO, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"fmt"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/mc/pkg/colorjson"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/minio/pkg/madmin"
)

var adminInfoServer = cli.Command{
	Name:   "server",
	Usage:  "display MinIO server information",
	Action: mainAdminServerInfo,
	Before: setGlobalsFromContext,
	Flags:  globalFlags,
	CustomHelpTemplate: `NAME:
  {{.HelpName}} - {{.Usage}}

USAGE:
  {{.HelpName}} TARGET

FLAGS:
  {{range .VisibleFlags}}{{.}}
  {{end}}
EXAMPLES:
  1. Get server information of the 'play' MinIO server.
	   $ {{.HelpName}} play/

`,
}

// backendType - indicates the type of backend storage
type backendType string

const (
	fsType      = backendType("FS")
	erasureType = backendType("Erasure")
)

// fsBackend contains specific FS storage information
type fsBackend struct {
	Type backendType `json:"backendType"`
}

// xlBackend contains specific erasure storage information
type xlBackend struct {
	Type         backendType `json:"backendType"`
	OnlineDisks  int         `json:"onlineDisks"`
	OfflineDisks int         `json:"offlineDisks"`
	// Data disks for currently configured Standard storage class.
	StandardSCData int `json:"standardSCData"`
	// Parity disks for currently configured Standard storage class.
	StandardSCParity int `json:"standardSCParity"`
	// Data disks for currently configured Reduced Redundancy storage class.
	RRSCData int `json:"rrSCData"`
	// Parity disks for currently configured Reduced Redundancy storage class.
	RRSCParity int `json:"rrSCParity"`

	// List of all disk status.
	Sets [][]madmin.DriveInfo `json:"sets"`
}

// backendStatus represents the overall information of all backend storage types
type backendStatus struct {
	// Total used space per tenant.
	Used uint64 `json:"used"`
	// Total available space.
	Available uint64 `json:"available"`
	// Total disk space.
	Total uint64 `json:"total"`
	// Backend type.
	Backend interface{} `json:"backend"`
}

// ServerInfo holds the whole server information that will be
// returned by ServerInfo API.
type ServerInfo struct {
	StorageInfo backendStatus             `json:"storage"`
	ConnStats   madmin.ServerConnStats    `json:"network"`
	Properties  madmin.ServerProperties   `json:"server"`
	CPULoad     madmin.ServerCPULoadInfo  `json:"cpu,omitempty"`
	MemUsage    madmin.ServerMemUsageInfo `json:"mem,omitempty"`
}

// infoMessage container to hold service status information.
type infoMessage struct {
	Status  string `json:"status"`
	Service string `json:"service"`
	Addr    string `json:"address"`
	Err     string `json:"error"`
	*ServerInfo
}

// String colorized service status message.
func (u infoMessage) String() (msg string) {
	dot := "●"

	// When service is offline
	if u.Service == "off" {
		msg += fmt.Sprintf("%s  %s\n", console.Colorize("InfoFail", dot), console.Colorize("PrintB", u.Addr))
		msg += fmt.Sprintf("   Uptime: %s\n", console.Colorize("InfoFail", "offline"))
		return
	}

	// Print error if any and exit
	if u.Err != "" {
		msg += fmt.Sprintf("%s  %s\n", console.Colorize("InfoFail", dot), console.Colorize("PrintB", u.Addr))
		msg += fmt.Sprintf("   Uptime: %s\n", console.Colorize("InfoFail", "offline"))
		e := u.Err
		if strings.Trim(e, " ") == "rpc: retry error" {
			e = "unreachable"
		}
		msg += fmt.Sprintf("    Error: %s", console.Colorize("InfoFail", e))
		return
	}

	// Print server title
	msg += fmt.Sprintf("%s  %s\n", console.Colorize("Info", dot), console.Colorize("PrintB", u.Addr))

	// Uptime
	msg += fmt.Sprintf("   Uptime: %s\n", console.Colorize("Info",
		humanize.RelTime(time.Now(), time.Now().Add(-u.ServerInfo.Properties.Uptime), "", "")))

	// Version
	version := u.ServerInfo.Properties.Version
	if u.ServerInfo.Properties.Version == "DEVELOPMENT.GOGET" {
		version = "<development>"
	}
	msg += fmt.Sprintf("  Version: %s\n", version)
	// Region
	if u.ServerInfo.Properties.Region != "" {
		msg += fmt.Sprintf("   Region: %s\n", u.ServerInfo.Properties.Region)
	}
	// ARNs
	sqsARNs := ""
	for _, v := range u.ServerInfo.Properties.SQSARN {
		sqsARNs += fmt.Sprintf("%s ", v)
	}
	if sqsARNs != "" {
		msg += fmt.Sprintf(" SQS ARNs: %s\n", sqsARNs)
	}

	// Incoming/outgoing
	msg += fmt.Sprintf("  Storage: Used %s, Free %s",
		humanize.IBytes(u.StorageInfo.Used),
		humanize.IBytes(u.StorageInfo.Available))
	if v, ok := u.ServerInfo.StorageInfo.Backend.(xlBackend); ok {
		upBackends := 0
		downBackends := 0
		for _, set := range v.Sets {
			for _, s := range set {
				if strings.Contains(s.Endpoint, u.Addr) || s.Endpoint[0] == '/' || s.Endpoint[0] == '.' {
					if s.State == "ok" {
						upBackends++
					} else {
						downBackends++
					}
				}
			}
		}
		upBackendsString := fmt.Sprintf("%d", upBackends)
		if downBackends != 0 {
			upBackendsString = console.Colorize("InfoFail", fmt.Sprintf("%d", upBackends))
		}
		msg += fmt.Sprintf("\n   Drives: %s/%d %s\n", upBackendsString,
			upBackends+downBackends, console.Colorize("Info", "OK"))
		msg += "\n"

		//CPU section
		msg += fmt.Sprintf("%s        min        avg      max\n", console.Colorize("Info", "   CPU"))
		for i := range u.CPULoad.Load {
			msg += fmt.Sprintf("   current    %.2f%%      %.2f%%    %.2f%%\n", u.CPULoad.Load[i].Min, u.CPULoad.Load[i].Avg, u.CPULoad.Load[i].Max)
			if len(u.CPULoad.HistoricLoad) > i {
				msg += fmt.Sprintf("   historic   %.2f%%      %.2f%%    %.2f%%\n", u.CPULoad.HistoricLoad[i].Min, u.CPULoad.HistoricLoad[i].Avg, u.CPULoad.HistoricLoad[i].Max)
			}
			msg += "\n"
		}

		// Mem section
		msg += fmt.Sprintf("%s        usage\n", console.Colorize("Info", "   MEM"))
		for i := range u.MemUsage.Usage {
			msg += fmt.Sprintf("   current    %s\n", humanize.IBytes(u.MemUsage.Usage[i].Mem))
			if len(u.MemUsage.HistoricUsage) > i {
				msg += fmt.Sprintf("   historic   %s\n", humanize.IBytes(u.MemUsage.HistoricUsage[i].Mem))
			}
			msg += "\n"
		}

	}
	return
}

// JSON jsonified service status message.
func (u infoMessage) JSON() string {
	u.Status = "success"
	statusJSONBytes, e := json.MarshalIndent(u, "", " ")
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")

	return string(statusJSONBytes)
}

// checkAdminInfoSyntax - validate all the passed arguments
func checkAdminServerInfoSyntax(ctx *cli.Context) {
	if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 {
		cli.ShowCommandHelpAndExit(ctx, "server", 1) // last argument is exit code
	}
}

func mainAdminServerInfo(ctx *cli.Context) error {
	checkAdminServerInfoSyntax(ctx)

	console.SetColor("Info", color.New(color.FgGreen, color.Bold))
	console.SetColor("InfoDegraded", color.New(color.FgYellow, color.Bold))
	console.SetColor("InfoFail", color.New(color.FgRed, color.Bold))

	// Get the alias parameter from cli
	args := ctx.Args()
	aliasedURL := args.Get(0)

	// Create a new MinIO Admin Client
	client, err := newAdminClient(aliasedURL)
	fatalIf(err, "Cannot get a configured admin connection.")

	printOfflineErrorMessage := func(err error) {
		errMsg := ""
		if err != nil {
			errMsg = err.Error()
		}
		printMsg(infoMessage{
			Addr:    aliasedURL,
			Service: "off",
			Err:     errMsg,
		})
	}

	processErr := func(e error) error {
		switch e.(type) {
		case *json.SyntaxError:
			printOfflineErrorMessage(e)
			return e
		case *url.Error:
			printOfflineErrorMessage(e)
			return e
		default:
			// If the error is not nil and unrecognized, just print it and exit
			fatalIf(probe.NewError(e), "Cannot get service status.")
		}
		return nil
	}

	// Fetch info of all servers (cluster or single server)
	serversInfo, e := client.ServerInfo()
	if err := processErr(e); err != nil {
		// exit immediately if error encountered
		return nil
	}
	// Fetch info of all CPU loads (all MinIO server instances)
	cpuLoads, e := client.ServerCPULoadInfo()

	if err := processErr(e); err != nil {
		// exit immediately if error encountered
		return nil
	}
	memUsages, e := client.ServerMemUsageInfo()
	if err := processErr(e); err != nil {
		// exit immediately if error encountered
		return nil
	}

	infoMessages := []infoMessage{}

	for i, serverInfo := range serversInfo {
		cpuLoad := cpuLoads[i]
		memUsage := memUsages[i]

		// Print the error if exists and jump to the next server
		if serverInfo.Error != "" {

			infoMessages = append(infoMessages, infoMessage{
				Service: "on",
				Addr:    serverInfo.Addr,
				Err:     serverInfo.Error,
			})
			continue
		}

		// Construct the backend status
		storageInfo := backendStatus{
			Used:      serverInfo.Data.StorageInfo.Used,
			Available: serverInfo.Data.StorageInfo.Available,
			Total:     serverInfo.Data.StorageInfo.Total,
		}

		if serverInfo.Data.StorageInfo.Backend.Type == madmin.Erasure {
			storageInfo.Backend = xlBackend{
				Type:             erasureType,
				OnlineDisks:      serverInfo.Data.StorageInfo.Backend.OnlineDisks,
				OfflineDisks:     serverInfo.Data.StorageInfo.Backend.OfflineDisks,
				StandardSCData:   serverInfo.Data.StorageInfo.Backend.StandardSCData,
				StandardSCParity: serverInfo.Data.StorageInfo.Backend.StandardSCParity,
				RRSCData:         serverInfo.Data.StorageInfo.Backend.RRSCData,
				RRSCParity:       serverInfo.Data.StorageInfo.Backend.RRSCParity,
				Sets:             serverInfo.Data.StorageInfo.Backend.Sets,
			}
		} else {
			storageInfo.Backend = fsBackend{
				Type: fsType,
			}
		}

		infoMessages = append(infoMessages, (infoMessage{
			Service: "on",
			Addr:    serverInfo.Addr,
			Err:     serverInfo.Error,
			ServerInfo: &ServerInfo{
				StorageInfo: storageInfo,
				ConnStats:   serverInfo.Data.ConnStats,
				Properties:  serverInfo.Data.Properties,
				CPULoad:     cpuLoad,
				MemUsage:    memUsage,
			},
		}))

	}

	sort.Stable(&sortInfoWrapper{infoMessages})

	for _, s := range infoMessages {
		printMsg(s)
	}

	return nil
}

type sortInfoWrapper struct {
	infos []infoMessage
}

func (s *sortInfoWrapper) Len() int {
	return len(s.infos)
}

func (s *sortInfoWrapper) Swap(i, j int) {
	if s.infos != nil {
		s.infos[i], s.infos[j] = s.infos[j], s.infos[i]
	}
}

func (s *sortInfoWrapper) Less(i, j int) bool {
	if s.infos != nil {
		return s.infos[i].Addr < s.infos[j].Addr
	}
	return false

}
<|endoftext|>


<commit_before>// Copyright 2014 The Go 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"
	"os"
	"sort"
	"strconv"
	"text/tabwriter"
)

var (
	changedOnly = flag.Bool("changed", false, "show only benchmarks that have changed")
	magSort     = flag.Bool("mag", false, "sort benchmarks by magnitude of change")
	best        = flag.Bool("best", false, "compare best times from old and new")
)

const usageFooter = `
Each input file should be from:
	go test -test.run=NONE -test.bench=. > [old,new].txt

Benchcmp compares old and new for each benchmark.

If -test.benchmem=true is added to the "go test" command
benchcmp will also compare memory allocations.
`

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: %s old.txt new.txt\n\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprint(os.Stderr, usageFooter)
		os.Exit(2)
	}
	flag.Parse()
	if flag.NArg() != 2 {
		flag.Usage()
	}

	before := parseFile(flag.Arg(0))
	after := parseFile(flag.Arg(1))

	cmps, warnings := Correlate(before, after)

	for _, warn := range warnings {
		fmt.Fprintln(os.Stderr, warn)
	}

	if len(cmps) == 0 {
		fatal("benchcmp: no repeated benchmarks")
	}

	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 0, 5, ' ', 0)
	defer w.Flush()

	var header bool // Has the header has been displayed yet for a given block?

	if *magSort {
		sort.Sort(ByDeltaNsOp(cmps))
	} else {
		sort.Sort(ByParseOrder(cmps))
	}
	for _, cmp := range cmps {
		if !cmp.Measured(NsOp) {
			continue
		}
		if delta := cmp.DeltaNsOp(); !*changedOnly || delta.Changed() {
			if !header {
				fmt.Fprint(w, "benchmark\told ns/op\tnew ns/op\tdelta\n")
				header = true
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", cmp.Name(), formatNs(cmp.Before.NsOp), formatNs(cmp.After.NsOp), delta.Percent())
		}
	}

	header = false
	if *magSort {
		sort.Sort(ByDeltaMbS(cmps))
	}
	for _, cmp := range cmps {
		if !cmp.Measured(MbS) {
			continue
		}
		if delta := cmp.DeltaMbS(); !*changedOnly || delta.Changed() {
			if !header {
				fmt.Fprint(w, "\nbenchmark\told MB/s\tnew MB/s\tspeedup\n")
				header = true
			}
			fmt.Fprintf(w, "%s\t%.2f\t%.2f\t%s\n", cmp.Name(), cmp.Before.MbS, cmp.After.MbS, delta.Multiple())
		}
	}

	header = false
	if *magSort {
		sort.Sort(ByDeltaAllocsOp(cmps))
	}
	for _, cmp := range cmps {
		if !cmp.Measured(AllocsOp) {
			continue
		}
		if delta := cmp.DeltaAllocsOp(); !*changedOnly || delta.Changed() {
			if !header {
				fmt.Fprint(w, "\nbenchmark\told allocs\tnew allocs\tdelta\n")
				header = true
			}
			fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.AllocsOp, cmp.After.AllocsOp, delta.Percent())
		}
	}

	header = false
	if *magSort {
		sort.Sort(ByDeltaBOp(cmps))
	}
	for _, cmp := range cmps {
		if !cmp.Measured(BOp) {
			continue
		}
		if delta := cmp.DeltaBOp(); !*changedOnly || delta.Changed() {
			if !header {
				fmt.Fprint(w, "\nbenchmark\told bytes\tnew bytes\tdelta\n")
				header = true
			}
			fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.BOp, cmp.After.BOp, cmp.DeltaBOp().Percent())
		}
	}
}

func fatal(msg interface{}) {
	fmt.Fprintln(os.Stderr, msg)
	os.Exit(1)
}

func parseFile(path string) BenchSet {
	f, err := os.Open(path)
	if err != nil {
		fatal(err)
	}
	bb, err := ParseBenchSet(f)
	if err != nil {
		fatal(err)
	}
	return bb
}

// formatNs formats ns measurements to expose a useful amount of
// precision. It mirrors the ns precision logic of testing.B.
func formatNs(ns float64) string {
	prec := 0
	switch {
	case ns < 10:
		prec = 2
	case ns < 100:
		prec = 1
	}
	return strconv.FormatFloat(ns, 'f', prec, 64)
}
<commit_msg>benchcmp: Fix invocation instructions.<commit_after>// Copyright 2014 The Go 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"
	"os"
	"sort"
	"strconv"
	"text/tabwriter"
)

var (
	changedOnly = flag.Bool("changed", false, "show only benchmarks that have changed")
	magSort     = flag.Bool("mag", false, "sort benchmarks by magnitude of change")
	best        = flag.Bool("best", false, "compare best times from old and new")
)

const usageFooter = `
Each input file should be from:
	go test -run=NONE -bench=. > [old,new].txt

Benchcmp compares old and new for each benchmark.

If -test.benchmem=true is added to the "go test" command
benchcmp will also compare memory allocations.
`

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: %s old.txt new.txt\n\n", os.Args[0])
		flag.PrintDefaults()
		fmt.Fprint(os.Stderr, usageFooter)
		os.Exit(2)
	}
	flag.Parse()
	if flag.NArg() != 2 {
		flag.Usage()
	}

	before := parseFile(flag.Arg(0))
	after := parseFile(flag.Arg(1))

	cmps, warnings := Correlate(before, after)

	for _, warn := range warnings {
		fmt.Fprintln(os.Stderr, warn)
	}

	if len(cmps) == 0 {
		fatal("benchcmp: no repeated benchmarks")
	}

	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 0, 5, ' ', 0)
	defer w.Flush()

	var header bool // Has the header has been displayed yet for a given block?

	if *magSort {
		sort.Sort(ByDeltaNsOp(cmps))
	} else {
		sort.Sort(ByParseOrder(cmps))
	}
	for _, cmp := range cmps {
		if !cmp.Measured(NsOp) {
			continue
		}
		if delta := cmp.DeltaNsOp(); !*changedOnly || delta.Changed() {
			if !header {
				fmt.Fprint(w, "benchmark\told ns/op\tnew ns/op\tdelta\n")
				header = true
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", cmp.Name(), formatNs(cmp.Before.NsOp), formatNs(cmp.After.NsOp), delta.Percent())
		}
	}

	header = false
	if *magSort {
		sort.Sort(ByDeltaMbS(cmps))
	}
	for _, cmp := range cmps {
		if !cmp.Measured(MbS) {
			continue
		}
		if delta := cmp.DeltaMbS(); !*changedOnly || delta.Changed() {
			if !header {
				fmt.Fprint(w, "\nbenchmark\told MB/s\tnew MB/s\tspeedup\n")
				header = true
			}
			fmt.Fprintf(w, "%s\t%.2f\t%.2f\t%s\n", cmp.Name(), cmp.Before.MbS, cmp.After.MbS, delta.Multiple())
		}
	}

	header = false
	if *magSort {
		sort.Sort(ByDeltaAllocsOp(cmps))
	}
	for _, cmp := range cmps {
		if !cmp.Measured(AllocsOp) {
			continue
		}
		if delta := cmp.DeltaAllocsOp(); !*changedOnly || delta.Changed() {
			if !header {
				fmt.Fprint(w, "\nbenchmark\told allocs\tnew allocs\tdelta\n")
				header = true
			}
			fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.AllocsOp, cmp.After.AllocsOp, delta.Percent())
		}
	}

	header = false
	if *magSort {
		sort.Sort(ByDeltaBOp(cmps))
	}
	for _, cmp := range cmps {
		if !cmp.Measured(BOp) {
			continue
		}
		if delta := cmp.DeltaBOp(); !*changedOnly || delta.Changed() {
			if !header {
				fmt.Fprint(w, "\nbenchmark\told bytes\tnew bytes\tdelta\n")
				header = true
			}
			fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.BOp, cmp.After.BOp, cmp.DeltaBOp().Percent())
		}
	}
}

func fatal(msg interface{}) {
	fmt.Fprintln(os.Stderr, msg)
	os.Exit(1)
}

func parseFile(path string) BenchSet {
	f, err := os.Open(path)
	if err != nil {
		fatal(err)
	}
	bb, err := ParseBenchSet(f)
	if err != nil {
		fatal(err)
	}
	return bb
}

// formatNs formats ns measurements to expose a useful amount of
// precision. It mirrors the ns precision logic of testing.B.
func formatNs(ns float64) string {
	prec := 0
	switch {
	case ns < 10:
		prec = 2
	case ns < 100:
		prec = 1
	}
	return strconv.FormatFloat(ns, 'f', prec, 64)
}
<|endoftext|>


<commit_before>// ccprices prints current currency prices in ledger format.
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"
)

const (
	euroAPI  = "http://data.fixer.io/api/latest"
	xauAPI   = "https://www.quandl.com/api/v3/datasets/LBMA/GOLD.json?limit=1"
	xagAPI   = "https://www.quandl.com/api/v3/datasets/LBMA/SILVER.json?limit=1"
	coinsAPI = "https://api.coinmarketcap.com/v1/ticker/?convert=EUR&limit=300"
)

var (
	// Fixer API key can be set via environment variable FIXER_API_KEY
	fixer = os.Getenv("FIXER_API_KEY")
	// Quandl API key can be set via environment variable QUANDL_API_KEY
	quandl = os.Getenv("QUANDL_API_KEY")
	coins  = []string{
		"Bitcoin",
		"Bitcoin Cash",
		"Bitcoin Gold",
		"Dash",
		"Decred",
		"Ethereum",
		"Litecoin",
		"Monero",
		"Particl",
		"Tezos",
		"Zcash",
	}
)

type result struct {
	symbol string
	price  float64
}

func httpGetWithWarning(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		warning(fmt.Sprintf("GET %s: %s", url, resp.Status))
		return nil, nil
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return b, err
}

func getEuroExchangeRates(api string) (map[string]interface{}, error) {
	if fixer != "" {
		api += "?access_key=" + fixer
	}
	b, err := httpGetWithWarning(api)
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return nil, err
	}
	return jsn["rates"].(map[string]interface{}), nil
}

func getLBMAPrice(api string, dataIndex int) (float64, error) {
	if quandl != "" {
		api += "&api_key=" + quandl
	}
	b, err := httpGetWithWarning(api)
	if err != nil {
		return 0, err
	}
	if b == nil {
		return 0, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return 0, err
	}
	data := jsn["dataset"].(map[string]interface{})["data"].([]interface{})
	var price float64
	if data[0].([]interface{})[dataIndex] != nil {
		// p.m. price is available
		price = data[0].([]interface{})[dataIndex].(float64)
	} else {
		// p.m. price is not available, use a.m. price instead
		price = data[0].([]interface{})[dataIndex-1].(float64)
	}
	return price, nil
}

func getCoinPrices() ([]interface{}, error) {
	b, err := httpGetWithWarning(coinsAPI)
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, nil
	}
	jsn := make([]interface{}, 0)
	if err := json.Unmarshal(b, &jsn); err != nil {
		return nil, err
	}
	return jsn, nil
}

func warning(warn string) {
	fmt.Fprintf(os.Stderr, "%s: warning: %s\n", os.Args[0], warn)
}

func fatal(err error) {
	fmt.Fprintf(os.Stderr, "%s: error: %s\n", os.Args[0], err)
	os.Exit(1)
}

func main() {
	// get euro exchange rates
	rates, err := getEuroExchangeRates(euroAPI)
	if err != nil {
		fatal(err)
	}
	// get gold price
	xau, err := getLBMAPrice(xauAPI, 6)
	if err != nil {
		fatal(err)
	}
	// get silver price
	xag, err := getLBMAPrice(xagAPI, 3)
	if err != nil {
		fatal(err)
	}
	// get all coin prices
	all, err := getCoinPrices()
	if err != nil {
		fatal(err)
	}
	// construct map of coin names we are interested in
	var (
		names  map[string]struct{}
		prices map[string]*result
	)
	if all != nil {
		names = make(map[string]struct{})
		for _, name := range coins {
			names[name] = struct{}{}
		}
		prices = make(map[string]*result)
		// iterate over all coin informations
		var btc, bch float64
		for _, info := range all {
			coin := info.(map[string]interface{})
			name := coin["name"].(string)
			_, ok := names[name]
			if ok {
				// we are interested in this coin -> store price and symbol
				f := coin["price_eur"].(string)
				p, err := strconv.ParseFloat(f, 64)
				if err != nil {
					fatal(err)
				}
				prices[name] = &result{symbol: coin["symbol"].(string), price: p}
				if coin["symbol"] == "BTC" {
					btc = p
				}
				if coin["symbol"] == "BCH" {
					bch = p
				}
			}
		}
		fmt.Fprintf(os.Stderr, "BCH/BTC ratio: %.2f%%\n", bch*100.0/btc)
	}
	// output all prices
	t := time.Now().Format("2006/01/02 15:04:05")
	if rates != nil {
		fmt.Printf("P %s USD %11.6f EUR\n", t, 1/rates["USD"].(float64))
		fmt.Printf("P %s GBP %11.6f EUR\n", t, 1/rates["GBP"].(float64))
		fmt.Printf("P %s CHF %11.6f EUR\n", t, 1/rates["CHF"].(float64))
		fmt.Printf("P %s CZK %11.6f EUR\n", t, 1/rates["CZK"].(float64))
		fmt.Printf("P %s THB %11.6f EUR\n", t, 1/rates["THB"].(float64))
	}
	if xau != 0 {
		fmt.Printf("P %s XAU %11.6f EUR\n", t, xau)
	}
	if xag != 0 {
		fmt.Printf("P %s XAG %11.6f EUR\n", t, xag)
	}
	if all != nil {
		for _, name := range coins {
			price, ok := prices[name]
			if ok {
				fmt.Printf("P %s %s %11.6f EUR\n", t, price.symbol, price.price)
			} else {
				fmt.Fprintf(os.Stderr, "price for \"%s\" does not exist\n", name)
			}
		}
	}
}
<commit_msg>ccprices: add BCHSV<commit_after>// ccprices prints current currency prices in ledger format.
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"
)

const (
	euroAPI  = "http://data.fixer.io/api/latest"
	xauAPI   = "https://www.quandl.com/api/v3/datasets/LBMA/GOLD.json?limit=1"
	xagAPI   = "https://www.quandl.com/api/v3/datasets/LBMA/SILVER.json?limit=1"
	coinsAPI = "https://api.coinmarketcap.com/v1/ticker/?convert=EUR&limit=2000"
)

var (
	// Fixer API key can be set via environment variable FIXER_API_KEY
	fixer = os.Getenv("FIXER_API_KEY")
	// Quandl API key can be set via environment variable QUANDL_API_KEY
	quandl = os.Getenv("QUANDL_API_KEY")
	coins  = []string{
		"Bitcoin",
		"Bitcoin Cash",
		"Bitcoin Cash SV",
		"Bitcoin Gold",
		"Dash",
		"Decred",
		"Ethereum",
		"Litecoin",
		"Monero",
		"Particl",
		"Tezos",
		"Zcash",
	}
)

type result struct {
	symbol string
	price  float64
}

func httpGetWithWarning(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		warning(fmt.Sprintf("GET %s: %s", url, resp.Status))
		return nil, nil
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return b, err
}

func getEuroExchangeRates(api string) (map[string]interface{}, error) {
	if fixer != "" {
		api += "?access_key=" + fixer
	}
	b, err := httpGetWithWarning(api)
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return nil, err
	}
	return jsn["rates"].(map[string]interface{}), nil
}

func getLBMAPrice(api string, dataIndex int) (float64, error) {
	if quandl != "" {
		api += "&api_key=" + quandl
	}
	b, err := httpGetWithWarning(api)
	if err != nil {
		return 0, err
	}
	if b == nil {
		return 0, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return 0, err
	}
	data := jsn["dataset"].(map[string]interface{})["data"].([]interface{})
	var price float64
	if data[0].([]interface{})[dataIndex] != nil {
		// p.m. price is available
		price = data[0].([]interface{})[dataIndex].(float64)
	} else {
		// p.m. price is not available, use a.m. price instead
		price = data[0].([]interface{})[dataIndex-1].(float64)
	}
	return price, nil
}

func getCoinPrices() ([]interface{}, error) {
	b, err := httpGetWithWarning(coinsAPI)
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, nil
	}
	jsn := make([]interface{}, 0)
	if err := json.Unmarshal(b, &jsn); err != nil {
		return nil, err
	}
	return jsn, nil
}

func warning(warn string) {
	fmt.Fprintf(os.Stderr, "%s: warning: %s\n", os.Args[0], warn)
}

func fatal(err error) {
	fmt.Fprintf(os.Stderr, "%s: error: %s\n", os.Args[0], err)
	os.Exit(1)
}

func main() {
	// get euro exchange rates
	rates, err := getEuroExchangeRates(euroAPI)
	if err != nil {
		fatal(err)
	}
	// get gold price
	xau, err := getLBMAPrice(xauAPI, 6)
	if err != nil {
		fatal(err)
	}
	// get silver price
	xag, err := getLBMAPrice(xagAPI, 3)
	if err != nil {
		fatal(err)
	}
	// get all coin prices
	all, err := getCoinPrices()
	if err != nil {
		fatal(err)
	}
	// construct map of coin names we are interested in
	var (
		names  map[string]struct{}
		prices map[string]*result
	)
	if all != nil {
		names = make(map[string]struct{})
		for _, name := range coins {
			names[name] = struct{}{}
		}
		prices = make(map[string]*result)
		// iterate over all coin informations
		var btc, bch float64
		for _, info := range all {
			coin := info.(map[string]interface{})
			name := coin["name"].(string)
			_, ok := names[name]
			if ok {
				// we are interested in this coin -> store price and symbol
				f := coin["price_eur"].(string)
				p, err := strconv.ParseFloat(f, 64)
				if err != nil {
					fatal(err)
				}
				prices[name] = &result{symbol: coin["symbol"].(string), price: p}
				if coin["symbol"] == "BTC" {
					btc = p
				}
				if coin["symbol"] == "BCH" {
					bch = p
				}
			}
		}
		fmt.Fprintf(os.Stderr, "BCH/BTC ratio: %.2f%%\n", bch*100.0/btc)
	}
	// output all prices
	t := time.Now().Format("2006/01/02 15:04:05")
	if rates != nil {
		fmt.Printf("P %s USD %11.6f EUR\n", t, 1/rates["USD"].(float64))
		fmt.Printf("P %s GBP %11.6f EUR\n", t, 1/rates["GBP"].(float64))
		fmt.Printf("P %s CHF %11.6f EUR\n", t, 1/rates["CHF"].(float64))
		fmt.Printf("P %s CZK %11.6f EUR\n", t, 1/rates["CZK"].(float64))
		fmt.Printf("P %s THB %11.6f EUR\n", t, 1/rates["THB"].(float64))
	}
	if xau != 0 {
		fmt.Printf("P %s XAU %11.6f EUR\n", t, xau)
	}
	if xag != 0 {
		fmt.Printf("P %s XAG %11.6f EUR\n", t, xag)
	}
	if all != nil {
		for _, name := range coins {
			price, ok := prices[name]
			if ok {
				fmt.Printf("P %s %s %11.6f EUR\n", t, price.symbol, price.price)
			} else {
				fmt.Fprintf(os.Stderr, "price for \"%s\" does not exist\n", name)
			}
		}
	}
}
<|endoftext|>


<commit_before>// epoxy_client is a command line utility for requesting nextboot configurations
// from the ePoxy server and executing them.
//
// epoxy_client should be embedded in initram images served by ePoxy. Once the
// network is initialized, epoxy_client can complete actions for the current
// boot stage. i.e. download config from epoxy, download kernel for stage3,
// kexec kernel.
package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net/url"

	"github.com/m-lab/epoxy/nextboot"
)

var (
	flagCmdline = flag.String("cmdline", "/proc/cmdline",
		"Read kernel cmdline parameters from the contents of this file.")
	flagAction = flag.String("action", "epoxy.stage2",
		"Execute the config loaded from the URL in this kernel parameter.")
	flagAddKargs = flag.Bool("extra-kargs", false,
		"Combine the local kargs with those returned from the action url. "+
			"Existing kargs are never replaced. Only useful for stage1.")
	flagReport = flag.String("report", "epoxy.report",
		"Report success or errors with the URL in this kernel parameter.")
	flagDryrun = flag.Bool("dryrun", false,
		"Request all configs but do not run commands. May change state in the ePoxy server.")
)

func main() {
	var result string

	flag.Parse()
	// TODO: Optionally retry in a loop until success or 6 hours of
	// failure have occurred. Automatically reboot after 6 hours of failure.
	c := &nextboot.Config{}

	b, err := ioutil.ReadFile(*flagCmdline)
	if err != nil {
		log.Fatal(err)
	}
	// Read and parse parameters from *flagCmdline.
	c.ParseCmdline(string(b))

	// Run the config loaded from the action URL.
	err = c.Run(*flagAction, *flagAddKargs, *flagDryrun)
	if err != nil {
		// Define a successful result.
		result = "error: " + err.Error()
	} else {
		result = "success"
	}
	log.Println("Result:", result)

	// Report a message to the ePoxy server after running.
	values := url.Values{}
	// TODO: report additional host information.
	// TODO: log the evaluate state of c.V1 -- helpful especially for errors.
	values.Set("message", result)

	err = c.Report(*flagReport, values, *flagDryrun)
	if err != nil {
		log.Fatal(err)
	}

	// Note: we may reboot without depending on the reboot command using:
	//   echo 1 > /proc/sys/kernel/sysrq
	//   echo b > /proc/sysrq-trigger
}
<commit_msg>Tweak -add-kargs flag name<commit_after>// epoxy_client is a command line utility for requesting nextboot configurations
// from the ePoxy server and executing them.
//
// epoxy_client should be embedded in initram images served by ePoxy. Once the
// network is initialized, epoxy_client can complete actions for the current
// boot stage. i.e. download config from epoxy, download kernel for stage3,
// kexec kernel.
package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net/url"

	"github.com/m-lab/epoxy/nextboot"
)

var (
	flagCmdline = flag.String("cmdline", "/proc/cmdline",
		"Read kernel cmdline parameters from the contents of this file.")
	flagAction = flag.String("action", "epoxy.stage2",
		"Execute the config loaded from the URL in this kernel parameter.")
	flagAddKargs = flag.Bool("add-kargs", false,
		"Combine the local kargs with those returned from the action url. "+
			"Existing kargs are never replaced. Only useful for stage1.")
	flagReport = flag.String("report", "epoxy.report",
		"Report success or errors with the URL in this kernel parameter.")
	flagDryrun = flag.Bool("dryrun", false,
		"Request all configs but do not run commands. May change state in the ePoxy server.")
)

func main() {
	var result string

	flag.Parse()
	// TODO: Optionally retry in a loop until success or 6 hours of
	// failure have occurred. Automatically reboot after 6 hours of failure.
	c := &nextboot.Config{}

	b, err := ioutil.ReadFile(*flagCmdline)
	if err != nil {
		log.Fatal(err)
	}
	// Read and parse parameters from *flagCmdline.
	c.ParseCmdline(string(b))

	// Run the config loaded from the action URL.
	err = c.Run(*flagAction, *flagAddKargs, *flagDryrun)
	if err != nil {
		// Define a successful result.
		result = "error: " + err.Error()
	} else {
		result = "success"
	}
	log.Println("Result:", result)

	// Report a message to the ePoxy server after running.
	values := url.Values{}
	// TODO: report additional host information.
	// TODO: log the evaluate state of c.V1 -- helpful especially for errors.
	values.Set("message", result)

	err = c.Report(*flagReport, values, *flagDryrun)
	if err != nil {
		log.Fatal(err)
	}

	// Note: we may reboot without depending on the reboot command using:
	//   echo 1 > /proc/sys/kernel/sysrq
	//   echo b > /proc/sysrq-trigger
}
<|endoftext|>


<commit_before>package main

import "github.com/chromium/hstspreload"

const (
	parallelism = 10
)

// A Result holds the outcome of PreloadableDomain() for a given Domain.
type Result struct {
	Domain       string                 `json:"domain"`
	Header       string                 `json:"header,omitempty"`
	ParsedHeader hstspreload.HSTSHeader `json:"parsed_header,omitempty"`
	Issues       hstspreload.Issues     `json:"issues"`
}

func worker(in chan string, out chan Result) {
	for d := range in {

		header, issues := hstspreload.PreloadableDomain(d)

		r := Result{
			Domain: d,
			Issues: issues,
		}
		if header != nil {
			r.Header = *header
			ParsedHeader, _ := hstspreload.ParseHeaderString(*header)
			r.ParsedHeader = ParsedHeader
		}

		out <- r
	}
}

// BatchPreloadable runs hstspreload.PreloadableDomain() over the given domains
// in parallel, and returns the results in an arbitrary order.
func BatchPreloadable(domains []string) chan Result {
	in := make(chan string)
	out := make(chan Result)
	for i := 0; i < parallelism; i++ {
		go worker(in, out)
	}

	go func() {
		for _, d := range domains {
			in <- d
		}
	}()

	results := make(chan Result)
	go func() {
		for range domains {
			results <- (<-out)
		}
		close(in)
		close(out)
		close(results)
	}()

	return results
}
<commit_msg>[cmd/hstspreload] Increase batch parallelism to 100.<commit_after>package main

import "github.com/chromium/hstspreload"

const (
	parallelism = 100
)

// A Result holds the outcome of PreloadableDomain() for a given Domain.
type Result struct {
	Domain       string                 `json:"domain"`
	Header       string                 `json:"header,omitempty"`
	ParsedHeader hstspreload.HSTSHeader `json:"parsed_header,omitempty"`
	Issues       hstspreload.Issues     `json:"issues"`
}

func worker(in chan string, out chan Result) {
	for d := range in {

		header, issues := hstspreload.PreloadableDomain(d)

		r := Result{
			Domain: d,
			Issues: issues,
		}
		if header != nil {
			r.Header = *header
			ParsedHeader, _ := hstspreload.ParseHeaderString(*header)
			r.ParsedHeader = ParsedHeader
		}

		out <- r
	}
}

// BatchPreloadable runs hstspreload.PreloadableDomain() over the given domains
// in parallel, and returns the results in an arbitrary order.
func BatchPreloadable(domains []string) chan Result {
	in := make(chan string)
	out := make(chan Result)
	for i := 0; i < parallelism; i++ {
		go worker(in, out)
	}

	go func() {
		for _, d := range domains {
			in <- d
		}
	}()

	results := make(chan Result)
	go func() {
		for range domains {
			results <- (<-out)
		}
		close(in)
		close(out)
		close(results)
	}()

	return results
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

var (
	part = `label: dos
label-id: 0xadc9f7b1
device: /dev/nbd0
unit: sectors

/dev/nbd0p1 : start=        2048, size=      102400, type=83, bootable
/dev/nbd0p2 : start=      104448, size=     6187008, type=39
`
	syslinuxconfig = []byte(`
TIMEOUT 50
DEFAULT vesamenu.c32
#DEFAULT 0
MENU TITLE Harvey OS
PROMPT 0
ONTIMEOUT 0

LABEL 0
TEXT HELP
Boot Harvey kernel with local filesystem
ENDTEXT
MENU LABEL Harvey OS (Local FS)
KERNEL mboot.c32
APPEND ../harvey service=cpu nobootprompt=local!#S/sdE0/ maxcores=1024 nvram=/boot/nvram nvrlen=512 nvroff=0 acpiirq=1 mouseport=ps2 vgasize=1024x768x24 monitor=vesa

LABEL 1
TEXT HELP
Boot Harvey kernel with remote filesystem (10.0.2.2)
ENDTEXT
MENU LABEL Harvey OS (Remote FS)
KERNEL mboot.c32
APPEND ../harvey service=cpu nobootprompt=tcp maxcores=1024 fs=10.0.2.2 auth=10.0.2.2 nvram=/boot/nvram nvrlen=512 nvroff=0 acpiirq=1 mouseport=ps2 vgasize=1024x768x24 monitor=vesa

LABEL 2
TEXT HELP
Install harvey on image
ENDTEXT
MENU LABEL Harvey OS (Install)
KERNEL mboot.c32
APPEND ../harvey service=cpu nobootprompt=tcp maxcores=1024 fs=10.0.2.2 auth=10.0.2.2 nvram=/boot/nvram nvrlen=512 nvroff=0 acpiirq=1 mouseport=ps2 vgasize=1024x768x24 monitor=vesa onstartup=/util/img/prepfs
`)
)

type command struct {
	*exec.Cmd
	in io.Reader
}

func gencmds(imgType string) []command {
	imgFilename := "harvey." + imgType
	return []command{
		{Cmd: exec.Command("qemu-img", "create", "-f", imgType, imgFilename, "3G")},
		{Cmd: exec.Command("chown", os.ExpandEnv("$SUDO_USER"), imgFilename)},
		{Cmd: exec.Command("chgrp", os.ExpandEnv("$SUDO_USER"), imgFilename)},
		{Cmd: exec.Command("modprobe", "nbd", "max_part=63")},
		{Cmd: exec.Command("qemu-nbd", "-c", "/dev/nbd0", "-f", imgType, imgFilename)},
		{Cmd: exec.Command("dd", "conv=notrunc", "bs=440", "count=1", "if=img/syslinux-bios/mbr.bin", "of=/dev/nbd0")},
		{Cmd: exec.Command("sfdisk", "/dev/nbd0"), in: bytes.NewBufferString(part)},
		{Cmd: exec.Command("mkfs.ext4", "/dev/nbd0p1")},
		{Cmd: exec.Command("mkdir", "-p", "disk/")},
		{Cmd: exec.Command("mount", "/dev/nbd0p1", "disk/")},
		{Cmd: exec.Command("extlinux", "-i", "disk/")},
		{Cmd: exec.Command("cp", "../sys/src/9/amd64/harvey.32bit", "disk/harvey")},
		{Cmd: exec.Command("ls", "-l", "disk")},
	}
}

func detach() {
	log.Println("Detaching")
	if err := exec.Command("umount", "disk").Run(); err != nil {
		log.Print(err)
	}
	if err := exec.Command("qemu-nbd", "-d", "/dev/nbd0").Run(); err != nil {
		log.Print(err)
	}
}

func main() {
	imgType := flag.String("imgtype", "raw", "Type of image to create (raw, qcow2)")
	flag.Parse()
	if *imgType != "qcow2" && *imgType != "raw" {
		log.Println("Invalid imgtype")
		return
	}

	if os.Getuid() != 0 {
		log.Println("You have to run me as root")
		return
	}

	defer detach()

	setupcmds := gencmds(*imgType)
	for _, c := range setupcmds {
		c.Stdin, c.Stdout, c.Stderr = c.in, os.Stdout, os.Stderr
		if err := c.Run(); err != nil {
			log.Printf("%v: %v", c.Cmd, err)
			return
		}
	}

	fi, err := ioutil.ReadDir("img/syslinux-bios/syslinux")
	if err != nil {
		log.Print(err)
		return
	}
	if err := os.Mkdir("disk/syslinux", 0777); err != nil {
		log.Print(err)
	}
	for _, f := range fi {
		log.Printf("Process %v", f)
		b, err := ioutil.ReadFile(filepath.Join("img/syslinux-bios/syslinux", f.Name()))
		if err != nil {
			log.Print(err)
			return
		}
		if err := ioutil.WriteFile(filepath.Join("disk/syslinux/", f.Name()), b, 0666); err != nil {
			log.Print(err)
			return
		}
	}
	if err := ioutil.WriteFile(filepath.Join("disk/syslinux/", "syslinux.cfg"), syslinuxconfig, 0666); err != nil {
		log.Print(err)
		return
	}
}
<commit_msg>syslinux: use $HARVEY environment variable (#37)<commit_after>package main

import (
	"bytes"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

var (
	part = `label: dos
label-id: 0xadc9f7b1
device: /dev/nbd0
unit: sectors

/dev/nbd0p1 : start=        2048, size=      102400, type=83, bootable
/dev/nbd0p2 : start=      104448, size=     6187008, type=39
`
	syslinuxconfig = []byte(`
TIMEOUT 50
DEFAULT vesamenu.c32
#DEFAULT 0
MENU TITLE Harvey OS
PROMPT 0
ONTIMEOUT 0

LABEL 0
TEXT HELP
Boot Harvey kernel with local filesystem
ENDTEXT
MENU LABEL Harvey OS (Local FS)
KERNEL mboot.c32
APPEND ../harvey service=cpu nobootprompt=local!#S/sdE0/ maxcores=1024 nvram=/boot/nvram nvrlen=512 nvroff=0 acpiirq=1 mouseport=ps2 vgasize=1024x768x24 monitor=vesa

LABEL 1
TEXT HELP
Boot Harvey kernel with remote filesystem (10.0.2.2)
ENDTEXT
MENU LABEL Harvey OS (Remote FS)
KERNEL mboot.c32
APPEND ../harvey service=cpu nobootprompt=tcp maxcores=1024 fs=10.0.2.2 auth=10.0.2.2 nvram=/boot/nvram nvrlen=512 nvroff=0 acpiirq=1 mouseport=ps2 vgasize=1024x768x24 monitor=vesa

LABEL 2
TEXT HELP
Install harvey on image
ENDTEXT
MENU LABEL Harvey OS (Install)
KERNEL mboot.c32
APPEND ../harvey service=cpu nobootprompt=tcp maxcores=1024 fs=10.0.2.2 auth=10.0.2.2 nvram=/boot/nvram nvrlen=512 nvroff=0 acpiirq=1 mouseport=ps2 vgasize=1024x768x24 monitor=vesa onstartup=/util/img/prepfs
`)
)

type command struct {
	*exec.Cmd
	in io.Reader
}

func gencmds(root, imgType string) []command {
	imgFilename := "harvey." + imgType
	mbr := filepath.Join(root, "util/img/syslinux-bios/mbr.bin")
	return []command{
		{Cmd: exec.Command("qemu-img", "create", "-f", imgType, imgFilename, "3G")},
		{Cmd: exec.Command("chown", os.ExpandEnv("$SUDO_USER"), imgFilename)},
		{Cmd: exec.Command("chgrp", os.ExpandEnv("$SUDO_USER"), imgFilename)},
		{Cmd: exec.Command("modprobe", "nbd", "max_part=63")},
		{Cmd: exec.Command("qemu-nbd", "-c", "/dev/nbd0", "-f", imgType, imgFilename)},
		{Cmd: exec.Command("dd", "conv=notrunc", "bs=440", "count=1", "if="+mbr, "of=/dev/nbd0")},
		{Cmd: exec.Command("sfdisk", "/dev/nbd0"), in: bytes.NewBufferString(part)},
		{Cmd: exec.Command("mkfs.ext4", "/dev/nbd0p1")},
		{Cmd: exec.Command("mkdir", "-p", "disk/")},
		{Cmd: exec.Command("mount", "/dev/nbd0p1", "disk/")},
		{Cmd: exec.Command("extlinux", "-i", "disk/")},
		{Cmd: exec.Command("cp", filepath.Join(root, "sys/src/9/amd64/harvey.32bit"), "disk/harvey")},
		{Cmd: exec.Command("ls", "-l", "disk")},
	}
}

func detach() {
	log.Println("Detaching")
	if err := exec.Command("umount", "disk").Run(); err != nil {
		log.Print(err)
	}
	if err := exec.Command("qemu-nbd", "-d", "/dev/nbd0").Run(); err != nil {
		log.Print(err)
	}
}

func main() {
	root := os.Getenv("HARVEY")
	if root == "" {
		log.Fatalf("$HARVEY environment variable is not set")
	}
	imgType := flag.String("imgtype", "raw", "Type of image to create (raw, qcow2)")
	flag.Parse()
	if *imgType != "qcow2" && *imgType != "raw" {
		log.Println("Invalid imgtype")
		return
	}

	if os.Getuid() != 0 {
		log.Println("You have to run me as root")
		return
	}

	defer detach()

	setupcmds := gencmds(root, *imgType)
	for _, c := range setupcmds {
		log.Printf("cmd: %v", c)
		c.Stdin, c.Stdout, c.Stderr = c.in, os.Stdout, os.Stderr
		if err := c.Run(); err != nil {
			log.Printf("%v: %v", c.Cmd, err)
			return
		}
	}

	dir := filepath.Join(root, "util/img/syslinux-bios/syslinux")
	fi, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Print(err)
		return
	}
	if err := os.Mkdir("disk/syslinux", 0777); err != nil {
		log.Print(err)
	}
	for _, f := range fi {
		log.Printf("Process %v", f)
		b, err := ioutil.ReadFile(filepath.Join(dir, f.Name()))
		if err != nil {
			log.Print(err)
			return
		}
		if err := ioutil.WriteFile(filepath.Join("disk/syslinux/", f.Name()), b, 0666); err != nil {
			log.Print(err)
			return
		}
	}
	if err := ioutil.WriteFile(filepath.Join("disk/syslinux/", "syslinux.cfg"), syslinuxconfig, 0666); err != nil {
		log.Print(err)
		return
	}
}
<|endoftext|>