<commit_before>package compiler

import (
	"bytes"
	"fmt"
	"log"
	"strings"
	"unicode/utf8"

	"github.com/wellington/sass/ast"
	"github.com/wellington/sass/parser"
	"github.com/wellington/sass/token"
)

type Context struct {
	buf      *bytes.Buffer
	fileName *ast.Ident
	// Records the current level of selectors
	// Each time a selector is encountered, increase
	// by one. Each time a block is exited, remove
	// the last selector
	sels      [][]*ast.Ident
	firstRule bool
	level     int
	printers  map[ast.Node]func(*Context, ast.Node)

	typ Scope
}

// stores types and values with scoping. To remove a scope
// use CloseScope(), to open a new Scope use OpenScope().
type Scope interface {
	// OpenScope() Typ
	// CloseScope() Typ
	Get(string) interface{}
	Set(string, interface{})
	// Number of Rules in this scope
	RuleAdd(*ast.RuleSpec)
	RuleLen() int
}

var (
	empty = new(emptyTyp)
)

type emptyTyp struct{}

func (*emptyTyp) Get(name string) interface{} {
	return nil
}

func (*emptyTyp) Set(name string, _ interface{}) {}

func (*emptyTyp) RuleLen() int { return 0 }

func (*emptyTyp) RuleAdd(*ast.RuleSpec) {}

type valueScope struct {
	Scope
	rules []*ast.RuleSpec
	m     map[string]interface{}
}

func (t *valueScope) RuleAdd(rule *ast.RuleSpec) {
	t.rules = append(t.rules, rule)
}

func (t *valueScope) RuleLen() int {
	return len(t.rules)
}

func (t *valueScope) Get(name string) interface{} {
	val, ok := t.m[name]
	if ok {
		return val
	}
	return t.Scope.Get(name)
}

func (t *valueScope) Set(name string, v interface{} /* should this just be string? */) {
	t.m[name] = v
}

func NewTyp() Scope {
	return &valueScope{Scope: empty, m: make(map[string]interface{})}
}

func NewScope(s Scope) Scope {
	return &valueScope{Scope: s, m: make(map[string]interface{})}
}

func CloseScope(typ Scope) Scope {
	s, ok := typ.(*valueScope)
	if !ok {
		return typ
	}
	return s.Scope
}

func fileRun(path string) (string, error) {
	ctx := &Context{}
	ctx.Init()
	out, err := ctx.Run(path)
	if err != nil {
		log.Fatal(err)
	}
	return out, err
}

// Run takes a single Sass file and compiles it
func (ctx *Context) Run(path string) (string, error) {
	// func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
	fset := token.NewFileSet()
	pf, err := parser.ParseFile(fset, path, nil, parser.ParseComments|parser.Trace)
	if err != nil {
		return "", err
	}

	ast.Walk(ctx, pf)
	lr, _ := utf8.DecodeLastRune(ctx.buf.Bytes())
	_ = lr
	if ctx.buf.Len() > 0 && lr != '\n' {
		ctx.out("\n")
	}
	// ctx.printSels(pf.Decls)
	return ctx.buf.String(), nil
}

// out prints with the appropriate indention, selectors always have indent
// 0
func (ctx *Context) out(v string) {
	fr, _ := utf8.DecodeRuneInString(v)
	if fr == '\n' {
		fmt.Fprintf(ctx.buf, v)
		return
	}
	ws := []byte("                                              ")
	lvl := ctx.level
	if lvl > 1 {
		lvl = 1
	}
	format := append(ws[:lvl*2], "%s"...)
	fmt.Fprintf(ctx.buf, string(format), v)
}

func (ctx *Context) blockIntro() {

	ctx.firstRule = false
	if ctx.buf.Len() > 0 && ctx.level == 0 {
		ctx.out("\n\n")
	}

	// Will probably need better logic around this
	sels := strings.Join(ctx.combineSels(), ", ")
	ctx.out(fmt.Sprintf("%s {\n", sels))
}

func (ctx *Context) combineSels() []string {
	return walkSelectors(ctx.sels)
}

func walkSelectors(in [][]*ast.Ident) []string {
	if len(in) == 1 {
		ret := make([]string, len(in[0]))
		for i, ident := range in[0] {
			ret[i] = ident.String()
		}
		return ret
	}

	d := in[0]
	w := walkSelectors(in[1:])
	var ret []string
	for i := 0; i < len(d); i++ {
		for j := 0; j < len(w); j++ {
			ret = append(ret, d[i].String()+" "+w[j])
		}
	}
	return ret
}

func (ctx *Context) blockOutro() {
	var skipParen bool
	if len(ctx.sels) > 0 {
		ctx.sels = ctx.sels[:len(ctx.sels)-1]
	}
	if ctx.firstRule {
		return
	}

	ctx.firstRule = true
	if !skipParen {
		fmt.Fprintf(ctx.buf, " }")
		// ctx.out(" }")
	}
	// fmt.Fprintf(ctx.buf, " }")
}

func (ctx *Context) Visit(node ast.Node) ast.Visitor {

	var key ast.Node
	switch v := node.(type) {
	case *ast.BlockStmt:
		if ctx.typ.RuleLen() > 0 {
			ctx.level = ctx.level + 1

			// fmt.Println("closing because of", ctx.typ.(*valueScope).rules)
			// Close the previous spec if any rules exist in it
			fmt.Fprintf(ctx.buf, " }\n")
		}
		ctx.typ = NewScope(ctx.typ)
		ctx.firstRule = true
		for _, node := range v.List {
			ast.Walk(ctx, node)
		}
		if ctx.level > 0 {
			ctx.level = ctx.level - 1
		}
		ctx.typ = CloseScope(ctx.typ)
		ctx.blockOutro()
		ctx.firstRule = true
		// ast.Walk(ctx, v.List)
		// fmt.Fprintf(ctx.buf, "}")
		return nil
	case *ast.SelDecl:
		key = selDecl
	case *ast.File, *ast.GenDecl, *ast.Value:
		// Nothing to print for these
	case *ast.Ident:
		// The first IDENT is always the filename, just preserve
		// it somewhere
		key = ident
	case *ast.PropValueSpec:
		key = propSpec
	case *ast.DeclStmt:
		key = declStmt
	case *ast.ValueSpec:
		key = valueSpec
	case *ast.RuleSpec:
		key = ruleSpec
	case *ast.SelStmt:
		// We will need to combine parent selectors
		// while printing these
		key = selStmt
		// Nothing to do
	case *ast.CommentGroup:
		key = comments
	case *ast.Comment:
		key = comment
	case *ast.BasicLit:
		key = expr
	case nil:
		return ctx
	default:
		fmt.Printf("add printer for: %T\n", v)
		fmt.Printf("% #v\n", v)
	}
	ctx.printers[key](ctx, node)
	return ctx
}

var (
	ident     *ast.Ident
	expr      ast.Expr
	declStmt  *ast.DeclStmt
	valueSpec *ast.ValueSpec
	ruleSpec  *ast.RuleSpec
	selDecl   *ast.SelDecl
	selStmt   *ast.SelStmt
	propSpec  *ast.PropValueSpec
	typeSpec  *ast.TypeSpec
	comments  *ast.CommentGroup
	comment   *ast.Comment
)

func (ctx *Context) Init() {
	ctx.buf = bytes.NewBuffer(nil)
	ctx.printers = make(map[ast.Node]func(*Context, ast.Node))
	ctx.printers[valueSpec] = visitValueSpec

	ctx.printers[ident] = printIdent
	ctx.printers[declStmt] = printDecl
	ctx.printers[ruleSpec] = printRuleSpec
	ctx.printers[selDecl] = printSelDecl
	ctx.printers[selStmt] = printSelStmt
	ctx.printers[propSpec] = printPropValueSpec
	ctx.printers[expr] = printExpr
	ctx.printers[comments] = printComments
	ctx.printers[comment] = printComment
	ctx.typ = NewScope(empty)
	// ctx.printers[typeSpec] = visitTypeSpec
	// assign printers
}

func printComments(ctx *Context, n ast.Node) {
	cmts := n.(*ast.CommentGroup)

	for _, cmt := range cmts.List {
		printComment(ctx, cmt)
	}
}

func printComment(ctx *Context, n ast.Node) {
	cmt := n.(*ast.Comment)
	ctx.out(cmt.Text)
}

func printExpr(ctx *Context, n ast.Node) {
	switch v := n.(type) {
	case *ast.File:
	case *ast.BasicLit:
		fmt.Fprintf(ctx.buf, "%s;", v.Value)
	case *ast.Value:
	default:
		// fmt.Printf("unmatched expr %T: % #v\n", v, v)
	}
}

func (ctx *Context) storeSelector(idents []*ast.Ident) {
	fmt.Printf("storeselector %q\n", idents)
	ctx.sels = append(ctx.sels, idents)
}

func printSelStmt(ctx *Context, n ast.Node) {
	stmt := n.(*ast.SelStmt)
	ctx.storeSelector(stmt.Names)
}

func printSelDecl(ctx *Context, n ast.Node) {
	decl := n.(*ast.SelDecl)
	ctx.storeSelector(decl.Names)
}

func printRuleSpec(ctx *Context, n ast.Node) {
	// Inspect the sel buffer and dump it
	// We'll also need to track what level was last dumped
	// so selectors don't get printed twice
	if ctx.firstRule {
		ctx.blockIntro()
	} else {
		ctx.out("\n")
	}
	spec := n.(*ast.RuleSpec)
	ctx.typ.RuleAdd(spec)
	ctx.out(fmt.Sprintf("  %s: ", spec.Name))
}

func printPropValueSpec(ctx *Context, n ast.Node) {
	spec := n.(*ast.PropValueSpec)
	fmt.Fprintf(ctx.buf, spec.Name.String()+";")
}

// Variable declarations
func visitValueSpec(ctx *Context, n ast.Node) {
	spec := n.(*ast.ValueSpec)

	names := make([]string, len(spec.Names))
	for i, nm := range spec.Names {
		names[i] = nm.Name
	}

	if len(spec.Values) > 0 {
		expr := simplifyExprs(ctx, spec.Values)
		fmt.Printf("setting %12s: %-10v\n", names[0], expr)
		ctx.typ.Set(names[0], expr)
	} else {
		fmt.Fprintf(ctx.buf, "%s;", ctx.typ.Get(names[0]))
	}
}

func simplifyExprs(ctx *Context, exprs []ast.Expr) string {
	var sums []string
	for _, expr := range exprs {
		// fmt.Printf("expr: % #v\n", expr)
		switch v := expr.(type) {
		case *ast.Value:
			// if v.Obj == nil {
			s, ok := ctx.typ.Get(v.Name).(string)
			if ok {
				sums = append(sums, s)
			} else {
				sums = append(sums, v.Name)
			}
			continue
			// }
			// switch v.Obj.Kind {
			// case ast.Var:
			// 	s, ok := ctx.typ.Get(v.Obj.Name).(string)
			// 	if ok {
			// 		sums = append(sums, s)
			// 	}
			// default:
			// 	fmt.Println("unsupported obj kind")
			// }
		case *ast.Ident:
			fmt.Printf("Ident % #v\n", v)
			if v.Obj == nil {
				sums = append(sums, v.Name)
				continue
			}
			switch v.Obj.Kind {
			case ast.Var:
				s, ok := ctx.typ.Get(v.Obj.Name).(string)
				if ok {
					sums = append(sums, s)
				}
			default:
				fmt.Println("unsupported obj kind")
			}
		case *ast.BasicLit:
			fmt.Printf("BasicLit % #v\n", v)
			switch v.Kind {
			case token.VAR:
				s, ok := ctx.typ.Get(v.Value).(string)
				if ok {
					sums = append(sums, s)
				}
			default:
				sums = append(sums, v.Value)
			}
		default:
			log.Fatalf("unhandled expr: % #v\n", v)
		}
	}
	return strings.Join(sums, " ")
}

