<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/pborman/uuid"
	log "gopkg.in/inconshreveable/log15.v2"

	"github.com/lxc/lxd/shared"
)

type eventsHandler struct {
}

func logContextMap(ctx []interface{}) map[string]string {
	var key string
	ctxMap := map[string]string{}

	for _, entry := range ctx {
		if key == "" {
			key = entry.(string)
		} else {
			ctxMap[key] = fmt.Sprintf("%s", entry)
			key = ""
		}
	}

	return ctxMap
}

func (h eventsHandler) Log(r *log.Record) error {
	eventSend("logging", shared.Jmap{
		"message": r.Msg,
		"level":   r.Lvl.String(),
		"context": logContextMap(r.Ctx)})
	return nil
}

var eventsLock sync.Mutex
var eventListeners map[string]*eventListener = make(map[string]*eventListener)

type eventListener struct {
	connection   *websocket.Conn
	messageTypes []string
	active       chan bool
	id           string
	msgLock      sync.Mutex
}

type eventsServe struct {
	req *http.Request
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(r *http.Request, w http.ResponseWriter) error {
	listener := eventListener{}

	typeStr := r.FormValue("type")
	if typeStr == "" {
		typeStr = "logging,operation"
	}

	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}

	listener.active = make(chan bool, 1)
	listener.connection = c
	listener.id = uuid.NewRandom().String()
	listener.messageTypes = strings.Split(typeStr, ",")

	eventsLock.Lock()
	eventListeners[listener.id] = &listener
	eventsLock.Unlock()

	shared.Debugf("New events listener: %s", listener.id)

	<-listener.active

	eventsLock.Lock()
	delete(eventListeners, listener.id)
	eventsLock.Unlock()

	listener.connection.Close()
	shared.Debugf("Disconnected events listener: %s", listener.id)

	return nil
}

func eventsGet(d *Daemon, r *http.Request) Response {
	return &eventsServe{r}
}

var eventsCmd = Command{name: "events", get: eventsGet}

func eventSend(eventType string, eventMessage interface{}) error {
	event := shared.Jmap{}
	event["type"] = eventType
	event["timestamp"] = time.Now()
	event["metadata"] = eventMessage

	body, err := json.Marshal(event)
	if err != nil {
		return err
	}

	eventsLock.Lock()
	listeners := eventListeners
	for _, listener := range listeners {
		if !shared.StringInSlice(eventType, listener.messageTypes) {
			continue
		}

		go func(listener *eventListener, body []byte) {
			if listener == nil {
				return
			}

			listener.msgLock.Lock()
			err = listener.connection.WriteMessage(websocket.TextMessage, body)
			listener.msgLock.Unlock()

			if err != nil {
				listener.active <- false
			}
		}(listener, body)
	}
	eventsLock.Unlock()

	return nil
}
<commit_msg>lxd/events: Debugf() --> LogDebugf()<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/pborman/uuid"
	log "gopkg.in/inconshreveable/log15.v2"

	"github.com/lxc/lxd/shared"
)

type eventsHandler struct {
}

func logContextMap(ctx []interface{}) map[string]string {
	var key string
	ctxMap := map[string]string{}

	for _, entry := range ctx {
		if key == "" {
			key = entry.(string)
		} else {
			ctxMap[key] = fmt.Sprintf("%s", entry)
			key = ""
		}
	}

	return ctxMap
}

func (h eventsHandler) Log(r *log.Record) error {
	eventSend("logging", shared.Jmap{
		"message": r.Msg,
		"level":   r.Lvl.String(),
		"context": logContextMap(r.Ctx)})
	return nil
}

var eventsLock sync.Mutex
var eventListeners map[string]*eventListener = make(map[string]*eventListener)

type eventListener struct {
	connection   *websocket.Conn
	messageTypes []string
	active       chan bool
	id           string
	msgLock      sync.Mutex
}

type eventsServe struct {
	req *http.Request
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(r *http.Request, w http.ResponseWriter) error {
	listener := eventListener{}

	typeStr := r.FormValue("type")
	if typeStr == "" {
		typeStr = "logging,operation"
	}

	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}

	listener.active = make(chan bool, 1)
	listener.connection = c
	listener.id = uuid.NewRandom().String()
	listener.messageTypes = strings.Split(typeStr, ",")

	eventsLock.Lock()
	eventListeners[listener.id] = &listener
	eventsLock.Unlock()

	shared.LogDebugf("New events listener: %s", listener.id)

	<-listener.active

	eventsLock.Lock()
	delete(eventListeners, listener.id)
	eventsLock.Unlock()

	listener.connection.Close()
	shared.LogDebugf("Disconnected events listener: %s", listener.id)

	return nil
}

func eventsGet(d *Daemon, r *http.Request) Response {
	return &eventsServe{r}
}

var eventsCmd = Command{name: "events", get: eventsGet}

