<commit_before>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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"
	"log"
	entity "github.com/LeungChiHo/agenda/tree/master/entity"
	"github.com/spf13/cobra"
)

// MeetingCreateCmd represents the MeetingCreate command
var MeetingCreateCmd = &cobra.Command{
	Use:   "create -t [Title] -p [Participator] -s [StartTime] -e [EndTime]",
	Short: "To create a new meeting",
	Long: `To create a new meeting with:

[Title] the Title of the meeting
[Participator] the Participator of the meeting,the Participator can only attend one meeting during one meeting time
[StartTime] the StartTime of the meeting
[EndTime] the EndTime of the meeting`,
	Run: func(cmd *cobra.Command, args []string) {
		debugLog := log.New(logFile,"[Result]", log.Ldate|log.Ltime|log.Lshortfile)
		if entity.StartAgenda() == false {
			debugLog.Println("Fail, please log in")
			fmt.Println("Fail, please log in")
		}
		arg_t, _ := cmd.Flags().GetString("Title")
		arg_p, _ := cmd.Flags().GetStringSlice("Participator")
		arg_s, _ := cmd.Flags().GetString("StartTime")
		arg_e, _ := cmd.Flags().GetString("EndTime")
		if entity.CreateMeeting(entity.CurrentUser.Name, arg_t, arg_s, arg_e, arg_p) {
			debugLog.Println("Create meeting successfully")
			fmt.Println("Create meeting successfully")
		} else {
			debugLog.Println("Fail to create meeting")
			fmt.Println("Fail to create meeting")
		}
		entity.QuitAgenda()
	},
}

func init() {
	RootCmd.AddCommand(MeetingCreateCmd)
	MeetingCreateCmd.Flags().StringP("Title", "t", "", "meeting title")
	MeetingCreateCmd.Flags().StringSliceP("Participator", "p", []string{}, "meeting's participator")
	MeetingCreateCmd.Flags().StringP("StartTime", "s", "", "meeting's startTime")
	MeetingCreateCmd.Flags().StringP("EndTime", "e", "", "meeting's endTime")
	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// MeetingCreateCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// MeetingCreateCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
<commit_msg>Update MeetingCreate.go<commit_after>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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"
	"log"
	entity "github.com/LeungChiHo/agenda/entity"
	"github.com/spf13/cobra"
)

// MeetingCreateCmd represents the MeetingCreate command
var MeetingCreateCmd = &cobra.Command{
	Use:   "create -t [Title] -p [Participator] -s [StartTime] -e [EndTime]",
	Short: "To create a new meeting",
	Long: `To create a new meeting with:

[Title] the Title of the meeting
[Participator] the Participator of the meeting,the Participator can only attend one meeting during one meeting time
[StartTime] the StartTime of the meeting
[EndTime] the EndTime of the meeting`,
	Run: func(cmd *cobra.Command, args []string) {
		debugLog := log.New(logFile,"[Result]", log.Ldate|log.Ltime|log.Lshortfile)
		if entity.StartAgenda() == false {
			debugLog.Println("Fail, please log in")
			fmt.Println("Fail, please log in")
		}
		arg_t, _ := cmd.Flags().GetString("Title")
		arg_p, _ := cmd.Flags().GetStringSlice("Participator")
		arg_s, _ := cmd.Flags().GetString("StartTime")
		arg_e, _ := cmd.Flags().GetString("EndTime")
		if entity.CreateMeeting(entity.CurrentUser.Name, arg_t, arg_s, arg_e, arg_p) {
			debugLog.Println("Create meeting successfully")
			fmt.Println("Create meeting successfully")
		} else {
			debugLog.Println("Fail to create meeting")
			fmt.Println("Fail to create meeting")
		}
		entity.QuitAgenda()
	},
}

func init() {
	RootCmd.AddCommand(MeetingCreateCmd)
	MeetingCreateCmd.Flags().StringP("Title", "t", "", "meeting title")
	MeetingCreateCmd.Flags().StringSliceP("Participator", "p", []string{}, "meeting's participator")
	MeetingCreateCmd.Flags().StringP("StartTime", "s", "", "meeting's startTime")
	MeetingCreateCmd.Flags().StringP("EndTime", "e", "", "meeting's endTime")
	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// MeetingCreateCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// MeetingCreateCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"strings"

	bmapp "github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/cliutil"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/bgentry/speakeasy"
	"github.com/urfave/cli"
)

// forceFlag is common to a bunch of commands and can have a generic Usage.
var forceFlag = cli.BoolFlag{
	Name:  "force",
	Usage: "Do not prompt for confirmation when destroying data or increasing costs.",
}

//commands is assembled during init()
var commands = make([]cli.Command, 0)

//adminCommands is assembled during init() and has the commands that're only available when --admin is specified.
// it gets merged in to commands
var adminCommands = make([]cli.Command, 0)

func main() {
	// watch for interrupts (Ctrl-C) and exit "gracefully" if they are encountered.
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	go func() {
		for range ch {
			log.Error("\r\nCaught an interrupt - exiting.\r\n")
			// os.Exit is not actually graceful but WHATEVER I don't
			// actually have a better way since bytemark-client has no
			// main-loop or anything - it's just a one-shot
			os.Exit(int(util.ExitCodeTrappedInterrupt))
		}

	}()

	overrideHelp()
	flags, args, config := prepConfig()

	// add admin commands if --admin is set
	wantAdminCmds, err := config.GetBool("admin")
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}

	myCommands := commands
	if wantAdminCmds {
		myCommands = cliutil.MergeCommands(commands, adminCommands)
	}

	app, err := bmapp.BaseAppSetup(flags, myCommands)
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}

	client, err := lib.NewWithURLs(lib.EndpointURLs{
		Brain:   config.GetIgnoreErr("endpoint"),
		API:     config.GetIgnoreErr("api-endpoint"),
		Billing: config.GetIgnoreErr("billing-endpoint"),
		SPP:     config.GetIgnoreErr("spp-endpoint"),
		Auth:    config.GetIgnoreErr("auth-endpoint"),
	})
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}
	client.SetDebugLevel(config.GetDebugLevel())

	app.Metadata = map[string]interface{}{
		"client": client,
		"config": config,
	}

	outputDebugInfo(config)

	err = app.Run(args)

	os.Exit(int(util.ProcessError(err)))
}

func outputDebugInfo(config util.ConfigManager) {
	log.Debugf(log.LvlOutline, "bytemark-client %s\r\n\r\n", lib.Version)
	// assemble a string of config vars (excluding token)
	vars, err := config.GetAll()
	if err != nil {
		log.Debugf(log.LvlFlags, "(not a real problem maybe): had trouble getting all config vars: %s\r\n", err.Error())
	}

	log.Debugf(log.LvlFlags, "reading config from %s\r\n\r\n", config.ConfigDir())
	log.Debug(log.LvlFlags, "config vars:")
	for _, v := range vars {
		if v.Name == "token" {
			log.Debugf(log.LvlFlags, "  %s (%s): not printed for security\r\n", v.Name, v.Source)
			continue
		}
		log.Debugf(log.LvlFlags, "  %s (%s): '%s'\r\n", v.Name, v.Source, v.Value)
	}
	log.Debug(log.LvlFlags, "")

	log.Debugf(log.LvlFlags, "invocation: %s\r\n\r\n", strings.Join(os.Args, " "))
}

// overrideHelp writes our own help templates into urfave/cli
func overrideHelp() {
	cli.SubcommandHelpTemplate = `NAME:
   {{.HelpName}} - {{.Usage}}

USAGE:
   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}

COMMANDS:{{range .VisibleCategories}}{{if .Name}}
   {{.Name}}:{{end}}{{range .VisibleCommands}}
     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
{{end}}{{if .VisibleFlags}}
OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`

	cli.CommandHelpTemplate = `NAME:
   {{.HelpName}} - {{.Usage}}

USAGE:
   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}

CATEGORY:
   {{.Category}}{{end}}{{if .Description}}

DESCRIPTION:
   {{.Description}}{{end}}{{if .VisibleFlags}}

OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`
}

func prepConfig() (flags []cli.Flag, args []string, config util.ConfigManager) {
	// set up our global flags because we need some config before we can set up our App
	flagset := flag.NewFlagSet("flags", flag.ContinueOnError)
	help := flagset.Bool("help", false, "")
	h := flagset.Bool("h", false, "")
	version := flagset.Bool("version", false, "")
	v := flagset.Bool("v", false, "")

	flags = bmapp.GlobalFlags()
	for _, f := range flags {
		f.Apply(flagset)
	}

	flagset.SetOutput(ioutil.Discard)

	err := flagset.Parse(os.Args[1:])
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}
	configDir := flagset.Lookup("config-dir").Value.String()
	config, err = util.NewConfig(configDir)
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}
	flargs := config.ImportFlags(flagset)

	//juggle the arguments in order to get the executable on the beginning
	args = make([]string, len(flargs)+1)
	if len(flargs) > 0 && flargs[0] == "help" {
		copy(args[1:], flargs[1:])
		args[len(args)-1] = "--help"
	} else if len(flargs) > 0 && flargs[0] == "version" {
		copy(args[1:], flargs[1:])
		args[len(args)-1] = "--version"
	} else {
		copy(args[1:], flargs)
	}
	args[0] = os.Args[0]

	if *help || *h {
		helpArgs := make([]string, len(args)+1)
		helpArgs[len(args)] = "--help"
		copy(helpArgs, args)
		args = helpArgs
	} else if *version || *v {
		versionArgs := make([]string, len(args)+1)
		versionArgs[len(args)] = "--version"
		copy(versionArgs, args)
		args = versionArgs
	}
	return
}

// PromptForCredentials ensures that user, pass and yubikey-otp are defined, by prompting the user for them.
// needs a for loop to ensure that they don't stay empty.
// returns nil on success or an error on failure
func PromptForCredentials(config util.ConfigManager) error {
	userVar, _ := config.GetV("user")
	for userVar.Value == "" || userVar.Source != "INTERACTION" {
		if userVar.Value != "" {
			user := util.Prompt(fmt.Sprintf("User [%s]: ", userVar.Value))
			if strings.TrimSpace(user) == "" {
				config.Set("user", userVar.Value, "INTERACTION")
			} else {
				config.Set("user", strings.TrimSpace(user), "INTERACTION")
			}
		} else {
			user := util.Prompt("User: ")
			config.Set("user", strings.TrimSpace(user), "INTERACTION")
		}
		userVar, _ = config.GetV("user")
	}

	for config.GetIgnoreErr("pass") == "" {
		pass, err := speakeasy.FAsk(os.Stderr, "Pass: ")

		if err != nil {
			return err
		}
		config.Set("pass", strings.TrimSpace(pass), "INTERACTION")
	}

	if config.GetIgnoreErr("yubikey") != "" {
		for config.GetIgnoreErr("yubikey-otp") == "" {
			yubikey := util.Prompt("Press yubikey: ")
			config.Set("yubikey-otp", strings.TrimSpace(yubikey), "INTERACTION")
		}
	}
	log.Log("")
	return nil
}
<commit_msg>get main() to use SetClientAndConfig<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"strings"

	bmapp "github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/cliutil"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/bgentry/speakeasy"
	"github.com/urfave/cli"
)

// forceFlag is common to a bunch of commands and can have a generic Usage.
var forceFlag = cli.BoolFlag{
	Name:  "force",
	Usage: "Do not prompt for confirmation when destroying data or increasing costs.",
}

//commands is assembled during init()
var commands = make([]cli.Command, 0)

//adminCommands is assembled during init() and has the commands that're only available when --admin is specified.
// it gets merged in to commands
var adminCommands = make([]cli.Command, 0)

func main() {
	// watch for interrupts (Ctrl-C) and exit "gracefully" if they are encountered.
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	go func() {
		for range ch {
			log.Error("\r\nCaught an interrupt - exiting.\r\n")
			// os.Exit is not actually graceful but WHATEVER I don't
			// actually have a better way since bytemark-client has no
			// main-loop or anything - it's just a one-shot
			os.Exit(int(util.ExitCodeTrappedInterrupt))
		}

	}()

	overrideHelp()
	flags, args, config := prepConfig()

	// add admin commands if --admin is set
	wantAdminCmds, err := config.GetBool("admin")
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}

	myCommands := commands
	if wantAdminCmds {
		myCommands = cliutil.MergeCommands(commands, adminCommands)
	}

	app, err := bmapp.BaseAppSetup(flags, myCommands)
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}

	client, err := lib.NewWithURLs(lib.EndpointURLs{
		Brain:   config.GetIgnoreErr("endpoint"),
		API:     config.GetIgnoreErr("api-endpoint"),
		Billing: config.GetIgnoreErr("billing-endpoint"),
		SPP:     config.GetIgnoreErr("spp-endpoint"),
		Auth:    config.GetIgnoreErr("auth-endpoint"),
	})
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}
	client.SetDebugLevel(config.GetDebugLevel())

	bmapp.SetClientAndConfig(app, client, config)

	outputDebugInfo(config)

	err = app.Run(args)

	os.Exit(int(util.ProcessError(err)))
}

