<commit_before>package update

import (
	"errors"
	"fmt"
	"net/url"
	"strconv"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/flags"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

type configVariable struct {
	name        string
	configName  string
	description string
	validate    func(*app.Context, string) error
	needAuth    bool
}

type configVars []configVariable

var configVariables = configVars{
	{
		name:        "endpoint",
		description: "brain endpoint to connect to",
		validate:    validateEndpointForConfigFunc("endpoint"),
	},
	{
		name:        "api-endpoint",
		description: "endpoint for domains",
		validate:    validateEndpointForConfigFunc("api-endpoint"),
	},
	{
		name:        "billing-endpoint",
		description: "billing API endpoint to connect to",
		validate:    validateEndpointForConfigFunc("billing-endpoint"),
	},
	{
		name:        "spp-endpoint",
		description: "SPP endpoint to use",
		validate:    validateEndpointForConfigFunc("spp-endpoint"),
	},
	{
		name:        "auth-endpoint",
		description: "endpoint to authenticate to",
		validate:    validateEndpointForConfigFunc("auth-endpoint"),
	},
	{
		name:        "default-debug-level",
		configName:  "debug_level",
		description: "default debug level",
		validate:    validateIntForConfigFunc("default-debug-level"),
	},
	{
		name:        "token",
		description: "token used for authentication",
	},
	{
		name:        "user",
		description: "user that you log in as by default",
	},
	{
		name:        "account",
		description: "default account",
		validate:    validateAccountForConfig,
		needAuth:    true,
	},
	{
		name:        "group",
		description: "default group",
		validate:    validateGroupForConfig,
		needAuth:    true,
	},
}

func (variable configVariable) confName() string {
	if variable.configName != "" {
		return variable.configName
	}
	return variable.name
}

func (variable configVariable) getFlags(c *app.Context) (string, bool) {
	return c.String(variable.name), c.Bool("unset-" + variable.name)
}

func (variable configVariable) present(c *app.Context) bool {
	set, unset := variable.getFlags(c)
	return set != "" || unset
}

func (variables configVars) present(c *app.Context) (out configVars) {
	out = configVars{}
	for _, variable := range variables {
		if variable.present(c) {
			out = append(out, variable)
		}
	}
	return
}

func (variables configVars) configFlags() (flags []cli.Flag) {
	flags = make([]cli.Flag, len(variables)*2)
	for i, variable := range variables {
		flags[i*2] = cli.StringFlag{
			Name:  variable.name,
			Usage: "Sets the " + variable.description,
		}
		flags[i*2+1] = cli.BoolFlag{
			Name:  "unset-" + variable.name,
			Usage: "Unsets the " + variable.description,
		}
	}
	return
}

func validateAccountForConfig(c *app.Context, name string) (err error) {
	_, err = c.Client().GetAccount(name)
	if err != nil {
		if _, ok := err.(lib.NotFoundError); ok {
			return fmt.Errorf("No such account %s - check your typing and specify --yubikey if necessary", name)
		}
		return err
	}
	return
}

func validateGroupForConfig(c *app.Context, name string) (err error) {
	groupName := lib.ParseGroupName(name, c.Config().GetGroup())
	_, err = c.Client().GetGroup(groupName)
	if err != nil {
		if _, ok := err.(lib.NotFoundError); ok {
			return fmt.Errorf("No such group %v - check your typing and specify --yubikey if necessary", groupName)
		}
		return err
	}
	return
}

func validateEndpointForConfigFunc(variable string) func(*app.Context, string) error {
	return func(c *app.Context, endpoint string) error {
		url, err := url.Parse(endpoint)
		if err != nil {
			return err
		}
		if url.Scheme != "http" && url.Scheme != "https" {
			return errors.New(variable + " URL should start with http:// or https://")
		}
		if url.Host == "" {
			return errors.New(variable + " URL should have a hostname")
		}
		return nil
	}
}

func validateIntForConfigFunc(variable string) func(*app.Context, string) error {
	return func(c *app.Context, value string) error {
		_, err := strconv.ParseUint(value, 10, 32)
		if err != nil {
			return errors.New(variable + " must be an integer")
		}
		return nil
	}
}

func init() {
	Commands = append(Commands, cli.Command{
		Name:      "config",
		Usage:     "manage the bytemark client's configuration",
		UsageText: "update config [flags]",
		Description: `Manipulate the bytemark-client configuration

    Available variables:
        account - the default account, used when you do not explicitly state an account - defaults to the same as your user name
        token - the token used for authentication
        user - the user that you log in as by default
        group - the default group, used when you do not explicitly state a group (defaults to 'default')

        debug-level - the default debug level. Set to 0 unless you like lots of output.
	api-endpoint - the endpoint for domains (among other things?)
        auth-endpoint - the endpoint to authenticate to. https://auth.bytemark.co.uk is the default.
        endpoint - the brain endpoint to connect to. https://uk0.bigv.io is the default.
        billing-endpoint - the billing API endpoint to connect to. https://bmbilling.bytemark.co.uk is the default.
        spp-endpoint - the SPP endpoint to use. https://spp-submissions.bytemark.co.uk is the default.`,
		Flags:  append(configVariables.configFlags(), flags.Force),
		Action: app.Action(configVariables.updateConfig),
	})
}

func (variables configVars) updateConfig(c *app.Context) error {
	presentVariables := variables.present(c)
	withAuth := false
	// first pass, validate
	if len(presentVariables) == 0 {
		return c.Help("missing arguments")
	}
	for _, variable := range presentVariables {
		set, unset := variable.getFlags(c)
		if set != "" && unset {
			return c.Help("cannot set and unset " + variable.name)
		}
		if set != "" && !flags.Forced(c) && variable.validate != nil {
			if variable.needAuth && !withAuth {
				err := with.Auth(c)
				if err != nil {
					return err
				}
				withAuth = true
			}
			err := variable.validate(c, set)
			if err != nil {
				return err
			}
		}
	}
	// second pass, apply
	for _, variable := range presentVariables {
		set, unset := variable.getFlags(c)
		if unset {
			err := c.Config().Unset(variable.name)
			if err != nil {
				return err
			}
			log.Logf("%s has been unset. \r\n", variable.name)
		} else {
			oldVar, err := c.Config().GetV(variable.name)
			if err != nil {
				return err
			}
			err = c.Config().SetPersistent(variable.name, set, "CMD set")
			if err != nil {
				return err
			}

			if oldVar.Source == "config" {
				log.Logf("%s has been changed.\r\nOld value: %s\r\nNew value: %s\r\n", variable.name, oldVar.Value, c.Config().GetIgnoreErr(variable.name))
			} else {
				log.Logf("%s has been set. \r\nNew value: %s\r\n", variable.name, c.Config().GetIgnoreErr(variable.name))
			}
		}
	}
	return nil
}
<commit_msg>use confName() when calling Config<commit_after>package update

import (
	"errors"
	"fmt"
	"net/url"
	"strconv"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/flags"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

type configVariable struct {
	name        string
	configName  string
	description string
	validate    func(*app.Context, string) error
	needAuth    bool
}

type configVars []configVariable

var configVariables = configVars{
	{
		name:        "endpoint",
		description: "brain endpoint to connect to",
		validate:    validateEndpointForConfigFunc("endpoint"),
	},
	{
		name:        "api-endpoint",
		description: "endpoint for domains",
		validate:    validateEndpointForConfigFunc("api-endpoint"),
	},
	{
		name:        "billing-endpoint",
		description: "billing API endpoint to connect to",
		validate:    validateEndpointForConfigFunc("billing-endpoint"),
	},
	{
		name:        "spp-endpoint",
		description: "SPP endpoint to use",
		validate:    validateEndpointForConfigFunc("spp-endpoint"),
	},
	{
		name:        "auth-endpoint",
		description: "endpoint to authenticate to",
		validate:    validateEndpointForConfigFunc("auth-endpoint"),
	},
	{
		name:        "default-debug-level",
		configName:  "debug_level",
		description: "default debug level",
		validate:    validateIntForConfigFunc("default-debug-level"),
	},
	{
		name:        "token",
		description: "token used for authentication",
	},
	{
		name:        "user",
		description: "user that you log in as by default",
	},
	{
		name:        "account",
		description: "default account",
		validate:    validateAccountForConfig,
		needAuth:    true,
	},
	{
		name:        "group",
		description: "default group",
		validate:    validateGroupForConfig,
		needAuth:    true,
	},
}

func (variable configVariable) confName() string {
	if variable.configName != "" {
		return variable.configName
	}
	return variable.name
}

func (variable configVariable) getFlags(c *app.Context) (string, bool) {
	return c.String(variable.name), c.Bool("unset-" + variable.name)
}

func (variable configVariable) present(c *app.Context) bool {
	set, unset := variable.getFlags(c)
	return set != "" || unset
}

func (variables configVars) present(c *app.Context) (out configVars) {
	out = configVars{}
	for _, variable := range variables {
		if variable.present(c) {
			out = append(out, variable)
		}
	}
	return
}

func (variables configVars) configFlags() (flags []cli.Flag) {
	flags = make([]cli.Flag, len(variables)*2)
	for i, variable := range variables {
		flags[i*2] = cli.StringFlag{
			Name:  variable.name,
			Usage: "Sets the " + variable.description,
		}
		flags[i*2+1] = cli.BoolFlag{
			Name:  "unset-" + variable.name,
			Usage: "Unsets the " + variable.description,
		}
	}
	return
}

func validateAccountForConfig(c *app.Context, name string) (err error) {
	_, err = c.Client().GetAccount(name)
	if err != nil {
		if _, ok := err.(lib.NotFoundError); ok {
			return fmt.Errorf("No such account %s - check your typing and specify --yubikey if necessary", name)
		}
		return err
	}
	return
}

func validateGroupForConfig(c *app.Context, name string) (err error) {
	groupName := lib.ParseGroupName(name, c.Config().GetGroup())
	_, err = c.Client().GetGroup(groupName)
	if err != nil {
		if _, ok := err.(lib.NotFoundError); ok {
			return fmt.Errorf("No such group %v - check your typing and specify --yubikey if necessary", groupName)
		}
		return err
	}
	return
}

func validateEndpointForConfigFunc(variable string) func(*app.Context, string) error {
	return func(c *app.Context, endpoint string) error {
		url, err := url.Parse(endpoint)
		if err != nil {
			return err
		}
		if url.Scheme != "http" && url.Scheme != "https" {
			return errors.New(variable + " URL should start with http:// or https://")
		}
		if url.Host == "" {
			return errors.New(variable + " URL should have a hostname")
		}
		return nil
	}
}

func validateIntForConfigFunc(variable string) func(*app.Context, string) error {
	return func(c *app.Context, value string) error {
		_, err := strconv.ParseUint(value, 10, 32)
		if err != nil {
			return errors.New(variable + " must be an integer")
		}
		return nil
	}
}

func init() {
	Commands = append(Commands, cli.Command{
		Name:      "config",
		Usage:     "manage the bytemark client's configuration",
		UsageText: "update config [flags]",
		Description: `Manipulate the bytemark-client configuration

    Available variables:
        account - the default account, used when you do not explicitly state an account - defaults to the same as your user name
        token - the token used for authentication
        user - the user that you log in as by default
        group - the default group, used when you do not explicitly state a group (defaults to 'default')

        debug-level - the default debug level. Set to 0 unless you like lots of output.
	api-endpoint - the endpoint for domains (among other things?)
        auth-endpoint - the endpoint to authenticate to. https://auth.bytemark.co.uk is the default.
        endpoint - the brain endpoint to connect to. https://uk0.bigv.io is the default.
        billing-endpoint - the billing API endpoint to connect to. https://bmbilling.bytemark.co.uk is the default.
        spp-endpoint - the SPP endpoint to use. https://spp-submissions.bytemark.co.uk is the default.`,
		Flags:  append(configVariables.configFlags(), flags.Force),
		Action: app.Action(configVariables.updateConfig),
	})
}

func (variables configVars) updateConfig(c *app.Context) error {
	presentVariables := variables.present(c)
	withAuth := false
	// first pass, validate
	if len(presentVariables) == 0 {
		return c.Help("missing arguments")
	}
	for _, variable := range presentVariables {
		set, unset := variable.getFlags(c)
		if set != "" && unset {
			return c.Help("cannot set and unset " + variable.name)
		}
		if set != "" && !flags.Forced(c) && variable.validate != nil {
			if variable.needAuth && !withAuth {
				err := with.Auth(c)
				if err != nil {
					return err
				}
				withAuth = true
			}
			err := variable.validate(c, set)
			if err != nil {
				return err
			}
		}
	}
	// second pass, apply
	for _, variable := range presentVariables {
		set, unset := variable.getFlags(c)
		if unset {
			err := c.Config().Unset(variable.confName())
			if err != nil {
				return err
			}
			log.Logf("%s has been unset. \r\n", variable.name)
		} else {
			oldVar, err := c.Config().GetV(variable.confName())
			if err != nil {
				return err
			}
			err = c.Config().SetPersistent(variable.confName(), set, "CMD set")
			if err != nil {
				return err
			}

			if oldVar.Source == "config" {
				log.Logf("%s has been changed.\r\nOld value: %s\r\nNew value: %s\r\n", variable.confName(), oldVar.Value, c.Config().GetIgnoreErr(variable.confName()))
			} else {
				log.Logf("%s has been set. \r\nNew value: %s\r\n", variable.confName(), c.Config().GetIgnoreErr(variable.confName()))
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package config

import (
	"encoding/json"
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/assets"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/mustload"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/reason"
	"k8s.io/minikube/pkg/minikube/style"
)

var addonListOutput string
var addonPrintDocs bool

// AddonListTemplate represents the addon list template
type AddonListTemplate struct {
	AddonName   string
	AddonStatus string
}

var addonsListCmd = &cobra.Command{
	Use:   "list",
	Short: "Lists all available minikube addons as well as their current statuses (enabled/disabled)",
	Long:  "Lists all available minikube addons as well as their current statuses (enabled/disabled)",
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) != 0 {
			exit.Message(reason.Usage, "usage: minikube addons list")
		}

		var cc *config.ClusterConfig
		if config.ProfileExists(ClusterFlagValue()) {
			_, cc = mustload.Partial(ClusterFlagValue())
		}
		switch strings.ToLower(addonListOutput) {
		case "list":
			printAddonsList(cc, addonPrintDocs)
		case "json":
			printAddonsJSON(cc)
		default:
			exit.Message(reason.Usage, fmt.Sprintf("invalid output format: %s. Valid values: 'list', 'json'", addonListOutput))
		}
	},
}

func init() {
	addonsListCmd.Flags().StringVarP(&addonListOutput, "output", "o", "list", "minikube addons list --output OUTPUT. json, list")
	addonsListCmd.Flags().BoolVarP(&addonPrintDocs, "docs", "d", false, "If true, print web links to addons' documentation.")
	AddonsCmd.AddCommand(addonsListCmd)
}

var iconFromStatus = func(addonStatus bool) string {
	if addonStatus {
		return "✅"
	}
	return "   " // because emoji indentation is different
}

var stringFromStatus = func(addonStatus bool) string {
	if addonStatus {
		return "enabled"
	}
	return "disabled"
}

var printAddonsList = func(cc *config.ClusterConfig, printDocs bool) {
	addonNames := make([]string, 0, len(assets.Addons))
	for addonName := range assets.Addons {
		addonNames = append(addonNames, addonName)
	}
	sort.Strings(addonNames)

	var tData [][]string
	table := tablewriter.NewWriter(os.Stdout)
	table.SetAutoFormatHeaders(true)
	table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
	table.SetCenterSeparator("|")

	var tHeader []string
	if cc == nil {
		tHeader = []string{"Addon Name", "Maintainer"}
	} else {
		tHeader = []string{"Addon Name", "Profile", "Status", "Maintainer"}
	}
	if printDocs {
		tHeader = append(tHeader, "Docs")
	}
	table.SetHeader(tHeader)

	for _, addonName := range addonNames {
		addonBundle := assets.Addons[addonName]
		maintainer := addonBundle.Maintainer
		if maintainer == "" {
			maintainer = "3rd party (unknown)"
		}
		docs := addonBundle.Docs
		if docs == "" {
			docs = "n/a"
		}
		if cc == nil {
			if printDocs {
				tData = append(tData, []string{addonName, maintainer, docs})
			} else {
				tData = append(tData, []string{addonName, maintainer})
			}
			continue
		} else {
			enabled := addonBundle.IsEnabled(cc)
			if printDocs {
				tData = append(tData, []string{addonName, cc.Name, fmt.Sprintf("%s %s", stringFromStatus(enabled), iconFromStatus(enabled)), maintainer, docs})
			} else {
				tData = append(tData, []string{addonName, cc.Name, fmt.Sprintf("%s %s", stringFromStatus(enabled), iconFromStatus(enabled)), maintainer})
			}
		}
	}

	table.AppendBulk(tData)
	table.Render()

	v, _, err := config.ListProfiles()
	if err != nil {
		klog.Errorf("list profiles returned error: %v", err)
	}
	if len(v) > 1 {
		out.Styled(style.Tip, "To see addons list for other profiles use: `minikube addons -p name list`")
	}
}

var printAddonsJSON = func(cc *config.ClusterConfig) {
	addonNames := make([]string, 0, len(assets.Addons))
	for addonName := range assets.Addons {
		addonNames = append(addonNames, addonName)
	}
	sort.Strings(addonNames)

	addonsMap := map[string]map[string]interface{}{}

	for _, addonName := range addonNames {
		if cc == nil {
			addonsMap[addonName] = map[string]interface{}{}
			continue
		}

		addonBundle := assets.Addons[addonName]
		enabled := addonBundle.IsEnabled(cc)

		addonsMap[addonName] = map[string]interface{}{
			"Status":  stringFromStatus(enabled),
			"Profile": cc.Name,
		}
	}
	jsonString, _ := json.Marshal(addonsMap)

	out.String(string(jsonString))
}
<commit_msg>Conditionally append docs to tData<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package config

import (
	"encoding/json"
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/assets"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/mustload"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/reason"
	"k8s.io/minikube/pkg/minikube/style"
)

var addonListOutput string
var addonPrintDocs bool

// AddonListTemplate represents the addon list template
type AddonListTemplate struct {
	AddonName   string
	AddonStatus string
}

var addonsListCmd = &cobra.Command{
	Use:   "list",
	Short: "Lists all available minikube addons as well as their current statuses (enabled/disabled)",
	Long:  "Lists all available minikube addons as well as their current statuses (enabled/disabled)",
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) != 0 {
			exit.Message(reason.Usage, "usage: minikube addons list")
		}

		var cc *config.ClusterConfig
		if config.ProfileExists(ClusterFlagValue()) {
			_, cc = mustload.Partial(ClusterFlagValue())
		}
		switch strings.ToLower(addonListOutput) {
		case "list":
			printAddonsList(cc, addonPrintDocs)
		case "json":
			printAddonsJSON(cc)
		default:
			exit.Message(reason.Usage, fmt.Sprintf("invalid output format: %s. Valid values: 'list', 'json'", addonListOutput))
		}
	},
}

func init() {
	addonsListCmd.Flags().StringVarP(&addonListOutput, "output", "o", "list", "minikube addons list --output OUTPUT. json, list")
	addonsListCmd.Flags().BoolVarP(&addonPrintDocs, "docs", "d", false, "If true, print web links to addons' documentation.")
	AddonsCmd.AddCommand(addonsListCmd)
}

var iconFromStatus = func(addonStatus bool) string {
	if addonStatus {
		return "✅"
	}
	return "   " // because emoji indentation is different
}

var stringFromStatus = func(addonStatus bool) string {
	if addonStatus {
		return "enabled"
	}
	return "disabled"
}

var printAddonsList = func(cc *config.ClusterConfig, printDocs bool) {
	addonNames := make([]string, 0, len(assets.Addons))
	for addonName := range assets.Addons {
		addonNames = append(addonNames, addonName)
	}
	sort.Strings(addonNames)

	table := tablewriter.NewWriter(os.Stdout)
	table.SetAutoFormatHeaders(true)
	table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
	table.SetCenterSeparator("|")

	// Create table header
	var tHeader []string
	if cc == nil {
		tHeader = []string{"Addon Name", "Maintainer"}
	} else {
		tHeader = []string{"Addon Name", "Profile", "Status", "Maintainer"}
	}
	if printDocs {
		tHeader = append(tHeader, "Docs")
	}
	table.SetHeader(tHeader)

	// Create table data
	var tData [][]string
	var temp []string
	for _, addonName := range addonNames {
		addonBundle := assets.Addons[addonName]
		maintainer := addonBundle.Maintainer
		if maintainer == "" {
			maintainer = "3rd party (unknown)"
		}
		docs := addonBundle.Docs
		if docs == "" {
			docs = "n/a"
		}
		if cc == nil {
			temp = []string{addonName, maintainer}
		} else {
			enabled := addonBundle.IsEnabled(cc)
			temp = []string{addonName, cc.Name, fmt.Sprintf("%s %s", stringFromStatus(enabled), iconFromStatus(enabled)), maintainer}
		}
		if printDocs {
			temp = append(temp, docs)
		}
		tData = append(tData, temp)
	}
	table.AppendBulk(tData)

	table.Render()

	v, _, err := config.ListProfiles()
	if err != nil {
		klog.Errorf("list profiles returned error: %v", err)
	}
	if len(v) > 1 {
		out.Styled(style.Tip, "To see addons list for other profiles use: `minikube addons -p name list`")
	}
}

var printAddonsJSON = func(cc *config.ClusterConfig) {
	addonNames := make([]string, 0, len(assets.Addons))
	for addonName := range assets.Addons {
		addonNames = append(addonNames, addonName)
	}
	sort.Strings(addonNames)

	addonsMap := map[string]map[string]interface{}{}

	for _, addonName := range addonNames {
		if cc == nil {
			addonsMap[addonName] = map[string]interface{}{}
			continue
		}

		addonBundle := assets.Addons[addonName]
		enabled := addonBundle.IsEnabled(cc)

		addonsMap[addonName] = map[string]interface{}{
			"Status":  stringFromStatus(enabled),
			"Profile": cc.Name,
		}
	}
	jsonString, _ := json.Marshal(addonsMap)

	out.String(string(jsonString))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	goflag "flag"
	"fmt"
	"io"
	"math/rand"
	"os"
	"time"

	"github.com/onsi/gomega"

	"github.com/onsi/ginkgo"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	utilflag "k8s.io/component-base/cli/flag"
	"k8s.io/component-base/logs"
	"k8s.io/klog"
	"k8s.io/kubernetes/pkg/kubectl/util/templates"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	"github.com/openshift/library-go/pkg/serviceability"
	"github.com/openshift/origin/pkg/monitor"
	testginkgo "github.com/openshift/origin/pkg/test/ginkgo"
	exutil "github.com/openshift/origin/test/extended/util"
)

func main() {
	logs.InitLogs()
	defer logs.FlushLogs()

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

	pflag.CommandLine.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)

	root := &cobra.Command{
		Long: templates.LongDesc(`
		OpenShift Tests

		This command verifies behavior of an OpenShift cluster by running remote tests against
		the cluster API that exercise functionality. In general these tests may be disruptive
		or require elevated privileges - see the descriptions of each test suite.
		`),
	}

	root.AddCommand(
		newRunCommand(),
		newRunUpgradeCommand(),
		newRunTestCommand(),
		newRunMonitorCommand(),
	)

	pflag.CommandLine = pflag.NewFlagSet("empty", pflag.ExitOnError)
	flag.CommandLine = flag.NewFlagSet("empty", flag.ExitOnError)
	exutil.InitStandardFlags()

	if err := func() error {
		defer serviceability.Profile(os.Getenv("OPENSHIFT_PROFILE")).Stop()
		return root.Execute()
	}(); err != nil {
		if ex, ok := err.(testginkgo.ExitError); ok {
			os.Exit(ex.Code)
		}
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}

func newRunMonitorCommand() *cobra.Command {
	monitorOpt := &monitor.Options{
		Out:    os.Stdout,
		ErrOut: os.Stderr,
	}
	cmd := &cobra.Command{
		Use:   "run-monitor",
		Short: "Continuously verify the cluster is functional",
		Long: templates.LongDesc(`
		Run a continuous verification process

		`),

		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			return monitorOpt.Run()
		},
	}
	return cmd
}

func newRunCommand() *cobra.Command {
	opt := &testginkgo.Options{
		Suites: staticSuites,
	}
	cmd := &cobra.Command{
		Use:   "run SUITE",
		Short: "Run a test suite",
		Long: templates.LongDesc(`
		Run a test suite against an OpenShift server

		This command will run one of the following suites against a cluster identified by the current
		KUBECONFIG file. See the suite description for more on what actions the suite will take.

		If you specify the --dry-run argument, the names of each individual test that is part of the
		suite will be printed, one per line. You may filter this list and pass it back to the run
		command with the --file argument. You may also pipe a list of test names, one per line, on
		standard input by passing "-f -".

		`) + testginkgo.SuitesString(opt.Suites, "\n\nAvailable test suites:\n\n"),

		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			return mirrorToFile(opt, func() error {
				if err := initProvider(opt.Provider); err != nil {
					return err
				}
				e2e.AfterReadingAllFlags(exutil.TestContext)
				e2e.TestContext.DumpLogsOnFailure = true
				exutil.TestContext.DumpLogsOnFailure = true
				return opt.Run(args)
			})
		},
	}
	bindOptions(opt, cmd.Flags())
	return cmd
}