func eventSend(eventType string, eventMessage interface{}) error {
	event := shared.Jmap{}
	event["type"] = eventType
	event["timestamp"] = time.Now()
	event["metadata"] = eventMessage

	body, err := json.Marshal(event)
	if err != nil {
		return err
	}

	eventsLock.Lock()
	listeners := eventListeners
	for _, listener := range listeners {
		if !shared.StringInSlice(eventType, listener.messageTypes) {
			continue
		}

		go func(listener *eventListener, body []byte) {
			if listener == nil {
				return
			}

			listener.msgLock.Lock()
			err = listener.connection.WriteMessage(websocket.TextMessage, body)
			listener.msgLock.Unlock()

			if err != nil {
				listener.active <- false
			}
		}(listener, body)
	}
	eventsLock.Unlock()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/pborman/uuid"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

type eventsHandler struct {
}

func logContextMap(ctx []interface{}) map[string]string {
	var key string
	ctxMap := map[string]string{}

	for _, entry := range ctx {
		if key == "" {
			key = entry.(string)
		} else {
			ctxMap[key] = fmt.Sprintf("%v", entry)
			key = ""
		}
	}

	return ctxMap
}

func (h eventsHandler) Log(r *log.Record) error {
	eventSend("logging", api.EventLogging{
		Message: r.Msg,
		Level:   r.Lvl.String(),
		Context: logContextMap(r.Ctx)})
	return nil
}

func eventSendLifecycle(action, source string,
	context map[string]interface{}) error {
	eventSend("lifecycle", api.EventLifecycle{
		Action:  action,
		Source:  source,
		Context: context})
	return nil
}

var eventsLock sync.Mutex
var eventListeners map[string]*eventListener = make(map[string]*eventListener)

type eventListener struct {
	connection   *websocket.Conn
	messageTypes []string
	active       chan bool
	id           string
	lock         sync.Mutex
	done         bool

	// If true, this listener won't get events forwarded from other
	// nodes. It only used by listeners created internally by LXD nodes
	// connecting to other LXD nodes to get their local events only.
	noForward bool
}

type eventsServe struct {
	req *http.Request
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(r *http.Request, w http.ResponseWriter) error {
	typeStr := r.FormValue("type")
	if typeStr == "" {
		typeStr = "logging,operation,lifecycle"
	}

	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}

	listener := eventListener{
		active:       make(chan bool, 1),
		connection:   c,
		id:           uuid.NewRandom().String(),
		messageTypes: strings.Split(typeStr, ","),
	}

	// If this request is an internal one initiated by another node wanting
	// to watch the events on this node, set the listener to broadcast only
	// local events.
	listener.noForward = isClusterNotification(r)

	eventsLock.Lock()
	eventListeners[listener.id] = &listener
	eventsLock.Unlock()

	logger.Debugf("New event listener: %s", listener.id)

	<-listener.active

	return nil
}

func eventsGet(d *Daemon, r *http.Request) Response {
	return &eventsServe{req: r}
}

var eventsCmd = Command{name: "events", get: eventsGet}

func eventSend(eventType string, eventMessage interface{}) error {
	event := shared.Jmap{}
	event["type"] = eventType
	event["timestamp"] = time.Now()
	event["metadata"] = eventMessage

	return eventBroadcast(event)
}

func eventBroadcast(event shared.Jmap) error {
	body, err := json.Marshal(event)
	if err != nil {
		return err
	}

	_, isForward := event["node"]
	eventsLock.Lock()
	listeners := eventListeners
	for _, listener := range listeners {
		if isForward && listener.noForward {
			continue
		}

		if !shared.StringInSlice(event["type"].(string), listener.messageTypes) {
			continue
		}

		go func(listener *eventListener, body []byte) {
			// Check that the listener still exists
			if listener == nil {
				return
			}

			// Ensure there is only a single even going out at the time
			listener.lock.Lock()
			defer listener.lock.Unlock()

			// Make sure we're not done already
			if listener.done {
				return
			}

			err = listener.connection.WriteMessage(websocket.TextMessage, body)
			if err != nil {
				// Remove the listener from the list
				eventsLock.Lock()
				delete(eventListeners, listener.id)
				eventsLock.Unlock()

				// Disconnect the listener
				listener.connection.Close()
				listener.active <- false
				listener.done = true
				logger.Debugf("Disconnected event listener: %s", listener.id)
			}
		}(listener, body)
	}
	eventsLock.Unlock()

	return nil
}

// Forward to the local events dispatcher an event received from another node .
func eventForward(id int64, data interface{}) {
	event := data.(map[string]interface{})
	event["node"] = id

	err := eventBroadcast(event)
	if err != nil {
		logger.Warnf("Failed to forward event from node %d: %v", id, err)
	}
}
<commit_msg>events: move structs<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/pborman/uuid"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

var eventsCmd = Command{
	name: "events",
	get:  eventsGet,
}

type eventsHandler struct {
}

func logContextMap(ctx []interface{}) map[string]string {
	var key string
	ctxMap := map[string]string{}

	for _, entry := range ctx {
		if key == "" {
			key = entry.(string)
		} else {
			ctxMap[key] = fmt.Sprintf("%v", entry)
			key = ""
		}
	}

	return ctxMap
}

func (h eventsHandler) Log(r *log.Record) error {
	eventSend("logging", api.EventLogging{
		Message: r.Msg,
		Level:   r.Lvl.String(),
		Context: logContextMap(r.Ctx)})
	return nil
}

func eventSendLifecycle(action, source string,
	context map[string]interface{}) error {
	eventSend("lifecycle", api.EventLifecycle{
		Action:  action,
		Source:  source,
		Context: context})
	return nil
}

var eventsLock sync.Mutex
var eventListeners map[string]*eventListener = make(map[string]*eventListener)

type eventListener struct {
	connection   *websocket.Conn
	messageTypes []string
	active       chan bool
	id           string
	lock         sync.Mutex
	done         bool

	// If true, this listener won't get events forwarded from other
	// nodes. It only used by listeners created internally by LXD nodes
	// connecting to other LXD nodes to get their local events only.
	noForward bool
}

type eventsServe struct {
	req *http.Request
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(r *http.Request, w http.ResponseWriter) error {
	typeStr := r.FormValue("type")
	if typeStr == "" {
		typeStr = "logging,operation,lifecycle"
	}

	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}

	listener := eventListener{
		active:       make(chan bool, 1),
		connection:   c,
		id:           uuid.NewRandom().String(),
		messageTypes: strings.Split(typeStr, ","),
	}

	// If this request is an internal one initiated by another node wanting
	// to watch the events on this node, set the listener to broadcast only
	// local events.
	listener.noForward = isClusterNotification(r)

	eventsLock.Lock()
	eventListeners[listener.id] = &listener
	eventsLock.Unlock()

	logger.Debugf("New event listener: %s", listener.id)

	<-listener.active

	return nil
}

func eventsGet(d *Daemon, r *http.Request) Response {
	return &eventsServe{req: r}
}

func eventSend(eventType string, eventMessage interface{}) error {
	event := shared.Jmap{}
	event["type"] = eventType
	event["timestamp"] = time.Now()
	event["metadata"] = eventMessage

	return eventBroadcast(event)
}

func eventBroadcast(event shared.Jmap) error {
	body, err := json.Marshal(event)
	if err != nil {
		return err
	}

	_, isForward := event["node"]
	eventsLock.Lock()
	listeners := eventListeners
	for _, listener := range listeners {
		if isForward && listener.noForward {
			continue
		}

		if !shared.StringInSlice(event["type"].(string), listener.messageTypes) {
			continue
		}

		go func(listener *eventListener, body []byte) {
			// Check that the listener still exists
			if listener == nil {
				return
			}

			// Ensure there is only a single even going out at the time
			listener.lock.Lock()
			defer listener.lock.Unlock()

			// Make sure we're not done already
			if listener.done {
				return
			}

			err = listener.connection.WriteMessage(websocket.TextMessage, body)
			if err != nil {
				// Remove the listener from the list
				eventsLock.Lock()
				delete(eventListeners, listener.id)
				eventsLock.Unlock()

				// Disconnect the listener
				listener.connection.Close()
				listener.active <- false
				listener.done = true
				logger.Debugf("Disconnected event listener: %s", listener.id)
			}
		}(listener, body)
	}
	eventsLock.Unlock()

	return nil
}

// Forward to the local events dispatcher an event received from another node .
func eventForward(id int64, data interface{}) {
	event := data.(map[string]interface{})
	event["node"] = id

	err := eventBroadcast(event)
	if err != nil {
		logger.Warnf("Failed to forward event from node %d: %v", id, err)
	}
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"
	"encoding/json"
	"errors"
	"io"
	"net"

	"code.google.com/p/go-uuid/uuid"
	"github.com/mistifyio/mistify-operator-admin/db"
)

type (
	IPRange struct {
		ID          string            `json:"id"`
		CIDR        *net.IPNet        `json:"cidr"`
		Gateway     net.IP            `json:"gateway"`
		Start       net.IP            `json:"start"`
		End         net.IP            `json:"end"`
		Metadata    map[string]string `json:"metadata"`
		Network     *Network          `json:"-"`
		Hypervisors []*Hypervisor     `json:"-"`
	}

	ipRangeData struct {
		ID       string            `json:"id"`
		CIDR     string            `json:"cidr"`
		Gateway  string            `json:"gateway"`
		Start    string            `json:"start"`
		End      string            `json:"end"`
		Metadata map[string]string `json:"metadata"`
	}
)

func (iprange *IPRange) id() string {
	return iprange.ID
}

func (iprange *IPRange) pkeyName() string {
	return "iprange_id"
}

func (iprange *IPRange) importData(data *ipRangeData) error {
	_, cidr, err := net.ParseCIDR(data.CIDR)
	if err != nil {
		return err
	}
	iprange.ID = data.ID
	iprange.CIDR = cidr
	iprange.Gateway = net.ParseIP(data.Gateway)
	iprange.Start = net.ParseIP(data.Start)
	iprange.End = net.ParseIP(data.End)
	iprange.Metadata = data.Metadata
	return nil
}

func (iprange *IPRange) exportData() *ipRangeData {
	return &ipRangeData{
		ID:       iprange.ID,
		CIDR:     fmtString(iprange.CIDR),
		Gateway:  fmtString(iprange.Gateway),
		Start:    fmtString(iprange.Start),
		End:      fmtString(iprange.End),
		Metadata: iprange.Metadata,
	}
}

func (iprange *IPRange) UnmarshalJSON(b []byte) error {
	data := &ipRangeData{}
	if err := json.Unmarshal(b, data); err != nil {
		return err
	}
	if err := iprange.importData(data); err != nil {
		return err
	}
	return nil
}

func (iprange IPRange) MarshalJSON() ([]byte, error) {
	return json.Marshal(iprange.exportData())
}

func (iprange *IPRange) Validate() error {
	if iprange.ID == "" {
		return errors.New("missing id")
	}
	if uuid.Parse(iprange.ID) == nil {
		return errors.New("invalid id. must be uuid")
	}
	if iprange.CIDR == nil {
		return errors.New("missing cidr")
	}
	if iprange.Gateway == nil {
		return errors.New("missing gateway")
	}
	if iprange.Start == nil {
		return errors.New("missing start ip")
	}
	if iprange.End == nil {
		return errors.New("missing end ip")
	}
	return nil
}

func (iprange *IPRange) Save() error {
	if err := iprange.Validate(); err != nil {
		return err
	}
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	// Writable CTE for an Upsert
	// See: http://stackoverflow.com/a/8702291
	// And: http://dba.stackexchange.com/a/78535
	sql := `
	WITH new_values (iprange_id, cidr, gateway, start_ip, end_ip, metadata) as (
		VALUES ($1::uuid, $2::cidr, $3::inet, $4::inet, $5::inet, $6::json)
	),
	upsert as (
		UPDATE ipranges i SET
			cidr = nv.cidr,
			gateway = nv.gateway,
			start_ip = nv.start_ip,
			end_ip = nv.end_ip,
			metadata = nv.metadata
		FROM new_values nv
		WHERE i.iprange_id = nv.iprange_id
		RETURNING i.network_id
	)
	INSERT INTO ipranges
		(iprange_id, cidr, gateway, start_ip, end_ip, metadata)
	SELECT iprange_id, cidr, gateway, start_ip, end_ip, metadata
	FROM new_values nv
	WHERE NOT EXISTS (SELECT 1 FROM upsert u WHERE nv.iprange_id = u.iprange_id)
    `
	data := iprange.exportData()
	metadata, err := json.Marshal(data.Metadata)
	if err != nil {
		return err
	}
	_, err = d.Exec(sql,
		data.ID,
		data.CIDR,
		data.Gateway,
		data.Start,
		data.End,
		string(metadata),
	)
	return err
}

func (iprange *IPRange) Delete() error {
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	sql := "DELETE FROM ipranges WHERE iprange_id = $1"
	_, err = d.Exec(sql, iprange.ID)
	return err
}

func (iprange *IPRange) Load() error {
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	sql := `
	SELECT iprange_id, cidr, gateway, start_ip, end_ip, metadata
	FROM ipranges
	WHERE iprange_id = $1
	`
	rows, err := d.Query(sql, iprange.ID)
	if err != nil {
		return err
	}
	defer rows.Close()
	rows.Next()
	if err := iprange.fromRows(rows); err != nil {
		return err
	}
	return rows.Err()
}

func (iprange *IPRange) fromRows(rows *sql.Rows) error {
	var metadata string
	data := &ipRangeData{}
	err := rows.Scan(
		&data.ID,
		&data.CIDR,
		&data.Gateway,
		&data.Start,
		&data.End,
		&metadata,
	)
	if err != nil {
		return err
	}
	if err := json.Unmarshal([]byte(metadata), &data.Metadata); err != nil {
		return err
	}
	return iprange.importData(data)
}

func (iprange *IPRange) Decode(data io.Reader) error {
	if err := json.NewDecoder(data).Decode(iprange); err != nil {
		return err
	}
	if iprange.Metadata == nil {
		iprange.Metadata = make(map[string]string)
	} else {
		for key, value := range iprange.Metadata {
			if value == "" {
				delete(iprange.Metadata, key)
			}
		}
	}
	return nil
}

func (iprange *IPRange) LoadHypervisors() error {
	hypervisors, err := HypervisorsByIPRange(iprange)
	if err != nil {
		return err
	}
	iprange.Hypervisors = hypervisors
	return nil
}

func (iprange *IPRange) AddHypervisor(hypervisor *Hypervisor) error {
	return AddRelation("hypervisors_ipranges", iprange, hypervisor)
}

func (iprange *IPRange) RemoveHypervisor(hypervisor *Hypervisor) error {
	return RemoveRelation("hypervisors_ipranges", iprange, hypervisor)
}

func (iprange *IPRange) SetHypervisors(hypervisors []*Hypervisor) error {
	if len(hypervisors) == 0 {
		return ClearRelations("hypervisors_ipranges", iprange)
	}
	relatables := make([]relatable, len(hypervisors))
	for i, hypervisor := range hypervisors {
		relatables[i] = relatable(hypervisor)
	}
	if err := SetRelations("hypervisors_ipranges", iprange, relatables); err != nil {
		return err
	}
	return iprange.LoadHypervisors()
}

func (iprange *IPRange) LoadNetwork() error {
	networks, err := NetworksByIPRange(iprange)
	if err != nil {
		return err
	}
	if len(networks) > 0 {
		iprange.Network = networks[0]
	}
	return nil
}
func (iprange *IPRange) SetNetwork(network *Network) error {
	// Only one can be set at a time
	relatables := make([]relatable, 1)
	relatables[0] = relatable(network)
	return SetRelations("iprange_networks", iprange, relatables)
}

func (iprange *IPRange) RemoveNetwork(network *Network) error {
	return RemoveRelation("iprange_networks", iprange, network)
}
func (iprange *IPRange) NewID() string {
	iprange.ID = uuid.New()
	return iprange.ID
}

func NewIPRange() *IPRange {
	iprange := &IPRange{
		ID:       uuid.New(),
		Metadata: make(map[string]string),
	}
	return iprange
}

func FetchIPRange(id string) (*IPRange, error) {
	iprange := &IPRange{
		ID: id,
	}
	if err := iprange.Load(); err != nil {
		return nil, err
	}
	return iprange, nil
}

func ListIPRanges() ([]*IPRange, error) {
	d, err := db.Connect(nil)
	if err != nil {
		return nil, err
	}
	sql := `
	SELECT iprange_id, cidr, gateway, start_ip, end_ip, metadata
	FROM ipranges
	ORDER BY iprange_id
	`
	rows, err := d.Query(sql)
	if err != nil {
		return nil, err
	}
	ipranges := make([]*IPRange, 0, 1)
	for rows.Next() {
		iprange := &IPRange{}
		if err := iprange.fromRows(rows); err != nil {
			return nil, err
		}
		ipranges = append(ipranges, iprange)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}
	return ipranges, nil
}

func IPRangesByHypervisor(hypervisor *Hypervisor) ([]*IPRange, error) {
	d, err := db.Connect(nil)
	if err != nil {
		return nil, err
	}
	sql := `
	SELECT i.iprange_id, i.cidr, i.gateway, i.start_ip, i.end_ip, i.metadata
	FROM ipranges i
	JOIN hypervisors_ipranges hi ON i.iprange_id = hi.iprange_id
	WHERE hi.hypervisor_id = $1
	ORDER BY i.iprange_id asc
	`
	rows, err := d.Query(sql, hypervisor.ID)
	if err != nil {
		return nil, err
	}
	ipranges, err := iprangesFromRows(rows)
	if err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return ipranges, nil
}

func IPRangesByNetwork(network *Network) ([]*IPRange, error) {
	d, err := db.Connect(nil)
	if err != nil {
		return nil, err
	}
	sql := `
	SELECT i.iprange_id, i.cidr, i.gateway, i.start_ip, i.end_ip, i.metadata
	FROM ipranges i
	JOIN iprange_networks i_n ON i.iprange_id = i_n.iprange_id
	WHERE i_n.network_id = $1
	ORDER BY i.iprange_id asc
	`
	rows, err := d.Query(sql, network.ID)
	if err != nil {
		return nil, err
	}
	ipranges, err := iprangesFromRows(rows)
	if err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return ipranges, nil
}

func iprangesFromRows(rows *sql.Rows) ([]*IPRange, error) {
	ipranges := make([]*IPRange, 0, 1)
	for rows.Next() {
		iprange := &IPRange{}
		if err := iprange.fromRows(rows); err != nil {
			return nil, err
		}
		ipranges = append(ipranges, iprange)
	}
	return ipranges, nil
}
<commit_msg>MIST-227 Fix typo<commit_after>package models

import (
	"database/sql"
	"encoding/json"
	"errors"
	"io"
	"net"

	"code.google.com/p/go-uuid/uuid"
	"github.com/mistifyio/mistify-operator-admin/db"
)

type (
	IPRange struct {
		ID          string            `json:"id"`
		CIDR        *net.IPNet        `json:"cidr"`
		Gateway     net.IP            `json:"gateway"`
		Start       net.IP            `json:"start"`
		End         net.IP            `json:"end"`
		Metadata    map[string]string `json:"metadata"`
		Network     *Network          `json:"-"`
		Hypervisors []*Hypervisor     `json:"-"`
	}

	ipRangeData struct {
		ID       string            `json:"id"`
		CIDR     string            `json:"cidr"`
		Gateway  string            `json:"gateway"`
		Start    string            `json:"start"`
		End      string            `json:"end"`
		Metadata map[string]string `json:"metadata"`
	}
)

func (iprange *IPRange) id() string {
	return iprange.ID
}

func (iprange *IPRange) pkeyName() string {
	return "iprange_id"
}

func (iprange *IPRange) importData(data *ipRangeData) error {
	_, cidr, err := net.ParseCIDR(data.CIDR)
	if err != nil {
		return err
	}
	iprange.ID = data.ID
	iprange.CIDR = cidr
	iprange.Gateway = net.ParseIP(data.Gateway)
	iprange.Start = net.ParseIP(data.Start)
	iprange.End = net.ParseIP(data.End)
	iprange.Metadata = data.Metadata
	return nil
}

func (iprange *IPRange) exportData() *ipRangeData {
	return &ipRangeData{
		ID:       iprange.ID,
		CIDR:     fmtString(iprange.CIDR),
		Gateway:  fmtString(iprange.Gateway),
		Start:    fmtString(iprange.Start),
		End:      fmtString(iprange.End),
		Metadata: iprange.Metadata,
	}
}

func (iprange *IPRange) UnmarshalJSON(b []byte) error {
	data := &ipRangeData{}
	if err := json.Unmarshal(b, data); err != nil {
		return err
	}
	if err := iprange.importData(data); err != nil {
		return err
	}
	return nil
}

func (iprange IPRange) MarshalJSON() ([]byte, error) {
	return json.Marshal(iprange.exportData())
}

func (iprange *IPRange) Validate() error {
	if iprange.ID == "" {
		return errors.New("missing id")
	}
	if uuid.Parse(iprange.ID) == nil {
		return errors.New("invalid id. must be uuid")
	}
	if iprange.CIDR == nil {
		return errors.New("missing cidr")
	}
	if iprange.Gateway == nil {
		return errors.New("missing gateway")
	}
	if iprange.Start == nil {
		return errors.New("missing start ip")
	}
	if iprange.End == nil {
		return errors.New("missing end ip")
	}
	return nil
}

func (iprange *IPRange) Save() error {
	if err := iprange.Validate(); err != nil {
		return err
	}
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	// Writable CTE for an Upsert
	// See: http://stackoverflow.com/a/8702291
	// And: http://dba.stackexchange.com/a/78535
	sql := `
	WITH new_values (iprange_id, cidr, gateway, start_ip, end_ip, metadata) as (
		VALUES ($1::uuid, $2::cidr, $3::inet, $4::inet, $5::inet, $6::json)
	),
	upsert as (
		UPDATE ipranges i SET
			cidr = nv.cidr,
			gateway = nv.gateway,
			start_ip = nv.start_ip,
			end_ip = nv.end_ip,
			metadata = nv.metadata
		FROM new_values nv
		WHERE i.iprange_id = nv.iprange_id
		RETURNING i.iprange_id
	)
	INSERT INTO ipranges
		(iprange_id, cidr, gateway, start_ip, end_ip, metadata)
	SELECT iprange_id, cidr, gateway, start_ip, end_ip, metadata
	FROM new_values nv
	WHERE NOT EXISTS (SELECT 1 FROM upsert u WHERE nv.iprange_id = u.iprange_id)
    `
	data := iprange.exportData()
	metadata, err := json.Marshal(data.Metadata)
	if err != nil {
		return err
	}
	_, err = d.Exec(sql,
		data.ID,
		data.CIDR,
		data.Gateway,
		data.Start,
		data.End,
		string(metadata),
	)
	return err
}

func (iprange *IPRange) Delete() error {
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	sql := "DELETE FROM ipranges WHERE iprange_id = $1"
	_, err = d.Exec(sql, iprange.ID)
	return err
}

func (iprange *IPRange) Load() error {
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	sql := `
	SELECT iprange_id, cidr, gateway, start_ip, end_ip, metadata
	FROM ipranges
	WHERE iprange_id = $1
	`
	rows, err := d.Query(sql, iprange.ID)
	if err != nil {
		return err
	}
	defer rows.Close()
	rows.Next()
	if err := iprange.fromRows(rows); err != nil {
		return err
	}
	return rows.Err()
}

func (iprange *IPRange) fromRows(rows *sql.Rows) error {
	var metadata string
	data := &ipRangeData{}
	err := rows.Scan(
		&data.ID,
		&data.CIDR,
		&data.Gateway,
		&data.Start,
		&data.End,
		&metadata,
	)
	if err != nil {
		return err
	}
	if err := json.Unmarshal([]byte(metadata), &data.Metadata); err != nil {
		return err
	}
	return iprange.importData(data)
}

func (iprange *IPRange) Decode(data io.Reader) error {
	if err := json.NewDecoder(data).Decode(iprange); err != nil {
		return err
	}
	if iprange.Metadata == nil {
		iprange.Metadata = make(map[string]string)
	} else {
		for key, value := range iprange.Metadata {
			if value == "" {
				delete(iprange.Metadata, key)
			}
		}
	}
	return nil
}

func (iprange *IPRange) LoadHypervisors() error {
	hypervisors, err := HypervisorsByIPRange(iprange)
	if err != nil {
		return err
	}
	iprange.Hypervisors = hypervisors
	return nil
}

func (iprange *IPRange) AddHypervisor(hypervisor *Hypervisor) error {
	return AddRelation("hypervisors_ipranges", iprange, hypervisor)
}

func (iprange *IPRange) RemoveHypervisor(hypervisor *Hypervisor) error {
	return RemoveRelation("hypervisors_ipranges", iprange, hypervisor)
}

func (iprange *IPRange) SetHypervisors(hypervisors []*Hypervisor) error {
	if len(hypervisors) == 0 {
		return ClearRelations("hypervisors_ipranges", iprange)
	}
	relatables := make([]relatable, len(hypervisors))
	for i, hypervisor := range hypervisors {
		relatables[i] = relatable(hypervisor)
	}
	if err := SetRelations("hypervisors_ipranges", iprange, relatables); err != nil {
		return err
	}
	return iprange.LoadHypervisors()
}

func (iprange *IPRange) LoadNetwork() error {
	networks, err := NetworksByIPRange(iprange)
	if err != nil {
		return err
	}
	if len(networks) > 0 {
		iprange.Network = networks[0]
	}
	return nil
}
func (iprange *IPRange) SetNetwork(network *Network) error {
	// Only one can be set at a time
	relatables := make([]relatable, 1)
	relatables[0] = relatable(network)
	return SetRelations("iprange_networks", iprange, relatables)
}

func (iprange *IPRange) RemoveNetwork(network *Network) error {
	return RemoveRelation("iprange_networks", iprange, network)
}
func (iprange *IPRange) NewID() string {
	iprange.ID = uuid.New()
	return iprange.ID
}

func NewIPRange() *IPRange {
	iprange := &IPRange{
		ID:       uuid.New(),
		Metadata: make(map[string]string),
	}
	return iprange
}

func FetchIPRange(id string) (*IPRange, error) {
	iprange := &IPRange{
		ID: id,
	}
	if err := iprange.Load(); err != nil {
		return nil, err
	}
	return iprange, nil
}

func ListIPRanges() ([]*IPRange, error) {
	d, err := db.Connect(nil)
	if err != nil {
		return nil, err
	}
	sql := `
	SELECT iprange_id, cidr, gateway, start_ip, end_ip, metadata
	FROM ipranges
	ORDER BY iprange_id
	`
	rows, err := d.Query(sql)
	if err != nil {
		return nil, err
	}
	ipranges := make([]*IPRange, 0, 1)
	for rows.Next() {
		iprange := &IPRange{}
		if err := iprange.fromRows(rows); err != nil {
			return nil, err
		}
		ipranges = append(ipranges, iprange)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}
	return ipranges, nil
}

func IPRangesByHypervisor(hypervisor *Hypervisor) ([]*IPRange, error) {
	d, err := db.Connect(nil)
	if err != nil {
		return nil, err
	}
	sql := `
	SELECT i.iprange_id, i.cidr, i.gateway, i.start_ip, i.end_ip, i.metadata
	FROM ipranges i
	JOIN hypervisors_ipranges hi ON i.iprange_id = hi.iprange_id
	WHERE hi.hypervisor_id = $1
	ORDER BY i.iprange_id asc
	`
	rows, err := d.Query(sql, hypervisor.ID)
	if err != nil {
		return nil, err
	}
	ipranges, err := iprangesFromRows(rows)
	if err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return ipranges, nil
}

func IPRangesByNetwork(network *Network) ([]*IPRange, error) {
	d, err := db.Connect(nil)
	if err != nil {
		return nil, err
	}
	sql := `
	SELECT i.iprange_id, i.cidr, i.gateway, i.start_ip, i.end_ip, i.metadata
	FROM ipranges i
	JOIN iprange_networks i_n ON i.iprange_id = i_n.iprange_id
	WHERE i_n.network_id = $1
	ORDER BY i.iprange_id asc
	`
	rows, err := d.Query(sql, network.ID)
	if err != nil {
		return nil, err
	}
	ipranges, err := iprangesFromRows(rows)
	if err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return ipranges, nil
}

func iprangesFromRows(rows *sql.Rows) ([]*IPRange, error) {
	ipranges := make([]*IPRange, 0, 1)
	for rows.Next() {
		iprange := &IPRange{}
		if err := iprange.fromRows(rows); err != nil {
			return nil, err
		}
		ipranges = append(ipranges, iprange)
	}
	return ipranges, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"reflect"
)

const (
	DEFAULT_FILE_MODE = 0777
)

type Task func() error

type Constructor func(target *Target, args Object) (Task, error)

var tasksMap map[string]Constructor

func init() {
	tasksMap = map[string]Constructor{
		"go":     Go,
		"print":  Print,
		"rm":     Rm,
		"cd":     Cd,
		"mkdir":  MkDir,
		"delete": Delete,
		"if":     If,
		"for":    For,
		"while":  While,
		"try":    Try,
	}
}

// TASKS DEFINITIONS

func Go(target *Target, args Object) (Task, error) {
	source, ok := args["go"].(string)
	if !ok {
		return nil, fmt.Errorf("argument of task go must be a string")
	}
	return func() error {
		_, err := target.Build.Context.Evaluate(source)
		if err != nil {
			return fmt.Errorf("evaluating go source: %v", err)
		}
		return nil
	}, nil
}

func Print(target *Target, args Object) (Task, error) {
	message, ok := args["print"].(string)
	if !ok {
		return nil, fmt.Errorf("argument of task print must be a string")
	}
	return func() error {
		evaluated, err := target.Build.Context.ReplaceProperties(message)
		if err != nil {
			return fmt.Errorf("processing print argument: %v", err)
		}
		fmt.Println(evaluated)
		return nil
	}, nil
}

func Rm(target *Target, args Object) (Task, error) {
	file, ok := args["rm"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task rm must be a string")
	}
	return func() error {
		file, err := target.Build.Context.ReplaceProperties(file)
		fmt.Printf("Removing file '%s'\n", file)
		if err != nil {
			return fmt.Errorf("processing rm argument: %v", err)
		}
		err = os.Remove(file)
		if err != nil {
			return fmt.Errorf("removing file '%s': %s", file, err)
		}
		return nil
	}, nil
}

func Cd(target *Target, args Object) (Task, error) {
	dir, ok := args["cd"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task cd must be a string")
	}
	return func() error {
		directory, err := target.Build.Context.ReplaceProperties(dir)
		fmt.Printf("Changing to directory '%s'\n", directory)
		if err != nil {
			return fmt.Errorf("processing cd argument: %v", err)
		}
		err = os.Chdir(directory)
		if err != nil {
			return fmt.Errorf("changing to directory '%s': %s", directory, err)
		}
		return nil
	}, nil
}

func MkDir(target *Target, args Object) (Task, error) {
	dir, ok := args["mkdir"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task mkdir must be a string")
	}
	return func() error {
		directory, err := target.Build.Context.ReplaceProperties(dir)
		fmt.Printf("Making directory '%s'\n", directory)
		if err != nil {
			return fmt.Errorf("processing mkdir argument: %v", err)
		}
		err = os.MkdirAll(directory, DEFAULT_FILE_MODE)
		if err != nil {
			return fmt.Errorf("making directory '%s': %s", directory, err)
		}
		return nil
	}, nil
}

func Delete(target *Target, args Object) (Task, error) {
	directories, err := args.GetListStringsOrString("delete")
	if err != nil {
		return nil, fmt.Errorf("delete argument must be string or list of strings")
	}
	return func() error {
		for _, dir := range directories {
			directory, err := target.Build.Context.ReplaceProperties(dir)
			if err != nil {
				return fmt.Errorf("evaluating directory in task delete: %v", err)
			}
			if _, err := os.Stat(directory); err == nil {
				fmt.Printf("Deleting directory '%s'\n", directory)
				err = os.RemoveAll(directory)
				if err != nil {
					return fmt.Errorf("deleting directory '%s': %v", directory, err)
				}
			}
		}
		return nil
	}, nil
}

func If(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"if", "then", "else"}); err != nil {
		return nil, fmt.Errorf("building if condition: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"if", "then"}); err != nil {
		return nil, fmt.Errorf("building if condition: %v", err)
	}
	condition, err := args.GetString("if")
	if err != nil {
		return nil, fmt.Errorf("evaluating if construct: %v", err)
	}
	thenSteps, err := ParseSteps(target, args, "then")
	if err != nil {
		return nil, err
	}
	var elseSteps []Step
	if FieldInList("else", fields) {
		elseSteps, err = ParseSteps(target, args, "else")
		if err != nil {
			return nil, err
		}
	}
	return func() error {
		result, err := target.Build.Context.Evaluate(condition)
		if err != nil {
			return fmt.Errorf("evaluating 'if' condition: %v", err)
		}
		boolean, ok := result.(bool)
		if !ok {
			return fmt.Errorf("evaluating if condition: must return a bool")
		}
		if boolean {
			err := RunSteps(thenSteps)
			if err != nil {
				return err
			}
		} else {
			err := RunSteps(elseSteps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func For(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"for", "in", "do"}); err != nil {
		return nil, fmt.Errorf("building 'for' loop: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"for", "in", "do"}); err != nil {
		return nil, fmt.Errorf("building 'for' loop: %v", err)
	}
	variable, err := args.GetString("for")
	if err != nil {
		return nil, fmt.Errorf("'for' field of a 'for' loop must be a string")
	}
	list, err := args.GetList("in")
	expression := ""
	if err != nil {
		expression, err = args.GetString("in")
		if err != nil {
			return nil, fmt.Errorf("'in' field of 'for' loop must be a list or string")
		}
	}
	steps, err := ParseSteps(target, args, "do")
	if err != nil {
		return nil, err
	}
	return func() error {
		if expression != "" {
			result, err := target.Build.Context.Evaluate(expression)
			if err != nil {
				return fmt.Errorf("evaluating in field of for loop: %v", err)
			}
			list, err = ToList(result)
			if err != nil {
				return fmt.Errorf("'in' field of 'for' loop must be an expression that returns a list")
			}
		}
		for _, value := range list {
			target.Build.Context.SetProperty(variable, value)
			if err != nil {
				return err
			}
			err := RunSteps(steps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil

}

func While(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"while", "do"}); err != nil {
		return nil, fmt.Errorf("building 'while' loop: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"while", "do"}); err != nil {
		return nil, fmt.Errorf("building 'while' loop: %v", err)
	}
	condition, err := args.GetString("while")
	if err != nil {
		return nil, fmt.Errorf("'while' field of a 'while' loop must be a string")
	}
	steps, err := ParseSteps(target, args, "do")
	if err != nil {
		return nil, err
	}
	return func() error {
		for {
			result, err := target.Build.Context.Evaluate(condition)
			if err != nil {
				return fmt.Errorf("evaluating 'while' field of 'while' loop: %v", err)
			}
			loop, ok := result.(bool)
			if !ok {
				return fmt.Errorf("evaluating 'while' condition: must return a bool")
			}
			if !loop {
				break
			}
			err = RunSteps(steps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func Try(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"try", "catch"}); err != nil {
		return nil, fmt.Errorf("building try construct: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"try", "catch"}); err != nil {
		return nil, fmt.Errorf("building try construct: %v", err)
	}
	trySteps, err := ParseSteps(target, args, "try")
	if err != nil {
		return nil, err
	}
	catchSteps, err := ParseSteps(target, args, "catch")
	if err != nil {
		return nil, err
	}
	return func() error {
		err := RunSteps(trySteps)
		if err != nil {
			target.Build.Context.SetProperty("error", err.Error())
			err = RunSteps(catchSteps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

// UTILITY FUNCTIONS

func FieldsInList(fields, list []string) error {
	for _, field := range fields {
		found := false
		for _, e := range list {
			if e == field {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("unknown field '%s'", field)
		}
	}
	return nil
}

func FieldInList(field string, list []string) bool {
	for _, f := range list {
		if f == field {
			return true
		}
	}
	return false
}

func FieldsMandatory(fields, mandatory []string) error {
	for _, manda := range mandatory {
		found := false
		for _, field := range fields {
			if manda == field {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("mandatory field '%s' not found", manda)
		}
	}
	return nil
}

func ParseSteps(target *Target, object Object, field string) ([]Step, error) {
	list, err := object.GetList(field)
	if err != nil {
		return nil, err
	}
	var steps []Step
	for index, element := range list {
		target.Build.Log("Parsing step %v in %s field", index, field)
		step, err := NewStep(target, element)
		if err != nil {
			return nil, fmt.Errorf("parsing target '%s': %v", target.Name, err)
		}
		steps = append(steps, step)
	}
	return steps, nil
}

func RunSteps(steps []Step) error {
	for _, step := range steps {
		err := step.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func ToList(object interface{}) ([]interface{}, error) {
	slice := reflect.ValueOf(object)
	if slice.Kind() == reflect.Slice {
		result := make([]interface{}, slice.Len())
		for i := 0; i < slice.Len(); i++ {
			result[i] = slice.Index(i).Interface()
		}
		return result, nil
	} else {
		return nil, fmt.Errorf("must be a list")
	}
}
<commit_msg>Added nop task<commit_after>package main

import (
	"fmt"
	"os"
	"reflect"
)

const (
	DEFAULT_FILE_MODE = 0777
)

type Task func() error

type Constructor func(target *Target, args Object) (Task, error)

var tasksMap map[string]Constructor

func init() {
	tasksMap = map[string]Constructor{
		"go":     Go,
		"print":  Print,
		"rm":     Rm,
		"cd":     Cd,
		"mkdir":  MkDir,
		"delete": Delete,
		"if":     If,
		"for":    For,
		"while":  While,
		"try":    Try,
		"nop":    Nop,
	}
}

// TASKS DEFINITIONS

func Go(target *Target, args Object) (Task, error) {
	source, ok := args["go"].(string)
	if !ok {
		return nil, fmt.Errorf("argument of task go must be a string")
	}
	return func() error {
		_, err := target.Build.Context.Evaluate(source)
		if err != nil {
			return fmt.Errorf("evaluating go source: %v", err)
		}
		return nil
	}, nil
}

func Print(target *Target, args Object) (Task, error) {
	message, ok := args["print"].(string)
	if !ok {
		return nil, fmt.Errorf("argument of task print must be a string")
	}
	return func() error {
		evaluated, err := target.Build.Context.ReplaceProperties(message)
		if err != nil {
			return fmt.Errorf("processing print argument: %v", err)
		}
		fmt.Println(evaluated)
		return nil
	}, nil
}

func Rm(target *Target, args Object) (Task, error) {
	file, ok := args["rm"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task rm must be a string")
	}
	return func() error {
		file, err := target.Build.Context.ReplaceProperties(file)
		fmt.Printf("Removing file '%s'\n", file)
		if err != nil {
			return fmt.Errorf("processing rm argument: %v", err)
		}
		err = os.Remove(file)
		if err != nil {
			return fmt.Errorf("removing file '%s': %s", file, err)
		}
		return nil
	}, nil
}

func Cd(target *Target, args Object) (Task, error) {
	dir, ok := args["cd"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task cd must be a string")
	}
	return func() error {
		directory, err := target.Build.Context.ReplaceProperties(dir)
		fmt.Printf("Changing to directory '%s'\n", directory)
		if err != nil {
			return fmt.Errorf("processing cd argument: %v", err)
		}
		err = os.Chdir(directory)
		if err != nil {
			return fmt.Errorf("changing to directory '%s': %s", directory, err)
		}
		return nil
	}, nil
}

func MkDir(target *Target, args Object) (Task, error) {
	dir, ok := args["mkdir"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task mkdir must be a string")
	}
	return func() error {
		directory, err := target.Build.Context.ReplaceProperties(dir)
		fmt.Printf("Making directory '%s'\n", directory)
		if err != nil {
			return fmt.Errorf("processing mkdir argument: %v", err)
		}
		err = os.MkdirAll(directory, DEFAULT_FILE_MODE)
		if err != nil {
			return fmt.Errorf("making directory '%s': %s", directory, err)
		}
		return nil
	}, nil
}

func Delete(target *Target, args Object) (Task, error) {
	directories, err := args.GetListStringsOrString("delete")
	if err != nil {
		return nil, fmt.Errorf("delete argument must be string or list of strings")
	}
	return func() error {
		for _, dir := range directories {
			directory, err := target.Build.Context.ReplaceProperties(dir)
			if err != nil {
				return fmt.Errorf("evaluating directory in task delete: %v", err)
			}
			if _, err := os.Stat(directory); err == nil {
				fmt.Printf("Deleting directory '%s'\n", directory)
				err = os.RemoveAll(directory)
				if err != nil {
					return fmt.Errorf("deleting directory '%s': %v", directory, err)
				}
			}
		}
		return nil
	}, nil
}

func If(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"if", "then", "else"}); err != nil {
		return nil, fmt.Errorf("building if condition: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"if", "then"}); err != nil {
		return nil, fmt.Errorf("building if condition: %v", err)
	}
	condition, err := args.GetString("if")
	if err != nil {
		return nil, fmt.Errorf("evaluating if construct: %v", err)
	}
	thenSteps, err := ParseSteps(target, args, "then")
	if err != nil {
		return nil, err
	}
	var elseSteps []Step
	if FieldInList("else", fields) {
		elseSteps, err = ParseSteps(target, args, "else")
		if err != nil {
			return nil, err
		}
	}
	return func() error {
		result, err := target.Build.Context.Evaluate(condition)
		if err != nil {
			return fmt.Errorf("evaluating 'if' condition: %v", err)
		}
		boolean, ok := result.(bool)
		if !ok {
			return fmt.Errorf("evaluating if condition: must return a bool")
		}
		if boolean {
			err := RunSteps(thenSteps)
			if err != nil {
				return err
			}
		} else {
			err := RunSteps(elseSteps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func For(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"for", "in", "do"}); err != nil {
		return nil, fmt.Errorf("building 'for' loop: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"for", "in", "do"}); err != nil {
		return nil, fmt.Errorf("building 'for' loop: %v", err)
	}
	variable, err := args.GetString("for")
	if err != nil {
		return nil, fmt.Errorf("'for' field of a 'for' loop must be a string")
	}
	list, err := args.GetList("in")
	expression := ""
	if err != nil {
		expression, err = args.GetString("in")
		if err != nil {
			return nil, fmt.Errorf("'in' field of 'for' loop must be a list or string")
		}
	}
	steps, err := ParseSteps(target, args, "do")
	if err != nil {
		return nil, err
	}
	return func() error {
		if expression != "" {
			result, err := target.Build.Context.Evaluate(expression)
			if err != nil {
				return fmt.Errorf("evaluating in field of for loop: %v", err)
			}
			list, err = ToList(result)
			if err != nil {
				return fmt.Errorf("'in' field of 'for' loop must be an expression that returns a list")
			}
		}
		for _, value := range list {
			target.Build.Context.SetProperty(variable, value)
			if err != nil {
				return err
			}
			err := RunSteps(steps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil

}

func While(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"while", "do"}); err != nil {
		return nil, fmt.Errorf("building 'while' loop: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"while", "do"}); err != nil {
		return nil, fmt.Errorf("building 'while' loop: %v", err)
	}
	condition, err := args.GetString("while")
	if err != nil {
		return nil, fmt.Errorf("'while' field of a 'while' loop must be a string")
	}
	steps, err := ParseSteps(target, args, "do")
	if err != nil {
		return nil, err
	}
	return func() error {
		for {
			result, err := target.Build.Context.Evaluate(condition)
			if err != nil {
				return fmt.Errorf("evaluating 'while' field of 'while' loop: %v", err)
			}
			loop, ok := result.(bool)
			if !ok {
				return fmt.Errorf("evaluating 'while' condition: must return a bool")
			}
			if !loop {
				break
			}
			err = RunSteps(steps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func Try(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"try", "catch"}); err != nil {
		return nil, fmt.Errorf("building try construct: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"try", "catch"}); err != nil {
		return nil, fmt.Errorf("building try construct: %v", err)
	}
	trySteps, err := ParseSteps(target, args, "try")
	if err != nil {
		return nil, err
	}
	catchSteps, err := ParseSteps(target, args, "catch")
	if err != nil {
		return nil, err
	}
	return func() error {
		err := RunSteps(trySteps)
		if err != nil {
			target.Build.Context.SetProperty("error", err.Error())
			err = RunSteps(catchSteps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func Nop(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"nop"}); err != nil {
		return nil, fmt.Errorf("building nop instruction: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"nop"}); err != nil {
		return nil, fmt.Errorf("building nop instruction: %v", err)
	}
	return func() error {
		return nil
	}, nil
}

// UTILITY FUNCTIONS

func FieldsInList(fields, list []string) error {
	for _, field := range fields {
		found := false
		for _, e := range list {
			if e == field {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("unknown field '%s'", field)
		}
	}
	return nil
}

func FieldInList(field string, list []string) bool {
	for _, f := range list {
		if f == field {
			return true
		}
	}
	return false
}

func FieldsMandatory(fields, mandatory []string) error {
	for _, manda := range mandatory {
		found := false
		for _, field := range fields {
			if manda == field {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("mandatory field '%s' not found", manda)
		}
	}
	return nil
}

func ParseSteps(target *Target, object Object, field string) ([]Step, error) {
	list, err := object.GetList(field)
	if err != nil {
		return nil, err
	}
	var steps []Step
	for index, element := range list {
		target.Build.Log("Parsing step %v in %s field", index, field)
		step, err := NewStep(target, element)
		if err != nil {
			return nil, fmt.Errorf("parsing target '%s': %v", target.Name, err)
		}
		steps = append(steps, step)
	}
	return steps, nil
}

func RunSteps(steps []Step) error {
	for _, step := range steps {
		err := step.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func ToList(object interface{}) ([]interface{}, error) {
	slice := reflect.ValueOf(object)
	if slice.Kind() == reflect.Slice {
		result := make([]interface{}, slice.Len())
		for i := 0; i < slice.Len(); i++ {
			result[i] = slice.Index(i).Interface()
		}
		return result, nil
	} else {
		return nil, fmt.Errorf("must be a list")
	}
}
<|endoftext|>


<commit_before>package containerd

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/containerd/containerd"
	containersapi "github.com/containerd/containerd/api/services/containers"
	"github.com/containerd/containerd/api/services/execution"
	"github.com/containerd/containerd/api/types/task"
	"github.com/containerd/fifo"
	dockermount "github.com/docker/docker/pkg/mount"
	"github.com/docker/swarmkit/agent/exec"
	"github.com/docker/swarmkit/api"
	"github.com/docker/swarmkit/api/naming"
	"github.com/docker/swarmkit/log"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

var (
	mountPropagationReverseMap = map[api.Mount_BindOptions_MountPropagation]string{
		api.MountPropagationPrivate:  "private",
		api.MountPropagationRPrivate: "rprivate",
		api.MountPropagationShared:   "shared",
		api.MountPropagationRShared:  "rshared",
		api.MountPropagationRSlave:   "slave",
		api.MountPropagationSlave:    "rslave",
	}
)

// containerAdapter conducts remote operations for a container. All calls
// are mostly naked calls to the client API, seeded with information from
// containerConfig.
type containerAdapter struct {
	client         *containerd.Client
	spec           *api.ContainerSpec
	secrets        exec.SecretGetter
	dir            string
	name           string
	image          containerd.Image // Pulled image
	container      containerd.Container
	task           containerd.Task
	deleteResponse *execution.DeleteResponse
}

func newContainerAdapter(client *containerd.Client, containerDir string, task *api.Task, secrets exec.SecretGetter) (*containerAdapter, error) {
	spec := task.Spec.GetContainer()
	if spec == nil {
		return nil, exec.ErrRuntimeUnsupported
	}

	dir := filepath.Join(containerDir, task.ID)

	return &containerAdapter{
		client:  client,
		spec:    spec,
		secrets: secrets,
		dir:     dir,
		name:    naming.Task(task),
	}, nil
}

// github.com/containerd/containerd cmd/ctr/utils.go, dropped stdin handling
func prepareStdio(stdout, stderr string, console bool) (wg *sync.WaitGroup, err error) {
	wg = &sync.WaitGroup{}
	ctx := context.Background()

	f, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700)
	if err != nil {
		return nil, err
	}
	defer func(c io.Closer) {
		if err != nil {
			c.Close()
		}
	}(f)
	wg.Add(1)
	go func(r io.ReadCloser) {
		io.Copy(os.Stdout, r)
		r.Close()
		wg.Done()
	}(f)

	f, err = fifo.OpenFifo(ctx, stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700)
	if err != nil {
		return nil, err
	}
	defer func(c io.Closer) {
		if err != nil {
			c.Close()
		}
	}(f)
	if !console {
		wg.Add(1)
		go func(r io.ReadCloser) {
			io.Copy(os.Stderr, r)
			r.Close()
			wg.Done()
		}(f)
	}

	return wg, nil
}

func (c *containerAdapter) pullImage(ctx context.Context) error {
	image, err := c.client.Pull(ctx, c.spec.Image, containerd.WithPullUnpack)
	if err != nil {
		return errors.Wrap(err, "pulling container image")
	}
	c.image = image

	return nil
}

func withMounts(ctx context.Context, ms []api.Mount) containerd.SpecOpts {
	sort.Sort(mounts(ms))

	return func(s *specs.Spec) error {
		for _, m := range ms {
			if !filepath.IsAbs(m.Target) {
				return errors.Errorf("mount %s is not absolute", m.Target)
			}

			switch m.Type {
			case api.MountTypeTmpfs:
				opts := []string{"noexec", "nosuid", "nodev", "rprivate"}
				if m.TmpfsOptions != nil {
					if m.TmpfsOptions.SizeBytes <= 0 {
						return errors.New("invalid tmpfs size give")
					}
					opts = append(opts, fmt.Sprintf("size=%d", m.TmpfsOptions.SizeBytes))
					opts = append(opts, fmt.Sprintf("mode=%o", m.TmpfsOptions.Mode))
				}
				if m.ReadOnly {
					opts = append(opts, "ro")
				} else {
					opts = append(opts, "rw")
				}

				opts, err := dockermount.MergeTmpfsOptions(opts)
				if err != nil {
					return err
				}

				s.Mounts = append(s.Mounts, specs.Mount{
					Destination: m.Target,
					Type:        "tmpfs",
					Source:      "tmpfs",
					Options:     opts,
				})

			case api.MountTypeVolume:
				return errors.Errorf("volume mounts not implemented, ignoring %v", m)

			case api.MountTypeBind:
				opts := []string{"rbind"}
				if m.ReadOnly {
					opts = append(opts, "ro")
				} else {
					opts = append(opts, "rw")
				}

				propagation := "rprivate"
				if m.BindOptions != nil {
					if p, ok := mountPropagationReverseMap[m.BindOptions.Propagation]; ok {
						propagation = p
					} else {
						log.G(ctx).Warningf("unknown bind mount propagation, using %q", propagation)
					}
				}
				opts = append(opts, propagation)

				s.Mounts = append(s.Mounts, specs.Mount{
					Destination: m.Target,
					Type:        "bind",
					Source:      m.Source,
					Options:     opts,
				})
			}
		}
		return nil
	}
}

func (c *containerAdapter) specOpts(ctx context.Context) []containerd.SpecOpts {
	opts := []containerd.SpecOpts{
		containerd.WithImageConfig(ctx, c.image),
		withMounts(ctx, c.spec.Mounts),
	}

	// spec.Process.Args is config.Entrypoint + config.Cmd at this
	// point from WithImageConfig above. If the ContainerSpec
	// specifies a Command then we can completely override. If it
	// does not then all we can do is append our Args and hope
	// they do not conflict.
	// TODO(ijc) Improve this
	if len(c.spec.Command) > 0 {
		args := append(c.spec.Command, c.spec.Args...)
		opts = append(opts, containerd.WithProcessArgs(args...))
	} else {
		opts = append(opts, func(s *specs.Spec) error {
			s.Process.Args = append(s.Process.Args, c.spec.Args...)
			return nil
		})
	}

	return opts
}

func (c *containerAdapter) create(ctx context.Context) error {
	if c.image == nil {
		return errors.New("image has not been pulled")
	}

	if err := os.MkdirAll(c.dir, 0755); err != nil {
		return err
	}

	// TODO(ijc) support ControllerLogs interface
	stdin := "/dev/null"
	stdout := filepath.Join(c.dir, "stdout")
	stderr := filepath.Join(c.dir, "stderr")

	spec, err := containerd.GenerateSpec(c.specOpts(ctx)...)
	if err != nil {
		return err
	}

	_, err = prepareStdio(stdout, stderr, spec.Process.Terminal)
	if err != nil {
		return err
	}

	c.container, err = c.client.NewContainer(ctx, c.name,
		containerd.WithSpec(spec),
		containerd.WithNewRootFS(c.name, c.image))
	if err != nil {
		return errors.Wrap(err, "creating container")
	}

	io := func() (*containerd.IO, error) {
		return &containerd.IO{
			Stdin:    stdin,
			Stdout:   stdout,
			Stderr:   stderr,
			Terminal: spec.Process.Terminal,
		}, nil
	}

	c.task, err = c.container.NewTask(ctx, io)
	if err != nil {
		return errors.Wrap(err, "creating task")
	}

	return nil
}

func (c *containerAdapter) start(ctx context.Context) error {
	tasks := c.client.TaskService()

	_, err := tasks.Start(ctx, &execution.StartRequest{
		ContainerID: c.name,
	})
	return err
}

func (c *containerAdapter) eventStream(ctx context.Context, id string) (<-chan task.Event, <-chan error, error) {

	var (
		evtch = make(chan task.Event)
		errch = make(chan error)
	)

	return evtch, errch, nil
}

// events issues a call to the events API and returns a channel with all
// events. The stream of events can be shutdown by cancelling the context.
//
// A chan struct{} is returned that will be closed if the event processing
// fails and needs to be restarted.
func (c *containerAdapter) events(ctx context.Context, opts ...grpc.CallOption) (<-chan task.Event, <-chan struct{}, error) {
	l := log.G(ctx).WithFields(logrus.Fields{
		"ID": c.name,
	})

	// TODO(stevvooe): Move this to a single, global event dispatch. For
	// now, we create a connection per container.
	var (
		eventsq = make(chan task.Event)
		closed  = make(chan struct{})
	)

	l.Debugf("waiting on events")

	tasks := c.client.TaskService()
	cl, err := tasks.Events(ctx, &execution.EventsRequest{}, opts...)
	if err != nil {
		l.WithError(err).Errorf("failed to start event stream")
		return nil, nil, err
	}

	go func() {
		defer close(closed)

		for {
			evt, err := cl.Recv()
			if err != nil {
				l.WithError(err).Error("fatal error from events stream")
				return
			}
			if evt.ID != c.name {
				l.Debugf("Event for a different container %s", evt.ID)
				continue
			}

			select {
			case eventsq <- *evt:
			case <-ctx.Done():
				return
			}
		}
	}()

	return eventsq, closed, nil
}

func (c *containerAdapter) inspect(ctx context.Context) (task.Task, error) {
	tasks := c.client.TaskService()
	rsp, err := tasks.Info(ctx, &execution.InfoRequest{ContainerID: c.name})
	if err != nil {
		return task.Task{}, err
	}
	return *rsp.Task, nil
}

func (c *containerAdapter) shutdown(ctx context.Context) (uint32, error) {
	l := log.G(ctx).WithFields(logrus.Fields{
		"ID": c.name,
	})

	if c.deleteResponse == nil {
		var err error
		l.Debug("Deleting")

		tasks := c.client.TaskService()
		rsp, err := tasks.Delete(ctx, &execution.DeleteRequest{ContainerID: c.name})
		if err != nil {
			return 0, err
		}
		l.Debugf("Status=%d", rsp.ExitStatus)
		c.deleteResponse = rsp

		containers := c.client.ContainerService()
		_, err = containers.Delete(ctx, &containersapi.DeleteContainerRequest{
			ID: c.name,
		})
		if err != nil {
			l.WithError(err).Warnf("failed to delete container")
		}
	}

	return c.deleteResponse.ExitStatus, nil
}

func (c *containerAdapter) terminate(ctx context.Context) error {
	l := log.G(ctx).WithFields(logrus.Fields{
		"ID": c.name,
	})
	l.Debug("Terminate")
	return errors.New("terminate not implemented")
}

func (c *containerAdapter) remove(ctx context.Context) error {
	l := log.G(ctx).WithFields(logrus.Fields{
		"ID": c.name,
	})
	l.Debug("Remove")
	return os.RemoveAll(c.dir)
}

func isContainerCreateNameConflict(err error) bool {
	// container ".*" already exists
	splits := strings.SplitN(err.Error(), "\"", 3)
	return splits[0] == "container " && splits[2] == " already exists"
}

func isUnknownContainer(err error) bool {
	return strings.Contains(err.Error(), "container does not exist")
}

// For sort.Sort
type mounts []api.Mount

// Len returns the number of mounts. Used in sorting.
func (m mounts) Len() int {
	return len(m)
}

// Less returns true if the number of parts (a/b/c would be 3 parts) in the
// mount indexed by parameter 1 is less than that of the mount indexed by
// parameter 2. Used in sorting.
func (m mounts) Less(i, j int) bool {
	return m.parts(i) < m.parts(j)
}

// Swap swaps two items in an array of mounts. Used in sorting
func (m mounts) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

// parts returns the number of parts in the destination of a mount. Used in sorting.
func (m mounts) parts(i int) int {
	return strings.Count(filepath.Clean(m[i].Target), string(os.PathSeparator))
}
<commit_msg>containerd: use client lib for stdio<commit_after>package containerd

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

	"github.com/Sirupsen/logrus"
	"github.com/containerd/containerd"
	containersapi "github.com/containerd/containerd/api/services/containers"
	"github.com/containerd/containerd/api/services/execution"
	"github.com/containerd/containerd/api/types/task"
	dockermount "github.com/docker/docker/pkg/mount"
	"github.com/docker/swarmkit/agent/exec"
	"github.com/docker/swarmkit/api"
	"github.com/docker/swarmkit/api/naming"
	"github.com/docker/swarmkit/log"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

var (
	devNull                    *os.File
	mountPropagationReverseMap = map[api.Mount_BindOptions_MountPropagation]string{
		api.MountPropagationPrivate:  "private",
		api.MountPropagationRPrivate: "rprivate",
		api.MountPropagationShared:   "shared",
		api.MountPropagationRShared:  "rshared",
		api.MountPropagationRSlave:   "slave",
		api.MountPropagationSlave:    "rslave",
	}
)

// containerAdapter conducts remote operations for a container. All calls
// are mostly naked calls to the client API, seeded with information from
// containerConfig.
type containerAdapter struct {
	client         *containerd.Client
	spec           *api.ContainerSpec
	secrets        exec.SecretGetter
	dir            string
	name           string
	image          containerd.Image // Pulled image
	container      containerd.Container
	task           containerd.Task
	deleteResponse *execution.DeleteResponse
}

func newContainerAdapter(client *containerd.Client, containerDir string, task *api.Task, secrets exec.SecretGetter) (*containerAdapter, error) {
	spec := task.Spec.GetContainer()
	if spec == nil {
		return nil, exec.ErrRuntimeUnsupported
	}

	dir := filepath.Join(containerDir, task.ID)

	return &containerAdapter{
		client:  client,
		spec:    spec,
		secrets: secrets,
		dir:     dir,
		name:    naming.Task(task),
	}, nil
}

func (c *containerAdapter) pullImage(ctx context.Context) error {
	image, err := c.client.Pull(ctx, c.spec.Image, containerd.WithPullUnpack)
	if err != nil {
		return errors.Wrap(err, "pulling container image")
	}
	c.image = image

	return nil
}

func withMounts(ctx context.Context, ms []api.Mount) containerd.SpecOpts {
	sort.Sort(mounts(ms))

	return func(s *specs.Spec) error {
		for _, m := range ms {
			if !filepath.IsAbs(m.Target) {
				return errors.Errorf("mount %s is not absolute", m.Target)
			}

			switch m.Type {
			case api.MountTypeTmpfs:
				opts := []string{"noexec", "nosuid", "nodev", "rprivate"}
				if m.TmpfsOptions != nil {
					if m.TmpfsOptions.SizeBytes <= 0 {
						return errors.New("invalid tmpfs size give")
					}
					opts = append(opts, fmt.Sprintf("size=%d", m.TmpfsOptions.SizeBytes))
					opts = append(opts, fmt.Sprintf("mode=%o", m.TmpfsOptions.Mode))
				}
				if m.ReadOnly {
					opts = append(opts, "ro")
				} else {
					opts = append(opts, "rw")
				}

				opts, err := dockermount.MergeTmpfsOptions(opts)
				if err != nil {
					return err
				}

				s.Mounts = append(s.Mounts, specs.Mount{
					Destination: m.Target,
					Type:        "tmpfs",
					Source:      "tmpfs",
					Options:     opts,
				})

			case api.MountTypeVolume:
				return errors.Errorf("volume mounts not implemented, ignoring %v", m)

			case api.MountTypeBind:
				opts := []string{"rbind"}
				if m.ReadOnly {
					opts = append(opts, "ro")
				} else {
					opts = append(opts, "rw")
				}

				propagation := "rprivate"
				if m.BindOptions != nil {
					if p, ok := mountPropagationReverseMap[m.BindOptions.Propagation]; ok {
						propagation = p
					} else {
						log.G(ctx).Warningf("unknown bind mount propagation, using %q", propagation)
					}
				}
				opts = append(opts, propagation)

				s.Mounts = append(s.Mounts, specs.Mount{
					Destination: m.Target,
					Type:        "bind",
					Source:      m.Source,
					Options:     opts,
				})
			}
		}
		return nil
	}
}

func (c *containerAdapter) specOpts(ctx context.Context) []containerd.SpecOpts {
	opts := []containerd.SpecOpts{
		containerd.WithImageConfig(ctx, c.image),
		withMounts(ctx, c.spec.Mounts),
	}

	// spec.Process.Args is config.Entrypoint + config.Cmd at this
	// point from WithImageConfig above. If the ContainerSpec
	// specifies a Command then we can completely override. If it
	// does not then all we can do is append our Args and hope
	// they do not conflict.
	// TODO(ijc) Improve this
	if len(c.spec.Command) > 0 {
		args := append(c.spec.Command, c.spec.Args...)
		opts = append(opts, containerd.WithProcessArgs(args...))
	} else {
		opts = append(opts, func(s *specs.Spec) error {
			s.Process.Args = append(s.Process.Args, c.spec.Args...)
			return nil
		})
	}

	return opts
}

func (c *containerAdapter) create(ctx context.Context) error {
	if c.image == nil {
		return errors.New("image has not been pulled")
	}

	if err := os.MkdirAll(c.dir, 0755); err != nil {
		return err
	}

	spec, err := containerd.GenerateSpec(c.specOpts(ctx)...)
	if err != nil {
		return err
	}

	// TODO(ijc) Consider an addition to container library which
	// directly attaches stdin to /dev/null.
	if devNull == nil {
		if devNull, err = os.Open(os.DevNull); err != nil {
			return errors.Wrap(err, "opening null device")
		}
	}

	c.container, err = c.client.NewContainer(ctx, c.name,
		containerd.WithSpec(spec),
		containerd.WithNewRootFS(c.name, c.image))
	if err != nil {
		return errors.Wrap(err, "creating container")
	}

	// TODO(ijc) support ControllerLogs interface.
	io := containerd.NewIOWithTerminal(devNull, os.Stdout, os.Stderr, spec.Process.Terminal)

	c.task, err = c.container.NewTask(ctx, io)
	if err != nil {
		return errors.Wrap(err, "creating task")
	}

	return nil
}

func (c *containerAdapter) start(ctx context.Context) error {
	tasks := c.client.TaskService()

	_, err := tasks.Start(ctx, &execution.StartRequest{
		ContainerID: c.name,
	})
	return err
}

func (c *containerAdapter) eventStream(ctx context.Context, id string) (<-chan task.Event, <-chan error, error) {

	var (
		evtch = make(chan task.Event)
		errch = make(chan error)
	)

	return evtch, errch, nil
}

// events issues a call to the events API and returns a channel with all
// events. The stream of events can be shutdown by cancelling the context.
//
// A chan struct{} is returned that will be closed if the event processing
// fails and needs to be restarted.
func (c *containerAdapter) events(ctx context.Context, opts ...grpc.CallOption) (<-chan task.Event, <-chan struct{}, error) {
	l := log.G(ctx).WithFields(logrus.Fields{
		"ID": c.name,
	})

	// TODO(stevvooe): Move this to a single, global event dispatch. For
	// now, we create a connection per container.
	var (
		eventsq = make(chan task.Event)
		closed  = make(chan struct{})
	)

	l.Debugf("waiting on events")

	tasks := c.client.TaskService()
	cl, err := tasks.Events(ctx, &execution.EventsRequest{}, opts...)
	if err != nil {
		l.WithError(err).Errorf("failed to start event stream")
		return nil, nil, err
	}

	go func() {
		defer close(closed)

		for {
			evt, err := cl.Recv()
			if err != nil {
				l.WithError(err).Error("fatal error from events stream")
				return
			}
			if evt.ID != c.name {
				l.Debugf("Event for a different container %s", evt.ID)
				continue
			}

			select {
			case eventsq <- *evt:
			case <-ctx.Done():
				return
			}
		}
	}()

	return eventsq, closed, nil
}

func (c *containerAdapter) inspect(ctx context.Context) (task.Task, error) {
	tasks := c.client.TaskService()
	rsp, err := tasks.Info(ctx, &execution.InfoRequest{ContainerID: c.name})
	if err != nil {
		return task.Task{}, err
	}
	return *rsp.Task, nil
}

func (c *containerAdapter) shutdown(ctx context.Context) (uint32, error) {
	l := log.G(ctx).WithFields(logrus.Fields{
		"ID": c.name,
	})

	if c.deleteResponse == nil {
		var err error
		l.Debug("Deleting")

		tasks := c.client.TaskService()
		rsp, err := tasks.Delete(ctx, &execution.DeleteRequest{ContainerID: c.name})
		if err != nil {
			return 0, err
		}
		l.Debugf("Status=%d", rsp.ExitStatus)
		c.deleteResponse = rsp

		containers := c.client.ContainerService()
		_, err = containers.Delete(ctx, &containersapi.DeleteContainerRequest{
			ID: c.name,
		})
		if err != nil {
			l.WithError(err).Warnf("failed to delete container")
		}
	}

	return c.deleteResponse.ExitStatus, nil
}

func (c *containerAdapter) terminate(ctx context.Context) error {
	l := log.G(ctx).WithFields(logrus.Fields{
		"ID": c.name,
	})
	l.Debug("Terminate")
	return errors.New("terminate not implemented")
}

func (c *containerAdapter) remove(ctx context.Context) error {
	l := log.G(ctx).WithFields(logrus.Fields{
		"ID": c.name,
	})
	l.Debug("Remove")
	return os.RemoveAll(c.dir)
}

func isContainerCreateNameConflict(err error) bool {
	// container ".*" already exists
	splits := strings.SplitN(err.Error(), "\"", 3)
	return splits[0] == "container " && splits[2] == " already exists"
}

func isUnknownContainer(err error) bool {
	return strings.Contains(err.Error(), "container does not exist")
}

// For sort.Sort
type mounts []api.Mount

// Len returns the number of mounts. Used in sorting.
func (m mounts) Len() int {
	return len(m)
}

// Less returns true if the number of parts (a/b/c would be 3 parts) in the
// mount indexed by parameter 1 is less than that of the mount indexed by
// parameter 2. Used in sorting.
func (m mounts) Less(i, j int) bool {
	return m.parts(i) < m.parts(j)
}

// Swap swaps two items in an array of mounts. Used in sorting
func (m mounts) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

// parts returns the number of parts in the destination of a mount. Used in sorting.
func (m mounts) parts(i int) int {
	return strings.Count(filepath.Clean(m[i].Target), string(os.PathSeparator))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/asaskevich/govalidator"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/spf13/cobra"
)

/******************************************************************************/
// Global options
type optionsLinkStruct struct {
	StackName       string `valid:"required,matches(\\w+)"`
	OutputDirectory string `valid:"required,matchesdirectoryw+)"`
}

var optionsLink optionsLinkStruct

var RootCmd = &cobra.Command{
	Use:   "link",
	Short: "Link is a tool to discover and serialize a prexisting CloudFormation stack",
	Long:  "",
	PreRunE: func(cmd *cobra.Command, args []string) error {
		_, validateErr := govalidator.ValidateStruct(optionsLink)
		if nil != validateErr {
			return validateErr
		}
		// Make sure the output value is a directory
		osStat, osStatErr := os.Stat(optionsLink.OutputDirectory)
		if nil != osStatErr {
			return osStatErr
		}
		if !osStat.IsDir() {
			return fmt.Errorf("--output is not a valid directory")
		}
		return nil
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		// Get the output and stuff it to a file
		sess, err := session.NewSession()
		if err != nil {
			fmt.Println("failed to create session,", err)
			return err
		}

		svc := cloudformation.New(sess)

		params := &cloudformation.DescribeStacksInput{
			StackName: aws.String(optionsLink.StackName),
		}
		describeStacksResponse, describeStacksResponseErr := svc.DescribeStacks(params)

		if describeStacksResponseErr != nil {
			return describeStacksResponseErr
		}

		stackInfo, _ := json.Marshal(describeStacksResponse)
		outputFilepath := filepath.Join(optionsLink.OutputDirectory, fmt.Sprintf("%s.json", optionsLink.StackName))
		err = ioutil.WriteFile(outputFilepath, stackInfo, 0644)
		if nil != err {
			return err
		}
		fmt.Println("Created file: " + outputFilepath)
		fmt.Println(describeStacksResponse)
		return nil
	},
}

func init() {
	cobra.OnInitialize()
	RootCmd.PersistentFlags().StringVar(&optionsLink.StackName, "stackName", "", "CloudFormation Stack Name/ID to query")
	RootCmd.PersistentFlags().StringVar(&optionsLink.OutputDirectory, "output", "", "Output directory")
}

func main() {
	// Take a stack name and an output file...
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}
<commit_msg>Add comment for exported RootCmd per: https://goreportcard.com/report/github.com/mweagle/Sparta<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/asaskevich/govalidator"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/spf13/cobra"
)

/******************************************************************************/
// Global options
type optionsLinkStruct struct {
	StackName       string `valid:"required,matches(\\w+)"`
	OutputDirectory string `valid:"required,matchesdirectoryw+)"`
}

var optionsLink optionsLinkStruct

// RootCmd represents the root Cobra command invoked for the discovery
// and serialization of an existing CloudFormation stack
var RootCmd = &cobra.Command{
	Use:   "link",
	Short: "Link is a tool to discover and serialize a prexisting CloudFormation stack",
	Long:  "",
	PreRunE: func(cmd *cobra.Command, args []string) error {
		_, validateErr := govalidator.ValidateStruct(optionsLink)
		if nil != validateErr {
			return validateErr
		}
		// Make sure the output value is a directory
		osStat, osStatErr := os.Stat(optionsLink.OutputDirectory)
		if nil != osStatErr {
			return osStatErr
		}
		if !osStat.IsDir() {
			return fmt.Errorf("--output is not a valid directory")
		}
		return nil
	},
	RunE: func(cmd *cobra.Command, args []string) error {
		// Get the output and stuff it to a file
		sess, err := session.NewSession()
		if err != nil {
			fmt.Println("failed to create session,", err)
			return err
		}

		svc := cloudformation.New(sess)

		params := &cloudformation.DescribeStacksInput{
			StackName: aws.String(optionsLink.StackName),
		}
		describeStacksResponse, describeStacksResponseErr := svc.DescribeStacks(params)

		if describeStacksResponseErr != nil {
			return describeStacksResponseErr
		}

		stackInfo, _ := json.Marshal(describeStacksResponse)
		outputFilepath := filepath.Join(optionsLink.OutputDirectory, fmt.Sprintf("%s.json", optionsLink.StackName))
		err = ioutil.WriteFile(outputFilepath, stackInfo, 0644)
		if nil != err {
			return err
		}
		fmt.Println("Created file: " + outputFilepath)
		fmt.Println(describeStacksResponse)
		return nil
	},
}

func init() {
	cobra.OnInitialize()
	RootCmd.PersistentFlags().StringVar(&optionsLink.StackName, "stackName", "", "CloudFormation Stack Name/ID to query")
	RootCmd.PersistentFlags().StringVar(&optionsLink.OutputDirectory, "output", "", "Output directory")
}

func main() {
	// Take a stack name and an output file...
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}
<|endoftext|>


<commit_before>package checks

import (
	"errors"
	"strings"
)

func NtpCheck() error {
	_, _, err := RunCommand("w32tm /resync")
	if err != nil {
		return errors.New(`
There was an error detecting ntp synchronization on your machine.
An accurate system clock is essential for internal Cloud Foundry metric reports.

Please configure your NTP settings, if not already done.
We recommend that your firewall have outbound rules set for UDP on port 123.
In addition, ensure that your 'DnsCache' service is running.  ` + "Error: \n\n" + stdout)
	}

	return nil
}
<commit_msg>Fixup PR so it compiles<commit_after>package checks

import "errors"

func NtpCheck() error {
	stdout, _, err := RunCommand("w32tm /resync")
	if err != nil {
		return errors.New(`
There was an error detecting ntp synchronization on your machine.
An accurate system clock is essential for internal Cloud Foundry metric reports.

Please configure your NTP settings, if not already done.
We recommend that your firewall have outbound rules set for UDP on port 123.
In addition, ensure that your 'DnsCache' service is running.  ` + "Error: \n\n" + stdout)
	}

	return nil
}
<|endoftext|>


<commit_before><commit_msg>Do not use unkeyed fields.<commit_after><|endoftext|>


<commit_before>// Copyright 2012 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 (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"

	"code.google.com/p/go.talks/pkg/present"
)

func init() {
	http.HandleFunc("/", dirHandler)
}

// dirHandler serves a directory listing for the requested path, rooted at basePath.
func dirHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" {
		http.Error(w, "not found", 404)
		return
	}
	const base = "."
	name := filepath.Join(base, r.URL.Path)
	if isDoc(name) {
		err := renderDoc(w, basePath, name)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), 500)
		}
		return
	}
	if isDir, err := dirList(w, name); err != nil {
		log.Println(err)
		http.Error(w, err.Error(), 500)
		return
	} else if isDir {
		return
	}
	http.FileServer(http.Dir(base)).ServeHTTP(w, r)
}

// extensions maps the presentable file extensions to the name of the
// template to be executed.
var extensions = map[string]string{
	".slide":   "slides.tmpl",
	".article": "article.tmpl",
}

func isDoc(path string) bool {
	_, ok := extensions[filepath.Ext(path)]
	return ok
}

// renderDoc reads the present file, builds its template representation,
// and executes the template, sending output to w.
func renderDoc(w io.Writer, base, docFile string) error {
	// Read the input and build the doc structure.
	doc, err := parse(docFile, 0)
	if err != nil {
		return err
	}

	// Find which template should be executed.
	ext := filepath.Ext(docFile)
	contentTmpl, ok := extensions[ext]
	if !ok {
		return fmt.Errorf("no template for extension %v", ext)
	}

	// Locate the template file.
	actionTmpl := filepath.Join(base, "templates/action.tmpl")
	contentTmpl = filepath.Join(base, "templates", contentTmpl)

	// Read and parse the input.
	tmpl := present.Template()
	if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
		return err
	}

	// Execute the template.
	return doc.Render(w, tmpl)
}

func parse(name string, mode present.ParseMode) (*present.Doc, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return present.Parse(f, name, 0)
}

// dirList scans the given path and writes a directory listing to w.
// It parses the first part of each .slide file it encounters to display the
// presentation title in the listing.
// If the given path is not a directory, it returns (isDir == false, err == nil)
// and writes nothing to w.
func dirList(w io.Writer, name string) (isDir bool, err error) {
	f, err := os.Open(name)
	if err != nil {
		return false, err
	}
	defer f.Close()
	fi, err := f.Stat()
	if err != nil {
		return false, err
	}
	if isDir = fi.IsDir(); !isDir {
		return false, nil
	}
	fis, err := f.Readdir(0)
	if err != nil {
		return false, err
	}
	d := &dirListData{Path: name}
	for _, fi := range fis {
		// skip the pkg directory
		if name == "." && fi.Name() == "pkg" {
			continue
		}
		e := dirEntry{
			Name: fi.Name(),
			Path: filepath.ToSlash(filepath.Join(name, fi.Name())),
		}
		if fi.IsDir() && showDir(e.Name) {
			d.Dirs = append(d.Dirs, e)
			continue
		}
		if isDoc(e.Name) {
			if p, err := parse(e.Path, present.TitlesOnly); err != nil {
				log.Println(err)
			} else {
				e.Title = p.Title
			}
			switch filepath.Ext(e.Path) {
			case ".article":
				d.Articles = append(d.Articles, e)
			case ".slide":
				d.Slides = append(d.Slides, e)
			}
		} else if showFile(e.Name) {
			d.Other = append(d.Other, e)
		}
	}
	if d.Path == "." {
		d.Path = ""
	}
	sort.Sort(d.Dirs)
	sort.Sort(d.Slides)
	sort.Sort(d.Articles)
	sort.Sort(d.Other)
	return true, dirListTemplate.Execute(w, d)
}

// showFile returns whether the given file should be displayed in the list.
func showFile(n string) bool {
	switch filepath.Ext(n) {
	case ".pdf":
	case ".html":
	case ".go":
	default:
		return isDoc(n)
	}
	return true
}

// showDir returns whether the given directory should be displayed in the list.
func showDir(n string) bool {
	if len(n) > 0 && (n[0] == '.' || n[0] == '_') || n == "present" {
		return false
	}
	return true
}

type dirListData struct {
	Path                          string
	Dirs, Slides, Articles, Other dirEntrySlice
}

type dirEntry struct {
	Name, Path, Title string
}

type dirEntrySlice []dirEntry

func (s dirEntrySlice) Len() int           { return len(s) }
func (s dirEntrySlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s dirEntrySlice) Less(i, j int) bool { return s[i].Name < s[j].Name }

var dirListTemplate = template.Must(template.New("").Parse(dirListHTML))

const dirListHTML = `<!DOCTYPE html>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Talks - The Go Programming Language</title>
  <link type="text/css" rel="stylesheet" href="/static/dir.css">

<form method="GET" action="http://golang.org/search">

Documents
References
Packages
The Project
Help
<input type="text" id="search" name="q" class="inactive" value="Search">

The Go Programming Language

</form>

Go talks

  {{with .Path}}
{{.}}
{{end}}

  {{with .Articles}}

Articles:

  {{range .}}

{{.Name}}: {{.Title}}

  {{end}}

  {{end}}

  {{with .Slides}}

Slide decks:

  {{range .}}

{{.Name}}: {{.Title}}

  {{end}}

  {{end}}

  {{with .Other}}

Files:

  {{range .}}

{{.Name}}

  {{end}}

  {{end}}

  {{with .Dirs}}

Sub-directories:

  {{range .}}

{{.Name}}

  {{end}}

  {{end}}

Except as noted,
the content of this page is licensed under the
Creative Commons Attribution 3.0 License,
and code is licensed under a BSD license.

Terms of Service |
Privacy Policy

`
<commit_msg>present/dir.go: be more idiomatic when documenting boolean return values. Phrases like "returns whether or not the image is opaque" could be describing what the function does (it always returns, regardless of the opacity) or what it returns (a boolean indicating the opacity). Even when the "or not" is missing, the phrasing is bizarre.<commit_after>// Copyright 2012 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 (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"

	"code.google.com/p/go.talks/pkg/present"
)

func init() {
	http.HandleFunc("/", dirHandler)
}

// dirHandler serves a directory listing for the requested path, rooted at basePath.
func dirHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" {
		http.Error(w, "not found", 404)
		return
	}
	const base = "."
	name := filepath.Join(base, r.URL.Path)
	if isDoc(name) {
		err := renderDoc(w, basePath, name)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), 500)
		}
		return
	}
	if isDir, err := dirList(w, name); err != nil {
		log.Println(err)
		http.Error(w, err.Error(), 500)
		return
	} else if isDir {
		return
	}
	http.FileServer(http.Dir(base)).ServeHTTP(w, r)
}

// extensions maps the presentable file extensions to the name of the
// template to be executed.
var extensions = map[string]string{
	".slide":   "slides.tmpl",
	".article": "article.tmpl",
}

func isDoc(path string) bool {
	_, ok := extensions[filepath.Ext(path)]
	return ok
}

// renderDoc reads the present file, builds its template representation,
// and executes the template, sending output to w.
func renderDoc(w io.Writer, base, docFile string) error {
	// Read the input and build the doc structure.
	doc, err := parse(docFile, 0)
	if err != nil {
		return err
	}

	// Find which template should be executed.
	ext := filepath.Ext(docFile)
	contentTmpl, ok := extensions[ext]
	if !ok {
		return fmt.Errorf("no template for extension %v", ext)
	}

	// Locate the template file.
	actionTmpl := filepath.Join(base, "templates/action.tmpl")
	contentTmpl = filepath.Join(base, "templates", contentTmpl)

	// Read and parse the input.
	tmpl := present.Template()
	if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
		return err
	}

	// Execute the template.
	return doc.Render(w, tmpl)
}

func parse(name string, mode present.ParseMode) (*present.Doc, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return present.Parse(f, name, 0)
}

// dirList scans the given path and writes a directory listing to w.
// It parses the first part of each .slide file it encounters to display the
// presentation title in the listing.
// If the given path is not a directory, it returns (isDir == false, err == nil)
// and writes nothing to w.
func dirList(w io.Writer, name string) (isDir bool, err error) {
	f, err := os.Open(name)
	if err != nil {
		return false, err
	}
	defer f.Close()
	fi, err := f.Stat()
	if err != nil {
		return false, err
	}
	if isDir = fi.IsDir(); !isDir {
		return false, nil
	}
	fis, err := f.Readdir(0)
	if err != nil {
		return false, err
	}
	d := &dirListData{Path: name}
	for _, fi := range fis {
		// skip the pkg directory
		if name == "." && fi.Name() == "pkg" {
			continue
		}
		e := dirEntry{
			Name: fi.Name(),
			Path: filepath.ToSlash(filepath.Join(name, fi.Name())),
		}
		if fi.IsDir() && showDir(e.Name) {
			d.Dirs = append(d.Dirs, e)
			continue
		}
		if isDoc(e.Name) {
			if p, err := parse(e.Path, present.TitlesOnly); err != nil {
				log.Println(err)
			} else {
				e.Title = p.Title
			}
			switch filepath.Ext(e.Path) {
			case ".article":
				d.Articles = append(d.Articles, e)
			case ".slide":
				d.Slides = append(d.Slides, e)
			}
		} else if showFile(e.Name) {
			d.Other = append(d.Other, e)
		}
	}
	if d.Path == "." {
		d.Path = ""
	}
	sort.Sort(d.Dirs)
	sort.Sort(d.Slides)
	sort.Sort(d.Articles)
	sort.Sort(d.Other)
	return true, dirListTemplate.Execute(w, d)
}

// showFile reports whether the given file should be displayed in the list.
func showFile(n string) bool {
	switch filepath.Ext(n) {
	case ".pdf":
	case ".html":
	case ".go":
	default:
		return isDoc(n)
	}
	return true
}

// showDir reports whether the given directory should be displayed in the list.
func showDir(n string) bool {
	if len(n) > 0 && (n[0] == '.' || n[0] == '_') || n == "present" {
		return false
	}
	return true
}

type dirListData struct {
	Path                          string
	Dirs, Slides, Articles, Other dirEntrySlice
}

type dirEntry struct {
	Name, Path, Title string
}

type dirEntrySlice []dirEntry

func (s dirEntrySlice) Len() int           { return len(s) }
func (s dirEntrySlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s dirEntrySlice) Less(i, j int) bool { return s[i].Name < s[j].Name }

var dirListTemplate = template.Must(template.New("").Parse(dirListHTML))

const dirListHTML = `<!DOCTYPE html>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Talks - The Go Programming Language</title>
  <link type="text/css" rel="stylesheet" href="/static/dir.css">

<form method="GET" action="http://golang.org/search">

Documents
References
Packages
The Project
Help
<input type="text" id="search" name="q" class="inactive" value="Search">

The Go Programming Language

</form>

Go talks

  {{with .Path}}
{{.}}
{{end}}

  {{with .Articles}}

Articles:

  {{range .}}

{{.Name}}: {{.Title}}

  {{end}}

  {{end}}

  {{with .Slides}}

Slide decks:

  {{range .}}

{{.Name}}: {{.Title}}

  {{end}}

  {{end}}

  {{with .Other}}

Files:

  {{range .}}

{{.Name}}

  {{end}}

  {{end}}

  {{with .Dirs}}

Sub-directories:

  {{range .}}

{{.Name}}

  {{end}}

  {{end}}

Except as noted,
the content of this page is licensed under the
Creative Commons Attribution 3.0 License,
and code is licensed under a BSD license.

Terms of Service |
Privacy Policy

`
<|endoftext|>


<commit_before>// Copyright 2017 The Prometheus Authors
// 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 promlog defines standardised ways to initialize Go kit loggers
// across Prometheus components.
// It should typically only ever be imported by main packages.
package promlog

import (
	"os"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pkg/errors"
)

// AllowedLevel is a settable identifier for the minimum level a log entry
// must be have.
type AllowedLevel struct {
	s string
	o level.Option
}

func (l *AllowedLevel) String() string {
	return l.s
}

// Set updates the value of the allowed level.
func (l *AllowedLevel) Set(s string) error {
	switch s {
	case "debug":
		l.o = level.AllowDebug()
	case "info":
		l.o = level.AllowInfo()
	case "warn":
		l.o = level.AllowWarn()
	case "error":
		l.o = level.AllowError()
	default:
		return errors.Errorf("unrecognized log level %q", s)
	}
	l.s = s
	return nil
}

// AllowedFormat is a settable identifier for the output format that the logger can have.
type AllowedFormat struct {
	s string
}

func (f *AllowedFormat) String() string {
	return f.s
}

// Set updates the value of the allowed format.
func (f *AllowedFormat) Set(s string) error {
	switch s {
	case "logfmt", "json":
		f.s = s
	default:
		return errors.Errorf("unrecognized log format %q", s)
	}
	return nil
}

// Config is a struct containing configurable settings for the logger
type Config struct {
	Level  *AllowedLevel
	Format *AllowedFormat
}

// New returns a new leveled oklog logger. Each logged line will be annotated
// with a timestamp. The output always goes to stderr.
func New(config *Config) log.Logger {
	var l log.Logger
	if config.Format.s == "logfmt" {
		l = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
	} else {
		l = log.NewJSONLogger(log.NewSyncWriter(os.Stderr))
	}

	l = level.NewFilter(l, config.Level.o)
	l = log.With(l, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
	return l
}
<commit_msg>Use fixed length millisecond timestamp format for logs (#172)<commit_after>// Copyright 2017 The Prometheus Authors
// 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 promlog defines standardised ways to initialize Go kit loggers
// across Prometheus components.
// It should typically only ever be imported by main packages.
package promlog

import (
	"os"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pkg/errors"
)

var (
	// This timestamp format differs from RFC3339Nano by using .000 instead
	// of .999999999 which changes the timestamp from 9 variable to 3 fixed
	// decimals (.130 instead of .130987456).
	timestampFormat = log.TimestampFormat(
		func() time.Time { return time.Now().UTC() },
		"2006-01-02T15:04:05.000Z07:00",
	)
)

// AllowedLevel is a settable identifier for the minimum level a log entry
// must be have.
type AllowedLevel struct {
	s string
	o level.Option
}

func (l *AllowedLevel) String() string {
	return l.s
}

// Set updates the value of the allowed level.
func (l *AllowedLevel) Set(s string) error {
	switch s {
	case "debug":
		l.o = level.AllowDebug()
	case "info":
		l.o = level.AllowInfo()
	case "warn":
		l.o = level.AllowWarn()
	case "error":
		l.o = level.AllowError()
	default:
		return errors.Errorf("unrecognized log level %q", s)
	}
	l.s = s
	return nil
}

// AllowedFormat is a settable identifier for the output format that the logger can have.
type AllowedFormat struct {
	s string
}

func (f *AllowedFormat) String() string {
	return f.s
}

// Set updates the value of the allowed format.
func (f *AllowedFormat) Set(s string) error {
	switch s {
	case "logfmt", "json":
		f.s = s
	default:
		return errors.Errorf("unrecognized log format %q", s)
	}
	return nil
}

// Config is a struct containing configurable settings for the logger
type Config struct {
	Level  *AllowedLevel
	Format *AllowedFormat
}

// New returns a new leveled oklog logger. Each logged line will be annotated
// with a timestamp. The output always goes to stderr.
func New(config *Config) log.Logger {
	var l log.Logger
	if config.Format.s == "logfmt" {
		l = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
	} else {
		l = log.NewJSONLogger(log.NewSyncWriter(os.Stderr))
	}

	l = level.NewFilter(l, config.Level.o)
	l = log.With(l, "ts", timestampFormat, "caller", log.DefaultCaller)
	return l
}
<|endoftext|>