<commit_before>package perpetua

import (
	"crypto/tls"
	"fmt"
	"log"
	"regexp"

	"github.com/thoj/go-ircevent"
)

const version = "perpetua quote bot v0.1a"

var connection *irc.Connection

func connect() {
	connection = irc.IRC(options.IRC.Nickname, options.IRC.User)
	connection.Version = version
	connection.UseTLS = options.Server.UseTLS
	if options.Server.SkipVerify == true {
		connection.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	}

	err := connection.Connect(fmt.Sprintf("%s:%d",
		options.Server.Hostname,
		options.Server.Port))

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

func handleEvents() {
	connection.AddCallback("001", doWelcome)
	connection.AddCallback("JOIN", doJoin)
	connection.AddCallback("PRIVMSG", doPrivmsg)
}

func doWelcome(event *irc.Event) {
	connection.Join("#" + options.IRC.Channel)
}

func doJoin(event *irc.Event) {
	connection.Privmsg(event.Arguments[0], "Hello! I'm "+version)
}

func doPrivmsg(event *irc.Event) {
	channel := event.Arguments[0]

	// Don't speak in private!
	if channel == options.IRC.Nickname {
		return
	}
	command, person := parseMessage(event.Message())

	if command != "" && person != "" {

		fmt.Println(person)
	}
}

func parseMessage(message string) (command, person string) {

	re := regexp.MustCompile(options.IRC.Nickname +
		`:?` +
		`\s*` +
		`(?P<command>cita|cosa dice|quote|what does it say)` +
		`\s*(?P<person>[\w\s]+)`)

	res := re.FindStringSubmatch(message)

	names := re.SubexpNames()
	md := map[string]string{}
	for i, n := range res {
		md[names[i]] = n
	}

	return md["command"], md["person"]
}

func startIRC() {
	connect()
	handleEvents()
	connection.Loop()
}
<commit_msg>Send random quote to channel<commit_after>package perpetua

import (
	"crypto/tls"
	"fmt"
	"log"
	"regexp"

	"github.com/thoj/go-ircevent"
)

const version = "perpetua quote bot v0.1a"

var connection *irc.Connection

func connect() {
	connection = irc.IRC(options.IRC.Nickname, options.IRC.User)
	connection.Version = version
	connection.UseTLS = options.Server.UseTLS
	if options.Server.SkipVerify == true {
		connection.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	}

	err := connection.Connect(fmt.Sprintf("%s:%d",
		options.Server.Hostname,
		options.Server.Port))

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

func handleEvents() {
	connection.AddCallback("001", doWelcome)
	connection.AddCallback("JOIN", doJoin)
	connection.AddCallback("PRIVMSG", doPrivmsg)
}

func doWelcome(event *irc.Event) {
	connection.Join("#" + options.IRC.Channel)
}

func doJoin(event *irc.Event) {
	connection.Privmsg(event.Arguments[0], "Hello! I'm "+version)
}

func doPrivmsg(event *irc.Event) {
	channel := event.Arguments[0]

	// Don't speak in private!
	if channel == options.IRC.Nickname {
		return
	}
	command, person := parseMessage(event.Message())

	if command != "" && person != "" {
		connection.Privmsg(event.Arguments[0], store.getQuote(person))
	}
}

func parseMessage(message string) (command, person string) {

	re := regexp.MustCompile(options.IRC.Nickname +
		`:?` +
		`\s*` +
		`(?P<command>cita|cosa dice|quote|what does it say)` +
		`\s*(?P<person>[\w\s]+)`)

	res := re.FindStringSubmatch(message)

	names := re.SubexpNames()
	md := map[string]string{}
	for i, n := range res {
		md[names[i]] = n
	}

	return md["command"], md["person"]
}

func startIRC() {
	connect()
	handleEvents()
	connection.Loop()
}
<|endoftext|>


<commit_before><commit_msg>Remove hardcode station<commit_after><|endoftext|>


<commit_before><commit_msg>s/newSEPosition/newSEVec2/<commit_after><|endoftext|>


<commit_before><commit_msg>r: line insert<commit_after><|endoftext|>


<commit_before><commit_msg>exclude vendor dir<commit_after><|endoftext|>


<commit_before>// Copyright (C) 2015 Space Monkey, 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 present

import (
	"bufio"
	"io"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/spacemonkeygo/monkit/v3"
)

// Result writes the expected data to io.Writer and returns any errors if
// found.
type Result func(io.Writer) error

func curry(reg *monkit.Registry,
	f func(*monkit.Registry, io.Writer) error) func(io.Writer) error {
	return func(w io.Writer) error {
		return f(reg, w)
	}
}

// FromRequest takes a registry (usually the Default registry), an incoming
// path, and optional query parameters, and returns a Result if possible.
//
// FromRequest understands the following paths:
//  * /ps, /ps/text       - returns the result of SpansText
//  * /ps/dot             - returns the result of SpansDot
//  * /ps/json            - returns the result of SpansJSON
//  * /funcs, /funcs/text - returns the result of FuncsText
//  * /funcs/dot          - returns the result of FuncsDot
//  * /funcs/json         - returns the result of FuncsJSON
//  * /stats, /stats/text - returns the result of StatsText
//  * /stats/json         - returns the result of StatsJSON
//  * /trace/svg          - returns the result of TraceQuerySVG
//  * /trace/json         - returns the result of TraceQueryJSON
//
// The last two paths are worth discussing in more detail, as they take
// query parameters. All trace endpoints require at least one of the following
// two query parameters:
//  * regex    - If provided, the very next Span that crosses a Func that has
//               a name that matches this regex will start a trace until that
//               triggering Span ends, provided the trace_id matches.
//  * trace_id - If provided, the very next Span on a trace with the given
//               trace id will start a trace until the triggering Span ends,
//               provided the regex matches. NOTE: the trace_id will be parsed
//               in hex.
// By default, regular expressions are matched ahead of time against all known
// Funcs, but perhaps the Func you want to trace hasn't been observed by the
// process yet, in which case the regex will fail to match anything. You can
// turn off this preselection behavior by providing preselect=false as an
// additional query param. Be advised that until a trace completes, whether
// or not it has started, it adds a small amount of overhead (a comparison or
// two) to every monitored function.
func FromRequest(reg *monkit.Registry, path string, query url.Values) (
	f Result, contentType string, err error) {

	defer func() {
		if err != nil {
			return
		}
		// wrap all functions with buffering
		unbuffered := f
		f = func(w io.Writer) (err error) {
			buf := bufio.NewWriter(w)
			err = unbuffered(buf)
			if err != nil {
				return err
			}
			err = buf.Flush()
			return err
		}
	}()

	first, rest := shift(path)
	second, _ := shift(rest)
	switch first {
	case "":
		return writeIndex, "text/html", nil
	case "ps":
		switch second {
		case "", "text":
			return curry(reg, SpansText), "text/plain; charset=utf-8", nil
		case "dot":
			return curry(reg, SpansDot), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, SpansJSON), "application/json; charset=utf-8", nil
		}

	case "funcs":
		switch second {
		case "", "text":
			return curry(reg, FuncsText), "text/plain; charset=utf-8", nil
		case "dot":
			return curry(reg, FuncsDot), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, FuncsJSON), "application/json; charset=utf-8", nil
		}

	case "stats":
		switch second {
		case "", "text", "old":
			return func(w io.Writer) error {
				return StatsText(reg, w)
			}, "text/plain; charset=utf-8", nil
		case "json":
			return func(w io.Writer) error {
				return StatsJSON(reg, w)
			}, "application/json; charset=utf-8", nil
		}

	case "trace":
		regexStr := query.Get("regex")
		traceIdStr := query.Get("trace_id")
		if regexStr == "" && traceIdStr == "" {
			return nil, "", errBadRequest.New("at least one of 'regex' or 'trace_id' " +
				"query parameters required")
		}
		fnMatcher := func(*monkit.Func) bool { return true }

		if regexStr != "" {
			re, err := regexp.Compile(regexStr)
			if err != nil {
				return nil, "", errBadRequest.New("invalid regex %#v: %v",
					regexStr, err)
			}
			fnMatcher = func(f *monkit.Func) bool {
				return re.MatchString(f.FullName())
			}

			preselect := true
			if query.Get("preselect") != "" {
				preselect, err = strconv.ParseBool(query.Get("preselect"))
				if err != nil {
					return nil, "", errBadRequest.New("invalid preselect %#v: %v",
						query.Get("preselect"), err)
				}
			}
			if preselect {
				funcs := map[*monkit.Func]bool{}
				reg.Funcs(func(f *monkit.Func) {
					if fnMatcher(f) {
						funcs[f] = true
					}
				})
				if len(funcs) <= 0 {
					return nil, "", errBadRequest.New("regex preselect matches 0 functions")
				}

				fnMatcher = func(f *monkit.Func) bool { return funcs[f] }
			}
		}

		spanMatcher := func(s *monkit.Span) bool { return fnMatcher(s.Func()) }

		if traceIdStr != "" {
			traceId, err := strconv.ParseUint(traceIdStr, 16, 64)
			if err != nil {
				return nil, "", errBadRequest.New(
					"trace_id expected to be hex unsigned 64 bit number: %#v", traceIdStr)
			}
			spanMatcher = func(s *monkit.Span) bool {
				return s.Trace().Id() == int64(traceId) && fnMatcher(s.Func())
			}
		}

		switch second {
		case "svg":
			return func(w io.Writer) error {
				return TraceQuerySVG(reg, w, spanMatcher)
			}, "image/svg+xml; charset=utf-8", nil
		case "json":
			return func(w io.Writer) error {
				return TraceQueryJSON(reg, w, spanMatcher)
			}, "application/json; charset=utf-8", nil
		}
	}
	return nil, "", errNotFound.New("path not found: %s", path)
}

func shift(path string) (dir, left string) {
	path = strings.TrimLeft(path, "/")
	split := strings.Index(path, "/")
	if split == -1 {
		return path, ""
	}
	return path[:split], path[split:]
}

func writeIndex(w io.Writer) error {
	_, err := w.Write([]byte(`<!DOCTYPE html>

		<meta charset="utf-8">
		<title>Monkit</title>
		<meta http-equiv="refresh" content="1">

/ps

/ps/json

/ps/dot

Information about active spans.

/funcs

/funcs/json

/funcs/dot

Information about the functions and their relations.

/stats

/stats/json

/stats/svg

Statistics about all observed functions, scopes and values.

/trace/json

/trace/svg

Trace the next scope that matches one of the ?regex= or ?trace_id= query arguments. By default, regular expressions are matched ahead of time against all known Funcs, but perhaps the Func you want to trace hasn't been observed by the process yet, in which case the regex will fail to match anything. You can turn off this preselection behavior by providing &preselect=false as an additional query param. Be advised that until a trace completes, whether or not it has started, it adds a small amount of overhead (a comparison or two) to every monitored function.

`))
	return err
}
<commit_msg>present: remove auto-refresh<commit_after>// Copyright (C) 2015 Space Monkey, 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 present

import (
	"bufio"
	"io"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/spacemonkeygo/monkit/v3"
)

// Result writes the expected data to io.Writer and returns any errors if
// found.
type Result func(io.Writer) error

func curry(reg *monkit.Registry,
	f func(*monkit.Registry, io.Writer) error) func(io.Writer) error {
	return func(w io.Writer) error {
		return f(reg, w)
	}
}