func newRunUpgradeCommand() *cobra.Command {
	opt := &testginkgo.Options{Suites: upgradeSuites}
	upgradeOpt := &UpgradeOptions{}

	cmd := &cobra.Command{
		Use:   "run-upgrade SUITE",
		Short: "Run an upgrade suite",
		Long: templates.LongDesc(`
		Run an upgrade test suite against an OpenShift server

		This command will run one of the following suites against a cluster identified by the current
		KUBECONFIG file. See the suite description for more on what actions the suite will take.

		If you specify the --dry-run argument, the actions the suite will take will be printed to the
		output.

		Supported options:

		* abort-at=NUMBER - Set to a number between 0 and 100 to control the percent of operators
		at which to stop the current upgrade and roll back to the current version.
		* disrupt-reboot=POLICY - During upgrades, periodically reboot master nodes. If set to 'graceful'
		the reboot will allow the node to shut down services in an orderly fashion. If set to 'force' the
		machine will terminate immediately without clean shutdown.

		`) + testginkgo.SuitesString(opt.Suites, "\n\nAvailable upgrade suites:\n\n"),

		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			return mirrorToFile(opt, func() error {
				if len(upgradeOpt.ToImage) == 0 {
					return fmt.Errorf("--to-image must be specified to run an upgrade test")
				}

				if len(args) > 0 {
					for _, suite := range opt.Suites {
						if suite.Name == args[0] {
							upgradeOpt.Suite = suite.Name
							upgradeOpt.JUnitDir = opt.JUnitDir
							value := upgradeOpt.ToEnv()
							if err := initUpgrade(value); err != nil {
								return err
							}
							opt.SuiteOptions = value
							break
						}
					}
				}

				if err := initProvider(opt.Provider); err != nil {
					return err
				}
				e2e.AfterReadingAllFlags(exutil.TestContext)
				e2e.TestContext.DumpLogsOnFailure = true
				exutil.TestContext.DumpLogsOnFailure = true
				return opt.Run(args)
			})
		},
	}

	bindOptions(opt, cmd.Flags())
	bindUpgradeOptions(upgradeOpt, cmd.Flags())
	return cmd
}

