<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"mig"
	"mig/client"
	migdb "mig/database"
	"os"
	"strings"

	"github.com/bobappleyard/readline"
)

var useShortNames bool

func main() {
	var err error
	homedir := client.FindHomedir()
	// command line options
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var shortnames = flag.Bool("s", false, "Shorten all agent names to first and last 5 characters)")
	var quiet = flag.Bool("q", false, "don't display banners and prompts")
	flag.Parse()

	// silence extra output
	out := os.Stdout
	if *quiet {
		out.Close()
		out, err = os.Open(os.DevNull)
		if err != nil {
			panic(err)
		}
	}
	defer out.Close()

	fmt.Fprintf(out, "\x1b[32;1m"+banner+"\x1b[0m")

	// append a space after completion
	readline.CompletionAppendChar = 0x20

	// shorten names for obfuscation, useful during demoes
	if *shortnames {
		useShortNames = true
	}

	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli := client.NewClient(conf)

	err = printStatus(cli)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprintf(out, "\nConnected to %s. Exit with \x1b[32;1mctrl+d\x1b[0m. Type \x1b[32;1mhelp\x1b[0m for help.\n", cli.Conf.API.URL)
	for {
		// completion
		var symbols = []string{"action", "agent", "create", "command", "help", "exit", "showcfg",
			"status", "investigator", "search", "where", "and"}
		readline.Completer = func(query, ctx string) []string {
			var res []string
			for _, sym := range symbols {
				if strings.HasPrefix(sym, query) {
					res = append(res, sym)
				}
			}
			return res
		}

		input, err := readline.String("\x1b[32;1mmig>\x1b[0m ")
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Println("error: ", err)
			break
		}
		orders := strings.Split(strings.TrimSpace(input), " ")
		switch orders[0] {
		case "action":
			if len(orders) == 2 {
				err = actionReader(input, cli)
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing action id in 'action <id>'")
			}
		case "agent":
			err = agentReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "create":
			if len(orders) == 2 {
				switch orders[1] {
				case "action":
					var a mig.Action
					err = actionLauncher(a, cli)
				case "investigator":
					err = investigatorCreator(cli)
				default:
					fmt.Printf("unknown order 'create %s'\n", orders[1])
				}
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing order, must be 'create <action|investigator>'")
			}
		case "command":
			err = commandReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "exit":
			fmt.Printf("exit\n")
			goto exit
		case "help":
			fmt.Printf(`The following orders are available:
action <id|new>		enter interactive action mode. if <id> is given, go to reader mode. if "new" is given, enter launcher mode.
agent <id>		enter interactive agent reader mode for agent <id>
create action		enter interactive action creation mode
create investigator	create a new investigator, will prompt for name and public key
command <id>		enter command reader mode for command <id>
exit			leave
help			show this help
investigator <id>	enter interactive investigator management mode for investigator <id>
search			perform a search. see "search help" for more information.
showcfg			display running configuration
status			display platform status: connected agents, latest actions, ...
`)
		case "investigator":
			err = investigatorReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "search":
			err = search(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "showcfg":
			fmt.Printf("homedir = %s\n[api]\n    url = %s\n[gpg]\n    home = %s\n    keyid = %s\n",
				cli.Conf.API.URL, cli.Conf.Homedir, cli.Conf.GPG.Home, cli.Conf.GPG.KeyID)
		case "status":
			err = printStatus(cli)
			if err != nil {
				log.Println(err)
			}
		case "":
			break
		default:
			fmt.Printf("Unknown order '%s'\n", orders[0])
		}
		readline.AddHistory(input)
	}
exit:
	fmt.Fprintf(out, footer)
}

func printStatus(cli client.Client) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("printStatus() -> %v", e)
		}
	}()
	st, err := cli.GetAPIResource("dashboard")
	if err != nil {
		panic(err)
	}
	agtout := make([]string, 5)
	agtout[0] = "Agents Summary:"
	actout := make([]string, 2)
	actout[0] = "Latest Actions:"
	actout[1] = "----    ID      ---- + ----         Name         ---- + ----    Date    ---- + ---- Investigators ----"
	for _, item := range st.Collection.Items {
		for _, data := range item.Data {
			switch data.Name {
			case "action":
				idstr, name, datestr, invs, err := actionPrintShort(data.Value)
				if err != nil {
					panic(err)
				}
				str := fmt.Sprintf("%s   %s   %s   %s", idstr, name, datestr, invs)
				actout = append(actout, str)
			case "active agents":
				agtout[1] = fmt.Sprintf("* %.0f agents have checked in during the last 5 minutes", data.Value)
			case "agents versions count":
				bData, err := json.Marshal(data.Value)
				if err != nil {
					panic(err)
				}
				var sum []migdb.AgentsSum
				err = json.Unmarshal(bData, &sum)
				if err != nil {
					panic(err)
				}
				for _, asum := range sum {
					s := fmt.Sprintf("* version %s: %.0f agent", asum.Version, asum.Count)
					if asum.Count > 1 {
						s += "s"
					}
					agtout = append(agtout, s)
				}
			case "agents started in the last 24 hours":
				agtout[2] = fmt.Sprintf("* %.0f agents (re)started in the last 24 hours", data.Value)
			case "endpoints running 2 or more agents":
				agtout[3] = fmt.Sprintf("* %.0f endpoints are running 2 or more agents", data.Value)
			case "endpoints that have disappeared over last 7 days":
				agtout[4] = fmt.Sprintf("* %.0f endpoints have disappeared over the last 7 days", data.Value)
			}
		}
	}
	fmt.Println("\x1b[31;1m+------\x1b[0m")
	for _, s := range agtout {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
	}
	fmt.Println("\x1b[31;1m|\x1b[0m")
	for _, s := range actout {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
		if len(actout) < 2 {
			fmt.Println("\x1b[31;1m|\x1b[0m * None")
			break
		}
	}
	fmt.Println("\x1b[31;1m+------\x1b[0m")
	return
}

func shorten(p string) string {
	if len(p) < 8 {
		return p
	}
	out := p[0:7]
	out += "."
	out += "."
	out += "."
	if len(p) > 18 {
		out += p[len(p)-7 : len(p)]
	}
	return out
}

var banner string = `
## ##                                     _.---._     .---.
# # # /-\ ---||  |    /\         __...---' .---. '---'-.   '.
#   #|   | / ||  |   /--\    .-''__.--' _.'( | )'.  '.  '._ :
#   # \_/ ---| \_ \_/    \ .'__-'_ .--'' ._'---'_.-.  '.   '-'.
     ###                         ~ -._ -._''---. -.    '-._   '.
      # |\ |\    /---------|          ~ -.._ _ _ _ ..-_ '.  '-._''--.._
      # | \| \  / |- |__ | |                       -~ -._  '-.  -. '-._''--.._.--''.
     ###|  \  \/  ---__| | |                            ~ ~-.__     -._  '-.__   '. '.
          #####                                               ~~ ~---...__ _    ._ .' '.
          #      /\  --- /-\ |--|----                                    ~  ~--.....--~
          # ### /--\  | |   ||-\  //
          #####/    \ |  \_/ |  \//__
`

var footer string = `
            .-._   _ _ _ _ _ _ _ _
 .-''-.__.-'Oo  '-' ' ' ' ' ' ' ' '-.
'.___ '    .   .--_'-' '-' '-' _'-' '._
 V: V 'vv-'   '_   '.       .'  _..' '.'.
   '=.____.=_.--'   :_.__.__:_   '.   : :
           (((____.-'        '-.  /   : :
                             (((-'\ .' /
                           _____..'  .'
                          '-._____.-'
              Gators are going back underwater.
`
<commit_msg>[minor] catch panic in console<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"mig"
	"mig/client"
	migdb "mig/database"
	"os"
	"strings"

	"github.com/bobappleyard/readline"
)

var useShortNames bool

func main() {
	var err error
	defer func() {
		if e := recover(); e != nil {
			fmt.Printf("FATAL: %v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	// command line options
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var shortnames = flag.Bool("s", false, "Shorten all agent names to first and last 5 characters)")
	var quiet = flag.Bool("q", false, "don't display banners and prompts")
	flag.Parse()

	// silence extra output
	out := os.Stdout
	if *quiet {
		out.Close()
		out, err = os.Open(os.DevNull)
		if err != nil {
			panic(err)
		}
	}
	defer out.Close()

	fmt.Fprintf(out, "\x1b[32;1m"+banner+"\x1b[0m")

	// append a space after completion
	readline.CompletionAppendChar = 0x20

	// shorten names for obfuscation, useful during demoes
	if *shortnames {
		useShortNames = true
	}

	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli := client.NewClient(conf)

	err = printStatus(cli)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprintf(out, "\nConnected to %s. Exit with \x1b[32;1mctrl+d\x1b[0m. Type \x1b[32;1mhelp\x1b[0m for help.\n", cli.Conf.API.URL)
	for {
		// completion
		var symbols = []string{"action", "agent", "create", "command", "help", "exit", "showcfg",
			"status", "investigator", "search", "where", "and"}
		readline.Completer = func(query, ctx string) []string {
			var res []string
			for _, sym := range symbols {
				if strings.HasPrefix(sym, query) {
					res = append(res, sym)
				}
			}
			return res
		}

		input, err := readline.String("\x1b[32;1mmig>\x1b[0m ")
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Println("error: ", err)
			break
		}
		orders := strings.Split(strings.TrimSpace(input), " ")
		switch orders[0] {
		case "action":
			if len(orders) == 2 {
				err = actionReader(input, cli)
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing action id in 'action <id>'")
			}
		case "agent":
			err = agentReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "create":
			if len(orders) == 2 {
				switch orders[1] {
				case "action":
					var a mig.Action
					err = actionLauncher(a, cli)
				case "investigator":
					err = investigatorCreator(cli)
				default:
					fmt.Printf("unknown order 'create %s'\n", orders[1])
				}
				if err != nil {
					log.Println(err)
				}
			} else {
				fmt.Println("error: missing order, must be 'create <action|investigator>'")
			}
		case "command":
			err = commandReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "exit":
			fmt.Printf("exit\n")
			goto exit
		case "help":
			fmt.Printf(`The following orders are available:
action <id|new>		enter interactive action mode. if <id> is given, go to reader mode. if "new" is given, enter launcher mode.
agent <id>		enter interactive agent reader mode for agent <id>
create action		enter interactive action creation mode
create investigator	create a new investigator, will prompt for name and public key
command <id>		enter command reader mode for command <id>
exit			leave
help			show this help
investigator <id>	enter interactive investigator management mode for investigator <id>
search			perform a search. see "search help" for more information.
showcfg			display running configuration
status			display platform status: connected agents, latest actions, ...
`)
		case "investigator":
			err = investigatorReader(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "search":
			err = search(input, cli)
			if err != nil {
				log.Println(err)
			}
		case "showcfg":
			fmt.Printf("homedir = %s\n[api]\n    url = %s\n[gpg]\n    home = %s\n    keyid = %s\n",
				cli.Conf.API.URL, cli.Conf.Homedir, cli.Conf.GPG.Home, cli.Conf.GPG.KeyID)
		case "status":
			err = printStatus(cli)
			if err != nil {
				log.Println(err)
			}
		case "":
			break
		default:
			fmt.Printf("Unknown order '%s'\n", orders[0])
		}
		readline.AddHistory(input)
	}
exit:
	fmt.Fprintf(out, footer)
}

func printStatus(cli client.Client) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("printStatus() -> %v", e)
		}
	}()
	st, err := cli.GetAPIResource("dashboard")
	if err != nil {
		panic(err)
	}
	agtout := make([]string, 5)
	agtout[0] = "Agents Summary:"
	actout := make([]string, 2)
	actout[0] = "Latest Actions:"
	actout[1] = "----    ID      ---- + ----         Name         ---- + ----    Date    ---- + ---- Investigators ----"
	for _, item := range st.Collection.Items {
		for _, data := range item.Data {
			switch data.Name {
			case "action":
				idstr, name, datestr, invs, err := actionPrintShort(data.Value)
				if err != nil {
					panic(err)
				}
				str := fmt.Sprintf("%s   %s   %s   %s", idstr, name, datestr, invs)
				actout = append(actout, str)
			case "active agents":
				agtout[1] = fmt.Sprintf("* %.0f agents have checked in during the last 5 minutes", data.Value)
			case "agents versions count":
				bData, err := json.Marshal(data.Value)
				if err != nil {
					panic(err)
				}
				var sum []migdb.AgentsSum
				err = json.Unmarshal(bData, &sum)
				if err != nil {
					panic(err)
				}
				for _, asum := range sum {
					s := fmt.Sprintf("* version %s: %.0f agent", asum.Version, asum.Count)
					if asum.Count > 1 {
						s += "s"
					}
					agtout = append(agtout, s)
				}
			case "agents started in the last 24 hours":
				agtout[2] = fmt.Sprintf("* %.0f agents (re)started in the last 24 hours", data.Value)
			case "endpoints running 2 or more agents":
				agtout[3] = fmt.Sprintf("* %.0f endpoints are running 2 or more agents", data.Value)
			case "endpoints that have disappeared over last 7 days":
				agtout[4] = fmt.Sprintf("* %.0f endpoints have disappeared over the last 7 days", data.Value)
			}
		}
	}
	fmt.Println("\x1b[31;1m+------\x1b[0m")
	for _, s := range agtout {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
	}
	fmt.Println("\x1b[31;1m|\x1b[0m")
	for _, s := range actout {
		fmt.Println("\x1b[31;1m|\x1b[0m " + s)
		if len(actout) < 2 {
			fmt.Println("\x1b[31;1m|\x1b[0m * None")
			break
		}
	}
	fmt.Println("\x1b[31;1m+------\x1b[0m")
	return
}