func outputDebugInfo(config util.ConfigManager) {
	log.Debugf(log.LvlOutline, "bytemark-client %s\r\n\r\n", lib.Version)
	// assemble a string of config vars (excluding token)
	vars, err := config.GetAll()
	if err != nil {
		log.Debugf(log.LvlFlags, "(not a real problem maybe): had trouble getting all config vars: %s\r\n", err.Error())
	}

	log.Debugf(log.LvlFlags, "reading config from %s\r\n\r\n", config.ConfigDir())
	log.Debug(log.LvlFlags, "config vars:")
	for _, v := range vars {
		if v.Name == "token" {
			log.Debugf(log.LvlFlags, "  %s (%s): not printed for security\r\n", v.Name, v.Source)
			continue
		}
		log.Debugf(log.LvlFlags, "  %s (%s): '%s'\r\n", v.Name, v.Source, v.Value)
	}
	log.Debug(log.LvlFlags, "")

	log.Debugf(log.LvlFlags, "invocation: %s\r\n\r\n", strings.Join(os.Args, " "))
}

// overrideHelp writes our own help templates into urfave/cli
func overrideHelp() {
	cli.SubcommandHelpTemplate = `NAME:
   {{.HelpName}} - {{.Usage}}

USAGE:
   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}

COMMANDS:{{range .VisibleCategories}}{{if .Name}}
   {{.Name}}:{{end}}{{range .VisibleCommands}}
     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
{{end}}{{if .VisibleFlags}}
OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`

	cli.CommandHelpTemplate = `NAME:
   {{.HelpName}} - {{.Usage}}

USAGE:
   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}

CATEGORY:
   {{.Category}}{{end}}{{if .Description}}

DESCRIPTION:
   {{.Description}}{{end}}{{if .VisibleFlags}}

OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`
}

func prepConfig() (flags []cli.Flag, args []string, config util.ConfigManager) {
	// set up our global flags because we need some config before we can set up our App
	flagset := flag.NewFlagSet("flags", flag.ContinueOnError)
	help := flagset.Bool("help", false, "")
	h := flagset.Bool("h", false, "")
	version := flagset.Bool("version", false, "")
	v := flagset.Bool("v", false, "")

	flags = bmapp.GlobalFlags()
	for _, f := range flags {
		f.Apply(flagset)
	}

	flagset.SetOutput(ioutil.Discard)

	err := flagset.Parse(os.Args[1:])
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}
	configDir := flagset.Lookup("config-dir").Value.String()
	config, err = util.NewConfig(configDir)
	if err != nil {
		os.Exit(int(util.ProcessError(err)))
	}
	flargs := config.ImportFlags(flagset)

	//juggle the arguments in order to get the executable on the beginning
	args = make([]string, len(flargs)+1)
	if len(flargs) > 0 && flargs[0] == "help" {
		copy(args[1:], flargs[1:])
		args[len(args)-1] = "--help"
	} else if len(flargs) > 0 && flargs[0] == "version" {
		copy(args[1:], flargs[1:])
		args[len(args)-1] = "--version"
	} else {
		copy(args[1:], flargs)
	}
	args[0] = os.Args[0]

	if *help || *h {
		helpArgs := make([]string, len(args)+1)
		helpArgs[len(args)] = "--help"
		copy(helpArgs, args)
		args = helpArgs
	} else if *version || *v {
		versionArgs := make([]string, len(args)+1)
		versionArgs[len(args)] = "--version"
		copy(versionArgs, args)
		args = versionArgs
	}
	return
}

// PromptForCredentials ensures that user, pass and yubikey-otp are defined, by prompting the user for them.
// needs a for loop to ensure that they don't stay empty.
// returns nil on success or an error on failure
func PromptForCredentials(config util.ConfigManager) error {
	userVar, _ := config.GetV("user")
	for userVar.Value == "" || userVar.Source != "INTERACTION" {
		if userVar.Value != "" {
			user := util.Prompt(fmt.Sprintf("User [%s]: ", userVar.Value))
			if strings.TrimSpace(user) == "" {
				config.Set("user", userVar.Value, "INTERACTION")
			} else {
				config.Set("user", strings.TrimSpace(user), "INTERACTION")
			}
		} else {
			user := util.Prompt("User: ")
			config.Set("user", strings.TrimSpace(user), "INTERACTION")
		}
		userVar, _ = config.GetV("user")
	}

	for config.GetIgnoreErr("pass") == "" {
		pass, err := speakeasy.FAsk(os.Stderr, "Pass: ")

		if err != nil {
			return err
		}
		config.Set("pass", strings.TrimSpace(pass), "INTERACTION")
	}

	if config.GetIgnoreErr("yubikey") != "" {
		for config.GetIgnoreErr("yubikey-otp") == "" {
			yubikey := util.Prompt("Press yubikey: ")
			config.Set("yubikey-otp", strings.TrimSpace(yubikey), "INTERACTION")
		}
	}
	log.Log("")
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/urfave/cli"
	"net"
)

// ProviderFunc is the function type that can be passed to With()
type ProviderFunc func(*Context) error

// With is a convenience function for making cli.Command.Actions that sets up a Context, runs all the providers, cleans up afterward and returns errors from the actions if there is one
func With(providers ...ProviderFunc) func(c *cli.Context) error {
	return func(cliContext *cli.Context) error {
		c := Context{Context: cliContext}
		err := foldProviders(&c, providers...)
		cleanup(&c)
		return err
	}
}

// cleanup resets the value of special flags between invocations of global.App.Run so that the tests pass.
// This is needed because the init() functions are only executed once during the testing cycle.
// Outside of the tests, global.App.Run is only called once before the program closes.
func cleanup(c *Context) {
	ips, ok := c.Context.Generic("ip").(*util.IPFlag)
	if ok {
		*ips = make([]net.IP, 0)
	}
	disc, ok := c.Context.Generic("disc").(*util.DiscSpecFlag)
	if ok {
		*disc = make([]brain.Disc, 0)
	}
	size, ok := c.Context.Generic("memory").(*util.SizeSpecFlag)
	if ok {
		*size = 0
	}
	server, ok := c.Context.Generic("server").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("from").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("to").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	group, ok := c.Context.Generic("group").(*GroupNameFlag)
	if ok {
		*group = GroupNameFlag{}
	}
}

// foldProviders runs all the providers with the given context, stopping if there's an error
func foldProviders(c *Context, providers ...ProviderFunc) (err error) {
	for _, provider := range providers {
		err = provider(c)
		if err != nil {
			return
		}
	}
	return
}

// OptionalArgs takes a list of flag names. For each flag name it attempts to read the next arg and set the flag with the corresponding name.
// for instance:
// OptionalArgs("server", "disc", "size")
// will attempt to read 3 arguments, setting the "server" flag to the first, "disc" to the 2nd, "size" to the third.
func OptionalArgs(args ...string) ProviderFunc {
	return func(c *Context) error {
		for _, name := range args {
			value, err := c.NextArg()
			if err != nil {
				// if c.NextArg errors that means there aren't more arguments
				// so we just return nil - returning an error would stop the execution of the action.
				return nil
			}
			err = c.Context.Set(name, value)
			if err != nil {
				return err
			}
		}
		return nil
	}
}

// JoinArgs is like OptionalArgs, but reads up to n arguments joined with spaces and sets the one named flag.
// if n is not set, reads all the remaining arguments.
func JoinArgs(flagName string, n ...int) ProviderFunc {
	return func(c *Context) (err error) {
		toRead := 0
		if len(n) == 0 {
			toRead = len(c.Args())
		} else {
			toRead = n[0]
		}

		value := ""
		for i := 0; i < toRead; i++ {
			arg, err := c.NextArg()
			if err != nil {
				// don't return the error - just means we ran out of arguments to slurp
				break
			}
			if i == 0 {
				value += arg
			} else {
				value += " " + arg
			}
		}
		err = c.Context.Set(flagName, value)
		return

	}
}

// AccountProvider gets an account name from a flag, then the account details from the API, then stitches it to the context
func AccountProvider(flagName string) ProviderFunc {
	return func(c *Context) (err error) {
		c.Account, err = global.Client.GetAccount(c.String(flagName))
		return
	}
}

// AuthProvider makes sure authentication has been successfully completed, attempting it if necessary.
func AuthProvider(c *Context) (err error) {
	if !c.Authed {
		err = EnsureAuth()
		if err != nil {
			return
		}
	}
	c.Authed = true
	return
}

// DefinitionsProvider gets the Definitions from the brain and attaches them to the Context.
func DefinitionsProvider(c *Context) (err error) {
	if c.Definitions != nil {
		return
	}
	c.Definitions, err = global.Client.ReadDefinitions()
	return
}

// GroupProvider gets a GroupName from a flag, then gets the named Group from the brain and attaches it to the Context.
func GroupProvider(flagName string) ProviderFunc {
	return func(c *Context) (err error) {
		if c.Group != nil {
			return
		}
		err = AuthProvider(c)
		if err != nil {
			return
		}

		groupName := c.GroupName(flagName)
		c.Group, err = global.Client.GetGroup(&groupName)
		return
	}
}

// UserProvider calls UserNameProvider, gets the User from the brain, and attaches it to the Context.
func UserProvider(flagName string) ProviderFunc {
	return func(c *Context) (err error) {
		if c.User != nil {
			return
		}
		user := c.String(flagName)
		if user != "" {
			user = global.Config.GetIgnoreErr("user")
		}
		if err = AuthProvider(c); err != nil {
			return
		}
		c.User, err = global.Client.GetUser(user)
		return
	}
}

// VirtualMachineProvider gets a VirtualMachineName from a flag, then gets the named VirtualMachine from the brain and attaches it to the Context.
func VirtualMachineProvider(flagName string) ProviderFunc {
	return func(c *Context) (err error) {
		if c.VirtualMachine != nil {
			return
		}
		err = AuthProvider(c)
		if err != nil {
			return
		}
		vmName := c.VirtualMachineName(flagName)
		c.VirtualMachine, err = global.Client.GetVirtualMachine(&vmName)
		return
	}
}
<commit_msg>Fiddle with JoinArgs<commit_after>package main

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/urfave/cli"
	"net"
)

// ProviderFunc is the function type that can be passed to With()
type ProviderFunc func(*Context) error

// With is a convenience function for making cli.Command.Actions that sets up a Context, runs all the providers, cleans up afterward and returns errors from the actions if there is one
func With(providers ...ProviderFunc) func(c *cli.Context) error {
	return func(cliContext *cli.Context) error {
		c := Context{Context: cliContext}
		err := foldProviders(&c, providers...)
		cleanup(&c)
		return err
	}
}

// cleanup resets the value of special flags between invocations of global.App.Run so that the tests pass.
// This is needed because the init() functions are only executed once during the testing cycle.
// Outside of the tests, global.App.Run is only called once before the program closes.
func cleanup(c *Context) {
	ips, ok := c.Context.Generic("ip").(*util.IPFlag)
	if ok {
		*ips = make([]net.IP, 0)
	}
	disc, ok := c.Context.Generic("disc").(*util.DiscSpecFlag)
	if ok {
		*disc = make([]brain.Disc, 0)
	}
	size, ok := c.Context.Generic("memory").(*util.SizeSpecFlag)
	if ok {
		*size = 0
	}
	server, ok := c.Context.Generic("server").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("from").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("to").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	group, ok := c.Context.Generic("group").(*GroupNameFlag)
	if ok {
		*group = GroupNameFlag{}
	}
}

// foldProviders runs all the providers with the given context, stopping if there's an error
func foldProviders(c *Context, providers ...ProviderFunc) (err error) {
	for _, provider := range providers {
		err = provider(c)
		if err != nil {
			return
		}
	}
	return
}

// OptionalArgs takes a list of flag names. For each flag name it attempts to read the next arg and set the flag with the corresponding name.
// for instance:
// OptionalArgs("server", "disc", "size")
// will attempt to read 3 arguments, setting the "server" flag to the first, "disc" to the 2nd, "size" to the third.
func OptionalArgs(args ...string) ProviderFunc {
	return func(c *Context) error {
		for _, name := range args {
			value, err := c.NextArg()
			if err != nil {
				// if c.NextArg errors that means there aren't more arguments
				// so we just return nil - returning an error would stop the execution of the action.
				return nil
			}
			err = c.Context.Set(name, value)
			if err != nil {
				return err
			}
		}
		return nil
	}
}