// FromRequest takes a registry (usually the Default registry), an incoming
// path, and optional query parameters, and returns a Result if possible.
//
// FromRequest understands the following paths:
//  * /ps, /ps/text       - returns the result of SpansText
//  * /ps/dot             - returns the result of SpansDot
//  * /ps/json            - returns the result of SpansJSON
//  * /funcs, /funcs/text - returns the result of FuncsText
//  * /funcs/dot          - returns the result of FuncsDot
//  * /funcs/json         - returns the result of FuncsJSON
//  * /stats, /stats/text - returns the result of StatsText
//  * /stats/json         - returns the result of StatsJSON
//  * /trace/svg          - returns the result of TraceQuerySVG
//  * /trace/json         - returns the result of TraceQueryJSON
//
// The last two paths are worth discussing in more detail, as they take
// query parameters. All trace endpoints require at least one of the following
// two query parameters:
//  * regex    - If provided, the very next Span that crosses a Func that has
//               a name that matches this regex will start a trace until that
//               triggering Span ends, provided the trace_id matches.
//  * trace_id - If provided, the very next Span on a trace with the given
//               trace id will start a trace until the triggering Span ends,
//               provided the regex matches. NOTE: the trace_id will be parsed
//               in hex.
// By default, regular expressions are matched ahead of time against all known
// Funcs, but perhaps the Func you want to trace hasn't been observed by the
// process yet, in which case the regex will fail to match anything. You can
// turn off this preselection behavior by providing preselect=false as an
// additional query param. Be advised that until a trace completes, whether
// or not it has started, it adds a small amount of overhead (a comparison or
// two) to every monitored function.
func FromRequest(reg *monkit.Registry, path string, query url.Values) (
	f Result, contentType string, err error) {

	defer func() {
		if err != nil {
			return
		}
		// wrap all functions with buffering
		unbuffered := f
		f = func(w io.Writer) (err error) {
			buf := bufio.NewWriter(w)
			err = unbuffered(buf)
			if err != nil {
				return err
			}
			err = buf.Flush()
			return err
		}
	}()

	first, rest := shift(path)
	second, _ := shift(rest)
	switch first {
	case "":
		return writeIndex, "text/html", nil
	case "ps":
		switch second {
		case "", "text":
			return curry(reg, SpansText), "text/plain; charset=utf-8", nil
		case "dot":
			return curry(reg, SpansDot), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, SpansJSON), "application/json; charset=utf-8", nil
		}

	case "funcs":
		switch second {
		case "", "text":
			return curry(reg, FuncsText), "text/plain; charset=utf-8", nil
		case "dot":
			return curry(reg, FuncsDot), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, FuncsJSON), "application/json; charset=utf-8", nil
		}

	case "stats":
		switch second {
		case "", "text", "old":
			return func(w io.Writer) error {
				return StatsText(reg, w)
			}, "text/plain; charset=utf-8", nil
		case "json":
			return func(w io.Writer) error {
				return StatsJSON(reg, w)
			}, "application/json; charset=utf-8", nil
		}

	case "trace":
		regexStr := query.Get("regex")
		traceIdStr := query.Get("trace_id")
		if regexStr == "" && traceIdStr == "" {
			return nil, "", errBadRequest.New("at least one of 'regex' or 'trace_id' " +
				"query parameters required")
		}
		fnMatcher := func(*monkit.Func) bool { return true }

		if regexStr != "" {
			re, err := regexp.Compile(regexStr)
			if err != nil {
				return nil, "", errBadRequest.New("invalid regex %#v: %v",
					regexStr, err)
			}
			fnMatcher = func(f *monkit.Func) bool {
				return re.MatchString(f.FullName())
			}

			preselect := true
			if query.Get("preselect") != "" {
				preselect, err = strconv.ParseBool(query.Get("preselect"))
				if err != nil {
					return nil, "", errBadRequest.New("invalid preselect %#v: %v",
						query.Get("preselect"), err)
				}
			}
			if preselect {
				funcs := map[*monkit.Func]bool{}
				reg.Funcs(func(f *monkit.Func) {
					if fnMatcher(f) {
						funcs[f] = true
					}
				})
				if len(funcs) <= 0 {
					return nil, "", errBadRequest.New("regex preselect matches 0 functions")
				}

				fnMatcher = func(f *monkit.Func) bool { return funcs[f] }
			}
		}

		spanMatcher := func(s *monkit.Span) bool { return fnMatcher(s.Func()) }

		if traceIdStr != "" {
			traceId, err := strconv.ParseUint(traceIdStr, 16, 64)
			if err != nil {
				return nil, "", errBadRequest.New(
					"trace_id expected to be hex unsigned 64 bit number: %#v", traceIdStr)
			}
			spanMatcher = func(s *monkit.Span) bool {
				return s.Trace().Id() == int64(traceId) && fnMatcher(s.Func())
			}
		}

		switch second {
		case "svg":
			return func(w io.Writer) error {
				return TraceQuerySVG(reg, w, spanMatcher)
			}, "image/svg+xml; charset=utf-8", nil
		case "json":
			return func(w io.Writer) error {
				return TraceQueryJSON(reg, w, spanMatcher)
			}, "application/json; charset=utf-8", nil
		}
	}
	return nil, "", errNotFound.New("path not found: %s", path)
}

func shift(path string) (dir, left string) {
	path = strings.TrimLeft(path, "/")
	split := strings.Index(path, "/")
	if split == -1 {
		return path, ""
	}
	return path[:split], path[split:]
}

func writeIndex(w io.Writer) error {
	_, err := w.Write([]byte(`<!DOCTYPE html>

		<meta charset="utf-8">
		<title>Monkit</title>

/ps

/ps/json

/ps/dot

Information about active spans.

/funcs

/funcs/json

/funcs/dot

Information about the functions and their relations.

/stats

/stats/json

/stats/svg

Statistics about all observed functions, scopes and values.

/trace/json

/trace/svg

Trace the next scope that matches one of the ?regex= or ?trace_id= query arguments. By default, regular expressions are matched ahead of time against all known Funcs, but perhaps the Func you want to trace hasn't been observed by the process yet, in which case the regex will fail to match anything. You can turn off this preselection behavior by providing &preselect=false as an additional query param. Be advised that until a trace completes, whether or not it has started, it adds a small amount of overhead (a comparison or two) to every monitored function.

`))
	return err
}
<|endoftext|>


<commit_before><commit_msg>Add retry logic to client's Validate method (#150)<commit_after><|endoftext|>


<commit_before><commit_msg>- Refactored client.setNick<commit_after><|endoftext|>


<commit_before><commit_msg>Check status code<commit_after><|endoftext|>


<commit_before>// Copyright 2017 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package integration

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
	"time"

	"gopkg.in/check.v1"
)

var (
	T            = NewCommand("tsuru").WithArgs
	allPlatforms = []string{
		"tsuru/python",
		"tsuru/go",
		"tsuru/buildpack",
		"tsuru/cordova",
		"tsuru/elixir",
		"tsuru/java",
		"tsuru/nodejs",
		"tsuru/php",
		"tsuru/play",
		"tsuru/pypy",
		"tsuru/python3",
		"tsuru/ruby",
		"tsuru/static",
	}
	allProvisioners = []string{
		"docker",
		"swarm",
	}
	flows = []ExecFlow{
		platformsToInstall(),
		installerConfigTest(),
		installerComposeTest(),
		installerTest(),
		targetTest(),
		loginTest(),
		removeInstallNodes(),
		quotaTest(),
		teamTest(),
		poolAdd(),
		platformAdd(),
		exampleApps(),
	}
)

var installerConfig = `driver:
  name: virtualbox
  options:
    virtualbox-cpu-count: 2
    virtualbox-memory: 2048
hosts:
  apps:
    size: 2
components:
    install-dashboard: false
`

