<commit_before>package null

import (
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"errors"
	"time"

	"github.com/reflexionhealth/vanilla/date"
)

var JsonNull = []byte("null")

// String is a nullable string that doesn't require an extra allocation or dereference
// The builting sql package has a NullString, but it doesn't implement json.Marshaler
type String sql.NullString

func (ns *String) Set(value string) {
	ns.Valid = true
	ns.String = value
}

// Implement sql.Scanner interface
func (ns *String) Scan(src interface{}) error {
	return (*sql.NullString)(ns).Scan(src)
}

// Implement sql.driver.Valuer interface
func (ns String) Value() (driver.Value, error) {
	return (sql.NullString)(ns).Value()
}

// Implement json.Marshaler interface
func (ns String) MarshalJSON() ([]byte, error) {
	if ns.Valid {
		return json.Marshal(ns.String)
	} else {
		return []byte("null"), nil
	}
}

// Int64 is a nullable int64 that doesn't require an extra allocation or dereference
// The builting sql package has a NullInt64, but it doesn't implement json.Marshaler
type Int64 sql.NullInt64

func (ni *Int64) Set(value int64) {
	ni.Valid = true
	ni.Int64 = value
}

// Implement sql.Scanner interface
func (ni *Int64) Scan(src interface{}) error {
	return (*sql.NullInt64)(ni).Scan(src)
}

// Implement sql.driver.Valuer interface
func (ni Int64) Value() (driver.Value, error) {
	return (sql.NullInt64)(ni).Value()
}

// Implement json.Marshaler interface
func (ni Int64) MarshalJSON() ([]byte, error) {
	if ni.Valid {
		return json.Marshal(ni.Int64)
	} else {
		return JsonNull, nil
	}
}

// Time is a nullable time.Time that doesn't require an extra allocation or dereference
type Time struct {
	Time  time.Time
	Valid bool
}

func (nt *Time) Set(value time.Time) {
	nt.Valid = true
	nt.Time = value
}

// Scan implements the sql.Scanner interface.
func (nt *Time) Scan(src interface{}) error {
	if src == nil {
		nt.Valid = false
		return nil
	}

	switch t := src.(type) {
	case string:
		var err error
		nt.Time, err = time.Parse("2006-01-02 15:04:05", t)
		if err != nil {
			return err
		}
	case []byte:
		var err error
		nt.Time, err = time.Parse("2006-01-02 15:04:05", string(t))
		if err != nil {
			return err
		}
	case time.Time:
		nt.Time = t
	default:
		return errors.New("sql/null: scan value was not a Time, []byte, string, or nil")
	}

	nt.Valid = true
	return nil
}

// Value implements the sql.driver.Valuer interface
func (nt Time) Value() (driver.Value, error) {
	if !nt.Valid {
		return nil, nil
	} else {
		return nt.Time, nil
	}
}

// Implement json.Marshaler interface
func (nt Time) MarshalJSON() ([]byte, error) {
	if nt.Valid {
		return json.Marshal(nt.Time)
	} else {
		return JsonNull, nil
	}
}

// Date is a nullable date.Date that doesn't require an extra allocation or dereference
type Date struct {
	Date  date.Date
	Valid bool
}

func (nd *Date) Set(value date.Date) {
	nd.Valid = true
	nd.Date = value
}

// Implement sql.Scanner interface
func (nd *Date) Scan(src interface{}) error {
	if src == nil {
		nd.Valid = false
		return nil
	}

	var nt Time
	err := nt.Scan(src)
	if err != nil {
		return err
	}

	nd.Valid = nt.Valid
	if nt.Valid {
		nd.Date = date.From(nt.Time)
	}

	return nil
}

// Implement sql.driver.Valuer interface
func (nd Date) Value() (driver.Value, error) {
	if !nd.Valid {
		return nil, nil
	} else {
		return nd.Date.Value()
	}
}

// Implement json.Marshaler interface
func (nd Date) MarshalJSON() ([]byte, error) {
	if nd.Valid {
		return nd.Date.MarshalJSON()
	} else {
		return JsonNull, nil
	}
}

// Implement json.Unmarshaler interface
func (nd Date) UnmarshalJSON(bytes []byte) error {
	if bytes == nil {
		nd.Valid = false
		nd.Date = date.Date{}
		return nil
	} else {
		nd.Valid = true
		err := nd.Date.UnmarshalJSON(bytes)
		return err
	}
}
<commit_msg>null: implement json unmarshaller interface for null string<commit_after>package null

import (
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"errors"
	"time"

	"github.com/reflexionhealth/vanilla/date"
)

var JsonNull = []byte("null")

// String is a nullable string that doesn't require an extra allocation or dereference
// The builting sql package has a NullString, but it doesn't implement json.Marshaler
type String sql.NullString

func (ns *String) Set(value string) {
	ns.Valid = true
	ns.String = value
}

// Implement sql.Scanner interface
func (ns *String) Scan(src interface{}) error {
	return (*sql.NullString)(ns).Scan(src)
}

// Implement sql.driver.Valuer interface
func (ns String) Value() (driver.Value, error) {
	return (sql.NullString)(ns).Value()
}

// Implement json.Marshaler interface
func (ns String) MarshalJSON() ([]byte, error) {
	if ns.Valid {
		return json.Marshal(ns.String)
	} else {
		return []byte("null"), nil
	}
}

// Implement json.Unmarshaler interface
func (ns String) UnmarshalJSON(bytes []byte) error {
	if bytes == nil {
		nd.Valid = false
		ns.String = ""
		return nil
	} else {
		ns.Valid = true
		return json.Unmarshal(bytes, ns)
	}
}

// Int64 is a nullable int64 that doesn't require an extra allocation or dereference
// The builting sql package has a NullInt64, but it doesn't implement json.Marshaler
type Int64 sql.NullInt64

func (ni *Int64) Set(value int64) {
	ni.Valid = true
	ni.Int64 = value
}

// Implement sql.Scanner interface
func (ni *Int64) Scan(src interface{}) error {
	return (*sql.NullInt64)(ni).Scan(src)
}

// Implement sql.driver.Valuer interface
func (ni Int64) Value() (driver.Value, error) {
	return (sql.NullInt64)(ni).Value()
}

// Implement json.Marshaler interface
func (ni Int64) MarshalJSON() ([]byte, error) {
	if ni.Valid {
		return json.Marshal(ni.Int64)
	} else {
		return JsonNull, nil
	}
}

// Time is a nullable time.Time that doesn't require an extra allocation or dereference
type Time struct {
	Time  time.Time
	Valid bool
}

func (nt *Time) Set(value time.Time) {
	nt.Valid = true
	nt.Time = value
}

// Scan implements the sql.Scanner interface.
func (nt *Time) Scan(src interface{}) error {
	if src == nil {
		nt.Valid = false
		return nil
	}

	switch t := src.(type) {
	case string:
		var err error
		nt.Time, err = time.Parse("2006-01-02 15:04:05", t)
		if err != nil {
			return err
		}
	case []byte:
		var err error
		nt.Time, err = time.Parse("2006-01-02 15:04:05", string(t))
		if err != nil {
			return err
		}
	case time.Time:
		nt.Time = t
	default:
		return errors.New("sql/null: scan value was not a Time, []byte, string, or nil")
	}

	nt.Valid = true
	return nil
}

// Value implements the sql.driver.Valuer interface
func (nt Time) Value() (driver.Value, error) {
	if !nt.Valid {
		return nil, nil
	} else {
		return nt.Time, nil
	}
}

// Implement json.Marshaler interface
func (nt Time) MarshalJSON() ([]byte, error) {
	if nt.Valid {
		return json.Marshal(nt.Time)
	} else {
		return JsonNull, nil
	}
}

// Date is a nullable date.Date that doesn't require an extra allocation or dereference
type Date struct {
	Date  date.Date
	Valid bool
}

func (nd *Date) Set(value date.Date) {
	nd.Valid = true
	nd.Date = value
}

// Implement sql.Scanner interface
func (nd *Date) Scan(src interface{}) error {
	if src == nil {
		nd.Valid = false
		return nil
	}

	var nt Time
	err := nt.Scan(src)
	if err != nil {
		return err
	}

	nd.Valid = nt.Valid
	if nt.Valid {
		nd.Date = date.From(nt.Time)
	}

	return nil
}

// Implement sql.driver.Valuer interface
func (nd Date) Value() (driver.Value, error) {
	if !nd.Valid {
		return nil, nil
	} else {
		return nd.Date.Value()
	}
}

// Implement json.Marshaler interface
func (nd Date) MarshalJSON() ([]byte, error) {
	if nd.Valid {
		return nd.Date.MarshalJSON()
	} else {
		return JsonNull, nil
	}
}

// Implement json.Unmarshaler interface
func (nd Date) UnmarshalJSON(bytes []byte) error {
	if bytes == nil {
		nd.Valid = false
		nd.Date = date.Date{}
		return nil
	} else {
		nd.Valid = true
		err := nd.Date.UnmarshalJSON(bytes)
		return err
	}
}
<|endoftext|>


<commit_before>package collector

// +build !nozfs