func shorten(p string) string {
	if len(p) < 8 {
		return p
	}
	out := p[0:7]
	out += "."
	out += "."
	out += "."
	if len(p) > 18 {
		out += p[len(p)-7 : len(p)]
	}
	return out
}

var banner string = `
## ##                                     _.---._     .---.
# # # /-\ ---||  |    /\         __...---' .---. '---'-.   '.
#   #|   | / ||  |   /--\    .-''__.--' _.'( | )'.  '.  '._ :
#   # \_/ ---| \_ \_/    \ .'__-'_ .--'' ._'---'_.-.  '.   '-'.
     ###                         ~ -._ -._''---. -.    '-._   '.
      # |\ |\    /---------|          ~ -.._ _ _ _ ..-_ '.  '-._''--.._
      # | \| \  / |- |__ | |                       -~ -._  '-.  -. '-._''--.._.--''.
     ###|  \  \/  ---__| | |                            ~ ~-.__     -._  '-.__   '. '.
          #####                                               ~~ ~---...__ _    ._ .' '.
          #      /\  --- /-\ |--|----                                    ~  ~--.....--~
          # ### /--\  | |   ||-\  //
          #####/    \ |  \_/ |  \//__
`

var footer string = `
            .-._   _ _ _ _ _ _ _ _
 .-''-.__.-'Oo  '-' ' ' ' ' ' ' ' '-.
'.___ '    .   .--_'-' '-' '-' _'-' '._
 V: V 'vv-'   '_   '.       .'  _..' '.'.
   '=.____.=_.--'   :_.__.__:_   '.   : :
           (((____.-'        '-.  /   : :
                             (((-'\ .' /
                           _____..'  .'
                          '-._____.-'
              Gators are going back underwater.
`
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package migrationtarget_test

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/migrationtarget"
	"github.com/juju/juju/apiserver/params"
	apiservertesting "github.com/juju/juju/apiserver/testing"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/juju/core/description"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/jujuclient/jujuclienttesting"
	"github.com/juju/juju/provider/dummy"
	_ "github.com/juju/juju/provider/dummy"
	"github.com/juju/juju/state"
	statetesting "github.com/juju/juju/state/testing"
	"github.com/juju/juju/testing"
)

type Suite struct {
	statetesting.StateSuite
	resources  *common.Resources
	authorizer apiservertesting.FakeAuthorizer
}

var _ = gc.Suite(&Suite{})

func (s *Suite) SetUpTest(c *gc.C) {
	// Required to allow model import test to work.
	s.StateSuite.SetUpTest(c)
	env, err := environs.Prepare(
		modelcmd.BootstrapContext(testing.Context(c)),
		jujuclienttesting.NewMemStore(),
		environs.PrepareParams{
			ControllerName: "dummycontroller",
			BaseConfig:     dummy.SampleConfig(),
			CloudName:      "dummy",
		},
	)
	c.Assert(err, jc.ErrorIsNil)
	s.InitialConfig = testing.CustomModelConfig(c, env.Config().AllAttrs())

	s.resources = common.NewResources()
	s.AddCleanup(func(*gc.C) { s.resources.StopAll() })

	s.authorizer = apiservertesting.FakeAuthorizer{
		Tag: s.Owner,
	}
}

func (s *Suite) TestFacadeRegistered(c *gc.C) {
	factory, err := common.Facades.GetFactory("MigrationTarget", 1)
	c.Assert(err, jc.ErrorIsNil)

	api, err := factory(s.State, s.resources, s.authorizer, "")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(api, gc.FitsTypeOf, new(migrationtarget.API))
}

func (s *Suite) TestNotUser(c *gc.C) {
	s.authorizer.Tag = names.NewMachineTag("0")
	_, err := s.newAPI()
	c.Assert(errors.Cause(err), gc.Equals, common.ErrPerm)
}

func (s *Suite) TestNotControllerAdmin(c *gc.C) {
	s.authorizer.Tag = names.NewUserTag("jrandomuser")
	_, err := s.newAPI()
	c.Assert(errors.Cause(err), gc.Equals, common.ErrPerm)
}

func (s *Suite) importModel(c *gc.C, api *migrationtarget.API) names.ModelTag {
	uuid, bytes := s.makeExportedModel(c)
	err := api.Import(params.SerializedModel{Bytes: bytes})
	c.Assert(err, jc.ErrorIsNil)
	return names.NewModelTag(uuid)
}

func (s *Suite) TestImport(c *gc.C) {
	api := s.mustNewAPI(c)
	tag := s.importModel(c, api)
	// Check the model was imported.
	model, err := s.State.GetModel(tag)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(model.Name(), gc.Equals, "some-model")
	c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeImporting)
}

func (s *Suite) TestAbort(c *gc.C) {
	api := s.mustNewAPI(c)
	tag := s.importModel(c, api)

	err := api.Abort(params.ModelArgs{ModelTag: tag.String()})
	c.Assert(err, jc.ErrorIsNil)

	// The model should no longer exist.
	_, err = s.State.GetModel(tag)
	c.Assert(err, gc.ErrorMatches, `model not found`)
}

func (s *Suite) TestAbortNotATag(c *gc.C) {
	api := s.mustNewAPI(c)
	err := api.Abort(params.ModelArgs{ModelTag: "not-a-tag"})
	c.Assert(err, gc.ErrorMatches, `"not-a-tag" is not a valid tag`)
}

func (s *Suite) TestAbortMissingEnv(c *gc.C) {
	api := s.mustNewAPI(c)
	newUUID := utils.MustNewUUID().String()
	err := api.Abort(params.ModelArgs{ModelTag: names.NewModelTag(newUUID).String()})
	c.Assert(err, gc.ErrorMatches, `model not found`)
}

func (s *Suite) TestAbortNotImportingEnv(c *gc.C) {
	st := s.Factory.MakeModel(c, nil)
	defer st.Close()
	model, err := st.Model()
	c.Assert(err, jc.ErrorIsNil)

	api := s.mustNewAPI(c)
	err = api.Abort(params.ModelArgs{ModelTag: model.ModelTag().String()})
	c.Assert(err, gc.ErrorMatches, `migration mode for the model is not importing`)
}

func (s *Suite) TestActivate(c *gc.C) {
	api := s.mustNewAPI(c)
	tag := s.importModel(c, api)

	err := api.Activate(params.ModelArgs{ModelTag: tag.String()})
	c.Assert(err, jc.ErrorIsNil)

	// The model should no longer exist.
	model, err := s.State.GetModel(tag)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeActive)
}

func (s *Suite) TestActivateNotATag(c *gc.C) {
	api := s.mustNewAPI(c)
	err := api.Activate(params.ModelArgs{ModelTag: "not-a-tag"})
	c.Assert(err, gc.ErrorMatches, `"not-a-tag" is not a valid tag`)
}

func (s *Suite) TestActivateMissingEnv(c *gc.C) {
	api := s.mustNewAPI(c)
	newUUID := utils.MustNewUUID().String()
	err := api.Activate(params.ModelArgs{ModelTag: names.NewModelTag(newUUID).String()})
	c.Assert(err, gc.ErrorMatches, `model not found`)
}

func (s *Suite) TestActivateNotImportingEnv(c *gc.C) {
	st := s.Factory.MakeModel(c, nil)
	defer st.Close()
	model, err := st.Model()
	c.Assert(err, jc.ErrorIsNil)

	api := s.mustNewAPI(c)
	err = api.Activate(params.ModelArgs{ModelTag: model.ModelTag().String()})
	c.Assert(err, gc.ErrorMatches, `migration mode for the model is not importing`)
}

func (s *Suite) newAPI() (*migrationtarget.API, error) {
	return migrationtarget.NewAPI(s.State, s.resources, s.authorizer)
}

func (s *Suite) mustNewAPI(c *gc.C) *migrationtarget.API {
	api, err := s.newAPI()
	c.Assert(err, jc.ErrorIsNil)
	return api
}

func (s *Suite) makeExportedModel(c *gc.C) (string, []byte) {
	model, err := s.State.Export()
	c.Assert(err, jc.ErrorIsNil)

	newUUID := utils.MustNewUUID().String()
	model.UpdateConfig(map[string]interface{}{
		"name":    "some-model",
		"uuid":    newUUID,
		"ca-cert": "not really a cert",
	})

	bytes, err := description.Serialize(model)
	c.Assert(err, jc.ErrorIsNil)
	return newUUID, bytes
}
<commit_msg>apiserver/migrationtarget: Fix test model setup<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package migrationtarget_test

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/migrationtarget"
	"github.com/juju/juju/apiserver/params"
	apiservertesting "github.com/juju/juju/apiserver/testing"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/juju/core/description"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/jujuclient/jujuclienttesting"
	"github.com/juju/juju/provider/dummy"
	"github.com/juju/juju/state"
	statetesting "github.com/juju/juju/state/testing"
	"github.com/juju/juju/testing"
)

type Suite struct {
	statetesting.StateSuite
	resources  *common.Resources
	authorizer apiservertesting.FakeAuthorizer
}

var _ = gc.Suite(&Suite{})

func (s *Suite) SetUpTest(c *gc.C) {
	// Set up InitialConfig with a dummy provider configuration. This
	// is required to allow model import test to work.
	env, err := environs.Prepare(
		modelcmd.BootstrapContext(testing.Context(c)),
		jujuclienttesting.NewMemStore(),
		environs.PrepareParams{
			ControllerName: "dummycontroller",
			BaseConfig:     dummy.SampleConfig(),
			CloudName:      "dummy",
		},
	)
	c.Assert(err, jc.ErrorIsNil)
	s.InitialConfig = testing.CustomModelConfig(c, env.Config().AllAttrs())

	// The call up to StateSuite's SetUpTest uses s.InitialConfig so
	// it has to happen here.
	s.StateSuite.SetUpTest(c)

	s.resources = common.NewResources()
	s.AddCleanup(func(*gc.C) { s.resources.StopAll() })

	s.authorizer = apiservertesting.FakeAuthorizer{
		Tag: s.Owner,
	}
}

func (s *Suite) TestFacadeRegistered(c *gc.C) {
	factory, err := common.Facades.GetFactory("MigrationTarget", 1)
	c.Assert(err, jc.ErrorIsNil)

	api, err := factory(s.State, s.resources, s.authorizer, "")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(api, gc.FitsTypeOf, new(migrationtarget.API))
}

func (s *Suite) TestNotUser(c *gc.C) {
	s.authorizer.Tag = names.NewMachineTag("0")
	_, err := s.newAPI()
	c.Assert(errors.Cause(err), gc.Equals, common.ErrPerm)
}

func (s *Suite) TestNotControllerAdmin(c *gc.C) {
	s.authorizer.Tag = names.NewUserTag("jrandomuser")
	_, err := s.newAPI()
	c.Assert(errors.Cause(err), gc.Equals, common.ErrPerm)
}

func (s *Suite) importModel(c *gc.C, api *migrationtarget.API) names.ModelTag {
	uuid, bytes := s.makeExportedModel(c)
	err := api.Import(params.SerializedModel{Bytes: bytes})
	c.Assert(err, jc.ErrorIsNil)
	return names.NewModelTag(uuid)
}

func (s *Suite) TestImport(c *gc.C) {
	api := s.mustNewAPI(c)
	tag := s.importModel(c, api)
	// Check the model was imported.
	model, err := s.State.GetModel(tag)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(model.Name(), gc.Equals, "some-model")
	c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeImporting)
}

func (s *Suite) TestAbort(c *gc.C) {
	api := s.mustNewAPI(c)
	tag := s.importModel(c, api)

	err := api.Abort(params.ModelArgs{ModelTag: tag.String()})
	c.Assert(err, jc.ErrorIsNil)

	// The model should no longer exist.
	_, err = s.State.GetModel(tag)
	c.Assert(err, gc.ErrorMatches, `model not found`)
}

func (s *Suite) TestAbortNotATag(c *gc.C) {
	api := s.mustNewAPI(c)
	err := api.Abort(params.ModelArgs{ModelTag: "not-a-tag"})
	c.Assert(err, gc.ErrorMatches, `"not-a-tag" is not a valid tag`)
}

func (s *Suite) TestAbortMissingEnv(c *gc.C) {
	api := s.mustNewAPI(c)
	newUUID := utils.MustNewUUID().String()
	err := api.Abort(params.ModelArgs{ModelTag: names.NewModelTag(newUUID).String()})
	c.Assert(err, gc.ErrorMatches, `model not found`)
}

func (s *Suite) TestAbortNotImportingEnv(c *gc.C) {
	st := s.Factory.MakeModel(c, nil)
	defer st.Close()
	model, err := st.Model()
	c.Assert(err, jc.ErrorIsNil)

	api := s.mustNewAPI(c)
	err = api.Abort(params.ModelArgs{ModelTag: model.ModelTag().String()})
	c.Assert(err, gc.ErrorMatches, `migration mode for the model is not importing`)
}