func newRunTestCommand() *cobra.Command {
	testOpt := &testginkgo.TestOptions{
		Out:    os.Stdout,
		ErrOut: os.Stderr,
	}
	cmd := &cobra.Command{
		Use:   "run-test NAME",
		Short: "Run a single test by name",
		Long: templates.LongDesc(`
		Execute a single test

		This executes a single test by name. It is used by the run command during suite execution but may also
		be used to test in isolation while developing new tests.
		`),

		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			if err := initProvider(os.Getenv("TEST_PROVIDER")); err != nil {
				return err
			}
			if err := initUpgrade(os.Getenv("TEST_SUITE_OPTIONS")); err != nil {
				return err
			}
			e2e.AfterReadingAllFlags(exutil.TestContext)
			e2e.TestContext.DumpLogsOnFailure = true
			exutil.TestContext.DumpLogsOnFailure = true
			return testOpt.Run(args)
		},
	}
	cmd.Flags().BoolVar(&testOpt.DryRun, "dry-run", testOpt.DryRun, "Print the test to run without executing them.")
	return cmd
}

// mirrorToFile ensures a copy of all output goes to the provided OutFile, including
// any error returned from fn. The function returns fn() or any error encountered while
// attempting to open the file.
func mirrorToFile(opt *testginkgo.Options, fn func() error) error {
	if len(opt.OutFile) == 0 {
		opt.Out, opt.ErrOut = os.Stdout, os.Stderr
		return fn()
	}

	f, err := os.OpenFile(opt.OutFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	opt.Out = io.MultiWriter(os.Stdout, f)
	opt.ErrOut = io.MultiWriter(os.Stderr, f)
	exitErr := fn()
	if exitErr != nil {
		fmt.Fprintf(f, "error: %s", exitErr)
	}
	if err := f.Close(); err != nil {
		fmt.Fprintf(os.Stderr, "error: Unable to close output file\n")
	}
	return exitErr
}

func bindOptions(opt *testginkgo.Options, flags *pflag.FlagSet) {
	flags.BoolVar(&opt.DryRun, "dry-run", opt.DryRun, "Print the tests to run without executing them.")
	flags.BoolVar(&opt.PrintCommands, "print-commands", opt.PrintCommands, "Print the sub-commands that would be executed instead.")
	flags.StringVar(&opt.JUnitDir, "junit-dir", opt.JUnitDir, "The directory to write test reports to.")
	flags.StringVar(&opt.Provider, "provider", opt.Provider, "The cluster infrastructure provider. Will automatically default to the correct value.")
	flags.StringVarP(&opt.TestFile, "file", "f", opt.TestFile, "Create a suite from the newline-delimited test names in this file.")
	flags.StringVarP(&opt.OutFile, "output-file", "o", opt.OutFile, "Write all test output to this file.")
	flags.DurationVar(&opt.Timeout, "timeout", opt.Timeout, "Set the maximum time a test can run before being aborted. This is read from the suite by default, but will be 10 minutes otherwise.")
	flags.BoolVar(&opt.IncludeSuccessOutput, "include-success", opt.IncludeSuccessOutput, "Print output from successful tests.")
}

func initProvider(provider string) error {
	// record the exit error to the output file
	if err := decodeProviderTo(provider, exutil.TestContext); err != nil {
		return err
	}
	exutil.TestContext.AllowedNotReadyNodes = 100
	exutil.TestContext.MaxNodesToGather = 0
	exutil.TestContext.Viper = os.Getenv("VIPERCONFIG")

	// set defaults so these tests don't log
	exutil.TestContext.LoggingSoak.Scale = 1
	exutil.TestContext.LoggingSoak.MilliSecondsBetweenWaves = 5000

	exutil.AnnotateTestSuite()
	exutil.InitTest()
	gomega.RegisterFailHandler(ginkgo.Fail)

	// TODO: infer SSH keys from the cluster
	return nil
}

func decodeProviderTo(provider string, testContext *e2e.TestContextType) error {
	switch provider {
	case "":
		if _, ok := os.LookupEnv("KUBE_SSH_USER"); ok {
			if _, ok := os.LookupEnv("LOCAL_SSH_KEY"); ok {
				testContext.Provider = "local"
			}
		}
		// TODO: detect which provider the cluster is running and use that as a default.
	default:
		var providerInfo struct{ Type string }
		if err := json.Unmarshal([]byte(provider), &providerInfo); err != nil {
			return fmt.Errorf("provider must be a JSON object with the 'type' key at a minimum: %v", err)
		}
		if len(providerInfo.Type) == 0 {
			return fmt.Errorf("provider must be a JSON object with the 'type' key")
		}
		testContext.Provider = providerInfo.Type
		if err := json.Unmarshal([]byte(provider), &testContext.CloudConfig); err != nil {
			return fmt.Errorf("provider must decode into the cloud config object: %v", err)
		}
	}
	if len(testContext.Provider) == 0 {
		testContext.Provider = "skeleton"
	}
	klog.V(2).Infof("Provider %s: %#v", testContext.Provider, testContext.CloudConfig)
	return nil
}
<commit_msg>cmd/openshift-tests: load the configuration for test from cluster when provider is azure<commit_after>package main

import (
	"encoding/json"
	"flag"
	goflag "flag"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"os"
	"time"

	"github.com/onsi/gomega"

	"github.com/onsi/ginkgo"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	utilflag "k8s.io/component-base/cli/flag"
	"k8s.io/component-base/logs"
	"k8s.io/klog"
	"k8s.io/kubernetes/pkg/kubectl/util/templates"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	"github.com/openshift/library-go/pkg/serviceability"
	"github.com/openshift/origin/pkg/monitor"
	testginkgo "github.com/openshift/origin/pkg/test/ginkgo"
	exutil "github.com/openshift/origin/test/extended/util"
	exutilazure "github.com/openshift/origin/test/extended/util/azure"
)

func main() {
	logs.InitLogs()
	defer logs.FlushLogs()

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

	pflag.CommandLine.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)

	root := &cobra.Command{
		Long: templates.LongDesc(`
		OpenShift Tests

		This command verifies behavior of an OpenShift cluster by running remote tests against
		the cluster API that exercise functionality. In general these tests may be disruptive
		or require elevated privileges - see the descriptions of each test suite.
		`),
	}

	root.AddCommand(
		newRunCommand(),
		newRunUpgradeCommand(),
		newRunTestCommand(),
		newRunMonitorCommand(),
	)

	pflag.CommandLine = pflag.NewFlagSet("empty", pflag.ExitOnError)
	flag.CommandLine = flag.NewFlagSet("empty", flag.ExitOnError)
	exutil.InitStandardFlags()

	if err := func() error {
		defer serviceability.Profile(os.Getenv("OPENSHIFT_PROFILE")).Stop()
		return root.Execute()
	}(); err != nil {
		if ex, ok := err.(testginkgo.ExitError); ok {
			os.Exit(ex.Code)
		}
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}

func newRunMonitorCommand() *cobra.Command {
	monitorOpt := &monitor.Options{
		Out:    os.Stdout,
		ErrOut: os.Stderr,
	}
	cmd := &cobra.Command{
		Use:   "run-monitor",
		Short: "Continuously verify the cluster is functional",
		Long: templates.LongDesc(`
		Run a continuous verification process

		`),

		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			return monitorOpt.Run()
		},
	}
	return cmd
}