import (
	"errors"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

type zfsMetricValue int

const zfsErrorValue = zfsMetricValue(-1)

var zfsNotAvailableError = errors.New("ZFS / ZFS statistics are not available")

type zfsSysctl string
type zfsSubsystemName string

const (
	arc = zfsSubsystemName("zfs_arc")
)

// Metrics

type zfsMetric struct {
	subsystem zfsSubsystemName // The Prometheus subsystem name.
	name      string           // The Prometheus name of the metric.
	sysctl    zfsSysctl        // The sysctl of the ZFS metric.
}

func (m *zfsMetric) BuildFQName() string {
	return prometheus.BuildFQName(Namespace, string(m.subsystem), m.name)
}

// Collector

func init() {
	Factories["zfs"] = NewZFSCollector
}

type zfsCollector struct {
	zfsMetrics     []zfsMetric
	metricProvider zfsMetricProvider
}

func NewZFSCollector() (Collector, error) {
	return &zfsCollector{
		zfsMetrics: []zfsMetric{
			zfsMetric{arc, "mru_size", zfsSysctl("kstat.zfs.misc.arcstats.p")},
			zfsMetric{arc, "size", zfsSysctl("kstat.zfs.misc.arcstats.size")},
			zfsMetric{arc, "target_min_size", zfsSysctl("kstat.zfs.misc.arcstats.c_min")},
			zfsMetric{arc, "target_size", zfsSysctl("kstat.zfs.misc.arcstats.c")},
			zfsMetric{arc, "target_max_size", zfsSysctl("kstat.zfs.misc.arcstats.c_max")},
			zfsMetric{arc, "hits", zfsSysctl("kstat.zfs.misc.arcstats.hits")},
			zfsMetric{arc, "misses", zfsSysctl("kstat.zfs.misc.arcstats.misses")},
		},
		metricProvider: NewZFSMetricProvider(),
	}, nil
}

func (c *zfsCollector) Update(ch chan<- prometheus.Metric) (err error) {

	log.Debug("Preparing metrics update")
	err = c.metricProvider.PrepareUpdate()
	switch {
	case err == zfsNotAvailableError:
		log.Debug(err)
		return nil
		return err
	}
	defer c.metricProvider.InvalidateCache()

	log.Debugf("Fetching %d metrics", len(c.zfsMetrics))
	for _, metric := range c.zfsMetrics {

		value, err := c.metricProvider.Value(metric.sysctl)
		if err != nil {
			return err
		}

		ch <- prometheus.MustNewConstMetric(
			prometheus.NewDesc(
				metric.BuildFQName(),
				metric.HelpString(),
				nil,
				nil,
			),
			prometheus.UntypedValue,
			float64(value),
		)

	}

	return err
}

// Metrics Provider
// Platform-dependend parts implemented in zfs_${os} files.

type zfsMetricProvider struct {
	values map[zfsSysctl]zfsMetricValue
}

func NewZFSMetricProvider() zfsMetricProvider {
	return zfsMetricProvider{
		values: make(map[zfsSysctl]zfsMetricValue),
	}

}

func (p *zfsMetricProvider) InvalidateCache() {
	p.values = make(map[zfsSysctl]zfsMetricValue)
}

func (p *zfsMetricProvider) Value(s zfsSysctl) (value zfsMetricValue, err error) {

	var ok bool
	value = zfsErrorValue

	value, ok = p.values[s]
	if !ok {
		value, err = p.handleMiss(s)
		if err != nil {
			return value, err
		}
	}

	return value, err
}
<commit_msg>Fix unreachable code.<commit_after>package collector

// +build !nozfs

import (
	"errors"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

type zfsMetricValue int

const zfsErrorValue = zfsMetricValue(-1)

var zfsNotAvailableError = errors.New("ZFS / ZFS statistics are not available")

type zfsSysctl string
type zfsSubsystemName string

const (
	arc = zfsSubsystemName("zfs_arc")
)

// Metrics

type zfsMetric struct {
	subsystem zfsSubsystemName // The Prometheus subsystem name.
	name      string           // The Prometheus name of the metric.
	sysctl    zfsSysctl        // The sysctl of the ZFS metric.
}

func (m *zfsMetric) BuildFQName() string {
	return prometheus.BuildFQName(Namespace, string(m.subsystem), m.name)
}

// Collector

func init() {
	Factories["zfs"] = NewZFSCollector
}

type zfsCollector struct {
	zfsMetrics     []zfsMetric
	metricProvider zfsMetricProvider
}

func NewZFSCollector() (Collector, error) {
	return &zfsCollector{
		zfsMetrics: []zfsMetric{
			zfsMetric{arc, "mru_size", zfsSysctl("kstat.zfs.misc.arcstats.p")},
			zfsMetric{arc, "size", zfsSysctl("kstat.zfs.misc.arcstats.size")},
			zfsMetric{arc, "target_min_size", zfsSysctl("kstat.zfs.misc.arcstats.c_min")},
			zfsMetric{arc, "target_size", zfsSysctl("kstat.zfs.misc.arcstats.c")},
			zfsMetric{arc, "target_max_size", zfsSysctl("kstat.zfs.misc.arcstats.c_max")},
			zfsMetric{arc, "hits", zfsSysctl("kstat.zfs.misc.arcstats.hits")},
			zfsMetric{arc, "misses", zfsSysctl("kstat.zfs.misc.arcstats.misses")},
		},
		metricProvider: NewZFSMetricProvider(),
	}, nil
}

func (c *zfsCollector) Update(ch chan<- prometheus.Metric) (err error) {

	log.Debug("Preparing metrics update")
	err = c.metricProvider.PrepareUpdate()
	switch {
	case err == zfsNotAvailableError:
		log.Debug(err)
		return nil
	case err != nil:
		return err
	}
	defer c.metricProvider.InvalidateCache()

	log.Debugf("Fetching %d metrics", len(c.zfsMetrics))
	for _, metric := range c.zfsMetrics {

		value, err := c.metricProvider.Value(metric.sysctl)
		if err != nil {
			return err
		}

		ch <- prometheus.MustNewConstMetric(
			prometheus.NewDesc(
				metric.BuildFQName(),
				metric.HelpString(),
				nil,
				nil,
			),
			prometheus.UntypedValue,
			float64(value),
		)

	}

	return err
}

// Metrics Provider
// Platform-dependend parts implemented in zfs_${os} files.

type zfsMetricProvider struct {
	values map[zfsSysctl]zfsMetricValue
}

func NewZFSMetricProvider() zfsMetricProvider {
	return zfsMetricProvider{
		values: make(map[zfsSysctl]zfsMetricValue),
	}

}

func (p *zfsMetricProvider) InvalidateCache() {
	p.values = make(map[zfsSysctl]zfsMetricValue)
}

func (p *zfsMetricProvider) Value(s zfsSysctl) (value zfsMetricValue, err error) {

	var ok bool
	value = zfsErrorValue

	value, ok = p.values[s]
	if !ok {
		value, err = p.handleMiss(s)
		if err != nil {
			return value, err
		}
	}

	return value, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"

	"github.com/codegangsta/cli"
)

func cmdNodeAdd(c *cli.Context) {
	keySlice := []string{"assetid", "name", "team", "server", "online"}
	reqSlice := []string{"assetid", "name", "team"}

	switch utl.GetCliArgumentCount(c) {
	case 6, 8, 10:
		break
	default:
		utl.Abort("Syntax error, unexpected argument count")
	}
	argSlice := utl.GetFullArgumentSlice(c)

	options, optional := utl.ParseVariableArguments(keySlice, reqSlice, argSlice)
	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}

	utl.ValidateStringAsNodeAssetId(options["assetid"])
	if utl.SliceContainsString("online", optional) {
		utl.ValidateStringAsBool(options["online"])
		req.Node.IsOnline, _ = strconv.ParseBool(options["online"])
	} else {
		req.Node.IsOnline = true
	}
	if utl.SliceContainsString("server", optional) {
		req.Node.Server = utl.TryGetServerByUUIDOrName(options["server"])
	}
	req.Node.AssetId, _ = strconv.ParseUint(options["assetid"], 10, 64)
	req.Node.Name = options["name"]
	req.Node.Team = utl.TryGetTeamByUUIDOrName(options["team"])

	resp := utl.PostRequestWithBody(req, "/nodes/")
	fmt.Println(resp)
}

func cmdNodeDel(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	_ = utl.DeleteRequest(path)
}

func cmdNodePurge(c *cli.Context) {
	var (
		path string
		req  somaproto.ProtoRequestNode
	)
	if c.Bool("all") {
		utl.ValidateCliArgumentCount(c, 0)
		path = "/nodes/"
	} else {
		utl.ValidateCliArgumentCount(c, 1)
		id := utl.TryGetNodeByUUIDOrName(c.Args().First())
		path = fmt.Sprintf("/nodes/%s", id)
	}

	req.Purge = true

	_ = utl.DeleteRequestWithBody(req, path)
}

func cmdNodeRestore(c *cli.Context) {
	var (
		path string
		req  somaproto.ProtoRequestNode
	)
	if c.Bool("all") {
		utl.ValidateCliArgumentCount(c, 0)
		path = "/nodes/"
	} else {
		utl.ValidateCliArgumentCount(c, 1)
		id := utl.TryGetNodeByUUIDOrName(c.Args().First())
		path = fmt.Sprintf("/nodes/%s", id)
	}

	req.Restore = true

	_ = utl.DeleteRequestWithBody(req, path)
}

func cmdNodeRename(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 3)
	utl.ValidateCliArgument(c, 2, "to")
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Name = c.Args().Get(2)

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeRepo(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 3)
	utl.ValidateCliArgument(c, 2, "to")
	id := utl.TryGetNodeByUUIDOrName(c.Args().Get(0))
	team := c.Args().Get(2)
	// try resolving team name to uuid as name validation
	_ = utl.GetTeamIdByName(team)
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Team = team

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeMove(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 3)
	utl.ValidateCliArgument(c, 2, "to")
	id := utl.TryGetNodeByUUIDOrName(c.Args().Get(0))
	server := c.Args().Get(2)
	// try resolving server name to uuid as name validation
	_ = utl.GetServerAssetIdByName(server)
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Server = server

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeOnline(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.IsOnline = true

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeOffline(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.IsOnline = false

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeAssign(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 3)
	multiple := []string{}
	unique := []string{"to"}
	required := []string{"to"}

	opts := utl.ParseVariadicArguments(multiple, unique, required, c.Args().Tail())
	bucketId := utl.BucketByUUIDOrName(opts["to"][0])
	repoId := utl.GetRepositoryIdForBucket(bucketId)
	nodeId := utl.TryGetNodeByUUIDOrName(c.Args().First())

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Id = nodeId
	req.Node.Config = &somaproto.ProtoNodeConfig{}
	req.Node.Config.RepositoryId = repoId
	req.Node.Config.BucketId = bucketId

	path := fmt.Sprintf("/nodes/%s/config", nodeId)
	resp := utl.PutRequestWithBody(req, path)
	fmt.Println(resp)
}

func cmdNodeList(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 0)

	resp := utl.GetRequest("/nodes/")
	fmt.Println(resp)
}

func cmdNodeShow(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	resp := utl.GetRequest(path)
	fmt.Println(resp)
}

func cmdNodePropertyAdd(c *cli.Context) {
	// preliminary argv validation
	switch utl.GetCliArgumentCount(c) {
	case 4, 6, 8, 10, 12:
		break
	default:
		utl.Abort("Syntax error, unexpected argument count")
	}
	utl.ValidateCliArgument(c, 3, "to")
	argSlice := utl.GetFullArgumentSlice(c)

	// get property types
	typSlice := utl.PropertyTypes

	// first argument must be a valid property type
	utl.ValidateStringInSlice(argSlice[0], typSlice)

	// TODO: validate property of that type and name exists
	propertyType := argSlice[0]
	property := argSlice[1]

	// get which node is being modified
	id := utl.TryGetNodeByUUIDOrName(argSlice[3])
	path := fmt.Sprintf("/nodes/%s/property/", id)

	// variable key/value part of argv
	argSlice = argSlice[4:]

	// define accepted and required keys
	keySlice := []string{"inheritance", "childrenonly", "view", "value"}
	reqSlice := []string{"view"}
	if propertyType != "service" {
		// non service properties require values, services are
		// predefined and do not
		reqSlice = append(reqSlice, "value")
	}
	options, optional := utl.ParseVariableArguments(keySlice, reqSlice, argSlice)

	// build node property JSON
	var prop somaproto.ProtoNodeProperty
	prop.Type = propertyType
	prop.View = options["view"] //required
	prop.Property = property
	// add value if it was required
	if utl.SliceContainsString("value", reqSlice) {
		prop.Value = options["value"]
	}

	// optional inheritance, default true
	if utl.SliceContainsString("inheritance", optional) {
		utl.ValidateStringAsBool(options["inheritance"])
		prop.Inheritance, _ = strconv.ParseBool(options["inheritance"])
	} else {
		prop.Inheritance = true
	}

	// optional childrenonly, default false
	if utl.SliceContainsString("childrenonly", optional) {
		utl.ValidateStringAsBool(options["childrenonly"])
		prop.ChildrenOnly, _ = strconv.ParseBool(options["childrenonly"])
	} else {
		prop.ChildrenOnly = false
	}

	// build request JSON
	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Properties = append(req.Node.Properties, prop)

	_ = utl.PostRequestWithBody(req, path)
	// TODO save jobid locally as outstanding
}

func cmdNodePropertyGet(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 6)
	utl.ValidateCliArgument(c, 3, "from")
	utl.ValidateCliArgument(c, 5, "view")
	argSlice := utl.GetFullArgumentSlice(c)

	// first argument must be a valid property type, sixth a view
	utl.ValidateStringInSlice(argSlice[0], utl.PropertyTypes)
	utl.ValidateStringInSlice(argSlice[5], utl.Views)

	// get which node is being modified
	id := utl.TryGetNodeByUUIDOrName(argSlice[3])

	// TODO: validate property of that type and name exists
	path := fmt.Sprintf("/nodes/%s/property/%s/%s/%s",
		id,
		argSlice[0], // type
		argSlice[5], // view
		argSlice[1], // property
	)

	_ = utl.GetRequest(path)
}