func (s *Suite) TestActivate(c *gc.C) {
	api := s.mustNewAPI(c)
	tag := s.importModel(c, api)

	err := api.Activate(params.ModelArgs{ModelTag: tag.String()})
	c.Assert(err, jc.ErrorIsNil)

	// The model should no longer exist.
	model, err := s.State.GetModel(tag)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeActive)
}

func (s *Suite) TestActivateNotATag(c *gc.C) {
	api := s.mustNewAPI(c)
	err := api.Activate(params.ModelArgs{ModelTag: "not-a-tag"})
	c.Assert(err, gc.ErrorMatches, `"not-a-tag" is not a valid tag`)
}

func (s *Suite) TestActivateMissingEnv(c *gc.C) {
	api := s.mustNewAPI(c)
	newUUID := utils.MustNewUUID().String()
	err := api.Activate(params.ModelArgs{ModelTag: names.NewModelTag(newUUID).String()})
	c.Assert(err, gc.ErrorMatches, `model not found`)
}

func (s *Suite) TestActivateNotImportingEnv(c *gc.C) {
	st := s.Factory.MakeModel(c, nil)
	defer st.Close()
	model, err := st.Model()
	c.Assert(err, jc.ErrorIsNil)

	api := s.mustNewAPI(c)
	err = api.Activate(params.ModelArgs{ModelTag: model.ModelTag().String()})
	c.Assert(err, gc.ErrorMatches, `migration mode for the model is not importing`)
}

func (s *Suite) newAPI() (*migrationtarget.API, error) {
	return migrationtarget.NewAPI(s.State, s.resources, s.authorizer)
}

func (s *Suite) mustNewAPI(c *gc.C) *migrationtarget.API {
	api, err := s.newAPI()
	c.Assert(err, jc.ErrorIsNil)
	return api
}

func (s *Suite) makeExportedModel(c *gc.C) (string, []byte) {
	model, err := s.State.Export()
	c.Assert(err, jc.ErrorIsNil)

	newUUID := utils.MustNewUUID().String()
	model.UpdateConfig(map[string]interface{}{
		"name":    "some-model",
		"uuid":    newUUID,
		"ca-cert": "not really a cert",
	})

	bytes, err := description.Serialize(model)
	c.Assert(err, jc.ErrorIsNil)
	return newUUID, bytes
}
<|endoftext|>


<commit_before>/* Package db provides database helpers for using Squirrel with Cookoo.

This package provides a few convenience tools for consistently working with
Squirrel-based database connections in Cookoo.

Usage:

	package main

	import (
		"github.com/Masterminds/cookoo"
		"github.com/technosophos/crazysquirrel/db"
		"database/sql"
	)

	func main() {
		reg, router, cxt := cookoo.Cookoo()

		conn, err := sql.Open("postgres", "something")
		// ...

		// Set up a Postgres-friendly Squirrel environment.
		db.SetupDatasource(cxt, conn, db.Postgres)
	}

	func MyCommand(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
		// Now we can use the database from anywhere that has a Context.

		rows, err := db.Builder(c).Select("*").From("MyTable").Query()

		// ...
	}

	func Example(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
		// Get the *sql.DB
		conn := db.Db(c)

		// Get a squirrel.DbProxy (which happens to also be a squirrel.BaseRunner)
		// This implements most of the operations that are on *sql.Db, but
		// it wraps them in the statement cache.
		cache := db.Runner(c)

		// Get a squirrel.Builder
		builder := db.Builder(c)
	}

*/
package db

import (
	"github.com/Masterminds/cookoo"
	"github.com/lann/squirrel"
	"database/sql"
)

const (
	// Key for the raw *sql.Db
	DbObj = "db.Obj"
	// Key for getting the Squirrel StatementCacheProxy.
	DbRunner = "db.Runner"
	// Key for getting the Squirrel StatementBuilder.
	DbBuilder = "db.Builder"
)

// Config provides configuration options to SetupDatasource.
//
// Predefined configurations for Postgres and MySQL are provided as variables.
type Config struct {
	Placeholder squirrel.PlaceholderFormat
}

// Predefined config for Postgres defaults
var Postgres = Config{Placeholder: squirrel.Dollar}

// Predifined config for MySQL defaults
var MySQL = Config{Placeholder: squirrel.Question}

// SetupDatasource takes a database connection and creates an easy-to-use
// Squirrel wrapper.
//
// Generally, this is called early in Cookoo's initialization. See the example
// in the package documentation.
//
// The Config struct is used to pass options into the setup. Default
// configurations are provided for Postgres and MySQL.
//
// Example:
//
// 	db.SetupDatasource(c, dbConn, db.MySQL)
func SetupDatasource(c cookoo.Context, db *sql.DB, cfg Config) {

	// Statement Cache is a cache for prepared statements.
	sc := squirrel.NewStmtCacheProxy(db)
	builder := squirrel.StatementBuilder.RunWith(sc)

	if cfg.Placeholder != nil {
		builder.PlaceholderFormat(cfg.Placeholder)
	}

	c.AddDatasource(DbObj, db)
	c.AddDatasource(DbRunner, sc)
	c.AddDatasource(DbBuilder, sc)
}

// Builder fetches the Squirrel statement builder datasource.
//
// Usage:
//
// 	db.Builder(cxt).Select("*").From("foo").Query()
//
func Builder(c cookoo.Context) *squirrel.StatementBuilderType {
	return c.Datasource(DbBuilder).(*squirrel.StatementBuilderType)
}

// Runner fetches a Squirrel DBProxyBeginner datasource.
//
// Usage:
//
// 	db.Runner(cxt).Exec("SELECT * FROM foo LIMIT 10")
func Runner(c cookoo.Context) squirrel.DBProxyBeginner {
	return c.Datasource(DbRunner).(squirrel.DBProxyBeginner)
}

// Db fetches the *sql.DB datasource.
func Db(c cookoo.Context) *sql.DB {
	return c.Datasource(DbObj).(*sql.DB)
}
<commit_msg>Update to use Masterminds/squirrel instead of lann/squirrel.<commit_after>/* Package db provides database helpers for using Squirrel with Cookoo.

This package provides a few convenience tools for consistently working with
Squirrel-based database connections in Cookoo.

Usage:

	package main

	import (
		"github.com/Masterminds/cookoo"
		"github.com/technosophos/crazysquirrel/db"
		"database/sql"
	)

	func main() {
		reg, router, cxt := cookoo.Cookoo()

		conn, err := sql.Open("postgres", "something")
		// ...

		// Set up a Postgres-friendly Squirrel environment.
		db.SetupDatasource(cxt, conn, db.Postgres)
	}

	func MyCommand(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
		// Now we can use the database from anywhere that has a Context.

		rows, err := db.Builder(c).Select("*").From("MyTable").Query()

		// ...
	}

	func Example(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
		// Get the *sql.DB
		conn := db.Db(c)

		// Get a squirrel.DbProxy (which happens to also be a squirrel.BaseRunner)
		// This implements most of the operations that are on *sql.Db, but
		// it wraps them in the statement cache.
		cache := db.Runner(c)

		// Get a squirrel.Builder
		builder := db.Builder(c)
	}

*/
package db

import (
	"database/sql"

	"github.com/Masterminds/cookoo"
	"github.com/Masterminds/squirrel"
)

const (
	// Key for the raw *sql.Db
	DbObj = "db.Obj"
	// Key for getting the Squirrel StatementCacheProxy.
	DbRunner = "db.Runner"
	// Key for getting the Squirrel StatementBuilder.
	DbBuilder = "db.Builder"
	// Key for getting the DB driver.
	DbDriver = "db.Driver"
)

// Config provides configuration options to SetupDatasource.
//
// Predefined configurations for Postgres and MySQL are provided as variables.
type Config struct {
	Placeholder squirrel.PlaceholderFormat
}

// Predefined config for Postgres defaults
var Postgres = Config{Placeholder: squirrel.Dollar}

// Predifined config for MySQL defaults
var MySQL = Config{Placeholder: squirrel.Question}

// SetupDatasource takes a database connection and creates an easy-to-use
// Squirrel wrapper.
//
// Generally, this is called early in Cookoo's initialization. See the example
// in the package documentation.
//
// The Config struct is used to pass options into the setup. Default
// configurations are provided for Postgres and MySQL.
//
// Example:
//
// 	db.SetupDatasource(c, dbConn, db.MySQL)
func SetupDatasource(c cookoo.Context, db *sql.DB, cfg Config) {

	// Statement Cache is a cache for prepared statements.
	sc := squirrel.NewStmtCacheProxy(db)
	builder := squirrel.StatementBuilder.RunWith(sc)

	if cfg.Placeholder != nil {
		builder.PlaceholderFormat(cfg.Placeholder)
	}

	c.AddDatasource(DbObj, db)
	c.AddDatasource(DbRunner, sc)
	c.AddDatasource(DbBuilder, sc)
}

// Builder fetches the Squirrel statement builder datasource.
//
// Usage:
//
// 	db.Builder(cxt).Select("*").From("foo").Query()
//
func Builder(c cookoo.Context) *squirrel.StatementBuilderType {
	return c.Datasource(DbBuilder).(*squirrel.StatementBuilderType)
}

// Runner fetches a Squirrel DBProxyBeginner datasource.
//
// Usage:
//
// 	db.Runner(cxt).Exec("SELECT * FROM foo LIMIT 10")
func Runner(c cookoo.Context) squirrel.DBProxyBeginner {
	return c.Datasource(DbRunner).(squirrel.DBProxyBeginner)
}

// Db fetches the *sql.DB datasource.
func Db(c cookoo.Context) *sql.DB {
	return c.Datasource(DbObj).(*sql.DB)
}
<|endoftext|>


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

// Package ems provides an expiring memory store
package ems

import "errors"
import "github.com/streamrail/concurrent-map"

type ExpiringMemoryStore struct {
	elementMap *cmap.ConcurrentMap
}

var expiredElementError = errors.New("Element expired")
var elementNotFoundError = errors.New("Element not found")

func NewExpiringMemoryStore() *ExpiringMemoryStore {
	return &ExpiringMemoryStore{elementMap: cmap.NewConcurrentMap()}
}

func (store *ExpiringMemoryStore) Read(name string) (string, error) {
	map_entry, ok := store.elementMap.Get(name)

	// Checks if item exists
	if ok == true {
		// Map stores items as interface{}, hence we'll have to cast.
		element := map_entry.(*Element)
		if element.IsExpired() {
			return "", expiredElementError
		} else {
			return element.value, nil
		}
	}

	return "", elementNotFoundError
}

func (store *ExpiringMemoryStore) Write(name string, value string) {
	element := NewElement(value)
	store.elementMap.Add(name, element)
}

func (store *ExpiringMemoryStore) WriteWithExpiration(name string, value string, expiresIn int64) {
	element := NewElementWithExpiration(value, expiresIn)
	store.elementMap.Add(name, element)
}

func (store *ExpiringMemoryStore) Remove(name string) {
	store.elementMap.Remove(name)
}

func (store *ExpiringMemoryStore) Exists(name string) bool {
	return store.elementMap.Has(name)
}

func (store *ExpiringMemoryStore) Clear() {
	store.elementMap.Clear()
}

func (store *ExpiringMemoryStore) CountActive() int {
	activeCount := 0
	for t := range store.elementMap.Iter() {
		element := t.Val.(*Element)
		if !element.IsExpired() {
			activeCount = activeCount + 1
		}
	}
	return activeCount
}

func (store *ExpiringMemoryStore) CountAll() int {
	return store.elementMap.Count()
}
<commit_msg>Updated to use latest changes to concurrent-map<commit_after>// Copyright 2014 Brighcove Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Package ems provides an expiring memory store
package ems

import "errors"
import "github.com/streamrail/concurrent-map"

type ExpiringMemoryStore struct {
	elementMap cmap.ConcurrentMap
}

var expiredElementError = errors.New("Element expired")
var elementNotFoundError = errors.New("Element not found")

func NewExpiringMemoryStore() *ExpiringMemoryStore {
	return &ExpiringMemoryStore{elementMap: cmap.New()}
}

func (store *ExpiringMemoryStore) Read(name string) (string, error) {
	map_entry, ok := store.elementMap.Get(name)

	// Checks if item exists
	if ok == true {
		// Map stores items as interface{}, hence we'll have to cast.
		element := map_entry.(*Element)
		if element.IsExpired() {
			return "", expiredElementError
		} else {
			return element.value, nil
		}
	}

	return "", elementNotFoundError
}

func (store *ExpiringMemoryStore) Write(name string, value string) {
	element := NewElement(value)
	store.elementMap.Set(name, element)
}

func (store *ExpiringMemoryStore) WriteWithExpiration(name string, value string, expiresIn int64) {
	element := NewElementWithExpiration(value, expiresIn)
	store.elementMap.Set(name, element)
}

func (store *ExpiringMemoryStore) Remove(name string) {
	store.elementMap.Remove(name)
}

func (store *ExpiringMemoryStore) Exists(name string) bool {
	return store.elementMap.Has(name)
}

func (store *ExpiringMemoryStore) Clear() {
	for t := range store.elementMap.Iter() {
		store.elementMap.Remove(t.Key)
	}
}

func (store *ExpiringMemoryStore) CountActive() int {
	activeCount := 0
	for t := range store.elementMap.Iter() {
		element := t.Val.(*Element)
		if !element.IsExpired() {
			activeCount = activeCount + 1
		}
	}
	return activeCount
}