func printDecl(ctx *Context, node ast.Node) {
	// I think... nothing to print we'll see
}

func printIdent(ctx *Context, node ast.Node) {
	// don't print these
	ident := node.(*ast.Ident)
	resolved := ctx.typ.Get(ident.String())
	if resolved != nil {
		fmt.Fprint(ctx.buf, resolved.(string), ";")
	} else {
		fmt.Fprint(ctx.buf, ident, ";")
	}
}
<commit_msg>improve } placement<commit_after>package compiler

import (
	"bytes"
	"fmt"
	"log"
	"strings"
	"unicode/utf8"

	"github.com/wellington/sass/ast"
	"github.com/wellington/sass/parser"
	"github.com/wellington/sass/token"
)

type Context struct {
	buf      *bytes.Buffer
	fileName *ast.Ident
	// Records the current level of selectors
	// Each time a selector is encountered, increase
	// by one. Each time a block is exited, remove
	// the last selector
	sels      [][]*ast.Ident
	firstRule bool
	level     int
	printers  map[ast.Node]func(*Context, ast.Node)

	typ Scope
}

// stores types and values with scoping. To remove a scope
// use CloseScope(), to open a new Scope use OpenScope().
type Scope interface {
	// OpenScope() Typ
	// CloseScope() Typ
	Get(string) interface{}
	Set(string, interface{})
	// Number of Rules in this scope
	RuleAdd(*ast.RuleSpec)
	RuleLen() int
}

var (
	empty = new(emptyTyp)
)

type emptyTyp struct{}

func (*emptyTyp) Get(name string) interface{} {
	return nil
}

func (*emptyTyp) Set(name string, _ interface{}) {}

func (*emptyTyp) RuleLen() int { return 0 }

func (*emptyTyp) RuleAdd(*ast.RuleSpec) {}

type valueScope struct {
	Scope
	rules []*ast.RuleSpec
	m     map[string]interface{}
}

func (t *valueScope) RuleAdd(rule *ast.RuleSpec) {
	t.rules = append(t.rules, rule)
}

func (t *valueScope) RuleLen() int {
	return len(t.rules)
}

func (t *valueScope) Get(name string) interface{} {
	val, ok := t.m[name]
	if ok {
		return val
	}
	return t.Scope.Get(name)
}

func (t *valueScope) Set(name string, v interface{} /* should this just be string? */) {
	t.m[name] = v
}

func NewTyp() Scope {
	return &valueScope{Scope: empty, m: make(map[string]interface{})}
}

func NewScope(s Scope) Scope {
	return &valueScope{Scope: s, m: make(map[string]interface{})}
}

func CloseScope(typ Scope) Scope {
	s, ok := typ.(*valueScope)
	if !ok {
		return typ
	}
	return s.Scope
}

func fileRun(path string) (string, error) {
	ctx := &Context{}
	ctx.Init()
	out, err := ctx.Run(path)
	if err != nil {
		log.Fatal(err)
	}
	return out, err
}

// Run takes a single Sass file and compiles it
func (ctx *Context) Run(path string) (string, error) {
	// func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
	fset := token.NewFileSet()
	pf, err := parser.ParseFile(fset, path, nil, parser.ParseComments|parser.Trace)
	if err != nil {
		return "", err
	}

	ast.Walk(ctx, pf)
	lr, _ := utf8.DecodeLastRune(ctx.buf.Bytes())
	_ = lr
	if ctx.buf.Len() > 0 && lr != '\n' {
		ctx.out("\n")
	}
	// ctx.printSels(pf.Decls)
	return ctx.buf.String(), nil
}

// out prints with the appropriate indention, selectors always have indent
// 0
func (ctx *Context) out(v string) {
	fr, _ := utf8.DecodeRuneInString(v)
	if fr == '\n' {
		fmt.Fprintf(ctx.buf, v)
		return
	}
	ws := []byte("                                              ")
	lvl := ctx.level
	if lvl > 1 {
		lvl = 1
	}
	format := append(ws[:lvl*2], "%s"...)
	fmt.Fprintf(ctx.buf, string(format), v)
}

func (ctx *Context) blockIntro() {

	ctx.firstRule = false
	if ctx.buf.Len() > 0 && ctx.level == 0 {
		ctx.out("\n\n")
	}

	// Will probably need better logic around this
	sels := strings.Join(ctx.combineSels(), ", ")
	ctx.out(fmt.Sprintf("%s {\n", sels))
}

func (ctx *Context) combineSels() []string {
	return walkSelectors(ctx.sels)
}

func walkSelectors(in [][]*ast.Ident) []string {
	if len(in) == 1 {
		ret := make([]string, len(in[0]))
		for i, ident := range in[0] {
			ret[i] = ident.String()
		}
		return ret
	}

	d := in[0]
	w := walkSelectors(in[1:])
	var ret []string
	for i := 0; i < len(d); i++ {
		for j := 0; j < len(w); j++ {
			ret = append(ret, d[i].String()+" "+w[j])
		}
	}
	return ret
}

func (ctx *Context) blockOutro() {
	var skipParen bool
	if len(ctx.sels) > 0 {
		ctx.sels = ctx.sels[:len(ctx.sels)-1]
	}
	if ctx.firstRule {
		return
	}
	_ = skipParen
	ctx.firstRule = true
	// if !skipParen {
	fmt.Fprintf(ctx.buf, " }")
	// }
}

func (ctx *Context) Visit(node ast.Node) ast.Visitor {

	var key ast.Node
	switch v := node.(type) {
	case *ast.BlockStmt:
		if ctx.typ.RuleLen() > 0 && !ctx.firstRule {
			ctx.level = ctx.level + 1
			fmt.Fprintf(ctx.buf, " }\n")
		}
		ctx.typ = NewScope(ctx.typ)
		ctx.firstRule = true
		for _, node := range v.List {
			ast.Walk(ctx, node)
		}
		if ctx.level > 0 {
			ctx.level = ctx.level - 1
		}
		ctx.typ = CloseScope(ctx.typ)
		ctx.blockOutro()
		ctx.firstRule = true
		// ast.Walk(ctx, v.List)
		// fmt.Fprintf(ctx.buf, "}")
		return nil
	case *ast.SelDecl:
		key = selDecl
	case *ast.File, *ast.GenDecl, *ast.Value:
		// Nothing to print for these
	case *ast.Ident:
		// The first IDENT is always the filename, just preserve
		// it somewhere
		key = ident
	case *ast.PropValueSpec:
		key = propSpec
	case *ast.DeclStmt:
		key = declStmt
	case *ast.ValueSpec:
		key = valueSpec
	case *ast.RuleSpec:
		key = ruleSpec
	case *ast.SelStmt:
		// We will need to combine parent selectors
		// while printing these
		key = selStmt
		// Nothing to do
	case *ast.CommentGroup:
		key = comments
	case *ast.Comment:
		key = comment
	case *ast.BasicLit:
		key = expr
	case nil:
		return ctx
	default:
		fmt.Printf("add printer for: %T\n", v)
		fmt.Printf("% #v\n", v)
	}
	ctx.printers[key](ctx, node)
	return ctx
}

var (
	ident     *ast.Ident
	expr      ast.Expr
	declStmt  *ast.DeclStmt
	valueSpec *ast.ValueSpec
	ruleSpec  *ast.RuleSpec
	selDecl   *ast.SelDecl
	selStmt   *ast.SelStmt
	propSpec  *ast.PropValueSpec
	typeSpec  *ast.TypeSpec
	comments  *ast.CommentGroup
	comment   *ast.Comment
)

func (ctx *Context) Init() {
	ctx.buf = bytes.NewBuffer(nil)
	ctx.printers = make(map[ast.Node]func(*Context, ast.Node))
	ctx.printers[valueSpec] = visitValueSpec

	ctx.printers[ident] = printIdent
	ctx.printers[declStmt] = printDecl
	ctx.printers[ruleSpec] = printRuleSpec
	ctx.printers[selDecl] = printSelDecl
	ctx.printers[selStmt] = printSelStmt
	ctx.printers[propSpec] = printPropValueSpec
	ctx.printers[expr] = printExpr
	ctx.printers[comments] = printComments
	ctx.printers[comment] = printComment
	ctx.typ = NewScope(empty)
	// ctx.printers[typeSpec] = visitTypeSpec
	// assign printers
}

func printComments(ctx *Context, n ast.Node) {
	cmts := n.(*ast.CommentGroup)

	for _, cmt := range cmts.List {
		printComment(ctx, cmt)
	}
}

func printComment(ctx *Context, n ast.Node) {
	cmt := n.(*ast.Comment)
	ctx.out(cmt.Text)
}

func printExpr(ctx *Context, n ast.Node) {
	switch v := n.(type) {
	case *ast.File:
	case *ast.BasicLit:
		fmt.Fprintf(ctx.buf, "%s;", v.Value)
	case *ast.Value:
	default:
		// fmt.Printf("unmatched expr %T: % #v\n", v, v)
	}
}

func (ctx *Context) storeSelector(idents []*ast.Ident) {
	fmt.Printf("storeselector %q\n", idents)
	ctx.sels = append(ctx.sels, idents)
}

func printSelStmt(ctx *Context, n ast.Node) {
	stmt := n.(*ast.SelStmt)
	ctx.storeSelector(stmt.Names)
}

func printSelDecl(ctx *Context, n ast.Node) {
	decl := n.(*ast.SelDecl)
	ctx.storeSelector(decl.Names)
}

func printRuleSpec(ctx *Context, n ast.Node) {
	// Inspect the sel buffer and dump it
	// We'll also need to track what level was last dumped
	// so selectors don't get printed twice
	if ctx.firstRule {
		ctx.blockIntro()
	} else {
		ctx.out("\n")
	}
	spec := n.(*ast.RuleSpec)
	ctx.typ.RuleAdd(spec)
	ctx.out(fmt.Sprintf("  %s: ", spec.Name))
}

func printPropValueSpec(ctx *Context, n ast.Node) {
	spec := n.(*ast.PropValueSpec)
	fmt.Fprintf(ctx.buf, spec.Name.String()+";")
}

// Variable declarations
func visitValueSpec(ctx *Context, n ast.Node) {
	spec := n.(*ast.ValueSpec)

	names := make([]string, len(spec.Names))
	for i, nm := range spec.Names {
		names[i] = nm.Name
	}

	if len(spec.Values) > 0 {
		expr := simplifyExprs(ctx, spec.Values)
		fmt.Printf("setting %12s: %-10v\n", names[0], expr)
		ctx.typ.Set(names[0], expr)
	} else {
		fmt.Fprintf(ctx.buf, "%s;", ctx.typ.Get(names[0]))
	}
}

