<commit_before>package dynamodb

import (
	"fmt"
	"strings"

	SDK "github.com/aws/aws-sdk-go/service/dynamodb"

	"github.com/evalphobia/aws-sdk-go-wrapper/private/pointers"
)

// ConditionList contains multiple condition.
type ConditionList struct {
	keyAttributes map[string]string
	conditions    map[string]*Condition
	filters       map[string]*Condition

	index        string
	limit        int64
	startKey     map[string]*SDK.AttributeValue
	isConsistent bool
	isDesc       bool // descending order
}

// NewConditionList returns initialized *ConditionList.
func NewConditionList(keyAttributes map[string]string) *ConditionList {
	return &ConditionList{
		keyAttributes: keyAttributes,
		conditions:    make(map[string]*Condition),
		filters:       make(map[string]*Condition),
	}
}

// HasCondition checks if at least one condition is set or not.
func (c *ConditionList) HasCondition() bool {
	return len(c.conditions) != 0
}

// HasFilter checks if at least one filter is set or not.
func (c *ConditionList) HasFilter() bool {
	return len(c.filters) != 0
}

// HasIndex checks if the index is set or not.
func (c *ConditionList) HasIndex() bool {
	return c.index != ""
}

// HasLimit checks if limit number is set or not.
func (c *ConditionList) HasLimit() bool {
	return c.limit != 0
}

// SetLimit sets limit number.
func (c *ConditionList) SetLimit(i int64) {
	c.limit = i
}

// SetIndex sets index to use.
func (c *ConditionList) SetIndex(v string) {
	c.index = v
}

// SetConsistent sets consistent read flag.
func (c *ConditionList) SetConsistent(b bool) {
	c.isConsistent = b
}

// SetDesc sets descending order flag.
func (c *ConditionList) SetDesc(b bool) {
	c.isDesc = b
}

// SetStartKey sets ExclusiveStartKey.
func (c *ConditionList) SetStartKey(startKey map[string]*SDK.AttributeValue) {
	c.startKey = startKey
}

// AndEQ adds EQ(equal) condition.
func (c *ConditionList) AndEQ(key string, val interface{}) {
	c.setCondition(conditionEQ, key, val)
}

// AndLE adds LE(less equal than) condition.
func (c *ConditionList) AndLE(key string, val interface{}) {
	c.setCondition(conditionLE, key, val)
}

// AndLT adds LT(less than) condition.
func (c *ConditionList) AndLT(key string, val interface{}) {
	c.setCondition(conditionLT, key, val)
}

// AndGE adds GE(greater equal than) condition.
func (c *ConditionList) AndGE(key string, val interface{}) {
	c.setCondition(conditionGE, key, val)
}

// AndGT adds GT(greater than) condition.
func (c *ConditionList) AndGT(key string, val interface{}) {
	c.setCondition(conditionGT, key, val)
}

// AndBETWEEN adds BETWEEN condition.
func (c *ConditionList) AndBETWEEN(key string, from, to interface{}) {
	c.setCondition(conditionBETWEEN, key, from, to)
}

func (c *ConditionList) setCondition(condition, key string, val interface{}, subVal ...interface{}) {
	if _, ok := c.conditions[key]; ok {
		return
	}

	cond := newCondition(condition, key, val)
	if len(subVal) == 1 {
		cond.SubValue = subVal[0]
	}
	c.conditions[key] = cond
}

// FilterEQ adds EQ(equal) filter.
func (c *ConditionList) FilterEQ(key string, val interface{}) {
	c.setFilter(conditionEQ, key, val)
}

// FilterLE adds LE(less equal than) filter.
func (c *ConditionList) FilterLE(key string, val interface{}) {
	c.setFilter(conditionLE, key, val)
}

// FilterLT adds LT(less than) filter.
func (c *ConditionList) FilterLT(key string, val interface{}) {
	c.setFilter(conditionLT, key, val)
}

// FilterGE adds GE(greater equal than) filter.
func (c *ConditionList) FilterGE(key string, val interface{}) {
	c.setFilter(conditionGE, key, val)
}

// FilterGT adds GT(greater than) filter.
func (c *ConditionList) FilterGT(key string, val interface{}) {
	c.setFilter(conditionGT, key, val)
}

// FilterBETWEEN adds BETWEEN filter.
func (c *ConditionList) FilterBETWEEN(key string, from, to interface{}) {
	c.setFilter(conditionBETWEEN, key, from, to)
}

func (c *ConditionList) setFilter(condition, key string, val interface{}, subVal ...interface{}) {
	if _, ok := c.filters[key]; ok {
		return
	}

	cond := newCondition(condition, key, val)
	cond.isFilter = true
	if len(subVal) == 1 {
		cond.SubValue = subVal[0]
	}
	c.filters[key] = cond
}

// FormatCondition returns string pointer for KeyConditionExpression.
func (c *ConditionList) FormatCondition() *string {
	return c.formatCondition(c.conditions)
}

// FormatFilter returns string pointer for KeyConditionExpression.
func (c *ConditionList) FormatFilter() *string {
	return c.formatCondition(c.filters)
}

// formatCondition returns string pointer for ConditionExpression and FilterExpression.
func (c *ConditionList) formatCondition(conditions map[string]*Condition) *string {
	max := len(conditions)
	if max == 0 {
		return nil
	}

	i := 1
	expression := make([]string, 0, max)
	for key, cond := range conditions {
		exp := cond.expression(key)

		// add space unless final expression
		if i < max {
			exp = exp + " " + cond.operator()
		}
		expression = append(expression, exp)
		i++
	}
	e := strings.Join(expression, " ")
	return &e
}

// FormatValues returns the parameter for ExpressionAttributeValues.
func (c *ConditionList) FormatValues() map[string]*SDK.AttributeValue {
	attrs := c.keyAttributes
	m := make(map[string]*SDK.AttributeValue)

	for k, cond := range c.getMergedConditions() {
		typ, ok := attrs[k]
		if !ok {
			continue
		}
		key := cond.valueName()
		m[key] = newAttributeValue(typ, cond.Value)
		// BETWEEN
		if cond.SubValue != nil {
			sub := cond.subValueName()
			m[sub] = newAttributeValue(typ, cond.SubValue)
		}
	}
	return m
}

// FormatNames returns the parameter for ExpressionAttributeNames.
func (c *ConditionList) FormatNames() map[string]*string {
	attrs := c.keyAttributes
	m := make(map[string]*string)

	for _, cond := range c.getMergedConditions() {
		if _, ok := attrs[cond.Key]; !ok {
			continue
		}
		m[cond.keyName()] = pointers.String(cond.Key)
	}
	return m
}

func (c *ConditionList) getMergedConditions() map[string]*Condition {
	list := make(map[string]*Condition)
	for k, v := range c.filters {
		list[k] = v
	}

	for k, v := range c.conditions {
		list[k] = v
	}
	return list
}

// Condition contains condition.
type Condition struct {
	Condition string
	Key       string
	Value     interface{}
	SubValue  interface{}
	OR        bool
	isFilter  bool
}

// newCondition returns initialized *Condition.
func newCondition(condition, key string, val interface{}) *Condition {
	return &Condition{
		Condition: condition,
		Key:       key,
		Value:     val,
	}
}

func (c *Condition) expression(key string) (expression string) {
	switch {
	case c.Condition == conditionBETWEEN:
		return fmt.Sprintf("%s BETWEEN %s AND %s", c.keyName(), c.valueName(), c.subValueName())
	default:
		return fmt.Sprintf("%s %s %s", c.keyName(), c.Condition, c.valueName())
	}
}

func (c *Condition) operator() string {
	if c.OR {
		return conditionOR
	}
	return conditionAND
}

func (c *Condition) keyName() string {
	switch {
	case c.isFilter:
		return fmt.Sprintf("#f_%s", c.Key)
	default:
		return fmt.Sprintf("#c_%s", c.Key)
	}
}

func (c *Condition) valueName() string {
	switch {
	case c.isFilter:
		return fmt.Sprintf(":f_%s", c.Key)
	default:
		return fmt.Sprintf(":c_%s", c.Key)
	}
}