// JoinArgs is like OptionalArgs, but reads up to n arguments joined with spaces and sets the one named flag.
// if n is not set, reads all the remaining arguments.
func JoinArgs(flagName string, n ...int) ProviderFunc {
	return func(c *Context) (err error) {
		toRead := len(c.Args())
		if len(n) > 0 {
			toRead = n[0]
		}

		value := ""
		for i := 0; i < toRead; i++ {
			arg, err := c.NextArg()
			if err != nil {
				// don't return the error - just means we ran out of arguments to slurp
				break
			}
			if i == 0 {
				value += arg
			} else {
				value += " " + arg
			}
		}
		err = c.Context.Set(flagName, value)
		return

	}
}

// AccountProvider gets an account name from a flag, then the account details from the API, then stitches it to the context
func AccountProvider(flagName string) ProviderFunc {
	return func(c *Context) (err error) {
		c.Account, err = global.Client.GetAccount(c.String(flagName))
		return
	}
}

// AuthProvider makes sure authentication has been successfully completed, attempting it if necessary.
func AuthProvider(c *Context) (err error) {
	if !c.Authed {
		err = EnsureAuth()
		if err != nil {
			return
		}
	}
	c.Authed = true
	return
}

// DefinitionsProvider gets the Definitions from the brain and attaches them to the Context.
func DefinitionsProvider(c *Context) (err error) {
	if c.Definitions != nil {
		return
	}
	c.Definitions, err = global.Client.ReadDefinitions()
	return
}

// GroupProvider gets a GroupName from a flag, then gets the named Group from the brain and attaches it to the Context.
func GroupProvider(flagName string) ProviderFunc {
	return func(c *Context) (err error) {
		if c.Group != nil {
			return
		}
		err = AuthProvider(c)
		if err != nil {
			return
		}

		groupName := c.GroupName(flagName)
		c.Group, err = global.Client.GetGroup(&groupName)
		return
	}
}

// UserProvider calls UserNameProvider, gets the User from the brain, and attaches it to the Context.
func UserProvider(flagName string) ProviderFunc {
	return func(c *Context) (err error) {
		if c.User != nil {
			return
		}
		user := c.String(flagName)
		if user != "" {
			user = global.Config.GetIgnoreErr("user")
		}
		if err = AuthProvider(c); err != nil {
			return
		}
		c.User, err = global.Client.GetUser(user)
		return
	}
}

// VirtualMachineProvider gets a VirtualMachineName from a flag, then gets the named VirtualMachine from the brain and attaches it to the Context.
func VirtualMachineProvider(flagName string) ProviderFunc {
	return func(c *Context) (err error) {
		if c.VirtualMachine != nil {
			return
		}
		err = AuthProvider(c)
		if err != nil {
			return
		}
		vmName := c.VirtualMachineName(flagName)
		c.VirtualMachine, err = global.Client.GetVirtualMachine(&vmName)
		return
	}
}
<|endoftext|>


<commit_before>package cloudinit

import (
	"io/ioutil"
	"strings"

	log "github.com/Sirupsen/logrus"
	"google.golang.org/cloud/compute/metadata"
	"gopkg.in/yaml.v2"
)

type GceCloudConfig struct {
	FileName           string
	UserData           string
	NonUserDataSSHKeys []string
}

const (
	gceCloudConfigFile = "/var/lib/rancher/conf/gce_cloudinit_config.yml"
)

func NewGceCloudConfig() *GceCloudConfig {

	userData, err := metadata.InstanceAttributeValue("user-data")
	if err != nil {
		log.Errorf("Could not retrieve user-data: %s", err)
	}

	projectSSHKeys, err := metadata.ProjectAttributeValue("sshKeys")
	if err != nil {
		log.Errorf("Could not retrieve project SSH Keys: %s", err)
	}

	instanceSSHKeys, err := metadata.InstanceAttributeValue("sshKeys")
	if err != nil {
		log.Errorf("Could not retrieve instance SSH Keys: %s", err)
	}

	nonUserDataSSHKeysRaw := projectSSHKeys + instanceSSHKeys
	nonUserDataSSHKeys := gceSshKeyFormatter(nonUserDataSSHKeysRaw)

	gceCC := &GceCloudConfig{
		FileName:           gceCloudConfigFile,
		UserData:           userData,
		NonUserDataSSHKeys: nonUserDataSSHKeys,
	}

	return gceCC
}

func GetAndCreateGceDataSourceFilename() (string, error) {
	gceCC := NewGceCloudConfig()
	err := gceCC.saveToFile(gceCC.FileName)
	if err != nil {
		log.Errorf("Error: %s", err)
		return "", err
	}
	return gceCC.FileName, nil
}

func (cc *GceCloudConfig) saveToFile(filename string) error {
	//Get Merged UserData sshkeys
	data, err := cc.getMergedUserData()
	if err != nil {
		log.Errorf("Could not process userdata: %s", err)
		return err
	}
	//write file
	writeFile(filename, data)
	return nil
}

func (cc *GceCloudConfig) getMergedUserData() ([]byte, error) {
	var returnUserData []byte
	userdata := make(map[string]interface{})

	if cc.UserData != "" {
		log.Infof("Found UserData Config")
		err := yaml.Unmarshal([]byte(cc.UserData), &userdata)
		if err != nil {
			log.Errorf("Could not unmarshal data: %s", err)
			return nil, err
		}
	}

	var auth_keys []string
	if _, exists := userdata["ssh_authorized_keys"]; exists {
		udSshKeys := userdata["ssh_authorized_keys"].([]interface{})
		log.Infof("userdata %s", udSshKeys)

		for _, value := range udSshKeys {
			auth_keys = append(auth_keys, value.(string))
		}
	}
	if cc.NonUserDataSSHKeys != nil {
		for _, value := range cc.NonUserDataSSHKeys {
			auth_keys = append(auth_keys, value)
		}
	}
	userdata["ssh_authorized_keys"] = auth_keys

	yamlUserData, err := yaml.Marshal(&userdata)
	if err != nil {
		log.Errorf("Could not Marshal userdata: %s", err)
		return nil, err
	} else {
		returnUserData = append([]byte("#cloud-config\n"), yamlUserData...)
	}

	return returnUserData, nil
}

func writeFile(filename string, data []byte) error {
	if err := ioutil.WriteFile(filename, data, 400); err != nil {
		log.Errorf("Could not write file %v", err)
		return err
	}
	return nil
}

func gceSshKeyFormatter(rawKeys string) []string {
	keySlice := strings.Split(rawKeys, "\n")
	var cloudFormatedKeys []string

	if len(keySlice) > 0 {
		for i := range keySlice {
			keyString := keySlice[i]
			sIdx := strings.Index(keyString, ":")
			if sIdx != -1 {
				key := strings.TrimSpace(keyString[sIdx+1:])
				keyA := strings.Split(key, " ")
				key = strings.Join(keyA, " ")
				if key != "" {
					cloudFormatedKeys = append(cloudFormatedKeys, key)
				}
			}
		}
	}
	return cloudFormatedKeys
}
<commit_msg>Needed to add a newline.<commit_after>package cloudinit

import (
	"io/ioutil"
	"strings"

	log "github.com/Sirupsen/logrus"
	"google.golang.org/cloud/compute/metadata"
	"gopkg.in/yaml.v2"
)

type GceCloudConfig struct {
	FileName           string
	UserData           string
	NonUserDataSSHKeys []string
}

const (
	gceCloudConfigFile = "/var/lib/rancher/conf/gce_cloudinit_config.yml"
)

func NewGceCloudConfig() *GceCloudConfig {

	userData, err := metadata.InstanceAttributeValue("user-data")
	if err != nil {
		log.Errorf("Could not retrieve user-data: %s", err)
	}

	projectSSHKeys, err := metadata.ProjectAttributeValue("sshKeys")
	if err != nil {
		log.Errorf("Could not retrieve project SSH Keys: %s", err)
	}

	instanceSSHKeys, err := metadata.InstanceAttributeValue("sshKeys")
	if err != nil {
		log.Errorf("Could not retrieve instance SSH Keys: %s", err)
	}

	nonUserDataSSHKeysRaw := projectSSHKeys + "\n" + instanceSSHKeys
	nonUserDataSSHKeys := gceSshKeyFormatter(nonUserDataSSHKeysRaw)

	gceCC := &GceCloudConfig{
		FileName:           gceCloudConfigFile,
		UserData:           userData,
		NonUserDataSSHKeys: nonUserDataSSHKeys,
	}

	return gceCC
}

func GetAndCreateGceDataSourceFilename() (string, error) {
	gceCC := NewGceCloudConfig()
	err := gceCC.saveToFile(gceCC.FileName)
	if err != nil {
		log.Errorf("Error: %s", err)
		return "", err
	}
	return gceCC.FileName, nil
}

func (cc *GceCloudConfig) saveToFile(filename string) error {
	//Get Merged UserData sshkeys
	data, err := cc.getMergedUserData()
	if err != nil {
		log.Errorf("Could not process userdata: %s", err)
		return err
	}
	//write file
	writeFile(filename, data)
	return nil
}

func (cc *GceCloudConfig) getMergedUserData() ([]byte, error) {
	var returnUserData []byte
	userdata := make(map[string]interface{})

	if cc.UserData != "" {
		log.Infof("Found UserData Config")
		err := yaml.Unmarshal([]byte(cc.UserData), &userdata)
		if err != nil {
			log.Errorf("Could not unmarshal data: %s", err)
			return nil, err
		}
	}

	var auth_keys []string
	if _, exists := userdata["ssh_authorized_keys"]; exists {
		udSshKeys := userdata["ssh_authorized_keys"].([]interface{})
		log.Infof("userdata %s", udSshKeys)

		for _, value := range udSshKeys {
			auth_keys = append(auth_keys, value.(string))
		}
	}
	if cc.NonUserDataSSHKeys != nil {
		for _, value := range cc.NonUserDataSSHKeys {
			auth_keys = append(auth_keys, value)
		}
	}
	userdata["ssh_authorized_keys"] = auth_keys

	yamlUserData, err := yaml.Marshal(&userdata)
	if err != nil {
		log.Errorf("Could not Marshal userdata: %s", err)
		return nil, err
	} else {
		returnUserData = append([]byte("#cloud-config\n"), yamlUserData...)
	}

	return returnUserData, nil
}

func writeFile(filename string, data []byte) error {
	if err := ioutil.WriteFile(filename, data, 400); err != nil {
		log.Errorf("Could not write file %v", err)
		return err
	}
	return nil
}

func gceSshKeyFormatter(rawKeys string) []string {
	keySlice := strings.Split(rawKeys, "\n")
	var cloudFormatedKeys []string

	if len(keySlice) > 0 {
		for i := range keySlice {
			keyString := keySlice[i]
			sIdx := strings.Index(keyString, ":")
			if sIdx != -1 {
				key := strings.TrimSpace(keyString[sIdx+1:])
				keyA := strings.Split(key, " ")
				key = strings.Join(keyA, " ")
				if key != "" {
					cloudFormatedKeys = append(cloudFormatedKeys, key)
				}
			}
		}
	}
	return cloudFormatedKeys
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"runtime"
	"runtime/pprof"
	"strconv"
	"time"

	"github.com/ChristopherRabotin/smd"
	"github.com/soniakeys/meeus/julian"
	"github.com/spf13/viper"
)

const (
	defaultScenario = "~~unset~~"
	dateTimeFormat  = "2006-01-02 15:04:05"
)

var (
	scenario               string
	numCPUs                int
	initLaunch, maxArrival time.Time
	periapsisRadii         []float64
	planets                []smd.CelestialObject
	maxDeltaVs             []float64
	maxC3, maxVinfArrival  float64
	cpuChan                chan (bool)
	rsltChan               chan (Result)
)

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
var memprofile = flag.String("memprofile", "", "write memory profile to this file")

func init() {
	// Read flags
	flag.StringVar(&scenario, "scenario", defaultScenario, "designer scenario TOML file")
	flag.IntVar(&numCPUs, "cpus", -1, "number of CPUs to use for after first finding (set to 0 for max CPUs)")
}