func newRunCommand() *cobra.Command {
	opt := &testginkgo.Options{
		Suites: staticSuites,
	}
	cmd := &cobra.Command{
		Use:   "run SUITE",
		Short: "Run a test suite",
		Long: templates.LongDesc(`
		Run a test suite against an OpenShift server

		This command will run one of the following suites against a cluster identified by the current
		KUBECONFIG file. See the suite description for more on what actions the suite will take.

		If you specify the --dry-run argument, the names of each individual test that is part of the
		suite will be printed, one per line. You may filter this list and pass it back to the run
		command with the --file argument. You may also pipe a list of test names, one per line, on
		standard input by passing "-f -".

		`) + testginkgo.SuitesString(opt.Suites, "\n\nAvailable test suites:\n\n"),

		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			return mirrorToFile(opt, func() error {
				if err := initProvider(opt.Provider); err != nil {
					return err
				}
				e2e.AfterReadingAllFlags(exutil.TestContext)
				e2e.TestContext.DumpLogsOnFailure = true
				exutil.TestContext.DumpLogsOnFailure = true
				return opt.Run(args)
			})
		},
	}
	bindOptions(opt, cmd.Flags())
	return cmd
}

func newRunUpgradeCommand() *cobra.Command {
	opt := &testginkgo.Options{Suites: upgradeSuites}
	upgradeOpt := &UpgradeOptions{}

	cmd := &cobra.Command{
		Use:   "run-upgrade SUITE",
		Short: "Run an upgrade suite",
		Long: templates.LongDesc(`
		Run an upgrade test suite against an OpenShift server

		This command will run one of the following suites against a cluster identified by the current
		KUBECONFIG file. See the suite description for more on what actions the suite will take.

		If you specify the --dry-run argument, the actions the suite will take will be printed to the
		output.

		Supported options:

		* abort-at=NUMBER - Set to a number between 0 and 100 to control the percent of operators
		at which to stop the current upgrade and roll back to the current version.
		* disrupt-reboot=POLICY - During upgrades, periodically reboot master nodes. If set to 'graceful'
		the reboot will allow the node to shut down services in an orderly fashion. If set to 'force' the
		machine will terminate immediately without clean shutdown.

		`) + testginkgo.SuitesString(opt.Suites, "\n\nAvailable upgrade suites:\n\n"),

		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			return mirrorToFile(opt, func() error {
				if len(upgradeOpt.ToImage) == 0 {
					return fmt.Errorf("--to-image must be specified to run an upgrade test")
				}

				if len(args) > 0 {
					for _, suite := range opt.Suites {
						if suite.Name == args[0] {
							upgradeOpt.Suite = suite.Name
							upgradeOpt.JUnitDir = opt.JUnitDir
							value := upgradeOpt.ToEnv()
							if err := initUpgrade(value); err != nil {
								return err
							}
							opt.SuiteOptions = value
							break
						}
					}
				}

				if err := initProvider(opt.Provider); err != nil {
					return err
				}
				e2e.AfterReadingAllFlags(exutil.TestContext)
				e2e.TestContext.DumpLogsOnFailure = true
				exutil.TestContext.DumpLogsOnFailure = true
				return opt.Run(args)
			})
		},
	}

	bindOptions(opt, cmd.Flags())
	bindUpgradeOptions(upgradeOpt, cmd.Flags())
	return cmd
}

func newRunTestCommand() *cobra.Command {
	testOpt := &testginkgo.TestOptions{
		Out:    os.Stdout,
		ErrOut: os.Stderr,
	}
	cmd := &cobra.Command{
		Use:   "run-test NAME",
		Short: "Run a single test by name",
		Long: templates.LongDesc(`
		Execute a single test

		This executes a single test by name. It is used by the run command during suite execution but may also
		be used to test in isolation while developing new tests.
		`),

		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			if err := initProvider(os.Getenv("TEST_PROVIDER")); err != nil {
				return err
			}
			if err := initUpgrade(os.Getenv("TEST_SUITE_OPTIONS")); err != nil {
				return err
			}
			e2e.AfterReadingAllFlags(exutil.TestContext)
			e2e.TestContext.DumpLogsOnFailure = true
			exutil.TestContext.DumpLogsOnFailure = true
			return testOpt.Run(args)
		},
	}
	cmd.Flags().BoolVar(&testOpt.DryRun, "dry-run", testOpt.DryRun, "Print the test to run without executing them.")
	return cmd
}

// mirrorToFile ensures a copy of all output goes to the provided OutFile, including
// any error returned from fn. The function returns fn() or any error encountered while
// attempting to open the file.
func mirrorToFile(opt *testginkgo.Options, fn func() error) error {
	if len(opt.OutFile) == 0 {
		opt.Out, opt.ErrOut = os.Stdout, os.Stderr
		return fn()
	}

	f, err := os.OpenFile(opt.OutFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	opt.Out = io.MultiWriter(os.Stdout, f)
	opt.ErrOut = io.MultiWriter(os.Stderr, f)
	exitErr := fn()
	if exitErr != nil {
		fmt.Fprintf(f, "error: %s", exitErr)
	}
	if err := f.Close(); err != nil {
		fmt.Fprintf(os.Stderr, "error: Unable to close output file\n")
	}
	return exitErr
}

func bindOptions(opt *testginkgo.Options, flags *pflag.FlagSet) {
	flags.BoolVar(&opt.DryRun, "dry-run", opt.DryRun, "Print the tests to run without executing them.")
	flags.BoolVar(&opt.PrintCommands, "print-commands", opt.PrintCommands, "Print the sub-commands that would be executed instead.")
	flags.StringVar(&opt.JUnitDir, "junit-dir", opt.JUnitDir, "The directory to write test reports to.")
	flags.StringVar(&opt.Provider, "provider", opt.Provider, "The cluster infrastructure provider. Will automatically default to the correct value.")
	flags.StringVarP(&opt.TestFile, "file", "f", opt.TestFile, "Create a suite from the newline-delimited test names in this file.")
	flags.StringVarP(&opt.OutFile, "output-file", "o", opt.OutFile, "Write all test output to this file.")
	flags.DurationVar(&opt.Timeout, "timeout", opt.Timeout, "Set the maximum time a test can run before being aborted. This is read from the suite by default, but will be 10 minutes otherwise.")
	flags.BoolVar(&opt.IncludeSuccessOutput, "include-success", opt.IncludeSuccessOutput, "Print output from successful tests.")
}

func initProvider(provider string) error {
	// record the exit error to the output file
	if err := decodeProviderTo(provider, exutil.TestContext); err != nil {
		return err
	}
	exutil.TestContext.AllowedNotReadyNodes = 100
	exutil.TestContext.MaxNodesToGather = 0
	exutil.TestContext.Viper = os.Getenv("VIPERCONFIG")

	// set defaults so these tests don't log
	exutil.TestContext.LoggingSoak.Scale = 1
	exutil.TestContext.LoggingSoak.MilliSecondsBetweenWaves = 5000

	exutil.AnnotateTestSuite()
	exutil.InitTest()
	gomega.RegisterFailHandler(ginkgo.Fail)

	// TODO: infer SSH keys from the cluster
	return nil
}

func decodeProviderTo(provider string, testContext *e2e.TestContextType) error {
	switch provider {
	case "":
		if _, ok := os.LookupEnv("KUBE_SSH_USER"); ok {
			if _, ok := os.LookupEnv("LOCAL_SSH_KEY"); ok {
				testContext.Provider = "local"
			}
		}
		// TODO: detect which provider the cluster is running and use that as a default.
	case "azure":
		tmpFile, err := ioutil.TempFile("", "e2e-*")
		if err != nil {
			return err
		}
		data, err := exutilazure.LoadConfigFile()
		if err != nil {
			return err
		}
		if _, err := tmpFile.Write(data); err != nil {
			return err
		}
		if err := tmpFile.Close(); err != nil {
			return err
		}
		testContext.Provider = "azure"
		testContext.CloudConfig = e2e.CloudConfig{ConfigFile: tmpFile.Name()}
	default:
		var providerInfo struct{ Type string }
		if err := json.Unmarshal([]byte(provider), &providerInfo); err != nil {
			return fmt.Errorf("provider must be a JSON object with the 'type' key at a minimum: %v", err)
		}
		if len(providerInfo.Type) == 0 {
			return fmt.Errorf("provider must be a JSON object with the 'type' key")
		}
		testContext.Provider = providerInfo.Type
		if err := json.Unmarshal([]byte(provider), &testContext.CloudConfig); err != nil {
			return fmt.Errorf("provider must decode into the cloud config object: %v", err)
		}
	}
	if len(testContext.Provider) == 0 {
		testContext.Provider = "skeleton"
	}
	klog.V(2).Infof("Provider %s: %#v", testContext.Provider, testContext.CloudConfig)
	return nil
}
<|endoftext|>


<commit_before>package bot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"

	"github.com/nlopes/slack"
)

type BusInfo struct {
	mBus    []string
	aBus    []string
	success bool
}

var (
	before_noon []string = []string{"남문", "제2공학관", "과학원", "광복관", "외솔관", "성암관(청송대)", "새천년관", "동문", "경복궁역"}
	after_noon  []string = []string{"남문", "제2공학관", "과학원", "광복관", "외솔관", "성암관(청송대)", "아식설계연구소", "무악학사"}
)