func simplifyExprs(ctx *Context, exprs []ast.Expr) string {
	var sums []string
	for _, expr := range exprs {
		// fmt.Printf("expr: % #v\n", expr)
		switch v := expr.(type) {
		case *ast.Value:
			// if v.Obj == nil {
			s, ok := ctx.typ.Get(v.Name).(string)
			if ok {
				sums = append(sums, s)
			} else {
				sums = append(sums, v.Name)
			}
			continue
			// }
			// switch v.Obj.Kind {
			// case ast.Var:
			// 	s, ok := ctx.typ.Get(v.Obj.Name).(string)
			// 	if ok {
			// 		sums = append(sums, s)
			// 	}
			// default:
			// 	fmt.Println("unsupported obj kind")
			// }
		case *ast.Ident:
			fmt.Printf("Ident % #v\n", v)
			if v.Obj == nil {
				sums = append(sums, v.Name)
				continue
			}
			switch v.Obj.Kind {
			case ast.Var:
				s, ok := ctx.typ.Get(v.Obj.Name).(string)
				if ok {
					sums = append(sums, s)
				}
			default:
				fmt.Println("unsupported obj kind")
			}
		case *ast.BasicLit:
			fmt.Printf("BasicLit % #v\n", v)
			switch v.Kind {
			case token.VAR:
				s, ok := ctx.typ.Get(v.Value).(string)
				if ok {
					sums = append(sums, s)
				}
			default:
				sums = append(sums, v.Value)
			}
		default:
			log.Fatalf("unhandled expr: % #v\n", v)
		}
	}
	return strings.Join(sums, " ")
}

func printDecl(ctx *Context, node ast.Node) {
	// I think... nothing to print we'll see
}

func printIdent(ctx *Context, node ast.Node) {
	// don't print these
	ident := node.(*ast.Ident)
	resolved := ctx.typ.Get(ident.String())
	if resolved != nil {
		fmt.Fprint(ctx.buf, resolved.(string), ";")
	} else {
		fmt.Fprint(ctx.buf, ident, ";")
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, 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 client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"path"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"

	"github.com/coreos/etcd/pkg/types"
)

var (
	defaultV2MembersPrefix = "/v2/members"
)

type Member struct {
	// ID is the unique identifier of this Member.
	ID string `json:"id"`

	// Name is a human-readable, non-unique identifier of this Member.
	Name string `json:"name"`

	// PeerURLs represents the HTTP(S) endpoints this Member uses to
	// participate in etcd's consensus protocol.
	PeerURLs []string `json:"peerURLs"`

	// ClientURLs represents the HTTP(S) endpoints on which this Member
	// serves it's client-facing APIs.
	ClientURLs []string `json:"clientURLs"`
}

type memberCollection []Member

func (c *memberCollection) UnmarshalJSON(data []byte) error {
	d := struct {
		Members []Member
	}{}

	if err := json.Unmarshal(data, &d); err != nil {
		return err
	}

	if d.Members == nil {
		*c = make([]Member, 0)
		return nil
	}

	*c = d.Members
	return nil
}

type memberCreateRequest struct {
	PeerURLs types.URLs
}

func (m *memberCreateRequest) MarshalJSON() ([]byte, error) {
	s := struct {
		PeerURLs []string `json:"peerURLs"`
	}{
		PeerURLs: make([]string, len(m.PeerURLs)),
	}

	for i, u := range m.PeerURLs {
		s.PeerURLs[i] = u.String()
	}

	return json.Marshal(&s)
}

// NewMembersAPI constructs a new MembersAPI that uses HTTP to
// interact with etcd's membership API.
func NewMembersAPI(c Client) MembersAPI {
	return &httpMembersAPI{
		client: c,
	}
}

type MembersAPI interface {
	// List enumerates the current cluster membership.
	List(ctx context.Context) ([]Member, error)

	// Add instructs etcd to accept a new Member into the cluster.
	Add(ctx context.Context, peerURL string) (*Member, error)

	// Remove demotes an existing Member out of the cluster.
	Remove(ctx context.Context, mID string) error
}

type httpMembersAPI struct {
	client httpClient
}

func (m *httpMembersAPI) List(ctx context.Context) ([]Member, error) {
	req := &membersAPIActionList{}
	resp, body, err := m.client.Do(ctx, req)
	if err != nil {
		return nil, err
	}

	if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
		return nil, err
	}

	var mCollection memberCollection
	if err := json.Unmarshal(body, &mCollection); err != nil {
		return nil, err
	}

	return []Member(mCollection), nil
}

func (m *httpMembersAPI) Add(ctx context.Context, peerURL string) (*Member, error) {
	urls, err := types.NewURLs([]string{peerURL})
	if err != nil {
		return nil, err
	}

	req := &membersAPIActionAdd{peerURLs: urls}
	resp, body, err := m.client.Do(ctx, req)
	if err != nil {
		return nil, err
	}

	if err := assertStatusCode(resp.StatusCode, http.StatusCreated, http.StatusConflict); err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusCreated {
		var merr membersError
		if err := json.Unmarshal(body, &merr); err != nil {
			return nil, err
		}
		return nil, merr
	}

	var memb Member
	if err := json.Unmarshal(body, &memb); err != nil {
		return nil, err
	}

	return &memb, nil
}

func (m *httpMembersAPI) Remove(ctx context.Context, memberID string) error {
	req := &membersAPIActionRemove{memberID: memberID}
	resp, _, err := m.client.Do(ctx, req)
	if err != nil {
		return err
	}

	return assertStatusCode(resp.StatusCode, http.StatusNoContent)
}

type membersAPIActionList struct{}

func (l *membersAPIActionList) HTTPRequest(ep url.URL) *http.Request {
	u := v2MembersURL(ep)
	req, _ := http.NewRequest("GET", u.String(), nil)
	return req
}

type membersAPIActionRemove struct {
	memberID string
}

func (d *membersAPIActionRemove) HTTPRequest(ep url.URL) *http.Request {
	u := v2MembersURL(ep)
	u.Path = path.Join(u.Path, d.memberID)
	req, _ := http.NewRequest("DELETE", u.String(), nil)
	return req
}

type membersAPIActionAdd struct {
	peerURLs types.URLs
}

func (a *membersAPIActionAdd) HTTPRequest(ep url.URL) *http.Request {
	u := v2MembersURL(ep)
	m := memberCreateRequest{PeerURLs: a.peerURLs}
	b, _ := json.Marshal(&m)
	req, _ := http.NewRequest("POST", u.String(), bytes.NewReader(b))
	req.Header.Set("Content-Type", "application/json")
	return req
}

func assertStatusCode(got int, want ...int) (err error) {
	for _, w := range want {
		if w == got {
			return nil
		}
	}
	return fmt.Errorf("unexpected status code %d", got)
}

// v2MembersURL add the necessary path to the provided endpoint
// to route requests to the default v2 members API.
func v2MembersURL(ep url.URL) *url.URL {
	ep.Path = path.Join(ep.Path, defaultV2MembersPrefix)
	return &ep
}

type membersError struct {
	Message string `json:"message"`
	Code    int    `json:"-"`
}

func (e membersError) Error() string {
	return e.Message
}
<commit_msg>client: 410 is a vaild response for member.Remove<commit_after>// Copyright 2015 CoreOS, 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 client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"path"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"

	"github.com/coreos/etcd/pkg/types"
)

var (
	defaultV2MembersPrefix = "/v2/members"
)

type Member struct {
	// ID is the unique identifier of this Member.
	ID string `json:"id"`

	// Name is a human-readable, non-unique identifier of this Member.
	Name string `json:"name"`

	// PeerURLs represents the HTTP(S) endpoints this Member uses to
	// participate in etcd's consensus protocol.
	PeerURLs []string `json:"peerURLs"`

	// ClientURLs represents the HTTP(S) endpoints on which this Member
	// serves it's client-facing APIs.
	ClientURLs []string `json:"clientURLs"`
}

type memberCollection []Member

func (c *memberCollection) UnmarshalJSON(data []byte) error {
	d := struct {
		Members []Member
	}{}

	if err := json.Unmarshal(data, &d); err != nil {
		return err
	}

	if d.Members == nil {
		*c = make([]Member, 0)
		return nil
	}

	*c = d.Members
	return nil
}

type memberCreateRequest struct {
	PeerURLs types.URLs
}

func (m *memberCreateRequest) MarshalJSON() ([]byte, error) {
	s := struct {
		PeerURLs []string `json:"peerURLs"`
	}{
		PeerURLs: make([]string, len(m.PeerURLs)),
	}

	for i, u := range m.PeerURLs {
		s.PeerURLs[i] = u.String()
	}

	return json.Marshal(&s)
}

// NewMembersAPI constructs a new MembersAPI that uses HTTP to
// interact with etcd's membership API.
func NewMembersAPI(c Client) MembersAPI {
	return &httpMembersAPI{
		client: c,
	}
}

type MembersAPI interface {
	// List enumerates the current cluster membership.
	List(ctx context.Context) ([]Member, error)

	// Add instructs etcd to accept a new Member into the cluster.
	Add(ctx context.Context, peerURL string) (*Member, error)

	// Remove demotes an existing Member out of the cluster.
	Remove(ctx context.Context, mID string) error
}

type httpMembersAPI struct {
	client httpClient
}

func (m *httpMembersAPI) List(ctx context.Context) ([]Member, error) {
	req := &membersAPIActionList{}
	resp, body, err := m.client.Do(ctx, req)
	if err != nil {
		return nil, err
	}

	if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
		return nil, err
	}

	var mCollection memberCollection
	if err := json.Unmarshal(body, &mCollection); err != nil {
		return nil, err
	}

	return []Member(mCollection), nil
}

func (m *httpMembersAPI) Add(ctx context.Context, peerURL string) (*Member, error) {
	urls, err := types.NewURLs([]string{peerURL})
	if err != nil {
		return nil, err
	}

	req := &membersAPIActionAdd{peerURLs: urls}
	resp, body, err := m.client.Do(ctx, req)
	if err != nil {
		return nil, err
	}

	if err := assertStatusCode(resp.StatusCode, http.StatusCreated, http.StatusConflict); err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusCreated {
		var merr membersError
		if err := json.Unmarshal(body, &merr); err != nil {
			return nil, err
		}
		return nil, merr
	}

	var memb Member
	if err := json.Unmarshal(body, &memb); err != nil {
		return nil, err
	}

	return &memb, nil
}

func (m *httpMembersAPI) Remove(ctx context.Context, memberID string) error {
	req := &membersAPIActionRemove{memberID: memberID}
	resp, _, err := m.client.Do(ctx, req)
	if err != nil {
		return err
	}

	return assertStatusCode(resp.StatusCode, http.StatusNoContent, http.StatusGone)
}

type membersAPIActionList struct{}

func (l *membersAPIActionList) HTTPRequest(ep url.URL) *http.Request {
	u := v2MembersURL(ep)
	req, _ := http.NewRequest("GET", u.String(), nil)
	return req
}