func main() {
	// Read the configuration file.
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	// End profiling
	if scenario == defaultScenario {
		log.Fatal("no scenario provided and no finder set")
	}
	availableCPUs := runtime.NumCPU()
	if numCPUs <= 0 || numCPUs > availableCPUs {
		numCPUs = availableCPUs
	}
	runtime.GOMAXPROCS(numCPUs)
	fmt.Printf("running on %d CPUs\n", numCPUs)

	cpuChan = make(chan (bool), numCPUs)
	// Load scenario
	viper.AddConfigPath(".")
	viper.SetConfigName(scenario)
	err := viper.ReadInConfig()
	if err != nil {
		log.Fatalf("./%s.toml not found", scenario)
	}
	// Read scenario
	prefix := viper.GetString("General.fileprefix")
	verbose := viper.GetBool("General.verbose")
	if verbose {
		log.Printf("[info] file prefix: %s\n", prefix)
	}
	timeStepStr := viper.GetString("General.step")
	timeStep, durErr := time.ParseDuration(timeStepStr)
	if durErr != nil {
		log.Fatalf("could not understand `step`: %s", durErr)
	}
	if verbose {
		log.Printf("[info] time step: %s\n", timeStep)
	}
	// Date time information
	var perr error
	initLaunchJD := viper.GetFloat64("General.from")
	if initLaunchJD == 0 {
		initLaunch, perr = time.Parse(dateTimeFormat, viper.GetString("General.from"))
		if perr != nil {
			log.Fatalf("could not understand `from`: %s", perr)
		}
	} else {
		initLaunch = julian.JDToTime(initLaunchJD)
	}
	if verbose {
		log.Printf("[info] init launch: %s\n", initLaunch)
	}
	maxArrivalJD := viper.GetFloat64("General.until")
	if maxArrivalJD == 0 {
		maxArrival, perr = time.Parse(dateTimeFormat, viper.GetString("General.until"))
		if perr != nil {
			log.Fatalf("could not understand `until`: %s", perr)
		}
	} else {
		maxArrival = julian.JDToTime(maxArrivalJD)
	}
	if verbose {
		log.Printf("[info] max arrival: %s\n", maxArrival)
	}
	// Read all the planets.
	planetSlice := viper.GetStringSlice("General.planets")
	planets = make([]smd.CelestialObject, len(planetSlice))
	periapsisRadii = make([]float64, len(planetSlice))
	maxDeltaVs = make([]float64, len(planetSlice))
	for pNo, planetStr := range planetSlice {
		planet, err := smd.CelestialObjectFromString(planetStr)
		if err != nil {
			log.Fatalf("could not read planet #%d: %s", pNo, err)
		}
		planets[pNo] = planet
	}
	// Read and compute the radii constraints
	for pNo, periRfactorStr := range viper.GetStringSlice("General.periRFactor") {
		periRfactor, err := strconv.ParseFloat(periRfactorStr, 64)
		if err != nil {
			log.Fatalf("could not read radius periapsis factor #%d: %s", pNo, err)
		}
		periapsisRadii[pNo] = periRfactor * planets[pNo].Radius
	}
	// Read the deltaV constraints
	for pNo, deltaVStr := range viper.GetStringSlice("General.maxDeltaV") {
		deltaV, err := strconv.ParseFloat(deltaVStr, 64)
		if err != nil {
			log.Fatalf("could not read maximum deltaV #%d: %s", pNo, err)
		}
		maxDeltaVs[pNo] = deltaV
	}
	// Now summarize the planet passages
	if verbose {
		for pNo, planet := range planets {
			if pNo != len(planets)-1 {
				log.Printf("[info] #%d: %s\trP: %f km\tdeltaV: %f km/s\n", pNo, planet.Name, periapsisRadii[pNo], maxDeltaVs[pNo])
			} else {
				log.Printf("[info] #%d: %s (destination)\n", pNo, planet.Name)
			}
		}
	}
	// Read departure/arrival constraints.
	if viper.IsSet("DepartureConstraints.c3") {
		maxC3 = viper.GetFloat64("DepartureConstraints.c3")
	}
	if verbose {
		if maxC3 > 0 {
			log.Printf("[info] max c3: %f km^2/s^2\n", maxC3)
		} else {
			log.Println("[warn] no max c3 set")
		}
	}
	if viper.IsSet("ArrivalConstraints.vInf") {
		maxVinfArrival = viper.GetFloat64("ArrivalConstraints.vInf")
	}
	if verbose {
		if maxVinfArrival > 0 {
			log.Printf("[info] max vInf: %f km/s\n", maxVinfArrival)
		} else {
			log.Println("[warn] no max vInf set")
		}
	}
	// Starting the streamer
	rsltChan = make(chan (Result), 10) // Buffered to not loose any data.
	go StreamResults(prefix, planets, rsltChan)

	// Let's do the magic.
	// Always leave Earth.
	// NOTE: This is a VERY broad sweep.
	if verbose {
		log.Printf("[info] searching for %s -> %s", smd.Earth.Name, planets[0].Name)
	}
	c3Map, tofMap, _, _, vInfArriVecs := smd.PCPGenerator(smd.Earth, planets[0], initLaunch, maxArrival, initLaunch, maxArrival, 1, 1, true, false, false)
	/*for initLaunch.Before(maxArrival) {
		smd.FreeEphemeralData(smd.Earth, initLaunch.Year())
		smd.FreeEphemeralData(planets[0], initLaunch.Year())
		initLaunch = initLaunch.AddDate(1, 0, 0)
	}*/
	if *cpuprofile != "" {
		return
	}
	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
		return
	}
	for launchDT, c3PerDay := range c3Map {
		for arrivalIdx, c3 := range c3PerDay {
			if c3 > maxC3 || c3 == 0 {
				continue // Cannot use this launch
			}
			arrivalTOF := tofMap[launchDT][arrivalIdx]
			arrivalDT := launchDT.Add(time.Duration(arrivalTOF*24) * time.Hour)
			if arrivalDT.After(maxArrival) {
				continue
			}
			vInfInVec := vInfArriVecs[launchDT][arrivalIdx]
			if r, _ := vInfInVec.Dims(); r == 0 {
				continue
			}
			// Fulfills the launch requirements.
			vInfIn := []float64{vInfInVec.At(0, 0), vInfInVec.At(1, 0), vInfInVec.At(2, 0)}
			prevResult := NewResult(launchDT, c3, len(planets)-1)
			if true {
				continue
			}
			cpuChan <- true
			go GAPCP(arrivalDT, 0, vInfIn, prevResult)
		}
	}
}

// GAPCP performs the recursion.
func GAPCP(launchDT time.Time, planetNo int, vInfIn []float64, prevResult Result) {
	isLastPlanet := planetNo == len(planets)-1
	log.Printf("[info] searching for %s -> %s (last = %v)", planets[planetNo].Name, planets[planetNo+1].Name, isLastPlanet)
	vinfDep, tofMap, vinfArr, vinfMapVecs, vInfNextInVecs := smd.PCPGenerator(planets[planetNo], planets[planetNo+1], launchDT, launchDT.Add(24*time.Hour), launchDT, maxArrival, 1, 1, false, false, false)
	// Go through solutions and move on with values which are within the constraints.
	vInfInNorm := smd.Norm(vInfIn)
	minRp := periapsisRadii[planetNo]
	maxDV := maxDeltaVs[planetNo]
	for depDT, vInfDepPerDay := range vinfDep {
		for arrIdx, vInfOutNorm := range vInfDepPerDay {
			vInfOut := []float64{vinfMapVecs[depDT][arrIdx].At(0, 0), vinfMapVecs[depDT][arrIdx].At(1, 0), vinfMapVecs[depDT][arrIdx].At(2, 0)}
			flybyDV := math.Abs(vInfInNorm - vInfOutNorm)
			if flybyDV < maxDV {
				log.Println("dv okay")
				// Check if the rP is okay
				_, rp, _, _, _, _ := smd.GAFromVinf(vInfIn, vInfOut, smd.Jupiter)
				if minRp > 0 && rp < minRp {
					log.Printf("[debug] rP no good (%f km)", rp)
					continue // Too close, ignore
				}
				TOF := tofMap[depDT][arrIdx]
				arrivalDT := launchDT.Add(time.Duration(TOF*24) * time.Hour)
				if isLastPlanet {
					vinfArr := vinfArr[depDT][arrIdx]
					if vinfArr < maxVinfArrival {
						log.Println("[debug] valid traj!")
						// This is a valid trajectory?
						// Add information to result.
						result := prevResult.Clone()
						result.arrival = arrivalDT
						result.vInf = vinfArr
						rsltChan <- result
					}
					// All done, let's free that CPU
					<-cpuChan
				} else {
					result := prevResult.Clone()
					result.flybys = append(result.flybys, GAResult{arrivalDT, flybyDV, rp})
					// Recursion
					vInfInNext := []float64{vInfNextInVecs[depDT][arrIdx].At(0, 0), vInfNextInVecs[depDT][arrIdx].At(1, 0), vInfNextInVecs[depDT][arrIdx].At(2, 0)}
					GAPCP(arrivalDT, planetNo+1, vInfInNext, result)
				}
			} else {
				log.Printf("dV = %f", flybyDV)
				// Won't go anywhere, let's move onto another date. and clear queue if needed.
				select {
				case <-cpuChan:
					continue
				default:
					continue
				}
			}
		}
	}
}
<commit_msg>More debug stuff<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"runtime"
	"runtime/pprof"
	"strconv"
	"time"

	"github.com/ChristopherRabotin/smd"
	"github.com/soniakeys/meeus/julian"
	"github.com/spf13/viper"
)

const (
	defaultScenario = "~~unset~~"
	dateTimeFormat  = "2006-01-02 15:04:05"
	ultraDebug      = false
)

var (
	scenario               string
	numCPUs                int
	initLaunch, maxArrival time.Time
	periapsisRadii         []float64
	planets                []smd.CelestialObject
	maxDeltaVs             []float64
	maxC3, maxVinfArrival  float64
	cpuChan                chan (bool)
	rsltChan               chan (Result)
)

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
var memprofile = flag.String("memprofile", "", "write memory profile to this file")

func init() {
	// Read flags
	flag.StringVar(&scenario, "scenario", defaultScenario, "designer scenario TOML file")
	flag.IntVar(&numCPUs, "cpus", -1, "number of CPUs to use for after first finding (set to 0 for max CPUs)")
}

func main() {
	// Read the configuration file.
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	// End profiling
	if scenario == defaultScenario {
		log.Fatal("no scenario provided and no finder set")
	}
	availableCPUs := runtime.NumCPU()
	if numCPUs <= 0 || numCPUs > availableCPUs {
		numCPUs = availableCPUs
	}
	runtime.GOMAXPROCS(numCPUs)
	fmt.Printf("running on %d CPUs\n", numCPUs)

	cpuChan = make(chan (bool), numCPUs)
	// Load scenario
	viper.AddConfigPath(".")
	viper.SetConfigName(scenario)
	err := viper.ReadInConfig()
	if err != nil {
		log.Fatalf("./%s.toml not found", scenario)
	}
	// Read scenario
	prefix := viper.GetString("General.fileprefix")
	verbose := viper.GetBool("General.verbose")
	if verbose {
		log.Printf("[info] file prefix: %s\n", prefix)
	}
	timeStepStr := viper.GetString("General.step")
	timeStep, durErr := time.ParseDuration(timeStepStr)
	if durErr != nil {
		log.Fatalf("could not understand `step`: %s", durErr)
	}
	if verbose {
		log.Printf("[info] time step: %s\n", timeStep)
	}
	// Date time information
	var perr error
	initLaunchJD := viper.GetFloat64("General.from")
	if initLaunchJD == 0 {
		initLaunch, perr = time.Parse(dateTimeFormat, viper.GetString("General.from"))
		if perr != nil {
			log.Fatalf("could not understand `from`: %s", perr)
		}
	} else {
		initLaunch = julian.JDToTime(initLaunchJD)
	}
	if verbose {
		log.Printf("[info] init launch: %s\n", initLaunch)
	}
	maxArrivalJD := viper.GetFloat64("General.until")
	if maxArrivalJD == 0 {
		maxArrival, perr = time.Parse(dateTimeFormat, viper.GetString("General.until"))
		if perr != nil {
			log.Fatalf("could not understand `until`: %s", perr)
		}
	} else {
		maxArrival = julian.JDToTime(maxArrivalJD)
	}
	if verbose {
		log.Printf("[info] max arrival: %s\n", maxArrival)
	}
	// Read all the planets.
	planetSlice := viper.GetStringSlice("General.planets")
	planets = make([]smd.CelestialObject, len(planetSlice))
	periapsisRadii = make([]float64, len(planetSlice))
	maxDeltaVs = make([]float64, len(planetSlice))
	for pNo, planetStr := range planetSlice {
		planet, err := smd.CelestialObjectFromString(planetStr)
		if err != nil {
			log.Fatalf("could not read planet #%d: %s", pNo, err)
		}
		planets[pNo] = planet
	}
	// Read and compute the radii constraints
	for pNo, periRfactorStr := range viper.GetStringSlice("General.periRFactor") {
		periRfactor, err := strconv.ParseFloat(periRfactorStr, 64)
		if err != nil {
			log.Fatalf("could not read radius periapsis factor #%d: %s", pNo, err)
		}
		periapsisRadii[pNo] = periRfactor * planets[pNo].Radius
	}
	// Read the deltaV constraints
	for pNo, deltaVStr := range viper.GetStringSlice("General.maxDeltaV") {
		deltaV, err := strconv.ParseFloat(deltaVStr, 64)
		if err != nil {
			log.Fatalf("could not read maximum deltaV #%d: %s", pNo, err)
		}
		maxDeltaVs[pNo] = deltaV
	}
	// Now summarize the planet passages
	if verbose {
		for pNo, planet := range planets {
			if pNo != len(planets)-1 {
				log.Printf("[info] #%d: %s\trP: %f km\tdeltaV: %f km/s\n", pNo, planet.Name, periapsisRadii[pNo], maxDeltaVs[pNo])
			} else {
				log.Printf("[info] #%d: %s (destination)\n", pNo, planet.Name)
			}
		}
	}
	// Read departure/arrival constraints.
	if viper.IsSet("DepartureConstraints.c3") {
		maxC3 = viper.GetFloat64("DepartureConstraints.c3")
	}
	if verbose {
		if maxC3 > 0 {
			log.Printf("[info] max c3: %f km^2/s^2\n", maxC3)
		} else {
			log.Println("[warn] no max c3 set")
		}
	}
	if viper.IsSet("ArrivalConstraints.vInf") {
		maxVinfArrival = viper.GetFloat64("ArrivalConstraints.vInf")
	}
	if verbose {
		if maxVinfArrival > 0 {
			log.Printf("[info] max vInf: %f km/s\n", maxVinfArrival)
		} else {
			log.Println("[warn] no max vInf set")
		}
	}
	// Starting the streamer
	rsltChan = make(chan (Result), 10) // Buffered to not loose any data.
	go StreamResults(prefix, planets, rsltChan)

	// Let's do the magic.
	// Always leave Earth.
	// NOTE: This is a VERY broad sweep.
	if verbose {
		log.Printf("[info] searching for %s -> %s", smd.Earth.Name, planets[0].Name)
	}
	c3Map, tofMap, _, _, vInfArriVecs := smd.PCPGenerator(smd.Earth, planets[0], initLaunch, maxArrival, initLaunch, maxArrival, 1, 1, true, false, false)
	/*for initLaunch.Before(maxArrival) {
		smd.FreeEphemeralData(smd.Earth, initLaunch.Year())
		smd.FreeEphemeralData(planets[0], initLaunch.Year())
		initLaunch = initLaunch.AddDate(1, 0, 0)
	}*/
	if *cpuprofile != "" {
		return
	}
	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
		return
	}
	for launchDT, c3PerDay := range c3Map {
		for arrivalIdx, c3 := range c3PerDay {
			if c3 > maxC3 || c3 == 0 {
				if ultraDebug {
					log.Printf("[debug] c3 not good (%f)", c3)
				}
				continue // Cannot use this launch
			}
			arrivalTOF := tofMap[launchDT][arrivalIdx]
			arrivalDT := launchDT.Add(time.Duration(arrivalTOF*24) * time.Hour)
			if arrivalDT.After(maxArrival) {
				if ultraDebug {
					log.Printf("[debug] DT not good (%s)", arrivalDT)
				}
				continue
			}
			vInfInVec := vInfArriVecs[launchDT][arrivalIdx]
			if r, _ := vInfInVec.Dims(); r == 0 {
				continue
			}
			// Fulfills the launch requirements.
			vInfIn := []float64{vInfInVec.At(0, 0), vInfInVec.At(1, 0), vInfInVec.At(2, 0)}
			prevResult := NewResult(launchDT, c3, len(planets)-1)
			if true {
				continue
			}
			cpuChan <- true
			go GAPCP(arrivalDT, 0, vInfIn, prevResult)
		}
	}
}