func shuttleErrorMessage(bot *BaseBot, channel string) {
	postResponse(bot, channel, ":bus:", "신촌 셔틀버스", "정보를 가져오는데 에러가 발생했습니다.\nhttp://www.yonsei.ac.kr/sc/campus/traffic1.jsp 에서 직접 확인해주세요.")
}

func getStationFromIndex(arr []string, index int) string {
	arr_len := len(arr)
	if index < arr_len {
		return arr[index]
	} else {
		return arr[arr_len-(index-arr_len+1)-1]
	}
}

func getPositionFromIndex(arr []string, index int) string {
	if index%2 == 0 {
		index /= 2
		return getStationFromIndex(arr, index)
	} else {
		index /= 2
		return fmt.Sprintf("%s -> %s", getStationFromIndex(arr, index), getStationFromIndex(arr, index+1))
	}
}

func getShuttlePosition(field *slack.AttachmentField, arr []string, index_str string) {
	arr_len := len(arr)
	index, _ := strconv.Atoi(index_str)
	pos := getPositionFromIndex(arr, index)

	field.Title = pos
	var direction string
	if index >= (arr_len-1)*2 {
		direction = arr[arr_len-1]
	} else {
		direction = arr[0]
	}
	field.Value = fmt.Sprintf("%s 방향", direction)
	field.Short = true
}

func processShuttleCommand(bot *BaseBot, channel string) {
	resp, err := http.Get("http://www.yonsei.ac.kr/_custom/yonsei/_common/shuttle_bus/get_bus_info.jsp")
	if err != nil {
		shuttleErrorMessage(bot, channel)
		return
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(resp.Body)
	b := buf.Bytes()

	var info BusInfo
	err = json.Unmarshal(b, &info)
	if err != nil {
		shuttleErrorMessage(bot, channel)
		return
	}

	attachments := make([]slack.AttachmentField, len(info.mBus)+len(info.aBus))
	index := 0
	for pos := range info.mBus {
		getShuttlePosition(&attachments[index], before_noon, pos)
		index++
	}
	for pos := range info.aBus {
		getShuttlePosition(&attachments[index], after_noon, pos)
		index++
	}

	attachment := slack.Attachment{
		Color: "#1766ff",
	}
	if len(attachments) == 0 {
		attachment.Text = "현재 운영중인 셔틀버스 정보 없음"
	} else {
		attachment.Text = "셔틀버스 위치"
		attachment.Fields = attachments
	}

	bot.PostMessage(channel, "", slack.PostMessageParameters{
		AsUser:    false,
		IconEmoji: ":bus:",
		Username:  "신촌 셔틀버스",
		Attachments: []slack.Attachment{
			attachment,
		},
	})
}
<commit_msg>range misuse<commit_after>package bot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"

	"github.com/nlopes/slack"
)

type BusInfo struct {
	mBus    []string
	aBus    []string
	success bool
}

var (
	before_noon []string = []string{"남문", "제2공학관", "과학원", "광복관", "외솔관", "성암관(청송대)", "새천년관", "동문", "경복궁역"}
	after_noon  []string = []string{"남문", "제2공학관", "과학원", "광복관", "외솔관", "성암관(청송대)", "아식설계연구소", "무악학사"}
)

func shuttleErrorMessage(bot *BaseBot, channel string) {
	postResponse(bot, channel, ":bus:", "신촌 셔틀버스", "정보를 가져오는데 에러가 발생했습니다.\nhttp://www.yonsei.ac.kr/sc/campus/traffic1.jsp 에서 직접 확인해주세요.")
}

func getStationFromIndex(arr []string, index int) string {
	arr_len := len(arr)
	if index < arr_len {
		return arr[index]
	} else {
		return arr[arr_len-(index-arr_len+1)-1]
	}
}

func getPositionFromIndex(arr []string, index int) string {
	if index%2 == 0 {
		index /= 2
		return getStationFromIndex(arr, index)
	} else {
		index /= 2
		return fmt.Sprintf("%s -> %s", getStationFromIndex(arr, index), getStationFromIndex(arr, index+1))
	}
}

func getShuttlePosition(field *slack.AttachmentField, arr []string, index_str string) {
	arr_len := len(arr)
	index, _ := strconv.Atoi(index_str)
	pos := getPositionFromIndex(arr, index)

	field.Title = pos
	var direction string
	if index >= (arr_len-1)*2 {
		direction = arr[arr_len-1]
	} else {
		direction = arr[0]
	}
	field.Value = fmt.Sprintf("%s 방향", direction)
	field.Short = true
}

func processShuttleCommand(bot *BaseBot, channel string) {
	resp, err := http.Get("http://www.yonsei.ac.kr/_custom/yonsei/_common/shuttle_bus/get_bus_info.jsp")
	if err != nil {
		shuttleErrorMessage(bot, channel)
		return
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(resp.Body)
	b := buf.Bytes()

	var info BusInfo
	err = json.Unmarshal(b, &info)
	if err != nil {
		shuttleErrorMessage(bot, channel)
		return
	}

	attachments := make([]slack.AttachmentField, len(info.mBus)+len(info.aBus))
	index := 0
	for _, pos := range info.mBus {
		getShuttlePosition(&attachments[index], before_noon, pos)
		index++
	}
	for _, pos := range info.aBus {
		getShuttlePosition(&attachments[index], after_noon, pos)
		index++
	}

	attachment := slack.Attachment{
		Color: "#1766ff",
	}
	if len(attachments) == 0 {
		attachment.Text = "현재 운영중인 셔틀버스 정보 없음"
	} else {
		attachment.Text = "셔틀버스 위치"
		attachment.Fields = attachments
	}

	bot.PostMessage(channel, "", slack.PostMessageParameters{
		AsUser:    false,
		IconEmoji: ":bus:",
		Username:  "신촌 셔틀버스",
		Attachments: []slack.Attachment{
			attachment,
		},
	})
}
<|endoftext|>


<commit_before>package java

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/ezbuy/tgen/global"
	"github.com/samuel/go-thrift/parser"
)

func TestGenerate(t *testing.T) {
	// 1 read thrift files from folder 'cases'
	// 2 generate & output
	// 3 read generated files, compared with corresponding files in folder 'test'

	casedir, _ := filepath.Abs("./../../example/java")

	// create output dir
	outdir, _ := filepath.Abs("./output")
	// if err := os.MkdirAll(outdir, 0755); err != nil {
	// 	t.Errorf("failed to create output directory %s", outdir)
	// }

	testdir, _ := filepath.Abs("./../../example/java/ref")

	gen := &JavaGen{}
	p := &parser.Parser{}

	visitfunc := func(path string, info os.FileInfo, err error) error {
		if strings.HasPrefix(filepath.Base(path), ".") || filepath.Ext(path) != ".thrift" {
			return nil
		}

		global.InputFile = path

		parsedThrift, _, err := p.ParseFile(path)
		if err != nil {
			t.Errorf("parse error: %s\n", err.Error())
		}

		gen.Generate(outdir, parsedThrift)

		for f, thrift := range parsedThrift {
			if f != global.InputFile {
				continue
			}

			ns := thrift.Namespaces["java"]
			p := strings.Replace(ns, ".", "/", -1)

			for _, m := range thrift.Structs {
				name := m.Name + ".java"

				// jsonrpc

				outfile := filepath.Join(outdir, global.MODE_JSONRPC, p, name)
				testfile := filepath.Join(testdir, global.MODE_JSONRPC, p, name)

				fileCompare(t, outfile, testfile)

				// rest
				outfile = filepath.Join(outdir, global.MODE_REST, p, name)
				testfile = filepath.Join(testdir, global.MODE_REST, p, name)

				fileCompare(t, outfile, testfile)
			}

			for _, s := range thrift.Services {
				name := s.Name + "Service.java"

				// jsonrpc
				outfile := filepath.Join(outdir, global.MODE_JSONRPC, p, name)
				testfile := filepath.Join(testdir, global.MODE_JSONRPC, p, name)

				fileCompare(t, outfile, testfile)

				// rest
				outfile = filepath.Join(outdir, global.MODE_REST, p, name)
				testfile = filepath.Join(testdir, global.MODE_REST, p, name)

				fileCompare(t, outfile, testfile)
			}
		}

		return nil
	}

	if err := filepath.Walk(casedir, visitfunc); err != nil {
		t.Errorf("walk error: %s\n", err.Error())
	}

	// do some clean
	os.RemoveAll(outdir)
}

func fileCompare(t *testing.T, src string, dest string) {
	if !pathexists(src) {
		t.Error("generate error\n")
	} else if !pathexists(dest) {
		t.Errorf("no test file found [%s]\n", dest)
	} else {
		// compare the output file with the case
		srcdata, srcerr := ioutil.ReadFile(src)
		destdata, desterr := ioutil.ReadFile(dest)

		if srcerr != nil || desterr != nil {
			t.Error("compare error [reading]")
		} else if string(srcdata) != string(destdata) {
			t.Errorf("mismatch: [%s, %s]", src, dest)
		} else {
			t.Log("PASS")
		}
	}
}

func pathexists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}

	if os.IsNotExist(err) {
		return false
	}

	return false
}
<commit_msg>modify go test<commit_after>package java

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/ezbuy/tgen/global"
	"github.com/samuel/go-thrift/parser"
)

func TestGenerate(t *testing.T) {
	// 1 read thrift files from folder 'cases'
	// 2 generate & output
	// 3 read generated files, compared with corresponding files in folder 'test'

	casedir, _ := filepath.Abs("./../../example/java")

	// create output dir
	outdir, _ := filepath.Abs("./output")
	// if err := os.MkdirAll(outdir, 0755); err != nil {
	// 	t.Errorf("failed to create output directory %s", outdir)
	// }

	testdir, _ := filepath.Abs("./../../example/java/ref")

	gen := &JavaGen{}
	p := &parser.Parser{}

	visitfunc := func(path string, info os.FileInfo, err error) error {
		if strings.HasPrefix(filepath.Base(path), ".") || filepath.Ext(path) != ".thrift" {
			return nil
		}

		if !strings.HasSuffix(path, "ShipForMe.thrift") {
			return nil
		}

		global.InputFile = path

		parsedThrift, _, err := p.ParseFile(path)
		if err != nil {
			t.Errorf("parse error: %s\n", err.Error())
		}

		gen.Generate(outdir, parsedThrift)

		for f, thrift := range parsedThrift {
			if f != global.InputFile {
				continue
			}

			ns := thrift.Namespaces["java"]
			p := strings.Replace(ns, ".", "/", -1)

			for _, m := range thrift.Structs {
				name := m.Name + ".java"

				// jsonrpc

				outfile := filepath.Join(outdir, global.MODE_JSONRPC, p, name)
				testfile := filepath.Join(testdir, global.MODE_JSONRPC, p, name)

				fileCompare(t, outfile, testfile)

				// rest
				outfile = filepath.Join(outdir, global.MODE_REST, p, name)
				testfile = filepath.Join(testdir, global.MODE_REST, p, name)

				fileCompare(t, outfile, testfile)
			}

			for _, s := range thrift.Services {
				name := s.Name + "Service.java"

				// jsonrpc
				outfile := filepath.Join(outdir, global.MODE_JSONRPC, p, name)
				testfile := filepath.Join(testdir, global.MODE_JSONRPC, p, name)

				fileCompare(t, outfile, testfile)

				// rest
				outfile = filepath.Join(outdir, global.MODE_REST, p, name)
				testfile = filepath.Join(testdir, global.MODE_REST, p, name)

				fileCompare(t, outfile, testfile)
			}
		}

		return nil
	}

	if err := filepath.Walk(casedir, visitfunc); err != nil {
		t.Errorf("walk error: %s\n", err.Error())
	}

	// do some clean
	os.RemoveAll(outdir)
}