func (store *ExpiringMemoryStore) CountAll() int {
	return store.elementMap.Count()
}
<|endoftext|>


<commit_before>// +build !darwin

package storage

type KeychainStorage struct {
}

func NewKeychainStorage() (*KeychainStorage, error) {
	storage := KeychainStorage{}

	return &storage, nil
}

func (s *KeychainStorage) ReadSecret(label string) ([]byte, error) {
	return nil, Unsupported
}

func (s *KeychainStorage) WriteSecret(label string, data []byte) error {
	return Unsupported
}
<commit_msg>Fix invalid variable<commit_after>// +build !darwin

package storage

type KeychainStorage struct {
}

func NewKeychainStorage() (*KeychainStorage, error) {
	storage := KeychainStorage{}

	return &storage, nil
}

func (s *KeychainStorage) ReadSecret(label string) ([]byte, error) {
	return nil, ErrUnsupported
}

func (s *KeychainStorage) WriteSecret(label string, data []byte) error {
	return ErrUnsupported
}
<|endoftext|>


<commit_before>package cloud

import (
	"os"
	"testing"

	"github.com/loadimpact/k6/lib"
	"github.com/stretchr/testify/assert"
)

func TestGetName(t *testing.T) {
	nameTests := []struct {
		lib      *lib.SourceData
		conf     loadimpactConfig
		expected string
	}{
		{&lib.SourceData{Filename: ""}, loadimpactConfig{}, TestName},
		{&lib.SourceData{Filename: "-"}, loadimpactConfig{}, TestName},
		{&lib.SourceData{Filename: "script.js"}, loadimpactConfig{}, "script.js"},
		{&lib.SourceData{Filename: "/file/name.js"}, loadimpactConfig{}, "name.js"},
		{&lib.SourceData{Filename: "/file/name"}, loadimpactConfig{}, "name"},
		{&lib.SourceData{Filename: "/file/name"}, loadimpactConfig{Name: "confName"}, "confName"},
	}

	for _, test := range nameTests {
		actual := getName(test.lib, test.conf)
		assert.Equal(t, actual, test.expected)
	}

	err := os.Setenv("K6CLOUD_NAME", "envname")
	assert.Nil(t, err)

	for _, test := range nameTests {
		actual := getName(test.lib, test.conf)
		assert.Equal(t, actual, "envname")

	}
}
<commit_msg>Add license<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2017 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cloud

import (
	"os"
	"testing"

	"github.com/loadimpact/k6/lib"
	"github.com/stretchr/testify/assert"
)

func TestGetName(t *testing.T) {
	nameTests := []struct {
		lib      *lib.SourceData
		conf     loadimpactConfig
		expected string
	}{
		{&lib.SourceData{Filename: ""}, loadimpactConfig{}, TestName},
		{&lib.SourceData{Filename: "-"}, loadimpactConfig{}, TestName},
		{&lib.SourceData{Filename: "script.js"}, loadimpactConfig{}, "script.js"},
		{&lib.SourceData{Filename: "/file/name.js"}, loadimpactConfig{}, "name.js"},
		{&lib.SourceData{Filename: "/file/name"}, loadimpactConfig{}, "name"},
		{&lib.SourceData{Filename: "/file/name"}, loadimpactConfig{Name: "confName"}, "confName"},
	}

	for _, test := range nameTests {
		actual := getName(test.lib, test.conf)
		assert.Equal(t, actual, test.expected)
	}

	err := os.Setenv("K6CLOUD_NAME", "envname")
	assert.Nil(t, err)

	for _, test := range nameTests {
		actual := getName(test.lib, test.conf)
		assert.Equal(t, actual, "envname")
	}
}
<|endoftext|>


<commit_before>package memdump

import (
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
)

func assertCompareDescriptors(t *testing.T, a interface{}, b interface{}, expected bool) {
	da := describe(reflect.TypeOf(a))
	db := describe(reflect.TypeOf(b))
	assert.Equal(t, expected, descriptorsEqual(da, db))
}

func TestDescribeScalar(t *testing.T) {
	assertCompareDescriptors(t, "abc", "d", true)
	assertCompareDescriptors(t, 123, 456, true)
	assertCompareDescriptors(t, 1.2, 3.4, true)
	assertCompareDescriptors(t, uint64(12), uint64(34), true)

	assertCompareDescriptors(t, int16(12), int64(34), false)
	assertCompareDescriptors(t, uint64(12), int64(34), false)
	assertCompareDescriptors(t, uint64(12), "34", false)
	assertCompareDescriptors(t, uint64(12), 4.5, false)
}

func TestDescribePointer(t *testing.T) {
	var x int
	var y uint64
	var z string

	assertCompareDescriptors(t, &x, &x, true)
	assertCompareDescriptors(t, &y, &y, true)
	assertCompareDescriptors(t, &z, &z, true)

	assertCompareDescriptors(t, &x, &y, false)
	assertCompareDescriptors(t, &x, &z, false)
	assertCompareDescriptors(t, &x, x, false)

	xptr := &x
	xptrptr := &xptr

	assertCompareDescriptors(t, xptrptr, xptrptr, true)
	assertCompareDescriptors(t, xptr, xptrptr, false)
	assertCompareDescriptors(t, x, xptrptr, false)
}

func TestDescribeSlice(t *testing.T) {
	var x []int
	var y []uint64
	var z [][]int16

	assertCompareDescriptors(t, x, x, true)
	assertCompareDescriptors(t, y, y, true)
	assertCompareDescriptors(t, z, z, true)
	assertCompareDescriptors(t, &x, &x, true)
	assertCompareDescriptors(t, &y, &y, true)
	assertCompareDescriptors(t, &z, &z, true)

	assertCompareDescriptors(t, x, y, false)
	assertCompareDescriptors(t, &x, &y, false)
	assertCompareDescriptors(t, x, z, false)
	assertCompareDescriptors(t, &x, x, false)
}

func TestDescribeArray(t *testing.T) {
	var x [4]int
	var y [10]int
	var z [2][3]int16

	assertCompareDescriptors(t, x, x, true)
	assertCompareDescriptors(t, y, y, true)
	assertCompareDescriptors(t, z, z, true)
	assertCompareDescriptors(t, &x, &x, true)
	assertCompareDescriptors(t, &y, &y, true)
	assertCompareDescriptors(t, &z, &z, true)

	assertCompareDescriptors(t, x, y, false)
	assertCompareDescriptors(t, &x, &y, false)
	assertCompareDescriptors(t, x, z, false)
	assertCompareDescriptors(t, &x, x, false)
}

func TestDescribeStruct(t *testing.T) {
	type u struct {
		A string
		B int
	}
	type v struct {
		A string
		B int32
	}
	type w struct {
		u
		A string
		B int
		c v
	}

	assertCompareDescriptors(t, u{}, u{}, true)
	assertCompareDescriptors(t, v{}, v{}, true)
	assertCompareDescriptors(t, w{}, w{}, true)
	assertCompareDescriptors(t, &u{}, &u{}, true)
	assertCompareDescriptors(t, &v{}, &v{}, true)
	assertCompareDescriptors(t, &w{}, &w{}, true)

	assertCompareDescriptors(t, u{}, v{}, false)
	assertCompareDescriptors(t, u{}, w{}, false)
	assertCompareDescriptors(t, v{}, w{}, false)
	assertCompareDescriptors(t, u{}, &u{}, false)
}

func TestDescribeStructWithTags(t *testing.T) {
	type u struct {
		A string
		B int
	}
	type v struct {
		A   string
		xyz int
	}
	type w struct {
		A string
		B int `memdump:"xyz"`
	}

	assertCompareDescriptors(t, u{}, v{}, false)
	assertCompareDescriptors(t, u{}, w{}, false)
	assertCompareDescriptors(t, v{}, w{}, true)
}
<commit_msg>add test for unsupported kinds<commit_after>package memdump

import (
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
)

func assertCompareDescriptors(t *testing.T, a interface{}, b interface{}, expected bool) {
	da := describe(reflect.TypeOf(a))
	db := describe(reflect.TypeOf(b))
	assert.Equal(t, expected, descriptorsEqual(da, db))
}

func TestDescribeScalar(t *testing.T) {
	assertCompareDescriptors(t, "abc", "d", true)
	assertCompareDescriptors(t, 123, 456, true)
	assertCompareDescriptors(t, 1.2, 3.4, true)
	assertCompareDescriptors(t, uint64(12), uint64(34), true)

	assertCompareDescriptors(t, int16(12), int64(34), false)
	assertCompareDescriptors(t, uint64(12), int64(34), false)
	assertCompareDescriptors(t, uint64(12), "34", false)
	assertCompareDescriptors(t, uint64(12), 4.5, false)
}

func TestDescribePointer(t *testing.T) {
	var x int
	var y uint64
	var z string

	assertCompareDescriptors(t, &x, &x, true)
	assertCompareDescriptors(t, &y, &y, true)
	assertCompareDescriptors(t, &z, &z, true)

	assertCompareDescriptors(t, &x, &y, false)
	assertCompareDescriptors(t, &x, &z, false)
	assertCompareDescriptors(t, &x, x, false)

	xptr := &x
	xptrptr := &xptr

	assertCompareDescriptors(t, xptrptr, xptrptr, true)
	assertCompareDescriptors(t, xptr, xptrptr, false)
	assertCompareDescriptors(t, x, xptrptr, false)
}

func TestDescribeSlice(t *testing.T) {
	var x []int
	var y []uint64
	var z [][]int16

	assertCompareDescriptors(t, x, x, true)
	assertCompareDescriptors(t, y, y, true)
	assertCompareDescriptors(t, z, z, true)
	assertCompareDescriptors(t, &x, &x, true)
	assertCompareDescriptors(t, &y, &y, true)
	assertCompareDescriptors(t, &z, &z, true)

	assertCompareDescriptors(t, x, y, false)
	assertCompareDescriptors(t, &x, &y, false)
	assertCompareDescriptors(t, x, z, false)
	assertCompareDescriptors(t, &x, x, false)
}

func TestDescribeArray(t *testing.T) {
	var x [4]int
	var y [10]int
	var z [2][3]int16

	assertCompareDescriptors(t, x, x, true)
	assertCompareDescriptors(t, y, y, true)
	assertCompareDescriptors(t, z, z, true)
	assertCompareDescriptors(t, &x, &x, true)
	assertCompareDescriptors(t, &y, &y, true)
	assertCompareDescriptors(t, &z, &z, true)

	assertCompareDescriptors(t, x, y, false)
	assertCompareDescriptors(t, &x, &y, false)
	assertCompareDescriptors(t, x, z, false)
	assertCompareDescriptors(t, &x, x, false)
}

func TestDescribeStruct(t *testing.T) {
	type u struct {
		A string
		B int
	}
	type v struct {
		A string
		B int32
	}
	type w struct {
		u
		A string
		B int
		c v
	}

	assertCompareDescriptors(t, u{}, u{}, true)
	assertCompareDescriptors(t, v{}, v{}, true)
	assertCompareDescriptors(t, w{}, w{}, true)
	assertCompareDescriptors(t, &u{}, &u{}, true)
	assertCompareDescriptors(t, &v{}, &v{}, true)
	assertCompareDescriptors(t, &w{}, &w{}, true)

	assertCompareDescriptors(t, u{}, v{}, false)
	assertCompareDescriptors(t, u{}, w{}, false)
	assertCompareDescriptors(t, v{}, w{}, false)
	assertCompareDescriptors(t, u{}, &u{}, false)
}

func TestDescribeStructWithTags(t *testing.T) {
	type u struct {
		A string
		B int
	}
	type v struct {
		A   string
		xyz int
	}
	type w struct {
		A string
		B int `memdump:"xyz"`
	}

	assertCompareDescriptors(t, u{}, v{}, false)
	assertCompareDescriptors(t, u{}, w{}, false)
	assertCompareDescriptors(t, v{}, w{}, true)
}

func TestDescribe_PanicsOnMap(t *testing.T) {
	type T struct {
		A map[string]int
	}
	assert.Panics(t, func() {
		describe(reflect.TypeOf(T{}))
	})
}
<|endoftext|>


<commit_before>package commandevaluators