// GAPCP performs the recursion.
func GAPCP(launchDT time.Time, planetNo int, vInfIn []float64, prevResult Result) {
	isLastPlanet := planetNo == len(planets)-1
	log.Printf("[info] searching for %s -> %s (last = %v)", planets[planetNo].Name, planets[planetNo+1].Name, isLastPlanet)
	vinfDep, tofMap, vinfArr, vinfMapVecs, vInfNextInVecs := smd.PCPGenerator(planets[planetNo], planets[planetNo+1], launchDT, launchDT.Add(24*time.Hour), launchDT, maxArrival, 1, 1, false, false, false)
	// Go through solutions and move on with values which are within the constraints.
	vInfInNorm := smd.Norm(vInfIn)
	minRp := periapsisRadii[planetNo]
	maxDV := maxDeltaVs[planetNo]
	for depDT, vInfDepPerDay := range vinfDep {
		for arrIdx, vInfOutNorm := range vInfDepPerDay {
			vInfOut := []float64{vinfMapVecs[depDT][arrIdx].At(0, 0), vinfMapVecs[depDT][arrIdx].At(1, 0), vinfMapVecs[depDT][arrIdx].At(2, 0)}
			flybyDV := math.Abs(vInfInNorm - vInfOutNorm)
			if flybyDV < maxDV {
				log.Println("dv okay")
				// Check if the rP is okay
				_, rp, _, _, _, _ := smd.GAFromVinf(vInfIn, vInfOut, smd.Jupiter)
				if minRp > 0 && rp < minRp {
					log.Printf("[debug] rP no good (%f km)", rp)
					continue // Too close, ignore
				}
				TOF := tofMap[depDT][arrIdx]
				arrivalDT := launchDT.Add(time.Duration(TOF*24) * time.Hour)
				if isLastPlanet {
					vinfArr := vinfArr[depDT][arrIdx]
					if vinfArr < maxVinfArrival {
						log.Println("[debug] valid traj!")
						// This is a valid trajectory?
						// Add information to result.
						result := prevResult.Clone()
						result.arrival = arrivalDT
						result.vInf = vinfArr
						rsltChan <- result
					}
					// All done, let's free that CPU
					<-cpuChan
				} else {
					result := prevResult.Clone()
					result.flybys = append(result.flybys, GAResult{arrivalDT, flybyDV, rp})
					// Recursion
					vInfInNext := []float64{vInfNextInVecs[depDT][arrIdx].At(0, 0), vInfNextInVecs[depDT][arrIdx].At(1, 0), vInfNextInVecs[depDT][arrIdx].At(2, 0)}
					GAPCP(arrivalDT, planetNo+1, vInfInNext, result)
				}
			} else {
				log.Printf("dV = %f", flybyDV)
				// Won't go anywhere, let's move onto another date. and clear queue if needed.
				select {
				case <-cpuChan:
					continue
				default:
					continue
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/binary"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"sync"
	"time"

	"github.com/calmh/syncthing/discover"
	"github.com/calmh/syncthing/protocol"
	"github.com/golang/groupcache/lru"
	"github.com/juju/ratelimit"
)

type node struct {
	addresses []address
	updated   time.Time
}

type address struct {
	ip   []byte
	port uint16
}

var (
	nodes      = make(map[protocol.NodeID]node)
	lock       sync.Mutex
	queries    = 0
	announces  = 0
	answered   = 0
	limited    = 0
	unknowns   = 0
	debug      = false
	lruSize    = 1024
	limitAvg   = 1
	limitBurst = 10
	limiter    *lru.Cache
)

func main() {
	var listen string
	var timestamp bool
	var statsIntv int
	var statsFile string

	flag.StringVar(&listen, "listen", ":22026", "Listen address")
	flag.BoolVar(&debug, "debug", false, "Enable debug output")
	flag.BoolVar(&timestamp, "timestamp", true, "Timestamp the log output")
	flag.IntVar(&statsIntv, "stats-intv", 0, "Statistics output interval (s)")
	flag.StringVar(&statsFile, "stats-file", "/var/log/discosrv.stats", "Statistics file name")
	flag.IntVar(&lruSize, "limit-cache", lruSize, "Limiter cache entries")
	flag.IntVar(&limitAvg, "limit-avg", limitAvg, "Allowed average package rate, per 10 s")
	flag.IntVar(&limitBurst, "limit-burst", limitBurst, "Allowed burst size, packets")
	flag.Parse()

	limiter = lru.New(lruSize)

	log.SetOutput(os.Stdout)
	if !timestamp {
		log.SetFlags(0)
	}

	addr, _ := net.ResolveUDPAddr("udp", listen)
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		log.Fatal(err)
	}

	if statsIntv > 0 {
		go logStats(statsFile, statsIntv)
	}

	var buf = make([]byte, 1024)
	for {
		buf = buf[:cap(buf)]
		n, addr, err := conn.ReadFromUDP(buf)

		if limit(addr) {
			// Rate limit in effect for source
			continue
		}

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

		if n < 4 {
			log.Printf("Received short packet (%d bytes)", n)
			continue
		}

		buf = buf[:n]
		magic := binary.BigEndian.Uint32(buf)

		switch magic {
		case discover.AnnouncementMagic:
			handleAnnounceV2(addr, buf)

		case discover.QueryMagic:
			handleQueryV2(conn, addr, buf)

		default:
			lock.Lock()
			unknowns++
			lock.Unlock()
		}
	}
}

func limit(addr *net.UDPAddr) bool {
	key := addr.IP.String()

	lock.Lock()
	defer lock.Unlock()

	bkt, ok := limiter.Get(key)
	if ok {
		bkt := bkt.(*ratelimit.Bucket)
		if bkt.TakeAvailable(1) != 1 {
			// Rate limit exceeded; ignore packet
			if debug {
				log.Println("Rate limit exceeded for", key)
			}
			limited++
			return true
		}
	} else {
		if debug {
			log.Println("New limiter for", key)
		}
		// One packet per ten seconds average rate, burst ten packets
		limiter.Add(key, ratelimit.NewBucket(10*time.Second/time.Duration(limitAvg), int64(limitBurst)))
	}

	return false
}

func handleAnnounceV2(addr *net.UDPAddr, buf []byte) {
	var pkt discover.Announce
	err := pkt.UnmarshalXDR(buf)
	if err != nil && err != io.EOF {
		log.Println("AnnounceV2 Unmarshal:", err)
		log.Println(hex.Dump(buf))
		return
	}
	if debug {
		log.Printf("<- %v %#v", addr, pkt)
	}

	lock.Lock()
	announces++
	lock.Unlock()

	ip := addr.IP.To4()
	if ip == nil {
		ip = addr.IP.To16()
	}

	var addrs []address
	for _, addr := range pkt.This.Addresses {
		tip := addr.IP
		if len(tip) == 0 {
			tip = ip
		}
		addrs = append(addrs, address{
			ip:   tip,
			port: addr.Port,
		})
	}

	node := node{
		addresses: addrs,
		updated:   time.Now(),
	}

	var id protocol.NodeID
	if len(pkt.This.ID) == 32 {
		// Raw node ID
		copy(id[:], pkt.This.ID)
	} else {
		id.UnmarshalText(pkt.This.ID)
	}

	lock.Lock()
	nodes[id] = node
	lock.Unlock()
}

func handleQueryV2(conn *net.UDPConn, addr *net.UDPAddr, buf []byte) {
	var pkt discover.Query
	err := pkt.UnmarshalXDR(buf)
	if err != nil {
		log.Println("QueryV2 Unmarshal:", err)
		log.Println(hex.Dump(buf))
		return
	}
	if debug {
		log.Printf("<- %v %#v", addr, pkt)
	}

	var id protocol.NodeID
	if len(pkt.NodeID) == 32 {
		// Raw node ID
		copy(id[:], pkt.NodeID)
	} else {
		id.UnmarshalText(pkt.NodeID)
	}

	lock.Lock()
	node, ok := nodes[id]
	queries++
	lock.Unlock()

	if ok && len(node.addresses) > 0 {
		ann := discover.Announce{
			Magic: discover.AnnouncementMagic,
			This: discover.Node{
				ID: pkt.NodeID,
			},
		}
		for _, addr := range node.addresses {
			ann.This.Addresses = append(ann.This.Addresses, discover.Address{IP: addr.ip, Port: addr.port})
		}
		if debug {
			log.Printf("-> %v %#v", addr, pkt)
		}

		tb := ann.MarshalXDR()
		_, _, err = conn.WriteMsgUDP(tb, nil, addr)
		if err != nil {
			log.Println("QueryV2 response write:", err)
		}

		lock.Lock()
		answered++
		lock.Unlock()
	}
}

func next(intv int) time.Time {
	d := time.Duration(intv) * time.Second
	t0 := time.Now()
	t1 := t0.Add(d).Truncate(d)
	time.Sleep(t1.Sub(t0))
	return t1
}

func logStats(file string, intv int) {
	f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	for {
		t := next(intv)

		lock.Lock()

		var deleted = 0
		for id, node := range nodes {
			if time.Since(node.updated) > 60*time.Minute {
				delete(nodes, id)
				deleted++
			}
		}

		fmt.Fprintf(f, "%d Nr:%d Ne:%d Qt:%d Qa:%d A:%d U:%d Lq:%d Lc:%d\n",
			t.Unix(), len(nodes), deleted, queries, answered, announces, unknowns, limited, limiter.Len())
		f.Sync()

		queries = 0
		announces = 0
		answered = 0
		limited = 0
		unknowns = 0

		lock.Unlock()
	}
}
<commit_msg>Update import paths, calmh -> syncthing<commit_after>// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/binary"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"sync"
	"time"

	"github.com/golang/groupcache/lru"
	"github.com/juju/ratelimit"
	"github.com/syncthing/syncthing/discover"
	"github.com/syncthing/syncthing/protocol"
)

type node struct {
	addresses []address
	updated   time.Time
}

type address struct {
	ip   []byte
	port uint16
}

var (
	nodes      = make(map[protocol.NodeID]node)
	lock       sync.Mutex
	queries    = 0
	announces  = 0
	answered   = 0
	limited    = 0
	unknowns   = 0
	debug      = false
	lruSize    = 1024
	limitAvg   = 1
	limitBurst = 10
	limiter    *lru.Cache
)

func main() {
	var listen string
	var timestamp bool
	var statsIntv int
	var statsFile string

	flag.StringVar(&listen, "listen", ":22026", "Listen address")
	flag.BoolVar(&debug, "debug", false, "Enable debug output")
	flag.BoolVar(&timestamp, "timestamp", true, "Timestamp the log output")
	flag.IntVar(&statsIntv, "stats-intv", 0, "Statistics output interval (s)")
	flag.StringVar(&statsFile, "stats-file", "/var/log/discosrv.stats", "Statistics file name")
	flag.IntVar(&lruSize, "limit-cache", lruSize, "Limiter cache entries")
	flag.IntVar(&limitAvg, "limit-avg", limitAvg, "Allowed average package rate, per 10 s")
	flag.IntVar(&limitBurst, "limit-burst", limitBurst, "Allowed burst size, packets")
	flag.Parse()

	limiter = lru.New(lruSize)

	log.SetOutput(os.Stdout)
	if !timestamp {
		log.SetFlags(0)
	}

	addr, _ := net.ResolveUDPAddr("udp", listen)
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		log.Fatal(err)
	}

	if statsIntv > 0 {
		go logStats(statsFile, statsIntv)
	}

	var buf = make([]byte, 1024)
	for {
		buf = buf[:cap(buf)]
		n, addr, err := conn.ReadFromUDP(buf)

		if limit(addr) {
			// Rate limit in effect for source
			continue
		}

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

		if n < 4 {
			log.Printf("Received short packet (%d bytes)", n)
			continue
		}

		buf = buf[:n]
		magic := binary.BigEndian.Uint32(buf)

		switch magic {
		case discover.AnnouncementMagic:
			handleAnnounceV2(addr, buf)

		case discover.QueryMagic:
			handleQueryV2(conn, addr, buf)

		default:
			lock.Lock()
			unknowns++
			lock.Unlock()
		}
	}
}

func limit(addr *net.UDPAddr) bool {
	key := addr.IP.String()

	lock.Lock()
	defer lock.Unlock()

	bkt, ok := limiter.Get(key)
	if ok {
		bkt := bkt.(*ratelimit.Bucket)
		if bkt.TakeAvailable(1) != 1 {
			// Rate limit exceeded; ignore packet
			if debug {
				log.Println("Rate limit exceeded for", key)
			}
			limited++
			return true
		}
	} else {
		if debug {
			log.Println("New limiter for", key)
		}
		// One packet per ten seconds average rate, burst ten packets
		limiter.Add(key, ratelimit.NewBucket(10*time.Second/time.Duration(limitAvg), int64(limitBurst)))
	}

	return false
}

func handleAnnounceV2(addr *net.UDPAddr, buf []byte) {
	var pkt discover.Announce
	err := pkt.UnmarshalXDR(buf)
	if err != nil && err != io.EOF {
		log.Println("AnnounceV2 Unmarshal:", err)
		log.Println(hex.Dump(buf))
		return
	}
	if debug {
		log.Printf("<- %v %#v", addr, pkt)
	}

	lock.Lock()
	announces++
	lock.Unlock()

	ip := addr.IP.To4()
	if ip == nil {
		ip = addr.IP.To16()
	}

	var addrs []address
	for _, addr := range pkt.This.Addresses {
		tip := addr.IP
		if len(tip) == 0 {
			tip = ip
		}
		addrs = append(addrs, address{
			ip:   tip,
			port: addr.Port,
		})
	}

	node := node{
		addresses: addrs,
		updated:   time.Now(),
	}

	var id protocol.NodeID
	if len(pkt.This.ID) == 32 {
		// Raw node ID
		copy(id[:], pkt.This.ID)
	} else {
		id.UnmarshalText(pkt.This.ID)
	}

	lock.Lock()
	nodes[id] = node
	lock.Unlock()
}

func handleQueryV2(conn *net.UDPConn, addr *net.UDPAddr, buf []byte) {
	var pkt discover.Query
	err := pkt.UnmarshalXDR(buf)
	if err != nil {
		log.Println("QueryV2 Unmarshal:", err)
		log.Println(hex.Dump(buf))
		return
	}
	if debug {
		log.Printf("<- %v %#v", addr, pkt)
	}

	var id protocol.NodeID
	if len(pkt.NodeID) == 32 {
		// Raw node ID
		copy(id[:], pkt.NodeID)
	} else {
		id.UnmarshalText(pkt.NodeID)
	}

	lock.Lock()
	node, ok := nodes[id]
	queries++
	lock.Unlock()

	if ok && len(node.addresses) > 0 {
		ann := discover.Announce{
			Magic: discover.AnnouncementMagic,
			This: discover.Node{
				ID: pkt.NodeID,
			},
		}
		for _, addr := range node.addresses {
			ann.This.Addresses = append(ann.This.Addresses, discover.Address{IP: addr.ip, Port: addr.port})
		}
		if debug {
			log.Printf("-> %v %#v", addr, pkt)
		}

		tb := ann.MarshalXDR()
		_, _, err = conn.WriteMsgUDP(tb, nil, addr)
		if err != nil {
			log.Println("QueryV2 response write:", err)
		}

		lock.Lock()
		answered++
		lock.Unlock()
	}
}

func next(intv int) time.Time {
	d := time.Duration(intv) * time.Second
	t0 := time.Now()
	t1 := t0.Add(d).Truncate(d)
	time.Sleep(t1.Sub(t0))
	return t1
}

func logStats(file string, intv int) {
	f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	for {
		t := next(intv)

		lock.Lock()

		var deleted = 0
		for id, node := range nodes {
			if time.Since(node.updated) > 60*time.Minute {
				delete(nodes, id)
				deleted++
			}
		}

		fmt.Fprintf(f, "%d Nr:%d Ne:%d Qt:%d Qa:%d A:%d U:%d Lq:%d Lc:%d\n",
			t.Unix(), len(nodes), deleted, queries, answered, announces, unknowns, limited, limiter.Len())
		f.Sync()

		queries = 0
		announces = 0
		answered = 0
		limited = 0
		unknowns = 0

		lock.Unlock()
	}
}
<|endoftext|>


<commit_before>package main

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

	"github.com/google/uuid"
	"github.com/pkg/errors"
)