func fileCompare(t *testing.T, src string, dest string) {
	if !pathexists(src) {
		t.Error("generate error\n")
	} else if !pathexists(dest) {
		t.Errorf("no test file found [%s]\n", dest)
	} else {
		// compare the output file with the case
		srcdata, srcerr := ioutil.ReadFile(src)
		destdata, desterr := ioutil.ReadFile(dest)

		if srcerr != nil || desterr != nil {
			t.Error("compare error [reading]")
		} else if string(srcdata) != string(destdata) {
			t.Errorf("mismatch: [%s, %s]", src, dest)
		} else {
			t.Log("PASS")
		}
	}
}

func pathexists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}

	if os.IsNotExist(err) {
		return false
	}

	return false
}
<|endoftext|>


<commit_before><commit_msg>Updated Example<commit_after><|endoftext|>


<commit_before><commit_msg>Reverse sort<commit_after><|endoftext|>


<commit_before><commit_msg>Update components new options and command flags<commit_after><|endoftext|>


<commit_before>// Simple utility for counting messages on a Kafka topic.
//
// Copyright (C) 2017 ENEO Tecnologia SL
// Author: Diego Fernández Barrera <bigomby@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"time"

	"github.com/fatih/color"
)

// PubKey should be set on compile time
var PubKey string

type licenseStatus struct {
	OrganizationUUID string
	Amount           uint64
	Expired          bool
}

// LimitBytes is a map containing the max bytes for each organization
type LimitBytes map[string]*licenseStatus

func (l LimitBytes) getOrganizationLimits() map[string]uint64 {
	total := make(map[string]uint64)

	for _, v := range l {
		if !v.Expired {
			total[v.OrganizationUUID] += v.Amount
		}
	}

	return total
}

// License contains information about the limits of bytes that an organization
// can send.
type License struct {
	UUID         string         `json:"uuid"`
	ExpireAt     int64          `json:"expire_at"`
	LimitBytes   uint64         `json:"limit_bytes"`
	ClusterUUID  string         `json:"cluster_uuid"`
	Sensors      map[string]int `json:"sensors"`
	Organization string         `json:"organization_uuid"`
}

// LoadLicenses reads a redBorder license from a file and returns a License
// struct holding the decoded information.
func LoadLicenses(config *AppConfig) (LimitBytes, error) {
	limits := make(LimitBytes)

	log := log.WithField("prefix", "license")

	files, err := ioutil.ReadDir(config.LicensesDirectory)
	if err != nil {
		return nil, err
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}

		encodedLicense, encodedSignature, err :=
			ReadLicense(config.LicensesDirectory + "/" + file.Name())
		if err != nil {
			return nil, err
		}

		if PubKey != "" {
			pub, err := DecodePEM([]byte(PubKey))
			if err != nil {
				return nil, err
			}

			err = VerifyLicense(pub, encodedLicense, encodedSignature)
			if err != nil {
				log.Errorf(
					"Error verifying license \"%s\": %s", file.Name(), err.Error())
				continue
			}
		}

		license, err := DecodeLicense(encodedLicense)
		if err != nil {
			return nil, err
		}

		limits[license.UUID] = &licenseStatus{}

		expires := time.Unix(license.ExpireAt, 0)
		if expires.Before(time.Now()) {
			log.Warnf("License %s has expired", license.UUID)
			limits[license.UUID].Expired = true
			continue
		}

		log.Infoln(FormatLicense(license))

		if config.OrganizationMode {
			if license.Organization == "" {
				log.Warnf("Ignoring license WITH NO organization %s", license.UUID)
				continue
			}

			limits[license.UUID].Amount += license.LimitBytes
			limits[license.UUID].OrganizationUUID = license.Organization
		} else {
			if license.Organization != "" {
				log.Warnf("Ignoring license WITH organization %s", license.UUID)
				continue
			}

			limits[license.UUID].Amount += license.LimitBytes
			limits[license.UUID].OrganizationUUID = "*"
		}
	}

	return limits, nil
}

// ReadLicense read the license from a file. Decode as a base64 string and
// parses it as a JSON.
func ReadLicense(filename string) (encodedInfo, signature []byte, err error) {
	var (
		encodedSignatureStr string
		encodedInfoStr      string
		ok                  bool
	)

	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, nil, err
	}

	var el map[string]interface{}
	err = json.Unmarshal(data, &el)
	if err != nil {
		return nil, nil, err
	}

	if encodedInfoStr, ok = el["encoded_info"].(string); !ok {
		return nil, nil, errors.New("Encoded info is not string")
	}

	if encodedSignatureStr, ok = el["signature"].(string); !ok {
		return nil, nil, errors.New("Signature info is not string")
	}

	return []byte(encodedInfoStr), []byte(encodedSignatureStr), nil
}

// DecodePEM reads and parses a PEM key with a public key
func DecodePEM(key []byte) (*rsa.PublicKey, error) {
	pemKey := strings.Replace(string(key), `\n`, "\n", -1)
	block, _ := pem.Decode([]byte(pemKey))
	if block == nil {
		return nil, errors.New("Error decoding PEM")
	}

	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	pubKey, ok := pub.(*rsa.PublicKey)
	if !ok {
		return nil, errors.New("Key is not an RSA key")
	}

	return pubKey, nil
}

// VerifyLicense checks if a encoded license has a valid signature using a
// public key.
func VerifyLicense(
	pub *rsa.PublicKey, encodedLicense, encodedSignature []byte,
) error {
	hashed := sha256.Sum256(encodedLicense)
	signatureLen := base64.URLEncoding.DecodedLen(len(encodedSignature))
	signature := make([]byte, signatureLen)

	n, err := base64.URLEncoding.Decode(signature, encodedSignature)
	if err != nil {
		return err
	}

	err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, hashed[:], signature[:n])
	if err != nil {
		return err
	}

	return nil
}

// DecodeLicense receives a JSON marshaled license a returns a struct with the
// information.
func DecodeLicense(encodedLicense []byte) (*License, error) {
	license := new(License)

	dataLen := base64.URLEncoding.DecodedLen(len(encodedLicense))
	data := make([]byte, dataLen)

	n, err := base64.URLEncoding.Decode(data, encodedLicense)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(data[:n], license)
	if err != nil {
		return nil, err
	}

	return license, nil
}

// FormatLicense create a pretty string given a license object
func FormatLicense(license *License) string {
	keyColor := color.New(color.FgYellow, color.Bold).SprintFunc()

	return fmt.Sprintf("Using license \n"+
		keyColor("\tUUID:         ")+"%s\n"+
		keyColor("\tCluster UUID: ")+"%s\n"+
		keyColor("\tExpires:      ")+"%s\n"+
		keyColor("\tLimit bytes:  ")+"%d\n",
		license.UUID,
		license.ClusterUUID,
		time.Unix(license.ExpireAt, 0).String(),
		license.LimitBytes,
	)
}
<commit_msg>:lipstick: Display organization_uuid license<commit_after>// Simple utility for counting messages on a Kafka topic.
//
// Copyright (C) 2017 ENEO Tecnologia SL
// Author: Diego Fernández Barrera <bigomby@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"time"

	"github.com/fatih/color"
)

// PubKey should be set on compile time
var PubKey string

type licenseStatus struct {
	OrganizationUUID string
	Amount           uint64
	Expired          bool
}

// LimitBytes is a map containing the max bytes for each organization
type LimitBytes map[string]*licenseStatus

func (l LimitBytes) getOrganizationLimits() map[string]uint64 {
	total := make(map[string]uint64)

	for _, v := range l {
		if !v.Expired {
			total[v.OrganizationUUID] += v.Amount
		}
	}

	return total
}

// License contains information about the limits of bytes that an organization
// can send.
type License struct {
	UUID         string         `json:"uuid"`
	ExpireAt     int64          `json:"expire_at"`
	LimitBytes   uint64         `json:"limit_bytes"`
	ClusterUUID  string         `json:"cluster_uuid"`
	Sensors      map[string]int `json:"sensors"`
	Organization string         `json:"organization_uuid"`
}

// LoadLicenses reads a redBorder license from a file and returns a License
// struct holding the decoded information.
func LoadLicenses(config *AppConfig) (LimitBytes, error) {
	limits := make(LimitBytes)

	log := log.WithField("prefix", "license")

	files, err := ioutil.ReadDir(config.LicensesDirectory)
	if err != nil {
		return nil, err
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}

		encodedLicense, encodedSignature, err :=
			ReadLicense(config.LicensesDirectory + "/" + file.Name())
		if err != nil {
			return nil, err
		}

		if PubKey != "" {
			pub, err := DecodePEM([]byte(PubKey))
			if err != nil {
				return nil, err
			}

			err = VerifyLicense(pub, encodedLicense, encodedSignature)
			if err != nil {
				log.Errorf(
					"Error verifying license \"%s\": %s", file.Name(), err.Error())
				continue
			}
		}

		license, err := DecodeLicense(encodedLicense)
		if err != nil {
			return nil, err
		}

		limits[license.UUID] = &licenseStatus{}

		expires := time.Unix(license.ExpireAt, 0)
		if expires.Before(time.Now()) {
			log.Warnf("License %s has expired", license.UUID)
			limits[license.UUID].Expired = true
			continue
		}

		log.Infoln(FormatLicense(license))

		if config.OrganizationMode {
			if license.Organization == "" {
				log.Warnf("Ignoring license WITH NO organization %s", license.UUID)
				continue
			}

			limits[license.UUID].Amount += license.LimitBytes
			limits[license.UUID].OrganizationUUID = license.Organization
		} else {
			if license.Organization != "" {
				log.Warnf("Ignoring license WITH organization %s", license.UUID)
				continue
			}

			limits[license.UUID].Amount += license.LimitBytes
			limits[license.UUID].OrganizationUUID = "*"
		}
	}

	return limits, nil
}

// ReadLicense read the license from a file. Decode as a base64 string and
// parses it as a JSON.
func ReadLicense(filename string) (encodedInfo, signature []byte, err error) {
	var (
		encodedSignatureStr string
		encodedInfoStr      string
		ok                  bool
	)

	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, nil, err
	}

	var el map[string]interface{}
	err = json.Unmarshal(data, &el)
	if err != nil {
		return nil, nil, err
	}

	if encodedInfoStr, ok = el["encoded_info"].(string); !ok {
		return nil, nil, errors.New("Encoded info is not string")
	}

	if encodedSignatureStr, ok = el["signature"].(string); !ok {
		return nil, nil, errors.New("Signature info is not string")
	}

	return []byte(encodedInfoStr), []byte(encodedSignatureStr), nil
}