import (
	"errors"
	"strings"

	"github.com/byuoitav/av-api/base"
	"github.com/byuoitav/av-api/dbo"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

//ChangeVideoInputDefault is struct that implements the CommandEvaluation struct
type ChangeVideoInputDefault struct {
}

//Evaluate fulfills the CommmandEvaluation evaluate requirement.
func (p *ChangeVideoInputDefault) Evaluate(room base.PublicRoom) (actions []base.ActionStructure, err error) {
	//RoomWideSetVideoInput
	if len(room.CurrentVideoInput) > 0 { // Check if the user sent a PUT body changing the current video input
		var tempActions []base.ActionStructure

		tempActions, err = generateChangeInputByRole(
			"VideoOut",
			room.CurrentVideoInput,
			room.Room,
			room.Building,
			"ChangeVideoInputDefault"
		)

		if err != nil {
			return
		}

		actions = append(actions, tempActions...)
	}

	//Displays
	for _, d := range room.Displays { // Loop through the devices array (potentially) passed in the user's PUT body
		if len(d.Input) < 1 {
			continue
		}

		var action base.ActionStructure

		action, err = generateChangeInputByDevice(d.Device, room.Room, room.Building, "ChangeVideoInputDefault")
		if err != nil {
			return
		}
		actions = append(actions, action)
	}

	return
}

//Validate fulfills the Fulfill requirement on the command interface
func (p *ChangeVideoInputDefault) Validate(action base.ActionStructure) (err error) {
	return nil
}

//GetIncompatibleCommands keeps track of actions that are incompatable (on the same device)
func (p *ChangeVideoInputDefault) GetIncompatibleCommands() (incompatableActions []string) {
	return
}

func generateChangeInputByDevice(dev base.Device, room string, building string, generatingEvaluator string) (action base.ActionStructure, err error) {
	var curDevice accessors.Device

	curDevice, err = dbo.GetDeviceByName(building, room, dev.Name)
	if err != nil {
		return
	}

	paramMap := make(map[string]string)

	for _, port := range curDevice.Ports {
		if strings.EqualFold(port.Source, dev.Input) {
			paramMap["port"] = port.Name
			break
		}
	}

	if len(paramMap) == 0 {
		err = errors.New("No port found for input.")
		return
	}

	action = base.ActionStructure{
		Action:              "ChangeInput",
		GeneratingEvaluator: generatingEvaluator,
		Device:              curDevice,
		Parameters:          paramMap,
		DeviceSpecific:      true,
		Overridden:          false,
	}

	return
}

func generateChangeInputByRole(role string, input string, room string, building string, generatingEvaluator string) (actions []base.ActionStructure, err error) {
	devicesToChange, err := dbo.GetDevicesByBuildingAndRoomAndRole(building, room, role)
	if err != nil {
		return
	}

	for _, d := range devicesToChange { // Loop through the devices in the room
		paramMap := make(map[string]string) // Start building parameter map

		//Get the port mapping for the device
		for _, curPort := range d.Ports { // Loop through the found ports
			if strings.EqualFold(curPort.Source, input) {
				paramMap["port"] = curPort.Name
				break
			}
		}

		if len(paramMap) == 0 {
			err = errors.New("No port found for input.")
			return
		}

		action := base.ActionStructure{
			Action:              "ChangeInput",
			GeneratingEvaluator: generatingEvaluator,
			Device:              d,
			Parameters:          paramMap,
			DeviceSpecific:      false,
			Overridden:          false,
		}

		actions = append(actions, action)

		return
	}

	return
}
<commit_msg>Forgot a comma<commit_after>package commandevaluators

import (
	"errors"
	"strings"

	"github.com/byuoitav/av-api/base"
	"github.com/byuoitav/av-api/dbo"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

//ChangeVideoInputDefault is struct that implements the CommandEvaluation struct
type ChangeVideoInputDefault struct {
}

//Evaluate fulfills the CommmandEvaluation evaluate requirement.
func (p *ChangeVideoInputDefault) Evaluate(room base.PublicRoom) (actions []base.ActionStructure, err error) {
	//RoomWideSetVideoInput
	if len(room.CurrentVideoInput) > 0 { // Check if the user sent a PUT body changing the current video input
		var tempActions []base.ActionStructure

		tempActions, err = generateChangeInputByRole(
			"VideoOut",
			room.CurrentVideoInput,
			room.Room,
			room.Building,
			"ChangeVideoInputDefault",
		)

		if err != nil {
			return
		}

		actions = append(actions, tempActions...)
	}

	//Displays
	for _, d := range room.Displays { // Loop through the devices array (potentially) passed in the user's PUT body
		if len(d.Input) < 1 {
			continue
		}

		var action base.ActionStructure

		action, err = generateChangeInputByDevice(d.Device, room.Room, room.Building, "ChangeVideoInputDefault")
		if err != nil {
			return
		}
		actions = append(actions, action)
	}

	return
}

//Validate fulfills the Fulfill requirement on the command interface
func (p *ChangeVideoInputDefault) Validate(action base.ActionStructure) (err error) {
	return nil
}

//GetIncompatibleCommands keeps track of actions that are incompatable (on the same device)
func (p *ChangeVideoInputDefault) GetIncompatibleCommands() (incompatableActions []string) {
	return
}

func generateChangeInputByDevice(dev base.Device, room string, building string, generatingEvaluator string) (action base.ActionStructure, err error) {
	var curDevice accessors.Device

	curDevice, err = dbo.GetDeviceByName(building, room, dev.Name)
	if err != nil {
		return
	}

	paramMap := make(map[string]string)

	for _, port := range curDevice.Ports {
		if strings.EqualFold(port.Source, dev.Input) {
			paramMap["port"] = port.Name
			break
		}
	}

	if len(paramMap) == 0 {
		err = errors.New("No port found for input.")
		return
	}

	action = base.ActionStructure{
		Action:              "ChangeInput",
		GeneratingEvaluator: generatingEvaluator,
		Device:              curDevice,
		Parameters:          paramMap,
		DeviceSpecific:      true,
		Overridden:          false,
	}

	return
}

func generateChangeInputByRole(role string, input string, room string, building string, generatingEvaluator string) (actions []base.ActionStructure, err error) {
	devicesToChange, err := dbo.GetDevicesByBuildingAndRoomAndRole(building, room, role)
	if err != nil {
		return
	}

	for _, d := range devicesToChange { // Loop through the devices in the room
		paramMap := make(map[string]string) // Start building parameter map

		//Get the port mapping for the device
		for _, curPort := range d.Ports { // Loop through the found ports
			if strings.EqualFold(curPort.Source, input) {
				paramMap["port"] = curPort.Name
				break
			}
		}

		if len(paramMap) == 0 {
			err = errors.New("No port found for input.")
			return
		}

		action := base.ActionStructure{
			Action:              "ChangeInput",
			GeneratingEvaluator: generatingEvaluator,
			Device:              d,
			Parameters:          paramMap,
			DeviceSpecific:      false,
			Overridden:          false,
		}

		actions = append(actions, action)

		return
	}

	return
}
<|endoftext|>


<commit_before>package scanner

import (
	"crypto/sha512"
	"github.com/Symantec/Dominator/sub/fsrateio"
	"io"
	"os"
	"path"
	"syscall"
)

func (fileSystem *FileSystem) getInode(stat *syscall.Stat_t) *Inode {
	inode := fileSystem.InodeTable[stat.Ino]
	if inode == nil {
		var _inode Inode
		inode = &_inode
		_inode.stat = *stat
		fileSystem.InodeTable[stat.Ino] = inode
	}
	return inode
}

func (directory *Directory) scan(fileSystem *FileSystem,
	parentName string) error {
	myPathName := path.Join(parentName, directory.name)
	file, err := os.Open(myPathName)
	if err != nil {
		return err
	}
	names, err := file.Readdirnames(-1)
	if err != nil {
		return err
	}
	file.Close()
	for _, name := range names {
		filename := path.Join(myPathName, name)
		var stat syscall.Stat_t
		err := syscall.Lstat(filename, &stat)
		if err != nil {
			return err
		}
		inode := fileSystem.getInode(&stat)
		if stat.Dev == directory.inode.stat.Dev {
			if stat.Mode&syscall.S_IFMT == syscall.S_IFDIR {
				var dir Directory
				dir.name = name
				dir.inode = inode
				err := dir.scan(fileSystem, myPathName)
				if err != nil {
					return err
				}
				directory.DirectoryList = append(directory.DirectoryList, &dir)
			} else {
				var file File
				file.name = name
				file.inode = inode
				err := file.scan(fileSystem, myPathName)
				if err != nil {
					return err
				}
				directory.FileList = append(directory.FileList, &file)
			}
		}
	}
	return nil
}

func (file *File) scan(fileSystem *FileSystem, parentName string) error {
	myPathName := path.Join(parentName, file.name)
	if file.inode.stat.Mode&syscall.S_IFMT != syscall.S_IFREG {
		return nil
	}
	if len(file.inode.hash) > 0 {
		return nil
	}
	f, err := os.Open(myPathName)
	if err != nil {
		return err
	}
	reader := fsrateio.NewReader(f, fileSystem.ctx)
	hash := sha512.New()
	io.Copy(hash, reader)
	f.Close()
	file.inode.hash = hash.Sum(nil)
	return nil
}
<commit_msg>Fix directory scanner to silently skip files which go missing.<commit_after>package scanner

import (
	"crypto/sha512"
	"github.com/Symantec/Dominator/sub/fsrateio"
	"io"
	"os"
	"path"
	"syscall"
)

func (fileSystem *FileSystem) getInode(stat *syscall.Stat_t) *Inode {
	inode := fileSystem.InodeTable[stat.Ino]
	if inode == nil {
		var _inode Inode
		inode = &_inode
		_inode.stat = *stat
		fileSystem.InodeTable[stat.Ino] = inode
	}
	return inode
}

func (directory *Directory) scan(fileSystem *FileSystem,
	parentName string) error {
	myPathName := path.Join(parentName, directory.name)
	file, err := os.Open(myPathName)
	if err != nil {
		return err
	}
	names, err := file.Readdirnames(-1)
	if err != nil {
		return err
	}
	file.Close()
	for _, name := range names {
		filename := path.Join(myPathName, name)
		var stat syscall.Stat_t
		err := syscall.Lstat(filename, &stat)
		if err != nil {
			if err == syscall.ENOENT {
				continue
			}
			return err
		}
		inode := fileSystem.getInode(&stat)
		if stat.Dev == directory.inode.stat.Dev {
			if stat.Mode&syscall.S_IFMT == syscall.S_IFDIR {
				var dir Directory
				dir.name = name
				dir.inode = inode
				err := dir.scan(fileSystem, myPathName)
				if err != nil {
					return err
				}
				directory.DirectoryList = append(directory.DirectoryList, &dir)
			} else {
				var file File
				file.name = name
				file.inode = inode
				err := file.scan(fileSystem, myPathName)
				if err != nil {
					return err
				}
				directory.FileList = append(directory.FileList, &file)
			}
		}
	}
	return nil
}

func (file *File) scan(fileSystem *FileSystem, parentName string) error {
	myPathName := path.Join(parentName, file.name)
	if file.inode.stat.Mode&syscall.S_IFMT != syscall.S_IFREG {
		return nil
	}
	if len(file.inode.hash) > 0 {
		return nil
	}
	f, err := os.Open(myPathName)
	if err != nil {
		return err
	}
	reader := fsrateio.NewReader(f, fileSystem.ctx)
	hash := sha512.New()
	io.Copy(hash, reader)
	f.Close()
	file.inode.hash = hash.Sum(nil)
	return nil
}
<|endoftext|>


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

package kube

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"

	"github.com/coreos/flannel/pkg/ip"
	"github.com/coreos/flannel/subnet"

	"github.com/golang/glog"
	"golang.org/x/net/context"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	clientset "k8s.io/client-go/kubernetes"
	listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
)

var (
	ErrUnimplemented = errors.New("unimplemented")
)

const (
	resyncPeriod              = 5 * time.Minute
	nodeControllerSyncTimeout = 10 * time.Minute

	subnetKubeManagedAnnotation = "flannel.alpha.coreos.com/kube-subnet-manager"
	backendDataAnnotation       = "flannel.alpha.coreos.com/backend-data"
	backendTypeAnnotation       = "flannel.alpha.coreos.com/backend-type"
	backendPublicIPAnnotation   = "flannel.alpha.coreos.com/public-ip"

	netConfPath = "/etc/kube-flannel/net-conf.json"
)

type kubeSubnetManager struct {
	client         clientset.Interface
	nodeName       string
	nodeStore      listers.NodeLister
	nodeController cache.Controller
	subnetConf     *subnet.Config
	events         chan subnet.Event
}

func NewSubnetManager() (subnet.Manager, error) {
	cfg, err := rest.InClusterConfig()
	if err != nil {
		return nil, fmt.Errorf("unable to initialize inclusterconfig: %v", err)
	}
	c, err := clientset.NewForConfig(cfg)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize client: %v", err)
	}

	podName := os.Getenv("POD_NAME")
	podNamespace := os.Getenv("POD_NAMESPACE")
	if podName == "" || podNamespace == "" {
		return nil, fmt.Errorf("env variables POD_NAME and POD_NAMESPACE must be set")
	}

	pod, err := c.Pods(podNamespace).Get(podName, metav1.GetOptions{})
	if err != nil {
		return nil, fmt.Errorf("error retrieving pod spec for '%s/%s': %v", podNamespace, podName, err)
	}
	nodeName := pod.Spec.NodeName
	if nodeName == "" {
		return nil, fmt.Errorf("node name not present in pod spec '%s/%s'", podNamespace, podName)
	}

	netConf, err := ioutil.ReadFile(netConfPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read net conf: %v", err)
	}

	sc, err := subnet.ParseConfig(string(netConf))
	if err != nil {
		return nil, fmt.Errorf("error parsing subnet config: %s", err)
	}

	sm, err := newKubeSubnetManager(c, sc, nodeName)
	if err != nil {
		return nil, fmt.Errorf("error creating network manager: %s", err)
	}
	go sm.Run(context.Background())

	glog.Infof("Waiting %s for node controller to sync", nodeControllerSyncTimeout)
	err = wait.Poll(time.Second, nodeControllerSyncTimeout, func() (bool, error) {
		return sm.nodeController.HasSynced(), nil
	})
	if err != nil {
		return nil, fmt.Errorf("error waiting for nodeController to sync state: %v", err)
	}
	glog.Infof("Node controller sync successful")

	return sm, nil
}