type Scout struct {
	installID string
	metadata  map[string]interface{}
}

type ScoutMeta struct {
	Key   string
	Value interface{}
}

func NewScout(mode string) (*Scout, error) {
	var installID string
	var newInstall bool

	// Get or create the persistent install ID for Edge Control
	err := func() error {
		// We store the persistent ID in ~/.config/edgectl/id to be consistent
		// with Telepresence. We could instead use os.UserConfigDir() as the
		// root of the destination, which may be the same on Linux, but is
		// definitely different on MacOS.
		home, err := os.UserHomeDir()
		if err != nil {
			return errors.Wrap(err, "install ID")
		}
		configDir := filepath.Join(home, ".config", "edgectl")
		if err := os.MkdirAll(configDir, 0755); err != nil {
			return errors.Wrap(err, "install ID")
		}
		idFilename := filepath.Join(configDir, "id")

		// Try to read an existing install ID
		if installIDBytes, err := ioutil.ReadFile(idFilename); err == nil {
			installID = string(installIDBytes)
			// Validate UUID format
			if _, err := uuid.Parse(installID); err == nil {
				newInstall = false
				return nil
			} // else the value is not a UUID
		} // else file doesn't exist, etc.

		// Try to create a new install ID
		installID = uuid.New().String()
		if err := ioutil.WriteFile(idFilename, []byte(installID), 0755); err != nil {
			return errors.Wrap(err, "install ID")
		}
		return nil
	}()
	if err != nil {
		return nil, err
	}

	metadata := make(map[string]interface{})
	metadata["mode"] = mode
	metadata["new_install"] = newInstall
	res := &Scout{installID: installID, metadata: metadata}
	return res, nil
}

func (s *Scout) SetMetadatum(key string, value interface{}) {
	oldValue, ok := s.metadata[key]
	if ok {
		panic(fmt.Sprintf("trying to replace metadata[%q] = %q with %q", key, oldValue, value))
	}
	s.metadata[key] = value
}

func (s *Scout) Report(action string, meta ...ScoutMeta) error {
	if os.Getenv("SCOUT_DISABLE") != "" {
		return nil
	}

	metadata := map[string]interface{}{
		"action": action,
	}
	for metaKey, metaValue := range s.metadata {
		metadata[metaKey] = metaValue
	}
	for _, metaItem := range meta {
		metadata[metaItem.Key] = metaItem.Value
	}

	data := map[string]interface{}{
		"application": "edgectl",
		"install_id":  s.installID,
		"version":     Version,
		"metadata":    metadata,
	}

	body, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		panic(err)
	}
	// metritonEndpoint := "https://metriton.datawire.io/scout"   // Prod URL
	metritonEndpoint := "https://metriton.datawire.io/beta/scout" // Beta URL
	resp, err := http.Post(metritonEndpoint, "application/json", bytes.NewBuffer(body))
	if err != nil {
		return errors.Wrap(err, "scout report")
	}

	// TODO: Do something useful (?) with the response?
	_, _ = ioutil.ReadAll(resp.Body)
	_ = resp.Body.Close()

	return nil
}
<commit_msg>Add traits trace_id and index so each install attempt can be traced<commit_after>package main

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

	"github.com/google/uuid"
	"github.com/pkg/errors"
)

type Scout struct {
	installID string
	index     int
	metadata  map[string]interface{}
}

type ScoutMeta struct {
	Key   string
	Value interface{}
}

func NewScout(mode string) (*Scout, error) {
	var installID string
	var newInstall bool

	// Get or create the persistent install ID for Edge Control
	err := func() error {
		// We store the persistent ID in ~/.config/edgectl/id to be consistent
		// with Telepresence. We could instead use os.UserConfigDir() as the
		// root of the destination, which may be the same on Linux, but is
		// definitely different on MacOS.
		home, err := os.UserHomeDir()
		if err != nil {
			return errors.Wrap(err, "install ID")
		}
		configDir := filepath.Join(home, ".config", "edgectl")
		if err := os.MkdirAll(configDir, 0755); err != nil {
			return errors.Wrap(err, "install ID")
		}
		idFilename := filepath.Join(configDir, "id")

		// Try to read an existing install ID
		if installIDBytes, err := ioutil.ReadFile(idFilename); err == nil {
			installID = string(installIDBytes)
			// Validate UUID format
			if _, err := uuid.Parse(installID); err == nil {
				newInstall = false
				return nil
			} // else the value is not a UUID
		} // else file doesn't exist, etc.

		// Try to create a new install ID
		installID = uuid.New().String()
		if err := ioutil.WriteFile(idFilename, []byte(installID), 0755); err != nil {
			return errors.Wrap(err, "install ID")
		}
		return nil
	}()
	if err != nil {
		return nil, err
	}

	metadata := make(map[string]interface{})
	metadata["mode"] = mode
	metadata["new_install"] = newInstall
	metadata["trace_id"] = uuid.New().String()
	res := &Scout{installID: installID, metadata: metadata}
	return res, nil
}

func (s *Scout) SetMetadatum(key string, value interface{}) {
	oldValue, ok := s.metadata[key]
	if ok {
		panic(fmt.Sprintf("trying to replace metadata[%q] = %q with %q", key, oldValue, value))
	}
	s.metadata[key] = value
}

func (s *Scout) Report(action string, meta ...ScoutMeta) error {
	if os.Getenv("SCOUT_DISABLE") != "" {
		return nil
	}

	// Construct the report's metadata. Include the fixed (growing) set of
	// metadata in the Scout structure and the pairs passed as arguments to this
	// call. Also include and increment the index, which can be used to
	// determine the correct order of reported events for this installation
	// attempt (correlated by the trace_id set at the start).
	s.index++
	metadata := map[string]interface{}{
		"action": action,
		"index":  s.index,
	}
	for metaKey, metaValue := range s.metadata {
		metadata[metaKey] = metaValue
	}
	for _, metaItem := range meta {
		metadata[metaItem.Key] = metaItem.Value
	}

	data := map[string]interface{}{
		"application": "edgectl",
		"install_id":  s.installID,
		"version":     Version,
		"metadata":    metadata,
	}

	body, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		panic(err)
	}
	// metritonEndpoint := "https://metriton.datawire.io/scout"   // Prod URL
	metritonEndpoint := "https://metriton.datawire.io/beta/scout" // Beta URL
	resp, err := http.Post(metritonEndpoint, "application/json", bytes.NewBuffer(body))
	if err != nil {
		return errors.Wrap(err, "scout report")
	}

	// TODO: Do something useful (?) with the response?
	_, _ = ioutil.ReadAll(resp.Body)
	_ = resp.Body.Close()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"strconv"

	log "github.com/sirupsen/logrus"
	"github.com/urfave/cli"

	"github.com/gorilla/handlers"
	"github.com/usefathom/fathom/pkg/api"
)