// DecodePEM reads and parses a PEM key with a public key
func DecodePEM(key []byte) (*rsa.PublicKey, error) {
	pemKey := strings.Replace(string(key), `\n`, "\n", -1)
	block, _ := pem.Decode([]byte(pemKey))
	if block == nil {
		return nil, errors.New("Error decoding PEM")
	}

	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	pubKey, ok := pub.(*rsa.PublicKey)
	if !ok {
		return nil, errors.New("Key is not an RSA key")
	}

	return pubKey, nil
}

// VerifyLicense checks if a encoded license has a valid signature using a
// public key.
func VerifyLicense(
	pub *rsa.PublicKey, encodedLicense, encodedSignature []byte,
) error {
	hashed := sha256.Sum256(encodedLicense)
	signatureLen := base64.URLEncoding.DecodedLen(len(encodedSignature))
	signature := make([]byte, signatureLen)

	n, err := base64.URLEncoding.Decode(signature, encodedSignature)
	if err != nil {
		return err
	}

	err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, hashed[:], signature[:n])
	if err != nil {
		return err
	}

	return nil
}

// DecodeLicense receives a JSON marshaled license a returns a struct with the
// information.
func DecodeLicense(encodedLicense []byte) (*License, error) {
	license := new(License)

	dataLen := base64.URLEncoding.DecodedLen(len(encodedLicense))
	data := make([]byte, dataLen)

	n, err := base64.URLEncoding.Decode(data, encodedLicense)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(data[:n], license)
	if err != nil {
		return nil, err
	}

	return license, nil
}

// FormatLicense create a pretty string given a license object
func FormatLicense(license *License) string {
	keyColor := color.New(color.FgYellow, color.Bold).SprintFunc()

	return fmt.Sprintf("Using license \n"+
		keyColor("\tUUID:              ")+"%s\n"+
		keyColor("\tCluster UUID:      ")+"%s\n"+
		keyColor("\tOrganization UUID: ")+"%s\n"+
		keyColor("\tExpires:           ")+"%s\n"+
		keyColor("\tLimit bytes:       ")+"%d",
		license.UUID,
		license.ClusterUUID,
		license.Organization,
		time.Unix(license.ExpireAt, 0).String(),
		license.LimitBytes,
	)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/ianremmler/dgrl"
	"github.com/ianremmler/lit"
)

const usage = `usage:

lit [help | usage]             Show usage
lit init                       Initialize new issue tracker
lit new                        Create new issue
lit list [<sort>] <spec>       Show summary list of issues in spec
lit id [<sort>] <spec>         List ids in spec
lit show [<sort>] <spec>       Show issues in spec
lit set <field> <val> <spec>   Set issue field
lit comment <id> [<text>]      Add issue comment (opens editor if no text given)
lit edit <spec>                Edit issues in spec
lit close <spec>               Close issues in spec
lit reopen <spec>              Reopen closed issues in spec

sort: (sort|rsort) <field>

spec: all | <ids> | (with|without) <field> [<val>] | (less|greater) <field> <val>
      If field is comment, compare contents or timestamps based on search type`

// id, closed?, priority, assigned, tags, summary
const listFmt = "%-8.8s %-1.1s %-1.1s %-8.8s %-17.17s %s"

var (
	args    = os.Args[1:]
	it      = lit.New()
	listHdr = fmt.Sprintf(listFmt, "id", "c", "p", "assigned", "tags", "summary")
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("lit: ")

	cmd := ""
	if len(args) > 0 {
		cmd = args[0]
		args = args[1:]
	}
	switch cmd {
	case "", "-h", "-help", "--help", "help", "-u", "-usage", "--usage", "usage":
		usageCmd()
	case "list":
		listCmd()
	case "init":
		initCmd()
	case "new":
		newCmd()
	case "id":
		idCmd()
	case "show":
		showCmd()
	case "set":
		setCmd()
	case "comment":
		commentCmd()
	case "edit":
		editCmd()
	case "close", "reopen":
		closeCmd(cmd)
	default:
		log.Fatalln(cmd + " is not a valid command")
	}
}

func usageCmd() {
	fmt.Println(usage)
}

func initCmd() {
	err := it.Init()
	checkErr("init", err)
}

func newCmd() {
	loadIssues("new")
	id, err := it.NewIssue()
	checkErr("new", err)
	storeIssues("new")
	fmt.Println(id)
}

func listCmd() {
	loadIssues("list")
	doSort, field, doAscend := dispOpts("list")
	ids := specIds()
	if doSort {
		it.Sort(ids, field, doAscend)
	}
	fmt.Println(listHdr)
	for _, id := range ids {
		issue := it.Issue(id)
		if issue != nil {
			fmt.Println(listInfo(id, issue))
		}
	}
}

func idCmd() {
	loadIssues("id")
	doSort, field, doAscend := dispOpts("id")
	ids := specIds()
	if doSort {
		it.Sort(ids, field, doAscend)
	}
	for _, id := range ids {
		if it.Issue(id) != nil {
			fmt.Println(id)
		}
	}
}

func showCmd() {
	loadIssues("show")
	doSort, field, doAscend := dispOpts("show")
	ids := specIds()
	if doSort {
		it.Sort(ids, field, doAscend)
	}
	for _, id := range ids {
		fmt.Println(it.Issue(id))
	}
}

func setCmd() {
	if len(args) < 3 {
		log.Fatalln("set: you must specify a field, value, and spec")
	}
	key, val := args[0], args[1]
	args = args[2:]
	loadIssues("set")
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("set: error finding issue %s\n", id)
			continue
		}
		ok := lit.Set(issue, key, val)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("set: error updating fields in issue %s\n", id)
			continue
		}
	}
	storeIssues("set")
}

func editCmd() {
	editor := getEditor()
	if editor == "" {
		log.Fatalln("edit: VISUAL or EDITOR environment variable must be set")
	}
	if len(args) < 1 {
		log.Fatalln("edit: you must specify a spec to edit")
	}

	loadIssues("edit")

	// create temp file
	tempFile, err := ioutil.TempFile("", "lit-")
	checkErr("edit", err)
	filename := tempFile.Name()

	// load issue content into temp file
	ids := specIds()
	for _, id := range ids {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("edit: error finding issue %s\n", id)
			continue
		}
		if !lit.Set(issue, "updated", lit.Stamp()) {
			log.Printf("edit: error setting update time for issue %s\n", id)
			continue
		}
		fmt.Fprintln(tempFile, issue)
	}
	tempFile.Close()

	// get original file state
	origStat, err := os.Stat(filename)
	checkErr("edit", err)

	// launch editor
	cmd := exec.Command(editor, filename)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	err = cmd.Run()
	checkErr("edit", err)

	// get updated file state, compare to original
	newStat, err := os.Stat(filename)
	checkErr("edit", err)
	if newStat.ModTime() == origStat.ModTime() {
		log.Fatalln("edit: file unchanged")
	}

	// parse issue from temp file
	tempFile, err = os.Open(filename)
	checkErr("edit", err)
	edited := dgrl.NewParser().Parse(tempFile)
	tempFile.Close()
	if edited == nil {
		log.Fatalln("edit: error parsing file")
	}

	// update issues if we find a match
	didUpdate := false
	for _, id := range ids {
		issue := it.Issue(id)
		if issue == nil {
			// already printed error, so don't repeat here
			continue
		}
		for _, node := range edited.Kids() {
			if node.Type() == dgrl.BranchType && strings.HasPrefix(node.Key(), id) {
				if editedIssue, ok := node.(*dgrl.Branch); ok {
					*issue = *editedIssue
					didUpdate = true
					break
				}
			}
		}
	}
	if !didUpdate {
		log.Fatalln("edit: did not update anything")
	}

	storeIssues("edit")
}

func closeCmd(cmd string) {
	if len(args) < 1 {
		log.Fatalf("%s: you must specify a spec\n", cmd)
	}
	loadIssues(cmd)
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("%s: error finding issue %s\n", cmd, id)
			continue
		}
		closedStamp := ""
		if cmd == "close" {
			closedStamp = stamp
		}
		ok := lit.Set(issue, "closed", closedStamp)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("%s: error updating fields for issue %s\n", cmd, id)
			continue
		}
	}
	storeIssues(cmd)
}

func commentCmd() {
	if len(args) < 1 {
		log.Fatalln("comment: you must specify an issue to comment on")
	}
	id := args[0]
	loadIssues("comment")
	issue := it.Issue(id)
	if issue == nil {
		log.Fatalf("comment: error finding issue %s\n", id)
	}
	comment := ""
	if len(args) > 1 {
		comment = args[1]
	} else {
		comment = editComment()
	}
	commentBranch := dgrl.NewBranch(lit.Stamp())
	commentBranch.Append(dgrl.NewText(comment))
	issue.Append(commentBranch)
	storeIssues("comment")
}

func editComment() string {
	editor := getEditor()
	if editor == "" {
		log.Fatalln("comment: VISUAL or EDITOR environment variable must be set")
	}
	// create temp file
	tempFile, err := ioutil.TempFile("", "lit-")
	checkErr("comment", err)
	filename := tempFile.Name()

	// get original file state
	origStat, err := os.Stat(filename)
	checkErr("comment", err)

	// launch editor
	cmd := exec.Command(editor, filename)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	err = cmd.Run()
	checkErr("comment", err)

	// get updated file state, compare to original
	newStat, err := os.Stat(filename)
	checkErr("comment", err)
	if newStat.ModTime() == origStat.ModTime() {
		log.Fatalln("comment: file unchanged")
	}

	// read comment from file
	commentData, err := ioutil.ReadFile(filename)
	checkErr("comment", err)
	return string(commentData)
}

func listInfo(id string, issue *dgrl.Branch) string {
	status := " "
	closed, _ := lit.Get(issue, "closed")
	if len(closed) > 0 {
		status = "*"
	}
	tags, _ := lit.Get(issue, "tags")
	priority, _ := lit.Get(issue, "priority")
	assigned, _ := lit.Get(issue, "assigned")
	summary, _ := lit.Get(issue, "summary")
	return fmt.Sprintf(listFmt, id, status, priority, assigned, tags, summary)
}

func keyval(kv []string) (string, string) {
	key, val := "", ""
	if len(kv) > 0 {
		key = kv[0]
	}
	if len(kv) > 1 {
		val = kv[1]
	}
	return key, val
}

func matchIds(kv []string, doesMatch bool) []string {
	key, val := keyval(kv)
	return it.Match(key, val, doesMatch)
}

func compareIds(kv []string, isLess bool) []string {
	key, val := keyval(kv)
	return it.Compare(key, val, isLess)
}

func dispOpts(cmd string) (bool, string, bool) {
	switch {
	case len(args) == 0:
		return false, "", true
	case args[0] == "sortby" || args[0] == "rsortby":
		if len(args) < 2 {
			log.Fatalf("%s: sort requested, but no field given to sort by\n", cmd)
		}
		doSort := true
		doAscend := (args[0] == "sortby")
		field := args[1]
		args = args[2:]
		return doSort, field, doAscend
	}
	return false, "", true
}