func newKubeSubnetManager(c clientset.Interface, sc *subnet.Config, nodeName string) (*kubeSubnetManager, error) {
	var ksm kubeSubnetManager
	ksm.client = c
	ksm.nodeName = nodeName
	ksm.subnetConf = sc
	ksm.events = make(chan subnet.Event, 100)
	indexer, controller := cache.NewIndexerInformer(
		&cache.ListWatch{
			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
				return ksm.client.CoreV1().Nodes().List(options)
			},
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				return ksm.client.CoreV1().Nodes().Watch(options)
			},
		},
		&v1.Node{},
		resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				ksm.handleAddLeaseEvent(subnet.EventAdded, obj)
			},
			UpdateFunc: ksm.handleUpdateLeaseEvent,
			DeleteFunc: func(obj interface{}) {
				ksm.handleAddLeaseEvent(subnet.EventRemoved, obj)
			},
		},
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
	)
	ksm.nodeController = controller
	ksm.nodeStore = listers.NewNodeLister(indexer)
	return &ksm, nil
}

func (ksm *kubeSubnetManager) handleAddLeaseEvent(et subnet.EventType, obj interface{}) {
	n := obj.(*v1.Node)
	if s, ok := n.Annotations[subnetKubeManagedAnnotation]; !ok || s != "true" {
		return
	}

	l, err := nodeToLease(*n)
	if err != nil {
		glog.Infof("Error turning node %q to lease: %v", n.ObjectMeta.Name, err)
		return
	}
	ksm.events <- subnet.Event{et, l}
}

func (ksm *kubeSubnetManager) handleUpdateLeaseEvent(oldObj, newObj interface{}) {
	o := oldObj.(*v1.Node)
	n := newObj.(*v1.Node)
	if s, ok := n.Annotations[subnetKubeManagedAnnotation]; !ok || s != "true" {
		return
	}
	if o.Annotations[backendDataAnnotation] == n.Annotations[backendDataAnnotation] &&
		o.Annotations[backendTypeAnnotation] == n.Annotations[backendTypeAnnotation] &&
		o.Annotations[backendPublicIPAnnotation] == n.Annotations[backendPublicIPAnnotation] {
		return // No change to lease
	}

	l, err := nodeToLease(*n)
	if err != nil {
		glog.Infof("Error turning node %q to lease: %v", n.ObjectMeta.Name, err)
		return
	}
	ksm.events <- subnet.Event{subnet.EventAdded, l}
}

func (ksm *kubeSubnetManager) GetNetworkConfig(ctx context.Context) (*subnet.Config, error) {
	return ksm.subnetConf, nil
}

func (ksm *kubeSubnetManager) AcquireLease(ctx context.Context, attrs *subnet.LeaseAttrs) (*subnet.Lease, error) {
	cachedNode, err := ksm.nodeStore.Get(ksm.nodeName)
	if err != nil {
		return nil, err
	}
	nobj, err := api.Scheme.DeepCopy(cachedNode)
	if err != nil {
		return nil, err
	}
	n := nobj.(*v1.Node)

	if n.Spec.PodCIDR == "" {
		return nil, fmt.Errorf("node %q pod cidr not assigned", ksm.nodeName)
	}
	bd, err := attrs.BackendData.MarshalJSON()
	if err != nil {
		return nil, err
	}
	_, cidr, err := net.ParseCIDR(n.Spec.PodCIDR)
	if err != nil {
		return nil, err
	}
	if n.Annotations[backendDataAnnotation] != string(bd) ||
		n.Annotations[backendTypeAnnotation] != attrs.BackendType ||
		n.Annotations[backendPublicIPAnnotation] != attrs.PublicIP.String() ||
		n.Annotations[subnetKubeManagedAnnotation] != "true" {
		n.Annotations[backendTypeAnnotation] = attrs.BackendType
		n.Annotations[backendDataAnnotation] = string(bd)
		n.Annotations[backendPublicIPAnnotation] = attrs.PublicIP.String()
		n.Annotations[subnetKubeManagedAnnotation] = "true"

		oldData, err := json.Marshal(cachedNode)
		if err != nil {
			return nil, err
		}

		newData, err := json.Marshal(n)
		if err != nil {
			return nil, err
		}

		patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{})
		if err != nil {
			return nil, fmt.Errorf("failed to create patch for node %q: %v", ksm.nodeName, err)
		}

		_, err = ksm.client.CoreV1().Nodes().Patch(ksm.nodeName, types.StrategicMergePatchType, patchBytes, "status")
		if err != nil {
			return nil, err
		}
	}
	return &subnet.Lease{
		Subnet:     ip.FromIPNet(cidr),
		Attrs:      *attrs,
		Expiration: time.Now().Add(24 * time.Hour),
	}, nil
}

func (ksm *kubeSubnetManager) WatchLeases(ctx context.Context, cursor interface{}) (subnet.LeaseWatchResult, error) {
	select {
	case event := <-ksm.events:
		return subnet.LeaseWatchResult{
			Events: []subnet.Event{event},
		}, nil
	case <-ctx.Done():
		return subnet.LeaseWatchResult{}, nil
	}
}

func (ksm *kubeSubnetManager) Run(ctx context.Context) {
	glog.Infof("Starting kube subnet manager")
	ksm.nodeController.Run(ctx.Done())
}

func nodeToLease(n v1.Node) (l subnet.Lease, err error) {
	l.Attrs.PublicIP, err = ip.ParseIP4(n.Annotations[backendPublicIPAnnotation])
	if err != nil {
		return l, err
	}

	l.Attrs.BackendType = n.Annotations[backendTypeAnnotation]
	l.Attrs.BackendData = json.RawMessage(n.Annotations[backendDataAnnotation])

	_, cidr, err := net.ParseCIDR(n.Spec.PodCIDR)
	if err != nil {
		return l, err
	}

	l.Subnet = ip.FromIPNet(cidr)
	return l, nil
}

// unimplemented
func (ksm *kubeSubnetManager) RenewLease(ctx context.Context, lease *subnet.Lease) error {
	return ErrUnimplemented
}

func (ksm *kubeSubnetManager) WatchLease(ctx context.Context, sn ip.IP4Net, cursor interface{}) (subnet.LeaseWatchResult, error) {
	return subnet.LeaseWatchResult{}, ErrUnimplemented
}

func (ksm *kubeSubnetManager) RevokeLease(ctx context.Context, sn ip.IP4Net) error {
	return ErrUnimplemented
}

func (ksm *kubeSubnetManager) AddReservation(ctx context.Context, r *subnet.Reservation) error {
	return ErrUnimplemented
}

func (ksm *kubeSubnetManager) RemoveReservation(ctx context.Context, subnet ip.IP4Net) error {
	return ErrUnimplemented
}

func (ksm *kubeSubnetManager) ListReservations(ctx context.Context) ([]subnet.Reservation, error) {
	return nil, ErrUnimplemented
}
<commit_msg>Allow more events to be buffered before the backend starts<commit_after>// Copyright 2016 flannel authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kube

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"

	"github.com/coreos/flannel/pkg/ip"
	"github.com/coreos/flannel/subnet"

	"github.com/golang/glog"
	"golang.org/x/net/context"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	clientset "k8s.io/client-go/kubernetes"
	listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
)

var (
	ErrUnimplemented = errors.New("unimplemented")
)

const (
	resyncPeriod              = 5 * time.Minute
	nodeControllerSyncTimeout = 10 * time.Minute

	subnetKubeManagedAnnotation = "flannel.alpha.coreos.com/kube-subnet-manager"
	backendDataAnnotation       = "flannel.alpha.coreos.com/backend-data"
	backendTypeAnnotation       = "flannel.alpha.coreos.com/backend-type"
	backendPublicIPAnnotation   = "flannel.alpha.coreos.com/public-ip"

	netConfPath = "/etc/kube-flannel/net-conf.json"
)

type kubeSubnetManager struct {
	client         clientset.Interface
	nodeName       string
	nodeStore      listers.NodeLister
	nodeController cache.Controller
	subnetConf     *subnet.Config
	events         chan subnet.Event
}

func NewSubnetManager() (subnet.Manager, error) {
	cfg, err := rest.InClusterConfig()
	if err != nil {
		return nil, fmt.Errorf("unable to initialize inclusterconfig: %v", err)
	}
	c, err := clientset.NewForConfig(cfg)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize client: %v", err)
	}

	podName := os.Getenv("POD_NAME")
	podNamespace := os.Getenv("POD_NAMESPACE")
	if podName == "" || podNamespace == "" {
		return nil, fmt.Errorf("env variables POD_NAME and POD_NAMESPACE must be set")
	}

	pod, err := c.Pods(podNamespace).Get(podName, metav1.GetOptions{})
	if err != nil {
		return nil, fmt.Errorf("error retrieving pod spec for '%s/%s': %v", podNamespace, podName, err)
	}
	nodeName := pod.Spec.NodeName
	if nodeName == "" {
		return nil, fmt.Errorf("node name not present in pod spec '%s/%s'", podNamespace, podName)
	}

	netConf, err := ioutil.ReadFile(netConfPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read net conf: %v", err)
	}

	sc, err := subnet.ParseConfig(string(netConf))
	if err != nil {
		return nil, fmt.Errorf("error parsing subnet config: %s", err)
	}

	sm, err := newKubeSubnetManager(c, sc, nodeName)
	if err != nil {
		return nil, fmt.Errorf("error creating network manager: %s", err)
	}
	go sm.Run(context.Background())

	glog.Infof("Waiting %s for node controller to sync", nodeControllerSyncTimeout)
	err = wait.Poll(time.Second, nodeControllerSyncTimeout, func() (bool, error) {
		return sm.nodeController.HasSynced(), nil
	})
	if err != nil {
		return nil, fmt.Errorf("error waiting for nodeController to sync state: %v", err)
	}
	glog.Infof("Node controller sync successful")

	return sm, nil
}

func newKubeSubnetManager(c clientset.Interface, sc *subnet.Config, nodeName string) (*kubeSubnetManager, error) {
	var ksm kubeSubnetManager
	ksm.client = c
	ksm.nodeName = nodeName
	ksm.subnetConf = sc
	ksm.events = make(chan subnet.Event, 5000)
	indexer, controller := cache.NewIndexerInformer(
		&cache.ListWatch{
			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
				return ksm.client.CoreV1().Nodes().List(options)
			},
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				return ksm.client.CoreV1().Nodes().Watch(options)
			},
		},
		&v1.Node{},
		resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				ksm.handleAddLeaseEvent(subnet.EventAdded, obj)
			},
			UpdateFunc: ksm.handleUpdateLeaseEvent,
			DeleteFunc: func(obj interface{}) {
				ksm.handleAddLeaseEvent(subnet.EventRemoved, obj)
			},
		},
		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
	)
	ksm.nodeController = controller
	ksm.nodeStore = listers.NewNodeLister(indexer)
	return &ksm, nil
}

func (ksm *kubeSubnetManager) handleAddLeaseEvent(et subnet.EventType, obj interface{}) {
	n := obj.(*v1.Node)
	if s, ok := n.Annotations[subnetKubeManagedAnnotation]; !ok || s != "true" {
		return
	}

	l, err := nodeToLease(*n)
	if err != nil {
		glog.Infof("Error turning node %q to lease: %v", n.ObjectMeta.Name, err)
		return
	}
	ksm.events <- subnet.Event{et, l}
}

func (ksm *kubeSubnetManager) handleUpdateLeaseEvent(oldObj, newObj interface{}) {
	o := oldObj.(*v1.Node)
	n := newObj.(*v1.Node)
	if s, ok := n.Annotations[subnetKubeManagedAnnotation]; !ok || s != "true" {
		return
	}
	if o.Annotations[backendDataAnnotation] == n.Annotations[backendDataAnnotation] &&
		o.Annotations[backendTypeAnnotation] == n.Annotations[backendTypeAnnotation] &&
		o.Annotations[backendPublicIPAnnotation] == n.Annotations[backendPublicIPAnnotation] {
		return // No change to lease
	}

	l, err := nodeToLease(*n)
	if err != nil {
		glog.Infof("Error turning node %q to lease: %v", n.ObjectMeta.Name, err)
		return
	}
	ksm.events <- subnet.Event{subnet.EventAdded, l}
}

func (ksm *kubeSubnetManager) GetNetworkConfig(ctx context.Context) (*subnet.Config, error) {
	return ksm.subnetConf, nil
}