type membersAPIActionRemove struct {
	memberID string
}

func (d *membersAPIActionRemove) HTTPRequest(ep url.URL) *http.Request {
	u := v2MembersURL(ep)
	u.Path = path.Join(u.Path, d.memberID)
	req, _ := http.NewRequest("DELETE", u.String(), nil)
	return req
}

type membersAPIActionAdd struct {
	peerURLs types.URLs
}

func (a *membersAPIActionAdd) HTTPRequest(ep url.URL) *http.Request {
	u := v2MembersURL(ep)
	m := memberCreateRequest{PeerURLs: a.peerURLs}
	b, _ := json.Marshal(&m)
	req, _ := http.NewRequest("POST", u.String(), bytes.NewReader(b))
	req.Header.Set("Content-Type", "application/json")
	return req
}

func assertStatusCode(got int, want ...int) (err error) {
	for _, w := range want {
		if w == got {
			return nil
		}
	}
	return fmt.Errorf("unexpected status code %d", got)
}

// v2MembersURL add the necessary path to the provided endpoint
// to route requests to the default v2 members API.
func v2MembersURL(ep url.URL) *url.URL {
	ep.Path = path.Join(ep.Path, defaultV2MembersPrefix)
	return &ep
}

type membersError struct {
	Message string `json:"message"`
	Code    int    `json:"-"`
}

func (e membersError) Error() string {
	return e.Message
}
<|endoftext|>


<commit_before>package client // import "github.com/docker/docker/client"

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/versions"
	"github.com/docker/docker/errdefs"
	"github.com/pkg/errors"
)

// serverResponse is a wrapper for http API responses.
type serverResponse struct {
	body       io.ReadCloser
	header     http.Header
	statusCode int
	reqURL     *url.URL
}

// head sends an http request to the docker API using the method HEAD.
func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "HEAD", path, query, nil, headers)
}

// get sends an http request to the docker API using the method GET with a specific Go context.
func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "GET", path, query, nil, headers)
}

// post sends an http request to the docker API using the method POST with a specific Go context.
func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
	body, headers, err := encodeBody(obj, headers)
	if err != nil {
		return serverResponse{}, err
	}
	return cli.sendRequest(ctx, "POST", path, query, body, headers)
}

func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "POST", path, query, body, headers)
}

// put sends an http request to the docker API using the method PUT.
func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
	body, headers, err := encodeBody(obj, headers)
	if err != nil {
		return serverResponse{}, err
	}
	return cli.sendRequest(ctx, "PUT", path, query, body, headers)
}

// putRaw sends an http request to the docker API using the method PUT.
func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "PUT", path, query, body, headers)
}

// delete sends an http request to the docker API using the method DELETE.
func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "DELETE", path, query, nil, headers)
}

type headers map[string][]string

func encodeBody(obj interface{}, headers headers) (io.Reader, headers, error) {
	if obj == nil {
		return nil, headers, nil
	}

	body, err := encodeData(obj)
	if err != nil {
		return nil, headers, err
	}
	if headers == nil {
		headers = make(map[string][]string)
	}
	headers["Content-Type"] = []string{"application/json"}
	return body, headers, nil
}

func (cli *Client) buildRequest(method, path string, body io.Reader, headers headers) (*http.Request, error) {
	expectedPayload := (method == "POST" || method == "PUT")
	if expectedPayload && body == nil {
		body = bytes.NewReader([]byte{})
	}

	req, err := http.NewRequest(method, path, body)
	if err != nil {
		return nil, err
	}
	req = cli.addHeaders(req, headers)

	if cli.proto == "unix" || cli.proto == "npipe" {
		// For local communications, it doesn't matter what the host is. We just
		// need a valid and meaningful host name. (See #189)
		req.Host = "docker"
	}

	req.URL.Host = cli.addr
	req.URL.Scheme = cli.scheme

	if expectedPayload && req.Header.Get("Content-Type") == "" {
		req.Header.Set("Content-Type", "text/plain")
	}
	return req, nil
}

func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers headers) (serverResponse, error) {
	req, err := cli.buildRequest(method, cli.getAPIPath(ctx, path, query), body, headers)
	if err != nil {
		return serverResponse{}, err
	}
	resp, err := cli.doRequest(ctx, req)
	if err != nil {
		return resp, errdefs.FromStatusCode(err, resp.statusCode)
	}
	err = cli.checkResponseErr(resp)
	return resp, errdefs.FromStatusCode(err, resp.statusCode)
}

func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResponse, error) {
	serverResp := serverResponse{statusCode: -1, reqURL: req.URL}

	req = req.WithContext(ctx)
	resp, err := cli.client.Do(req)
	if err != nil {
		if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") {
			return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
		}

		if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") {
			return serverResp, errors.Wrap(err, "The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings")
		}

		// Don't decorate context sentinel errors; users may be comparing to
		// them directly.
		switch err {
		case context.Canceled, context.DeadlineExceeded:
			return serverResp, err
		}

		if nErr, ok := err.(*url.Error); ok {
			if nErr, ok := nErr.Err.(*net.OpError); ok {
				if os.IsPermission(nErr.Err) {
					return serverResp, errors.Wrapf(err, "Got permission denied while trying to connect to the Docker daemon socket at %v", cli.host)
				}
			}
		}

		if err, ok := err.(net.Error); ok {
			if err.Timeout() {
				return serverResp, ErrorConnectionFailed(cli.host)
			}
			if !err.Temporary() {
				if strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
					return serverResp, ErrorConnectionFailed(cli.host)
				}
			}
		}

		// Although there's not a strongly typed error for this in go-winio,
		// lots of people are using the default configuration for the docker
		// daemon on Windows where the daemon is listening on a named pipe
		// `//./pipe/docker_engine, and the client must be running elevated.
		// Give users a clue rather than the not-overly useful message
		// such as `error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.26/info:
		// open //./pipe/docker_engine: The system cannot find the file specified.`.
		// Note we can't string compare "The system cannot find the file specified" as
		// this is localised - for example in French the error would be
		// `open //./pipe/docker_engine: Le fichier spécifié est introuvable.`
		if strings.Contains(err.Error(), `open //./pipe/docker_engine`) {
			// Checks if client is running with elevated privileges
			if f, elevatedErr := os.Open("\\\\.\\PHYSICALDRIVE0"); elevatedErr == nil {
				err = errors.Wrap(err, "In the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect.")
			} else {
				f.Close()
				err = errors.Wrap(err, "This error may indicate that the docker daemon is not running.")
			}
		}

		return serverResp, errors.Wrap(err, "error during connect")
	}

	if resp != nil {
		serverResp.statusCode = resp.StatusCode
		serverResp.body = resp.Body
		serverResp.header = resp.Header
	}
	return serverResp, nil
}

func (cli *Client) checkResponseErr(serverResp serverResponse) error {
	if serverResp.statusCode >= 200 && serverResp.statusCode < 400 {
		return nil
	}

	var body []byte
	var err error
	if serverResp.body != nil {
		bodyMax := 1 * 1024 * 1024 // 1 MiB
		bodyR := &io.LimitedReader{
			R: serverResp.body,
			N: int64(bodyMax),
		}
		body, err = ioutil.ReadAll(bodyR)
		if err != nil {
			return err
		}
		if bodyR.N == 0 {
			return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), bodyMax, serverResp.reqURL)
		}
	}
	if len(body) == 0 {
		return fmt.Errorf("request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), serverResp.reqURL)
	}

	var ct string
	if serverResp.header != nil {
		ct = serverResp.header.Get("Content-Type")
	}

	var errorMessage string
	if (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) && ct == "application/json" {
		var errorResponse types.ErrorResponse
		if err := json.Unmarshal(body, &errorResponse); err != nil {
			return errors.Wrap(err, "Error reading JSON")
		}
		errorMessage = strings.TrimSpace(errorResponse.Message)
	} else {
		errorMessage = strings.TrimSpace(string(body))
	}

	return errors.Wrap(errors.New(errorMessage), "Error response from daemon")
}

func (cli *Client) addHeaders(req *http.Request, headers headers) *http.Request {
	// Add CLI Config's HTTP Headers BEFORE we set the Docker headers
	// then the user can't change OUR headers
	for k, v := range cli.customHTTPHeaders {
		if versions.LessThan(cli.version, "1.25") && k == "User-Agent" {
			continue
		}
		req.Header.Set(k, v)
	}

	if headers != nil {
		for k, v := range headers {
			req.Header[k] = v
		}
	}
	return req
}

func encodeData(data interface{}) (*bytes.Buffer, error) {
	params := bytes.NewBuffer(nil)
	if data != nil {
		if err := json.NewEncoder(params).Encode(data); err != nil {
			return nil, err
		}
	}
	return params, nil
}

func ensureReaderClosed(response serverResponse) {
	if response.body != nil {
		// Drain up to 512 bytes and close the body to let the Transport reuse the connection
		io.CopyN(ioutil.Discard, response.body, 512)
		response.body.Close()
	}
}
<commit_msg>client: remove put()<commit_after>package client // import "github.com/docker/docker/client"

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/versions"
	"github.com/docker/docker/errdefs"
	"github.com/pkg/errors"
)

// serverResponse is a wrapper for http API responses.
type serverResponse struct {
	body       io.ReadCloser
	header     http.Header
	statusCode int
	reqURL     *url.URL
}

// head sends an http request to the docker API using the method HEAD.
func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "HEAD", path, query, nil, headers)
}

// get sends an http request to the docker API using the method GET with a specific Go context.
func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "GET", path, query, nil, headers)
}

// post sends an http request to the docker API using the method POST with a specific Go context.
func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
	body, headers, err := encodeBody(obj, headers)
	if err != nil {
		return serverResponse{}, err
	}
	return cli.sendRequest(ctx, "POST", path, query, body, headers)
}

func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "POST", path, query, body, headers)
}

// putRaw sends an http request to the docker API using the method PUT.
func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "PUT", path, query, body, headers)
}

// delete sends an http request to the docker API using the method DELETE.
func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
	return cli.sendRequest(ctx, "DELETE", path, query, nil, headers)
}

type headers map[string][]string

func encodeBody(obj interface{}, headers headers) (io.Reader, headers, error) {
	if obj == nil {
		return nil, headers, nil
	}

	body, err := encodeData(obj)
	if err != nil {
		return nil, headers, err
	}
	if headers == nil {
		headers = make(map[string][]string)
	}
	headers["Content-Type"] = []string{"application/json"}
	return body, headers, nil
}

func (cli *Client) buildRequest(method, path string, body io.Reader, headers headers) (*http.Request, error) {
	expectedPayload := (method == "POST" || method == "PUT")
	if expectedPayload && body == nil {
		body = bytes.NewReader([]byte{})
	}

	req, err := http.NewRequest(method, path, body)
	if err != nil {
		return nil, err
	}
	req = cli.addHeaders(req, headers)

	if cli.proto == "unix" || cli.proto == "npipe" {
		// For local communications, it doesn't matter what the host is. We just
		// need a valid and meaningful host name. (See #189)
		req.Host = "docker"
	}

	req.URL.Host = cli.addr
	req.URL.Scheme = cli.scheme

	if expectedPayload && req.Header.Get("Content-Type") == "" {
		req.Header.Set("Content-Type", "text/plain")
	}
	return req, nil
}