func specIds() []string {
	ids := []string{}
	switch {
	case len(args) == 0:
		return ids
	case args[0] == "with":
		ids = matchIds(args[1:], true)
	case args[0] == "without":
		ids = matchIds(args[1:], false)
	case args[0] == "less":
		ids = compareIds(args[1:], true)
	case args[0] == "greater":
		ids = compareIds(args[1:], false)
	case args[0] == "all":
		ids = it.IssueIds()
	default:
		ids = args
	}
	return ids
}

func loadIssues(cmd string) {
	err := it.Load()
	checkErr(cmd, err)
}

func storeIssues(cmd string) {
	err := it.Store()
	checkErr(cmd, err)
}

func checkErr(cmd string, err error) {
	if err != nil {
		log.Fatalf("%s: %s\n", cmd, err)
	}
}

func getEditor() string {
	editor := os.Getenv("VISUAL")
	if editor == "" {
		editor = os.Getenv("EDITOR")
	}
	return editor
}
<commit_msg>Added package cmd var to avoid passing it around.<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/ianremmler/dgrl"
	"github.com/ianremmler/lit"
)

const usage = `usage:

lit [help | usage]              Show usage
lit init                        Initialize new issue tracker
lit new                         Create new issue
lit list [<sort>] <spec>        Show summary list of issues in spec
lit id [<sort>] <spec>          List ids in spec
lit show [<sort>] <spec>        Show issues in spec
lit set <field> <val> <spec>    Set issue field
lit comment <id> [<text>]       Add issue comment (opens editor if no text given)
lit edit <spec>                 Edit issues in spec
lit close <spec>                Close issues in spec
lit reopen <spec>               Reopen closed issues in spec

sort: (sortby|rsortby) <field>  Sort (reverse if rsortby) based on field

spec: all | <ids> | (with|without) <field> [<val>] | (less|greater) <field> <val>
      If field is 'comment', compare contents or timestamps based on search type`

// id, closed?, priority, assigned, tags, summary
const listFmt = "%-8.8s %-1.1s %-1.1s %-8.8s %-17.17s %s"

var (
	args    = os.Args[1:]
	it      = lit.New()
	listHdr = fmt.Sprintf(listFmt, "id", "c", "p", "assigned", "tags", "summary")
	cmd     string
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("lit: ")

	if len(args) > 0 {
		cmd = args[0]
		args = args[1:]
	}
	switch cmd {
	case "", "-h", "-help", "--help", "help", "-u", "-usage", "--usage", "usage":
		usageCmd()
	case "list":
		listCmd()
	case "init":
		initCmd()
	case "new":
		newCmd()
	case "id":
		idCmd()
	case "show":
		showCmd()
	case "set":
		setCmd()
	case "comment":
		commentCmd()
	case "edit":
		editCmd()
	case "close", "reopen":
		closeCmd()
	default:
		log.Fatalln(cmd + " is not a valid command")
	}
}

func usageCmd() {
	fmt.Println(usage)
}

func initCmd() {
	err := it.Init()
	checkErr(err)
}

func newCmd() {
	loadIssues()
	id, err := it.NewIssue()
	checkErr(err)
	storeIssues()
	fmt.Println(id)
}

func listCmd() {
	loadIssues()
	doSort, field, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, field, doAscend)
	}
	fmt.Println(listHdr)
	for _, id := range ids {
		issue := it.Issue(id)
		if issue != nil {
			fmt.Println(listInfo(id, issue))
		}
	}
}

func idCmd() {
	loadIssues()
	doSort, field, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, field, doAscend)
	}
	for _, id := range ids {
		if it.Issue(id) != nil {
			fmt.Println(id)
		}
	}
}

func showCmd() {
	loadIssues()
	doSort, field, doAscend := dispOpts()
	ids := specIds()
	if doSort {
		it.Sort(ids, field, doAscend)
	}
	for _, id := range ids {
		fmt.Println(it.Issue(id))
	}
}

func setCmd() {
	if len(args) < 3 {
		log.Fatalln("set: you must specify a field, value, and spec")
	}
	key, val := args[0], args[1]
	args = args[2:]
	loadIssues()
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("set: error finding issue %s\n", id)
			continue
		}
		ok := lit.Set(issue, key, val)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("set: error updating fields in issue %s\n", id)
			continue
		}
	}
	storeIssues()
}

func editCmd() {
	editor := getEditor()
	if editor == "" {
		log.Fatalln("edit: VISUAL or EDITOR environment variable must be set")
	}
	if len(args) < 1 {
		log.Fatalln("edit: you must specify a spec to edit")
	}

	loadIssues()

	// create temp file
	tempFile, err := ioutil.TempFile("", "lit-")
	checkErr(err)
	filename := tempFile.Name()

	// load issue content into temp file
	ids := specIds()
	for _, id := range ids {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("edit: error finding issue %s\n", id)
			continue
		}
		if !lit.Set(issue, "updated", lit.Stamp()) {
			log.Printf("edit: error setting update time for issue %s\n", id)
			continue
		}
		fmt.Fprintln(tempFile, issue)
	}
	tempFile.Close()

	// get original file state
	origStat, err := os.Stat(filename)
	checkErr(err)

	// launch editor
	ed := exec.Command(editor, filename)
	ed.Stdin, ed.Stdout, ed.Stderr = os.Stdin, os.Stdout, os.Stderr
	err = ed.Run()
	checkErr(err)

	// get updated file state, compare to original
	newStat, err := os.Stat(filename)
	checkErr(err)
	if newStat.ModTime() == origStat.ModTime() {
		log.Fatalln("edit: file unchanged")
	}

	// parse issue from temp file
	tempFile, err = os.Open(filename)
	checkErr(err)
	edited := dgrl.NewParser().Parse(tempFile)
	tempFile.Close()
	if edited == nil {
		log.Fatalln("edit: error parsing file")
	}

	// update issues if we find a match
	didUpdate := false
	for _, id := range ids {
		issue := it.Issue(id)
		if issue == nil {
			// already printed error, so don't repeat here
			continue
		}
		for _, node := range edited.Kids() {
			if node.Type() == dgrl.BranchType && strings.HasPrefix(node.Key(), id) {
				if editedIssue, ok := node.(*dgrl.Branch); ok {
					*issue = *editedIssue
					didUpdate = true
					break
				}
			}
		}
	}
	if !didUpdate {
		log.Fatalln("edit: did not update anything")
	}

	storeIssues()
}

func closeCmd() {
	if len(args) < 1 {
		log.Fatalf("%s: you must specify a spec\n", cmd)
	}
	loadIssues()
	stamp := lit.Stamp()
	for _, id := range specIds() {
		issue := it.Issue(id)
		if issue == nil {
			log.Printf("%s: error finding issue %s\n", cmd, id)
			continue
		}
		closedStamp := ""
		if cmd == "close" {
			closedStamp = stamp
		}
		ok := lit.Set(issue, "closed", closedStamp)
		ok = ok && lit.Set(issue, "updated", stamp)
		if !ok {
			log.Printf("%s: error updating fields for issue %s\n", cmd, id)
			continue
		}
	}
	storeIssues()
}

func commentCmd() {
	if len(args) < 1 {
		log.Fatalln("comment: you must specify an issue to comment on")
	}
	id := args[0]
	loadIssues()
	issue := it.Issue(id)
	if issue == nil {
		log.Fatalf("comment: error finding issue %s\n", id)
	}
	comment := ""
	if len(args) > 1 {
		comment = args[1]
	} else {
		comment = editComment()
	}
	commentBranch := dgrl.NewBranch(lit.Stamp())
	commentBranch.Append(dgrl.NewText(comment))
	issue.Append(commentBranch)
	storeIssues()
}

func editComment() string {
	editor := getEditor()
	if editor == "" {
		log.Fatalln("comment: VISUAL or EDITOR environment variable must be set")
	}
	// create temp file
	tempFile, err := ioutil.TempFile("", "lit-")
	checkErr(err)
	filename := tempFile.Name()

	// get original file state
	origStat, err := os.Stat(filename)
	checkErr(err)

	// launch editor
	ed := exec.Command(editor, filename)
	ed.Stdin, ed.Stdout, ed.Stderr = os.Stdin, os.Stdout, os.Stderr
	err = ed.Run()
	checkErr(err)

	// get updated file state, compare to original
	newStat, err := os.Stat(filename)
	checkErr(err)
	if newStat.ModTime() == origStat.ModTime() {
		log.Fatalln("comment: file unchanged")
	}

	// read comment from file
	commentData, err := ioutil.ReadFile(filename)
	checkErr(err)
	return string(commentData)
}

func listInfo(id string, issue *dgrl.Branch) string {
	status := " "
	closed, _ := lit.Get(issue, "closed")
	if len(closed) > 0 {
		status = "*"
	}
	tags, _ := lit.Get(issue, "tags")
	priority, _ := lit.Get(issue, "priority")
	assigned, _ := lit.Get(issue, "assigned")
	summary, _ := lit.Get(issue, "summary")
	return fmt.Sprintf(listFmt, id, status, priority, assigned, tags, summary)
}

func keyval(kv []string) (string, string) {
	key, val := "", ""
	if len(kv) > 0 {
		key = kv[0]
	}
	if len(kv) > 1 {
		val = kv[1]
	}
	return key, val
}

func matchIds(kv []string, doesMatch bool) []string {
	key, val := keyval(kv)
	return it.Match(key, val, doesMatch)
}

func compareIds(kv []string, isLess bool) []string {
	key, val := keyval(kv)
	return it.Compare(key, val, isLess)
}

func dispOpts() (bool, string, bool) {
	switch {
	case len(args) == 0:
		return false, "", true
	case args[0] == "sortby" || args[0] == "rsortby":
		if len(args) < 2 {
			log.Fatalf("%s: sort requested, but no field given to sort by\n", cmd)
		}
		doSort := true
		doAscend := (args[0] == "sortby")
		field := args[1]
		args = args[2:]
		return doSort, field, doAscend
	}
	return false, "", true
}

func specIds() []string {
	ids := []string{}
	switch {
	case len(args) == 0:
		return ids
	case args[0] == "with":
		ids = matchIds(args[1:], true)
	case args[0] == "without":
		ids = matchIds(args[1:], false)
	case args[0] == "less":
		ids = compareIds(args[1:], true)
	case args[0] == "greater":
		ids = compareIds(args[1:], false)
	case args[0] == "all":
		ids = it.IssueIds()
	default:
		ids = args
	}
	return ids
}

func loadIssues() {
	err := it.Load()
	checkErr(err)
}

func storeIssues() {
	err := it.Store()
	checkErr(err)
}

func checkErr(err error) {
	if err != nil {
		log.Fatalf("%s: %s\n", cmd, err)
	}
}

func getEditor() string {
	editor := os.Getenv("VISUAL")
	if editor == "" {
		editor = os.Getenv("EDITOR")
	}
	return editor
}
<|endoftext|>