func platformsToInstall() ExecFlow {
	flow := ExecFlow{
		provides: []string{"platformimages"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		for _, platImg := range allPlatforms {
			env.Add("platformimages", platImg)
		}
	}
	return flow
}

func installerConfigTest() ExecFlow {
	flow := ExecFlow{
		provides: []string{"installerconfig"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		f, err := ioutil.TempFile("", "installer-config")
		c.Assert(err, check.IsNil)
		defer f.Close()
		f.Write([]byte(installerConfig))
		c.Assert(err, check.IsNil)
		env.Set("installerconfig", f.Name())
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := NewCommand("rm", "{{.installerconfig}}").Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func installerComposeTest() ExecFlow {
	flow := ExecFlow{
		provides: []string{"installercompose"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		composeFile, err := ioutil.TempFile("", "installer-compose")
		c.Assert(err, check.IsNil)
		defer composeFile.Close()
		f, err := ioutil.TempFile("", "installer-config")
		c.Assert(err, check.IsNil)
		defer func() {
			res := NewCommand("rm", f.Name()).Run(env)
			c.Check(res, ResultOk)
			f.Close()
		}()
		res := T("install-config-init", f.Name(), composeFile.Name()).Run(env)
		c.Assert(res, ResultOk)
		composeData, err := ioutil.ReadFile(composeFile.Name())
		c.Assert(err, check.IsNil)
		composeData = []byte(strings.Replace(string(composeData), "tsuru/api:v1", "tsuru/api:latest", 1))
		err = ioutil.WriteFile(composeFile.Name(), composeData, 0644)
		env.Set("installercompose", composeFile.Name())
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := NewCommand("rm", "{{.installercompose}}").Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func installerTest() ExecFlow {
	flow := ExecFlow{
		provides: []string{"targetaddr"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		res := T("install", "--config", "{{.installerconfig}}", "--compose", "{{.installercompose}}").WithTimeout(30 * time.Minute).Run(env)
		c.Assert(res, ResultOk)
		regex := regexp.MustCompile(`(?si).*Core Hosts:.*?([\d.]+)\s.*`)
		parts := regex.FindStringSubmatch(res.Stdout.String())
		c.Assert(parts, check.HasLen, 2)
		targetHost := parts[1]
		regex = regexp.MustCompile(`(?si).*tsuru_tsuru.*?\|\s(\d+)`)
		parts = regex.FindStringSubmatch(res.Stdout.String())
		c.Assert(parts, check.HasLen, 2)
		targetPort := parts[1]
		env.Set("targetaddr", fmt.Sprintf("http://%s:%s", targetHost, targetPort))
		regex = regexp.MustCompile(`\| (https?[^\s]+?) \|`)
		allParts := regex.FindAllStringSubmatch(res.Stdout.String(), -1)
		for _, parts = range allParts {
			c.Assert(parts, check.HasLen, 2)
			env.Add("nodeopts", fmt.Sprintf("--register address=%s --cacert ~/.tsuru/installs/tsuru/certs/ca.pem --clientcert ~/.tsuru/installs/tsuru/certs/cert.pem --clientkey ~/.tsuru/installs/tsuru/certs/key.pem", parts[1]))
			env.Add("nodestoremove", parts[1])
		}
		regex = regexp.MustCompile(`Username: ([[:print:]]+)`)
		parts = regex.FindStringSubmatch(res.Stdout.String())
		env.Set("adminuser", parts[1])
		regex = regexp.MustCompile(`Password: ([[:print:]]+)`)
		parts = regex.FindStringSubmatch(res.Stdout.String())
		env.Set("adminpassword", parts[1])
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := T("uninstall", "-y").Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func targetTest() ExecFlow {
	flow := ExecFlow{}
	flow.forward = func(c *check.C, env *Environment) {
		targetName := "integration-target"
		res := T("target-add", targetName, "{{.targetaddr}}").Run(env)
		c.Assert(res, ResultOk)
		res = T("target-list").Run(env)
		c.Assert(res, ResultMatches, Expected{Stdout: `\s+` + targetName + ` .*`})
		res = T("target-set", targetName).Run(env)
		c.Assert(res, ResultOk)
	}
	return flow
}

func loginTest() ExecFlow {
	flow := ExecFlow{}
	flow.forward = func(c *check.C, env *Environment) {
		res := T("login", "{{.adminuser}}").WithInput("{{.adminpassword}}").Run(env)
		c.Assert(res, ResultOk)
	}
	return flow
}

func removeInstallNodes() ExecFlow {
	flow := ExecFlow{
		matrix: map[string]string{
			"node": "nodestoremove",
		},
	}
	flow.forward = func(c *check.C, env *Environment) {
		res := T("node-remove", "-y", "--no-rebalance", "{{.node}}").Run(env)
		c.Assert(res, ResultOk)
	}
	return flow
}

func quotaTest() ExecFlow {
	flow := ExecFlow{
		requires: []string{"adminuser"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		res := T("user-quota-change", "{{.adminuser}}", "100").Run(env)
		c.Assert(res, ResultOk)
		res = T("user-quota-view", "{{.adminuser}}").Run(env)
		c.Assert(res, ResultMatches, Expected{Stdout: `(?s)Apps usage.*/100`})
	}
	return flow
}

func teamTest() ExecFlow {
	flow := ExecFlow{
		provides: []string{"team"},
	}
	teamName := "integration-team"
	flow.forward = func(c *check.C, env *Environment) {
		res := T("team-create", teamName).Run(env)
		c.Assert(res, ResultOk)
		env.Set("team", teamName)
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := T("team-remove", "-y", teamName).Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func poolAdd() ExecFlow {
	flow := ExecFlow{
		provides: []string{"poolnames"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		for _, prov := range allProvisioners {
			poolName := "ipool-" + prov
			res := T("pool-add", "--provisioner", prov, poolName).Run(env)
			c.Assert(res, ResultOk)
			env.Add("poolnames", poolName)
			res = T("pool-teams-add", poolName, "{{.team}}").Run(env)
			c.Assert(res, ResultOk)
			res = T("node-add", "{{.nodeopts}}", "pool="+poolName).Run(env)
			c.Assert(res, ResultOk)
			res = T("event-list").Run(env)
			c.Assert(res, ResultOk)
			nodeopts := env.All("nodeopts")
			env.Set("nodeopts", append(nodeopts[1:], nodeopts[0])...)
			regex := regexp.MustCompile(`node.create.*?node:\s+(.*?)\s+`)
			parts := regex.FindStringSubmatch(res.Stdout.String())
			c.Assert(parts, check.HasLen, 2)
			env.Add("nodeaddrs", parts[1])
			regex = regexp.MustCompile(parts[1] + `.*?ready`)
			ok := retry(time.Minute, func() bool {
				res = T("node-list").Run(env)
				return regex.MatchString(res.Stdout.String())
			})
			c.Assert(ok, check.Equals, true, check.Commentf("node not ready after 1 minute: %v", res))
		}
	}
	flow.backward = func(c *check.C, env *Environment) {
		for _, node := range env.All("nodeaddrs") {
			res := T("node-remove", "-y", "--no-rebalance", node).Run(env)
			c.Check(res, ResultOk)
		}
		for _, prov := range allProvisioners {
			poolName := "ipool-" + prov
			res := T("pool-teams-remove", poolName, "{{.team}}").Run(env)
			c.Check(res, ResultOk)
			res = T("pool-remove", "-y", poolName).Run(env)
			c.Check(res, ResultOk)
		}
	}
	return flow
}

func platformAdd() ExecFlow {
	flow := ExecFlow{
		provides: []string{"platforms"},
		matrix: map[string]string{
			"platimg": "platformimages",
		},
		parallel: true,
	}
	flow.forward = func(c *check.C, env *Environment) {
		img := env.Get("platimg")
		suffix := img[strings.LastIndex(img, "/")+1:]
		platName := "iplat-" + suffix
		res := T("platform-add", platName, "-i", img).WithTimeout(15 * time.Minute).Run(env)
		c.Assert(res, ResultOk)
		env.Add("platforms", platName)
		res = T("platform-list").Run(env)
		c.Assert(res, ResultOk)
		c.Assert(res, ResultMatches, Expected{Stdout: "(?s).*- " + platName + ".*"})
	}
	flow.backward = func(c *check.C, env *Environment) {
		img := env.Get("platimg")
		suffix := img[strings.LastIndex(img, "/")+1:]
		platName := "iplat-" + suffix
		res := T("platform-remove", "-y", platName).Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func exampleApps() ExecFlow {
	flow := ExecFlow{
		matrix: map[string]string{
			"pool": "poolnames",
			"plat": "platforms",
		},
		parallel: true,
	}
	appName := "iapp-{{.plat}}-{{.pool}}"
	flow.forward = func(c *check.C, env *Environment) {
		res := T("app-create", appName, "{{.plat}}", "-t", "{{.team}}", "-o", "{{.pool}}").Run(env)
		c.Assert(res, ResultOk)
		res = T("app-info", "-a", appName).Run(env)
		c.Assert(res, ResultOk)
		platRE := regexp.MustCompile(`(?s)Platform: (.*?)\n`)
		parts := platRE.FindStringSubmatch(res.Stdout.String())
		c.Assert(parts, check.HasLen, 2)
		lang := strings.Replace(parts[1], "iplat-", "", -1)
		res = T("app-deploy", "-a", appName, "{{.examplesdir}}/"+lang+"/").Run(env)
		c.Assert(res, ResultOk)
		res = T("app-info", "-a", appName).Run(env)
		c.Assert(res, ResultOk)
		addrRE := regexp.MustCompile(`(?s)Address: (.*?)\n`)
		parts = addrRE.FindStringSubmatch(res.Stdout.String())
		c.Assert(parts, check.HasLen, 2)
		cmd := NewCommand("curl", "-sSf", "http://"+parts[1])
		ok := retry(5*time.Minute, func() bool {
			res = cmd.Run(env)
			return res.ExitCode == 0
		})
		c.Assert(ok, check.Equals, true, check.Commentf("invalid result: %v", res))
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := T("app-remove", "-y", "-a", appName).Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func (s *S) TestBase(c *check.C) {
	env := NewEnvironment()
	if !env.Has("enabled") {
		return
	}
	var executedFlows []*ExecFlow
	defer func() {
		for i := len(executedFlows) - 1; i >= 0; i-- {
			executedFlows[i].Rollback(c, env)
		}
	}()
	for i := range flows {
		f := &flows[i]
		if len(f.provides) > 0 {
			providesAll := true
			for _, envVar := range f.provides {
				if env.Get(envVar) == "" {
					providesAll = false
					break
				}
			}
			if providesAll {
				continue
			}
		}
		executedFlows = append(executedFlows, f)
		f.Run(c, env)
	}
}
<commit_msg>integration: uses installerconfig on uninstall<commit_after>// Copyright 2017 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package integration

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
	"time"

	"gopkg.in/check.v1"
)

var (
	T            = NewCommand("tsuru").WithArgs
	allPlatforms = []string{
		"tsuru/python",
		"tsuru/go",
		"tsuru/buildpack",
		"tsuru/cordova",
		"tsuru/elixir",
		"tsuru/java",
		"tsuru/nodejs",
		"tsuru/php",
		"tsuru/play",
		"tsuru/pypy",
		"tsuru/python3",
		"tsuru/ruby",
		"tsuru/static",
	}
	allProvisioners = []string{
		"docker",
		"swarm",
	}
	flows = []ExecFlow{
		platformsToInstall(),
		installerConfigTest(),
		installerComposeTest(),
		installerTest(),
		targetTest(),
		loginTest(),
		removeInstallNodes(),
		quotaTest(),
		teamTest(),
		poolAdd(),
		platformAdd(),
		exampleApps(),
	}
)

var installerConfig = `driver:
  name: virtualbox
  options:
    virtualbox-cpu-count: 2
    virtualbox-memory: 2048
hosts:
  apps:
    size: 2
components:
    install-dashboard: false
`

func platformsToInstall() ExecFlow {
	flow := ExecFlow{
		provides: []string{"platformimages"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		for _, platImg := range allPlatforms {
			env.Add("platformimages", platImg)
		}
	}
	return flow
}

func installerConfigTest() ExecFlow {
	flow := ExecFlow{
		provides: []string{"installerconfig"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		f, err := ioutil.TempFile("", "installer-config")
		c.Assert(err, check.IsNil)
		defer f.Close()
		f.Write([]byte(installerConfig))
		c.Assert(err, check.IsNil)
		env.Set("installerconfig", f.Name())
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := NewCommand("rm", "{{.installerconfig}}").Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func installerComposeTest() ExecFlow {
	flow := ExecFlow{
		provides: []string{"installercompose"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		composeFile, err := ioutil.TempFile("", "installer-compose")
		c.Assert(err, check.IsNil)
		defer composeFile.Close()
		f, err := ioutil.TempFile("", "installer-config")
		c.Assert(err, check.IsNil)
		defer func() {
			res := NewCommand("rm", f.Name()).Run(env)
			c.Check(res, ResultOk)
			f.Close()
		}()
		res := T("install-config-init", f.Name(), composeFile.Name()).Run(env)
		c.Assert(res, ResultOk)
		composeData, err := ioutil.ReadFile(composeFile.Name())
		c.Assert(err, check.IsNil)
		composeData = []byte(strings.Replace(string(composeData), "tsuru/api:v1", "tsuru/api:latest", 1))
		err = ioutil.WriteFile(composeFile.Name(), composeData, 0644)
		env.Set("installercompose", composeFile.Name())
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := NewCommand("rm", "{{.installercompose}}").Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func installerTest() ExecFlow {
	flow := ExecFlow{
		provides: []string{"targetaddr"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		res := T("install", "--config", "{{.installerconfig}}", "--compose", "{{.installercompose}}").WithTimeout(30 * time.Minute).Run(env)
		c.Assert(res, ResultOk)
		regex := regexp.MustCompile(`(?si).*Core Hosts:.*?([\d.]+)\s.*`)
		parts := regex.FindStringSubmatch(res.Stdout.String())
		c.Assert(parts, check.HasLen, 2)
		targetHost := parts[1]
		regex = regexp.MustCompile(`(?si).*tsuru_tsuru.*?\|\s(\d+)`)
		parts = regex.FindStringSubmatch(res.Stdout.String())
		c.Assert(parts, check.HasLen, 2)
		targetPort := parts[1]
		env.Set("targetaddr", fmt.Sprintf("http://%s:%s", targetHost, targetPort))
		regex = regexp.MustCompile(`\| (https?[^\s]+?) \|`)
		allParts := regex.FindAllStringSubmatch(res.Stdout.String(), -1)
		for _, parts = range allParts {
			c.Assert(parts, check.HasLen, 2)
			env.Add("nodeopts", fmt.Sprintf("--register address=%s --cacert ~/.tsuru/installs/tsuru/certs/ca.pem --clientcert ~/.tsuru/installs/tsuru/certs/cert.pem --clientkey ~/.tsuru/installs/tsuru/certs/key.pem", parts[1]))
			env.Add("nodestoremove", parts[1])
		}
		regex = regexp.MustCompile(`Username: ([[:print:]]+)`)
		parts = regex.FindStringSubmatch(res.Stdout.String())
		env.Set("adminuser", parts[1])
		regex = regexp.MustCompile(`Password: ([[:print:]]+)`)
		parts = regex.FindStringSubmatch(res.Stdout.String())
		env.Set("adminpassword", parts[1])
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := T("uninstall", "--config", "{{.installerconfig}}", "-y").Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func targetTest() ExecFlow {
	flow := ExecFlow{}
	flow.forward = func(c *check.C, env *Environment) {
		targetName := "integration-target"
		res := T("target-add", targetName, "{{.targetaddr}}").Run(env)
		c.Assert(res, ResultOk)
		res = T("target-list").Run(env)
		c.Assert(res, ResultMatches, Expected{Stdout: `\s+` + targetName + ` .*`})
		res = T("target-set", targetName).Run(env)
		c.Assert(res, ResultOk)
	}
	return flow
}

func loginTest() ExecFlow {
	flow := ExecFlow{}
	flow.forward = func(c *check.C, env *Environment) {
		res := T("login", "{{.adminuser}}").WithInput("{{.adminpassword}}").Run(env)
		c.Assert(res, ResultOk)
	}
	return flow
}

func removeInstallNodes() ExecFlow {
	flow := ExecFlow{
		matrix: map[string]string{
			"node": "nodestoremove",
		},
	}
	flow.forward = func(c *check.C, env *Environment) {
		res := T("node-remove", "-y", "--no-rebalance", "{{.node}}").Run(env)
		c.Assert(res, ResultOk)
	}
	return flow
}

func quotaTest() ExecFlow {
	flow := ExecFlow{
		requires: []string{"adminuser"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		res := T("user-quota-change", "{{.adminuser}}", "100").Run(env)
		c.Assert(res, ResultOk)
		res = T("user-quota-view", "{{.adminuser}}").Run(env)
		c.Assert(res, ResultMatches, Expected{Stdout: `(?s)Apps usage.*/100`})
	}
	return flow
}

func teamTest() ExecFlow {
	flow := ExecFlow{
		provides: []string{"team"},
	}
	teamName := "integration-team"
	flow.forward = func(c *check.C, env *Environment) {
		res := T("team-create", teamName).Run(env)
		c.Assert(res, ResultOk)
		env.Set("team", teamName)
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := T("team-remove", "-y", teamName).Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func poolAdd() ExecFlow {
	flow := ExecFlow{
		provides: []string{"poolnames"},
	}
	flow.forward = func(c *check.C, env *Environment) {
		for _, prov := range allProvisioners {
			poolName := "ipool-" + prov
			res := T("pool-add", "--provisioner", prov, poolName).Run(env)
			c.Assert(res, ResultOk)
			env.Add("poolnames", poolName)
			res = T("pool-teams-add", poolName, "{{.team}}").Run(env)
			c.Assert(res, ResultOk)
			res = T("node-add", "{{.nodeopts}}", "pool="+poolName).Run(env)
			c.Assert(res, ResultOk)
			res = T("event-list").Run(env)
			c.Assert(res, ResultOk)
			nodeopts := env.All("nodeopts")
			env.Set("nodeopts", append(nodeopts[1:], nodeopts[0])...)
			regex := regexp.MustCompile(`node.create.*?node:\s+(.*?)\s+`)
			parts := regex.FindStringSubmatch(res.Stdout.String())
			c.Assert(parts, check.HasLen, 2)
			env.Add("nodeaddrs", parts[1])
			regex = regexp.MustCompile(parts[1] + `.*?ready`)
			ok := retry(time.Minute, func() bool {
				res = T("node-list").Run(env)
				return regex.MatchString(res.Stdout.String())
			})
			c.Assert(ok, check.Equals, true, check.Commentf("node not ready after 1 minute: %v", res))
		}
	}
	flow.backward = func(c *check.C, env *Environment) {
		for _, node := range env.All("nodeaddrs") {
			res := T("node-remove", "-y", "--no-rebalance", node).Run(env)
			c.Check(res, ResultOk)
		}
		for _, prov := range allProvisioners {
			poolName := "ipool-" + prov
			res := T("pool-teams-remove", poolName, "{{.team}}").Run(env)
			c.Check(res, ResultOk)
			res = T("pool-remove", "-y", poolName).Run(env)
			c.Check(res, ResultOk)
		}
	}
	return flow
}

func platformAdd() ExecFlow {
	flow := ExecFlow{
		provides: []string{"platforms"},
		matrix: map[string]string{
			"platimg": "platformimages",
		},
		parallel: true,
	}
	flow.forward = func(c *check.C, env *Environment) {
		img := env.Get("platimg")
		suffix := img[strings.LastIndex(img, "/")+1:]
		platName := "iplat-" + suffix
		res := T("platform-add", platName, "-i", img).WithTimeout(15 * time.Minute).Run(env)
		c.Assert(res, ResultOk)
		env.Add("platforms", platName)
		res = T("platform-list").Run(env)
		c.Assert(res, ResultOk)
		c.Assert(res, ResultMatches, Expected{Stdout: "(?s).*- " + platName + ".*"})
	}
	flow.backward = func(c *check.C, env *Environment) {
		img := env.Get("platimg")
		suffix := img[strings.LastIndex(img, "/")+1:]
		platName := "iplat-" + suffix
		res := T("platform-remove", "-y", platName).Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func exampleApps() ExecFlow {
	flow := ExecFlow{
		matrix: map[string]string{
			"pool": "poolnames",
			"plat": "platforms",
		},
		parallel: true,
	}
	appName := "iapp-{{.plat}}-{{.pool}}"
	flow.forward = func(c *check.C, env *Environment) {
		res := T("app-create", appName, "{{.plat}}", "-t", "{{.team}}", "-o", "{{.pool}}").Run(env)
		c.Assert(res, ResultOk)
		res = T("app-info", "-a", appName).Run(env)
		c.Assert(res, ResultOk)
		platRE := regexp.MustCompile(`(?s)Platform: (.*?)\n`)
		parts := platRE.FindStringSubmatch(res.Stdout.String())
		c.Assert(parts, check.HasLen, 2)
		lang := strings.Replace(parts[1], "iplat-", "", -1)
		res = T("app-deploy", "-a", appName, "{{.examplesdir}}/"+lang+"/").Run(env)
		c.Assert(res, ResultOk)
		res = T("app-info", "-a", appName).Run(env)
		c.Assert(res, ResultOk)
		addrRE := regexp.MustCompile(`(?s)Address: (.*?)\n`)
		parts = addrRE.FindStringSubmatch(res.Stdout.String())
		c.Assert(parts, check.HasLen, 2)
		cmd := NewCommand("curl", "-sSf", "http://"+parts[1])
		ok := retry(5*time.Minute, func() bool {
			res = cmd.Run(env)
			return res.ExitCode == 0
		})
		c.Assert(ok, check.Equals, true, check.Commentf("invalid result: %v", res))
	}
	flow.backward = func(c *check.C, env *Environment) {
		res := T("app-remove", "-y", "-a", appName).Run(env)
		c.Check(res, ResultOk)
	}
	return flow
}

func (s *S) TestBase(c *check.C) {
	env := NewEnvironment()
	if !env.Has("enabled") {
		return
	}
	var executedFlows []*ExecFlow
	defer func() {
		for i := len(executedFlows) - 1; i >= 0; i-- {
			executedFlows[i].Rollback(c, env)
		}
	}()
	for i := range flows {
		f := &flows[i]
		if len(f.provides) > 0 {
			providesAll := true
			for _, envVar := range f.provides {
				if env.Get(envVar) == "" {
					providesAll = false
					break
				}
			}
			if providesAll {
				continue
			}
		}
		executedFlows = append(executedFlows, f)
		f.Run(c, env)
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build docker_integration
// +build docker_integration

package docker

const integrationDockerImage = "mcr.microsoft.com/dotnet/sdk:latest"

func integrationCreateContainerOpts(imageName string, hostConfig *HostConfig) CreateContainerOptions {
	return CreateContainerOptions{
		Config: &Config{
			Image: imageName,
			Cmd:   []string{"powershell", "-Command", `Write-Host "hello hello"`},
		},
		HostConfig: hostConfig,
	}
}
<commit_msg>Try a different windows base image for integration tests<commit_after>// Copyright 2019 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build docker_integration
// +build docker_integration

package docker

const integrationDockerImage = "mcr.microsoft.com/windows/servercore:ltsc2022"

func integrationCreateContainerOpts(imageName string, hostConfig *HostConfig) CreateContainerOptions {
	return CreateContainerOptions{
		Config: &Config{
			Image: imageName,
			Cmd:   []string{"powershell", "-Command", `Write-Host "hello hello"`},
		},
		HostConfig: hostConfig,
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package watch 提供热编译功能。
//
// 功能与 github.com/caixw/gobuild 相似。
package watch

import (
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/caixw/gobuild"
)

// TODO 自动编译 .proto 文件？

var (
	recursive, showIgnore                     bool
	mainFiles, outputName, extString, appArgs string

	flagset = flag.NewFlagSet("watch", flag.ExitOnError)
)

func init() {
	flagset.BoolVar(&recursive, "r", true, "是否查找子目录；")
	flagset.BoolVar(&showIgnore, "i", false, "是否显示被标记为 IGNORE 的日志内容；")
	flagset.StringVar(&outputName, "o", "", "指定输出名称，程序的工作目录随之改变；")
	flagset.StringVar(&appArgs, "x", "", "传递给编译程序的参数；")
	flagset.StringVar(&extString, "ext", "go", "指定监视的文件扩展，区分大小写。* 表示监视所有类型文件，空值代表不监视任何文件；")
	flagset.StringVar(&mainFiles, "main", "", "指定需要编译的文件；")
}

// Do 执行子命令
func Do(output io.Writer) error {
	if err := flagset.Parse(os.Args[2:]); err != nil {
		return err
	}

	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	dirs := append([]string{wd}, flag.Args()...)

	logs := gobuild.NewConsoleLogs(showIgnore)
	defer logs.Stop()
	return gobuild.Build(logs.Logs, mainFiles, outputName, extString, recursive, appArgs, dirs...)
}

// Usage 当前子命令的用法
func Usage(output io.Writer) {
	fmt.Fprintln(output, `热编译当前目录下的项目

命令行语法：
 web watch [options] [dependents]

 options:`)

	flagset.SetOutput(output)
	flagset.PrintDefaults()

	fmt.Fprintln(output, `
 dependents:
  指定其它依赖的目录，只能出现在命令的尾部。

常见用法:

 web watch
   监视当前目录，若有变动，则重新编译当前目录下的 *.go 文件；

 web watch -main=main.go
   监视当前目录，若有变动，则重新编译当前目录下的 main.go 文件；

 web watch -main="main.go" dir1 dir2
   监视当前目录及 dir1 和 dir2，若有变动，则重新编译当前目录下的 main.go 文件；

NOTE: 不会监视隐藏文件和隐藏目录下的文件。`)
}
<commit_msg>[cmd/web] 更新文档<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package watch 提供热编译功能。
//
// 功能与 github.com/caixw/gobuild 相同。
package watch

import (
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/caixw/gobuild"
)

var (
	recursive, showIgnore                     bool
	mainFiles, outputName, extString, appArgs string

	flagset = flag.NewFlagSet("watch", flag.ExitOnError)
)

func init() {
	flagset.BoolVar(&recursive, "r", true, "是否查找子目录；")
	flagset.BoolVar(&showIgnore, "i", false, "是否显示被标记为 IGNORE 的日志内容；")
	flagset.StringVar(&outputName, "o", "", "指定输出名称，程序的工作目录随之改变；")
	flagset.StringVar(&appArgs, "x", "", "传递给编译程序的参数；")
	flagset.StringVar(&extString, "ext", "go", "指定监视的文件扩展，区分大小写。* 表示监视所有类型文件，空值代表不监视任何文件；")
	flagset.StringVar(&mainFiles, "main", "", "指定需要编译的文件；")
}

// Do 执行子命令
func Do(output io.Writer) error {
	if err := flagset.Parse(os.Args[2:]); err != nil {
		return err
	}

	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	dirs := append([]string{wd}, flag.Args()...)

	logs := gobuild.NewConsoleLogs(showIgnore)
	defer logs.Stop()
	return gobuild.Build(logs.Logs, mainFiles, outputName, extString, recursive, appArgs, dirs...)
}

// Usage 当前子命令的用法
func Usage(output io.Writer) {
	fmt.Fprintln(output, `热编译当前目录下的项目

命令行语法：
 web watch [options] [dependents]

 options:`)

	flagset.SetOutput(output)
	flagset.PrintDefaults()

	fmt.Fprintln(output, `
 dependents:
  指定其它依赖的目录，只能出现在命令的尾部。

常见用法:

 web watch
   监视当前目录，若有变动，则重新编译当前目录下的 *.go 文件；

 web watch -main=main.go
   监视当前目录，若有变动，则重新编译当前目录下的 main.go 文件；

 web watch -main="main.go" dir1 dir2
   监视当前目录及 dir1 和 dir2，若有变动，则重新编译当前目录下的 main.go 文件；

NOTE: 不会监视隐藏文件和隐藏目录下的文件。`)
}
<|endoftext|>


<commit_before>package hscan

import (
	"reflect"
	"strings"
	"sync"
)

// structMap contains the map of struct fields for target structs
// indexed by the struct type.
type structMap struct {
	m sync.Map
}

func newStructMap() *structMap {
	return new(structMap)
}

func (s *structMap) get(t reflect.Type) *structSpec {
	if v, ok := s.m.Load(t); ok {
		return v.(*structSpec)
	}

	spec := newStructSpec(t, "redis")
	s.m.Store(t, spec)
	return spec
}

//------------------------------------------------------------------------------

// structSpec contains the list of all fields in a target struct.
type structSpec struct {
	m map[string]*structField
}

func (s *structSpec) set(tag string, sf *structField) {
	s.m[tag] = sf
}

func (s *structSpec) get(tag string) (*structField, bool) {
	f, ok := s.m[tag]
	return f, ok
}

func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
	out := &structSpec{
		m: make(map[string]*structField),
	}

	num := t.NumField()
	for i := 0; i < num; i++ {
		f := t.Field(i)

		tag := f.Tag.Get(fieldTag)
		if tag == "" || tag == "-" {
			continue
		}

		tag = strings.Split(tag, ",")[0]
		if tag == "" {
			continue
		}

		// Use the built-in decoder.
		out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]})
	}

	return out
}

//------------------------------------------------------------------------------

// structField represents a single field in a target struct.
type structField struct {
	index int
	fn    decoderFunc
}

//------------------------------------------------------------------------------

type StructValue struct {
	spec  *structSpec
	value reflect.Value
}

func (s StructValue) Scan(key string, value string) error {
	field, ok := s.spec.m[key]
	if !ok {
		return nil
	}
	return field.fn(s.value.Field(field.index), value)
}
<commit_msg>Fix build<commit_after>package hscan

import (
	"reflect"
	"strings"
	"sync"
)

// structMap contains the map of struct fields for target structs
// indexed by the struct type.
type structMap struct {
	m sync.Map
}

func newStructMap() *structMap {
	return new(structMap)
}

func (s *structMap) get(t reflect.Type) *structSpec {
	if v, ok := s.m.Load(t); ok {
		return v.(*structSpec)
	}

	spec := newStructSpec(t, "redis")
	s.m.Store(t, spec)
	return spec
}

//------------------------------------------------------------------------------

// structSpec contains the list of all fields in a target struct.
type structSpec struct {
	m map[string]*structField
}

func (s *structSpec) set(tag string, sf *structField) {
	s.m[tag] = sf
}

func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
	out := &structSpec{
		m: make(map[string]*structField),
	}

	num := t.NumField()
	for i := 0; i < num; i++ {
		f := t.Field(i)

		tag := f.Tag.Get(fieldTag)
		if tag == "" || tag == "-" {
			continue
		}

		tag = strings.Split(tag, ",")[0]
		if tag == "" {
			continue
		}

		// Use the built-in decoder.
		out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]})
	}

	return out
}

//------------------------------------------------------------------------------

// structField represents a single field in a target struct.
type structField struct {
	index int
	fn    decoderFunc
}

//------------------------------------------------------------------------------

type StructValue struct {
	spec  *structSpec
	value reflect.Value
}

func (s StructValue) Scan(key string, value string) error {
	field, ok := s.spec.m[key]
	if !ok {
		return nil
	}
	return field.fn(s.value.Field(field.index), value)
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package version records versioning information about this module.
package version

import (
	"fmt"
	"strings"
)

// These constants determine the current version of this module.
//
//
// For our release process, we enforce the following rules:
//	* Tagged releases use a tag that is identical to String.
//	* Tagged releases never reference a commit where the String
//	contains "devel".
//	* The set of all commits in this repository where String
//	does not contain "devel" must have a unique String.
//
//
// Steps for tagging a new release:
//	1. Create a new CL.
//
//	2. Update Minor, Patch, and/or PreRelease as necessary.
//	PreRelease must not contain the string "devel".
//
//	3. Since the last released minor version, have there been any changes to
//	generator that relies on new functionality in the runtime?
//	If yes, then increment RequiredGenerated.
//
//	4. Since the last released minor version, have there been any changes to
//	the runtime that removes support for old .pb.go source code?
//	If yes, then increment SupportMinimum.
//
//	5. Send out the CL for review and submit it.
//	Note that the next CL in step 8 must be submitted after this CL
//	without any other CLs in-between.
//
//	6. Tag a new version, where the tag is is the current String.
//
//	7. Write release notes for all notable changes
//	between this release and the last release.
//
//	8. Create a new CL.
//
//	9. Update PreRelease to include the string "devel".
//	For example: "" -> "devel" or "rc.1" -> "rc.1.devel"
//
//	10. Send out the CL for review and submit it.
const (
	Major      = 1
	Minor      = 26
	Patch      = 0
	PreRelease = "devel"
)

// String formats the version string for this module in semver format.
//
// Examples:
//	v1.20.1
//	v1.21.0-rc.1
func String() string {
	v := fmt.Sprintf("v%d.%d.%d", Major, Minor, Patch)
	if PreRelease != "" {
		v += "-" + PreRelease

		// TODO: Add metadata about the commit or build hash.
		// See https://golang.org/issue/29814
		// See https://golang.org/issue/33533
		var metadata string
		if strings.Contains(PreRelease, "devel") && metadata != "" {
			v += "+" + metadata
		}
	}
	return v
}
<commit_msg>all: release v1.27.0<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package version records versioning information about this module.
package version

import (
	"fmt"
	"strings"
)

// These constants determine the current version of this module.
//
//
// For our release process, we enforce the following rules:
//	* Tagged releases use a tag that is identical to String.
//	* Tagged releases never reference a commit where the String
//	contains "devel".
//	* The set of all commits in this repository where String
//	does not contain "devel" must have a unique String.
//
//
// Steps for tagging a new release:
//	1. Create a new CL.
//
//	2. Update Minor, Patch, and/or PreRelease as necessary.
//	PreRelease must not contain the string "devel".
//
//	3. Since the last released minor version, have there been any changes to
//	generator that relies on new functionality in the runtime?
//	If yes, then increment RequiredGenerated.
//
//	4. Since the last released minor version, have there been any changes to
//	the runtime that removes support for old .pb.go source code?
//	If yes, then increment SupportMinimum.
//
//	5. Send out the CL for review and submit it.
//	Note that the next CL in step 8 must be submitted after this CL
//	without any other CLs in-between.
//
//	6. Tag a new version, where the tag is is the current String.
//
//	7. Write release notes for all notable changes
//	between this release and the last release.
//
//	8. Create a new CL.
//
//	9. Update PreRelease to include the string "devel".
//	For example: "" -> "devel" or "rc.1" -> "rc.1.devel"
//
//	10. Send out the CL for review and submit it.
const (
	Major      = 1
	Minor      = 27
	Patch      = 0
	PreRelease = ""
)

// String formats the version string for this module in semver format.
//
// Examples:
//	v1.20.1
//	v1.21.0-rc.1
func String() string {
	v := fmt.Sprintf("v%d.%d.%d", Major, Minor, Patch)
	if PreRelease != "" {
		v += "-" + PreRelease

		// TODO: Add metadata about the commit or build hash.
		// See https://golang.org/issue/29814
		// See https://golang.org/issue/33533
		var metadata string
		if strings.Contains(PreRelease, "devel") && metadata != "" {
			v += "+" + metadata
		}
	}
	return v
}
<|endoftext|>


<commit_before>package filesystem

import (
	"bytes"
	"io"
	"os"
	"path"
	"testing"

	. "gopkg.in/check.v1"
	"gopkg.in/src-d/go-billy.v3"
	"gopkg.in/src-d/go-billy.v3/memfs"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/utils/merkletrie"
	"gopkg.in/src-d/go-git.v4/utils/merkletrie/noder"
)

func Test(t *testing.T) { TestingT(t) }

type NoderSuite struct{}

var _ = Suite(&NoderSuite{})

func (s *NoderSuite) TestDiff(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)
	WriteFile(fsA, "qux/bar", []byte("foo"), 0644)
	WriteFile(fsA, "qux/qux", []byte("foo"), 0644)
	fsA.Symlink("foo", "bar")

	fsB := memfs.New()
	WriteFile(fsB, "foo", []byte("foo"), 0644)
	WriteFile(fsB, "qux/bar", []byte("foo"), 0644)
	WriteFile(fsB, "qux/qux", []byte("foo"), 0644)
	fsB.Symlink("foo", "bar")

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 0)
}

func (s *NoderSuite) TestDiffChangeLink(c *C) {
	fsA := memfs.New()
	fsA.Symlink("qux", "foo")

	fsB := memfs.New()
	fsB.Symlink("bar", "foo")

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)
}

func (s *NoderSuite) TestDiffChangeContent(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)
	WriteFile(fsA, "qux/bar", []byte("foo"), 0644)
	WriteFile(fsA, "qux/qux", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "foo", []byte("foo"), 0644)
	WriteFile(fsB, "qux/bar", []byte("bar"), 0644)
	WriteFile(fsB, "qux/qux", []byte("foo"), 0644)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)
}

func (s *NoderSuite) TestDiffChangeMissing(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "bar", []byte("bar"), 0644)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 2)
}

func (s *NoderSuite) TestDiffChangeMode(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "foo", []byte("foo"), 0755)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)
}

func (s *NoderSuite) TestDiffChangeModeNotRelevant(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "foo", []byte("foo"), 0655)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 0)
}

func (s *NoderSuite) TestDiffDirectory(c *C) {
	dir := path.Join("qux", "bar")
	fsA := memfs.New()
	fsA.MkdirAll(dir, 0644)

	fsB := memfs.New()
	fsB.MkdirAll(dir, 0644)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, map[string]plumbing.Hash{
			dir: plumbing.NewHash("aa102815663d23f8b75a47e7a01965dcdc96468c"),
		}),
		NewRootNode(fsB, map[string]plumbing.Hash{
			dir: plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c"),
		}),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)

	a, err := ch[0].Action()
	c.Assert(err, IsNil)
	c.Assert(a, Equals, merkletrie.Modify)
}

func WriteFile(fs billy.Filesystem, filename string, data []byte, perm os.FileMode) error {
	f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
	if err != nil {
		return err
	}

	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	if err1 := f.Close(); err == nil {
		err = err1
	}
	return err
}

var empty = make([]byte, 24)

func IsEquals(a, b noder.Hasher) bool {
	if bytes.Equal(a.Hash(), empty) || bytes.Equal(b.Hash(), empty) {
		return false
	}

	return bytes.Equal(a.Hash(), b.Hash())
}
<commit_msg>utils: merkletrie, filesystem fix symlinks to dir<commit_after>package filesystem

import (
	"bytes"
	"io"
	"os"
	"path"
	"testing"

	. "gopkg.in/check.v1"
	"gopkg.in/src-d/go-billy.v3"
	"gopkg.in/src-d/go-billy.v3/memfs"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/utils/merkletrie"
	"gopkg.in/src-d/go-git.v4/utils/merkletrie/noder"
)

func Test(t *testing.T) { TestingT(t) }

type NoderSuite struct{}

var _ = Suite(&NoderSuite{})

func (s *NoderSuite) TestDiff(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)
	WriteFile(fsA, "qux/bar", []byte("foo"), 0644)
	WriteFile(fsA, "qux/qux", []byte("foo"), 0644)
	fsA.Symlink("foo", "bar")

	fsB := memfs.New()
	WriteFile(fsB, "foo", []byte("foo"), 0644)
	WriteFile(fsB, "qux/bar", []byte("foo"), 0644)
	WriteFile(fsB, "qux/qux", []byte("foo"), 0644)
	fsB.Symlink("foo", "bar")

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 0)
}

func (s *NoderSuite) TestDiffChangeLink(c *C) {
	fsA := memfs.New()
	fsA.Symlink("qux", "foo")

	fsB := memfs.New()
	fsB.Symlink("bar", "foo")

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)
}

func (s *NoderSuite) TestDiffChangeContent(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)
	WriteFile(fsA, "qux/bar", []byte("foo"), 0644)
	WriteFile(fsA, "qux/qux", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "foo", []byte("foo"), 0644)
	WriteFile(fsB, "qux/bar", []byte("bar"), 0644)
	WriteFile(fsB, "qux/qux", []byte("foo"), 0644)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)
}

func (s *NoderSuite) TestDiffSymlinkDirOnA(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "qux/qux", []byte("foo"), 0644)

	fsB := memfs.New()
	fsB.Symlink("qux", "foo")
	WriteFile(fsB, "qux/qux", []byte("foo"), 0644)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)
}

func (s *NoderSuite) TestDiffSymlinkDirOnB(c *C) {
	fsA := memfs.New()
	fsA.Symlink("qux", "foo")
	WriteFile(fsA, "qux/qux", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "qux/qux", []byte("foo"), 0644)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)
}

func (s *NoderSuite) TestDiffChangeMissing(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "bar", []byte("bar"), 0644)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 2)
}

func (s *NoderSuite) TestDiffChangeMode(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "foo", []byte("foo"), 0755)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)
}

func (s *NoderSuite) TestDiffChangeModeNotRelevant(c *C) {
	fsA := memfs.New()
	WriteFile(fsA, "foo", []byte("foo"), 0644)

	fsB := memfs.New()
	WriteFile(fsB, "foo", []byte("foo"), 0655)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, nil),
		NewRootNode(fsB, nil),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 0)
}

func (s *NoderSuite) TestDiffDirectory(c *C) {
	dir := path.Join("qux", "bar")
	fsA := memfs.New()
	fsA.MkdirAll(dir, 0644)

	fsB := memfs.New()
	fsB.MkdirAll(dir, 0644)

	ch, err := merkletrie.DiffTree(
		NewRootNode(fsA, map[string]plumbing.Hash{
			dir: plumbing.NewHash("aa102815663d23f8b75a47e7a01965dcdc96468c"),
		}),
		NewRootNode(fsB, map[string]plumbing.Hash{
			dir: plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c"),
		}),
		IsEquals,
	)

	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 1)

	a, err := ch[0].Action()
	c.Assert(err, IsNil)
	c.Assert(a, Equals, merkletrie.Modify)
}

func WriteFile(fs billy.Filesystem, filename string, data []byte, perm os.FileMode) error {
	f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
	if err != nil {
		return err
	}

	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	if err1 := f.Close(); err == nil {
		err = err1
	}
	return err
}

var empty = make([]byte, 24)

func IsEquals(a, b noder.Hasher) bool {
	if bytes.Equal(a.Hash(), empty) || bytes.Equal(b.Hash(), empty) {
		return false
	}

	return bytes.Equal(a.Hash(), b.Hash())
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"strings"

	"github.com/funkygao/gocli"
	"github.com/siddontang/go-mysql/canal"
)

type Canal struct {
	Ui  cli.Ui
	Cmd string
}

type myRowsEventHandler struct {
}

func (h *myRowsEventHandler) Do(e *canal.RowsEvent) error {
	fmt.Println(*e)
	return nil
}

func (h *myRowsEventHandler) String() string {
	return "myRowsEventHandler"
}

func (this *Canal) Run(args []string) (exitCode int) {
	cfg := canal.NewDefaultConfig()

	cmdFlags := flag.NewFlagSet("canal", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&cfg.User, "user", "", "")
	cmdFlags.StringVar(&cfg.Password, "pass", "", "")
	cmdFlags.StringVar(&cfg.Addr, "dsn", "", "")
	cmdFlags.StringVar(&cfg.Dump.TableDB, "db", "", "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	cfg.Dump.Tables = []string{"logs"}

	c, err := canal.NewCanal(cfg)
	if err != nil {
		this.Ui.Error(err.Error())
		return
	}

	c.RegRowsEventHandler(&myRowsEventHandler{})

	// Start canal
	c.Start()

	return
}

func (*Canal) Synopsis() string {
	return "Sync binlog from MySQL master to elsewhere"
}

func (this *Canal) Help() string {
	help := fmt.Sprintf(`
Usage: %s canal [options]

    %s

Options:

    -user db user

    -pass db password

    -dsn db dsn

    -db db name

    -table table name

`, this.Cmd, this.Synopsis())
	return strings.TrimSpace(help)
}
<commit_msg>canal<commit_after>package command

import (
	"flag"
	"fmt"
	"strings"
	"time"

	"github.com/funkygao/gocli"
	"github.com/siddontang/go-mysql/canal"
)

type Canal struct {
	Ui  cli.Ui
	Cmd string
}

type myRowsEventHandler struct {
}

func (h *myRowsEventHandler) Do(e *canal.RowsEvent) error {
	fmt.Println(*e)
	return nil
}

func (h *myRowsEventHandler) String() string {
	return "myRowsEventHandler"
}

func (this *Canal) Run(args []string) (exitCode int) {
	cfg := canal.NewDefaultConfig()

	cmdFlags := flag.NewFlagSet("canal", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&cfg.User, "user", "", "")
	cmdFlags.StringVar(&cfg.Password, "pass", "", "")
	cmdFlags.StringVar(&cfg.Addr, "dsn", "", "")
	cmdFlags.StringVar(&cfg.Dump.TableDB, "db", "", "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	cfg.Dump.Tables = []string{"logs"}

	c, err := canal.NewCanal(cfg)
	if err != nil {
		this.Ui.Error(err.Error())
		return
	}
	defer c.Close()

	c.RegRowsEventHandler(&myRowsEventHandler{})

	// Start canal
	c.Start()

	time.Sleep(time.Hour)

	return
}

func (*Canal) Synopsis() string {
	return "Sync binlog from MySQL master to elsewhere"
}

func (this *Canal) Help() string {
	help := fmt.Sprintf(`
Usage: %s canal [options]

    %s

Options:

    -user db user

    -pass db password

    -dsn db dsn

    -db db name

    -table table name

`, this.Cmd, this.Synopsis())
	return strings.TrimSpace(help)
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The linkcheck command finds missing links in the godoc website.
// It crawls a URL recursively and notes URLs and URL fragments
// that it's seen and prints a report of missing links at the end.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"strings"
	"sync"
)

var (
	root    = flag.String("root", "http://localhost:6060", "Root to crawl")
	verbose = flag.Bool("verbose", false, "verbose")
)

var wg sync.WaitGroup        // outstanding fetches
var urlq = make(chan string) // URLs to crawl

// urlFrag is a URL and its optional #fragment (without the #)
type urlFrag struct {
	url, frag string
}

var (
	mu          sync.Mutex
	crawled     = make(map[string]bool)      // URL without fragment -> true
	neededFrags = make(map[urlFrag][]string) // URL#frag -> who needs it
)

var aRx = regexp.MustCompile(`<a href=['"]?(/[^\s'">]+)`)

// Owned by crawlLoop goroutine:
var (
	linkSources = make(map[string][]string) // url no fragment -> sources
	fragExists  = make(map[urlFrag]bool)
	problems    []string
)

func localLinks(body string) (links []string) {
	seen := map[string]bool{}
	mv := aRx.FindAllStringSubmatch(body, -1)
	for _, m := range mv {
		ref := m[1]
		if strings.HasPrefix(ref, "/src/") {
			continue
		}
		if !seen[ref] {
			seen[ref] = true
			links = append(links, m[1])
		}
	}
	return
}

var idRx = regexp.MustCompile(`\bid=['"]?([^\s'">]+)`)

func pageIDs(body string) (ids []string) {
	mv := idRx.FindAllStringSubmatch(body, -1)
	for _, m := range mv {
		ids = append(ids, m[1])
	}
	return
}

// url may contain a #fragment, and the fragment is then noted as needing to exist.
func crawl(url string, sourceURL string) {
	if strings.Contains(url, "/devel/release") {
		return
	}
	mu.Lock()
	defer mu.Unlock()
	var frag string
	if i := strings.Index(url, "#"); i >= 0 {
		frag = url[i+1:]
		url = url[:i]
		if frag != "" {
			uf := urlFrag{url, frag}
			neededFrags[uf] = append(neededFrags[uf], sourceURL)
		}
	}
	if crawled[url] {
		return
	}
	crawled[url] = true

	wg.Add(1)
	go func() {
		urlq <- url
	}()
}

func addProblem(url, errmsg string) {
	msg := fmt.Sprintf("Error on %s: %s (from %s)", url, errmsg, linkSources[url])
	log.Print(msg)
	problems = append(problems, msg)
}

func crawlLoop() {
	for url := range urlq {
		res, err := http.Get(url)
		if err != nil {
			addProblem(url, fmt.Sprintf("Error fetching: %v", err))
			wg.Done()
			continue
		}
		if res.StatusCode != 200 {
			addProblem(url, fmt.Sprintf("Status code = %d", res.StatusCode))
			wg.Done()
			continue
		}
		slurp, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		if err != nil {
			log.Fatalf("Error reading %s body: %v", url, err)
		}
		if *verbose {
			log.Printf("Len of %s: %d", url, len(slurp))
		}
		body := string(slurp)
		for _, ref := range localLinks(body) {
			if *verbose {
				log.Printf("  links to %s", ref)
			}
			dest := *root + ref
			linkSources[dest] = append(linkSources[dest], url)
			crawl(dest, url)
		}
		for _, id := range pageIDs(body) {
			if *verbose {
				log.Printf(" url %s has #%s", url, id)
			}
			fragExists[urlFrag{url, id}] = true
		}

		wg.Done()
	}
}

func main() {
	flag.Parse()

	go crawlLoop()
	crawl(*root, "")
	crawl(*root+"/doc/go1.1.html", "")

	wg.Wait()
	close(urlq)
	for uf, needers := range neededFrags {
		if !fragExists[uf] {
			problems = append(problems, fmt.Sprintf("Missing fragment for %+v from %v", uf, needers))
		}
	}

	for _, s := range problems {
		fmt.Println(s)
	}
}
<commit_msg>misc/linkcheck: better redirect handling, use meaningful exit code<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The linkcheck command finds missing links in the godoc website.
// It crawls a URL recursively and notes URLs and URL fragments
// that it's seen and prints a report of missing links at the end.
package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
	"sync"
)

var (
	root    = flag.String("root", "http://localhost:6060", "Root to crawl")
	verbose = flag.Bool("verbose", false, "verbose")
)

var wg sync.WaitGroup        // outstanding fetches
var urlq = make(chan string) // URLs to crawl

// urlFrag is a URL and its optional #fragment (without the #)
type urlFrag struct {
	url, frag string
}

var (
	mu          sync.Mutex
	crawled     = make(map[string]bool)      // URL without fragment -> true
	neededFrags = make(map[urlFrag][]string) // URL#frag -> who needs it
)

var aRx = regexp.MustCompile(`<a href=['"]?(/[^\s'">]+)`)

// Owned by crawlLoop goroutine:
var (
	linkSources = make(map[string][]string) // url no fragment -> sources
	fragExists  = make(map[urlFrag]bool)
	problems    []string
)

func localLinks(body string) (links []string) {
	seen := map[string]bool{}
	mv := aRx.FindAllStringSubmatch(body, -1)
	for _, m := range mv {
		ref := m[1]
		if strings.HasPrefix(ref, "/src/") {
			continue
		}
		if !seen[ref] {
			seen[ref] = true
			links = append(links, m[1])
		}
	}
	return
}

var idRx = regexp.MustCompile(`\bid=['"]?([^\s'">]+)`)

func pageIDs(body string) (ids []string) {
	mv := idRx.FindAllStringSubmatch(body, -1)
	for _, m := range mv {
		ids = append(ids, m[1])
	}
	return
}

// url may contain a #fragment, and the fragment is then noted as needing to exist.
func crawl(url string, sourceURL string) {
	if strings.Contains(url, "/devel/release") {
		return
	}
	mu.Lock()
	defer mu.Unlock()
	var frag string
	if i := strings.Index(url, "#"); i >= 0 {
		frag = url[i+1:]
		url = url[:i]
		if frag != "" {
			uf := urlFrag{url, frag}
			neededFrags[uf] = append(neededFrags[uf], sourceURL)
		}
	}
	if crawled[url] {
		return
	}
	crawled[url] = true

	wg.Add(1)
	go func() {
		urlq <- url
	}()
}

func addProblem(url, errmsg string) {
	msg := fmt.Sprintf("Error on %s: %s (from %s)", url, errmsg, linkSources[url])
	if *verbose {
		log.Print(msg)
	}
	problems = append(problems, msg)
}

func crawlLoop() {
	for url := range urlq {
		if err := doCrawl(url); err != nil {
			addProblem(url, err.Error())
		}
	}
}

func doCrawl(url string) error {
	defer wg.Done()

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	res, err := http.DefaultTransport.RoundTrip(req)
	if err != nil {
		return err
	}
	// Handle redirects.
	if res.StatusCode/100 == 3 {
		newURL, err := res.Location()
		if err != nil {
			return fmt.Errorf("resolving redirect: %v", err)
		}
		if !strings.HasPrefix(newURL.String(), *root) {
			// Skip off-site redirects.
			return nil
		}
		crawl(newURL.String(), url)
		return nil
	}
	if res.StatusCode != 200 {
		return errors.New(res.Status)
	}
	slurp, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatalf("Error reading %s body: %v", url, err)
	}
	if *verbose {
		log.Printf("Len of %s: %d", url, len(slurp))
	}
	body := string(slurp)
	for _, ref := range localLinks(body) {
		if *verbose {
			log.Printf("  links to %s", ref)
		}
		dest := *root + ref
		linkSources[dest] = append(linkSources[dest], url)
		crawl(dest, url)
	}
	for _, id := range pageIDs(body) {
		if *verbose {
			log.Printf(" url %s has #%s", url, id)
		}
		fragExists[urlFrag{url, id}] = true
	}
	return nil
}

func main() {
	flag.Parse()

	go crawlLoop()
	crawl(*root, "")

	wg.Wait()
	close(urlq)
	for uf, needers := range neededFrags {
		if !fragExists[uf] {
			problems = append(problems, fmt.Sprintf("Missing fragment for %+v from %v", uf, needers))
		}
	}

	for _, s := range problems {
		fmt.Println(s)
	}
	if len(problems) > 0 {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before><commit_msg>[Bug fix] port by command-line option.<commit_after><|endoftext|>


<commit_before>package mmail

import (
	"crypto/tls"
	"net/mail"
	"strings"
	"sync"
	"time"

	"github.com/emersion/go-imap"
	idle "github.com/emersion/go-imap-idle"
	"github.com/emersion/go-imap/client"
	"github.com/rodcorsi/mattermail/model"
)

// MailProviderImap implements MailProvider using imap
type MailProviderImap struct {
	imapClient *client.Client
	cfg        *model.Email
	log        Logger
	idle       bool
	debug      bool
}

const mailBox = "INBOX"

// NewMailProviderImap creates a new MailProviderImap implementing MailProvider
func NewMailProviderImap(cfg *model.Email, log Logger, debug bool) *MailProviderImap {
	return &MailProviderImap{
		cfg:   cfg,
		log:   log,
		debug: debug,
	}
}

// CheckNewMessage gets new email from server
func (m *MailProviderImap) CheckNewMessage(handler MailHandler) error {
	m.log.Debug("MailProviderImap.CheckNewMessage")

	if err := m.checkConnection(); err != nil {
		return err
	}

	if err := m.selectMailBox(); err != nil {
		return err
	}

	criteria := &imap.SearchCriteria{
		WithoutFlags: []string{imap.SeenFlag},
	}

	uid, err := m.imapClient.UidSearch(criteria)
	if err != nil {
		m.log.Debug("MailProviderImap.CheckNewMessage: Error UIDSearch")
		return err
	}

	if len(uid) == 0 {
		m.log.Debug("MailProviderImap.CheckNewMessage: No new messages")
		return nil
	}

	m.log.Debugf("MailProviderImap.CheckNewMessage: found %v uid", len(uid))

	seqset := &imap.SeqSet{}
	seqset.AddNum(uid...)

	messages := make(chan *imap.Message, len(uid))
	done := make(chan error, 1)
	go func() {
		done <- m.imapClient.UidFetch(seqset, []string{imap.EnvelopeMsgAttr, "BODY[]"}, messages)
	}()

	emailPosted := make(map[uint32]bool)
	for _, v := range uid {
		emailPosted[v] = false
	}

	for imapMsg := range messages {
		m.log.Debug("MailProviderImap.CheckNewMessage: PostMail uid:", imapMsg.Uid)
		if emailPosted[imapMsg.Uid] {
			m.log.Debug("MailProviderImap.CheckNewMessage: Email was posted uid:", imapMsg.Uid)
			continue
		}

		r := imapMsg.GetBody("BODY[]")
		if r == nil {
			m.log.Error("MailProviderImap.CheckNewMessage: message.GetBody(BODY[]) returns nil")
			continue
		}

		msg, err := mail.ReadMessage(r)
		if err != nil {
			m.log.Error("MailProviderImap.CheckNewMessage: Error on parse imap/message to mail/message")
			continue
		}

		if err := handler(msg); err != nil {
			m.log.Debug("MailProviderImap.CheckNewMessage: Error handler:", err.Error())
			continue
		} else {
			emailPosted[imapMsg.Uid] = true
		}
	}

	// Check command completion status
	if err := <-done; err != nil {
		m.log.Error("MailProviderImap.CheckNewMessage: Error on terminate fetch command")
		return err
	}

	errorset := &imap.SeqSet{}
	for k, posted := range emailPosted {
		if !posted {
			errorset.AddNum(k)
		}
	}

	if errorset.Empty() {
		return nil
	}

	// Mark all valid messages as read
	err = m.imapClient.UidStore(errorset, imap.RemoveFlags, []interface{}{imap.SeenFlag}, nil)
	if err != nil {
		m.log.Error("MailProviderImap.CheckNewMessage: Error UIDStore UNSEEN")
		return err
	}

	return nil
}

// WaitNewMessage waits for a new message (idle or time.Sleep)
func (m *MailProviderImap) WaitNewMessage(timeout int) error {
	m.log.Debug("MailProviderImap.WaitNewMessage")

	// Idle mode
	if err := m.checkConnection(); err != nil {
		return err
	}

	m.log.Debug("MailProviderImap.WaitNewMessage: idle mode:", m.idle)

	if !m.idle {
		time.Sleep(time.Second * time.Duration(timeout))
		return nil
	}

	if err := m.selectMailBox(); err != nil {
		return err
	}

	idleClient := idle.NewClient(m.imapClient)

	// Create a channel to receive mailbox updates
	statuses := make(chan *imap.MailboxStatus)
	m.imapClient.MailboxUpdates = statuses

	stop := make(chan struct{})
	done := make(chan error, 1)
	go func() {
		done <- idleClient.Idle(stop)
	}()

	reset := time.After(time.Second * time.Duration(timeout))

	var lock sync.Mutex
	closed := false
	closeChannel := func() {
		lock.Lock()
		if !closed {
			close(stop)
			closed = true
		}
		lock.Unlock()
	}

	for {
		select {
		case status := <-statuses:
			m.log.Debug("MailProviderImap.WaitNewMessage: New mailbox status:", status.Format())
			closeChannel()

		case err := <-done:
			if err != nil {
				m.log.Error("MailProviderImap.WaitNewMessage: Error on terminate idle", err.Error())
				return err
			}
			return nil
		case <-reset:
			closeChannel()
		}
	}
}

func (m *MailProviderImap) selectMailBox() error {
	if m.imapClient.Mailbox != nil && m.imapClient.Mailbox.Name == mailBox {
		return nil
	}

	_, err := m.imapClient.Select(mailBox, false)
	if err != nil {
		m.log.Error("MailProviderImap.selectMailBox: Error on select", mailBox)
		return err
	}
	return nil
}

// checkConnection if is connected return nil or try to connect
func (m *MailProviderImap) checkConnection() error {
	if m.imapClient != nil && m.imapClient.State != imap.LogoutState {
		m.log.Debug("MailProviderImap.CheckConnection: Connection state", m.imapClient.State)
		return nil
	}

	var err error

	//Start connection with server
	if strings.HasSuffix(m.cfg.ImapServer, ":993") {
		m.log.Debug("MailProviderImap.CheckConnection: DialTLS")
		m.imapClient, err = client.DialTLS(m.cfg.ImapServer, nil)
	} else {
		m.log.Debug("MailProviderImap.CheckConnection: Dial")
		m.imapClient, err = client.Dial(m.cfg.ImapServer)
	}

	if err != nil {
		m.log.Error("MailProviderImap.CheckConnection: Unable to connect:", m.cfg.ImapServer)
		return err
	}

	if m.debug {
		m.imapClient.SetDebug(m.log)
	}

	// Max timeout awaiting a command
	m.imapClient.Timeout = time.Minute * 3

	if *m.cfg.StartTLS {
		starttls, err := m.imapClient.SupportStartTLS()
		if err != nil {
			return err
		}

		if starttls {
			m.log.Debug("MailProviderImap.CheckConnection:StartTLS")
			var tconfig tls.Config
			if *m.cfg.TLSAcceptAllCerts {
				tconfig.InsecureSkipVerify = true
			}
			err = m.imapClient.StartTLS(&tconfig)
			if err != nil {
				return err
			}
		}
	}

	m.log.Infof("Connected with %q\n", m.cfg.ImapServer)

	err = m.imapClient.Login(m.cfg.Address, m.cfg.Password)
	if err != nil {
		m.log.Error("MailProviderImap.CheckConnection: Unable to login:", m.cfg.Address)
		return err
	}

	if err = m.selectMailBox(); err != nil {
		return err
	}

	idleClient := idle.NewClient(m.imapClient)

	m.idle, err = idleClient.SupportIdle()
	if err != nil {
		m.idle = false
		m.log.Error("MailProviderImap.CheckConnection: Error on check idle support")
		return err
	}

	return nil
}

// Terminate imap connection
func (m *MailProviderImap) Terminate() error {
	if m.imapClient != nil {
		m.log.Info("MailProviderImap.Terminate Logout")
		if err := m.imapClient.Logout(); err != nil {
			m.log.Error("MailProviderImap.Terminate Error:", err.Error())
			return err
		}
	}

	return nil
}
<commit_msg>remove mutex mail_provider<commit_after>package mmail

import (
	"crypto/tls"
	"net/mail"
	"strings"
	"time"

	"github.com/emersion/go-imap"
	idle "github.com/emersion/go-imap-idle"
	"github.com/emersion/go-imap/client"
	"github.com/rodcorsi/mattermail/model"
)

// MailProviderImap implements MailProvider using imap
type MailProviderImap struct {
	imapClient *client.Client
	cfg        *model.Email
	log        Logger
	idle       bool
	debug      bool
}

const mailBox = "INBOX"

// NewMailProviderImap creates a new MailProviderImap implementing MailProvider
func NewMailProviderImap(cfg *model.Email, log Logger, debug bool) *MailProviderImap {
	return &MailProviderImap{
		cfg:   cfg,
		log:   log,
		debug: debug,
	}
}

// CheckNewMessage gets new email from server
func (m *MailProviderImap) CheckNewMessage(handler MailHandler) error {
	m.log.Debug("MailProviderImap.CheckNewMessage")

	if err := m.checkConnection(); err != nil {
		return err
	}

	if err := m.selectMailBox(); err != nil {
		return err
	}

	criteria := &imap.SearchCriteria{
		WithoutFlags: []string{imap.SeenFlag},
	}

	uid, err := m.imapClient.UidSearch(criteria)
	if err != nil {
		m.log.Debug("MailProviderImap.CheckNewMessage: Error UIDSearch")
		return err
	}

	if len(uid) == 0 {
		m.log.Debug("MailProviderImap.CheckNewMessage: No new messages")
		return nil
	}

	m.log.Debugf("MailProviderImap.CheckNewMessage: found %v uid", len(uid))

	seqset := &imap.SeqSet{}
	seqset.AddNum(uid...)

	messages := make(chan *imap.Message, len(uid))
	done := make(chan error, 1)
	go func() {
		done <- m.imapClient.UidFetch(seqset, []string{imap.EnvelopeMsgAttr, "BODY[]"}, messages)
	}()

	emailPosted := make(map[uint32]bool)
	for _, v := range uid {
		emailPosted[v] = false
	}

	for imapMsg := range messages {
		m.log.Debug("MailProviderImap.CheckNewMessage: PostMail uid:", imapMsg.Uid)
		if emailPosted[imapMsg.Uid] {
			m.log.Debug("MailProviderImap.CheckNewMessage: Email was posted uid:", imapMsg.Uid)
			continue
		}

		r := imapMsg.GetBody("BODY[]")
		if r == nil {
			m.log.Error("MailProviderImap.CheckNewMessage: message.GetBody(BODY[]) returns nil")
			continue
		}

		msg, err := mail.ReadMessage(r)
		if err != nil {
			m.log.Error("MailProviderImap.CheckNewMessage: Error on parse imap/message to mail/message")
			continue
		}

		if err := handler(msg); err != nil {
			m.log.Debug("MailProviderImap.CheckNewMessage: Error handler:", err.Error())
			continue
		} else {
			emailPosted[imapMsg.Uid] = true
		}
	}

	// Check command completion status
	if err := <-done; err != nil {
		m.log.Error("MailProviderImap.CheckNewMessage: Error on terminate fetch command")
		return err
	}

	errorset := &imap.SeqSet{}
	for k, posted := range emailPosted {
		if !posted {
			errorset.AddNum(k)
		}
	}

	if errorset.Empty() {
		return nil
	}

	// Mark all valid messages as read
	err = m.imapClient.UidStore(errorset, imap.RemoveFlags, []interface{}{imap.SeenFlag}, nil)
	if err != nil {
		m.log.Error("MailProviderImap.CheckNewMessage: Error UIDStore UNSEEN")
		return err
	}

	return nil
}

// WaitNewMessage waits for a new message (idle or time.Sleep)
func (m *MailProviderImap) WaitNewMessage(timeout int) error {
	m.log.Debug("MailProviderImap.WaitNewMessage")

	// Idle mode
	if err := m.checkConnection(); err != nil {
		return err
	}

	m.log.Debug("MailProviderImap.WaitNewMessage: idle mode:", m.idle)

	if !m.idle {
		time.Sleep(time.Second * time.Duration(timeout))
		return nil
	}

	if err := m.selectMailBox(); err != nil {
		return err
	}

	idleClient := idle.NewClient(m.imapClient)

	// Create a channel to receive mailbox updates
	statuses := make(chan *imap.MailboxStatus)
	m.imapClient.MailboxUpdates = statuses

	stop := make(chan struct{})
	done := make(chan error, 1)
	go func() {
		done <- idleClient.Idle(stop)
	}()

	reset := time.After(time.Second * time.Duration(timeout))

	closed := false
	closeChannel := func() {
		if !closed {
			close(stop)
			closed = true
		}
	}

	for {
		select {
		case status := <-statuses:
			m.log.Debug("MailProviderImap.WaitNewMessage: New mailbox status:", status)
			closeChannel()

		case err := <-done:
			if err != nil {
				m.log.Error("MailProviderImap.WaitNewMessage: Error on terminate idle", err.Error())
				return err
			}
			return nil
		case <-reset:
			closeChannel()
		}
	}
}

func (m *MailProviderImap) selectMailBox() error {
	if m.imapClient.Mailbox != nil && m.imapClient.Mailbox.Name == mailBox {
		return nil
	}

	_, err := m.imapClient.Select(mailBox, false)
	if err != nil {
		m.log.Error("MailProviderImap.selectMailBox: Error on select", mailBox)
		return err
	}
	return nil
}

// checkConnection if is connected return nil or try to connect
func (m *MailProviderImap) checkConnection() error {
	if m.imapClient != nil && (m.imapClient.State == imap.AuthenticatedState || m.imapClient.State == imap.SelectedState) {
		m.log.Debug("MailProviderImap.CheckConnection: Connection state", m.imapClient.State)
		return nil
	}

	var err error

	//Start connection with server
	if strings.HasSuffix(m.cfg.ImapServer, ":993") {
		m.log.Debug("MailProviderImap.CheckConnection: DialTLS")
		m.imapClient, err = client.DialTLS(m.cfg.ImapServer, nil)
	} else {
		m.log.Debug("MailProviderImap.CheckConnection: Dial")
		m.imapClient, err = client.Dial(m.cfg.ImapServer)
	}

	if err != nil {
		m.log.Error("MailProviderImap.CheckConnection: Unable to connect:", m.cfg.ImapServer)
		return err
	}

	if m.debug {
		m.imapClient.SetDebug(m.log)
	}

	// Max timeout awaiting a command
	m.imapClient.Timeout = time.Minute * 3

	if *m.cfg.StartTLS {
		starttls, err := m.imapClient.SupportStartTLS()
		if err != nil {
			return err
		}

		if starttls {
			m.log.Debug("MailProviderImap.CheckConnection:StartTLS")
			var tconfig tls.Config
			if *m.cfg.TLSAcceptAllCerts {
				tconfig.InsecureSkipVerify = true
			}
			err = m.imapClient.StartTLS(&tconfig)
			if err != nil {
				return err
			}
		}
	}

	m.log.Infof("Connected with %q\n", m.cfg.ImapServer)

	err = m.imapClient.Login(m.cfg.Address, m.cfg.Password)
	if err != nil {
		m.log.Error("MailProviderImap.CheckConnection: Unable to login:", m.cfg.Address)
		return err
	}

	if err = m.selectMailBox(); err != nil {
		return err
	}

	idleClient := idle.NewClient(m.imapClient)

	m.idle, err = idleClient.SupportIdle()
	if err != nil {
		m.idle = false
		m.log.Error("MailProviderImap.CheckConnection: Error on check idle support")
		return err
	}

	return nil
}

// Terminate imap connection
func (m *MailProviderImap) Terminate() error {
	if m.imapClient != nil {
		m.log.Info("MailProviderImap.Terminate Logout")
		if err := m.imapClient.Logout(); err != nil {
			m.log.Error("MailProviderImap.Terminate Error:", err.Error())
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"strings"
	"sync"

	docker "github.com/fsouza/go-dockerclient"
)

type Gateway struct {
	Destinations map[string]DestinationMap
	*sync.Mutex
}

func NewGateway() *Gateway {
	return &Gateway{
		map[string]DestinationMap{},
		&sync.Mutex{},
	}
}

func (gw *Gateway) fetchDomain(c *docker.Container) string {
	for _, v := range c.Config.Env {
		if strings.Contains(v, "APP_DOMAIN=") {
			return strings.Replace(v, "APP_DOMAIN=", "", 1)
		}
	}

	return c.ID
}

func (gw *Gateway) Add(container *docker.Container) error {
	log.Println("Adding container:", container.ID)

	key := gw.fetchDomain(container)

	if gw.Destinations[key][container.ID] != nil {
		return fmt.Errorf("Destination alreaady exists!")
	}

	if len(container.Config.ExposedPorts) == 0 {
		log.Printf("Container %s does not have any exposed ports\n", container.ID)
		return nil
	}

	dest, err := NewDestination(container)
	if err != nil {
		return err
	}

	gw.Lock()
	defer gw.Unlock()

	if gw.Destinations[key] == nil {
		gw.Destinations[key] = DestinationMap{}
	}

	gw.Destinations[key][container.ID] = dest
	return nil
}

func (gw *Gateway) Remove(container *docker.Container) error {
	log.Println("Removing container:", container.ID)
	key := gw.fetchDomain(container)

	if len(gw.Destinations[key]) == 0 {
		return nil
	}

	gw.Lock()
	defer gw.Unlock()

	delete(gw.Destinations[key], container.ID)
	return nil
}

func (gw *Gateway) Find(host string) *Destination {
	if len(gw.Destinations[host]) == 0 {
		return nil
	}

	list := []*Destination{}
	for _, dst := range gw.Destinations[host] {
		list = append(list, dst)
	}

	return list[rand.Intn(len(list))]
}

func (gw *Gateway) Handle(w http.ResponseWriter, r *http.Request) {
	destination := gw.Find(r.Host)

	log.Printf("Request method=%s host=%s path=%s -> %s\n", r.Method, r.Host, r.RequestURI, destination)

	if destination == nil {
		http.Error(w, "No route", http.StatusBadGateway)
		return
	}

	destination.proxy.ServeHTTP(w, r)
}

func (gw *Gateway) RenderDestinations(w http.ResponseWriter, r *http.Request) {
	result := map[string][]string{}

	for k, dstMap := range gw.Destinations {
		for _, dst := range dstMap {
			result[k] = append(result[k], dst.String())
		}
	}

	data, _ := json.Marshal(result)
	fmt.Fprintf(w, "%s", data)
}

func (gw *Gateway) Start(bind string) error {
	log.Printf("Starting gateway server on http://%s\n", bind)

	http.HandleFunc("/_destinations", gw.RenderDestinations)
	http.HandleFunc("/", gw.Handle)
	return http.ListenAndServe(bind, nil)
}
<commit_msg>Use DOMAIN var instead of APP_DOMAIN<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"strings"
	"sync"

	docker "github.com/fsouza/go-dockerclient"
)

type Gateway struct {
	Destinations map[string]DestinationMap
	*sync.Mutex
}

func NewGateway() *Gateway {
	return &Gateway{
		map[string]DestinationMap{},
		&sync.Mutex{},
	}
}

func (gw *Gateway) fetchDomain(c *docker.Container) string {
	for _, v := range c.Config.Env {
		if strings.Contains(v, "DOMAIN=") {
			return strings.Replace(v, "DOMAIN=", "", 1)
		}
	}

	return c.ID
}

func (gw *Gateway) Add(container *docker.Container) error {
	log.Println("Adding container:", container.ID)

	key := gw.fetchDomain(container)

	if gw.Destinations[key][container.ID] != nil {
		return fmt.Errorf("Destination alreaady exists!")
	}

	if len(container.Config.ExposedPorts) == 0 {
		log.Printf("Container %s does not have any exposed ports\n", container.ID)
		return nil
	}

	dest, err := NewDestination(container)
	if err != nil {
		return err
	}

	gw.Lock()
	defer gw.Unlock()

	if gw.Destinations[key] == nil {
		gw.Destinations[key] = DestinationMap{}
	}

	gw.Destinations[key][container.ID] = dest
	return nil
}

func (gw *Gateway) Remove(container *docker.Container) error {
	log.Println("Removing container:", container.ID)
	key := gw.fetchDomain(container)

	if len(gw.Destinations[key]) == 0 {
		return nil
	}

	gw.Lock()
	defer gw.Unlock()

	delete(gw.Destinations[key], container.ID)
	return nil
}

func (gw *Gateway) Find(host string) *Destination {
	if len(gw.Destinations[host]) == 0 {
		return nil
	}

	list := []*Destination{}
	for _, dst := range gw.Destinations[host] {
		list = append(list, dst)
	}

	return list[rand.Intn(len(list))]
}

func (gw *Gateway) Handle(w http.ResponseWriter, r *http.Request) {
	destination := gw.Find(r.Host)

	log.Printf("Request method=%s host=%s path=%s -> %s\n", r.Method, r.Host, r.RequestURI, destination)

	if destination == nil {
		http.Error(w, "No route", http.StatusBadGateway)
		return
	}

	destination.proxy.ServeHTTP(w, r)
}

func (gw *Gateway) RenderDestinations(w http.ResponseWriter, r *http.Request) {
	result := map[string][]string{}

	for k, dstMap := range gw.Destinations {
		for _, dst := range dstMap {
			result[k] = append(result[k], dst.String())
		}
	}

	data, _ := json.Marshal(result)
	fmt.Fprintf(w, "%s", data)
}

func (gw *Gateway) Start(bind string) error {
	log.Printf("Starting gateway server on http://%s\n", bind)

	http.HandleFunc("/_destinations", gw.RenderDestinations)
	http.HandleFunc("/", gw.Handle)
	return http.ListenAndServe(bind, nil)
}
<|endoftext|>


<commit_before>package moneybird

import (
	"testing"
	"time"
)

// Because Moneybird schedules a background job when you create a new invoice, this test will fail when running too soon after a previous run.
func TestInvoiceGatewayListAndDelete(t *testing.T) {
	invoices, err := testClient.Invoice().List()
	if err != nil {
		t.Error(err)
	}

	for _, invoice := range invoices {
		err := testClient.Invoice().Delete(invoice)
		if err != nil {
			// let's ignore this error for now... (see func doc)
			if err.Error() == "moneybird: Sales invoice cannot be destroyed" {
				continue
			}

			t.Error(err)
		}
	}

}

func TestInvoiceGatewayCRUD(t *testing.T) {
	var err error
	// create contact
	contact := &Contact{
		Email:     "johndoe@email.com",
		FirstName: "John",
		LastName:  "Doe",
	}
	contact, err = testClient.Contact().Create(contact)
	if err != nil {
		t.Fatalf("ContactGateway.Create: %s", err)
	}

	// delete contact (deferred)
	defer func() {
		err = testClient.Contact().Delete(contact)
		if err != nil {
			t.Errorf("ContactGateway.Delete: %s", err)
		}
	}()

	gateway := testClient.Invoice()
	// create invoice
	invoice := &Invoice{
		ContactID:   contact.ID,
		InvoiceDate: time.Now().Format("2006-01-02"),
		Details: []*InvoiceDetails{
			&InvoiceDetails{
				Amount:      "1",
				Price:       "10.00",
				Description: "Test Service",
			},
		},
	}
	invoice, err = gateway.Create(invoice)
	if err != nil {
		t.Fatalf("InvoiceGateway.Create: %s", err) // abandon test if invoice creation fails
	}

	// update invoice
	invoice.Reference = "my-reference"
	invoice, err = gateway.Update(invoice)
	if err != nil {
		t.Errorf("InvoiceGateway.Update: %s", err)
	}

	if invoice.Reference != "my-reference" {
		t.Error("InvoiceGateway.Update: reference was not properly updated")
	}

	// get invoice
	invoice, err = gateway.Get(invoice.ID)
	if err != nil {
		t.Errorf("InvoiceGateway.Get: %s", err)
	}

	if invoice.Contact.ID != contact.ID {
		t.Errorf("InvoiceGateway.Get: invoice contact ID does not match, got %#v", invoice.Contact.ID)
	}

	//  create invoice sending (send invoice)
	err = testClient.InvoiceSending().Create(invoice, &InvoiceSending{
		DeliveryMethod: "Manual",
	})
	if err != nil {
		t.Fatalf("InvoiceSendingGateway.Create: %s", err)
	}

	// create invoice payment (mark invoice as paid)
	err = testClient.InvoicePayment().Create(invoice, &InvoicePayment{
		Price:       invoice.TotalUnpaid,
		PriceBase:   invoice.TotalUnpaid,
		PaymentDate: time.Now().Format("2006-01-02"),
	})
	if err != nil {
		t.Fatalf("InvoicePaymentGateway.Create: %s ", err)
	}

	// create invoice note
	note, err := testClient.InvoiceNote().Create(invoice, &InvoiceNote{
		Note: "my note",
	})
	if err != nil {
		t.Fatalf("InvoiceNoteGateway.Create: %s", err)
	}

	if note.Note != "my note" {
		t.Errorf("InvoiceNoteGateway.Create: note does not match input string. Got %#v", note.Note)
	}

	// delete invoice note
	err = testClient.InvoiceNote().Delete(invoice, note)
	if err != nil {
		t.Errorf("InvoiceNoteGateway.Delete: %s", err)
	}

}
<commit_msg>Set PriceBase as TotalUnpaidBase<commit_after>package moneybird

import (
	"testing"
	"time"
)

// Because Moneybird schedules a background job when you create a new invoice, this test will fail when running too soon after a previous run.
func TestInvoiceGatewayListAndDelete(t *testing.T) {
	invoices, err := testClient.Invoice().List()
	if err != nil {
		t.Error(err)
	}

	for _, invoice := range invoices {
		err := testClient.Invoice().Delete(invoice)
		if err != nil {
			// let's ignore this error for now... (see func doc)
			if err.Error() == "moneybird: Sales invoice cannot be destroyed" {
				continue
			}

			t.Error(err)
		}
	}

}

func TestInvoiceGatewayCRUD(t *testing.T) {
	var err error
	// create contact
	contact := &Contact{
		Email:     "johndoe@email.com",
		FirstName: "John",
		LastName:  "Doe",
	}
	contact, err = testClient.Contact().Create(contact)
	if err != nil {
		t.Fatalf("ContactGateway.Create: %s", err)
	}

	// delete contact (deferred)
	defer func() {
		err = testClient.Contact().Delete(contact)
		if err != nil {
			t.Errorf("ContactGateway.Delete: %s", err)
		}
	}()

	gateway := testClient.Invoice()
	// create invoice
	invoice := &Invoice{
		ContactID:   contact.ID,
		InvoiceDate: time.Now().Format("2006-01-02"),
		Details: []*InvoiceDetails{
			&InvoiceDetails{
				Amount:      "1",
				Price:       "10.00",
				Description: "Test Service",
			},
		},
	}
	invoice, err = gateway.Create(invoice)
	if err != nil {
		t.Fatalf("InvoiceGateway.Create: %s", err) // abandon test if invoice creation fails
	}

	// update invoice
	invoice.Reference = "my-reference"
	invoice, err = gateway.Update(invoice)
	if err != nil {
		t.Errorf("InvoiceGateway.Update: %s", err)
	}

	if invoice.Reference != "my-reference" {
		t.Error("InvoiceGateway.Update: reference was not properly updated")
	}

	// get invoice
	invoice, err = gateway.Get(invoice.ID)
	if err != nil {
		t.Errorf("InvoiceGateway.Get: %s", err)
	}

	if invoice.Contact.ID != contact.ID {
		t.Errorf("InvoiceGateway.Get: invoice contact ID does not match, got %#v", invoice.Contact.ID)
	}

	//  create invoice sending (send invoice)
	err = testClient.InvoiceSending().Create(invoice, &InvoiceSending{
		DeliveryMethod: "Manual",
	})
	if err != nil {
		t.Fatalf("InvoiceSendingGateway.Create: %s", err)
	}

	// create invoice payment (mark invoice as paid)
	err = testClient.InvoicePayment().Create(invoice, &InvoicePayment{
		Price:       invoice.TotalUnpaid,
		PriceBase:   invoice.TotalUnpaidBase,
		PaymentDate: time.Now().Format("2006-01-02"),
	})
	if err != nil {
		t.Fatalf("InvoicePaymentGateway.Create: %s ", err)
	}

	// create invoice note
	note, err := testClient.InvoiceNote().Create(invoice, &InvoiceNote{
		Note: "my note",
	})
	if err != nil {
		t.Fatalf("InvoiceNoteGateway.Create: %s", err)
	}

	if note.Note != "my note" {
		t.Errorf("InvoiceNoteGateway.Create: note does not match input string. Got %#v", note.Note)
	}

	// delete invoice note
	err = testClient.InvoiceNote().Delete(invoice, note)
	if err != nil {
		t.Errorf("InvoiceNoteGateway.Delete: %s", err)
	}

}
<|endoftext|>