func cmdNodePropertyDel(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 6)
	utl.ValidateCliArgument(c, 3, "from")
	utl.ValidateCliArgument(c, 5, "view")
	argSlice := utl.GetFullArgumentSlice(c)

	// first argument must be a valid property type, sixth a view
	utl.ValidateStringInSlice(argSlice[0], utl.PropertyTypes)
	utl.ValidateStringInSlice(argSlice[5], utl.Views)

	// get which node is being modified
	id := utl.TryGetNodeByUUIDOrName(argSlice[3])

	// TODO: validate property of that type and name exists
	path := fmt.Sprintf("/nodes/%s/property/%s/%s/%s",
		id,
		argSlice[0], //type
		argSlice[5], //view
		argSlice[1], //property
	)

	_ = utl.DeleteRequest(path)
}

func cmdNodePropertyList(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s/property/", id)

	req := somaproto.ProtoRequestNode{}
	req.Filter = &somaproto.ProtoNodeFilter{}

	if c.Bool("all") {
		req.Filter.LocalProperty = false
		_ = utl.GetRequest(path)
	} else {
		req.Filter.LocalProperty = true
		_ = utl.GetRequestWithBody(req, path)
	}
}

func cmdNodePropertyShow(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s/property/", id)

	_ = utl.GetRequest(path)
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Make somaadm compile after somaproto.ProtoNode changes<commit_after>package main

import (
	"fmt"
	"strconv"

	"github.com/codegangsta/cli"
)

func cmdNodeAdd(c *cli.Context) {
	keySlice := []string{"assetid", "name", "team", "server", "online"}
	reqSlice := []string{"assetid", "name", "team"}

	switch utl.GetCliArgumentCount(c) {
	case 6, 8, 10:
		break
	default:
		utl.Abort("Syntax error, unexpected argument count")
	}
	argSlice := utl.GetFullArgumentSlice(c)

	options, optional := utl.ParseVariableArguments(keySlice, reqSlice, argSlice)
	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}

	utl.ValidateStringAsNodeAssetId(options["assetid"])
	if utl.SliceContainsString("online", optional) {
		utl.ValidateStringAsBool(options["online"])
		req.Node.IsOnline, _ = strconv.ParseBool(options["online"])
	} else {
		req.Node.IsOnline = true
	}
	if utl.SliceContainsString("server", optional) {
		req.Node.Server = utl.TryGetServerByUUIDOrName(options["server"])
	}
	req.Node.AssetId, _ = strconv.ParseUint(options["assetid"], 10, 64)
	req.Node.Name = options["name"]
	req.Node.Team = utl.TryGetTeamByUUIDOrName(options["team"])

	resp := utl.PostRequestWithBody(req, "/nodes/")
	fmt.Println(resp)
}

func cmdNodeDel(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	_ = utl.DeleteRequest(path)
}

func cmdNodePurge(c *cli.Context) {
	var (
		path string
		req  somaproto.ProtoRequestNode
	)
	if c.Bool("all") {
		utl.ValidateCliArgumentCount(c, 0)
		path = "/nodes/"
	} else {
		utl.ValidateCliArgumentCount(c, 1)
		id := utl.TryGetNodeByUUIDOrName(c.Args().First())
		path = fmt.Sprintf("/nodes/%s", id)
	}

	req.Purge = true

	_ = utl.DeleteRequestWithBody(req, path)
}

func cmdNodeRestore(c *cli.Context) {
	var (
		path string
		req  somaproto.ProtoRequestNode
	)
	if c.Bool("all") {
		utl.ValidateCliArgumentCount(c, 0)
		path = "/nodes/"
	} else {
		utl.ValidateCliArgumentCount(c, 1)
		id := utl.TryGetNodeByUUIDOrName(c.Args().First())
		path = fmt.Sprintf("/nodes/%s", id)
	}

	req.Restore = true

	_ = utl.DeleteRequestWithBody(req, path)
}

func cmdNodeRename(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 3)
	utl.ValidateCliArgument(c, 2, "to")
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Name = c.Args().Get(2)

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeRepo(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 3)
	utl.ValidateCliArgument(c, 2, "to")
	id := utl.TryGetNodeByUUIDOrName(c.Args().Get(0))
	team := c.Args().Get(2)
	// try resolving team name to uuid as name validation
	_ = utl.GetTeamIdByName(team)
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Team = team

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeMove(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 3)
	utl.ValidateCliArgument(c, 2, "to")
	id := utl.TryGetNodeByUUIDOrName(c.Args().Get(0))
	server := c.Args().Get(2)
	// try resolving server name to uuid as name validation
	_ = utl.GetServerAssetIdByName(server)
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Server = server

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeOnline(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.IsOnline = true

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeOffline(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.IsOnline = false

	_ = utl.PatchRequestWithBody(req, path)
}

func cmdNodeAssign(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 3)
	multiple := []string{}
	unique := []string{"to"}
	required := []string{"to"}

	opts := utl.ParseVariadicArguments(multiple, unique, required, c.Args().Tail())
	bucketId := utl.BucketByUUIDOrName(opts["to"][0])
	repoId := utl.GetRepositoryIdForBucket(bucketId)
	nodeId := utl.TryGetNodeByUUIDOrName(c.Args().First())

	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	req.Node.Id = nodeId
	req.Node.Config = &somaproto.ProtoNodeConfig{}
	req.Node.Config.RepositoryId = repoId
	req.Node.Config.BucketId = bucketId

	path := fmt.Sprintf("/nodes/%s/config", nodeId)
	resp := utl.PutRequestWithBody(req, path)
	fmt.Println(resp)
}

func cmdNodeList(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 0)

	resp := utl.GetRequest("/nodes/")
	fmt.Println(resp)
}

func cmdNodeShow(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s", id)

	resp := utl.GetRequest(path)
	fmt.Println(resp)
}

func cmdNodePropertyAdd(c *cli.Context) {
	// preliminary argv validation
	switch utl.GetCliArgumentCount(c) {
	case 4, 6, 8, 10, 12:
		break
	default:
		utl.Abort("Syntax error, unexpected argument count")
	}
	utl.ValidateCliArgument(c, 3, "to")
	argSlice := utl.GetFullArgumentSlice(c)

	// XXX THIS WHOLE THING IS BROKEN, NEEDS ADAPTION
	// XXX TO WORK WITH somaproto.TreeProperty
	// XXX IS JUST FIXED UP ENOUGH TO COMPILE

	// get property types
	typSlice := utl.PropertyTypes

	// first argument must be a valid property type
	utl.ValidateStringInSlice(argSlice[0], typSlice)

	// TODO: validate property of that type and name exists
	propertyType := argSlice[0]
	//property := argSlice[1]

	// get which node is being modified
	id := utl.TryGetNodeByUUIDOrName(argSlice[3])
	path := fmt.Sprintf("/nodes/%s/property/%s/", id, argSlice[0])

	// variable key/value part of argv
	argSlice = argSlice[4:]

	// define accepted and required keys
	keySlice := []string{"inheritance", "childrenonly", "view", "value"}
	reqSlice := []string{"view"}
	if propertyType != "service" {
		// non service properties require values, services are
		// predefined and do not
		reqSlice = append(reqSlice, "value")
	}
	options, optional := utl.ParseVariableArguments(keySlice, reqSlice, argSlice)

	// build node property JSON
	var prop somaproto.TreeProperty
	prop.PropertyType = propertyType
	prop.View = options["view"] //required
	//prop.Property = property XXX BROKEN
	// add value if it was required
	if utl.SliceContainsString("value", reqSlice) {
		//prop.Value = options["value"]
	}

	// optional inheritance, default true
	if utl.SliceContainsString("inheritance", optional) {
		utl.ValidateStringAsBool(options["inheritance"])
		prop.Inheritance, _ = strconv.ParseBool(options["inheritance"])
	} else {
		prop.Inheritance = true
	}

	// optional childrenonly, default false
	if utl.SliceContainsString("childrenonly", optional) {
		utl.ValidateStringAsBool(options["childrenonly"])
		prop.ChildrenOnly, _ = strconv.ParseBool(options["childrenonly"])
	} else {
		prop.ChildrenOnly = false
	}

	// build request JSON
	req := somaproto.ProtoRequestNode{}
	req.Node = &somaproto.ProtoNode{}
	*req.Node.Properties = append(*req.Node.Properties, prop)

	_ = utl.PostRequestWithBody(req, path)
	// TODO save jobid locally as outstanding
}

func cmdNodePropertyGet(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 6)
	utl.ValidateCliArgument(c, 3, "from")
	utl.ValidateCliArgument(c, 5, "view")
	argSlice := utl.GetFullArgumentSlice(c)

	// first argument must be a valid property type, sixth a view
	utl.ValidateStringInSlice(argSlice[0], utl.PropertyTypes)
	utl.ValidateStringInSlice(argSlice[5], utl.Views)

	// get which node is being modified
	id := utl.TryGetNodeByUUIDOrName(argSlice[3])

	// TODO: validate property of that type and name exists
	path := fmt.Sprintf("/nodes/%s/property/%s/%s/%s",
		id,
		argSlice[0], // type
		argSlice[5], // view
		argSlice[1], // property
	)

	_ = utl.GetRequest(path)
}