func (c *Condition) subValueName() string {
	switch {
	case c.isFilter:
		return fmt.Sprintf(":fs_%s", c.Key)
	default:
		return fmt.Sprintf(":cs_%s", c.Key)
	}
}
<commit_msg>[DynamoDB] feat: Add 'ConditionList.AddKeyAttribute' method (#50)<commit_after>package dynamodb

import (
	"fmt"
	"strings"

	SDK "github.com/aws/aws-sdk-go/service/dynamodb"

	"github.com/evalphobia/aws-sdk-go-wrapper/private/pointers"
)

// ConditionList contains multiple condition.
type ConditionList struct {
	keyAttributes map[string]string
	conditions    map[string]*Condition
	filters       map[string]*Condition

	index        string
	limit        int64
	startKey     map[string]*SDK.AttributeValue
	isConsistent bool
	isDesc       bool // descending order
}

// NewConditionList returns initialized *ConditionList.
func NewConditionList(keyAttributes map[string]string) *ConditionList {
	return &ConditionList{
		keyAttributes: keyAttributes,
		conditions:    make(map[string]*Condition),
		filters:       make(map[string]*Condition),
	}
}

// HasCondition checks if at least one condition is set or not.
func (c *ConditionList) HasCondition() bool {
	return len(c.conditions) != 0
}

// HasFilter checks if at least one filter is set or not.
func (c *ConditionList) HasFilter() bool {
	return len(c.filters) != 0
}

// HasIndex checks if the index is set or not.
func (c *ConditionList) HasIndex() bool {
	return c.index != ""
}

// HasLimit checks if limit number is set or not.
func (c *ConditionList) HasLimit() bool {
	return c.limit != 0
}

// SetLimit sets limit number.
func (c *ConditionList) SetLimit(i int64) {
	c.limit = i
}

// SetIndex sets index to use.
func (c *ConditionList) SetIndex(v string) {
	c.index = v
}

// SetConsistent sets consistent read flag.
func (c *ConditionList) SetConsistent(b bool) {
	c.isConsistent = b
}

// SetDesc sets descending order flag.
func (c *ConditionList) SetDesc(b bool) {
	c.isDesc = b
}

// SetStartKey sets ExclusiveStartKey.
func (c *ConditionList) SetStartKey(startKey map[string]*SDK.AttributeValue) {
	c.startKey = startKey
}

// AndEQ adds EQ(equal) condition.
func (c *ConditionList) AndEQ(key string, val interface{}) {
	c.setCondition(conditionEQ, key, val)
}

// AndLE adds LE(less equal than) condition.
func (c *ConditionList) AndLE(key string, val interface{}) {
	c.setCondition(conditionLE, key, val)
}

// AndLT adds LT(less than) condition.
func (c *ConditionList) AndLT(key string, val interface{}) {
	c.setCondition(conditionLT, key, val)
}

// AndGE adds GE(greater equal than) condition.
func (c *ConditionList) AndGE(key string, val interface{}) {
	c.setCondition(conditionGE, key, val)
}

// AndGT adds GT(greater than) condition.
func (c *ConditionList) AndGT(key string, val interface{}) {
	c.setCondition(conditionGT, key, val)
}

// AndBETWEEN adds BETWEEN condition.
func (c *ConditionList) AndBETWEEN(key string, from, to interface{}) {
	c.setCondition(conditionBETWEEN, key, from, to)
}

func (c *ConditionList) setCondition(condition, key string, val interface{}, subVal ...interface{}) {
	if _, ok := c.conditions[key]; ok {
		return
	}

	cond := newCondition(condition, key, val)
	if len(subVal) == 1 {
		cond.SubValue = subVal[0]
	}
	c.conditions[key] = cond
}

// FilterEQ adds EQ(equal) filter.
func (c *ConditionList) FilterEQ(key string, val interface{}) {
	c.setFilter(conditionEQ, key, val)
}

// FilterLE adds LE(less equal than) filter.
func (c *ConditionList) FilterLE(key string, val interface{}) {
	c.setFilter(conditionLE, key, val)
}

// FilterLT adds LT(less than) filter.
func (c *ConditionList) FilterLT(key string, val interface{}) {
	c.setFilter(conditionLT, key, val)
}

// FilterGE adds GE(greater equal than) filter.
func (c *ConditionList) FilterGE(key string, val interface{}) {
	c.setFilter(conditionGE, key, val)
}

// FilterGT adds GT(greater than) filter.
func (c *ConditionList) FilterGT(key string, val interface{}) {
	c.setFilter(conditionGT, key, val)
}

// FilterBETWEEN adds BETWEEN filter.
func (c *ConditionList) FilterBETWEEN(key string, from, to interface{}) {
	c.setFilter(conditionBETWEEN, key, from, to)
}

func (c *ConditionList) setFilter(condition, key string, val interface{}, subVal ...interface{}) {
	if _, ok := c.filters[key]; ok {
		return
	}

	cond := newCondition(condition, key, val)
	cond.isFilter = true
	if len(subVal) == 1 {
		cond.SubValue = subVal[0]
	}
	c.filters[key] = cond
}

// FormatCondition returns string pointer for KeyConditionExpression.
func (c *ConditionList) FormatCondition() *string {
	return c.formatCondition(c.conditions)
}

// FormatFilter returns string pointer for KeyConditionExpression.
func (c *ConditionList) FormatFilter() *string {
	return c.formatCondition(c.filters)
}

// formatCondition returns string pointer for ConditionExpression and FilterExpression.
func (c *ConditionList) formatCondition(conditions map[string]*Condition) *string {
	max := len(conditions)
	if max == 0 {
		return nil
	}

	i := 1
	expression := make([]string, 0, max)
	for key, cond := range conditions {
		exp := cond.expression(key)

		// add space unless final expression
		if i < max {
			exp = exp + " " + cond.operator()
		}
		expression = append(expression, exp)
		i++
	}
	e := strings.Join(expression, " ")
	return &e
}

// FormatValues returns the parameter for ExpressionAttributeValues.
func (c *ConditionList) FormatValues() map[string]*SDK.AttributeValue {
	attrs := c.keyAttributes
	m := make(map[string]*SDK.AttributeValue)

	for k, cond := range c.getMergedConditions() {
		typ, ok := attrs[k]
		if !ok {
			continue
		}
		key := cond.valueName()
		m[key] = newAttributeValue(typ, cond.Value)
		// BETWEEN
		if cond.SubValue != nil {
			sub := cond.subValueName()
			m[sub] = newAttributeValue(typ, cond.SubValue)
		}
	}
	return m
}

// FormatNames returns the parameter for ExpressionAttributeNames.
func (c *ConditionList) FormatNames() map[string]*string {
	attrs := c.keyAttributes
	m := make(map[string]*string)

	for _, cond := range c.getMergedConditions() {
		if _, ok := attrs[cond.Key]; !ok {
			continue
		}
		m[cond.keyName()] = pointers.String(cond.Key)
	}
	return m
}

func (c *ConditionList) getMergedConditions() map[string]*Condition {
	list := make(map[string]*Condition)
	for k, v := range c.filters {
		list[k] = v
	}

	for k, v := range c.conditions {
		list[k] = v
	}
	return list
}

// AddKeyAttribute adds to attribute to keyAttributes.
func (c *ConditionList) AddKeyAttribute(attr AttributeDefinition) {
	c.keyAttributes[attr.Name] = attr.Type
}

// Condition contains condition.
type Condition struct {
	Condition string
	Key       string
	Value     interface{}
	SubValue  interface{}
	OR        bool
	isFilter  bool
}

// newCondition returns initialized *Condition.
func newCondition(condition, key string, val interface{}) *Condition {
	return &Condition{
		Condition: condition,
		Key:       key,
		Value:     val,
	}
}

func (c *Condition) expression(key string) (expression string) {
	switch {
	case c.Condition == conditionBETWEEN:
		return fmt.Sprintf("%s BETWEEN %s AND %s", c.keyName(), c.valueName(), c.subValueName())
	default:
		return fmt.Sprintf("%s %s %s", c.keyName(), c.Condition, c.valueName())
	}
}

func (c *Condition) operator() string {
	if c.OR {
		return conditionOR
	}
	return conditionAND
}

func (c *Condition) keyName() string {
	switch {
	case c.isFilter:
		return fmt.Sprintf("#f_%s", c.Key)
	default:
		return fmt.Sprintf("#c_%s", c.Key)
	}
}

func (c *Condition) valueName() string {
	switch {
	case c.isFilter:
		return fmt.Sprintf(":f_%s", c.Key)
	default:
		return fmt.Sprintf(":c_%s", c.Key)
	}
}

func (c *Condition) subValueName() string {
	switch {
	case c.isFilter:
		return fmt.Sprintf(":fs_%s", c.Key)
	default:
		return fmt.Sprintf(":cs_%s", c.Key)
	}
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker, Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"syscall"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	// Backend registrations
	_ "github.com/docker/api/azure"
	_ "github.com/docker/api/example"
	_ "github.com/docker/api/local"

	"github.com/docker/api/cli/cmd"
	"github.com/docker/api/cli/cmd/compose"
	contextcmd "github.com/docker/api/cli/cmd/context"
	"github.com/docker/api/cli/cmd/login"
	"github.com/docker/api/cli/cmd/run"
	"github.com/docker/api/cli/mobycli"
	cliopts "github.com/docker/api/cli/options"
	"github.com/docker/api/config"
	apicontext "github.com/docker/api/context"
	"github.com/docker/api/context/store"
)

var (
	ownCommands = map[string]struct{}{
		"context": {},
		"login":   {},
		"serve":   {},
		"version": {},
	}
)

func init() {
	// initial hack to get the path of the project's bin dir
	// into the env of this cli for development
	path, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fatal(errors.Wrap(err, "unable to get absolute bin path"))
	}
	if err := os.Setenv("PATH", fmt.Sprintf("%s:%s", os.Getenv("PATH"), path)); err != nil {
		panic(err)
	}
	// Seed random
	rand.Seed(time.Now().UnixNano())
}

func isOwnCommand(cmd *cobra.Command) bool {
	if cmd == nil {
		return false
	}
	if _, ok := ownCommands[cmd.Name()]; ok {
		return true
	}
	return isOwnCommand(cmd.Parent())
}