func (ksm *kubeSubnetManager) AcquireLease(ctx context.Context, attrs *subnet.LeaseAttrs) (*subnet.Lease, error) {
	cachedNode, err := ksm.nodeStore.Get(ksm.nodeName)
	if err != nil {
		return nil, err
	}
	nobj, err := api.Scheme.DeepCopy(cachedNode)
	if err != nil {
		return nil, err
	}
	n := nobj.(*v1.Node)

	if n.Spec.PodCIDR == "" {
		return nil, fmt.Errorf("node %q pod cidr not assigned", ksm.nodeName)
	}
	bd, err := attrs.BackendData.MarshalJSON()
	if err != nil {
		return nil, err
	}
	_, cidr, err := net.ParseCIDR(n.Spec.PodCIDR)
	if err != nil {
		return nil, err
	}
	if n.Annotations[backendDataAnnotation] != string(bd) ||
		n.Annotations[backendTypeAnnotation] != attrs.BackendType ||
		n.Annotations[backendPublicIPAnnotation] != attrs.PublicIP.String() ||
		n.Annotations[subnetKubeManagedAnnotation] != "true" {
		n.Annotations[backendTypeAnnotation] = attrs.BackendType
		n.Annotations[backendDataAnnotation] = string(bd)
		n.Annotations[backendPublicIPAnnotation] = attrs.PublicIP.String()
		n.Annotations[subnetKubeManagedAnnotation] = "true"

		oldData, err := json.Marshal(cachedNode)
		if err != nil {
			return nil, err
		}

		newData, err := json.Marshal(n)
		if err != nil {
			return nil, err
		}

		patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{})
		if err != nil {
			return nil, fmt.Errorf("failed to create patch for node %q: %v", ksm.nodeName, err)
		}

		_, err = ksm.client.CoreV1().Nodes().Patch(ksm.nodeName, types.StrategicMergePatchType, patchBytes, "status")
		if err != nil {
			return nil, err
		}
	}
	return &subnet.Lease{
		Subnet:     ip.FromIPNet(cidr),
		Attrs:      *attrs,
		Expiration: time.Now().Add(24 * time.Hour),
	}, nil
}

func (ksm *kubeSubnetManager) WatchLeases(ctx context.Context, cursor interface{}) (subnet.LeaseWatchResult, error) {
	select {
	case event := <-ksm.events:
		return subnet.LeaseWatchResult{
			Events: []subnet.Event{event},
		}, nil
	case <-ctx.Done():
		return subnet.LeaseWatchResult{}, nil
	}
}

func (ksm *kubeSubnetManager) Run(ctx context.Context) {
	glog.Infof("Starting kube subnet manager")
	ksm.nodeController.Run(ctx.Done())
}

func nodeToLease(n v1.Node) (l subnet.Lease, err error) {
	l.Attrs.PublicIP, err = ip.ParseIP4(n.Annotations[backendPublicIPAnnotation])
	if err != nil {
		return l, err
	}

	l.Attrs.BackendType = n.Annotations[backendTypeAnnotation]
	l.Attrs.BackendData = json.RawMessage(n.Annotations[backendDataAnnotation])

	_, cidr, err := net.ParseCIDR(n.Spec.PodCIDR)
	if err != nil {
		return l, err
	}

	l.Subnet = ip.FromIPNet(cidr)
	return l, nil
}

// unimplemented
func (ksm *kubeSubnetManager) RenewLease(ctx context.Context, lease *subnet.Lease) error {
	return ErrUnimplemented
}

func (ksm *kubeSubnetManager) WatchLease(ctx context.Context, sn ip.IP4Net, cursor interface{}) (subnet.LeaseWatchResult, error) {
	return subnet.LeaseWatchResult{}, ErrUnimplemented
}

func (ksm *kubeSubnetManager) RevokeLease(ctx context.Context, sn ip.IP4Net) error {
	return ErrUnimplemented
}

func (ksm *kubeSubnetManager) AddReservation(ctx context.Context, r *subnet.Reservation) error {
	return ErrUnimplemented
}

func (ksm *kubeSubnetManager) RemoveReservation(ctx context.Context, subnet ip.IP4Net) error {
	return ErrUnimplemented
}

func (ksm *kubeSubnetManager) ListReservations(ctx context.Context) ([]subnet.Reservation, error) {
	return nil, ErrUnimplemented
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"time"
)

const (
	slackFolderName = ".slack"
	nowFormat       = "2006-01-02 15:04:05"
)

type SlackWorkflow struct {
	User      SlackUser
	Channel   SlackChannel
	Responses chan SlackMessage
	Quit      chan struct{}
}

func (wf SlackWorkflow) SendError(err error) {
	fmt.Println("Notifying", wf.User.Name, "error:", err.Error())
	wf.Responses <- SlackMessage{Text: "Error:" + err.Error()}
}

func (wf SlackWorkflow) Request(authReq chan Auth) {
	defer close(wf.Responses)
	var auth Auth
	select {
	case <-wf.Quit:
		return
	case auth = <-authReq:
	}
	sf := auth.Login
	if auth.Redirect != nil {
		close(auth.Redirect)
	}
	folder, share, err := SetupRequestShare(sf, wf.FolderName())
	if err != nil {
		wf.SendError(err)
		return
	}
	msg := SlackMessage{
		Text:         fmt.Sprintf("%v is requesting files: %v", wf.User.Name, share.Uri),
		ResponseType: "in_channel"}
	wf.Responses <- msg
	poller := sf.FolderPoller(folder.Id)
	go poller.PollForRequest()
	defer close(poller.Quit)
	for {
		select {
		case <-wf.Quit:
			return
		case newItems, ok := <-poller.NewItems:
			if !ok {
				return
			}
			var files []SfFile
			for _, item := range newItems {
				if file, err := item.File(); err == nil {
					files = append(files, file)
				}
			}
			if len(files) == 0 {
				continue
			}
			sendShare, err := sf.CreateSendShare(files)
			if err != nil {
				wf.SendError(err)
				continue
			}
			wf.Responses <- sendShare.BuildRequestNotification(files)
		}
	}
}

func (share SfShare) BuildRequestNotification(files []SfFile) SlackMessage {
	var msg SlackMessage
	if len(files) == 1 {
		msg.Text = fmt.Sprintf("Received %v: %v", files[0].FileName, share.DownloadAllUrl())
	} else {
		msg.Text = fmt.Sprintf("Received %v files: %v", len(files), share.DownloadAllUrl())
		var fileNames []string
		for _, file := range files {
			fileNames = append(fileNames, file.FileName)
		}
		msg.Attachments = []SlackAttachment{
			SlackAttachment{
				Text:     strings.Join(fileNames, "\n"),
				Fallback: strings.Join(fileNames, " "),
			},
		}
	}
	return msg
}

func (wf SlackWorkflow) Send(authReq chan Auth) {
	defer close(wf.Responses)
	var auth Auth
	select {
	case <-wf.Quit:
		return
	case auth = <-authReq:
	}
	sf := auth.Login
	folder, share, err := SetupRequestShare(sf, wf.FolderName())
	if err != nil {
		wf.SendError(err)
		return
	}
	if auth.Redirect == nil {
		wf.Responses <- SlackMessage{Text: fmt.Sprintf("Upload your files: %v", share.Uri)}
	} else {
		auth.Redirect <- share.Uri
		close(auth.Redirect)
	}
	poller := sf.FolderPoller(folder.Id)
	go poller.PollForSend()
	defer close(poller.Quit)
	for {
		select {
		case <-wf.Quit:
			return
		case newItems, ok := <-poller.NewItems:
			if !ok {
				return
			}
			var files []SfFile
			for _, item := range newItems {
				if file, err := item.File(); err == nil {
					files = append(files, file)
				}
			}
			if len(files) == 0 {
				continue
			}
			sendShare, err := sf.CreateSendShare(files)
			if err != nil {
				wf.SendError(err)
				return
			}
			wf.Responses <- sendShare.BuildSendNotification(files, wf.User)
			return
		}
	}
}

func (share SfShare) BuildSendNotification(files []SfFile, slackUser SlackUser) SlackMessage {
	msg := SlackMessage{ResponseType: "in_channel"}
	if len(files) == 1 {
		msg.Text = fmt.Sprintf("%v has shared %v: %v", slackUser.Name, files[0].FileName, share.DownloadAllUrl())
	} else {
		msg.Text = fmt.Sprintf("%v has shared %v files: %v", slackUser.Name, len(files), share.DownloadAllUrl())
		var fileNames []string
		for _, file := range files {
			fileNames = append(fileNames, file.FileName)
		}
		msg.Attachments = []SlackAttachment{
			SlackAttachment{
				Text:     strings.Join(fileNames, "\n"),
				Fallback: strings.Join(fileNames, " "),
			},
		}
	}
	return msg
}

func (wf SlackWorkflow) FolderName() string {
	requestTime := time.Now().Format(nowFormat)
	return wf.Channel.Name + " " + requestTime
}

func SetupRequestShare(sf SfLogin, folderName string) (SfFolder, SfShare, error) {
	slackFolder, err := sf.FindOrCreateSlackFolder()
	if err != nil {
		return SfFolder{}, SfShare{}, err
	}
	shareFolder, err := sf.CreateFolder(folderName, slackFolder.Id)
	if err != nil {
		return SfFolder{}, SfShare{}, err
	}
	share, err := sf.CreateRequestShare(shareFolder.Id)
	if err != nil {
		// cleanup folder?
		return SfFolder{}, SfShare{}, err
	}

	return shareFolder, share, nil
}

func (sf SfLogin) FindOrCreateSlackFolder() (SfFolder, error) {
	home, err := sf.GetChildren("home")
	if err != nil {
		return SfFolder{}, err
	}
	for _, item := range home {
		if item.FileName == slackFolderName {
			folder, err := item.Folder()
			if err != nil {
				return SfFolder{}, err
			}
			return folder, nil
		}
	}
	return sf.CreateFolder(slackFolderName, "home")
}
<commit_msg>print users<commit_after>package main

import (
	"fmt"
	"strings"
	"time"
)

const (
	slackFolderName = ".slack"
	nowFormat       = "2006-01-02 15:04:05"
)

type SlackWorkflow struct {
	User      SlackUser
	Channel   SlackChannel
	Responses chan SlackMessage
	Quit      chan struct{}
}

func (wf SlackWorkflow) SendError(err error) {
	fmt.Println("Notifying", wf.User.Name, "error:", err.Error())
	wf.Responses <- SlackMessage{Text: "Error:" + err.Error()}
}

func (wf SlackWorkflow) Request(authReq chan Auth) {
	defer close(wf.Responses)
	var auth Auth
	select {
	case <-wf.Quit:
		return
	case auth = <-authReq:
	}
	sf := auth.Login
	if auth.Redirect != nil {
		close(auth.Redirect)
	}
	folder, share, err := SetupRequestShare(sf, wf.FolderName())
	if err != nil {
		wf.SendError(err)
		return
	}
	msg := SlackMessage{
		Text:         fmt.Sprintf("%v is requesting files: %v", wf.User.Name, share.Uri),
		ResponseType: "in_channel"}
	wf.Responses <- msg
	fmt.Printf("%v %v request", time.Now(), wf.User.Name)
	poller := sf.FolderPoller(folder.Id)
	go poller.PollForRequest()
	defer close(poller.Quit)
	for {
		select {
		case <-wf.Quit:
			return
		case newItems, ok := <-poller.NewItems:
			if !ok {
				return
			}
			var files []SfFile
			for _, item := range newItems {
				if file, err := item.File(); err == nil {
					files = append(files, file)
				}
			}
			if len(files) == 0 {
				continue
			}
			sendShare, err := sf.CreateSendShare(files)
			if err != nil {
				wf.SendError(err)
				continue
			}
			wf.Responses <- sendShare.BuildRequestNotification(files)
		}
	}
}

func (share SfShare) BuildRequestNotification(files []SfFile) SlackMessage {
	var msg SlackMessage
	if len(files) == 1 {
		msg.Text = fmt.Sprintf("Received %v: %v", files[0].FileName, share.DownloadAllUrl())
	} else {
		msg.Text = fmt.Sprintf("Received %v files: %v", len(files), share.DownloadAllUrl())
		var fileNames []string
		for _, file := range files {
			fileNames = append(fileNames, file.FileName)
		}
		msg.Attachments = []SlackAttachment{
			SlackAttachment{
				Text:     strings.Join(fileNames, "\n"),
				Fallback: strings.Join(fileNames, " "),
			},
		}
	}
	return msg
}

func (wf SlackWorkflow) Send(authReq chan Auth) {
	defer close(wf.Responses)
	var auth Auth
	select {
	case <-wf.Quit:
		return
	case auth = <-authReq:
	}
	sf := auth.Login
	folder, share, err := SetupRequestShare(sf, wf.FolderName())
	if err != nil {
		wf.SendError(err)
		return
	}
	if auth.Redirect == nil {
		wf.Responses <- SlackMessage{Text: fmt.Sprintf("Upload your files: %v", share.Uri)}
	} else {
		auth.Redirect <- share.Uri
		close(auth.Redirect)
	}
	fmt.Printf("%v %v send", time.Now(), wf.User.Name)
	poller := sf.FolderPoller(folder.Id)
	go poller.PollForSend()
	defer close(poller.Quit)
	for {
		select {
		case <-wf.Quit:
			return
		case newItems, ok := <-poller.NewItems:
			if !ok {
				return
			}
			var files []SfFile
			for _, item := range newItems {
				if file, err := item.File(); err == nil {
					files = append(files, file)
				}
			}
			if len(files) == 0 {
				continue
			}
			sendShare, err := sf.CreateSendShare(files)
			if err != nil {
				wf.SendError(err)
				return
			}
			wf.Responses <- sendShare.BuildSendNotification(files, wf.User)
			return
		}
	}
}