var serverCmd = cli.Command{
	Name:    "server",
	Aliases: []string{"s"},
	Usage:   "start the fathom web server",
	Action:  server,
	Flags: []cli.Flag{
		cli.StringFlag{
			EnvVar: "FATHOM_SERVER_ADDR",
			Name:   "addr,port",
			Usage:  "server address",
			Value:  ":8080",
		},
		cli.BoolFlag{
			EnvVar: "FATHOM_DEBUG",
			Name:   "debug, d",
		},
	},
}

func server(c *cli.Context) error {
	var h http.Handler
	a := api.New(app.database, app.config.Secret)
	h = a.Routes()

	// set debug log level if --debug was passed
	if c.Bool("debug") {
		log.SetLevel(log.DebugLevel)
		h = handlers.LoggingHandler(log.StandardLogger().Writer(), h)
	} else {
		log.SetLevel(log.WarnLevel)
	}

	// if addr looks like a number, prefix with :
	addr := c.String("addr")
	if _, err := strconv.Atoi(addr); err == nil {
		addr = ":" + addr
	}

	// start listening
	log.Printf("Server is now listening on %s", addr)
	err := http.ListenAndServe(addr, h)
	if err != nil {
		log.Errorln(err)
	}
	return nil
}
<commit_msg>accept PORT env var for setting server addr (through cascade). #59<commit_after>package main

import (
	"net/http"
	"strconv"

	log "github.com/sirupsen/logrus"
	"github.com/urfave/cli"

	"github.com/gorilla/handlers"
	"github.com/usefathom/fathom/pkg/api"
)

var serverCmd = cli.Command{
	Name:    "server",
	Aliases: []string{"s"},
	Usage:   "start the fathom web server",
	Action:  server,
	Flags: []cli.Flag{
		cli.StringFlag{
			EnvVar: "FATHOM_SERVER_ADDR,PORT",
			Name:   "addr,port",
			Usage:  "server address",
			Value:  ":8080",
		},
		cli.BoolFlag{
			EnvVar: "FATHOM_DEBUG",
			Name:   "debug, d",
		},
	},
}

func server(c *cli.Context) error {
	var h http.Handler
	a := api.New(app.database, app.config.Secret)
	h = a.Routes()

	// set debug log level if --debug was passed
	if c.Bool("debug") {
		log.SetLevel(log.DebugLevel)
		h = handlers.LoggingHandler(log.StandardLogger().Writer(), h)
	} else {
		log.SetLevel(log.WarnLevel)
	}

	// if addr looks like a number, prefix with :
	addr := c.String("addr")
	if _, err := strconv.Atoi(addr); err == nil {
		addr = ":" + addr
	}

	// start listening
	log.Printf("Server is now listening on %s", addr)
	err := http.ListenAndServe(addr, h)
	if err != nil {
		log.Errorln(err)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
)

var version = os.Getenv("VERSION")
var backend string

func main() {

	port := ":80"
	backend = *flag.String("backend-service", "http://gceme-backend:8080", "hostname of backend server")
	flag.Parse()

	log.Printf("Frontend version: %s\n", version)

	r := gin.Default()
	r.LoadHTMLGlob("templates/*")
	r.GET("/", handleIndex)
	r.GET("/version", handleVersion)
	r.GET("/healthz", handleHealthz)
	r.Run(port)

}

func handleIndex(c *gin.Context) {
	// Call backend for info
	resp, err := http.Get(backend)
	if err != nil {
		c.String(http.StatusInternalServerError, "%v", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		c.String(http.StatusInternalServerError, "%v", err)
		return
	}
	var i InstanceMetadata
	err = json.Unmarshal([]byte(body), &i)
	if err != nil {
		c.String(http.StatusInternalServerError, "%v", err)
		return
	}
	c.HTML(http.StatusOK, "index.html", i)
}

func handleVersion(c *gin.Context) {
	c.String(http.StatusOK, "%s", c.Value("version"))
}

func handleHealthz(c *gin.Context) {
	c.String(http.StatusOK, "", "")
}

// InstanceMetadata stores info about the instance this code is running on.
type InstanceMetadata struct {
	ID         string
	Name       string
	Version    string
	Hostname   string
	Zone       string
	Project    string
	InternalIP string
	ExternalIP string
	LBRequest  string
	ClientIP   string
	Error      string
}
<commit_msg>Augment error messages<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
)

var version = os.Getenv("VERSION")
var backend string

func main() {

	port := ":80"
	backend = *flag.String("backend-service", "http://gceme-backend:8080", "hostname of backend server")
	flag.Parse()

	log.Printf("Frontend version: %s\n", version)

	r := gin.Default()
	r.LoadHTMLGlob("templates/*")
	r.GET("/", handleIndex)
	r.GET("/version", handleVersion)
	r.GET("/healthz", handleHealthz)
	r.Run(port)

}

func handleIndex(c *gin.Context) {
	// Call backend for info
	resp, err := http.Get(backend)
	if err != nil {
		c.String(http.StatusInternalServerError, "Request to backend server ()%s failed:\n%v", backend, err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		c.String(http.StatusInternalServerError, "Unable to read body from backend request:\n%v", err)
		return
	}
	var i InstanceMetadata
	err = json.Unmarshal([]byte(body), &i)
	if err != nil {
		c.String(http.StatusInternalServerError, "Unable to parse JSON from backend request:\n%v", err)
		return
	}
	c.HTML(http.StatusOK, "index.html", i)
}

func handleVersion(c *gin.Context) {
	c.String(http.StatusOK, "%s", c.Value("version"))
}

func handleHealthz(c *gin.Context) {
	c.String(http.StatusOK, "", "")
}

// InstanceMetadata stores info about the instance this code is running on.
type InstanceMetadata struct {
	ID         string
	Name       string
	Version    string
	Hostname   string
	Zone       string
	Project    string
	InternalIP string
	ExternalIP string
	LBRequest  string
	ClientIP   string
	Error      string
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"strconv"

	"github.com/cetacean/magiism/dominos"
)

func main() {
	g := &game{dominos.NewGame([]string{"Xena", "Vic"})}
	log.Printf("%s is the starting player!", g.GetActivePlayer().ID)
	for {
		g.Menu()

		p, status := g.NextTurn()
		if status == "noknock" {
			log.Printf("%s did not knock, drawn two tiles", p.ID)
		}
	}
}

type game struct {
	*dominos.Game
}

func atoi(s string) int {
	result, _ := strconv.Atoi(s)
	return result
}

// End of turn sentry error
var (
	ErrEndOfTurn = errors.New("end of turn")
)

func (g *game) Menu() error {
	if g.UnresolvedDouble {
		log.Println("Unresolved double")
	}
	log.Printf("%s IS NOW UP", g.GetActivePlayer().ID)
	log.Printf("CENTER PIECE: (%d, %d)\n", g.Center.Left, g.Center.Right)
	for i, e := range g.Trains {
		log.Printf("%d: %s", i, e.Display())
	}

	drawn := false
	played := false

	defer func() {
		/*r := recover()
		if r != nil {
			panic(r)
		}*/

		if !played {
			log.Println("Setting train on " + g.GetActivePlayer().ID)
			p := g.GetActivePlayer().Path
			p.Train = true
		}
	}()

	log.Println(g.GetActivePlayer().Display())
	log.Printf("Commands: (p)lace | (b)ig turn | (k)nock | (d)raw | (e)ndturn")
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print("> ")

	for scanner.Scan() {
		log.Println(g.GetActivePlayer().Display())
		if scanner.Err() != nil {
			log.Println(scanner.Err())
			return scanner.Err()
		}

		t := scanner.Text()

		switch t {
		case "p":
			fmt.Printf("hand index to place> ")
			scanner.Scan()
			hIndex := scanner.Text()

			fmt.Printf("path index to play on> ")
			scanner.Scan()
			pIndex := scanner.Text()

			hIndexInt := atoi(hIndex)
			pIndexInt := atoi(pIndex)
			path := g.Trains[pIndexInt]
			p := g.GetActivePlayer()
			d := p.RemoveFromHand(hIndexInt)

			err := g.Place(p, d, path)
			if err != nil {
				p.Hand = append(p.Hand, d)
				switch err {
				case dominos.ErrDontOwnPath, dominos.ErrNotPlayable, dominos.ErrDanglingDouble:
					log.Println(err)
					goto end
				default:
					log.Println(err)
					return err
				}
			}

			log.Println("Tile played successfully")

			if d.IsDouble() {
				g.UnresolvedDouble = true
				path.UnresolvedDouble = true
				log.Println("You must resolve this double if you can")
				goto end
			}

			played = true
			return ErrEndOfTurn
		case "b":
			log.Println("big turn not implemented")
		case "k":
			p := g.GetActivePlayer()
			if g.Knock(p) {
				log.Printf("%s has knocked, they only have one domino left!", p.ID)
			} else {
				log.Println("cannot knock, you have more than one tile in your hand")
			}
		case "d":
			if !drawn {
				drawn = true
				err := g.Draw(g.GetActivePlayer())
				if err != nil {
					log.Println("Out of tiles, cannot draw.")
					return ErrEndOfTurn
				}

				log.Printf("you have drawn")
				log.Printf("%s", g.GetActivePlayer().Display())
			} else {
				log.Println("already drawn, cannot draw again")
			}
		case "e":
			p := g.GetActivePlayer()
			nagged := false
			for i, d := range p.Hand {
				for j, path := range g.Trains {
					_, err := g.CanPlace(p, d, path)
					if err == nil {
						nagged = true
						log.Printf("you can place tile %s (%d) in your hand on path %d", d.Display(), i, j)
					}
				}
			}

			if nagged {
				goto end
			}

			if !drawn {
				log.Println("you have not drawn a tile, please draw a tile")
			} else {
				return ErrEndOfTurn
			}
		default:
			log.Println("Command not understood, please try again.")
		}

	end:
		fmt.Print("> ")
	}

	return nil
}
<commit_msg>cmd/gametest: fix for API changes<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"log"
	"os"
	"strconv"

	"github.com/cetacean/magiism/dominos"
)

func main() {
	g := &game{dominos.NewGame([]string{"Xena", "Vic"})}
	log.Printf("%s is the starting player!", g.GetActivePlayer().ID)
	for {
		g.Menu()

		p, status := g.NextTurn()
		if status == "noknock" {
			log.Printf("%s did not knock, drawn two tiles", p.ID)
		}
	}
}

type game struct {
	*dominos.Game
}

func atoi(s string) int {
	result, _ := strconv.Atoi(s)
	return result
}

// End of turn sentry error
var (
	ErrEndOfTurn = errors.New("end of turn")
)

func (g *game) Menu() error {
	if g.UnresolvedDouble {
		log.Println("Unresolved double")
	}
	log.Printf("%s IS NOW UP", g.GetActivePlayer().ID)
	log.Printf("CENTER PIECE: (%d, %d)\n", g.Center.Left, g.Center.Right)
	for i, e := range g.Trains {
		log.Printf("%d: %s", i, e.Display())
	}

	drawn := false
	played := false

	defer func() {
		/*r := recover()
		if r != nil {
			panic(r)
		}*/

		if !played {
			log.Println("Setting train on " + g.GetActivePlayer().ID)
			p := g.GetActivePlayer().Path
			p.Train = true
		}
	}()

	log.Println(g.GetActivePlayer().Display())
	log.Printf("Commands: (p)lace | (b)ig turn | (k)nock | (d)raw | (e)ndturn")
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print("> ")

	for scanner.Scan() {
		log.Println(g.GetActivePlayer().Display())
		if scanner.Err() != nil {
			log.Println(scanner.Err())
			return scanner.Err()
		}

		t := scanner.Text()

		switch t {
		case "p":
			fmt.Printf("hand index to place> ")
			scanner.Scan()
			hIndex := scanner.Text()

			fmt.Printf("path index to play on> ")
			scanner.Scan()
			pIndex := scanner.Text()

			hIndexInt := atoi(hIndex)
			pIndexInt := atoi(pIndex)
			path := g.Trains[pIndexInt]
			p := g.GetActivePlayer()
			d, ok := p.RemoveFromHand(hIndexInt)
			if !ok {
				log.Println("Invalid hand index.")
				goto end
			}

			err := g.Place(p, d, path)
			if err != nil {
				p.Hand = append(p.Hand, d)
				switch err {
				case dominos.ErrDontOwnPath, dominos.ErrNotPlayable, dominos.ErrDanglingDouble:
					log.Println(err)
					goto end
				default:
					log.Println(err)
					return err
				}
			}

			log.Println("Tile played successfully")

			if d.IsDouble() {
				g.UnresolvedDouble = true
				path.UnresolvedDouble = true
				log.Println("You must resolve this double if you can")
				goto end
			}

			played = true
			return ErrEndOfTurn
		case "b":
			log.Println("big turn not implemented")
		case "k":
			p := g.GetActivePlayer()
			if g.Knock(p) {
				log.Printf("%s has knocked, they only have one domino left!", p.ID)
			} else {
				log.Println("cannot knock, you have more than one tile in your hand")
			}
		case "d":
			if !drawn {
				drawn = true
				err := g.Draw(g.GetActivePlayer())
				if err != nil {
					log.Println("Out of tiles, cannot draw.")
					return ErrEndOfTurn
				}

				log.Printf("you have drawn")
				log.Printf("%s", g.GetActivePlayer().Display())
			} else {
				log.Println("already drawn, cannot draw again")
			}
		case "e":
			p := g.GetActivePlayer()
			nagged := false
			for i, d := range p.Hand {
				for j, path := range g.Trains {
					_, err := g.CanPlace(p, d, path)
					if err == nil {
						nagged = true
						log.Printf("you can place tile %s (%d) in your hand on path %d", d.Display(), i, j)
					}
				}
			}

			if nagged {
				goto end
			}

			if !drawn {
				log.Println("you have not drawn a tile, please draw a tile")
			} else {
				return ErrEndOfTurn
			}
		default:
			log.Println("Command not understood, please try again.")
		}

	end:
		fmt.Print("> ")
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rsa"
	"errors"
	goflag "flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	flag "github.com/spf13/pflag"
	"k8s.io/apimachinery/pkg/runtime"
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/cert"

	ssv1alpha1 "github.com/bitnami/sealed-secrets/apis/v1alpha1"

	// Register v1.Secret type
	_ "k8s.io/client-go/pkg/api/install"

	// Register Auth providers
	_ "k8s.io/client-go/plugin/pkg/client/auth"
)

var (
	// TODO: Verify k8s server signature against cert in kube client config.
	certFile       = flag.String("cert", "", "Certificate / public key to use for encryption. Overrides --controller-*")
	controllerNs   = flag.String("controller-namespace", api.NamespaceSystem, "Namespace of sealed-secrets controller.")
	controllerName = flag.String("controller-name", "sealed-secrets-controller", "Name of sealed-secrets controller.")
	outputFormat   = flag.String("format", "json", "Output format for sealed secret. Either json or yaml")

	clientConfig clientcmd.ClientConfig
)

func init() {
	// The "usual" clientcmd/kubectl flags
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
	overrides := clientcmd.ConfigOverrides{}
	kflags := clientcmd.RecommendedConfigOverrideFlags("")
	flag.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
	clientcmd.BindOverrideFlags(&overrides, flag.CommandLine, kflags)
	clientConfig = clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)

	// Standard goflags (glog in particular)
	flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}

func parseKey(r io.Reader) (*rsa.PublicKey, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	certs, err := cert.ParseCertsPEM(data)
	if err != nil {
		return nil, err
	}

	// ParseCertsPem returns error if len(certs) == 0, but best to be sure...
	if len(certs) == 0 {
		return nil, errors.New("Failed to read any certificates")
	}

	cert, ok := certs[0].PublicKey.(*rsa.PublicKey)
	if !ok {
		return nil, fmt.Errorf("Expected RSA public key but found %v", certs[0].PublicKey)
	}

	return cert, nil
}

func readSecret(codec runtime.Decoder, r io.Reader) (*v1.Secret, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	var ret v1.Secret
	if err = runtime.DecodeInto(codec, data, &ret); err != nil {
		return nil, err
	}

	return &ret, nil
}

func prettyEncoder(codecs runtimeserializer.CodecFactory, mediaType string, gv runtime.GroupVersioner) (runtime.Encoder, error) {
	info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
	if !ok {
		return nil, fmt.Errorf("binary can't serialize %s", mediaType)
	}

	prettyEncoder := info.PrettySerializer
	if prettyEncoder == nil {
		prettyEncoder = info.Serializer
	}

	enc := codecs.EncoderForVersion(prettyEncoder, gv)
	return enc, nil
}

func openCertFile(certFile string) (io.ReadCloser, error) {
	f, err := os.Open(certFile)
	if err != nil {
		return nil, fmt.Errorf("Error reading %s: %v", certFile, err)
	}
	return f, nil
}

func openCertHTTP(c corev1.CoreV1Interface, namespace, name string) (io.ReadCloser, error) {
	f, err := c.
		Services(namespace).
		ProxyGet("http", name, "", "/v1/cert.pem", nil).
		Stream()
	if err != nil {
		return nil, fmt.Errorf("Error fetching certificate: %v", err)
	}
	return f, nil
}

func openCert() (io.ReadCloser, error) {
	if *certFile != "" {
		return openCertFile(*certFile)
	}

	conf, err := clientConfig.ClientConfig()
	if err != nil {
		return nil, err
	}
	conf.AcceptContentTypes = "application/x-pem-file, */*"
	restClient, err := corev1.NewForConfig(conf)
	if err != nil {
		return nil, err
	}
	return openCertHTTP(restClient, *controllerNs, *controllerName)
}

func seal(in io.Reader, out io.Writer, codecs runtimeserializer.CodecFactory, pubKey *rsa.PublicKey) error {
	secret, err := readSecret(codecs.UniversalDecoder(), in)
	if err != nil {
		return err
	}

	if secret.GetNamespace() == "" {
		ns, _, err := clientConfig.Namespace()
		if err != nil {
			return err
		}
		secret.SetNamespace(ns)
	}

	ssecret, err := ssv1alpha1.NewSealedSecret(codecs, pubKey, secret)
	if err != nil {
		return err
	}

	var contentType string
	switch strings.ToLower(*outputFormat) {
	case "json", "":
		contentType = runtime.ContentTypeJSON
	case "yaml":
		contentType = "application/yaml"
	default:
		return fmt.Errorf("unsupported output format: %s", *outputFormat)

	}
	prettyEnc, err := prettyEncoder(codecs, contentType, ssv1alpha1.SchemeGroupVersion)
	if err != nil {
		return err
	}

	buf, err := runtime.Encode(prettyEnc, ssecret)
	if err != nil {
		return err
	}

	out.Write(buf)
	fmt.Fprint(out, "\n")

	return nil
}

func main() {
	flag.Parse()
	goflag.CommandLine.Parse([]string{})

	f, err := openCert()
	if err != nil {
		panic(err.Error())
	}
	defer f.Close()

	pubKey, err := parseKey(f)
	if err != nil {
		panic(err.Error())
	}

	if err := seal(os.Stdin, os.Stdout, api.Codecs, pubKey); err != nil {
		panic(err.Error())
	}
}
<commit_msg>kubeseal: Abort on empty data or missing metadata.name in input<commit_after>package main

import (
	"crypto/rsa"
	"errors"
	goflag "flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	flag "github.com/spf13/pflag"
	"k8s.io/apimachinery/pkg/runtime"
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/cert"

	ssv1alpha1 "github.com/bitnami/sealed-secrets/apis/v1alpha1"

	// Register v1.Secret type
	_ "k8s.io/client-go/pkg/api/install"

	// Register Auth providers
	_ "k8s.io/client-go/plugin/pkg/client/auth"
)

var (
	// TODO: Verify k8s server signature against cert in kube client config.
	certFile       = flag.String("cert", "", "Certificate / public key to use for encryption. Overrides --controller-*")
	controllerNs   = flag.String("controller-namespace", api.NamespaceSystem, "Namespace of sealed-secrets controller.")
	controllerName = flag.String("controller-name", "sealed-secrets-controller", "Name of sealed-secrets controller.")
	outputFormat   = flag.String("format", "json", "Output format for sealed secret. Either json or yaml")

	clientConfig clientcmd.ClientConfig
)

func init() {
	// The "usual" clientcmd/kubectl flags
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
	overrides := clientcmd.ConfigOverrides{}
	kflags := clientcmd.RecommendedConfigOverrideFlags("")
	flag.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
	clientcmd.BindOverrideFlags(&overrides, flag.CommandLine, kflags)
	clientConfig = clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)

	// Standard goflags (glog in particular)
	flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}