func main() {
	var opts cliopts.GlobalOpts
	root := &cobra.Command{
		Use:           "docker",
		SilenceErrors: true,
		SilenceUsage:  true,
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if !isOwnCommand(cmd) {
				mobycli.ExecIfDefaultCtxType(cmd.Context())
			}
			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			return cmd.Help()
		},
	}

	root.AddCommand(
		contextcmd.Command(),
		cmd.PsCommand(),
		cmd.ServeCommand(),
		run.Command(),
		cmd.ExecCommand(),
		cmd.LogsCommand(),
		cmd.RmCommand(),
		cmd.InspectCommand(),
		compose.Command(),
		login.Command(),
		cmd.VersionCommand(),
	)

	helpFunc := root.HelpFunc()
	root.SetHelpFunc(func(cmd *cobra.Command, args []string) {
		if !isOwnCommand(cmd) {
			mobycli.ExecIfDefaultCtxType(cmd.Context())
		}
		helpFunc(cmd, args)
	})

	root.PersistentFlags().BoolVarP(&opts.Debug, "debug", "d", false, "enable debug output in the logs")
	opts.AddConfigFlags(root.PersistentFlags())
	opts.AddContextFlags(root.PersistentFlags())

	// populate the opts with the global flags
	_ = root.PersistentFlags().Parse(os.Args[1:])
	if opts.Debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	ctx, cancel := newSigContext()
	defer cancel()

	if opts.Config == "" {
		fatal(errors.New("config path cannot be empty"))
	}
	configDir := opts.Config
	ctx = config.WithDir(ctx, configDir)

	currentContext := determineCurrentContext(opts.Context, configDir)

	s, err := store.New(store.WithRoot(configDir))
	if err != nil {
		fatal(errors.Wrap(err, "unable to create context store"))
	}
	ctx = apicontext.WithCurrentContext(ctx, currentContext)
	ctx = store.WithContextStore(ctx, s)

	err = root.ExecuteContext(ctx)
	if err != nil {
		// Context should always be handled by new CLI
		requiredCmd, _, _ := root.Find(os.Args[1:])
		if requiredCmd != nil && isOwnCommand(requiredCmd) {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		mobycli.ExecIfDefaultCtxType(ctx)

		checkIfUnknownCommandExistInDefaultContext(err, currentContext)
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string) {
	re := regexp.MustCompile(`unknown command "([^"]*)"`)
	submatch := re.FindSubmatch([]byte(err.Error()))
	if len(submatch) == 2 {
		dockerCommand := string(submatch[1])

		if mobycli.IsDefaultContextCommand(dockerCommand) {
			fmt.Fprintf(os.Stderr, "Command \"%s\" not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
			os.Exit(1)
		}
	}
}

func newSigContext() (context.Context, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	s := make(chan os.Signal)
	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-s
		cancel()
	}()
	return ctx, cancel
}

func determineCurrentContext(flag string, configDir string) string {
	res := flag
	if res == "" {
		config, err := config.LoadFile(configDir)
		if err != nil {
			fmt.Fprintln(os.Stderr, errors.Wrap(err, "WARNING"))
			return "default"
		}
		res = config.CurrentContext
	}
	if res == "" {
		res = "default"
	}
	return res
}

func fatal(err error) {
	fmt.Fprint(os.Stderr, err)
	os.Exit(1)
}
<commit_msg>Changed root level debug shorthand to capital D, make it consistent with Moby flag and not clash with —detach future option<commit_after>/*
   Copyright 2020 Docker, Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"syscall"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	// Backend registrations
	_ "github.com/docker/api/azure"
	_ "github.com/docker/api/example"
	_ "github.com/docker/api/local"

	"github.com/docker/api/cli/cmd"
	"github.com/docker/api/cli/cmd/compose"
	contextcmd "github.com/docker/api/cli/cmd/context"
	"github.com/docker/api/cli/cmd/login"
	"github.com/docker/api/cli/cmd/run"
	"github.com/docker/api/cli/mobycli"
	cliopts "github.com/docker/api/cli/options"
	"github.com/docker/api/config"
	apicontext "github.com/docker/api/context"
	"github.com/docker/api/context/store"
)

var (
	ownCommands = map[string]struct{}{
		"context": {},
		"login":   {},
		"serve":   {},
		"version": {},
	}
)

func init() {
	// initial hack to get the path of the project's bin dir
	// into the env of this cli for development
	path, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fatal(errors.Wrap(err, "unable to get absolute bin path"))
	}
	if err := os.Setenv("PATH", fmt.Sprintf("%s:%s", os.Getenv("PATH"), path)); err != nil {
		panic(err)
	}
	// Seed random
	rand.Seed(time.Now().UnixNano())
}

func isOwnCommand(cmd *cobra.Command) bool {
	if cmd == nil {
		return false
	}
	if _, ok := ownCommands[cmd.Name()]; ok {
		return true
	}
	return isOwnCommand(cmd.Parent())
}

func main() {
	var opts cliopts.GlobalOpts
	root := &cobra.Command{
		Use:           "docker",
		SilenceErrors: true,
		SilenceUsage:  true,
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if !isOwnCommand(cmd) {
				mobycli.ExecIfDefaultCtxType(cmd.Context())
			}
			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			return cmd.Help()
		},
	}

	root.AddCommand(
		contextcmd.Command(),
		cmd.PsCommand(),
		cmd.ServeCommand(),
		run.Command(),
		cmd.ExecCommand(),
		cmd.LogsCommand(),
		cmd.RmCommand(),
		cmd.InspectCommand(),
		compose.Command(),
		login.Command(),
		cmd.VersionCommand(),
	)

	helpFunc := root.HelpFunc()
	root.SetHelpFunc(func(cmd *cobra.Command, args []string) {
		if !isOwnCommand(cmd) {
			mobycli.ExecIfDefaultCtxType(cmd.Context())
		}
		helpFunc(cmd, args)
	})

	root.PersistentFlags().BoolVarP(&opts.Debug, "debug", "D", false, "enable debug output in the logs")
	opts.AddConfigFlags(root.PersistentFlags())
	opts.AddContextFlags(root.PersistentFlags())

	// populate the opts with the global flags
	_ = root.PersistentFlags().Parse(os.Args[1:])
	if opts.Debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	ctx, cancel := newSigContext()
	defer cancel()

	if opts.Config == "" {
		fatal(errors.New("config path cannot be empty"))
	}
	configDir := opts.Config
	ctx = config.WithDir(ctx, configDir)

	currentContext := determineCurrentContext(opts.Context, configDir)

	s, err := store.New(store.WithRoot(configDir))
	if err != nil {
		fatal(errors.Wrap(err, "unable to create context store"))
	}
	ctx = apicontext.WithCurrentContext(ctx, currentContext)
	ctx = store.WithContextStore(ctx, s)

	err = root.ExecuteContext(ctx)
	if err != nil {
		// Context should always be handled by new CLI
		requiredCmd, _, _ := root.Find(os.Args[1:])
		if requiredCmd != nil && isOwnCommand(requiredCmd) {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		mobycli.ExecIfDefaultCtxType(ctx)

		checkIfUnknownCommandExistInDefaultContext(err, currentContext)
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string) {
	re := regexp.MustCompile(`unknown command "([^"]*)"`)
	submatch := re.FindSubmatch([]byte(err.Error()))
	if len(submatch) == 2 {
		dockerCommand := string(submatch[1])

		if mobycli.IsDefaultContextCommand(dockerCommand) {
			fmt.Fprintf(os.Stderr, "Command \"%s\" not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
			os.Exit(1)
		}
	}
}

func newSigContext() (context.Context, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	s := make(chan os.Signal)
	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-s
		cancel()
	}()
	return ctx, cancel
}

func determineCurrentContext(flag string, configDir string) string {
	res := flag
	if res == "" {
		config, err := config.LoadFile(configDir)
		if err != nil {
			fmt.Fprintln(os.Stderr, errors.Wrap(err, "WARNING"))
			return "default"
		}
		res = config.CurrentContext
	}
	if res == "" {
		res = "default"
	}
	return res
}

func fatal(err error) {
	fmt.Fprint(os.Stderr, err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"strings"
)

type User struct{}

func (c *User) Info() *Info {
	return &Info{Name: "user"}
}

func (c *User) Subcommands() map[string]interface{} {
	return map[string]interface{}{
		"create": &UserCreate{},
	}
}

type UserCreate struct{}

func (c *UserCreate) Info() *Info {
	return &Info{Name: "create"}
}

func (c *UserCreate) Run(context *Context, client Doer) error {
	email, password := context.Args[0], context.Args[1]
	b := bytes.NewBufferString(`{"email":"` + email + `", "password":"` + password + `"}`)
	request, err := http.NewRequest("POST", "http://tsuru.plataformas.glb.com:8080/users", b)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, fmt.Sprintf(`User "%s" created with success!`+"\n", email))
	return nil
}

type Login struct{}

func (c *Login) Run(context *Context, client Doer) error {
	email, password := context.Args[0], context.Args[1]
	b := bytes.NewBufferString(`{"password":"` + password + `"}`)
	request, err := http.NewRequest("POST", "http://tsuru.plataformas.glb.com:8080/users/"+email+"/tokens", b)
	if err != nil {
		return err
	}
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	out := make(map[string]string)
	err = json.Unmarshal(result, &out)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Successfully logged!\n")
	WriteToken(out["token"])
	return nil
}

func (c *Login) Info() *Info {
	return &Info{
		Name:  "login",
		Usage: "glb login email password",
	}
}

func readKey() (string, error) {
	user, err := user.Current()
	keyPath := user.HomeDir + "/.ssh/id_rsa.pub"
	output, err := ioutil.ReadFile(keyPath)
	return string(output), err
}

type Key struct{}

func (c *Key) Info() *Info {
	return &Info{Name: "key"}
}

func (c *Key) Subcommands() map[string]interface{} {
	return map[string]interface{}{
		"add":    &AddKeyCommand{},
		"remove": &RemoveKey{},
	}
}

type RemoveKey struct{}

func (c *RemoveKey) Info() *Info {
	return &Info{Name: "remove"}
}

func (c *RemoveKey) Run(context *Context, client Doer) error {
	key, err := readKey()
	if os.IsNotExist(err) {
		io.WriteString(context.Stderr, "You don't have a public key\n")
		io.WriteString(context.Stderr, "To generate a key use 'ssh-keygen' command\n")
		return nil
	}
	b := bytes.NewBufferString(fmt.Sprintf(`{"key":"%s"}`, strings.Replace(key, "\n", "", -1)))
	request, err := http.NewRequest("DELETE", "http://tsuru.plataformas.glb.com:8080/users/keys", b)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Key removed with success!\n")
	return nil
}

type AddKeyCommand struct{}

func (c *AddKeyCommand) Info() *Info {
	return &Info{Name: "add"}
}

func (c *AddKeyCommand) Run(context *Context, client Doer) error {
	key, err := readKey()
	if os.IsNotExist(err) {
		io.WriteString(context.Stderr, "You don't have a public key\n")
		io.WriteString(context.Stderr, "To generate a key use 'ssh-keygen' command\n")
		return nil
	}
	b := bytes.NewBufferString(fmt.Sprintf(`{"key":"%s"}`, strings.Replace(key, "\n", "", -1)))
	request, err := http.NewRequest("POST", "http://tsuru.plataformas.glb.com:8080/users/keys", b)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Key added with success!\n")
	return nil
}

type Logout struct{}

func (c *Logout) Info() *Info {
	return &Info{Name: "logout"}
}

func (c *Logout) Run(context *Context, client Doer) error {
	err := WriteToken("")
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Successfully logout!\n")
	return nil
}

type Team struct{}

func (c *Team) Subcommands() map[string]interface{} {
	return map[string]interface{}{
		"add-user":    &TeamAddUser{},
		"remove-user": &TeamRemoveUser{},
		"create":      &TeamCreate{},
	}
}

func (c *Team) Info() *Info {
	return &Info{Name: "team"}
}

func (c *Team) Run(context *Context, client Doer) error {
	return nil
}

type TeamCreate struct{}

func (c *TeamCreate) Info() *Info {
	return &Info{Name: "create"}
}

func (c *TeamCreate) Run(context *Context, client Doer) error {
	team := context.Args[0]
	b := bytes.NewBufferString(fmt.Sprintf(`{"name":"%s"}`, team))
	request, err := http.NewRequest("POST", "http://tsuru.plataformas.glb.com:8080/teams", b)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, fmt.Sprintf(`Team "%s" created with success!`+"\n", team))
	return nil
}

type TeamAddUser struct{}

func (c *TeamAddUser) Info() *Info {
	return &Info{Name: "add-user"}
}

func (c *TeamAddUser) Run(context *Context, client Doer) error {
	teamName, userName := context.Args[0], context.Args[1]
	request, err := http.NewRequest("PUT", fmt.Sprintf("http://tsuru.plataformas.glb.com:8080/teams/%s/%s", teamName, userName), nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, fmt.Sprintf(`User "%s" was added to the "%s" team`+"\n", userName, teamName))
	return nil
}

type TeamRemoveUser struct{}

func (c *TeamRemoveUser) Info() *Info {
	return &Info{Name: "remove-user"}
}

func (c *TeamRemoveUser) Run(context *Context, client Doer) error {
	teamName, userName := context.Args[0], context.Args[1]
	request, err := http.NewRequest("DELETE", fmt.Sprintf("http://tsuru.plataformas.glb.com:8080/teams/%s/%s", teamName, userName), nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, fmt.Sprintf(`User "%s" was removed from the "%s" team`+"\n", userName, teamName))
	return nil
}
<commit_msg>added usage for logout<commit_after>package cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"strings"
)

type User struct{}

func (c *User) Info() *Info {
	return &Info{Name: "user"}
}

func (c *User) Subcommands() map[string]interface{} {
	return map[string]interface{}{
		"create": &UserCreate{},
	}
}

type UserCreate struct{}

func (c *UserCreate) Info() *Info {
	return &Info{Name: "create"}
}

func (c *UserCreate) Run(context *Context, client Doer) error {
	email, password := context.Args[0], context.Args[1]
	b := bytes.NewBufferString(`{"email":"` + email + `", "password":"` + password + `"}`)
	request, err := http.NewRequest("POST", "http://tsuru.plataformas.glb.com:8080/users", b)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, fmt.Sprintf(`User "%s" created with success!`+"\n", email))
	return nil
}

type Login struct{}

func (c *Login) Run(context *Context, client Doer) error {
	email, password := context.Args[0], context.Args[1]
	b := bytes.NewBufferString(`{"password":"` + password + `"}`)
	request, err := http.NewRequest("POST", "http://tsuru.plataformas.glb.com:8080/users/"+email+"/tokens", b)
	if err != nil {
		return err
	}
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	out := make(map[string]string)
	err = json.Unmarshal(result, &out)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Successfully logged!\n")
	WriteToken(out["token"])
	return nil
}

func (c *Login) Info() *Info {
	return &Info{
		Name:  "login",
		Usage: "glb login email password",
	}
}

func readKey() (string, error) {
	user, err := user.Current()
	keyPath := user.HomeDir + "/.ssh/id_rsa.pub"
	output, err := ioutil.ReadFile(keyPath)
	return string(output), err
}

type Key struct{}

func (c *Key) Info() *Info {
	return &Info{Name: "key"}
}

func (c *Key) Subcommands() map[string]interface{} {
	return map[string]interface{}{
		"add":    &AddKeyCommand{},
		"remove": &RemoveKey{},
	}
}

type RemoveKey struct{}

func (c *RemoveKey) Info() *Info {
	return &Info{Name: "remove"}
}

func (c *RemoveKey) Run(context *Context, client Doer) error {
	key, err := readKey()
	if os.IsNotExist(err) {
		io.WriteString(context.Stderr, "You don't have a public key\n")
		io.WriteString(context.Stderr, "To generate a key use 'ssh-keygen' command\n")
		return nil
	}
	b := bytes.NewBufferString(fmt.Sprintf(`{"key":"%s"}`, strings.Replace(key, "\n", "", -1)))
	request, err := http.NewRequest("DELETE", "http://tsuru.plataformas.glb.com:8080/users/keys", b)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Key removed with success!\n")
	return nil
}

type AddKeyCommand struct{}

func (c *AddKeyCommand) Info() *Info {
	return &Info{Name: "add"}
}

func (c *AddKeyCommand) Run(context *Context, client Doer) error {
	key, err := readKey()
	if os.IsNotExist(err) {
		io.WriteString(context.Stderr, "You don't have a public key\n")
		io.WriteString(context.Stderr, "To generate a key use 'ssh-keygen' command\n")
		return nil
	}
	b := bytes.NewBufferString(fmt.Sprintf(`{"key":"%s"}`, strings.Replace(key, "\n", "", -1)))
	request, err := http.NewRequest("POST", "http://tsuru.plataformas.glb.com:8080/users/keys", b)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Key added with success!\n")
	return nil
}

type Logout struct{}

func (c *Logout) Info() *Info {
	return &Info{
		Name:  "logout",
		Usage: "glb logout",
	}
}

func (c *Logout) Run(context *Context, client Doer) error {
	err := WriteToken("")
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Successfully logout!\n")
	return nil
}

type Team struct{}

func (c *Team) Subcommands() map[string]interface{} {
	return map[string]interface{}{
		"add-user":    &TeamAddUser{},
		"remove-user": &TeamRemoveUser{},
		"create":      &TeamCreate{},
	}
}

func (c *Team) Info() *Info {
	return &Info{Name: "team"}
}

func (c *Team) Run(context *Context, client Doer) error {
	return nil
}

type TeamCreate struct{}

func (c *TeamCreate) Info() *Info {
	return &Info{Name: "create"}
}

func (c *TeamCreate) Run(context *Context, client Doer) error {
	team := context.Args[0]
	b := bytes.NewBufferString(fmt.Sprintf(`{"name":"%s"}`, team))
	request, err := http.NewRequest("POST", "http://tsuru.plataformas.glb.com:8080/teams", b)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, fmt.Sprintf(`Team "%s" created with success!`+"\n", team))
	return nil
}

type TeamAddUser struct{}

func (c *TeamAddUser) Info() *Info {
	return &Info{Name: "add-user"}
}

func (c *TeamAddUser) Run(context *Context, client Doer) error {
	teamName, userName := context.Args[0], context.Args[1]
	request, err := http.NewRequest("PUT", fmt.Sprintf("http://tsuru.plataformas.glb.com:8080/teams/%s/%s", teamName, userName), nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, fmt.Sprintf(`User "%s" was added to the "%s" team`+"\n", userName, teamName))
	return nil
}

type TeamRemoveUser struct{}

func (c *TeamRemoveUser) Info() *Info {
	return &Info{Name: "remove-user"}
}

func (c *TeamRemoveUser) Run(context *Context, client Doer) error {
	teamName, userName := context.Args[0], context.Args[1]
	request, err := http.NewRequest("DELETE", fmt.Sprintf("http://tsuru.plataformas.glb.com:8080/teams/%s/%s", teamName, userName), nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, fmt.Sprintf(`User "%s" was removed from the "%s" team`+"\n", userName, teamName))
	return nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io/ioutil"

	fn "github.com/boson-project/func"
	"github.com/boson-project/func/cloudevents"
	"github.com/boson-project/func/knative"
	"github.com/google/uuid"
	"github.com/ory/viper"
	"github.com/spf13/cobra"
)

func init() {
	e := cloudevents.NewEmitter()
	root.AddCommand(emitCmd)
	// TODO: do these env vars make sense?
	emitCmd.Flags().StringP("sink", "k", "", "Send the CloudEvent to the function running at [sink]. The special value \"local\" can be used to send the event to a function running on the local host. When provided, the --path flag is ignored  (Env: $FUNC_SINK)")
	emitCmd.Flags().StringP("source", "s", e.Source, "CloudEvent source (Env: $FUNC_SOURCE)")
	emitCmd.Flags().StringP("type", "t", e.Type, "CloudEvent type  (Env: $FUNC_TYPE)")
	emitCmd.Flags().StringP("id", "i", uuid.NewString(), "CloudEvent ID (Env: $FUNC_ID)")
	emitCmd.Flags().StringP("data", "d", "", "Any arbitrary string to be sent as the CloudEvent data. Ignored if --file is provided  (Env: $FUNC_DATA)")
	emitCmd.Flags().StringP("file", "f", "", "Path to a local file containing CloudEvent data to be sent  (Env: $FUNC_FILE)")
	emitCmd.Flags().StringP("content-type", "c", "application/json", "The MIME Content-Type for the CloudEvent data  (Env: $FUNC_CONTENT_TYPE)")
	emitCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory. Ignored when --sink is provided (Env: $FUNC_PATH)")
}

var emitCmd = &cobra.Command{
	Use:   "emit",
	Short: "Emit a CloudEvent to a function endpoint",
	Long: `Emit event

Emits a CloudEvent, sending it to the deployed function.
`,
	Example: `
# Send a CloudEvent to the deployed function with no data and default values
# for source, type and ID
kn func emit

# Send a CloudEvent to the deployed function with the data found in ./test.json
kn func emit --file ./test.json

# Send a CloudEvent to the function running locally with a CloudEvent containing
# "Hello World!" as the data field, with a content type of "text/plain"
kn func emit --data "Hello World!" --content-type "text/plain" -s local

# Send a CloudEvent to the function running locally with an event type of "my.event"
kn func emit --type my.event --sink local

# Send a CloudEvent to the deployed function found at /path/to/fn with an id of "fn.test"
kn func emit --path /path/to/fn -i fn.test

# Send a CloudEvent to an arbitrary endpoint
kn func emit --sink "http://my.event.broker.com"
`,
	SuggestFor: []string{"meit", "emti", "send"},
	PreRunE:    bindEnv("source", "type", "id", "data", "file", "path", "sink", "content-type"),
	RunE:       runEmit,
}

func runEmit(cmd *cobra.Command, args []string) (err error) {
	config := newEmitConfig()
	var endpoint string
	if config.Sink != "" {
		if config.Sink == "local" {
			endpoint = "http://localhost:8080"
		} else {
			endpoint = config.Sink
		}
	} else {
		var f fn.Function
		f, err = fn.NewFunction(config.Path)
		if err != nil {
			return
		}
		// What happens if the function hasn't been deployed but they don't run with --local=true
		// Maybe we should be thinking about saving the endpoint URL in func.yaml after each deploy
		var d *knative.Describer
		d, err = knative.NewDescriber("")
		if err != nil {
			return
		}
		var desc fn.Description
		desc, err = d.Describe(f.Name)
		if err != nil {
			return
		}
		// Use the first available route
		endpoint = desc.Routes[0]
	}

	emitter := cloudevents.NewEmitter()
	emitter.Source = config.Source
	emitter.Type = config.Type
	emitter.Id = config.Id
	emitter.ContentType = config.ContentType
	emitter.Data = config.Data
	if config.File != "" {
		var buf []byte
		if emitter.Data != "" && config.Verbose {
			// TODO: This made me wonder whether we should switch to a real logging library
			fmt.Printf("WARN: Found both --data and --file. Using file: %v\n", config.File)
		}
		buf, err = ioutil.ReadFile(config.File)
		if err != nil {
			return
		}
		emitter.Data = string(buf)
	}

	client := fn.New(
		fn.WithEmitter(emitter),
	)
	return client.Emit(cmd.Context(), endpoint)
}

type emitConfig struct {
	Path        string
	Source      string
	Type        string
	Id          string
	Data        string
	File        string
	ContentType string
	Sink        string
	Verbose     bool
}

func newEmitConfig() emitConfig {
	return emitConfig{
		Path:        viper.GetString("path"),
		Source:      viper.GetString("source"),
		Type:        viper.GetString("type"),
		Id:          viper.GetString("id"),
		Data:        viper.GetString("data"),
		File:        viper.GetString("file"),
		ContentType: viper.GetString("content-type"),
		Sink:        viper.GetString("sink"),
		Verbose:     viper.GetBool("verbose"),
	}
}
<commit_msg>src: fail fast with conflicting flags (#348)<commit_after>package cmd

import (
	"fmt"
	"io/ioutil"

	fn "github.com/boson-project/func"
	"github.com/boson-project/func/cloudevents"
	"github.com/boson-project/func/knative"
	"github.com/google/uuid"
	"github.com/ory/viper"
	"github.com/spf13/cobra"
)

func init() {
	e := cloudevents.NewEmitter()
	root.AddCommand(emitCmd)
	// TODO: do these env vars make sense?
	emitCmd.Flags().StringP("sink", "k", "", "Send the CloudEvent to the function running at [sink]. The special value \"local\" can be used to send the event to a function running on the local host. When provided, the --path flag is ignored  (Env: $FUNC_SINK)")
	emitCmd.Flags().StringP("source", "s", e.Source, "CloudEvent source (Env: $FUNC_SOURCE)")
	emitCmd.Flags().StringP("type", "t", e.Type, "CloudEvent type  (Env: $FUNC_TYPE)")
	emitCmd.Flags().StringP("id", "i", uuid.NewString(), "CloudEvent ID (Env: $FUNC_ID)")
	emitCmd.Flags().StringP("data", "d", "", "Any arbitrary string to be sent as the CloudEvent data. Ignored if --file is provided  (Env: $FUNC_DATA)")
	emitCmd.Flags().StringP("file", "f", "", "Path to a local file containing CloudEvent data to be sent  (Env: $FUNC_FILE)")
	emitCmd.Flags().StringP("content-type", "c", "application/json", "The MIME Content-Type for the CloudEvent data  (Env: $FUNC_CONTENT_TYPE)")
	emitCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory. Ignored when --sink is provided (Env: $FUNC_PATH)")
}

var emitCmd = &cobra.Command{
	Use:   "emit",
	Short: "Emit a CloudEvent to a function endpoint",
	Long: `Emit event

Emits a CloudEvent, sending it to the deployed function.
`,
	Example: `
# Send a CloudEvent to the deployed function with no data and default values
# for source, type and ID
kn func emit

# Send a CloudEvent to the deployed function with the data found in ./test.json
kn func emit --file ./test.json

# Send a CloudEvent to the function running locally with a CloudEvent containing
# "Hello World!" as the data field, with a content type of "text/plain"
kn func emit --data "Hello World!" --content-type "text/plain" -s local

# Send a CloudEvent to the function running locally with an event type of "my.event"
kn func emit --type my.event --sink local

# Send a CloudEvent to the deployed function found at /path/to/fn with an id of "fn.test"
kn func emit --path /path/to/fn -i fn.test

# Send a CloudEvent to an arbitrary endpoint
kn func emit --sink "http://my.event.broker.com"
`,
	SuggestFor: []string{"meit", "emti", "send"},
	PreRunE:    bindEnv("source", "type", "id", "data", "file", "path", "sink", "content-type"),
	RunE:       runEmit,
}

func runEmit(cmd *cobra.Command, args []string) (err error) {
	config := newEmitConfig()
	var endpoint string
	if config.Sink != "" {
		if config.Sink == "local" {
			endpoint = "http://localhost:8080"
		} else {
			endpoint = config.Sink
		}
	} else {
		var f fn.Function
		f, err = fn.NewFunction(config.Path)
		if err != nil {
			return
		}
		// What happens if the function hasn't been deployed but they don't run with --local=true
		// Maybe we should be thinking about saving the endpoint URL in func.yaml after each deploy
		var d *knative.Describer
		d, err = knative.NewDescriber("")
		if err != nil {
			return
		}
		var desc fn.Description
		desc, err = d.Describe(f.Name)
		if err != nil {
			return
		}
		// Use the first available route
		endpoint = desc.Routes[0]
	}

	emitter := cloudevents.NewEmitter()
	emitter.Source = config.Source
	emitter.Type = config.Type
	emitter.Id = config.Id
	emitter.ContentType = config.ContentType
	emitter.Data = config.Data
	if config.File != "" {
		var buf []byte
		if emitter.Data != "" && config.Verbose {
			return fmt.Errorf("Only one of --data and --file may be specified \n")
		}
		buf, err = ioutil.ReadFile(config.File)
		if err != nil {
			return
		}
		emitter.Data = string(buf)
	}

	client := fn.New(
		fn.WithEmitter(emitter),
	)
	return client.Emit(cmd.Context(), endpoint)
}

type emitConfig struct {
	Path        string
	Source      string
	Type        string
	Id          string
	Data        string
	File        string
	ContentType string
	Sink        string
	Verbose     bool
}

func newEmitConfig() emitConfig {
	return emitConfig{
		Path:        viper.GetString("path"),
		Source:      viper.GetString("source"),
		Type:        viper.GetString("type"),
		Id:          viper.GetString("id"),
		Data:        viper.GetString("data"),
		File:        viper.GetString("file"),
		ContentType: viper.GetString("content-type"),
		Sink:        viper.GetString("sink"),
		Verbose:     viper.GetBool("verbose"),
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 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 image

import (
	"image/color"
	"testing"
)

type image interface {
	Image
	Opaque() bool
	Set(int, int, color.Color)
	SubImage(Rectangle) Image
}

func cmp(cm color.Model, c0, c1 color.Color) bool {
	r0, g0, b0, a0 := cm.Convert(c0).RGBA()
	r1, g1, b1, a1 := cm.Convert(c1).RGBA()
	return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
}

func TestImage(t *testing.T) {
	testImage := []image{
		NewRGBA(Rect(0, 0, 10, 10)),
		NewRGBA64(Rect(0, 0, 10, 10)),
		NewNRGBA(Rect(0, 0, 10, 10)),
		NewNRGBA64(Rect(0, 0, 10, 10)),
		NewAlpha(Rect(0, 0, 10, 10)),
		NewAlpha16(Rect(0, 0, 10, 10)),
		NewGray(Rect(0, 0, 10, 10)),
		NewGray16(Rect(0, 0, 10, 10)),
		NewPaletted(Rect(0, 0, 10, 10), color.Palette{
			Transparent,
			Opaque,
		}),
	}
	for _, m := range testImage {
		if !Rect(0, 0, 10, 10).Eq(m.Bounds()) {
			t.Errorf("%T: want bounds %v, got %v", m, Rect(0, 0, 10, 10), m.Bounds())
			continue
		}
		if !cmp(m.ColorModel(), Transparent, m.At(6, 3)) {
			t.Errorf("%T: at (6, 3), want a zero color, got %v", m, m.At(6, 3))
			continue
		}
		m.Set(6, 3, Opaque)
		if !cmp(m.ColorModel(), Opaque, m.At(6, 3)) {
			t.Errorf("%T: at (6, 3), want a non-zero color, got %v", m, m.At(6, 3))
			continue
		}
		if !m.SubImage(Rect(6, 3, 7, 4)).(image).Opaque() {
			t.Errorf("%T: at (6, 3) was not opaque", m)
			continue
		}
		m = m.SubImage(Rect(3, 2, 9, 8)).(image)
		if !Rect(3, 2, 9, 8).Eq(m.Bounds()) {
			t.Errorf("%T: sub-image want bounds %v, got %v", m, Rect(3, 2, 9, 8), m.Bounds())
			continue
		}
		if !cmp(m.ColorModel(), Opaque, m.At(6, 3)) {
			t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", m, m.At(6, 3))
			continue
		}
		if !cmp(m.ColorModel(), Transparent, m.At(3, 3)) {
			t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", m, m.At(3, 3))
			continue
		}
		m.Set(3, 3, Opaque)
		if !cmp(m.ColorModel(), Opaque, m.At(3, 3)) {
			t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", m, m.At(3, 3))
			continue
		}
		// Test that taking an empty sub-image starting at a corner does not panic.
		m.SubImage(Rect(0, 0, 0, 0))
		m.SubImage(Rect(10, 0, 10, 0))
		m.SubImage(Rect(0, 10, 0, 10))
		m.SubImage(Rect(10, 10, 10, 10))
	}
}

func Test16BitsPerColorChannel(t *testing.T) {
	testColorModel := []color.Model{
		color.RGBA64Model,
		color.NRGBA64Model,
		color.Alpha16Model,
		color.Gray16Model,
	}
	for _, cm := range testColorModel {
		c := cm.Convert(color.RGBA64{0x1234, 0x1234, 0x1234, 0x1234}) // Premultiplied alpha.
		r, _, _, _ := c.RGBA()
		if r != 0x1234 {
			t.Errorf("%T: want red value 0x%04x got 0x%04x", c, 0x1234, r)
			continue
		}
	}
	testImage := []image{
		NewRGBA64(Rect(0, 0, 10, 10)),
		NewNRGBA64(Rect(0, 0, 10, 10)),
		NewAlpha16(Rect(0, 0, 10, 10)),
		NewGray16(Rect(0, 0, 10, 10)),
	}
	for _, m := range testImage {
		m.Set(1, 2, color.NRGBA64{0xffff, 0xffff, 0xffff, 0x1357}) // Non-premultiplied alpha.
		r, _, _, _ := m.At(1, 2).RGBA()
		if r != 0x1357 {
			t.Errorf("%T: want red value 0x%04x got 0x%04x", m, 0x1357, r)
			continue
		}
	}
}
<commit_msg>image: add benchmarks for At and Set methods<commit_after>// Copyright 2011 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 image

import (
	"image/color"
	"testing"
)

type image interface {
	Image
	Opaque() bool
	Set(int, int, color.Color)
	SubImage(Rectangle) Image
}

func cmp(cm color.Model, c0, c1 color.Color) bool {
	r0, g0, b0, a0 := cm.Convert(c0).RGBA()
	r1, g1, b1, a1 := cm.Convert(c1).RGBA()
	return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
}

var testImages = []struct {
	name  string
	image image
}{
	{"rgba", NewRGBA(Rect(0, 0, 10, 10))},
	{"rgba64", NewRGBA64(Rect(0, 0, 10, 10))},
	{"nrgba", NewNRGBA(Rect(0, 0, 10, 10))},
	{"nrgba64", NewNRGBA64(Rect(0, 0, 10, 10))},
	{"alpha", NewAlpha(Rect(0, 0, 10, 10))},
	{"alpha16", NewAlpha16(Rect(0, 0, 10, 10))},
	{"gray", NewGray(Rect(0, 0, 10, 10))},
	{"gray16", NewGray16(Rect(0, 0, 10, 10))},
	{"paletted", NewPaletted(Rect(0, 0, 10, 10), color.Palette{
		Transparent,
		Opaque,
	})},
}

func TestImage(t *testing.T) {
	for _, tc := range testImages {
		m := tc.image
		if !Rect(0, 0, 10, 10).Eq(m.Bounds()) {
			t.Errorf("%T: want bounds %v, got %v", m, Rect(0, 0, 10, 10), m.Bounds())
			continue
		}
		if !cmp(m.ColorModel(), Transparent, m.At(6, 3)) {
			t.Errorf("%T: at (6, 3), want a zero color, got %v", m, m.At(6, 3))
			continue
		}
		m.Set(6, 3, Opaque)
		if !cmp(m.ColorModel(), Opaque, m.At(6, 3)) {
			t.Errorf("%T: at (6, 3), want a non-zero color, got %v", m, m.At(6, 3))
			continue
		}
		if !m.SubImage(Rect(6, 3, 7, 4)).(image).Opaque() {
			t.Errorf("%T: at (6, 3) was not opaque", m)
			continue
		}
		m = m.SubImage(Rect(3, 2, 9, 8)).(image)
		if !Rect(3, 2, 9, 8).Eq(m.Bounds()) {
			t.Errorf("%T: sub-image want bounds %v, got %v", m, Rect(3, 2, 9, 8), m.Bounds())
			continue
		}
		if !cmp(m.ColorModel(), Opaque, m.At(6, 3)) {
			t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", m, m.At(6, 3))
			continue
		}
		if !cmp(m.ColorModel(), Transparent, m.At(3, 3)) {
			t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", m, m.At(3, 3))
			continue
		}
		m.Set(3, 3, Opaque)
		if !cmp(m.ColorModel(), Opaque, m.At(3, 3)) {
			t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", m, m.At(3, 3))
			continue
		}
		// Test that taking an empty sub-image starting at a corner does not panic.
		m.SubImage(Rect(0, 0, 0, 0))
		m.SubImage(Rect(10, 0, 10, 0))
		m.SubImage(Rect(0, 10, 0, 10))
		m.SubImage(Rect(10, 10, 10, 10))
	}
}

func Test16BitsPerColorChannel(t *testing.T) {
	testColorModel := []color.Model{
		color.RGBA64Model,
		color.NRGBA64Model,
		color.Alpha16Model,
		color.Gray16Model,
	}
	for _, cm := range testColorModel {
		c := cm.Convert(color.RGBA64{0x1234, 0x1234, 0x1234, 0x1234}) // Premultiplied alpha.
		r, _, _, _ := c.RGBA()
		if r != 0x1234 {
			t.Errorf("%T: want red value 0x%04x got 0x%04x", c, 0x1234, r)
			continue
		}
	}
	testImage := []image{
		NewRGBA64(Rect(0, 0, 10, 10)),
		NewNRGBA64(Rect(0, 0, 10, 10)),
		NewAlpha16(Rect(0, 0, 10, 10)),
		NewGray16(Rect(0, 0, 10, 10)),
	}
	for _, m := range testImage {
		m.Set(1, 2, color.NRGBA64{0xffff, 0xffff, 0xffff, 0x1357}) // Non-premultiplied alpha.
		r, _, _, _ := m.At(1, 2).RGBA()
		if r != 0x1357 {
			t.Errorf("%T: want red value 0x%04x got 0x%04x", m, 0x1357, r)
			continue
		}
	}
}

func BenchmarkAt(b *testing.B) {
	for _, tc := range testImages {
		b.Run(tc.name, func(b *testing.B) {
			b.ReportAllocs()
			for i := 0; i < b.N; i++ {
				tc.image.At(4, 5)
			}
		})
	}
}

func BenchmarkSet(b *testing.B) {
	c := color.Gray{0xff}
	for _, tc := range testImages {
		b.Run(tc.name, func(b *testing.B) {
			b.ReportAllocs()
			for i := 0; i < b.N; i++ {
				tc.image.Set(4, 5, c)
			}
		})
	}
}

func BenchmarkRGBAAt(b *testing.B) {
	m := NewRGBA(Rect(0, 0, 10, 10))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.RGBAAt(4, 5)
	}
}

func BenchmarkRGBASetRGBA(b *testing.B) {
	m := NewRGBA(Rect(0, 0, 10, 10))
	c := color.RGBA{0xff, 0xff, 0xff, 0x13}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.SetRGBA(4, 5, c)
	}
}

func BenchmarkRGBA64At(b *testing.B) {
	m := NewRGBA64(Rect(0, 0, 10, 10))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.RGBA64At(4, 5)
	}
}

func BenchmarkRGBA64SetRGBA64(b *testing.B) {
	m := NewRGBA64(Rect(0, 0, 10, 10))
	c := color.RGBA64{0xffff, 0xffff, 0xffff, 0x1357}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.SetRGBA64(4, 5, c)
	}
}

func BenchmarkNRGBAAt(b *testing.B) {
	m := NewNRGBA(Rect(0, 0, 10, 10))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.NRGBAAt(4, 5)
	}
}

func BenchmarkNRGBASetNRGBA(b *testing.B) {
	m := NewNRGBA(Rect(0, 0, 10, 10))
	c := color.NRGBA{0xff, 0xff, 0xff, 0x13}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.SetNRGBA(4, 5, c)
	}
}

func BenchmarkNRGBA64At(b *testing.B) {
	m := NewNRGBA64(Rect(0, 0, 10, 10))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.NRGBA64At(4, 5)
	}
}

func BenchmarkNRGBA64SetNRGBA64(b *testing.B) {
	m := NewNRGBA64(Rect(0, 0, 10, 10))
	c := color.NRGBA64{0xffff, 0xffff, 0xffff, 0x1357}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.SetNRGBA64(4, 5, c)
	}
}

func BenchmarkAlphaAt(b *testing.B) {
	m := NewAlpha(Rect(0, 0, 10, 10))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.AlphaAt(4, 5)
	}
}

func BenchmarkAlphaSetAlpha(b *testing.B) {
	m := NewAlpha(Rect(0, 0, 10, 10))
	c := color.Alpha{0x13}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.SetAlpha(4, 5, c)
	}
}

func BenchmarkAlpha16At(b *testing.B) {
	m := NewAlpha16(Rect(0, 0, 10, 10))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.Alpha16At(4, 5)
	}
}

func BenchmarkAlphaSetAlpha16(b *testing.B) {
	m := NewAlpha16(Rect(0, 0, 10, 10))
	c := color.Alpha16{0x13}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.SetAlpha16(4, 5, c)
	}
}

func BenchmarkGrayAt(b *testing.B) {
	m := NewGray(Rect(0, 0, 10, 10))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.GrayAt(4, 5)
	}
}

func BenchmarkGraySetGray(b *testing.B) {
	m := NewGray(Rect(0, 0, 10, 10))
	c := color.Gray{0x13}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.SetGray(4, 5, c)
	}
}

func BenchmarkGray16At(b *testing.B) {
	m := NewGray16(Rect(0, 0, 10, 10))
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.Gray16At(4, 5)
	}
}

func BenchmarkGraySetGray16(b *testing.B) {
	m := NewGray16(Rect(0, 0, 10, 10))
	c := color.Gray16{0x13}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m.SetGray16(4, 5, c)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/jakubknejzlik/kontena-git-cli/kontena"
	"github.com/jakubknejzlik/kontena-git-cli/model"
	"github.com/jakubknejzlik/kontena-git-cli/utils"

	"github.com/urfave/cli"
)

// GridCommand ...
func GridCommand() cli.Command {
	return cli.Command{
		Name: "grid",
		Subcommands: []cli.Command{
			gridInstallCommand(),
			gridDeployCommand(),
		},
	}
}

func gridInstallCommand() cli.Command {
	return cli.Command{
		Name:      "install",
		ArgsUsage: "GRID",
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "deploy",
				Usage: "automatically deploy all services",
			},
		},
		Action: func(c *cli.Context) error {
			client := kontena.Client{}
			grid := c.Args().First()

			if err := client.EnsureMasterLogin(); err != nil {
				return cli.NewExitError(err, 1)
			}

			if grid == "" {
				return cli.NewExitError("GRID argument not specified", 1)
			}

			if err := client.GridUse(grid); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := installCoreCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := installCertificatesCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := installRegistriesCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := pruneStacksCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := installOrUpgradeStacksCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if c.Bool("deploy") {
				if err := deployStacksCommand().Run(c); err != nil {
					return cli.NewExitError(err, 1)
				}
			}

			return nil
		},
	}
}

func gridDeployCommand() cli.Command {
	return cli.Command{
		Name:        "deploy",
		ArgsUsage:   "GRID",
		Description: "Deploy all stacks in grid",
		Action: func(c *cli.Context) error {
			client := kontena.Client{}
			grid := c.Args().First()

			if err := client.EnsureMasterLogin(); err != nil {
				return cli.NewExitError(err, 1)
			}

			if grid == "" {
				return cli.NewExitError("GRID argument not specified", 1)
			}

			if err := client.GridUse(grid); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := deployStacksCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			return nil
		},
	}
}

func installOrUpgradeStacksCommand() cli.Command {
	return cli.Command{
		Name: "stacks",
		Action: func(c *cli.Context) error {
			utils.LogSection("Installing/upgrading stacks")
			client := kontena.Client{}

			stacks, _ := ioutil.ReadDir("./stacks")
			for _, stack := range stacks {
				stackName := stack.Name()
				if err := client.SecretsImport(stackName, fmt.Sprintf("./stacks/%s/secrets.yml", stackName)); err != nil {
					return cli.NewExitError(err, 1)
				}
				if !client.StackExists(stackName) {
					utils.Log("installing stack", stackName)
					dc, stackErr := getStackFromGrid(stackName)
					if stackErr != nil {
						dc = getDefaultStack(stackName, client.SecretExists("VIRTUAL_HOSTS", stackName))
					}

					if err := client.StackInstall(dc); err != nil {
						return cli.NewExitError(err, 1)
					}
				} else {
					if stack, err := getStackFromGrid(stackName); err == nil {
						utils.Log("upgrading stack", stackName)
						if err := client.StackUpgrade(stack); err != nil {
							return cli.NewExitError(err, 1)
						}
					}
				}
				time.Sleep(time.Second * 1)
			}
			return nil
		},
	}
}

func deployStacksCommand() cli.Command {
	return cli.Command{
		Name: "stacks",
		Action: func(c *cli.Context) error {
			utils.LogSection("Deploying stacks")
			client := kontena.Client{}

			stacks, _ := ioutil.ReadDir("./stacks")
			for _, stack := range stacks {
				stackName := stack.Name()

				utils.Log("deploying stack", stackName)
				if err := client.StackDeploy(stackName); err != nil {
					return cli.NewExitError(err, 1)
				}
			}
			return nil
		},
	}
}

func getStackFromGrid(name string) (model.KontenaStack, error) {
	var k model.KontenaStack
	stackConfigPath := fmt.Sprintf("./stacks/%s/kontena.yml", name)
	if _, err := os.Stat(stackConfigPath); err != nil {
		return k, err
	}
	return model.KontenaLoad(stackConfigPath)
}

func getDefaultStack(name string, hasHost bool) model.KontenaStack {
	secrets := []model.KontenaSecret{}
	links := []string{}

	if hasHost {
		hostSecret := model.KontenaSecret{
			Secret: "VIRTUAL_HOSTS",
			Name:   "KONTENA_LB_VIRTUAL_HOSTS",
			Type:   "env",
		}
		secrets = append(secrets, hostSecret)
		links = append(links, "core/internet_lb")
	}

	return model.KontenaStack{
		Name:    name,
		Version: "0.0.1",
		Services: map[string]model.KontenaService{
			"web": model.KontenaService{
				Image:   "ksdn117/test-page",
				Links:   links,
				Secrets: secrets,
			},
		},
	}
}
<commit_msg>Add core installation before certificates command<commit_after>package cmd

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/jakubknejzlik/kontena-git-cli/kontena"
	"github.com/jakubknejzlik/kontena-git-cli/model"
	"github.com/jakubknejzlik/kontena-git-cli/utils"

	"github.com/urfave/cli"
)

// GridCommand ...
func GridCommand() cli.Command {
	return cli.Command{
		Name: "grid",
		Subcommands: []cli.Command{
			gridInstallCommand(),
			gridDeployCommand(),
		},
	}
}

func gridInstallCommand() cli.Command {
	return cli.Command{
		Name:      "install",
		ArgsUsage: "GRID",
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "deploy",
				Usage: "automatically deploy all services",
			},
		},
		Action: func(c *cli.Context) error {
			client := kontena.Client{}
			grid := c.Args().First()

			if err := client.EnsureMasterLogin(); err != nil {
				return cli.NewExitError(err, 1)
			}

			if grid == "" {
				return cli.NewExitError("GRID argument not specified", 1)
			}

			if err := client.GridUse(grid); err != nil {
				return cli.NewExitError(err, 1)
			}

			// install stack if not already installed to be able to run installCertificatesCommand
			if client.StackExists("core") == false {
				if err := installCoreCommand().Run(c); err != nil {
					return cli.NewExitError(err, 1)
				}
			}

			if err := installCertificatesCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := installCoreCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := installRegistriesCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := pruneStacksCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := installOrUpgradeStacksCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			if c.Bool("deploy") {
				if err := deployStacksCommand().Run(c); err != nil {
					return cli.NewExitError(err, 1)
				}
			}

			return nil
		},
	}
}

func gridDeployCommand() cli.Command {
	return cli.Command{
		Name:        "deploy",
		ArgsUsage:   "GRID",
		Description: "Deploy all stacks in grid",
		Action: func(c *cli.Context) error {
			client := kontena.Client{}
			grid := c.Args().First()

			if err := client.EnsureMasterLogin(); err != nil {
				return cli.NewExitError(err, 1)
			}

			if grid == "" {
				return cli.NewExitError("GRID argument not specified", 1)
			}

			if err := client.GridUse(grid); err != nil {
				return cli.NewExitError(err, 1)
			}

			if err := deployStacksCommand().Run(c); err != nil {
				return cli.NewExitError(err, 1)
			}

			return nil
		},
	}
}

func installOrUpgradeStacksCommand() cli.Command {
	return cli.Command{
		Name: "stacks",
		Action: func(c *cli.Context) error {
			utils.LogSection("Installing/upgrading stacks")
			client := kontena.Client{}

			stacks, _ := ioutil.ReadDir("./stacks")
			for _, stack := range stacks {
				stackName := stack.Name()
				if err := client.SecretsImport(stackName, fmt.Sprintf("./stacks/%s/secrets.yml", stackName)); err != nil {
					return cli.NewExitError(err, 1)
				}
				if !client.StackExists(stackName) {
					utils.Log("installing stack", stackName)
					dc, stackErr := getStackFromGrid(stackName)
					if stackErr != nil {
						dc = getDefaultStack(stackName, client.SecretExists("VIRTUAL_HOSTS", stackName))
					}

					if err := client.StackInstall(dc); err != nil {
						return cli.NewExitError(err, 1)
					}
				} else {
					if stack, err := getStackFromGrid(stackName); err == nil {
						utils.Log("upgrading stack", stackName)
						if err := client.StackUpgrade(stack); err != nil {
							return cli.NewExitError(err, 1)
						}
					}
				}
				time.Sleep(time.Second * 1)
			}
			return nil
		},
	}
}

func deployStacksCommand() cli.Command {
	return cli.Command{
		Name: "stacks",
		Action: func(c *cli.Context) error {
			utils.LogSection("Deploying stacks")
			client := kontena.Client{}

			stacks, _ := ioutil.ReadDir("./stacks")
			for _, stack := range stacks {
				stackName := stack.Name()

				utils.Log("deploying stack", stackName)
				if err := client.StackDeploy(stackName); err != nil {
					return cli.NewExitError(err, 1)
				}
			}
			return nil
		},
	}
}

func getStackFromGrid(name string) (model.KontenaStack, error) {
	var k model.KontenaStack
	stackConfigPath := fmt.Sprintf("./stacks/%s/kontena.yml", name)
	if _, err := os.Stat(stackConfigPath); err != nil {
		return k, err
	}
	return model.KontenaLoad(stackConfigPath)
}

func getDefaultStack(name string, hasHost bool) model.KontenaStack {
	secrets := []model.KontenaSecret{}
	links := []string{}

	if hasHost {
		hostSecret := model.KontenaSecret{
			Secret: "VIRTUAL_HOSTS",
			Name:   "KONTENA_LB_VIRTUAL_HOSTS",
			Type:   "env",
		}
		secrets = append(secrets, hostSecret)
		links = append(links, "core/internet_lb")
	}

	return model.KontenaStack{
		Name:    name,
		Version: "0.0.1",
		Services: map[string]model.KontenaService{
			"web": model.KontenaService{
				Image:   "ksdn117/test-page",
				Links:   links,
				Secrets: secrets,
			},
		},
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"path"

	kubeClientModels "git.containerum.net/ch/kube-client/pkg/model"
	"github.com/blang/semver"
	"github.com/containerum/chkit/pkg/chkitErrors"
	"github.com/containerum/chkit/pkg/client"
	"github.com/containerum/chkit/pkg/model"
	"github.com/sirupsen/logrus"
	cli "gopkg.in/urfave/cli.v2"
)

const (
	Version        = "3.0.0-alpha"
	FlagConfigFile = "config"
	FlagAPIaddr    = "apiaddr"
)

func Run(args []string) error {
	log := logrus.New()
	log.Formatter = &logrus.TextFormatter{}
	log.Level = logrus.InfoLevel

	configPath, err := configPath()
	if err != nil {
		log.WithError(err).
			Errorf("error while getting homedir path")
		return err
	}
	var App = &cli.App{
		Name:    "chkit",
		Usage:   "containerum cli",
		Version: semver.MustParse(Version).String(),
		Action: func(ctx *cli.Context) error {
			log := getLog(ctx)
			switch err := setupConfig(ctx).(type) {
			case nil:
				//
			case chkitErrors.ErrMatcher:
				if err.Match(ErrInvalidUserInfo) {
					config := getConfig(ctx)
					user, err := login(ctx)
					if err != nil {
						return err
					}
					config.UserInfo = user
					setConfig(ctx, config)
				} else {
					return err
				}
			default:
				return err
			}
			if err := setupClient(ctx); err != nil {
				log.Fatalf("error while client setup: %v", err)
			}
			if err := persist(ctx); err != nil {
				log.Fatalf("%v", err)
			}
			clientConfig := getClient(ctx).Config
			log.Infof("Hello, %q!", clientConfig.Username)
			if err := mainActivity(ctx); err != nil {
				log.Fatalf("error in main activity: %v", err)
			}
			return nil
		},
		After: func(ctx *cli.Context) error {
			return nil
		},
		Metadata: map[string]interface{}{
			"client":     chClient.Client{},
			"configPath": configPath,
			"log":        log,
			"config":     model.Config{},
			"tokens":     kubeClientModels.Tokens{},
		},
		Commands: []*cli.Command{
			commandLogin,
			commandGet,
		},
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "config",
				Usage:   "config file",
				Aliases: []string{"c"},
				Value:   path.Join(configPath, "config.toml"),
			},
			&cli.StringFlag{
				Name:    "api",
				Usage:   "API address",
				Value:   "",
				Hidden:  true,
				EnvVars: []string{"CONTAINERUM_API"},
			},
			&cli.StringFlag{
				Name:   "test",
				Usage:  "test presets",
				Value:  "api",
				Hidden: false,
			},
		},
	}
	return App.Run(args)
}
<commit_msg>load tokens to setup client<commit_after>package cmd

import (
	"path"

	kubeClientModels "git.containerum.net/ch/kube-client/pkg/model"
	"github.com/blang/semver"
	"github.com/containerum/chkit/pkg/chkitErrors"
	"github.com/containerum/chkit/pkg/client"
	"github.com/containerum/chkit/pkg/model"
	"github.com/sirupsen/logrus"
	cli "gopkg.in/urfave/cli.v2"
)

const (
	Version        = "3.0.0-alpha"
	FlagConfigFile = "config"
	FlagAPIaddr    = "apiaddr"
)

func Run(args []string) error {
	log := logrus.New()
	log.Formatter = &logrus.TextFormatter{}
	log.Level = logrus.InfoLevel

	configPath, err := configPath()
	if err != nil {
		log.WithError(err).
			Errorf("error while getting homedir path")
		return err
	}
	var App = &cli.App{
		Name:    "chkit",
		Usage:   "containerum cli",
		Version: semver.MustParse(Version).String(),
		Action: func(ctx *cli.Context) error {
			log := getLog(ctx)
			switch err := setupConfig(ctx).(type) {
			case nil:
				//
			case chkitErrors.ErrMatcher:
				if err.Match(ErrInvalidUserInfo) {
					config := getConfig(ctx)
					user, err := login(ctx)
					if err != nil {
						return err
					}
					config.UserInfo = user
					setConfig(ctx, config)
				} else {
					return err
				}
			default:
				return err
			}
			if err := setupClient(ctx); err != nil {
				log.Fatalf("error while client setup: %v", err)
			}
			tokens, err := loadTokens(ctx)
			if err != nil {
				return chkitErrors.NewExitCoder(err)
			}
			client := getClient(ctx)
			client.Tokens = tokens
			if err := client.Auth(); err != nil {
				return err
			}
			if err := persist(ctx); err != nil {
				log.Fatalf("%v", err)
			}
			if err := saveTokens(ctx, tokens); err != nil {
				return chkitErrors.NewExitCoder(err)
			}
			clientConfig := client.Config
			log.Infof("Hello, %q!", clientConfig.Username)
			if err := mainActivity(ctx); err != nil {
				log.Fatalf("error in main activity: %v", err)
			}
			return nil
		},
		After: func(ctx *cli.Context) error {
			return nil
		},
		Metadata: map[string]interface{}{
			"client":     chClient.Client{},
			"configPath": configPath,
			"log":        log,
			"config":     model.Config{},
			"tokens":     kubeClientModels.Tokens{},
		},
		Commands: []*cli.Command{
			commandLogin,
			commandGet,
		},
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "config",
				Usage:   "config file",
				Aliases: []string{"c"},
				Value:   path.Join(configPath, "config.toml"),
			},
			&cli.StringFlag{
				Name:    "api",
				Usage:   "API address",
				Value:   "",
				Hidden:  true,
				EnvVars: []string{"CONTAINERUM_API"},
			},
			&cli.StringFlag{
				Name:   "test",
				Usage:  "test presets",
				Value:  "api",
				Hidden: false,
			},
		},
	}
	return App.Run(args)
}
<|endoftext|>


<commit_before>package cmd

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

	"github.com/spf13/cobra"
)

var logTime bool
var separate bool
var tail bool

// logsCmd represents the logs command
var logsCmd = &cobra.Command{
	Use:   "logs",
	Short: "View output from containers",
	Long:  ``,
	Run:   logs,
}

func init() {
	logsCmd.PersistentFlags().BoolVarP(&logTime, "time", "T", false, "append time to logs")
	logsCmd.PersistentFlags().BoolVarP(&separate, "separate", "s", false, "print logs by each container")
	logsCmd.PersistentFlags().BoolVarP(&tail, "tail", "t", false, "continue to stream log output to stdout.")
	RootCmd.AddCommand(logsCmd)
}

// logs cli command
// Usage: run inside folder with harbor-compose.yml file
// Flags: -t: adds time to the logs
// TODO: add the rest of the flags to match docker-compose
func logs(cmd *cobra.Command, args []string) {
	//read the harbor compose file
	var harborCompose = DeserializeHarborCompose(HarborComposeFile)
	//iterate shipments
	for shipmentName, shipment := range harborCompose.Shipments {
		fmt.Println("Logs For:  " + shipmentName + " " + shipment.Env)
		helmitObject := HelmitResponse{}
		var response = GetLogs(shipment.Barge, shipmentName, shipment.Env)
		err := json.Unmarshal([]byte(response), &helmitObject)
		if err != nil {
			fmt.Println(err)
		}

		if separate == true {
			printSeparateLogs(helmitObject)
		} else {
			printMergedLogs(helmitObject)
		}
	}
}

// logsObject that contains a containers logs
type logsObject struct {
	Name      string
	ID        string
	Image     string
	Logstream string
	Logs      Logs
}

// logObject is a log object
type logObject struct {
	Time time.Time
	Log  string
}

// Logs is a list
type Logs []logObject

func (slice Logs) Len() int {
	return len(slice)
}

func (slice Logs) Less(i, j int) bool {
	return slice[i].Time.Before(slice[j].Time)
}

func (slice Logs) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

// parseContainerLog will parse a log from docker and create an object containing needed information
func parseContainerLog(log string) (logObj logObject, errstring string) {
	layout := time.RFC3339
	line := strings.Fields(log)
	errstring = ""
	if len(line) > 2 {
		timeValue, err := time.Parse(layout, line[0])
		if err != nil {
			errstring = "Could not Parse"
		}

		logObj.Time = timeValue
		line = append(line[:0], line[1:]...)
		logObj.Log = strings.Join(line, " ")
	} else {
		errstring = "Format was off."
	}

	return
}

func printMergedLogs(shipment HelmitResponse) {
	shipmentLogs := []logsObject{}
	for _, provider := range shipment.Replicas {
		for _, container := range provider.Containers {
			var containerLogs = Logs{}
			for _, logstring := range container.Logs {
				parsedLog, err := parseContainerLog(logstring)
				if err != "" {
					continue
				}
				containerLogs = append(containerLogs, parsedLog)
			}

			// set current log object
			var logsObject = logsObject{}
			logsObject.Name = container.Name
			logsObject.ID = container.ID
			logsObject.Image = container.Image
			logsObject.Logs = containerLogs
			logsObject.Logstream = container.Logstream
			shipmentLogs = append(shipmentLogs, logsObject)

		}
	}

	var mergedLogs Logs
	for _, logObject := range shipmentLogs {
		for _, logObj := range logObject.Logs {
			newLog := logObject.Name + ":" + logObject.ID[0:5] + "  | "
			if logTime == true {
				newLog = newLog + logObj.Time.String() + ", "
			}

			logObj.Log = newLog + logObj.Log + "\n"
			mergedLogs = append(mergedLogs, logObj)
		}
	}

	sort.Sort(mergedLogs)

	for _, log := range mergedLogs {
		fmt.Printf(log.Log)
	}

	if tail == true {
		for _, streamObj := range shipmentLogs {
			go followStream(streamObj)
		}
		var input string
		fmt.Scanln(&input)
	}
}

// followStream will take a logsObject param and print out all data that comes from the
// logStream field. This is a normal http response, which never ends.
func followStream(streamObj logsObject) {
	stream := strings.Replace(streamObj.Logstream, "tail=500", "tail=0", -1)
	streamer, err := GetLogStreamer(stream)

	if err != nil {
		log.Fatal(err)
	}
	for {
		line, streamErr := streamer.ReadBytes('\n')

		if streamErr != nil {
			log.Fatal(streamErr)
		}

		logObj, err := parseContainerLog(string(line)[8:])

		if err != "" {
			fmt.Println(err)
			fmt.Println(string(line))
			continue
		}

		newLog := streamObj.Name + ":" + streamObj.ID[0:5] + "  | "
		if logTime == true {
			newLog = newLog + logObj.Time.String() + ", "
		}

		logObj.Log = newLog + logObj.Log
		fmt.Println(logObj.Log)
	}
}

// printShipmentLogs
// prints the logs separatly for each shipment
func printSeparateLogs(shipment HelmitResponse) {
	for _, provider := range shipment.Replicas {
		for _, container := range provider.Containers {
			fmt.Printf("--- Name: %s\n", container.Name)
			fmt.Printf("--- Id: %s\n", container.ID)
			fmt.Printf("--- Image %s\n", container.Image)
			for _, log := range container.Logs {
				line := strings.Fields(log)

				if len(line) > 2 && logTime == false {
					line = append(line[:0], line[1:]...)
				}

				fmt.Println(strings.Join(line, " "))
			}

			if tail == true {
				logsObj := logsObject{}
				logsObj.Logstream = container.Logstream
				logsObj.Name = container.Name
				logsObj.ID = container.ID
				go followStream(logsObj)
			}
		}
	}

	if tail == true {
		var input string
		fmt.Scanln(&input)
	}
}
<commit_msg>feat(logs): allow for a specific query of one to n containers (#54)<commit_after>package cmd

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

	"github.com/spf13/cobra"
)

var logTime bool
var separate bool
var tail bool

// logsCmd represents the logs command
var logsCmd = &cobra.Command{
	Use:   "logs [logid ...]",
	Short: "View output from containers",
	Long: `
    View output of contianers. There are few options available to make this easier to view.

    You can also pass in arguments to the function, to allow for n number of specific queries.
    eg. logs $id $id1 $id2

    This will query for only those 3 containers. You can pass in any number of container IDs
	`,
	Run: logs,
}

func init() {
	logsCmd.PersistentFlags().BoolVarP(&logTime, "time", "T", false, "append time to logs")
	logsCmd.PersistentFlags().BoolVarP(&separate, "separate", "s", false, "print logs by each container")
	logsCmd.PersistentFlags().BoolVarP(&tail, "tail", "t", false, "continue to stream log output to stdout.")
	RootCmd.AddCommand(logsCmd)
}

// logs cli command
// Usage: run inside folder with harbor-compose.yml file
// Flags: -t: adds time to the logs
// TODO: add the rest of the flags to match docker-compose
func logs(cmd *cobra.Command, args []string) {
	//read the harbor compose file
	var harborCompose = DeserializeHarborCompose(HarborComposeFile)
	//iterate shipments
	for shipmentName, shipment := range harborCompose.Shipments {
		fmt.Printf("Logs For:  %s %s\n", shipmentName, shipment.Env)

		if len(args) > 0 && Verbose == true {
			fmt.Println("Make sure the ID is either the 7 char shortstring of the contianer or the entire ID")
			for _, arg := range args {
				fmt.Printf("Getting Logs for Container:  %s\n", arg)
			}

		}
		helmitObject := HelmitResponse{}
		var response = GetLogs(shipment.Barge, shipmentName, shipment.Env)
		err := json.Unmarshal([]byte(response), &helmitObject)
		if err != nil {
			fmt.Println(err)
		}

		fmt.Println(args)

		if separate == true {
			printSeparateLogs(helmitObject, args)
		} else {
			printMergedLogs(helmitObject, args)
		}
	}
}

// logsObject that contains a containers logs
type logsObject struct {
	Name      string
	ID        string
	Image     string
	Logstream string
	Logs      Logs
}

// logObject is a log object
type logObject struct {
	Time time.Time
	Log  string
}

// Logs is a list
type Logs []logObject

func (slice Logs) Len() int {
	return len(slice)
}

func (slice Logs) Less(i, j int) bool {
	return slice[i].Time.Before(slice[j].Time)
}

func (slice Logs) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

// check to see if string is in the array
// http://stackoverflow.com/questions/15323767/does-golang-have-if-x-in-construct-similar-to-python
func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

// parseContainerLog will parse a log from docker and create an object containing needed information
func parseContainerLog(log string) (logObj logObject, errstring string) {
	layout := time.RFC3339
	line := strings.Fields(log)
	errstring = ""
	if len(line) > 2 {
		timeValue, err := time.Parse(layout, line[0])
		if err != nil {
			errstring = "Could not Parse"
		}

		logObj.Time = timeValue
		line = append(line[:0], line[1:]...)
		logObj.Log = strings.Join(line, " ")
	} else {
		errstring = "Format was off."
	}

	return
}

func printMergedLogs(shipment HelmitResponse, ids []string) {
	shipmentLogs := []logsObject{}
	for _, provider := range shipment.Replicas {
		for _, container := range provider.Containers {

			if len(ids) > 0 && !stringInSlice(container.ID, ids) && !stringInSlice(container.ID[0:7], ids) {
				continue
			}

			var containerLogs = Logs{}
			for _, logstring := range container.Logs {
				parsedLog, err := parseContainerLog(logstring)
				if err != "" {
					continue
				}
				containerLogs = append(containerLogs, parsedLog)
			}

			// set current log object
			var logsObject = logsObject{}
			logsObject.Name = container.Name
			logsObject.ID = container.ID
			logsObject.Image = container.Image
			logsObject.Logs = containerLogs
			logsObject.Logstream = container.Logstream
			shipmentLogs = append(shipmentLogs, logsObject)

		}
	}

	var mergedLogs Logs
	for _, logObject := range shipmentLogs {
		for _, logObj := range logObject.Logs {
			newLog := logObject.Name + ":" + logObject.ID[0:7] + "  | "
			if logTime == true {
				newLog = newLog + logObj.Time.String() + ", "
			}

			logObj.Log = newLog + logObj.Log + "\n"
			mergedLogs = append(mergedLogs, logObj)
		}
	}

	sort.Sort(mergedLogs)

	for _, log := range mergedLogs {
		fmt.Printf(log.Log)
	}

	if tail == true {
		for _, streamObj := range shipmentLogs {
			go followStream(streamObj)
		}
		var input string
		fmt.Scanln(&input)
	}
}

// followStream will take a logsObject param and print out all data that comes from the
// logStream field. This is a normal http response, which never ends.
func followStream(streamObj logsObject) {
	stream := strings.Replace(streamObj.Logstream, "tail=500", "tail=0", -1)
	streamer, err := GetLogStreamer(stream)

	if err != nil {
		log.Fatal(err)
	}
	for {
		line, streamErr := streamer.ReadBytes('\n')

		if streamErr != nil {
			log.Fatal(streamErr)
		}

		logObj, err := parseContainerLog(string(line)[8:])

		if err != "" {
			fmt.Println(err)
			fmt.Println(string(line))
			continue
		}

		newLog := streamObj.Name + ":" + streamObj.ID[0:7] + "  | "
		if logTime == true {
			newLog = newLog + logObj.Time.String() + ", "
		}

		logObj.Log = newLog + logObj.Log
		fmt.Println(logObj.Log)
	}
}

// printShipmentLogs
// prints the logs separatly for each shipment
func printSeparateLogs(shipment HelmitResponse, ids []string) {
	for _, provider := range shipment.Replicas {
		for _, container := range provider.Containers {

			if len(ids) > 0 && !stringInSlice(container.ID, ids) && !stringInSlice(container.ID[0:7], ids) {
				continue
			}

			fmt.Printf("--- Name: %s\n", container.Name)
			fmt.Printf("--- Id: %s\n", container.ID)
			fmt.Printf("--- Image %s\n", container.Image)
			for _, log := range container.Logs {
				line := strings.Fields(log)

				if len(line) > 2 && logTime == false {
					line = append(line[:0], line[1:]...)
				}

				fmt.Println(strings.Join(line, " "))
			}

			if tail == true {
				logsObj := logsObject{}
				logsObj.Logstream = container.Logstream
				logsObj.Name = container.Name
				logsObj.ID = container.ID
				go followStream(logsObj)
			}
		}
	}

	if tail == true {
		var input string
		fmt.Scanln(&input)
	}
}
<|endoftext|>


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

package cmd

import (
	"fmt"
	"io"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

var (
	Version   string
	BuildTime string
)

// TODO maybe give this an Add method which will ensure two command
// with same name aren't added
var subcommandFns = map[string]func(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command{}

func NewRootCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command {
	setupVersionBuild()
	rc := &cobra.Command{
		Use:   "pilosa",
		Short: "Pilosa - A Distributed In-memory Binary Bitmap Index.",
		// TODO - is documentation actually there?
		Long: `Pilosa is a fast index to turbocharge your database.

This binary contains Pilosa itself, as well as common
tools for administering pilosa, importing/exporting data,
backing up, and more. Complete documentation is available
at http://pilosa.com/docs

Version: ` + Version + `
Build Time: ` + BuildTime + "\n",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			v := viper.New()
			err := setAllConfig(v, cmd.Flags(), "PILOSA")
			if err != nil {
				return err
			}

			// return "dry run" error if "dry-run" flag is set
			if ret, err := cmd.Flags().GetBool("dry-run"); ret && err == nil {
				if cmd.Parent() != nil {
					return fmt.Errorf("dry run")
				} else if err != nil {
					return fmt.Errorf("problem getting dry-run flag: %v", err)
				}
			}

			return nil
		},
	}
	rc.PersistentFlags().Bool("dry-run", false, "stop before executing")
	_ = rc.PersistentFlags().MarkHidden("dry-run")
	rc.PersistentFlags().StringP("config", "c", "", "Configuration file to read from.")
	for _, subcomFn := range subcommandFns {
		rc.AddCommand(subcomFn(stdin, stdout, stderr))
	}
	rc.SetOutput(stderr)
	return rc
}

func setupVersionBuild() {
	if Version == "" {
		Version = "v0.0.0"
	}
	if BuildTime == "" {
		BuildTime = "not recorded"
	}
}

// setAllConfig takes a FlagSet to be the definition of all configuration
// options, as well as their defaults. It then reads from the command line, the
// environment, and a config file (if specified), and applies the configuration
// in that priority order. Since each flag in the set contains a pointer to
// where its value should be stored, setAllConfig can directly modify the value
// of each config variable.
//
// setAllConfig looks for environment variables which are capitalized versions
// of the flag names with dashes replaced by underscores, and prefixed with
// envPrefix plus an underscore.
func setAllConfig(v *viper.Viper, flags *pflag.FlagSet, envPrefix string) error {
	// add cmd line flag def to viper
	err := v.BindPFlags(flags)
	if err != nil {
		return err
	}

	// add env to viper
	v.SetEnvPrefix(envPrefix)
	v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
	v.AutomaticEnv()

	c := v.GetString("config")

	// add config file to viper
	if c != "" {
		v.SetConfigFile(c)
		v.SetConfigType("toml")
		err := v.ReadInConfig()
		if err != nil {
			return fmt.Errorf("error reading configuration file '%s': %v", c, err)
		}
	}

	// set all values from viper
	var flagErr error
	flags.VisitAll(func(f *pflag.Flag) {
		if flagErr != nil {
			return
		}
		var value string
		if f.Value.Type() == "stringSlice" {
			// special handling is needed for stringSlice as v.GetString will
			// always return "" in the case that the value is an actual string
			// slice from a config file rather than a comma separated string
			// from a flag or env var.
			vss := v.GetStringSlice(f.Name)
			value = strings.Join(vss, ",")
		} else {
			value = v.GetString(f.Name)
		}

		if f.Changed {
			// If f.Changed is true, that means the value has already been set
			// by a flag, and we don't need to ask viper for it since the flag
			// is the highest priority. This works around a problem with string
			// slices where f.Value.Set(csvString) would cause the elements of
			// csvString to be appended to the existing value rather than
			// replacing it.
			return
		}
		flagErr = f.Value.Set(value)
	})
	return flagErr
}
<commit_msg>Correct documentation link<commit_after>// Copyright 2017 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"
	"io"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

var (
	Version   string
	BuildTime string
)

// TODO maybe give this an Add method which will ensure two command
// with same name aren't added
var subcommandFns = map[string]func(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command{}

func NewRootCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command {
	setupVersionBuild()
	rc := &cobra.Command{
		Use:   "pilosa",
		Short: "Pilosa - A Distributed In-memory Binary Bitmap Index.",
		// TODO - is documentation actually there?
		Long: `Pilosa is a fast index to turbocharge your database.

This binary contains Pilosa itself, as well as common
tools for administering pilosa, importing/exporting data,
backing up, and more. Complete documentation is available
at https://www.pilosa.com/docs/

Version: ` + Version + `
Build Time: ` + BuildTime + "\n",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			v := viper.New()
			err := setAllConfig(v, cmd.Flags(), "PILOSA")
			if err != nil {
				return err
			}

			// return "dry run" error if "dry-run" flag is set
			if ret, err := cmd.Flags().GetBool("dry-run"); ret && err == nil {
				if cmd.Parent() != nil {
					return fmt.Errorf("dry run")
				} else if err != nil {
					return fmt.Errorf("problem getting dry-run flag: %v", err)
				}
			}

			return nil
		},
	}
	rc.PersistentFlags().Bool("dry-run", false, "stop before executing")
	_ = rc.PersistentFlags().MarkHidden("dry-run")
	rc.PersistentFlags().StringP("config", "c", "", "Configuration file to read from.")
	for _, subcomFn := range subcommandFns {
		rc.AddCommand(subcomFn(stdin, stdout, stderr))
	}
	rc.SetOutput(stderr)
	return rc
}

func setupVersionBuild() {
	if Version == "" {
		Version = "v0.0.0"
	}
	if BuildTime == "" {
		BuildTime = "not recorded"
	}
}

// setAllConfig takes a FlagSet to be the definition of all configuration
// options, as well as their defaults. It then reads from the command line, the
// environment, and a config file (if specified), and applies the configuration
// in that priority order. Since each flag in the set contains a pointer to
// where its value should be stored, setAllConfig can directly modify the value
// of each config variable.
//
// setAllConfig looks for environment variables which are capitalized versions
// of the flag names with dashes replaced by underscores, and prefixed with
// envPrefix plus an underscore.
func setAllConfig(v *viper.Viper, flags *pflag.FlagSet, envPrefix string) error {
	// add cmd line flag def to viper
	err := v.BindPFlags(flags)
	if err != nil {
		return err
	}

	// add env to viper
	v.SetEnvPrefix(envPrefix)
	v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
	v.AutomaticEnv()

	c := v.GetString("config")

	// add config file to viper
	if c != "" {
		v.SetConfigFile(c)
		v.SetConfigType("toml")
		err := v.ReadInConfig()
		if err != nil {
			return fmt.Errorf("error reading configuration file '%s': %v", c, err)
		}
	}

	// set all values from viper
	var flagErr error
	flags.VisitAll(func(f *pflag.Flag) {
		if flagErr != nil {
			return
		}
		var value string
		if f.Value.Type() == "stringSlice" {
			// special handling is needed for stringSlice as v.GetString will
			// always return "" in the case that the value is an actual string
			// slice from a config file rather than a comma separated string
			// from a flag or env var.
			vss := v.GetStringSlice(f.Name)
			value = strings.Join(vss, ",")
		} else {
			value = v.GetString(f.Name)
		}

		if f.Changed {
			// If f.Changed is true, that means the value has already been set
			// by a flag, and we don't need to ask viper for it since the flag
			// is the highest priority. This works around a problem with string
			// slices where f.Value.Set(csvString) would cause the elements of
			// csvString to be appended to the existing value rather than
			// replacing it.
			return
		}
		flagErr = f.Value.Set(value)
	})
	return flagErr
}
<|endoftext|>