func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers headers) (serverResponse, error) {
	req, err := cli.buildRequest(method, cli.getAPIPath(ctx, path, query), body, headers)
	if err != nil {
		return serverResponse{}, err
	}
	resp, err := cli.doRequest(ctx, req)
	if err != nil {
		return resp, errdefs.FromStatusCode(err, resp.statusCode)
	}
	err = cli.checkResponseErr(resp)
	return resp, errdefs.FromStatusCode(err, resp.statusCode)
}

func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResponse, error) {
	serverResp := serverResponse{statusCode: -1, reqURL: req.URL}

	req = req.WithContext(ctx)
	resp, err := cli.client.Do(req)
	if err != nil {
		if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") {
			return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
		}

		if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") {
			return serverResp, errors.Wrap(err, "The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings")
		}

		// Don't decorate context sentinel errors; users may be comparing to
		// them directly.
		switch err {
		case context.Canceled, context.DeadlineExceeded:
			return serverResp, err
		}

		if nErr, ok := err.(*url.Error); ok {
			if nErr, ok := nErr.Err.(*net.OpError); ok {
				if os.IsPermission(nErr.Err) {
					return serverResp, errors.Wrapf(err, "Got permission denied while trying to connect to the Docker daemon socket at %v", cli.host)
				}
			}
		}

		if err, ok := err.(net.Error); ok {
			if err.Timeout() {
				return serverResp, ErrorConnectionFailed(cli.host)
			}
			if !err.Temporary() {
				if strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
					return serverResp, ErrorConnectionFailed(cli.host)
				}
			}
		}

		// Although there's not a strongly typed error for this in go-winio,
		// lots of people are using the default configuration for the docker
		// daemon on Windows where the daemon is listening on a named pipe
		// `//./pipe/docker_engine, and the client must be running elevated.
		// Give users a clue rather than the not-overly useful message
		// such as `error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.26/info:
		// open //./pipe/docker_engine: The system cannot find the file specified.`.
		// Note we can't string compare "The system cannot find the file specified" as
		// this is localised - for example in French the error would be
		// `open //./pipe/docker_engine: Le fichier spécifié est introuvable.`
		if strings.Contains(err.Error(), `open //./pipe/docker_engine`) {
			// Checks if client is running with elevated privileges
			if f, elevatedErr := os.Open("\\\\.\\PHYSICALDRIVE0"); elevatedErr == nil {
				err = errors.Wrap(err, "In the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect.")
			} else {
				f.Close()
				err = errors.Wrap(err, "This error may indicate that the docker daemon is not running.")
			}
		}

		return serverResp, errors.Wrap(err, "error during connect")
	}

	if resp != nil {
		serverResp.statusCode = resp.StatusCode
		serverResp.body = resp.Body
		serverResp.header = resp.Header
	}
	return serverResp, nil
}

func (cli *Client) checkResponseErr(serverResp serverResponse) error {
	if serverResp.statusCode >= 200 && serverResp.statusCode < 400 {
		return nil
	}

	var body []byte
	var err error
	if serverResp.body != nil {
		bodyMax := 1 * 1024 * 1024 // 1 MiB
		bodyR := &io.LimitedReader{
			R: serverResp.body,
			N: int64(bodyMax),
		}
		body, err = ioutil.ReadAll(bodyR)
		if err != nil {
			return err
		}
		if bodyR.N == 0 {
			return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), bodyMax, serverResp.reqURL)
		}
	}
	if len(body) == 0 {
		return fmt.Errorf("request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), serverResp.reqURL)
	}

	var ct string
	if serverResp.header != nil {
		ct = serverResp.header.Get("Content-Type")
	}

	var errorMessage string
	if (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) && ct == "application/json" {
		var errorResponse types.ErrorResponse
		if err := json.Unmarshal(body, &errorResponse); err != nil {
			return errors.Wrap(err, "Error reading JSON")
		}
		errorMessage = strings.TrimSpace(errorResponse.Message)
	} else {
		errorMessage = strings.TrimSpace(string(body))
	}

	return errors.Wrap(errors.New(errorMessage), "Error response from daemon")
}

func (cli *Client) addHeaders(req *http.Request, headers headers) *http.Request {
	// Add CLI Config's HTTP Headers BEFORE we set the Docker headers
	// then the user can't change OUR headers
	for k, v := range cli.customHTTPHeaders {
		if versions.LessThan(cli.version, "1.25") && k == "User-Agent" {
			continue
		}
		req.Header.Set(k, v)
	}

	if headers != nil {
		for k, v := range headers {
			req.Header[k] = v
		}
	}
	return req
}

func encodeData(data interface{}) (*bytes.Buffer, error) {
	params := bytes.NewBuffer(nil)
	if data != nil {
		if err := json.NewEncoder(params).Encode(data); err != nil {
			return nil, err
		}
	}
	return params, nil
}

func ensureReaderClosed(response serverResponse) {
	if response.body != nil {
		// Drain up to 512 bytes and close the body to let the Transport reuse the connection
		io.CopyN(ioutil.Discard, response.body, 512)
		response.body.Close()
	}
}
<|endoftext|>