func cmdNodePropertyDel(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 6)
	utl.ValidateCliArgument(c, 3, "from")
	utl.ValidateCliArgument(c, 5, "view")
	argSlice := utl.GetFullArgumentSlice(c)

	// first argument must be a valid property type, sixth a view
	utl.ValidateStringInSlice(argSlice[0], utl.PropertyTypes)
	utl.ValidateStringInSlice(argSlice[5], utl.Views)

	// get which node is being modified
	id := utl.TryGetNodeByUUIDOrName(argSlice[3])

	// TODO: validate property of that type and name exists
	path := fmt.Sprintf("/nodes/%s/property/%s/%s/%s",
		id,
		argSlice[0], //type
		argSlice[5], //view
		argSlice[1], //property
	)

	_ = utl.DeleteRequest(path)
}

func cmdNodePropertyList(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s/property/", id)

	req := somaproto.ProtoRequestNode{}
	req.Filter = &somaproto.ProtoNodeFilter{}

	if c.Bool("all") {
		req.Filter.LocalProperty = false
		_ = utl.GetRequest(path)
	} else {
		req.Filter.LocalProperty = true
		_ = utl.GetRequestWithBody(req, path)
	}
}

func cmdNodePropertyShow(c *cli.Context) {
	utl.ValidateCliArgumentCount(c, 1)
	id := utl.TryGetNodeByUUIDOrName(c.Args().First())
	path := fmt.Sprintf("/nodes/%s/property/", id)

	_ = utl.GetRequest(path)
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package imap

import "fmt"

const STORE_ARG_RANGE int = 2
const STORE_ARG_OPERATION int = 3
const STORE_ARG_SILENT int = 4
const STORE_ARG_FLAGS int = 5

func cmdStoreFlags(args commandArgs, c *Conn) {
	fmt.Printf("STORE command args: %+v\n\n", args)
	operation := args.Arg(STORE_ARG_OPERATION)
	flags := args.Arg(STORE_ARG_FLAGS)

	silent := false
	if args.Arg(STORE_ARG_SILENT) == ".SILENT" {
		silent = true
	}
	if silent {
		fmt.Printf("Silently ")
	}

	if operation == "+" {
		fmt.Printf("Add flags %s\n", flags)
	} else if operation == "-" {
		fmt.Printf("Remove flags %s\n", flags)
	} else {
		fmt.Printf("Set flags %s\n", flags)
	}

	c.writeResponse(args.Id(), "OK STORE Completed")
}
<commit_msg>User correct casing for (and de-export) constants in STORE command #8<commit_after>package imap

import "fmt"

const storeArgRange int = 2
const storeArgOperation int = 3
const storeArgSilent int = 4
const storeArgFlags int = 5

func cmdStoreFlags(args commandArgs, c *Conn) {
	fmt.Printf("STORE command args: %+v\n\n", args)
	operation := args.Arg(storeArgOperation)
	flags := args.Arg(storeArgFlags)

	silent := false
	if args.Arg(storeArgSilent) == ".SILENT" {
		silent = true
	}
	if silent {
		fmt.Printf("Silently ")
	}

	if operation == "+" {
		fmt.Printf("Add flags %s\n", flags)
	} else if operation == "-" {
		fmt.Printf("Remove flags %s\n", flags)
	} else {
		fmt.Printf("Set flags %s\n", flags)
	}

	c.writeResponse(args.Id(), "OK STORE Completed")
}
<|endoftext|>


<commit_before>/*
Copyright 2017 WALLIX

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 commands

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/wallix/awless/aws"
	"github.com/wallix/awless/cloud"
	"github.com/wallix/awless/config"
	"github.com/wallix/awless/console"
	"github.com/wallix/awless/graph"
	"github.com/wallix/awless/logger"
	"github.com/wallix/awless/sync"
)

var (
	listAllSiblingsFlag bool
)

func init() {
	RootCmd.AddCommand(showCmd)
	showCmd.Flags().BoolVar(&listAllSiblingsFlag, "siblings", false, "List all the resource's siblings")
}

var showCmd = &cobra.Command{
	Use:   "show REFERENCE",
	Short: "Show a resource and its interrelations given a REFERENCE: id or name",
	Example: `  awless show i-8d43b21b            # show an instance via its id
  awless show AIDAJ3Z24GOKHTZO4OIX6 # show a user via its id
  awless show jsmith                # show a user via its name`,
	PersistentPreRun:  applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook),
	PersistentPostRun: applyHooks(saveHistoryHook, verifyNewVersionHook),

	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) < 1 {
			return errors.New("REFERENCE required. See examples.")
		}

		ref := args[0]
		notFound := fmt.Sprintf("resource with reference %s not found", deprefix(ref))

		var resource *graph.Resource
		var gph *graph.Graph

		resource, gph = findResourceInLocalGraphs(ref)

		if resource == nil && localGlobalFlag {
			logger.Info(notFound)
			return nil
		} else if resource == nil {
			runFullSync()

			if resource, gph = findResourceInLocalGraphs(ref); resource == nil {
				logger.Info(notFound)
				return nil
			}
		}

		if !localGlobalFlag && config.GetAutosync() {
			srv, err := cloud.GetServiceForType(resource.Type())
			exitOn(err)
			logger.Verbosef("syncing service for %s type", resource.Type())
			if _, err = sync.DefaultSyncer.Sync(srv); err != nil {
				logger.Error(err)
			}
		}

		if resource != nil {
			showResource(resource, gph)
		}

		return nil
	},
}

func showResource(resource *graph.Resource, gph *graph.Graph) {
	displayer := console.BuildOptions(
		console.WithHeaders(console.DefaultsColumnDefinitions[resource.Type()]),
		console.WithFormat(listingFormat),
	).SetSource(resource).Build()

	exitOn(displayer.Print(os.Stderr))

	var parents []*graph.Resource
	err := gph.Accept(&graph.ParentsVisitor{From: resource, Each: graph.VisitorCollectFunc(&parents)})
	exitOn(err)

	fmt.Println(renderCyanBoldFn("\nRelations:"))

	var count int
	for i := len(parents) - 1; i >= 0; i-- {
		if count == 0 {
			fmt.Printf("%s\n", parents[i])
		} else {
			fmt.Printf("%s↳ %s\n", strings.Repeat("\t", count), parents[i])
		}
		count++
	}

	printWithTabs := func(r *graph.Resource, distance int) error {
		var tabs bytes.Buffer
		tabs.WriteString(strings.Repeat("\t", count))
		for i := 0; i < distance; i++ {
			tabs.WriteByte('\t')
		}

		display := r.String()
		if r.Same(resource) {
			display = renderGreenFn(resource.String())
		}
		fmt.Printf("%s↳ %s\n", tabs.String(), display)

		return nil
	}

	err = gph.Accept(&graph.ChildrenVisitor{From: resource, Each: printWithTabs, IncludeFrom: true})
	exitOn(err)

	var siblings []*graph.Resource
	err = gph.Accept(&graph.SiblingsVisitor{From: resource, Each: graph.VisitorCollectFunc(&siblings)})
	exitOn(err)
	printResourceList(renderCyanBoldFn("Siblings"), siblings, "display all with flag --siblings")

	appliedOn, err := gph.ListResourcesAppliedOn(resource)
	exitOn(err)
	printResourceList(renderCyanBoldFn("Applied on"), appliedOn)

	dependingOn, err := gph.ListResourcesDependingOn(resource)
	exitOn(err)
	printResourceList(renderCyanBoldFn("Depending on"), dependingOn)
}

func runFullSync() {
	if !config.GetAutosync() {
		logger.Info("autosync disabled")
		return
	}

	logger.Info("cannot resolve resource - running full sync")

	var services []cloud.Service
	for _, srv := range cloud.ServiceRegistry {
		services = append(services, srv)
	}

	if _, err := sync.DefaultSyncer.Sync(services...); err != nil {
		logger.Verbose(err)
	}
}

func findResourceInLocalGraphs(ref string) (*graph.Resource, *graph.Graph) {
	resources := resolveResourceFromRef(ref)
	switch len(resources) {
	case 0:
		return nil, nil
	case 1:
		res := resources[0]
		return res, sync.LoadCurrentLocalGraph(aws.ServicePerResourceType[res.Type()])
	default:
		var all []string
		for _, res := range resources {
			all = append(all, fmt.Sprintf("%s[%s]", res.Id(), res.Type()))
		}
		logger.Infof("%d resources found with name '%s': %s", len(resources), deprefix(ref), strings.Join(all, ", "))
		logger.Info("Show them using the id:")
		for _, res := range resources {
			logger.Infof("\t`awless show %s` for the %s", res.Id(), res.Type())
		}

		os.Exit(0)
	}

	return nil, nil
}

func resolveResourceFromRef(ref string) []*graph.Resource {
	g, err := sync.LoadAllGraphs()
	exitOn(err)

	name := deprefix(ref)
	byName := &graph.ByProperty{"Name", name}

	if strings.HasPrefix(ref, "@") {
		logger.Verbosef("prefixed with @: forcing research by name '%s'", name)
		rs, err := g.ResolveResources(byName)
		exitOn(err)
		return rs
	} else {

		rs, err := g.ResolveResources(&graph.ById{name})
		exitOn(err)

		if len(rs) > 0 {
			return rs
		} else {
			rs, err := g.ResolveResources(
				byName,
				&graph.ByProperty{"Arn", name},
			)
			exitOn(err)

			return rs
		}
	}
}

func deprefix(s string) string {
	return strings.TrimPrefix(s, "@")
}

func printResourceList(title string, list []*graph.Resource, shortenListMsg ...string) {
	all := graph.Resources(list).Map(func(r *graph.Resource) string { return r.String() })
	if len(all) > 0 {
		if !listAllSiblingsFlag && len(shortenListMsg) > 0 {
			max := 5
			if max > len(all) {
				max = len(all)
			}
			fmt.Printf("\n%s: %s, ... (%s)\n", title, strings.Join(all[0:max], ", "), shortenListMsg[0])
		} else {
			fmt.Printf("\n%s: %s\n", title, strings.Join(all, ", "))
		}
	}
}
<commit_msg>show: hide 'display all with --siblings' when number of siblings < max<commit_after>/*
Copyright 2017 WALLIX

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 commands

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/wallix/awless/aws"
	"github.com/wallix/awless/cloud"
	"github.com/wallix/awless/config"
	"github.com/wallix/awless/console"
	"github.com/wallix/awless/graph"
	"github.com/wallix/awless/logger"
	"github.com/wallix/awless/sync"
)

var (
	listAllSiblingsFlag bool
)

func init() {
	RootCmd.AddCommand(showCmd)
	showCmd.Flags().BoolVar(&listAllSiblingsFlag, "siblings", false, "List all the resource's siblings")
}

var showCmd = &cobra.Command{
	Use:   "show REFERENCE",
	Short: "Show a resource and its interrelations given a REFERENCE: id or name",
	Example: `  awless show i-8d43b21b            # show an instance via its id
  awless show AIDAJ3Z24GOKHTZO4OIX6 # show a user via its id
  awless show jsmith                # show a user via its name`,
	PersistentPreRun:  applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook),
	PersistentPostRun: applyHooks(saveHistoryHook, verifyNewVersionHook),

	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) < 1 {
			return errors.New("REFERENCE required. See examples.")
		}

		ref := args[0]
		notFound := fmt.Sprintf("resource with reference %s not found", deprefix(ref))

		var resource *graph.Resource
		var gph *graph.Graph

		resource, gph = findResourceInLocalGraphs(ref)

		if resource == nil && localGlobalFlag {
			logger.Info(notFound)
			return nil
		} else if resource == nil {
			runFullSync()

			if resource, gph = findResourceInLocalGraphs(ref); resource == nil {
				logger.Info(notFound)
				return nil
			}
		}

		if !localGlobalFlag && config.GetAutosync() {
			srv, err := cloud.GetServiceForType(resource.Type())
			exitOn(err)
			logger.Verbosef("syncing service for %s type", resource.Type())
			if _, err = sync.DefaultSyncer.Sync(srv); err != nil {
				logger.Error(err)
			}
		}

		if resource != nil {
			showResource(resource, gph)
		}

		return nil
	},
}

func showResource(resource *graph.Resource, gph *graph.Graph) {
	displayer := console.BuildOptions(
		console.WithHeaders(console.DefaultsColumnDefinitions[resource.Type()]),
		console.WithFormat(listingFormat),
	).SetSource(resource).Build()

	exitOn(displayer.Print(os.Stderr))

	var parents []*graph.Resource
	err := gph.Accept(&graph.ParentsVisitor{From: resource, Each: graph.VisitorCollectFunc(&parents)})
	exitOn(err)

	fmt.Println(renderCyanBoldFn("\nRelations:"))

	var count int
	for i := len(parents) - 1; i >= 0; i-- {
		if count == 0 {
			fmt.Printf("%s\n", parents[i])
		} else {
			fmt.Printf("%s↳ %s\n", strings.Repeat("\t", count), parents[i])
		}
		count++
	}

	printWithTabs := func(r *graph.Resource, distance int) error {
		var tabs bytes.Buffer
		tabs.WriteString(strings.Repeat("\t", count))
		for i := 0; i < distance; i++ {
			tabs.WriteByte('\t')
		}

		display := r.String()
		if r.Same(resource) {
			display = renderGreenFn(resource.String())
		}
		fmt.Printf("%s↳ %s\n", tabs.String(), display)

		return nil
	}

	err = gph.Accept(&graph.ChildrenVisitor{From: resource, Each: printWithTabs, IncludeFrom: true})
	exitOn(err)

	var siblings []*graph.Resource
	err = gph.Accept(&graph.SiblingsVisitor{From: resource, Each: graph.VisitorCollectFunc(&siblings)})
	exitOn(err)
	printResourceList(renderCyanBoldFn("Siblings"), siblings, "display all with flag --siblings")

	appliedOn, err := gph.ListResourcesAppliedOn(resource)
	exitOn(err)
	printResourceList(renderCyanBoldFn("Applied on"), appliedOn)

	dependingOn, err := gph.ListResourcesDependingOn(resource)
	exitOn(err)
	printResourceList(renderCyanBoldFn("Depending on"), dependingOn)
}

func runFullSync() {
	if !config.GetAutosync() {
		logger.Info("autosync disabled")
		return
	}

	logger.Info("cannot resolve resource - running full sync")

	var services []cloud.Service
	for _, srv := range cloud.ServiceRegistry {
		services = append(services, srv)
	}

	if _, err := sync.DefaultSyncer.Sync(services...); err != nil {
		logger.Verbose(err)
	}
}

func findResourceInLocalGraphs(ref string) (*graph.Resource, *graph.Graph) {
	resources := resolveResourceFromRef(ref)
	switch len(resources) {
	case 0:
		return nil, nil
	case 1:
		res := resources[0]
		return res, sync.LoadCurrentLocalGraph(aws.ServicePerResourceType[res.Type()])
	default:
		var all []string
		for _, res := range resources {
			all = append(all, fmt.Sprintf("%s[%s]", res.Id(), res.Type()))
		}
		logger.Infof("%d resources found with name '%s': %s", len(resources), deprefix(ref), strings.Join(all, ", "))
		logger.Info("Show them using the id:")
		for _, res := range resources {
			logger.Infof("\t`awless show %s` for the %s", res.Id(), res.Type())
		}

		os.Exit(0)
	}

	return nil, nil
}

func resolveResourceFromRef(ref string) []*graph.Resource {
	g, err := sync.LoadAllGraphs()
	exitOn(err)

	name := deprefix(ref)
	byName := &graph.ByProperty{"Name", name}

	if strings.HasPrefix(ref, "@") {
		logger.Verbosef("prefixed with @: forcing research by name '%s'", name)
		rs, err := g.ResolveResources(byName)
		exitOn(err)
		return rs
	} else {

		rs, err := g.ResolveResources(&graph.ById{name})
		exitOn(err)

		if len(rs) > 0 {
			return rs
		} else {
			rs, err := g.ResolveResources(
				byName,
				&graph.ByProperty{"Arn", name},
			)
			exitOn(err)

			return rs
		}
	}
}

func deprefix(s string) string {
	return strings.TrimPrefix(s, "@")
}

func printResourceList(title string, list []*graph.Resource, shortenListMsg ...string) {
	all := graph.Resources(list).Map(func(r *graph.Resource) string { return r.String() })
	count := len(all)
	max := 3
	if count > 0 {
		if !listAllSiblingsFlag && len(shortenListMsg) > 0 && count > max {
			fmt.Printf("\n%s: %s, ... (%s)\n", title, strings.Join(all[0:max], ", "), shortenListMsg[0])
		} else {
			fmt.Printf("\n%s: %s\n", title, strings.Join(all, ", "))
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The rkt 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 common defines values shared by different parts
// of rkt (e.g. stage0 and stage1)
package common

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/appc/spec/aci"
	"github.com/appc/spec/schema/types"
	"github.com/hashicorp/errwrap"
)

const (
	sharedVolumesDir = "/sharedVolumes"
	stage1Dir        = "/stage1"
	stage2Dir        = "/opt/stage2"
	AppsInfoDir      = "/appsinfo"

	EnvLockFd                    = "RKT_LOCK_FD"
	EnvSELinuxContext            = "RKT_SELINUX_CONTEXT"
	EnvSELinuxMountContext       = "RKT_SELINUX_MOUNT_CONTEXT"
	Stage1TreeStoreIDFilename    = "stage1TreeStoreID"
	AppTreeStoreIDFilename       = "treeStoreID"
	OverlayPreparedFilename      = "overlay-prepared"
	PrivateUsersPreparedFilename = "private-users-prepared"

	PrepareLock = "prepareLock"

	MetadataServicePort    = 18112
	MetadataServiceRegSock = "/run/rkt/metadata-svc.sock"

	APIServiceListenAddr = "localhost:15441"

	DefaultLocalConfigDir  = "/etc/rkt"
	DefaultSystemConfigDir = "/usr/lib/rkt"

	// Default perm bits for the regular files
	// within the stage1 directory. (e.g. image manifest,
	// pod manifest, stage1ID, etc).
	DefaultRegularFilePerm = os.FileMode(0640)

	// Default perm bits for the regular directories
	// within the stage1 directory.
	DefaultRegularDirPerm = os.FileMode(0750)
)

const (
	FsMagicAUFS = 0x61756673 // https://goo.gl/CBwx43
	FsMagicZFS  = 0x2FC12FC1 // https://goo.gl/xTvzO5
)

// Stage1ImagePath returns the path where the stage1 app image (unpacked ACI) is rooted,
// (i.e. where its contents are extracted during stage0).
func Stage1ImagePath(root string) string {
	return filepath.Join(root, stage1Dir)
}

// Stage1RootfsPath returns the path to the stage1 rootfs
func Stage1RootfsPath(root string) string {
	return filepath.Join(Stage1ImagePath(root), aci.RootfsDir)
}

// Stage1ManifestPath returns the path to the stage1's manifest file inside the expanded ACI.
func Stage1ManifestPath(root string) string {
	return filepath.Join(Stage1ImagePath(root), aci.ManifestFile)
}

// PodManifestPath returns the path in root to the Pod Manifest
func PodManifestPath(root string) string {
	return filepath.Join(root, "pod")
}

// AppsPath returns the path where the apps within a pod live.
func AppsPath(root string) string {
	return filepath.Join(Stage1RootfsPath(root), stage2Dir)
}

// AppPath returns the path to an app's rootfs.
func AppPath(root string, appName types.ACName) string {
	return filepath.Join(AppsPath(root), appName.String())
}

// AppRootfsPath returns the path to an app's rootfs.
func AppRootfsPath(root string, appName types.ACName) string {
	return filepath.Join(AppPath(root, appName), aci.RootfsDir)
}

// RelAppPath returns the path of an app relative to the stage1 chroot.
func RelAppPath(appName types.ACName) string {
	return filepath.Join(stage2Dir, appName.String())
}

// RelAppRootfsPath returns the path of an app's rootfs relative to the stage1 chroot.
func RelAppRootfsPath(appName types.ACName) string {
	return filepath.Join(RelAppPath(appName), aci.RootfsDir)
}

// ImageManifestPath returns the path to the app's manifest file of a pod.
func ImageManifestPath(root string, appName types.ACName) string {
	return filepath.Join(AppPath(root, appName), aci.ManifestFile)
}

// AppsInfoPath returns the path to the appsinfo directory of a pod.
func AppsInfoPath(root string) string {
	return filepath.Join(root, AppsInfoDir)
}

// AppInfoPath returns the path to the app's appsinfo directory of a pod.
func AppInfoPath(root string, appName types.ACName) string {
	return filepath.Join(AppsInfoPath(root), appName.String())
}

// AppTreeStoreIDPath returns the path to the app's treeStoreID file of a pod.
func AppTreeStoreIDPath(root string, appName types.ACName) string {
	return filepath.Join(AppInfoPath(root, appName), AppTreeStoreIDFilename)
}

// AppImageManifestPath returns the path to the app's ImageManifest file
func AppImageManifestPath(root string, appName types.ACName) string {
	return filepath.Join(AppInfoPath(root, appName), aci.ManifestFile)
}

// SharedVolumesPath returns the path to the shared (empty) volumes of a pod.
func SharedVolumesPath(root string) string {
	return filepath.Join(root, sharedVolumesDir)
}

// MetadataServicePublicURL returns the public URL used to host the metadata service
func MetadataServicePublicURL(ip net.IP, token string) string {
	return fmt.Sprintf("http://%v:%v/%v", ip, MetadataServicePort, token)
}

func GetRktLockFD() (int, error) {
	if v := os.Getenv(EnvLockFd); v != "" {
		fd, err := strconv.ParseUint(v, 10, 32)
		if err != nil {
			return -1, err
		}
		return int(fd), nil
	}
	return -1, fmt.Errorf("%v env var is not set", EnvLockFd)
}

// SupportsOverlay returns whether the system supports overlay filesystem
func SupportsOverlay() bool {
	exec.Command("modprobe", "overlay").Run()

	f, err := os.Open("/proc/filesystems")
	if err != nil {
		fmt.Println("error opening /proc/filesystems")
		return false
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		if s.Text() == "nodev\toverlay" {
			return true
		}
	}
	return false
}

// SupportsUserNS returns whether the kernel has CONFIG_USER_NS set
func SupportsUserNS() bool {
	if _, err := os.Stat("/proc/self/uid_map"); err == nil {
		return true
	}

	return false
}

// NetList implements the flag.Value interface to allow specification of --net with and without values
// Example: --net="all,net1:k1=v1;k2=v2,net2:l1=w1"
type NetList struct {
	mapping map[string]string
}

func (l *NetList) String() string {
	return strings.Join(l.Strings(), ",")
}

func (l *NetList) Set(value string) error {
	if l.mapping == nil {
		l.mapping = make(map[string]string)
	}
	for _, s := range strings.Split(value, ",") {
		netArgsPair := strings.Split(s, ":")
		netName := netArgsPair[0]

		if netName == "" {
			return fmt.Errorf("netname must not be empty")
		}

		if _, duplicate := l.mapping[netName]; duplicate {
			return fmt.Errorf("found duplicate netname %q", netName)
		}

		switch {
		case len(netArgsPair) == 1:
			l.mapping[netName] = ""
		case len(netArgsPair) == 2:
			if netName == "all" ||
				netName == "host" {
				return fmt.Errorf("arguments are not supported by special netname %q", netName)
			}
			l.mapping[netName] = netArgsPair[1]
		case len(netArgsPair) > 2:
			return fmt.Errorf("network %q provided with invalid arguments: %v", netName, netArgsPair[1:])
		default:
			return fmt.Errorf("unexpected case when processing network %q", s)
		}
	}
	return nil
}

func (l *NetList) Type() string {
	return "netList"
}

func (l *NetList) Strings() []string {
	if len(l.mapping) == 0 {
		return []string{"default"}
	}

	var list []string
	for k, v := range l.mapping {
		if v == "" {
			list = append(list, k)
		} else {
			list = append(list, fmt.Sprintf("%s:%s", k, v))
		}
	}
	return list
}

func (l *NetList) StringsOnlyNames() (list []string) {
	for k := range l.mapping {
		list = append(list, k)
	}

	return
}

// Check if host networking has been requested
func (l *NetList) Host() bool {
	return l.Specific("host")
}

// Check if 'none' (loopback only) networking has been requested
func (l *NetList) None() bool {
	return l.Specific("none")
}

// Check if the container needs to be put in a separate network namespace
func (l *NetList) Contained() bool {
	return !l.Host() && len(l.mapping) > 0
}

func (l *NetList) Specific(net string) bool {
	_, exists := l.mapping[net]
	return exists
}

func (l *NetList) SpecificArgs(net string) string {
	return l.mapping[net]
}

func (l *NetList) All() bool {
	return l.Specific("all")
}

// LookupPath search for bin in paths. If found, it returns its absolute path,
// if not, an error
func LookupPath(bin string, paths string) (string, error) {
	pathsArr := filepath.SplitList(paths)
	for _, path := range pathsArr {
		binPath := filepath.Join(path, bin)
		binAbsPath, err := filepath.Abs(binPath)
		if err != nil {
			return "", fmt.Errorf("unable to find absolute path for %s", binPath)
		}
		d, err := os.Stat(binAbsPath)
		if err != nil {
			continue
		}
		// Check the executable bit, inspired by os.exec.LookPath()
		if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
			return binAbsPath, nil
		}
	}
	return "", fmt.Errorf("unable to find %q in %q", bin, paths)
}

// SystemdVersion parses and returns the version of a given systemd binary
func SystemdVersion(systemdBinaryPath string) (int, error) {
	versionBytes, err := exec.Command(systemdBinaryPath, "--version").CombinedOutput()
	if err != nil {
		return -1, errwrap.Wrap(fmt.Errorf("unable to probe %s version", systemdBinaryPath), err)
	}
	versionStr := strings.SplitN(string(versionBytes), "\n", 2)[0]
	var version int
	n, err := fmt.Sscanf(versionStr, "systemd %d", &version)
	if err != nil || n != 1 {
		return -1, fmt.Errorf("cannot parse version: %q", versionStr)
	}

	return version, nil
}

// FSSupportsOverlay checks whether the filesystem under which
// a specified path resides is compatible with OverlayFS
func FSSupportsOverlay(path string) bool {
	var data syscall.Statfs_t
	err := syscall.Statfs(path, &data)
	if err != nil {
		return false
	}

	if data.Type == FsMagicAUFS ||
		data.Type == FsMagicZFS {
		return false
	}

	return true
}

// RemoveEmptyLines removes empty lines from the given string
// and breaks it up into a list of strings at newline characters
func RemoveEmptyLines(str string) []string {
	lines := make([]string, 0)

	for _, v := range strings.Split(str, "\n") {
		if len(v) > 0 {
			lines = append(lines, v)
		}
	}

	return lines
}
<commit_msg>common: use fileutil.IsExecutable()<commit_after>// Copyright 2014 The rkt 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 common defines values shared by different parts
// of rkt (e.g. stage0 and stage1)
package common

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/appc/spec/aci"
	"github.com/appc/spec/schema/types"
	"github.com/hashicorp/errwrap"

	"github.com/coreos/rkt/pkg/fileutil"
)

const (
	sharedVolumesDir = "/sharedVolumes"
	stage1Dir        = "/stage1"
	stage2Dir        = "/opt/stage2"
	AppsInfoDir      = "/appsinfo"

	EnvLockFd                    = "RKT_LOCK_FD"
	EnvSELinuxContext            = "RKT_SELINUX_CONTEXT"
	EnvSELinuxMountContext       = "RKT_SELINUX_MOUNT_CONTEXT"
	Stage1TreeStoreIDFilename    = "stage1TreeStoreID"
	AppTreeStoreIDFilename       = "treeStoreID"
	OverlayPreparedFilename      = "overlay-prepared"
	PrivateUsersPreparedFilename = "private-users-prepared"

	PrepareLock = "prepareLock"

	MetadataServicePort    = 18112
	MetadataServiceRegSock = "/run/rkt/metadata-svc.sock"

	APIServiceListenAddr = "localhost:15441"

	DefaultLocalConfigDir  = "/etc/rkt"
	DefaultSystemConfigDir = "/usr/lib/rkt"

	// Default perm bits for the regular files
	// within the stage1 directory. (e.g. image manifest,
	// pod manifest, stage1ID, etc).
	DefaultRegularFilePerm = os.FileMode(0640)

	// Default perm bits for the regular directories
	// within the stage1 directory.
	DefaultRegularDirPerm = os.FileMode(0750)
)

const (
	FsMagicAUFS = 0x61756673 // https://goo.gl/CBwx43
	FsMagicZFS  = 0x2FC12FC1 // https://goo.gl/xTvzO5
)

// Stage1ImagePath returns the path where the stage1 app image (unpacked ACI) is rooted,
// (i.e. where its contents are extracted during stage0).
func Stage1ImagePath(root string) string {
	return filepath.Join(root, stage1Dir)
}

// Stage1RootfsPath returns the path to the stage1 rootfs
func Stage1RootfsPath(root string) string {
	return filepath.Join(Stage1ImagePath(root), aci.RootfsDir)
}

// Stage1ManifestPath returns the path to the stage1's manifest file inside the expanded ACI.
func Stage1ManifestPath(root string) string {
	return filepath.Join(Stage1ImagePath(root), aci.ManifestFile)
}

// PodManifestPath returns the path in root to the Pod Manifest
func PodManifestPath(root string) string {
	return filepath.Join(root, "pod")
}

// AppsPath returns the path where the apps within a pod live.
func AppsPath(root string) string {
	return filepath.Join(Stage1RootfsPath(root), stage2Dir)
}

// AppPath returns the path to an app's rootfs.
func AppPath(root string, appName types.ACName) string {
	return filepath.Join(AppsPath(root), appName.String())
}

// AppRootfsPath returns the path to an app's rootfs.
func AppRootfsPath(root string, appName types.ACName) string {
	return filepath.Join(AppPath(root, appName), aci.RootfsDir)
}

// RelAppPath returns the path of an app relative to the stage1 chroot.
func RelAppPath(appName types.ACName) string {
	return filepath.Join(stage2Dir, appName.String())
}

// RelAppRootfsPath returns the path of an app's rootfs relative to the stage1 chroot.
func RelAppRootfsPath(appName types.ACName) string {
	return filepath.Join(RelAppPath(appName), aci.RootfsDir)
}

// ImageManifestPath returns the path to the app's manifest file of a pod.
func ImageManifestPath(root string, appName types.ACName) string {
	return filepath.Join(AppPath(root, appName), aci.ManifestFile)
}

// AppsInfoPath returns the path to the appsinfo directory of a pod.
func AppsInfoPath(root string) string {
	return filepath.Join(root, AppsInfoDir)
}

// AppInfoPath returns the path to the app's appsinfo directory of a pod.
func AppInfoPath(root string, appName types.ACName) string {
	return filepath.Join(AppsInfoPath(root), appName.String())
}

// AppTreeStoreIDPath returns the path to the app's treeStoreID file of a pod.
func AppTreeStoreIDPath(root string, appName types.ACName) string {
	return filepath.Join(AppInfoPath(root, appName), AppTreeStoreIDFilename)
}

// AppImageManifestPath returns the path to the app's ImageManifest file
func AppImageManifestPath(root string, appName types.ACName) string {
	return filepath.Join(AppInfoPath(root, appName), aci.ManifestFile)
}

// SharedVolumesPath returns the path to the shared (empty) volumes of a pod.
func SharedVolumesPath(root string) string {
	return filepath.Join(root, sharedVolumesDir)
}

// MetadataServicePublicURL returns the public URL used to host the metadata service
func MetadataServicePublicURL(ip net.IP, token string) string {
	return fmt.Sprintf("http://%v:%v/%v", ip, MetadataServicePort, token)
}

func GetRktLockFD() (int, error) {
	if v := os.Getenv(EnvLockFd); v != "" {
		fd, err := strconv.ParseUint(v, 10, 32)
		if err != nil {
			return -1, err
		}
		return int(fd), nil
	}
	return -1, fmt.Errorf("%v env var is not set", EnvLockFd)
}

// SupportsOverlay returns whether the system supports overlay filesystem
func SupportsOverlay() bool {
	exec.Command("modprobe", "overlay").Run()

	f, err := os.Open("/proc/filesystems")
	if err != nil {
		fmt.Println("error opening /proc/filesystems")
		return false
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		if s.Text() == "nodev\toverlay" {
			return true
		}
	}
	return false
}

// SupportsUserNS returns whether the kernel has CONFIG_USER_NS set
func SupportsUserNS() bool {
	if _, err := os.Stat("/proc/self/uid_map"); err == nil {
		return true
	}

	return false
}

// NetList implements the flag.Value interface to allow specification of --net with and without values
// Example: --net="all,net1:k1=v1;k2=v2,net2:l1=w1"
type NetList struct {
	mapping map[string]string
}

func (l *NetList) String() string {
	return strings.Join(l.Strings(), ",")
}

func (l *NetList) Set(value string) error {
	if l.mapping == nil {
		l.mapping = make(map[string]string)
	}
	for _, s := range strings.Split(value, ",") {
		netArgsPair := strings.Split(s, ":")
		netName := netArgsPair[0]

		if netName == "" {
			return fmt.Errorf("netname must not be empty")
		}

		if _, duplicate := l.mapping[netName]; duplicate {
			return fmt.Errorf("found duplicate netname %q", netName)
		}

		switch {
		case len(netArgsPair) == 1:
			l.mapping[netName] = ""
		case len(netArgsPair) == 2:
			if netName == "all" ||
				netName == "host" {
				return fmt.Errorf("arguments are not supported by special netname %q", netName)
			}
			l.mapping[netName] = netArgsPair[1]
		case len(netArgsPair) > 2:
			return fmt.Errorf("network %q provided with invalid arguments: %v", netName, netArgsPair[1:])
		default:
			return fmt.Errorf("unexpected case when processing network %q", s)
		}
	}
	return nil
}

func (l *NetList) Type() string {
	return "netList"
}

func (l *NetList) Strings() []string {
	if len(l.mapping) == 0 {
		return []string{"default"}
	}

	var list []string
	for k, v := range l.mapping {
		if v == "" {
			list = append(list, k)
		} else {
			list = append(list, fmt.Sprintf("%s:%s", k, v))
		}
	}
	return list
}

func (l *NetList) StringsOnlyNames() (list []string) {
	for k := range l.mapping {
		list = append(list, k)
	}

	return
}

// Check if host networking has been requested
func (l *NetList) Host() bool {
	return l.Specific("host")
}

// Check if 'none' (loopback only) networking has been requested
func (l *NetList) None() bool {
	return l.Specific("none")
}

// Check if the container needs to be put in a separate network namespace
func (l *NetList) Contained() bool {
	return !l.Host() && len(l.mapping) > 0
}

func (l *NetList) Specific(net string) bool {
	_, exists := l.mapping[net]
	return exists
}

func (l *NetList) SpecificArgs(net string) string {
	return l.mapping[net]
}

func (l *NetList) All() bool {
	return l.Specific("all")
}

// LookupPath search for bin in paths. If found, it returns its absolute path,
// if not, an error
func LookupPath(bin string, paths string) (string, error) {
	pathsArr := filepath.SplitList(paths)
	for _, path := range pathsArr {
		binPath := filepath.Join(path, bin)
		binAbsPath, err := filepath.Abs(binPath)
		if err != nil {
			return "", fmt.Errorf("unable to find absolute path for %s", binPath)
		}
		if fileutil.IsExecutable(binAbsPath) {
			return binAbsPath, nil
		}
	}
	return "", fmt.Errorf("unable to find %q in %q", bin, paths)
}

// SystemdVersion parses and returns the version of a given systemd binary
func SystemdVersion(systemdBinaryPath string) (int, error) {
	versionBytes, err := exec.Command(systemdBinaryPath, "--version").CombinedOutput()
	if err != nil {
		return -1, errwrap.Wrap(fmt.Errorf("unable to probe %s version", systemdBinaryPath), err)
	}
	versionStr := strings.SplitN(string(versionBytes), "\n", 2)[0]
	var version int
	n, err := fmt.Sscanf(versionStr, "systemd %d", &version)
	if err != nil || n != 1 {
		return -1, fmt.Errorf("cannot parse version: %q", versionStr)
	}

	return version, nil
}

// FSSupportsOverlay checks whether the filesystem under which
// a specified path resides is compatible with OverlayFS
func FSSupportsOverlay(path string) bool {
	var data syscall.Statfs_t
	err := syscall.Statfs(path, &data)
	if err != nil {
		return false
	}

	if data.Type == FsMagicAUFS ||
		data.Type == FsMagicZFS {
		return false
	}

	return true
}

// RemoveEmptyLines removes empty lines from the given string
// and breaks it up into a list of strings at newline characters
func RemoveEmptyLines(str string) []string {
	lines := make([]string, 0)

	for _, v := range strings.Split(str, "\n") {
		if len(v) > 0 {
			lines = append(lines, v)
		}
	}

	return lines
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"strconv"
	"text/tabwriter"
	"time"

	"google.golang.org/grpc"

	"github.com/apache/thrift/lib/go/thrift"
	lightstep "github.com/lightstep/lightstep-tracer-go"
	stdopentracing "github.com/opentracing/opentracing-go"
	zipkin "github.com/openzipkin/zipkin-go"
	zipkinot "github.com/openzipkin/zipkin-go-opentracing"
	zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
	"sourcegraph.com/sourcegraph/appdash"
	appdashot "sourcegraph.com/sourcegraph/appdash/opentracing"

	"github.com/go-kit/kit/log"

	"github.com/go-kit/kit/examples/addsvc/pkg/addservice"
	"github.com/go-kit/kit/examples/addsvc/pkg/addtransport"
	addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
)

func main() {
	// The addcli presumes no service discovery system, and expects users to
	// provide the direct address of an addsvc. This presumption is reflected in
	// the addcli binary and the client packages: the -transport.addr flags
	// and various client constructors both expect host:port strings. For an
	// example service with a client built on top of a service discovery system,
	// see profilesvc.
	fs := flag.NewFlagSet("addcli", flag.ExitOnError)
	var (
		httpAddr       = fs.String("http-addr", "", "HTTP address of addsvc")
		grpcAddr       = fs.String("grpc-addr", "", "gRPC address of addsvc")
		thriftAddr     = fs.String("thrift-addr", "", "Thrift address of addsvc")
		jsonRPCAddr    = fs.String("jsonrpc-addr", "", "JSON RPC address of addsvc")
		thriftProtocol = fs.String("thrift-protocol", "binary", "binary, compact, json, simplejson")
		thriftBuffer   = fs.Int("thrift-buffer", 0, "0 for unbuffered")
		thriftFramed   = fs.Bool("thrift-framed", false, "true to enable framing")
		zipkinV2URL    = fs.String("zipkin-url", "", "Enable Zipkin v2 tracing (zipkin-go) via HTTP Reporter URL e.g. http://localhost:94111/api/v2/spans")
		zipkinV1URL    = fs.String("zipkin-v1-url", "", "Enable Zipkin v1 tracing (zipkin-go-opentracing) via a collector URL e.g. http://localhost:9411/api/v1/spans")
		lightstepToken = fs.String("lightstep-token", "", "Enable LightStep tracing via a LightStep access token")
		appdashAddr    = fs.String("appdash-addr", "", "Enable Appdash tracing via an Appdash server host:port")
		method         = fs.String("method", "sum", "sum, concat")
	)
	fs.Usage = usageFor(fs, os.Args[0]+" [flags] <a> <b>")
	fs.Parse(os.Args[1:])
	if len(fs.Args()) != 2 {
		fs.Usage()
		os.Exit(1)
	}

	// This is a demonstration client, which supports multiple tracers.
	// Your clients will probably just use one tracer.
	var otTracer stdopentracing.Tracer
	{
		if *zipkinV1URL != "" && *zipkinV2URL == "" {
			collector, err := zipkinot.NewHTTPCollector(*zipkinV1URL)
			if err != nil {
				fmt.Fprintln(os.Stderr, err.Error())
				os.Exit(1)
			}
			defer collector.Close()
			var (
				debug       = false
				hostPort    = "localhost:0"
				serviceName = "addsvc-cli"
			)
			recorder := zipkinot.NewRecorder(collector, debug, hostPort, serviceName)
			otTracer, err = zipkinot.NewTracer(recorder)
			if err != nil {
				fmt.Fprintln(os.Stderr, err.Error())
				os.Exit(1)
			}
		} else if *lightstepToken != "" {
			otTracer = lightstep.NewTracer(lightstep.Options{
				AccessToken: *lightstepToken,
			})
			defer lightstep.FlushLightStepTracer(otTracer)
		} else if *appdashAddr != "" {
			otTracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr))
		} else {
			otTracer = stdopentracing.GlobalTracer() // no-op
		}
	}

	// This is a demonstration of the native Zipkin tracing client. If using
	// Zipkin this is the more idiomatic client over OpenTracing.
	var zipkinTracer *zipkin.Tracer
	{
		var (
			err           error
			hostPort      = "" // if host:port is unknown we can keep this empty
			serviceName   = "addsvc-cli"
			useNoopTracer = (*zipkinV2URL == "")
			reporter      = zipkinhttp.NewReporter(*zipkinV2URL)
		)
		defer reporter.Close()
		zEP, _ := zipkin.NewEndpoint(serviceName, hostPort)
		zipkinTracer, err = zipkin.NewTracer(
			reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer),
		)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unable to create zipkin tracer: %s\n", err.Error())
			os.Exit(1)
		}
	}

	// This is a demonstration client, which supports multiple transports.
	// Your clients will probably just define and stick with 1 transport.
	var (
		svc addservice.Service
		err error
	)
	if *httpAddr != "" {
		svc, err = addtransport.NewHTTPClient(*httpAddr, otTracer, zipkinTracer, log.NewNopLogger())
	} else if *grpcAddr != "" {
		conn, err := grpc.Dial(*grpcAddr, grpc.WithInsecure(), grpc.WithTimeout(time.Second))
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v", err)
			os.Exit(1)
		}
		defer conn.Close()
		svc = addtransport.NewGRPCClient(conn, otTracer, zipkinTracer, log.NewNopLogger())
	} else if *jsonRPCAddr != "" {
		svc, err = addtransport.NewJSONRPCClient(*jsonRPCAddr, otTracer, log.NewNopLogger())
	} else if *thriftAddr != "" {
		// It's necessary to do all of this construction in the func main,
		// because (among other reasons) we need to control the lifecycle of the
		// Thrift transport, i.e. close it eventually.
		var protocolFactory thrift.TProtocolFactory
		switch *thriftProtocol {
		case "compact":
			protocolFactory = thrift.NewTCompactProtocolFactory()
		case "simplejson":
			protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
		case "json":
			protocolFactory = thrift.NewTJSONProtocolFactory()
		case "binary", "":
			protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
		default:
			fmt.Fprintf(os.Stderr, "error: invalid protocol %q\n", *thriftProtocol)
			os.Exit(1)
		}
		var transportFactory thrift.TTransportFactory
		if *thriftBuffer > 0 {
			transportFactory = thrift.NewTBufferedTransportFactory(*thriftBuffer)
		} else {
			transportFactory = thrift.NewTTransportFactory()
		}
		if *thriftFramed {
			transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
		}
		transportSocket, err := thrift.NewTSocket(*thriftAddr)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		transport, err := transportFactory.GetTransport(transportSocket)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		if err := transport.Open(); err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		defer transport.Close()
		client := addthrift.NewAddServiceClientFactory(transport, protocolFactory)
		svc = addtransport.NewThriftClient(client)
	} else {
		fmt.Fprintf(os.Stderr, "error: no remote address specified\n")
		os.Exit(1)
	}
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	switch *method {
	case "sum":
		a, _ := strconv.ParseInt(fs.Args()[0], 10, 64)
		b, _ := strconv.ParseInt(fs.Args()[1], 10, 64)
		v, err := svc.Sum(context.Background(), int(a), int(b))
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		fmt.Fprintf(os.Stdout, "%d + %d = %d\n", a, b, v)

	case "concat":
		a := fs.Args()[0]
		b := fs.Args()[1]
		v, err := svc.Concat(context.Background(), a, b)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		fmt.Fprintf(os.Stdout, "%q + %q = %q\n", a, b, v)

	default:
		fmt.Fprintf(os.Stderr, "error: invalid method %q\n", *method)
		os.Exit(1)
	}
}

func usageFor(fs *flag.FlagSet, short string) func() {
	return func() {
		fmt.Fprintf(os.Stderr, "USAGE\n")
		fmt.Fprintf(os.Stderr, "  %s\n", short)
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "FLAGS\n")
		w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0)
		fs.VisitAll(func(f *flag.Flag) {
			fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage)
		})
		w.Flush()
		fmt.Fprintf(os.Stderr, "\n")
	}
}
<commit_msg>typo in port number<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"strconv"
	"text/tabwriter"
	"time"

	"google.golang.org/grpc"

	"github.com/apache/thrift/lib/go/thrift"
	lightstep "github.com/lightstep/lightstep-tracer-go"
	stdopentracing "github.com/opentracing/opentracing-go"
	zipkin "github.com/openzipkin/zipkin-go"
	zipkinot "github.com/openzipkin/zipkin-go-opentracing"
	zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
	"sourcegraph.com/sourcegraph/appdash"
	appdashot "sourcegraph.com/sourcegraph/appdash/opentracing"

	"github.com/go-kit/kit/log"

	"github.com/go-kit/kit/examples/addsvc/pkg/addservice"
	"github.com/go-kit/kit/examples/addsvc/pkg/addtransport"
	addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc"
)

func main() {
	// The addcli presumes no service discovery system, and expects users to
	// provide the direct address of an addsvc. This presumption is reflected in
	// the addcli binary and the client packages: the -transport.addr flags
	// and various client constructors both expect host:port strings. For an
	// example service with a client built on top of a service discovery system,
	// see profilesvc.
	fs := flag.NewFlagSet("addcli", flag.ExitOnError)
	var (
		httpAddr       = fs.String("http-addr", "", "HTTP address of addsvc")
		grpcAddr       = fs.String("grpc-addr", "", "gRPC address of addsvc")
		thriftAddr     = fs.String("thrift-addr", "", "Thrift address of addsvc")
		jsonRPCAddr    = fs.String("jsonrpc-addr", "", "JSON RPC address of addsvc")
		thriftProtocol = fs.String("thrift-protocol", "binary", "binary, compact, json, simplejson")
		thriftBuffer   = fs.Int("thrift-buffer", 0, "0 for unbuffered")
		thriftFramed   = fs.Bool("thrift-framed", false, "true to enable framing")
		zipkinV2URL    = fs.String("zipkin-url", "", "Enable Zipkin v2 tracing (zipkin-go) via HTTP Reporter URL e.g. http://localhost:9411/api/v2/spans")
		zipkinV1URL    = fs.String("zipkin-v1-url", "", "Enable Zipkin v1 tracing (zipkin-go-opentracing) via a collector URL e.g. http://localhost:9411/api/v1/spans")
		lightstepToken = fs.String("lightstep-token", "", "Enable LightStep tracing via a LightStep access token")
		appdashAddr    = fs.String("appdash-addr", "", "Enable Appdash tracing via an Appdash server host:port")
		method         = fs.String("method", "sum", "sum, concat")
	)
	fs.Usage = usageFor(fs, os.Args[0]+" [flags] <a> <b>")
	fs.Parse(os.Args[1:])
	if len(fs.Args()) != 2 {
		fs.Usage()
		os.Exit(1)
	}

	// This is a demonstration client, which supports multiple tracers.
	// Your clients will probably just use one tracer.
	var otTracer stdopentracing.Tracer
	{
		if *zipkinV1URL != "" && *zipkinV2URL == "" {
			collector, err := zipkinot.NewHTTPCollector(*zipkinV1URL)
			if err != nil {
				fmt.Fprintln(os.Stderr, err.Error())
				os.Exit(1)
			}
			defer collector.Close()
			var (
				debug       = false
				hostPort    = "localhost:0"
				serviceName = "addsvc-cli"
			)
			recorder := zipkinot.NewRecorder(collector, debug, hostPort, serviceName)
			otTracer, err = zipkinot.NewTracer(recorder)
			if err != nil {
				fmt.Fprintln(os.Stderr, err.Error())
				os.Exit(1)
			}
		} else if *lightstepToken != "" {
			otTracer = lightstep.NewTracer(lightstep.Options{
				AccessToken: *lightstepToken,
			})
			defer lightstep.FlushLightStepTracer(otTracer)
		} else if *appdashAddr != "" {
			otTracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr))
		} else {
			otTracer = stdopentracing.GlobalTracer() // no-op
		}
	}

	// This is a demonstration of the native Zipkin tracing client. If using
	// Zipkin this is the more idiomatic client over OpenTracing.
	var zipkinTracer *zipkin.Tracer
	{
		var (
			err           error
			hostPort      = "" // if host:port is unknown we can keep this empty
			serviceName   = "addsvc-cli"
			useNoopTracer = (*zipkinV2URL == "")
			reporter      = zipkinhttp.NewReporter(*zipkinV2URL)
		)
		defer reporter.Close()
		zEP, _ := zipkin.NewEndpoint(serviceName, hostPort)
		zipkinTracer, err = zipkin.NewTracer(
			reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer),
		)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unable to create zipkin tracer: %s\n", err.Error())
			os.Exit(1)
		}
	}

	// This is a demonstration client, which supports multiple transports.
	// Your clients will probably just define and stick with 1 transport.
	var (
		svc addservice.Service
		err error
	)
	if *httpAddr != "" {
		svc, err = addtransport.NewHTTPClient(*httpAddr, otTracer, zipkinTracer, log.NewNopLogger())
	} else if *grpcAddr != "" {
		conn, err := grpc.Dial(*grpcAddr, grpc.WithInsecure(), grpc.WithTimeout(time.Second))
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v", err)
			os.Exit(1)
		}
		defer conn.Close()
		svc = addtransport.NewGRPCClient(conn, otTracer, zipkinTracer, log.NewNopLogger())
	} else if *jsonRPCAddr != "" {
		svc, err = addtransport.NewJSONRPCClient(*jsonRPCAddr, otTracer, log.NewNopLogger())
	} else if *thriftAddr != "" {
		// It's necessary to do all of this construction in the func main,
		// because (among other reasons) we need to control the lifecycle of the
		// Thrift transport, i.e. close it eventually.
		var protocolFactory thrift.TProtocolFactory
		switch *thriftProtocol {
		case "compact":
			protocolFactory = thrift.NewTCompactProtocolFactory()
		case "simplejson":
			protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
		case "json":
			protocolFactory = thrift.NewTJSONProtocolFactory()
		case "binary", "":
			protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
		default:
			fmt.Fprintf(os.Stderr, "error: invalid protocol %q\n", *thriftProtocol)
			os.Exit(1)
		}
		var transportFactory thrift.TTransportFactory
		if *thriftBuffer > 0 {
			transportFactory = thrift.NewTBufferedTransportFactory(*thriftBuffer)
		} else {
			transportFactory = thrift.NewTTransportFactory()
		}
		if *thriftFramed {
			transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
		}
		transportSocket, err := thrift.NewTSocket(*thriftAddr)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		transport, err := transportFactory.GetTransport(transportSocket)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		if err := transport.Open(); err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		defer transport.Close()
		client := addthrift.NewAddServiceClientFactory(transport, protocolFactory)
		svc = addtransport.NewThriftClient(client)
	} else {
		fmt.Fprintf(os.Stderr, "error: no remote address specified\n")
		os.Exit(1)
	}
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	switch *method {
	case "sum":
		a, _ := strconv.ParseInt(fs.Args()[0], 10, 64)
		b, _ := strconv.ParseInt(fs.Args()[1], 10, 64)
		v, err := svc.Sum(context.Background(), int(a), int(b))
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		fmt.Fprintf(os.Stdout, "%d + %d = %d\n", a, b, v)

	case "concat":
		a := fs.Args()[0]
		b := fs.Args()[1]
		v, err := svc.Concat(context.Background(), a, b)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
		fmt.Fprintf(os.Stdout, "%q + %q = %q\n", a, b, v)

	default:
		fmt.Fprintf(os.Stderr, "error: invalid method %q\n", *method)
		os.Exit(1)
	}
}

func usageFor(fs *flag.FlagSet, short string) func() {
	return func() {
		fmt.Fprintf(os.Stderr, "USAGE\n")
		fmt.Fprintf(os.Stderr, "  %s\n", short)
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "FLAGS\n")
		w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0)
		fs.VisitAll(func(f *flag.Flag) {
			fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage)
		})
		w.Flush()
		fmt.Fprintf(os.Stderr, "\n")
	}
}
<|endoftext|>