func parseKey(r io.Reader) (*rsa.PublicKey, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	certs, err := cert.ParseCertsPEM(data)
	if err != nil {
		return nil, err
	}

	// ParseCertsPem returns error if len(certs) == 0, but best to be sure...
	if len(certs) == 0 {
		return nil, errors.New("Failed to read any certificates")
	}

	cert, ok := certs[0].PublicKey.(*rsa.PublicKey)
	if !ok {
		return nil, fmt.Errorf("Expected RSA public key but found %v", certs[0].PublicKey)
	}

	return cert, nil
}

func readSecret(codec runtime.Decoder, r io.Reader) (*v1.Secret, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	var ret v1.Secret
	if err = runtime.DecodeInto(codec, data, &ret); err != nil {
		return nil, err
	}

	return &ret, nil
}

func prettyEncoder(codecs runtimeserializer.CodecFactory, mediaType string, gv runtime.GroupVersioner) (runtime.Encoder, error) {
	info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
	if !ok {
		return nil, fmt.Errorf("binary can't serialize %s", mediaType)
	}

	prettyEncoder := info.PrettySerializer
	if prettyEncoder == nil {
		prettyEncoder = info.Serializer
	}

	enc := codecs.EncoderForVersion(prettyEncoder, gv)
	return enc, nil
}

func openCertFile(certFile string) (io.ReadCloser, error) {
	f, err := os.Open(certFile)
	if err != nil {
		return nil, fmt.Errorf("Error reading %s: %v", certFile, err)
	}
	return f, nil
}

func openCertHTTP(c corev1.CoreV1Interface, namespace, name string) (io.ReadCloser, error) {
	f, err := c.
		Services(namespace).
		ProxyGet("http", name, "", "/v1/cert.pem", nil).
		Stream()
	if err != nil {
		return nil, fmt.Errorf("Error fetching certificate: %v", err)
	}
	return f, nil
}

func openCert() (io.ReadCloser, error) {
	if *certFile != "" {
		return openCertFile(*certFile)
	}

	conf, err := clientConfig.ClientConfig()
	if err != nil {
		return nil, err
	}
	conf.AcceptContentTypes = "application/x-pem-file, */*"
	restClient, err := corev1.NewForConfig(conf)
	if err != nil {
		return nil, err
	}
	return openCertHTTP(restClient, *controllerNs, *controllerName)
}

func seal(in io.Reader, out io.Writer, codecs runtimeserializer.CodecFactory, pubKey *rsa.PublicKey) error {
	secret, err := readSecret(codecs.UniversalDecoder(), in)
	if err != nil {
		return err
	}

	if len(secret.Data) == 0 {
		// No data. This is _theoretically_ just fine, but
		// almost certainly indicates a misuse of the tools.
		// If you _really_ want to encrypt an empty secret,
		// then a PR to skip this check with some sort of
		// --force flag would be welcomed.
		return fmt.Errorf("Secret.data is empty in input Secret, assuming this is an error and aborting")
	}

	if secret.GetName() == "" {
		return fmt.Errorf("Missing metadata.name in input Secret")
	}

	if secret.GetNamespace() == "" {
		ns, _, err := clientConfig.Namespace()
		if err != nil {
			return err
		}
		secret.SetNamespace(ns)
	}

	ssecret, err := ssv1alpha1.NewSealedSecret(codecs, pubKey, secret)
	if err != nil {
		return err
	}

	var contentType string
	switch strings.ToLower(*outputFormat) {
	case "json", "":
		contentType = runtime.ContentTypeJSON
	case "yaml":
		contentType = "application/yaml"
	default:
		return fmt.Errorf("unsupported output format: %s", *outputFormat)

	}
	prettyEnc, err := prettyEncoder(codecs, contentType, ssv1alpha1.SchemeGroupVersion)
	if err != nil {
		return err
	}

	buf, err := runtime.Encode(prettyEnc, ssecret)
	if err != nil {
		return err
	}

	out.Write(buf)
	fmt.Fprint(out, "\n")

	return nil
}

func main() {
	flag.Parse()
	goflag.CommandLine.Parse([]string{})

	f, err := openCert()
	if err != nil {
		panic(err.Error())
	}
	defer f.Close()

	pubKey, err := parseKey(f)
	if err != nil {
		panic(err.Error())
	}

	if err := seal(os.Stdin, os.Stdout, api.Codecs, pubKey); err != nil {
		panic(err.Error())
	}
}
<|endoftext|>