func (share SfShare) BuildSendNotification(files []SfFile, slackUser SlackUser) SlackMessage {
	msg := SlackMessage{ResponseType: "in_channel"}
	if len(files) == 1 {
		msg.Text = fmt.Sprintf("%v has shared %v: %v", slackUser.Name, files[0].FileName, share.DownloadAllUrl())
	} else {
		msg.Text = fmt.Sprintf("%v has shared %v files: %v", slackUser.Name, len(files), share.DownloadAllUrl())
		var fileNames []string
		for _, file := range files {
			fileNames = append(fileNames, file.FileName)
		}
		msg.Attachments = []SlackAttachment{
			SlackAttachment{
				Text:     strings.Join(fileNames, "\n"),
				Fallback: strings.Join(fileNames, " "),
			},
		}
	}
	return msg
}

func (wf SlackWorkflow) FolderName() string {
	requestTime := time.Now().Format(nowFormat)
	return wf.Channel.Name + " " + requestTime
}

func SetupRequestShare(sf SfLogin, folderName string) (SfFolder, SfShare, error) {
	slackFolder, err := sf.FindOrCreateSlackFolder()
	if err != nil {
		return SfFolder{}, SfShare{}, err
	}
	shareFolder, err := sf.CreateFolder(folderName, slackFolder.Id)
	if err != nil {
		return SfFolder{}, SfShare{}, err
	}
	share, err := sf.CreateRequestShare(shareFolder.Id)
	if err != nil {
		// cleanup folder?
		return SfFolder{}, SfShare{}, err
	}

	return shareFolder, share, nil
}

func (sf SfLogin) FindOrCreateSlackFolder() (SfFolder, error) {
	home, err := sf.GetChildren("home")
	if err != nil {
		return SfFolder{}, err
	}
	for _, item := range home {
		if item.FileName == slackFolderName {
			folder, err := item.Folder()
			if err != nil {
				return SfFolder{}, err
			}
			return folder, nil
		}
	}
	return sf.CreateFolder(slackFolderName, "home")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/abiosoft/ishell"
	"github.com/olekukonko/tablewriter"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"time"
)

type Task struct {
	Id                 int
	Name               string
	Status             string
	TotalTimeInSeconds int
}

type TaskTimer struct {
	Timer     *time.Timer
	StartedAt time.Time
	Task      *Task
}

var (
	InvalidArgumentList                      = errors.New("invalid argument list")
	InvalidNumericArgument                   = errors.New("invalid numeric argument")
	tasksFilepath          string            = "/Users/dlt/golang/src/SelfControl/tasks.json"
	timers                 map[int]TaskTimer = make(map[int]TaskTimer)
	tasks                  []Task
	lastId                 int
)

func main() {
	shell := ishell.New()
	shell.Println("The greatest conquest is self–control")

	shell.Register("list", listTasks)
	shell.Register("add", addTask)
	shell.Register("delete", deleteTask)
	shell.Register("start", startTask)
	shell.Register("status", setTaskStatus)

	shell.Start()
}

// Print all tasks in a ASCII table
func listTasks(args ...string) (string, error) {
	readDatabaseFile()
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"id", "name", "status", "time", "ellapsed time"})

	for _, row := range createRows() {
		table.Append(row)
	}
	table.Render()

	return "", nil
}

func setTaskStatus(args ...string) (string, error) {
	if len(args) != 2 {
		return "", InvalidArgumentList
	}
	id, err := strconv.Atoi(args[0])
	if err != nil {
		return args[0], InvalidNumericArgument
	}
	status := args[1]
	task := findTaskWithId(id)
	task.setStatus(status)
	return "", nil
}

// Creates a new task given name and status
func addTask(args ...string) (string, error) {
	if len(args) != 1 {
		return "", InvalidArgumentList
	}
	name := args[0]
	saveTask(name)
	return "task created", nil
}

// Deletes a task with given id
func deleteTask(args ...string) (string, error) {
	if len(args) != 1 {
		return "", InvalidArgumentList
	}
	id, err := strconv.Atoi(args[0])
	if err != nil {
		return args[0], InvalidNumericArgument
	}
	if tasks == nil {
		readDatabaseFile()
	}
	removeTaskWithId(id)
	persist()
	listTasks("")
	return "", nil
}

// Starts a timer for a given task id
func startTask(args ...string) (string, error) {
	if len(args) != 2 {
		return "", InvalidArgumentList
	}
	id, err := strconv.Atoi(args[0])
	if err != nil {
		return args[0], InvalidNumericArgument
	}

	timeInMinutes, err := time.ParseDuration(args[1] + "s")
	if err != nil {
		return args[1], InvalidNumericArgument
	}
	task := findTaskWithId(id)
	task.setStatus("DOING")
	task.startTimerForTask(timeInMinutes)
	return "", nil
}

func (t *Task) setStatus(status string) {
	t.Status = status
	removeTaskWithId(t.Id)
	tasks = append(tasks, *t)
	persist()
}

func (t *Task) startTimerForTask(d time.Duration) {
	timer := time.NewTimer(d)
	timers[t.Id] = TaskTimer{
		Task:      t,
		Timer:     timer,
		StartedAt: time.Now(),
	}

	go func() {
		<-timer.C
		fmt.Println("Timer expired")
	}()
}

func removeTaskWithId(id int) {
	var newTasks []Task
	for _, t := range tasks {
		if t.Id != id {
			newTasks = append(newTasks, t)
		}
	}
	tasks = newTasks
}

func findTaskWithId(id int) *Task {
	for _, t := range getTasks() {
		if t.Id == id {
			return &t
		}
	}
	return nil
}

func getTasks() []Task {
	if isDatabaseFileEmpty() {
		return make([]Task, 0)
	} else {
		readDatabaseFile()
		return tasks
	}
}

func createRows() [][]string {
	var rows = [][]string{}

	for _, t := range getTasks() {
		row := []string{
			strconv.Itoa(t.Id),
			t.Name,
			t.Status,
			strconv.Itoa(t.TotalTimeInSeconds),
			ellapsedTime(t),
		}
		rows = append(rows, row)
	}

	return rows
}

func ellapsedTime(t Task) string {
	timer := timers[t.Id]
	minutes := timer.StartedAt.Sub(time.Now()).Minutes()
	if t.Status != "DOING" {
		return "-"
	}
	return strconv.FormatFloat(minutes, 'E', -1, 32)
}

func readDatabaseFile() {
	raw, err := ioutil.ReadFile(tasksFilepath)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	json.Unmarshal(raw, &tasks)
}

func saveTask(name string) {
	task := Task{
		Id:                 0,
		Name:               name,
		Status:             "TODO",
		TotalTimeInSeconds: 0,
	}

	tasks = getTasks()
	task.Id = maxId(tasks) + 1

	tasks = append(tasks, task)
	persist()
}

func persist() {
	writeToDatabaseFile(toJSON(tasks))
}

func maxId(tasks []Task) int {
	id := 0

	for _, t := range tasks {
		if t.Id > id {
			id = t.Id
		}
	}
	return id
}

func writeToDatabaseFile(jsonString []byte) {
	f, err := os.Create(tasksFilepath)
	defer f.Close()

	if err != nil {
		fmt.Println("error:", err)
	}

	_, err = f.Write(jsonString)
	if err != nil {
		fmt.Println("error:", err)
	}
}

func toJSON(tasks []Task) []byte {
	j, err := json.Marshal(tasks)
	if err != nil {
		fmt.Println("error:", err)
	}
	return j
}

func isDatabaseFileEmpty() bool {
	file, err := os.Open(tasksFilepath)

	if err != nil {
		log.Fatal(err)
	}
	f, err := file.Stat()
	if err != nil {
		log.Fatal(err)
	}
	return f.Size() == 0
}
<commit_msg>Refactored after linting code<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/abiosoft/ishell"
	"github.com/deckarep/gosx-notifier"
	"github.com/olekukonko/tablewriter"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"time"
)

// Task struct
type Task struct {
	ID                 int
	Name               string
	Status             string
	TotalTimeInSeconds int
}

type taskTimer struct {
	Timer     *time.Timer
	StartedAt time.Time
	Task      *Task
}

var (
	errInvalidArgumentList    = errors.New("invalid argument list")
	errInvalidNumericArgument = errors.New("invalid numeric argument")
	tasksFilepath             = "/Users/dlt/golang/src/SelfControl/tasks.json"
	timers                    = make(map[int]taskTimer)
	tasks                     []Task
	lastID                    int
)

func main() {
	shell := ishell.New()
	shell.Println("The greatest conquest is self–control")

	shell.Register("list", listTasks)
	shell.Register("add", addTask)
	shell.Register("delete", deleteTask)
	shell.Register("start", startTask)
	shell.Register("status", setTaskStatus)

	shell.Start()
}

// Print all tasks in a ASCII table
func listTasks(args ...string) (string, error) {
	readDatabaseFile()
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"id", "name", "status", "time", "ellapsed time"})

	for _, row := range createRows() {
		table.Append(row)
	}
	table.Render()

	return "", nil
}

func setTaskStatus(args ...string) (string, error) {
	if len(args) != 2 {
		return "", errInvalidArgumentList
	}
	id, err := strconv.Atoi(args[0])
	if err != nil {
		return args[0], errInvalidNumericArgument
	}
	status := args[1]
	task := findTaskWithID(id)
	task.setStatus(status)
	return "", nil
}

// Creates a new task given name and status
func addTask(args ...string) (string, error) {
	if len(args) != 1 {
		return "", errInvalidArgumentList
	}
	name := args[0]
	saveTask(name)
	return "task created", nil
}

// Deletes a task with given id
func deleteTask(args ...string) (string, error) {
	if len(args) != 1 {
		return "", errInvalidArgumentList
	}
	id, err := strconv.Atoi(args[0])
	if err != nil {
		return args[0], errInvalidNumericArgument
	}
	if tasks == nil {
		readDatabaseFile()
	}
	removeTaskWithID(id)
	persist()
	listTasks("")
	return "", nil
}

// Starts a timer for a given task id
func startTask(args ...string) (string, error) {
	if len(args) != 2 {
		return "", errInvalidArgumentList
	}
	id, err := strconv.Atoi(args[0])
	if err != nil {
		return args[0], errInvalidNumericArgument
	}

	timeInMinutes, err := time.ParseDuration(args[1] + "s")
	if err != nil {
		return args[1], errInvalidNumericArgument
	}
	task := findTaskWithID(id)
	task.setStatus("DOING")
	task.startTimerForTask(timeInMinutes)
	return "", nil
}

func (t *Task) setStatus(status string) {
	t.Status = status
	removeTaskWithID(t.ID)
	tasks = append(tasks, *t)
	persist()
}

func (t *Task) startTimerForTask(d time.Duration) {
	timer := time.NewTimer(d)
	timers[t.ID] = taskTimer{
		Task:      t,
		Timer:     timer,
		StartedAt: time.Now(),
	}
	message := "Timer for '" + t.Name + "' finished!"
	go func() {
		<-timer.C
		pushNotification(message)
	}()
}

func removeTaskWithID(id int) {
	var newTasks []Task
	for _, t := range tasks {
		if t.ID != id {
			newTasks = append(newTasks, t)
		}
	}
	tasks = newTasks
}

func findTaskWithID(id int) *Task {
	for _, t := range getTasks() {
		if t.ID == id {
			return &t
		}
	}
	return nil
}

func getTasks() []Task {
	if isDatabaseFileEmpty() {
		return make([]Task, 0)
	}
	readDatabaseFile()
	return tasks
}

func createRows() [][]string {
	var rows = [][]string{}

	for _, t := range getTasks() {
		row := []string{
			strconv.Itoa(t.ID),
			t.Name,
			t.Status,
			strconv.Itoa(t.TotalTimeInSeconds),
			ellapsedTime(t),
		}
		rows = append(rows, row)
	}

	return rows
}

func ellapsedTime(t Task) string {
	timer := timers[t.ID]
	minutes := timer.StartedAt.Sub(time.Now()).Minutes()
	if t.Status != "DOING" {
		return "-"
	}
	return strconv.FormatFloat(minutes, 'E', -1, 32)
}

func readDatabaseFile() {
	raw, err := ioutil.ReadFile(tasksFilepath)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	json.Unmarshal(raw, &tasks)
}

func saveTask(name string) {
	task := Task{
		ID:                 0,
		Name:               name,
		Status:             "TODO",
		TotalTimeInSeconds: 0,
	}

	tasks = getTasks()
	task.ID = maxID(tasks) + 1

	tasks = append(tasks, task)
	persist()
}

func persist() {
	writeToDatabaseFile(toJSON(tasks))
}

func maxID(tasks []Task) int {
	id := 0

	for _, t := range tasks {
		if t.ID > id {
			id = t.ID
		}
	}
	return id
}

func writeToDatabaseFile(jsonString []byte) {
	f, err := os.Create(tasksFilepath)
	defer f.Close()

	if err != nil {
		fmt.Println("error:", err)
	}

	_, err = f.Write(jsonString)
	if err != nil {
		fmt.Println("error:", err)
	}
}

func toJSON(tasks []Task) []byte {
	j, err := json.Marshal(tasks)
	if err != nil {
		fmt.Println("error:", err)
	}
	return j
}

func isDatabaseFileEmpty() bool {
	file, err := os.Open(tasksFilepath)

	if err != nil {
		log.Fatal(err)
	}
	f, err := file.Stat()
	if err != nil {
		log.Fatal(err)
	}
	return f.Size() == 0
}

func pushNotification(message string) {
	gosxnotifier.NewNotification(message).Push()
}
<|endoftext|>