<commit_before>/*-
Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package main

import (
	"database/sql"
	"log"
	"time"

	"github.com/mjolnir42/scrypth64"
	"github.com/satori/go.uuid"
)

func (s *supervisor) startupLoad() {

	s.startupRoot()

	s.startupTokens()

}

func (s *supervisor) startupRoot() {
	var (
		err                  error
		flag, crypt          string
		mcf                  scrypth64.Mcf
		validFrom, expiresAt time.Time
		state                bool
		rows                 *sql.Rows
	)

	rows, err = s.conn.Query(stmt.LoadRootFlags)
	if err != nil {
		log.Fatal(`supervisor/load-root-flags,query: `, err)
	}
	defer rows.Close()

	for rows.Next() {
		if err = rows.Scan(
			&flag,
			&state,
		); err != nil {
			log.Fatal(`supervisor/load-root-flags,scan: `, err)
		}
		switch flag {
		case `disabled`:
			s.root_disabled = state
		case `restricted`:
			s.root_restricted = state
		}
	}
	if err = rows.Err(); err != nil {
		log.Fatal(`supervisor/load-root-flags,next: `, err)
	}

	if err = s.conn.QueryRow(stmt.LoadRootPassword).Scan(
		&crypt,
		&validFrom,
		&expiresAt,
	); err == sql.ErrNoRows {
		// root bootstrap outstanding
		return
	} else if err != nil {
		log.Fatal(`supervisor/load-root-password: `, err)
	}
	if mcf, err = scrypth64.FromString(crypt); err != nil {
		log.Fatal(`supervisor/string-to-mcf: `, err)
	}
	s.credentials.insert(`root`, uuid.Nil, validFrom.UTC(),
		PosTimeInf.UTC(), mcf)
}

func (s *supervisor) startupTokens() {
	var (
		err                         error
		token, salt, valid, expires string
		validFrom, expiresAt        time.Time
		rows                        *sql.Rows
	)

	rows, err = s.conn.Query(stmt.LoadAllTokens)
	if err != nil {
		log.Fatal(`supervisor/load-tokens,query: `, err)
	}
	defer rows.Close()

	for rows.Next() {
		if err = rows.Scan(
			&token,
			&salt,
			&validFrom,
			&expiresAt,
		); err != nil {
			log.Fatal(`supervisor/load-tokens,scan: `, err)
		}
		valid = validFrom.Format(rfc3339Milli)
		expires = expiresAt.Format(rfc3339Milli)

		if err = s.tokens.insert(token, valid, expires, salt); err != nil {
			log.Fatal(`supervisor/load-tokens,insert: `, err)
		}
	}
	if err = rows.Err(); err != nil {
		log.Fatal(`supervisor/load-tokens,next: `, err)
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Add supervisor startup: user credentials<commit_after>/*-
Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package main

import (
	"database/sql"
	"log"
	"time"

	"github.com/mjolnir42/scrypth64"
	"github.com/satori/go.uuid"
)

func (s *supervisor) startupLoad() {

	s.startupRoot()

	s.startupCredentials()

	s.startupTokens()

}

func (s *supervisor) startupRoot() {
	var (
		err                  error
		flag, crypt          string
		mcf                  scrypth64.Mcf
		validFrom, expiresAt time.Time
		state                bool
		rows                 *sql.Rows
	)

	rows, err = s.conn.Query(stmt.LoadRootFlags)
	if err != nil {
		log.Fatal(`supervisor/load-root-flags,query: `, err)
	}
	defer rows.Close()

	for rows.Next() {
		if err = rows.Scan(
			&flag,
			&state,
		); err != nil {
			log.Fatal(`supervisor/load-root-flags,scan: `, err)
		}
		switch flag {
		case `disabled`:
			s.root_disabled = state
		case `restricted`:
			s.root_restricted = state
		}
	}
	if err = rows.Err(); err != nil {
		log.Fatal(`supervisor/load-root-flags,next: `, err)
	}

	if err = s.conn.QueryRow(stmt.LoadRootPassword).Scan(
		&crypt,
		&validFrom,
		&expiresAt,
	); err == sql.ErrNoRows {
		// root bootstrap outstanding
		return
	} else if err != nil {
		log.Fatal(`supervisor/load-root-password: `, err)
	}
	if mcf, err = scrypth64.FromString(crypt); err != nil {
		log.Fatal(`supervisor/string-to-mcf: `, err)
	}
	s.credentials.insert(`root`, uuid.Nil, validFrom.UTC(),
		PosTimeInf.UTC(), mcf)
}

func (s *supervisor) startupCredentials() {
	var (
		err                  error
		rows                 *sql.Rows
		user_id, user, crypt string
		reset                bool
		validFrom, expiresAt time.Time
		id                   uuid.UUID
		mcf                  scrypth64.Mcf
	)

	rows, err = s.conn.Query(stmt.LoadAllUserCredentials)
	if err != nil {
		log.Fatal(`supervisor/load-credentials,query: `, err)
	}
	defer rows.Close()

	for rows.Next() {
		if err = rows.Scan(
			&user_id,
			&crypt,
			&reset,
			&validFrom,
			&expiresAt,
			&user,
		); err != nil {
			log.Fatal(`supervisor/load-credentials,scan: `, err)
		}

		if id, err = uuid.FromString(user_id); err != nil {
			log.Fatal(`supervisor/string-to-uuid: `, err)
		}
		if mcf, err = scrypth64.FromString(crypt); err != nil {
			log.Fatal(`supervisor/string-to-mcf: `, err)
		}

		s.credentials.restore(user, id, validFrom, expiresAt, mcf, reset, true)
	}
	if err = rows.Err(); err != nil {
		log.Fatal(`supervisor/load-credentials,next: `, err)
	}
}

func (s *supervisor) startupTokens() {
	var (
		err                         error
		token, salt, valid, expires string
		validFrom, expiresAt        time.Time
		rows                        *sql.Rows
	)

	rows, err = s.conn.Query(stmt.LoadAllTokens)
	if err != nil {
		log.Fatal(`supervisor/load-tokens,query: `, err)
	}
	defer rows.Close()

	for rows.Next() {
		if err = rows.Scan(
			&token,
			&salt,
			&validFrom,
			&expiresAt,
		); err != nil {
			log.Fatal(`supervisor/load-tokens,scan: `, err)
		}
		valid = validFrom.Format(rfc3339Milli)
		expires = expiresAt.Format(rfc3339Milli)

		if err = s.tokens.insert(token, valid, expires, salt); err != nil {
			log.Fatal(`supervisor/load-tokens,insert: `, err)
		}
	}
	if err = rows.Err(); err != nil {
		log.Fatal(`supervisor/load-tokens,next: `, err)
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>// Copyright (c) 2014 Pagoda Box Inc.
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

package jobs

import (
	"fmt"

	"github.com/pagodabox/nanobox-golang-stylish"
	"github.com/pagodabox/nanobox-server/util"
)

type ImageUpdate struct {
}

func (j *ImageUpdate) Process() {
	images, err := util.ListImages()
	if err != nil {
		util.HandleError("Unable to pull images:" + err.Error())
		util.UpdateStatus(j, "errored")
		return
	}
	for _, image := range images {
		for _, tag := range image.RepoTags {
			util.LogInfo(stylish.Bullet(fmt.Sprintf("Updating image: %s", tag)))
			err := util.UpdateImage(tag)
			if err != nil {
				util.HandleError("Unable to pull images:" + err.Error())
				util.UpdateStatus(j, "errored")
			}
		}
	}

	util.UpdateStatus(j, "complete")
}
<commit_msg>minor cleanup resulting from some debugging<commit_after>// Copyright (c) 2014 Pagoda Box Inc.
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

package jobs

import (
	"fmt"

	"github.com/pagodabox/nanobox-golang-stylish"
	"github.com/pagodabox/nanobox-server/util"
)

type ImageUpdate struct {
}

func (j *ImageUpdate) Process() {
	images, err := util.ListImages()
	if err != nil {
		util.HandleError("Unable to pull images:" + err.Error())
		util.UpdateStatus(j, "errored")
		return
	}

	for _, image := range images {
		for _, tag := range image.RepoTags {
			util.LogInfo(stylish.Bullet(fmt.Sprintf("Updating image: %s", tag)))
			if err := util.UpdateImage(tag); err != nil {
				util.HandleError("Unable to pull images:" + err.Error())
				util.UpdateStatus(j, "errored")
			}
		}
	}

	util.UpdateStatus(j, "complete")
}
<|endoftext|>


<commit_before>// Copyright 2018 Sean.ZH

package tools

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql" // Just import mysql, no need to specify it
	"strconv"
)

// DBConfig for mysql
type DBConfig struct {
	User   string `json:"user"`
	Pass   string `json:"pass"`
	Host   string `json:"host"`
	Port   int    `json:"port"`
	DBName string `json:"db"`
}

func initDB(conf *DBConfig) (*sql.DB, error) {
	dsn := conf.User + ":" + conf.Pass + "@tcp"
	dsn = dsn + "(" + conf.Host + ":"
	dsn = dsn + strconv.Itoa(conf.Port) + ")"
	dsn = dsn + "/" + conf.DBName
	dsn = dsn + "?timeout=10s"
	return sql.Open("mysql", dsn)
}

// InitDB create new db object for mysql
func InitDB(conf *DBConfig) (*sql.DB, error) {
	db, _ := initDB(conf)
	return db, db.Ping()
}
<commit_msg>add ext<commit_after>// Copyright 2018 Sean.ZH

package tools

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql" // Just import mysql, no need to specify it
	"strconv"
)

// DBConfig for mysql
type DBConfig struct {
	User   string `json:"user"`
	Pass   string `json:"pass"`
	Host   string `json:"host"`
	Port   int    `json:"port"`
	DBName string `json:"db"`
	Ext    string `json:"ext"`
}

func initDB(conf *DBConfig) (*sql.DB, error) {
	dsn := conf.User + ":" + conf.Pass + "@tcp"
	dsn = dsn + "(" + conf.Host + ":"
	dsn = dsn + strconv.Itoa(conf.Port) + ")"
	dsn = dsn + "/" + conf.DBName
	dsn = dsn + "?timeout=10s"
	if conf.Ext != "" {
		dsn = dsn + "&" + conf.Ext
	}
	return sql.Open("mysql", dsn)
}

// InitDB create new db object for mysql
func InitDB(conf *DBConfig) (*sql.DB, error) {
	db, _ := initDB(conf)
	return db, db.Ping()
}
<|endoftext|>


<commit_before>package uuid

import (
	"database/sql/driver"
)

// Scan scans a uuid from the given interface instance and stores it
func (u *UUID) Scan(val interface{}) error {
	if s, ok := val.(string); ok {
		err := u.SetString(s)
		if err != nil {
			return err
		}
	}

	return ErrInvalid
}

// Value gives the database driver representation of the UUID
func (u UUID) Value() (driver.Value, error) {
	return u.String(), nil
}
<commit_msg>Scan: Added verbose error message<commit_after>package uuid

import (
	"database/sql/driver"
	"fmt"
	"reflect"
)

type ErrInvalidType struct {
	Type reflect.Type
}

func (e *ErrInvalidType) Error() string {
	return fmt.Sprintf("uuid Scan(): invalid type '%s', expected string.", e.Type.String())
}

// Scan scans a uuid from the given interface instance and stores it
func (u *UUID) Scan(val interface{}) error {
	if s, ok := val.(string); ok {
		err := u.SetString(s)
		if err != nil {
			return err
		}
	}

	return &ErrInvalidType{reflect.TypeOf(val)}
}

// Value gives the database driver representation of the UUID
func (u UUID) Value() (driver.Value, error) {
	return u.String(), nil
}
<|endoftext|>


<commit_before>package authcontroller

import (
	"context"

	"golang.org/x/oauth2"
	googleoauth "google.golang.org/api/oauth2/v2"

	"github.com/gilcrest/errs"
	"github.com/gilcrest/go-api-basic/domain/auth"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/gilcrest/go-api-basic/gateway/authgateway"
)

// AuthorizeAccessToken takes an access token string, validates
// that the user exists by calling out to Google's Userinfo API and
// then authorizes the user
func AuthorizeAccessToken(ctx context.Context, token string) (*user.User, error) {
	const op errs.Op = "controller/authcontroller/AuthorizeTokenController"

	// Setup oauth token
	oauthToken := oauth2.Token{AccessToken: token, TokenType: "Bearer"}
	// use Google Oauth2 API to get user info
	userInfo, err := authgateway.UserInfo(ctx, &oauthToken)
	if err != nil {
		// "In summary, a 401 Unauthorized response should be used for missing or
		// bad authentication, and a 403 Forbidden response should be used afterwards,
		// when the user is authenticated but isn’t authorized to perform the
		// requested operation on the given resource."
		// In this case, we are getting a bad response from Google service, assume
		// they are not able to authenticate properly
		return nil, errs.E(op, errs.Unauthenticated, err)
	}

	// Set userInfo from google into domain user
	u := newUser(userInfo)

	// validate that user is authorized
	err = auth.AuthorizeUser(ctx, u)
	if err != nil {
		return nil, errs.E(op, err)
	}

	return u, nil
}

// newUser initializes the user.User struct given a Userinfo struct
// from Google
func newUser(userinfo *googleoauth.Userinfo) *user.User {
	return &user.User{
		Email:     userinfo.Email,
		LastName:  userinfo.FamilyName,
		FirstName: userinfo.GivenName,
		FullName:  userinfo.Name,
		//Gender:       userinfo.Gender,
		HostedDomain: userinfo.Hd,
		PictureURL:   userinfo.Picture,
		ProfileLink:  userinfo.Link,
	}
}
<commit_msg>separate controller not needed<commit_after><|endoftext|>


<commit_before>package cluster

import (
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"time"

	"github.com/raintank/worldping-api/pkg/log"
	"github.com/rakyll/globalconf"
)

var (
	ClusterName        string
	primary            bool
	peersStr           string
	mode               string
	maxPrio            int
	httpTimeout        time.Duration
	minAvailableShards int

	swimUseConfig               string
	swimBindAddrStr             string
	swimBindAddr                *net.TCPAddr
	swimTCPTimeout              time.Duration
	swimIndirectChecks          int
	swimRetransmitMult          int
	swimSuspicionMult           int
	swimSuspicionMaxTimeoutMult int
	swimPushPullInterval        time.Duration
	swimProbeInterval           time.Duration
	swimProbeTimeout            time.Duration
	swimDisableTcpPings         bool
	swimAwarenessMaxMultiplier  int
	swimGossipInterval          time.Duration
	swimGossipNodes             int
	swimGossipToTheDeadTime     time.Duration
	swimEnableCompression       bool
	swimDNSConfigPath           string

	client http.Client
)

func ConfigSetup() {
	clusterCfg := flag.NewFlagSet("cluster", flag.ExitOnError)
	clusterCfg.StringVar(&ClusterName, "name", "metrictank", "Unique name of the cluster.")
	clusterCfg.BoolVar(&primary, "primary-node", false, "the primary node writes data to cassandra. There should only be 1 primary node per shardGroup.")
	clusterCfg.StringVar(&peersStr, "peers", "", "TCP addresses of other nodes, comma separated. use this if you shard your data and want to query other instances")
	clusterCfg.StringVar(&mode, "mode", "single", "Operating mode of cluster. (single|multi)")
	clusterCfg.DurationVar(&httpTimeout, "http-timeout", time.Second*60, "How long to wait before aborting http requests to cluster peers and returning a http 503 service unavailable")
	clusterCfg.IntVar(&maxPrio, "max-priority", 10, "maximum priority before a node should be considered not-ready.")
	clusterCfg.IntVar(&minAvailableShards, "min-available-shards", 0, "minimum number of shards that must be available for a query to be handled.")
	globalconf.Register("cluster", clusterCfg)

	swimCfg := flag.NewFlagSet("swim", flag.ExitOnError)
	swimCfg.StringVar(&swimUseConfig, "use-config", "default-lan", "config setting to use. If set, will override all other swim settings. Use none|default-lan|default-local|default-wan. see https://godoc.org/github.com/hashicorp/memberlist#Config . Note all our swim settings correspond to default-lan")
	swimCfg.StringVar(&swimBindAddrStr, "bind-addr", "0.0.0.0:7946", "binding TCP Address for UDP and TCP gossip")
	swimCfg.DurationVar(&swimTCPTimeout, "tcp-timeout", 10*time.Second, "timeout for establishing a stream connection with peers for a full state sync, and for stream reads and writes")
	swimCfg.IntVar(&swimIndirectChecks, "indirect-checks", 3, "number of nodes that will be asked to perform an indirect probe of a node in the case a direct probe fails")
	swimCfg.IntVar(&swimRetransmitMult, "retransmit-mult", 4, "multiplier for number of retransmissions for gossip messages. Retransmits = RetransmitMult * log(N+1)")
	swimCfg.IntVar(&swimSuspicionMult, "suspicion-multi", 4, "multiplier for determining when inaccessible/suspect node is delared dead. SuspicionTimeout = SuspicionMult * log(N+1) * ProbeInterval")
	swimCfg.IntVar(&swimSuspicionMaxTimeoutMult, "suspicion-max-timeout-mult", 6, "multiplier for upper bound on detection time.  SuspicionMaxTimeout = SuspicionMaxTimeoutMult * SuspicionTimeout")
	swimCfg.DurationVar(&swimPushPullInterval, "push-pull-interval", 30*time.Second, "interval between complete state syncs. 0 will disable state push/pull syncs")
	swimCfg.DurationVar(&swimProbeInterval, "probe-interval", 1*time.Second, "interval between random node probes")
	swimCfg.DurationVar(&swimProbeTimeout, "probe-timeout", 500*time.Millisecond, "timeout to wait for an ack from a probed node before assuming it is unhealthy. This should be set to 99-percentile of network RTT")
	swimCfg.BoolVar(&swimDisableTcpPings, "disable-tcp-pings", false, "turn off the fallback TCP pings that are attempted if the direct UDP ping fails")
	swimCfg.IntVar(&swimAwarenessMaxMultiplier, "awareness-max-multiplier", 8, "will increase the probe interval if the node becomes aware that it might be degraded and not meeting the soft real time requirements to reliably probe other nodes.")
	swimCfg.IntVar(&swimGossipNodes, "gossip-nodes", 3, "number of random nodes to send gossip messages to per GossipInterval")
	swimCfg.DurationVar(&swimGossipInterval, "gossip-interval", 200*time.Millisecond, "interval between sending messages that need to be gossiped that haven't been able to piggyback on probing messages. 0 disables non-piggyback gossip")
	swimCfg.DurationVar(&swimGossipToTheDeadTime, "gossip-to-the-dead-time", 30*time.Second, "interval after which a node has died that we will still try to gossip to it. This gives it a chance to refute")
	swimCfg.BoolVar(&swimEnableCompression, "enable-compression", true, "message compression")
	swimCfg.StringVar(&swimDNSConfigPath, "dns-config-path", "/etc/resolv.conf", "system's DNS config file. Override allows for easier testing")
	globalconf.Register("swim", swimCfg)
}

func ConfigProcess() {
	if !validMode(mode) {
		log.Fatal(4, "CLU Config: invalid cluster operating mode")
	}

	var err error
	swimBindAddr, err = net.ResolveTCPAddr("tcp", swimBindAddrStr)
	if err != nil {
		log.Fatal(4, "CLU Config: swim-bind-addr is not a valid TCP address: %s", err.Error())
	}

	if httpTimeout == 0 {
		log.Fatal(4, "CLU Config: http-timeout must be a non-zero duration string like 60s")
	}

	Mode = ModeType(mode)

	client = http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			Proxy:           http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   time.Second * 5,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: time.Second,
		},
		Timeout: httpTimeout,
	}

	if mode == "multi" {
		if swimUseConfig != "none" && swimUseConfig != "default-lan" && swimUseConfig != "default-local" && swimUseConfig != "default-wan" {
			log.Fatal(4, "CLU Config: invalid swim-use-config setting")
		}
	}
}
<commit_msg>set default for unit tests<commit_after>package cluster

import (
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"time"

	"github.com/raintank/worldping-api/pkg/log"
	"github.com/rakyll/globalconf"
)

var (
	ClusterName        string
	primary            bool
	peersStr           string
	mode               string
	maxPrio            int
	httpTimeout        time.Duration
	minAvailableShards int

	swimUseConfig               = "default-lan"
	swimBindAddrStr             string
	swimBindAddr                *net.TCPAddr
	swimTCPTimeout              time.Duration
	swimIndirectChecks          int
	swimRetransmitMult          int
	swimSuspicionMult           int
	swimSuspicionMaxTimeoutMult int
	swimPushPullInterval        time.Duration
	swimProbeInterval           time.Duration
	swimProbeTimeout            time.Duration
	swimDisableTcpPings         bool
	swimAwarenessMaxMultiplier  int
	swimGossipInterval          time.Duration
	swimGossipNodes             int
	swimGossipToTheDeadTime     time.Duration
	swimEnableCompression       bool
	swimDNSConfigPath           string

	client http.Client
)

func ConfigSetup() {
	clusterCfg := flag.NewFlagSet("cluster", flag.ExitOnError)
	clusterCfg.StringVar(&ClusterName, "name", "metrictank", "Unique name of the cluster.")
	clusterCfg.BoolVar(&primary, "primary-node", false, "the primary node writes data to cassandra. There should only be 1 primary node per shardGroup.")
	clusterCfg.StringVar(&peersStr, "peers", "", "TCP addresses of other nodes, comma separated. use this if you shard your data and want to query other instances")
	clusterCfg.StringVar(&mode, "mode", "single", "Operating mode of cluster. (single|multi)")
	clusterCfg.DurationVar(&httpTimeout, "http-timeout", time.Second*60, "How long to wait before aborting http requests to cluster peers and returning a http 503 service unavailable")
	clusterCfg.IntVar(&maxPrio, "max-priority", 10, "maximum priority before a node should be considered not-ready.")
	clusterCfg.IntVar(&minAvailableShards, "min-available-shards", 0, "minimum number of shards that must be available for a query to be handled.")
	globalconf.Register("cluster", clusterCfg)

	swimCfg := flag.NewFlagSet("swim", flag.ExitOnError)
	swimCfg.StringVar(&swimUseConfig, "use-config", "default-lan", "config setting to use. If set, will override all other swim settings. Use none|default-lan|default-local|default-wan. see https://godoc.org/github.com/hashicorp/memberlist#Config . Note all our swim settings correspond to default-lan")
	swimCfg.StringVar(&swimBindAddrStr, "bind-addr", "0.0.0.0:7946", "binding TCP Address for UDP and TCP gossip")
	swimCfg.DurationVar(&swimTCPTimeout, "tcp-timeout", 10*time.Second, "timeout for establishing a stream connection with peers for a full state sync, and for stream reads and writes")
	swimCfg.IntVar(&swimIndirectChecks, "indirect-checks", 3, "number of nodes that will be asked to perform an indirect probe of a node in the case a direct probe fails")
	swimCfg.IntVar(&swimRetransmitMult, "retransmit-mult", 4, "multiplier for number of retransmissions for gossip messages. Retransmits = RetransmitMult * log(N+1)")
	swimCfg.IntVar(&swimSuspicionMult, "suspicion-multi", 4, "multiplier for determining when inaccessible/suspect node is delared dead. SuspicionTimeout = SuspicionMult * log(N+1) * ProbeInterval")
	swimCfg.IntVar(&swimSuspicionMaxTimeoutMult, "suspicion-max-timeout-mult", 6, "multiplier for upper bound on detection time.  SuspicionMaxTimeout = SuspicionMaxTimeoutMult * SuspicionTimeout")
	swimCfg.DurationVar(&swimPushPullInterval, "push-pull-interval", 30*time.Second, "interval between complete state syncs. 0 will disable state push/pull syncs")
	swimCfg.DurationVar(&swimProbeInterval, "probe-interval", 1*time.Second, "interval between random node probes")
	swimCfg.DurationVar(&swimProbeTimeout, "probe-timeout", 500*time.Millisecond, "timeout to wait for an ack from a probed node before assuming it is unhealthy. This should be set to 99-percentile of network RTT")
	swimCfg.BoolVar(&swimDisableTcpPings, "disable-tcp-pings", false, "turn off the fallback TCP pings that are attempted if the direct UDP ping fails")
	swimCfg.IntVar(&swimAwarenessMaxMultiplier, "awareness-max-multiplier", 8, "will increase the probe interval if the node becomes aware that it might be degraded and not meeting the soft real time requirements to reliably probe other nodes.")
	swimCfg.IntVar(&swimGossipNodes, "gossip-nodes", 3, "number of random nodes to send gossip messages to per GossipInterval")
	swimCfg.DurationVar(&swimGossipInterval, "gossip-interval", 200*time.Millisecond, "interval between sending messages that need to be gossiped that haven't been able to piggyback on probing messages. 0 disables non-piggyback gossip")
	swimCfg.DurationVar(&swimGossipToTheDeadTime, "gossip-to-the-dead-time", 30*time.Second, "interval after which a node has died that we will still try to gossip to it. This gives it a chance to refute")
	swimCfg.BoolVar(&swimEnableCompression, "enable-compression", true, "message compression")
	swimCfg.StringVar(&swimDNSConfigPath, "dns-config-path", "/etc/resolv.conf", "system's DNS config file. Override allows for easier testing")
	globalconf.Register("swim", swimCfg)
}

func ConfigProcess() {
	if !validMode(mode) {
		log.Fatal(4, "CLU Config: invalid cluster operating mode")
	}

	var err error
	swimBindAddr, err = net.ResolveTCPAddr("tcp", swimBindAddrStr)
	if err != nil {
		log.Fatal(4, "CLU Config: swim-bind-addr is not a valid TCP address: %s", err.Error())
	}

	if httpTimeout == 0 {
		log.Fatal(4, "CLU Config: http-timeout must be a non-zero duration string like 60s")
	}

	Mode = ModeType(mode)

	client = http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			Proxy:           http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   time.Second * 5,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: time.Second,
		},
		Timeout: httpTimeout,
	}

	if mode == "multi" {
		if swimUseConfig != "none" && swimUseConfig != "default-lan" && swimUseConfig != "default-local" && swimUseConfig != "default-wan" {
			log.Fatal(4, "CLU Config: invalid swim-use-config setting")
		}
	}
}
<|endoftext|>


<commit_before>package convey

import (
	"strconv"
	"testing"
)

func TestSingleScope(t *testing.T) {
	output := prepare()

	Convey("hi", t, func() {
		output += "done"
	})

	expectEqual(t, "done", output)
}

func TestSingleScopeWithMultipleConveys(t *testing.T) {
	output := prepare()

	Convey("1", t, func() {
		output += "1"
	})

	Convey("2", t, func() {
		output += "2"
	})

	expectEqual(t, "12", output)
}

func TestNestedScopes(t *testing.T) {
	output := prepare()

	Convey("a", t, func() {
		output += "a "

		Convey("bb", func() {
			output += "bb "

			Convey("ccc", func() {
				output += "ccc | "
			})
		})
	})

	expectEqual(t, "a bb ccc | ", output)
}

func TestNestedScopesWithIsolatedExecution(t *testing.T) {
	output := prepare()

	Convey("a", t, func() {
		output += "a "

		Convey("aa", func() {
			output += "aa "

			Convey("aaa", func() {
				output += "aaa | "
			})

			Convey("aaa1", func() {
				output += "aaa1 | "
			})
		})

		Convey("ab", func() {
			output += "ab "

			Convey("abb", func() {
				output += "abb | "
			})
		})
	})

	expectEqual(t, "a aa aaa | a aa aaa1 | a ab abb | ", output)
}

func TestSingleScopeWithConveyAndNestedReset(t *testing.T) {
	output := prepare()

	Convey("1", t, func() {
		output += "1"

		Reset(func() {
			output += "a"
		})
	})

	expectEqual(t, "1a", output)
}

func TestSingleScopeWithMultipleRegistrationsAndReset(t *testing.T) {
	output := prepare()

	Convey("reset after each nested convey", t, func() {
		Convey("first output", func() {
			output += "1"
		})

		Convey("second output", func() {
			output += "2"
		})

		Reset(func() {
			output += "a"
		})
	})

	expectEqual(t, "1a2a", output)
}

func TestSingleScopeWithMultipleRegistrationsAndMultipleResets(t *testing.T) {
	output := prepare()

	Convey("each reset is run at end of each nested convey", t, func() {
		Convey("1", func() {
			output += "1"
		})

		Convey("2", func() {
			output += "2"
		})

		Reset(func() {
			output += "a"
		})

		Reset(func() {
			output += "b"
		})
	})

	expectEqual(t, "1ab2ab", output)
}

func TestPanicAtHigherLevelScopePreventsChildScopesFromRunning(t *testing.T) {
	output := prepare()

	Convey("This step panics", t, func() {
		Convey("this should NOT be executed", func() {
			output += "1"
		})

		panic("Hi")
	})

	expectEqual(t, "", output)
}

func TestPanicInChildScopeDoes_NOT_PreventExecutionOfSiblingScopes(t *testing.T) {
	output := prepare()

	Convey("This is the parent", t, func() {
		Convey("This step panics", func() {
			panic("Hi")
			output += "1"
		})

		Convey("This sibling should execute", func() {
			output += "2"
		})
	})

	expectEqual(t, "2", output)
}

func TestResetsAreAlwaysExecutedAfterScopePanics(t *testing.T) {
	output := prepare()

	Convey("This is the parent", t, func() {
		Convey("This step panics", func() {
			panic("Hi")
			output += "1"
		})

		Convey("This sibling step does not panic", func() {
			output += "a"

			Reset(func() {
				output += "b"
			})
		})

		Reset(func() {
			output += "2"
		})
	})

	expectEqual(t, "2ab2", output)
}

func TestSkipTopLevel(t *testing.T) {
	output := prepare()

	SkipConvey("hi", t, func() {
		output += "This shouldn't be executed!"
	})

	expectEqual(t, "", output)
}

func TestSkipNestedLevel(t *testing.T) {
	output := prepare()

	Convey("hi", t, func() {
		output += "yes"

		SkipConvey("bye", func() {
			output += "no"
		})
	})

	expectEqual(t, "yes", output)
}

func TestSkipNestedLevelSkipsAllChildLevels(t *testing.T) {
	output := prepare()

	Convey("hi", t, func() {
		output += "yes"

		SkipConvey("bye", func() {
			output += "no"

			Convey("byebye", func() {
				output += "no-no"
			})
		})
	})

	expectEqual(t, "yes", output)
}

func TestIterativeConveys(t *testing.T) {
	output := prepare()

	Convey("Test", t, func() {
		for x := 0; x < 10; x++ {
			y := strconv.Itoa(x)

			Convey(y, func() {
				output += y
			})
		}
	})

	expectEqual(t, "0123456789", output)
}

func prepare() string {
	testReporter = newNilReporter()
	return ""
}
<commit_msg>Additional tests for "fail fast" behavior.<commit_after>package convey

import (
	"strconv"
	"testing"
)

func TestSingleScope(t *testing.T) {
	output := prepare()

	Convey("hi", t, func() {
		output += "done"
	})

	expectEqual(t, "done", output)
}

func TestSingleScopeWithMultipleConveys(t *testing.T) {
	output := prepare()

	Convey("1", t, func() {
		output += "1"
	})

	Convey("2", t, func() {
		output += "2"
	})

	expectEqual(t, "12", output)
}

func TestNestedScopes(t *testing.T) {
	output := prepare()

	Convey("a", t, func() {
		output += "a "

		Convey("bb", func() {
			output += "bb "

			Convey("ccc", func() {
				output += "ccc | "
			})
		})
	})

	expectEqual(t, "a bb ccc | ", output)
}

func TestNestedScopesWithIsolatedExecution(t *testing.T) {
	output := prepare()

	Convey("a", t, func() {
		output += "a "

		Convey("aa", func() {
			output += "aa "

			Convey("aaa", func() {
				output += "aaa | "
			})

			Convey("aaa1", func() {
				output += "aaa1 | "
			})
		})

		Convey("ab", func() {
			output += "ab "

			Convey("abb", func() {
				output += "abb | "
			})
		})
	})

	expectEqual(t, "a aa aaa | a aa aaa1 | a ab abb | ", output)
}

func TestSingleScopeWithConveyAndNestedReset(t *testing.T) {
	output := prepare()

	Convey("1", t, func() {
		output += "1"

		Reset(func() {
			output += "a"
		})
	})

	expectEqual(t, "1a", output)
}

func TestSingleScopeWithMultipleRegistrationsAndReset(t *testing.T) {
	output := prepare()

	Convey("reset after each nested convey", t, func() {
		Convey("first output", func() {
			output += "1"
		})

		Convey("second output", func() {
			output += "2"
		})

		Reset(func() {
			output += "a"
		})
	})

	expectEqual(t, "1a2a", output)
}

func TestSingleScopeWithMultipleRegistrationsAndMultipleResets(t *testing.T) {
	output := prepare()

	Convey("each reset is run at end of each nested convey", t, func() {
		Convey("1", func() {
			output += "1"
		})

		Convey("2", func() {
			output += "2"
		})

		Reset(func() {
			output += "a"
		})

		Reset(func() {
			output += "b"
		})
	})

	expectEqual(t, "1ab2ab", output)
}

func Test_Failure_AtHigherLevelScopePreventsChildScopesFromRunning(t *testing.T) {
	output := prepare()

	Convey("This step fails", t, func() {
		So(1, ShouldEqual, 2)

		Convey("this should NOT be executed", func() {
			output += "a"
		})
	})

	expectEqual(t, "", output)
}

func Test_Panic_AtHigherLevelScopePreventsChildScopesFromRunning(t *testing.T) {
	output := prepare()

	Convey("This step panics", t, func() {
		Convey("this should NOT be executed", func() {
			output += "1"
		})

		panic("Hi")
	})

	expectEqual(t, "", output)
}

func Test_Panic_InChildScopeDoes_NOT_PreventExecutionOfSiblingScopes(t *testing.T) {
	output := prepare()

	Convey("This is the parent", t, func() {
		Convey("This step panics", func() {
			panic("Hi")
			output += "1"
		})

		Convey("This sibling should execute", func() {
			output += "2"
		})
	})

	expectEqual(t, "2", output)
}

func Test_Failure_InChildScopeDoes_NOT_PreventExecutionOfSiblingScopes(t *testing.T) {
	output := prepare()

	Convey("This is the parent", t, func() {
		Convey("This step fails", func() {
			So(1, ShouldEqual, 2)
			output += "1"
		})

		Convey("This sibling should execute", func() {
			output += "2"
		})
	})

	expectEqual(t, "2", output)
}

func TestResetsAreAlwaysExecutedAfterScope_Panics(t *testing.T) {
	output := prepare()

	Convey("This is the parent", t, func() {
		Convey("This step panics", func() {
			panic("Hi")
			output += "1"
		})

		Convey("This sibling step does not panic", func() {
			output += "a"

			Reset(func() {
				output += "b"
			})
		})

		Reset(func() {
			output += "2"
		})
	})

	expectEqual(t, "2ab2", output)
}

func TestResetsAreAlwaysExecutedAfterScope_Failures(t *testing.T) {
	output := prepare()

	Convey("This is the parent", t, func() {
		Convey("This step fails", func() {
			So(1, ShouldEqual, 2)
			output += "1"
		})

		Convey("This sibling step does not fail", func() {
			output += "a"

			Reset(func() {
				output += "b"
			})
		})

		Reset(func() {
			output += "2"
		})
	})

	expectEqual(t, "2ab2", output)
}

func TestSkipTopLevel(t *testing.T) {
	output := prepare()

	SkipConvey("hi", t, func() {
		output += "This shouldn't be executed!"
	})

	expectEqual(t, "", output)
}

func TestSkipNestedLevel(t *testing.T) {
	output := prepare()

	Convey("hi", t, func() {
		output += "yes"

		SkipConvey("bye", func() {
			output += "no"
		})
	})

	expectEqual(t, "yes", output)
}

func TestSkipNestedLevelSkipsAllChildLevels(t *testing.T) {
	output := prepare()

	Convey("hi", t, func() {
		output += "yes"

		SkipConvey("bye", func() {
			output += "no"

			Convey("byebye", func() {
				output += "no-no"
			})
		})
	})

	expectEqual(t, "yes", output)
}

func TestIterativeConveys(t *testing.T) {
	output := prepare()

	Convey("Test", t, func() {
		for x := 0; x < 10; x++ {
			y := strconv.Itoa(x)

			Convey(y, func() {
				output += y
			})
		}
	})

	expectEqual(t, "0123456789", output)
}

func prepare() string {
	testReporter = newNilReporter()
	return ""
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"os/exec"
	"time"
)

func SshRunning() bool {
	c, err := net.Dial("tcp", config.socksAddr)
	if err != nil {
		return false
	}
	c.Close()
	return true
}

func runSSH() {
	if config.sshServer == "" {
		return
	}

	_, port := splitHostPort(config.socksAddr)
	alreadyRunPrinted := false

	for {
		if SshRunning() {
			if !alreadyRunPrinted {
				errl.Println("ssh socks server maybe already running, as cow can connect to",
					config.socksAddr)
				alreadyRunPrinted = true
			}
			// check server liveness in 1 minute
			time.Sleep(60 * time.Second)
			continue
		}

		// -n redirects stdin from /dev/null
		// -N do not execute remote command
		cmd := exec.Command("ssh", "-n", "-N", "-D", port, config.sshServer)
		if err := cmd.Run(); err != nil {
			errl.Println("ssh:", err)
		}
		info.Println("ssh exited, reconnect")
		time.Sleep(5 * time.Second)
		alreadyRunPrinted = false
	}
}
<commit_msg>Use debug output for ssh error message.<commit_after>package main

import (
	"net"
	"os/exec"
	"time"
)

func SshRunning() bool {
	c, err := net.Dial("tcp", config.socksAddr)
	if err != nil {
		return false
	}
	c.Close()
	return true
}

func runSSH() {
	if config.sshServer == "" {
		return
	}

	_, port := splitHostPort(config.socksAddr)
	alreadyRunPrinted := false

	for {
		if SshRunning() {
			if !alreadyRunPrinted {
				errl.Println("ssh socks server maybe already running, as cow can connect to",
					config.socksAddr)
				alreadyRunPrinted = true
			}
			// check server liveness in 1 minute
			time.Sleep(60 * time.Second)
			continue
		}

		// -n redirects stdin from /dev/null
		// -N do not execute remote command
		cmd := exec.Command("ssh", "-n", "-N", "-D", port, config.sshServer)
		if err := cmd.Run(); err != nil {
			debug.Println("ssh:", err)
		}
		info.Println("ssh exited, reconnect")
		time.Sleep(5 * time.Second)
		alreadyRunPrinted = false
	}
}
<|endoftext|>