<commit_before>/*
 * gomacro - A Go interpreter with Lisp-like macros
 *
 * Copyright (C) 2017 Massimiliano Ghilardi
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published
 *     by the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 *
 * declaration.go
 *
 *  Created on Apr 01, 2017
 *      Author Massimiliano Ghilardi
 */

package fast

import (
	"go/ast"
	"go/token"
	r "reflect"
)

// Assign compiles an *ast.AssignStmt into an assignment to one or more place
func (c *Comp) Assign(node *ast.AssignStmt) {
	c.Pos = node.Pos()

	lhs, rhs := node.Lhs, node.Rhs
	if node.Tok == token.DEFINE {
		c.DeclVarsShort(lhs, rhs)
		return
	}
	ln, rn := len(lhs), len(rhs)
	if node.Tok == token.ASSIGN {
		if ln < 1 || (rn != 1 && ln != rn) {
			c.Errorf("invalid assignment, cannot assign %d values to %d places: %v", rn, ln, node)
		}
	} else if ln != 1 || rn != 1 {
		c.Errorf("invalid assignment, operator %s does not support multiple parallel assignments: %v", node.Tok, node)
	}

	// the naive loop
	//   for i := range lhs { c.assign1(lhs[i], node.Tok, rhs[i]) }
	// is bugged. It breaks, among others, the common Go idiom to swap two values: a,b = b,a
	//
	// More accurately, Go states at: https://golang.org/ref/spec#Assignments
	//
	// "The assignment proceeds in two phases. First, the operands of index expressions
	// and pointer indirections (including implicit pointer indirections in selectors)
	// on the left and the expressions on the right are all evaluated in the usual order.
	// Second, the assignments are carried out in left-to-right order."
	//
	// A solution is to evaluate left-to-right all places on the left,
	// then all expressions on the right, then perform all the assignments

	places := make([]*Place, ln)
	exprs := make([]*Expr, rn)
	canreorder := true
	for i, li := range lhs {
		places[i] = c.Place(li)
		canreorder = canreorder && places[i].IsVar() // ach, needed. see for example i := 0; i, x[i] = 1, 2  // set i = 1, x[0] = 2
	}
	if rn == 1 && ln > 1 {
		exprs[0] = c.Expr(rhs[0])
	} else {
		for i, ri := range rhs {
			exprs[i] = c.Expr1(ri)
			canreorder = canreorder && exprs[i].Const()
		}
	}
	if ln == rn && (ln <= 1 || canreorder) {
		for i := range lhs {
			c.assign1(lhs[i], node.Tok, rhs[i], places[i], exprs[i])
		}
		return
	}
	// problem: we need to create temporary copies of the evaluations
	// before performing the assignments. Such temporary copies must be per-goroutine!
	//
	// so a technique like the following is bugged,
	// because create a *single* global location for the temporary copy:
	//   var tmp r.Value
	//   func set(env *Env) { tmp = places[i].Fun(env) }
	//   func get(env *Env) r.Value { return tmp }

	type Assign struct {
		placefun func(*Env) r.Value
		placekey func(*Env) r.Value
		setvar   func(*Env, r.Value)
		setplace func(r.Value, r.Value, r.Value)
	}
	assign := make([]Assign, ln)
	for i, place := range places {
		a := &assign[i]
		if place.IsVar() {
			a.setvar = c.varSetValue(&place.Var)
		} else {
			a.placefun = place.Fun
			a.placekey = place.MapKey
			a.setplace = c.placeSetValue(place)
		}
	}

	exprfuns, exprxv := c.assignPrepareRhs(node, places, exprs)

	c.Code.Append(func(env *Env) (Stmt, *Env) {
		n := len(assign)
		// these buffers must be allocated at runtime, per goroutine!
		objs := make([]r.Value, n)
		keys := make([]r.Value, n)
		var tmp r.Value
		var a *Assign
		// evaluate all lhs
		for i := range assign {
			if a = &assign[i]; a.placefun == nil {
				continue
			}
			objs[i] = a.placefun(env)
			if a.placekey == nil {
				continue
			}
			// assigning to obj[key] where obj is a map:
			// obj and key do NOT need to be settable,
			// and actually Go spec tell to make a copy of their values
			if tmp = objs[i]; tmp.CanSet() {
				objs[i] = tmp.Convert(tmp.Type())
			}
			if tmp = a.placekey(env); tmp.CanSet() {
				tmp = tmp.Convert(tmp.Type())
			}
			keys[i] = tmp
		}
		// evaluate all rhs
		var vals []r.Value
		if exprxv != nil {
			_, vals = exprxv(env)
		} else {
			vals = make([]r.Value, n)
			for i, exprfun := range exprfuns {
				vals[i] = exprfun(env)
			}
		}
		// execute assignments
		for i := range assign {
			a := &assign[i]
			if a.setvar != nil {
				a.setvar(env, vals[i])
			} else {
				a.setplace(objs[i], keys[i], vals[i])
			}
		}
		env.IP++
		return env.Code[env.IP], env
	})
}

func (c *Comp) assignPrepareRhs(node *ast.AssignStmt, places []*Place, exprs []*Expr) ([]func(*Env) r.Value, func(*Env) (r.Value, []r.Value)) {
	lhs, rhs := node.Lhs, node.Rhs
	ln, rn := len(lhs), len(rhs)
	if ln == rn {
		exprfuns := make([]func(*Env) r.Value, rn)
		for i, expr := range exprs {
			tplace := places[i].Type
			if expr.Const() {
				expr.ConstTo(tplace)
			} else if !expr.Type.AssignableTo(tplace) {
				c.Pos = rhs[i].Pos()
				c.Errorf("cannot use <%v> as <%v> in assignment: %v %v %v", expr.Type, tplace, lhs[i], node.Tok, rhs[i])
			}
			exprfuns[i] = expr.AsX1()
		}
		return exprfuns, nil
	}
	if rn == 1 {
		expr := exprs[0]
		nexpr := expr.NumOut()
		if nexpr != ln {
			c.Pos = node.Pos()
			c.Errorf("invalid assignment: expression returns %d values, cannot assign them to %d places: %v", nexpr, ln, node)
		}
		for i := 0; i < nexpr; i++ {
			texpr := expr.Out(i)
			tplace := places[i].Type
			if !texpr.AssignableTo(tplace) {
				c.Pos = lhs[i].Pos()
				c.Errorf("cannot assign <%v> to %v <%v> in multiple assignment", texpr, lhs[i], tplace)
			}
		}
		return nil, expr.AsXV(CompileDefaults)
	}
	c.Pos = node.Pos()
	c.Errorf("invalid assignment, cannot assign %d values to %d places: %v", rn, ln, node)
	return nil, nil
}

// assign1 compiles a single assignment to a place
func (c *Comp) assign1(lhs ast.Expr, op token.Token, rhs ast.Expr, place *Place, init *Expr) {
	panicking := true
	defer func() {
		if !panicking {
			return
		}
		rec := recover()
		node := &ast.AssignStmt{Lhs: []ast.Expr{lhs}, Tok: op, Rhs: []ast.Expr{rhs}} // for nice error messages
		c.Errorf("error compiling assignment: %v\n\t%v", node, rec)
	}()
	if place.IsVar() {
		c.SetVar(&place.Var, op, init)
	} else {
		c.SetPlace(place, op, init)
	}
	panicking = false
}

// LookupVar compiles the left-hand-side of an assignment, in case it's an identifier (i.e. a variable name)
func (c *Comp) LookupVar(name string) *Var {
	if name == "_" {
		return &Var{}
	}
	sym := c.Resolve(name)
	return sym.AsVar(PlaceSettable)
}

// Place compiles the left-hand-side of an assignment
func (c *Comp) Place(node ast.Expr) *Place {
	return c.placeOrAddress(node, false)
}

// PlaceOrAddress compiles the left-hand-side of an assignment or the location of an address-of
func (c *Comp) placeOrAddress(in ast.Expr, opt PlaceOption) *Place {
	for {
		if in != nil {
			c.Pos = in.Pos()
		}
		switch node := in.(type) {
		case *ast.CompositeLit:
			// composite literals are addressable but not settable
			if opt == PlaceSettable {
				c.Errorf("%s composite literal", opt)
			}
			e := c.Expr1(node)
			fun := e.AsX1()
			var addr func(*Env) r.Value
			switch e.Type.Kind() {
			case r.Array, r.Struct:
				// array and struct composite literals are directly addressable
				// because they are created with reflect.New(t).Elem()
				addr = func(env *Env) r.Value {
					return fun(env).Addr()
				}
			default:
				// other composite literals (maps, slices) are not directly addressable:
				// the result of reflect.MakeMap and reflect.MakeSlice is not addressable,
				// so implement a workaround to behave as compiled Go.
				//
				// 'addr' below creates a new pointer-to-t at each execution,
				// but since the map or slice is freshly created each time
				// and 'addr' below is the only one code accessing it,
				// it's not a problem
				addr = func(env *Env) r.Value {
					obj := fun(env)
					place := r.New(obj.Type())
					place.Elem().Set(obj)
					return place
				}
			}
			return &Place{Var: Var{Type: e.Type}, Fun: fun, Addr: addr}
		case *ast.Ident:
			return c.IdentPlace(node.Name, opt)
		case *ast.IndexExpr:
			return c.IndexPlace(node, opt)
		case *ast.ParenExpr:
			in = node.X
			continue
		case *ast.StarExpr:
			e := c.Expr1(node.X)
			if e.Const() {
				c.Errorf("%s a constant: %v <%v>", opt, node, e.Type)
				return nil
			}
			// we cannot optimize the case "node.X is a variable" because we are compiling *variable, not variable
			// e.Fun is already the address we want, dereference its type
			t := e.Type.Elem()
			// c.Debugf("placeOrAddress: %v has type %v, transformed into: %v has type %v", node.X, e.Type, node, t)
			addr := e.AsX1()
			fun := func(env *Env) r.Value {
				return addr(env).Elem()
			}
			return &Place{Var: Var{Type: t}, Fun: fun, Addr: addr}
		case *ast.SelectorExpr:
			return c.SelectorPlace(node, opt)
		default:
			c.Errorf("%s: %v", opt, in)
			return nil
		}
	}
}

// placeForSideEffects compiles the left-hand-side of a do-nothing assignment,
// as for example *addressOfInt() += 0, in order to apply its side effects
func (c *Comp) placeForSideEffects(place *Place) {
	if place.IsVar() {
		return
	}
	var ret Stmt
	fun := place.Fun
	if mapkey := place.MapKey; mapkey != nil {
		ret = func(env *Env) (Stmt, *Env) {
			fun(env)
			mapkey(env)
			// no need to call obj.MapIndex(key): it has no side effects and cannot panic.
			// obj := fun(env)
			// key := mapkey(env)
			// obj.MapIndex(key)
			env.IP++
			return env.Code[env.IP], env
		}
	} else {
		ret = func(env *Env) (Stmt, *Env) {
			fun(env)
			env.IP++
			return env.Code[env.IP], env
		}
	}
	c.Code.Append(ret)
}
<commit_msg>fast: optimized swap idiom a,b = b,a<commit_after>/*
 * gomacro - A Go interpreter with Lisp-like macros
 *
 * Copyright (C) 2017 Massimiliano Ghilardi
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published
 *     by the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 *
 * declaration.go
 *
 *  Created on Apr 01, 2017
 *      Author Massimiliano Ghilardi
 */

package fast

import (
	"go/ast"
	"go/token"
	r "reflect"
)

type Assign struct {
	placefun func(*Env) r.Value
	placekey func(*Env) r.Value
	setvar   func(*Env, r.Value)
	setplace func(r.Value, r.Value, r.Value)
}

// Assign compiles an *ast.AssignStmt into an assignment to one or more place
func (c *Comp) Assign(node *ast.AssignStmt) {
	c.Pos = node.Pos()

	lhs, rhs := node.Lhs, node.Rhs
	if node.Tok == token.DEFINE {
		c.DeclVarsShort(lhs, rhs)
		return
	}
	ln, rn := len(lhs), len(rhs)
	if node.Tok == token.ASSIGN {
		if ln < 1 || (rn != 1 && ln != rn) {
			c.Errorf("invalid assignment, cannot assign %d values to %d places: %v", rn, ln, node)
		}
	} else if ln != 1 || rn != 1 {
		c.Errorf("invalid assignment, operator %s does not support multiple parallel assignments: %v", node.Tok, node)
	}

	// the naive loop
	//   for i := range lhs { c.assign1(lhs[i], node.Tok, rhs[i]) }
	// is bugged. It breaks, among others, the common Go idiom to swap two values: a,b = b,a
	//
	// More accurately, Go states at: https://golang.org/ref/spec#Assignments
	//
	// "The assignment proceeds in two phases. First, the operands of index expressions
	// and pointer indirections (including implicit pointer indirections in selectors)
	// on the left and the expressions on the right are all evaluated in the usual order.
	// Second, the assignments are carried out in left-to-right order."
	//
	// A solution is to evaluate left-to-right all places on the left,
	// then all expressions on the right, then perform all the assignments

	places := make([]*Place, ln)
	exprs := make([]*Expr, rn)
	canreorder := true
	for i, li := range lhs {
		places[i] = c.Place(li)
		canreorder = canreorder && places[i].IsVar() // ach, needed. see for example i := 0; i, x[i] = 1, 2  // set i = 1, x[0] = 2
	}
	if rn == 1 && ln > 1 {
		exprs[0] = c.Expr(rhs[0])
		canreorder = false
	} else {
		for i, ri := range rhs {
			exprs[i] = c.Expr1(ri)
			canreorder = canreorder && exprs[i].Const()
		}
	}
	if ln == rn && (ln <= 1 || canreorder) {
		for i := range lhs {
			c.assign1(lhs[i], node.Tok, rhs[i], places[i], exprs[i])
		}
		return
	}
	// problem: we need to create temporary copies of the evaluations
	// before performing the assignments. Such temporary copies must be per-goroutine!
	//
	// so a technique like the following is bugged,
	// because create a *single* global location for the temporary copy:
	//   var tmp r.Value
	//   func set(env *Env) { tmp = places[i].Fun(env) }
	//   func get(env *Env) r.Value { return tmp }

	assign := make([]Assign, ln)
	for i, place := range places {
		a := &assign[i]
		if place.IsVar() {
			a.setvar = c.varSetValue(&place.Var)
		} else {
			a.placefun = place.Fun
			a.placekey = place.MapKey
			a.setplace = c.placeSetValue(place)
		}
	}

	exprfuns, exprxv := c.assignPrepareRhs(node, places, exprs)

	if ln == 2 && rn == 2 && assign[0].placekey == nil && assign[1].placekey == nil {
		c.assign2(assign, exprfuns)
	} else {
		c.assignMulti(assign, exprfuns, exprxv)
	}
}

func (c *Comp) assignPrepareRhs(node *ast.AssignStmt, places []*Place, exprs []*Expr) ([]func(*Env) r.Value, func(*Env) (r.Value, []r.Value)) {
	lhs, rhs := node.Lhs, node.Rhs
	ln, rn := len(lhs), len(rhs)
	if ln == rn {
		exprfuns := make([]func(*Env) r.Value, rn)
		for i, expr := range exprs {
			tplace := places[i].Type
			if expr.Const() {
				expr.ConstTo(tplace)
			} else if !expr.Type.AssignableTo(tplace) {
				c.Pos = rhs[i].Pos()
				c.Errorf("cannot use <%v> as <%v> in assignment: %v %v %v", expr.Type, tplace, lhs[i], node.Tok, rhs[i])
			}
			exprfuns[i] = expr.AsX1()
		}
		return exprfuns, nil
	}
	if rn == 1 {
		expr := exprs[0]
		nexpr := expr.NumOut()
		if nexpr != ln {
			c.Pos = node.Pos()
			c.Errorf("invalid assignment: expression returns %d values, cannot assign them to %d places: %v", nexpr, ln, node)
		}
		for i := 0; i < nexpr; i++ {
			texpr := expr.Out(i)
			tplace := places[i].Type
			if !texpr.AssignableTo(tplace) {
				c.Pos = lhs[i].Pos()
				c.Errorf("cannot assign <%v> to %v <%v> in multiple assignment", texpr, lhs[i], tplace)
			}
		}
		return nil, expr.AsXV(CompileDefaults)
	}
	c.Pos = node.Pos()
	c.Errorf("invalid assignment, cannot assign %d values to %d places: %v", rn, ln, node)
	return nil, nil
}

// assign2 compiles multiple assignment to two places
func (c *Comp) assign2(assign []Assign, exprfuns []func(*Env) r.Value) {
	efuns := [2]func(*Env) r.Value{exprfuns[0], exprfuns[1]}
	var stmt Stmt
	if assign[0].placefun == nil {
		if assign[1].placefun == nil {
			setvars := [2]func(*Env, r.Value){assign[0].setvar, assign[1].setvar}
			stmt = func(env *Env) (Stmt, *Env) {
				val0 := efuns[0](env)
				val1 := efuns[1](env)
				setvars[0](env, val0)
				setvars[1](env, val1)
				env.IP++
				return env.Code[env.IP], env
			}
		} else {
			stmt = func(env *Env) (Stmt, *Env) {
				obj1 := assign[1].placefun(env)
				val0 := efuns[0](env)
				val1 := efuns[1](env)
				assign[0].setvar(env, val0)
				assign[1].setplace(obj1, obj1, val1)
				env.IP++
				return env.Code[env.IP], env
			}
		}
	} else {
		if assign[1].placefun == nil {
			stmt = func(env *Env) (Stmt, *Env) {
				obj0 := assign[0].placefun(env)
				val0 := efuns[0](env)
				val1 := efuns[1](env)
				assign[0].setplace(obj0, obj0, val0)
				assign[1].setvar(env, val1)
				env.IP++
				return env.Code[env.IP], env
			}
		} else {
			stmt = func(env *Env) (Stmt, *Env) {
				obj0 := assign[0].placefun(env)
				obj1 := assign[1].placefun(env)
				val0 := efuns[0](env)
				val1 := efuns[1](env)
				assign[0].setplace(obj0, obj0, val0)
				assign[1].setplace(obj1, obj1, val1)
				env.IP++
				return env.Code[env.IP], env
			}
		}
	}
	c.Code.Append(stmt)
}

// assignMulti compiles multiple assignment to places
func (c *Comp) assignMulti(assign []Assign, exprfuns []func(*Env) r.Value, exprxv func(*Env) (r.Value, []r.Value)) {
	c.Code.Append(func(env *Env) (Stmt, *Env) {
		n := len(assign)
		// these buffers must be allocated at runtime, per goroutine!
		objs := make([]r.Value, n)
		keys := make([]r.Value, n)
		var tmp r.Value
		var a *Assign
		// evaluate all lhs
		for i := range assign {
			if a = &assign[i]; a.placefun == nil {
				continue
			}
			objs[i] = a.placefun(env)
			if a.placekey == nil {
				continue
			}
			// assigning to obj[key] where obj is a map:
			// obj and key do NOT need to be settable,
			// and actually Go spec tell to make a copy of their values
			if tmp = objs[i]; tmp.CanSet() {
				objs[i] = tmp.Convert(tmp.Type())
			}
			if tmp = a.placekey(env); tmp.CanSet() {
				tmp = tmp.Convert(tmp.Type())
			}
			keys[i] = tmp
		}
		// evaluate all rhs
		var vals []r.Value
		if exprxv != nil {
			_, vals = exprxv(env)
		} else {
			vals = make([]r.Value, n)
			for i, exprfun := range exprfuns {
				vals[i] = exprfun(env)
			}
		}
		// execute assignments
		for i := range assign {
			a := &assign[i]
			if a.setvar != nil {
				a.setvar(env, vals[i])
			} else {
				a.setplace(objs[i], keys[i], vals[i])
			}
		}
		env.IP++
		return env.Code[env.IP], env
	})
}

// assign1 compiles a single assignment to a place
func (c *Comp) assign1(lhs ast.Expr, op token.Token, rhs ast.Expr, place *Place, init *Expr) {
	panicking := true
	defer func() {
		if !panicking {
			return
		}
		rec := recover()
		node := &ast.AssignStmt{Lhs: []ast.Expr{lhs}, Tok: op, Rhs: []ast.Expr{rhs}} // for nice error messages
		c.Errorf("error compiling assignment: %v\n\t%v", node, rec)
	}()
	if place.IsVar() {
		c.SetVar(&place.Var, op, init)
	} else {
		c.SetPlace(place, op, init)
	}
	panicking = false
}

// LookupVar compiles the left-hand-side of an assignment, in case it's an identifier (i.e. a variable name)
func (c *Comp) LookupVar(name string) *Var {
	if name == "_" {
		return &Var{}
	}
	sym := c.Resolve(name)
	return sym.AsVar(PlaceSettable)
}

// Place compiles the left-hand-side of an assignment
func (c *Comp) Place(node ast.Expr) *Place {
	return c.placeOrAddress(node, false)
}

// PlaceOrAddress compiles the left-hand-side of an assignment or the location of an address-of
func (c *Comp) placeOrAddress(in ast.Expr, opt PlaceOption) *Place {
	for {
		if in != nil {
			c.Pos = in.Pos()
		}
		switch node := in.(type) {
		case *ast.CompositeLit:
			// composite literals are addressable but not settable
			if opt == PlaceSettable {
				c.Errorf("%s composite literal", opt)
			}
			e := c.Expr1(node)
			fun := e.AsX1()
			var addr func(*Env) r.Value
			switch e.Type.Kind() {
			case r.Array, r.Struct:
				// array and struct composite literals are directly addressable
				// because they are created with reflect.New(t).Elem()
				addr = func(env *Env) r.Value {
					return fun(env).Addr()
				}
			default:
				// other composite literals (maps, slices) are not directly addressable:
				// the result of reflect.MakeMap and reflect.MakeSlice is not addressable,
				// so implement a workaround to behave as compiled Go.
				//
				// 'addr' below creates a new pointer-to-t at each execution,
				// but since the map or slice is freshly created each time
				// and 'addr' below is the only one code accessing it,
				// it's not a problem
				addr = func(env *Env) r.Value {
					obj := fun(env)
					place := r.New(obj.Type())
					place.Elem().Set(obj)
					return place
				}
			}
			return &Place{Var: Var{Type: e.Type}, Fun: fun, Addr: addr}
		case *ast.Ident:
			return c.IdentPlace(node.Name, opt)
		case *ast.IndexExpr:
			return c.IndexPlace(node, opt)
		case *ast.ParenExpr:
			in = node.X
			continue
		case *ast.StarExpr:
			e := c.Expr1(node.X)
			if e.Const() {
				c.Errorf("%s a constant: %v <%v>", opt, node, e.Type)
				return nil
			}
			// we cannot optimize the case "node.X is a variable" because we are compiling *variable, not variable
			// e.Fun is already the address we want, dereference its type
			t := e.Type.Elem()
			// c.Debugf("placeOrAddress: %v has type %v, transformed into: %v has type %v", node.X, e.Type, node, t)
			addr := e.AsX1()
			fun := func(env *Env) r.Value {
				return addr(env).Elem()
			}
			return &Place{Var: Var{Type: t}, Fun: fun, Addr: addr}
		case *ast.SelectorExpr:
			return c.SelectorPlace(node, opt)
		default:
			c.Errorf("%s: %v", opt, in)
			return nil
		}
	}
}

// placeForSideEffects compiles the left-hand-side of a do-nothing assignment,
// as for example *addressOfInt() += 0, in order to apply its side effects
func (c *Comp) placeForSideEffects(place *Place) {
	if place.IsVar() {
		return
	}
	var ret Stmt
	fun := place.Fun
	if mapkey := place.MapKey; mapkey != nil {
		ret = func(env *Env) (Stmt, *Env) {
			fun(env)
			mapkey(env)
			// no need to call obj.MapIndex(key): it has no side effects and cannot panic.
			// obj := fun(env)
			// key := mapkey(env)
			// obj.MapIndex(key)
			env.IP++
			return env.Code[env.IP], env
		}
	} else {
		ret = func(env *Env) (Stmt, *Env) {
			fun(env)
			env.IP++
			return env.Code[env.IP], env
		}
	}
	c.Code.Append(ret)
}
<|endoftext|>


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

package zktopo

import (
	"fmt"
	"path"
	"strings"
	"time"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/zk"
	"golang.org/x/net/context"
	"launchpad.net/gozk/zookeeper"
)

/*
This file contains the lock management code for zktopo.Server
*/

// lockForAction creates the action node in zookeeper, waits for the
// queue lock, displays a nice error message if it cant get it
func (zkts *Server) lockForAction(ctx context.Context, actionDir, contents string) (string, error) {
	// create the action path
	actionPath, err := zkts.zconn.Create(actionDir, contents, zookeeper.SEQUENCE|zookeeper.EPHEMERAL, zookeeper.WorldACL(zk.PERM_FILE))
	if err != nil {
		return "", err
	}

	// get the timeout from the context
	var timeout time.Duration
	deadline, ok := ctx.Deadline()
	if !ok {
		// enforce a default timeout
		timeout = 30 * time.Second
	} else {
		timeout = deadline.Sub(time.Now())
	}

	// get the interrupted channel from context or don't interrupt
	interrupted := ctx.Done()
	if interrupted == nil {
		interrupted = make(chan struct{})
	}

	err = zk.ObtainQueueLock(zkts.zconn, actionPath, timeout, interrupted)
	if err != nil {
		var errToReturn error
		switch err {
		case zk.ErrInterrupted, zk.ErrTimeout:
			// the context failed, get the error from it
			if ctx.Err() == context.DeadlineExceeded {
				errToReturn = topo.ErrTimeout
			} else {
				errToReturn = topo.ErrInterrupted
			}
		default:
			errToReturn = fmt.Errorf("failed to obtain action lock: %v %v", actionPath, err)
		}

		// Regardless of the reason, try to cleanup.
		log.Warningf("Failed to obtain action lock: %v", err)
		zkts.zconn.Delete(actionPath, -1)

		// Show the other actions in the directory
		dir := path.Dir(actionPath)
		children, _, err := zkts.zconn.Children(dir)
		if err != nil {
			log.Warningf("Failed to get children of %v: %v", dir, err)
			return "", errToReturn
		}

		if len(children) == 0 {
			log.Warningf("No other action running, you may just try again now.")
			return "", errToReturn
		}

		childPath := path.Join(dir, children[0])
		data, _, err := zkts.zconn.Get(childPath)
		if err != nil {
			log.Warningf("Failed to get first action node %v (may have just ended): %v", childPath, err)
			return "", errToReturn
		}

		log.Warningf("------ Most likely blocking action: %v\n%v", childPath, data)
		return "", errToReturn
	}

	return actionPath, nil
}

func (zkts *Server) unlockForAction(lockPath, results string) error {
	// Write the data to the actionlog
	actionLogPath := strings.Replace(lockPath, "/action/", "/actionlog/", 1)
	if _, err := zk.CreateRecursive(zkts.zconn, actionLogPath, results, 0, zookeeper.WorldACL(zookeeper.PERM_ALL)); err != nil {
		log.Warningf("Cannot create actionlog path %v (check the permissions with 'zk stat'), will keep the lock, use 'zk rm' to clear the lock", actionLogPath)
		return err
	}

	// and delete the action
	return zk.DeleteRecursive(zkts.zconn, lockPath, -1)
}

func (zkts *Server) LockKeyspaceForAction(ctx context.Context, keyspace, contents string) (string, error) {
	// Action paths end in a trailing slash to that when we create
	// sequential nodes, they are created as children, not siblings.
	actionDir := path.Join(globalKeyspacesPath, keyspace, "action") + "/"
	return zkts.lockForAction(ctx, actionDir, contents)
}

func (zkts *Server) UnlockKeyspaceForAction(keyspace, lockPath, results string) error {
	return zkts.unlockForAction(lockPath, results)
}

func (zkts *Server) LockShardForAction(ctx context.Context, keyspace, shard, contents string) (string, error) {
	// Action paths end in a trailing slash to that when we create
	// sequential nodes, they are created as children, not siblings.
	actionDir := path.Join(globalKeyspacesPath, keyspace, "shards", shard, "action") + "/"
	return zkts.lockForAction(ctx, actionDir, contents)
}

func (zkts *Server) UnlockShardForAction(keyspace, shard, lockPath, results string) error {
	return zkts.unlockForAction(lockPath, results)
}

func (zkts *Server) LockSrvShardForAction(ctx context.Context, cell, keyspace, shard, contents string) (string, error) {
	// Action paths end in a trailing slash to that when we create
	// sequential nodes, they are created as children, not siblings.
	actionDir := path.Join(zkPathForVtShard(cell, keyspace, shard), "action")

	// if we can't create the lock file because the directory doesn't exist,
	// create it
	p, err := zkts.lockForAction(ctx, actionDir+"/", contents)
	if err != nil && zookeeper.IsError(err, zookeeper.ZNONODE) {
		_, err = zk.CreateRecursive(zkts.zconn, actionDir, "", 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
		if err != nil && !zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
			return "", err
		}
		p, err = zkts.lockForAction(ctx, actionDir+"/", contents)
	}
	return p, err
}

func (zkts *Server) UnlockSrvShardForAction(cell, keyspace, shard, lockPath, results string) error {
	return zkts.unlockForAction(lockPath, results)
}
<commit_msg>Don't check ctx.Err() unless ctx.Done() was closed.<commit_after>// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package zktopo

import (
	"fmt"
	"path"
	"strings"
	"time"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/zk"
	"golang.org/x/net/context"
	"launchpad.net/gozk/zookeeper"
)

/*
This file contains the lock management code for zktopo.Server
*/

// lockForAction creates the action node in zookeeper, waits for the
// queue lock, displays a nice error message if it cant get it
func (zkts *Server) lockForAction(ctx context.Context, actionDir, contents string) (string, error) {
	// create the action path
	actionPath, err := zkts.zconn.Create(actionDir, contents, zookeeper.SEQUENCE|zookeeper.EPHEMERAL, zookeeper.WorldACL(zk.PERM_FILE))
	if err != nil {
		return "", err
	}

	// get the timeout from the context
	var timeout time.Duration
	deadline, ok := ctx.Deadline()
	if !ok {
		// enforce a default timeout
		timeout = 30 * time.Second
	} else {
		timeout = deadline.Sub(time.Now())
	}

	// get the interrupted channel from context or don't interrupt
	interrupted := ctx.Done()
	if interrupted == nil {
		interrupted = make(chan struct{})
	}

	err = zk.ObtainQueueLock(zkts.zconn, actionPath, timeout, interrupted)
	if err != nil {
		var errToReturn error
		switch err {
		case zk.ErrTimeout:
			errToReturn = topo.ErrTimeout
		case zk.ErrInterrupted:
			// the context failed, get the error from it
			if ctx.Err() == context.DeadlineExceeded {
				errToReturn = topo.ErrTimeout
			} else {
				errToReturn = topo.ErrInterrupted
			}
		default:
			errToReturn = fmt.Errorf("failed to obtain action lock: %v %v", actionPath, err)
		}

		// Regardless of the reason, try to cleanup.
		log.Warningf("Failed to obtain action lock: %v", err)
		zkts.zconn.Delete(actionPath, -1)

		// Show the other actions in the directory
		dir := path.Dir(actionPath)
		children, _, err := zkts.zconn.Children(dir)
		if err != nil {
			log.Warningf("Failed to get children of %v: %v", dir, err)
			return "", errToReturn
		}

		if len(children) == 0 {
			log.Warningf("No other action running, you may just try again now.")
			return "", errToReturn
		}

		childPath := path.Join(dir, children[0])
		data, _, err := zkts.zconn.Get(childPath)
		if err != nil {
			log.Warningf("Failed to get first action node %v (may have just ended): %v", childPath, err)
			return "", errToReturn
		}

		log.Warningf("------ Most likely blocking action: %v\n%v", childPath, data)
		return "", errToReturn
	}

	return actionPath, nil
}

func (zkts *Server) unlockForAction(lockPath, results string) error {
	// Write the data to the actionlog
	actionLogPath := strings.Replace(lockPath, "/action/", "/actionlog/", 1)
	if _, err := zk.CreateRecursive(zkts.zconn, actionLogPath, results, 0, zookeeper.WorldACL(zookeeper.PERM_ALL)); err != nil {
		log.Warningf("Cannot create actionlog path %v (check the permissions with 'zk stat'), will keep the lock, use 'zk rm' to clear the lock", actionLogPath)
		return err
	}

	// and delete the action
	return zk.DeleteRecursive(zkts.zconn, lockPath, -1)
}

func (zkts *Server) LockKeyspaceForAction(ctx context.Context, keyspace, contents string) (string, error) {
	// Action paths end in a trailing slash to that when we create
	// sequential nodes, they are created as children, not siblings.
	actionDir := path.Join(globalKeyspacesPath, keyspace, "action") + "/"
	return zkts.lockForAction(ctx, actionDir, contents)
}

func (zkts *Server) UnlockKeyspaceForAction(keyspace, lockPath, results string) error {
	return zkts.unlockForAction(lockPath, results)
}

func (zkts *Server) LockShardForAction(ctx context.Context, keyspace, shard, contents string) (string, error) {
	// Action paths end in a trailing slash to that when we create
	// sequential nodes, they are created as children, not siblings.
	actionDir := path.Join(globalKeyspacesPath, keyspace, "shards", shard, "action") + "/"
	return zkts.lockForAction(ctx, actionDir, contents)
}

func (zkts *Server) UnlockShardForAction(keyspace, shard, lockPath, results string) error {
	return zkts.unlockForAction(lockPath, results)
}

func (zkts *Server) LockSrvShardForAction(ctx context.Context, cell, keyspace, shard, contents string) (string, error) {
	// Action paths end in a trailing slash to that when we create
	// sequential nodes, they are created as children, not siblings.
	actionDir := path.Join(zkPathForVtShard(cell, keyspace, shard), "action")

	// if we can't create the lock file because the directory doesn't exist,
	// create it
	p, err := zkts.lockForAction(ctx, actionDir+"/", contents)
	if err != nil && zookeeper.IsError(err, zookeeper.ZNONODE) {
		_, err = zk.CreateRecursive(zkts.zconn, actionDir, "", 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
		if err != nil && !zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
			return "", err
		}
		p, err = zkts.lockForAction(ctx, actionDir+"/", contents)
	}
	return p, err
}

func (zkts *Server) UnlockSrvShardForAction(cell, keyspace, shard, lockPath, results string) error {
	return zkts.unlockForAction(lockPath, results)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os/exec"
	"time"

	pb "github.com/brotherlogic/gobuildslave/proto"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(job *pb.JobAssignment) {
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(job.Job)
		if job.Job.NonBootstrap {
			if key != "" {
				job.Server = s.Registry.Identifier
				job.State = pb.State_BUILT
				job.RunningVersion = key
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
			job.Server = s.Registry.Identifier
		}
	case pb.State_BUILDING:
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output := s.scheduler.getOutput(job.CommandKey)
		if len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(job, output)
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
		}
	case pb.State_PENDING:
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		if s.taskComplete(job.CommandKey) {
			output := s.scheduler.getOutput(job.CommandKey)
			s.deliverCrashReport(job, output)
			job.State = pb.State_DIED
		}
	case pb.State_DIED:
		job.State = pb.State_ACKNOWLEDGED
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(job *pb.JobAssignment) bool
}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(job *pb.Job) string {
	if !job.NonBootstrap {
		c := s.translator.build(job)
		return s.scheduler.Schedule(&rCommand{command: c})
	}

	versions := s.builder.build(job)

	if len(versions) == 0 {
		s.Log(fmt.Sprintf("No versions received for %v", job.Name))
		return ""
	}

	s.Log(fmt.Sprintf("COPYING WITH %v", s.builder))
	err := s.builder.copy(versions[0])
	if err != nil {
		return ""
	}
	return versions[0].Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<commit_msg>Adds some more logging in to the mix<commit_after>package main

import (
	"fmt"
	"os/exec"
	"time"

	pb "github.com/brotherlogic/gobuildslave/proto"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(job *pb.JobAssignment) {
	stState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(job.Job)
		if job.Job.NonBootstrap {
			if key != "" {
				job.Server = s.Registry.Identifier
				job.State = pb.State_BUILT
				job.RunningVersion = key
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
			job.Server = s.Registry.Identifier
		}
	case pb.State_BUILDING:
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output := s.scheduler.getOutput(job.CommandKey)
		if len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(job, output)
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
		}
	case pb.State_PENDING:
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		if s.taskComplete(job.CommandKey) {
			output := s.scheduler.getOutput(job.CommandKey)
			s.deliverCrashReport(job, output)
			job.State = pb.State_DIED
		}
	case pb.State_DIED:
		job.State = pb.State_ACKNOWLEDGED
	}
	if job.State != stState {
		s.Log(fmt.Sprintf("Job %v went from %v to %v", job.Job.Name, stState, job.State))
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(job *pb.JobAssignment) bool
}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(job *pb.Job) string {
	if !job.NonBootstrap {
		c := s.translator.build(job)
		return s.scheduler.Schedule(&rCommand{command: c})
	}

	versions := s.builder.build(job)

	if len(versions) == 0 {
		s.Log(fmt.Sprintf("No versions received for %v", job.Name))
		return ""
	}

	s.Log(fmt.Sprintf("COPYING WITH %v", s.builder))
	err := s.builder.copy(versions[0])
	if err != nil {
		return ""
	}
	return versions[0].Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<|endoftext|>


<commit_before>// Extensions to the go-check unittest framework.
//
// NOTE: see https://github.com/go-check/check/pull/6 for reasons why these
// checkers live here.
package gocheck2

import (
    "bytes"

	. "gopkg.in/check.v1"
)

// -----------------------------------------------------------------------
// IsTrue / IsFalse checker.

type isBoolValueChecker struct {
	*CheckerInfo
	expected bool
}

func (checker *isBoolValueChecker) Check(
	params []interface{},
	names []string) (
	result bool,
	error string) {

	obtained, ok := params[0].(bool)
	if !ok {
		return false, "Argument to " + checker.Name + " must be bool"
	}

	return obtained == checker.expected, ""
}

// The IsTrue checker verifies that the obtained value is true.
//
// For example:
//
//     c.Assert(value, IsTrue)
//
var IsTrue Checker = &isBoolValueChecker{
	&CheckerInfo{Name: "IsTrue", Params: []string{"obtained"}},
	true,
}

// The IsFalse checker verifies that the obtained value is false.
//
// For example:
//
//     c.Assert(value, IsFalse)
//
var IsFalse Checker = &isBoolValueChecker{
	&CheckerInfo{Name: "IsFalse", Params: []string{"obtained"}},
	false,
}

// -----------------------------------------------------------------------
// BytesEqual checker compares two bytes sequence using bytes.Equal

type bytesEquals struct{}

func (b *bytesEquals) Check(params []interface{}, names []string) (bool, string) {
    if len(params) != 2 {
        return false, "BytesEqual takes 2 bytestring arguments"
    }
    b1, ok1 := params[0].([]byte)
    b2, ok2 := params[1].([]byte)

    if !(ok1 && ok2) {
        return false, "Arguments to BytesEqual must both be bytestrings"
    }

    if bytes.Equal(b1, b2) {
        return true, ""
    }
    return false, "Byte arrays were different"
}

func (b *bytesEquals) Info() *CheckerInfo {
    return &CheckerInfo{
        Name:   "BytesEquals",
        Params: []string{"bytes_one", "bytes_two"},
    }
}

var BytesEquals = &bytesEquals{}
<commit_msg>Apply gofmt<commit_after>// Extensions to the go-check unittest framework.
//
// NOTE: see https://github.com/go-check/check/pull/6 for reasons why these
// checkers live here.
package gocheck2

import (
	"bytes"

	. "gopkg.in/check.v1"
)

// -----------------------------------------------------------------------
// IsTrue / IsFalse checker.

type isBoolValueChecker struct {
	*CheckerInfo
	expected bool
}

func (checker *isBoolValueChecker) Check(
	params []interface{},
	names []string) (
	result bool,
	error string) {

	obtained, ok := params[0].(bool)
	if !ok {
		return false, "Argument to " + checker.Name + " must be bool"
	}

	return obtained == checker.expected, ""
}

// The IsTrue checker verifies that the obtained value is true.
//
// For example:
//
//     c.Assert(value, IsTrue)
//
var IsTrue Checker = &isBoolValueChecker{
	&CheckerInfo{Name: "IsTrue", Params: []string{"obtained"}},
	true,
}

// The IsFalse checker verifies that the obtained value is false.
//
// For example:
//
//     c.Assert(value, IsFalse)
//
var IsFalse Checker = &isBoolValueChecker{
	&CheckerInfo{Name: "IsFalse", Params: []string{"obtained"}},
	false,
}

// -----------------------------------------------------------------------
// BytesEqual checker compares two bytes sequence using bytes.Equal

type bytesEquals struct{}

func (b *bytesEquals) Check(params []interface{}, names []string) (bool, string) {
	if len(params) != 2 {
		return false, "BytesEqual takes 2 bytestring arguments"
	}
	b1, ok1 := params[0].([]byte)
	b2, ok2 := params[1].([]byte)

	if !(ok1 && ok2) {
		return false, "Arguments to BytesEqual must both be bytestrings"
	}

	if bytes.Equal(b1, b2) {
		return true, ""
	}
	return false, "Byte arrays were different"
}

func (b *bytesEquals) Info() *CheckerInfo {
	return &CheckerInfo{
		Name:   "BytesEquals",
		Params: []string{"bytes_one", "bytes_two"},
	}
}

var BytesEquals = &bytesEquals{}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

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

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

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

package importx

import (
	"errors"
	"flag"
	"fmt"
	"path"
	"reflect"

	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/govc/util"
	"github.com/vmware/govmomi/vim25"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/soap"
	"github.com/vmware/govmomi/vim25/types"
)

type vmdk struct {
	*flags.DatastoreFlag
	*flags.ResourcePoolFlag
	*flags.OutputFlag

	upload bool
	force  bool
	keep   bool

	Client       *govmomi.Client
	Datacenter   *govmomi.Datacenter
	Datastore    *govmomi.Datastore
	ResourcePool *govmomi.ResourcePool
}

func init() {
	cli.Register("import.vmdk", &vmdk{})
	cli.Alias("import.vmdk", "datastore.import")
}

func (cmd *vmdk) Register(f *flag.FlagSet) {
	f.BoolVar(&cmd.upload, "upload", true, "Upload specified disk")
	f.BoolVar(&cmd.force, "force", false, "Overwrite existing disk")
	f.BoolVar(&cmd.keep, "keep", false, "Keep uploaded disk after import")
}

func (cmd *vmdk) Process() error { return nil }

func (cmd *vmdk) Run(f *flag.FlagSet) error {
	var err error

	args := f.Args()
	if len(args) != 1 {
		return errors.New("no file to import")
	}

	file := importable(f.Arg(0))

	cmd.Client, err = cmd.DatastoreFlag.Client()
	if err != nil {
		return err
	}

	cmd.Datacenter, err = cmd.DatastoreFlag.Datacenter()
	if err != nil {
		return err
	}

	cmd.Datastore, err = cmd.DatastoreFlag.Datastore()
	if err != nil {
		return err
	}

	cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool()
	if err != nil {
		return err
	}

	if cmd.upload {
		err = cmd.Upload(file)
		if err != nil {
			return err
		}
	}

	return cmd.Import(file)
}

func (cmd *vmdk) Import(i importable) error {
	err := cmd.Copy(i)
	if err != nil {
		return err
	}

	if !cmd.keep {
		err = cmd.Delete(path.Dir(i.RemoteVMDK()))
		if err != nil {
			return err
		}
	}

	return nil
}

func (cmd *vmdk) Upload(i importable) error {
	u, err := cmd.Datastore.URL(cmd.Client, cmd.Datacenter, i.RemoteVMDK())
	if err != nil {
		return err
	}

	p := soap.DefaultUpload
	if cmd.OutputFlag.TTY {
		ch := make(chan vim25.Progress)
		wg := cmd.ProgressLogger("Uploading... ", ch)
		defer wg.Wait()

		p.ProgressCh = ch
	}

	return cmd.Client.Client.UploadFile(string(i), u, &p)
}

func (cmd *vmdk) Copy(i importable) error {
	var err error

	pa := util.NewProgressAggregator(1)
	wg := cmd.ProgressLogger("Importing... ", pa.C)
	switch p := cmd.Client.ServiceContent.About.ApiType; p {
	case "HostAgent":
		err = cmd.CopyHostAgent(i, pa)
	case "VirtualCenter":
		err = cmd.CopyVirtualCenter(i, pa)
	default:
		return fmt.Errorf("unsupported product line: %s", p)
	}

	pa.Done()
	wg.Wait()

	return err
}

type basicProgressWrapper struct {
	detail string
	err    error
}

func (b basicProgressWrapper) Percentage() float32 {
	return 0.0
}

func (b basicProgressWrapper) Detail() string {
	return b.detail
}

func (b basicProgressWrapper) Error() error {
	return b.err
}

// PrepareDestination makes sure that the destination VMDK does not yet exist.
// If the force flag is passed, it removes the existing VMDK. This functions
// exists to give a meaningful error if the remote VMDK already exists.
//
// CopyVirtualDisk can return a "<src> file does not exist" error while in fact
// the source file *does* exist and the *destination* file also exist.
//
func (cmd *vmdk) PrepareDestination(i importable) error {
	b, err := cmd.Datastore.Browser(cmd.Client)
	if err != nil {
		return err
	}

	vmdkPath := i.RemoteDst()
	spec := types.HostDatastoreBrowserSearchSpec{
		Details: &types.FileQueryFlags{
			FileType:  true,
			FileOwner: true, // TODO: omitempty is generated, but seems to be required
		},
		MatchPattern: []string{path.Base(vmdkPath)},
	}

	dsPath := cmd.Datastore.Path(path.Dir(vmdkPath))
	task, err := b.SearchDatastore(cmd.Client, dsPath, &spec)
	if err != nil {
		return err
	}

	// Don't use progress aggregator here; an error may be a good thing.
	info, err := task.WaitForResult(nil)
	if err != nil {
		if info.Error != nil {
			_, ok := info.Error.Fault.(*types.FileNotFound)
			if ok {
				// FileNotFound means the base path doesn't exist. Create it.
				dsPath := cmd.Datastore.Path(path.Dir(vmdkPath))
				return cmd.Client.FileManager().MakeDirectory(dsPath, cmd.Datacenter, true)
			}
		}

		return err
	}

	res := info.Result.(types.HostDatastoreBrowserSearchResults)
	if len(res.File) == 0 {
		// Destination path doesn't exist; all good to continue with import.
		return nil
	}

	// Check that the returned entry has the right type.
	switch res.File[0].(type) {
	case *types.VmDiskFileInfo:
	default:
		expected := "VmDiskFileInfo"
		actual := reflect.TypeOf(res.File[0])
		panic(fmt.Sprintf("Expected: %s, actual: %s", expected, actual))
	}

	if !cmd.force {
		dsPath := cmd.Datastore.Path(vmdkPath)
		err = fmt.Errorf("File %s already exists", dsPath)
		return err
	}

	// Delete existing disk.
	vdm := cmd.Client.VirtualDiskManager()
	task, err = vdm.DeleteVirtualDisk(cmd.Datastore.Path(vmdkPath), cmd.Datacenter)
	if err != nil {
		return err
	}

	return task.Wait()
}

func (cmd *vmdk) CopyHostAgent(i importable, pa *util.ProgressAggregator) error {
	pch := pa.NewChannel("preparing destination")
	pch <- basicProgressWrapper{}
	err := cmd.PrepareDestination(i)
	pch <- basicProgressWrapper{err: err}
	close(pch)
	if err != nil {
		return err
	}

	spec := &types.VirtualDiskSpec{
		AdapterType: "lsiLogic",
		DiskType:    "thin",
	}

	dc := cmd.Datacenter
	src := cmd.Datastore.Path(i.RemoteVMDK())
	dst := cmd.Datastore.Path(i.RemoteDst())
	vdm := cmd.Client.VirtualDiskManager()
	task, err := vdm.CopyVirtualDisk(src, dc, dst, dc, spec, false)
	if err != nil {
		return err
	}

	pch = pa.NewChannel("copying disk")
	_, err = task.WaitForResult(pch)
	if err != nil {
		return err
	}

	return nil
}

func (cmd *vmdk) CopyVirtualCenter(i importable, pa *util.ProgressAggregator) error {
	var err error

	dstName := path.Dir(i.RemoteDst())
	srcName := dstName + "-src"

	spec := &configSpec{
		Name:    srcName,
		GuestId: "otherGuest",
		Files: &types.VirtualMachineFileInfo{
			VmPathName: fmt.Sprintf("[%s]", cmd.Datastore.Name()),
		},
	}

	spec.AddDisk(cmd.Datastore, i.RemoteVMDK())

	src, err := cmd.CreateVM(spec)
	if err != nil {
		return err
	}

	dst, err := cmd.CloneVM(src, dstName)
	if err != nil {
		return err
	}

	err = cmd.DestroyVM(src)
	if err != nil {
		return err
	}

	err = cmd.DestroyVM(dst)
	if err != nil {
		return err
	}

	return nil
}

func (cmd *vmdk) Move(src, dst string) error {
	fm := cmd.Client.FileManager()
	dsSrc := cmd.Datastore.Path(src)
	dsDst := cmd.Datastore.Path(dst)
	task, err := fm.MoveDatastoreFile(dsSrc, cmd.Datacenter, dsDst, cmd.Datacenter, true)
	if err != nil {
		return err
	}

	return task.Wait()
}

func (cmd *vmdk) Delete(path string) error {
	fm := cmd.Client.FileManager()
	dsPath := cmd.Datastore.Path(path)
	task, err := fm.DeleteDatastoreFile(dsPath, cmd.Datacenter)
	if err != nil {
		return err
	}

	return task.Wait()
}

func (cmd *vmdk) CreateVM(spec *configSpec) (*govmomi.VirtualMachine, error) {
	folders, err := cmd.Datacenter.Folders(cmd.Client)
	if err != nil {
		return nil, err
	}

	task, err := folders.VmFolder.CreateVM(cmd.Client, spec.ToSpec(), cmd.ResourcePool, nil)
	if err != nil {
		return nil, err
	}

	info, err := task.WaitForResult(nil)
	if err != nil {
		return nil, err
	}

	return govmomi.NewVirtualMachine(info.Result.(types.ManagedObjectReference)), nil
}

func (cmd *vmdk) CloneVM(vm *govmomi.VirtualMachine, name string) (*govmomi.VirtualMachine, error) {
	folders, err := cmd.Datacenter.Folders(cmd.Client)
	if err != nil {
		return nil, err
	}

	spec := types.VirtualMachineCloneSpec{
		Config:   &types.VirtualMachineConfigSpec{},
		Location: types.VirtualMachineRelocateSpec{},
	}

	task, err := vm.Clone(cmd.Client, folders.VmFolder, name, spec)
	if err != nil {
		return nil, err
	}

	info, err := task.WaitForResult(nil)
	if err != nil {
		return nil, err
	}

	return govmomi.NewVirtualMachine(info.Result.(types.ManagedObjectReference)), nil
}

func (cmd *vmdk) DestroyVM(vm *govmomi.VirtualMachine) error {
	var mvm mo.VirtualMachine

	err := cmd.Client.Properties(vm.Reference(), []string{"config.hardware"}, &mvm)
	if err != nil {
		return err
	}

	spec := new(configSpec)
	spec.RemoveDisks(&mvm)

	task, err := vm.Reconfigure(cmd.Client, spec.ToSpec())
	if err != nil {
		return err
	}

	err = task.Wait()
	if err != nil {
		return err
	}

	task, err = vm.Destroy(cmd.Client)
	if err != nil {
		return err
	}

	err = task.Wait()
	if err != nil {
		return err
	}

	return nil
}

type configSpec types.VirtualMachineConfigSpec

func (c *configSpec) ToSpec() types.VirtualMachineConfigSpec {
	return types.VirtualMachineConfigSpec(*c)
}

func (c *configSpec) AddChange(d types.BaseVirtualDeviceConfigSpec) {
	c.DeviceChange = append(c.DeviceChange, d)
}

func (c *configSpec) AddDisk(ds *govmomi.Datastore, path string) {
	controller := &types.VirtualLsiLogicController{
		VirtualSCSIController: types.VirtualSCSIController{
			SharedBus: types.VirtualSCSISharingNoSharing,
			VirtualController: types.VirtualController{
				BusNumber: 0,
				VirtualDevice: types.VirtualDevice{
					Key: -1,
				},
			},
		},
	}

	controllerSpec := &types.VirtualDeviceConfigSpec{
		Device:    controller,
		Operation: types.VirtualDeviceConfigSpecOperationAdd,
	}

	c.AddChange(controllerSpec)

	disk := &types.VirtualDisk{
		VirtualDevice: types.VirtualDevice{
			Key:           -1,
			ControllerKey: -1,
			UnitNumber:    -1,
			Backing: &types.VirtualDiskFlatVer2BackingInfo{
				VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
					FileName: ds.Path(path),
				},
				DiskMode:        string(types.VirtualDiskModePersistent),
				ThinProvisioned: true,
			},
		},
	}

	diskSpec := &types.VirtualDeviceConfigSpec{
		Device:    disk,
		Operation: types.VirtualDeviceConfigSpecOperationAdd,
	}

	c.AddChange(diskSpec)
}

func (c *configSpec) RemoveDisks(vm *mo.VirtualMachine) {
	for _, d := range vm.Config.Hardware.Device {
		switch device := d.(type) {
		case *types.VirtualDisk:
			removeOp := &types.VirtualDeviceConfigSpec{
				Operation: types.VirtualDeviceConfigSpecOperationRemove,
				Device:    device,
			}

			c.AddChange(removeOp)
		}
	}
}
<commit_msg>Use DatastoreFlag.Stat method in vmdk.PrepareDestination<commit_after>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

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

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

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

package importx

import (
	"errors"
	"flag"
	"fmt"
	"path"
	"reflect"

	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/govc/util"
	"github.com/vmware/govmomi/vim25"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/soap"
	"github.com/vmware/govmomi/vim25/types"
)

type vmdk struct {
	*flags.DatastoreFlag
	*flags.ResourcePoolFlag
	*flags.OutputFlag

	upload bool
	force  bool
	keep   bool

	Client       *govmomi.Client
	Datacenter   *govmomi.Datacenter
	Datastore    *govmomi.Datastore
	ResourcePool *govmomi.ResourcePool
}

func init() {
	cli.Register("import.vmdk", &vmdk{})
	cli.Alias("import.vmdk", "datastore.import")
}

func (cmd *vmdk) Register(f *flag.FlagSet) {
	f.BoolVar(&cmd.upload, "upload", true, "Upload specified disk")
	f.BoolVar(&cmd.force, "force", false, "Overwrite existing disk")
	f.BoolVar(&cmd.keep, "keep", false, "Keep uploaded disk after import")
}

func (cmd *vmdk) Process() error { return nil }

func (cmd *vmdk) Run(f *flag.FlagSet) error {
	var err error

	args := f.Args()
	if len(args) != 1 {
		return errors.New("no file to import")
	}

	file := importable(f.Arg(0))

	cmd.Client, err = cmd.DatastoreFlag.Client()
	if err != nil {
		return err
	}

	cmd.Datacenter, err = cmd.DatastoreFlag.Datacenter()
	if err != nil {
		return err
	}

	cmd.Datastore, err = cmd.DatastoreFlag.Datastore()
	if err != nil {
		return err
	}

	cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool()
	if err != nil {
		return err
	}

	if cmd.upload {
		err = cmd.Upload(file)
		if err != nil {
			return err
		}
	}

	return cmd.Import(file)
}

func (cmd *vmdk) Import(i importable) error {
	err := cmd.Copy(i)
	if err != nil {
		return err
	}

	if !cmd.keep {
		err = cmd.Delete(path.Dir(i.RemoteVMDK()))
		if err != nil {
			return err
		}
	}

	return nil
}

func (cmd *vmdk) Upload(i importable) error {
	u, err := cmd.Datastore.URL(cmd.Client, cmd.Datacenter, i.RemoteVMDK())
	if err != nil {
		return err
	}

	p := soap.DefaultUpload
	if cmd.OutputFlag.TTY {
		ch := make(chan vim25.Progress)
		wg := cmd.ProgressLogger("Uploading... ", ch)
		defer wg.Wait()

		p.ProgressCh = ch
	}

	return cmd.Client.Client.UploadFile(string(i), u, &p)
}

func (cmd *vmdk) Copy(i importable) error {
	var err error

	pa := util.NewProgressAggregator(1)
	wg := cmd.ProgressLogger("Importing... ", pa.C)
	switch p := cmd.Client.ServiceContent.About.ApiType; p {
	case "HostAgent":
		err = cmd.CopyHostAgent(i, pa)
	case "VirtualCenter":
		err = cmd.CopyVirtualCenter(i, pa)
	default:
		return fmt.Errorf("unsupported product line: %s", p)
	}

	pa.Done()
	wg.Wait()

	return err
}

type basicProgressWrapper struct {
	detail string
	err    error
}

func (b basicProgressWrapper) Percentage() float32 {
	return 0.0
}

func (b basicProgressWrapper) Detail() string {
	return b.detail
}

func (b basicProgressWrapper) Error() error {
	return b.err
}

// PrepareDestination makes sure that the destination VMDK does not yet exist.
// If the force flag is passed, it removes the existing VMDK. This functions
// exists to give a meaningful error if the remote VMDK already exists.
//
// CopyVirtualDisk can return a "<src> file does not exist" error while in fact
// the source file *does* exist and the *destination* file also exist.
//
func (cmd *vmdk) PrepareDestination(i importable) error {
	vmdkPath := i.RemoteDst()

	res, err := cmd.Stat(vmdkPath)
	if err != nil {
		switch err {
		case flags.ErrDatastoreDirNotExist:
			// The base path doesn't exist. Create it.
			dsPath := cmd.Datastore.Path(path.Dir(vmdkPath))
			return cmd.Client.FileManager().MakeDirectory(dsPath, cmd.Datacenter, true)
		case flags.ErrDatastoreFileNotExist:
			// Destination path doesn't exist; all good to continue with import.
			return nil
		}

		return err
	}

	// Check that the returned entry has the right type.
	switch res.(type) {
	case *types.VmDiskFileInfo:
	default:
		expected := "VmDiskFileInfo"
		actual := reflect.TypeOf(res)
		panic(fmt.Sprintf("Expected: %s, actual: %s", expected, actual))
	}

	if !cmd.force {
		dsPath := cmd.Datastore.Path(vmdkPath)
		err = fmt.Errorf("File %s already exists", dsPath)
		return err
	}

	// Delete existing disk.
	vdm := cmd.Client.VirtualDiskManager()
	task, err := vdm.DeleteVirtualDisk(cmd.Datastore.Path(vmdkPath), cmd.Datacenter)
	if err != nil {
		return err
	}

	return task.Wait()
}

func (cmd *vmdk) CopyHostAgent(i importable, pa *util.ProgressAggregator) error {
	pch := pa.NewChannel("preparing destination")
	pch <- basicProgressWrapper{}
	err := cmd.PrepareDestination(i)
	pch <- basicProgressWrapper{err: err}
	close(pch)
	if err != nil {
		return err
	}

	spec := &types.VirtualDiskSpec{
		AdapterType: "lsiLogic",
		DiskType:    "thin",
	}

	dc := cmd.Datacenter
	src := cmd.Datastore.Path(i.RemoteVMDK())
	dst := cmd.Datastore.Path(i.RemoteDst())
	vdm := cmd.Client.VirtualDiskManager()
	task, err := vdm.CopyVirtualDisk(src, dc, dst, dc, spec, false)
	if err != nil {
		return err
	}

	pch = pa.NewChannel("copying disk")
	_, err = task.WaitForResult(pch)
	if err != nil {
		return err
	}

	return nil
}

func (cmd *vmdk) CopyVirtualCenter(i importable, pa *util.ProgressAggregator) error {
	var err error

	dstName := path.Dir(i.RemoteDst())
	srcName := dstName + "-src"

	spec := &configSpec{
		Name:    srcName,
		GuestId: "otherGuest",
		Files: &types.VirtualMachineFileInfo{
			VmPathName: fmt.Sprintf("[%s]", cmd.Datastore.Name()),
		},
	}

	spec.AddDisk(cmd.Datastore, i.RemoteVMDK())

	src, err := cmd.CreateVM(spec)
	if err != nil {
		return err
	}

	dst, err := cmd.CloneVM(src, dstName)
	if err != nil {
		return err
	}

	err = cmd.DestroyVM(src)
	if err != nil {
		return err
	}

	err = cmd.DestroyVM(dst)
	if err != nil {
		return err
	}

	return nil
}

func (cmd *vmdk) Move(src, dst string) error {
	fm := cmd.Client.FileManager()
	dsSrc := cmd.Datastore.Path(src)
	dsDst := cmd.Datastore.Path(dst)
	task, err := fm.MoveDatastoreFile(dsSrc, cmd.Datacenter, dsDst, cmd.Datacenter, true)
	if err != nil {
		return err
	}

	return task.Wait()
}

func (cmd *vmdk) Delete(path string) error {
	fm := cmd.Client.FileManager()
	dsPath := cmd.Datastore.Path(path)
	task, err := fm.DeleteDatastoreFile(dsPath, cmd.Datacenter)
	if err != nil {
		return err
	}

	return task.Wait()
}

func (cmd *vmdk) CreateVM(spec *configSpec) (*govmomi.VirtualMachine, error) {
	folders, err := cmd.Datacenter.Folders(cmd.Client)
	if err != nil {
		return nil, err
	}

	task, err := folders.VmFolder.CreateVM(cmd.Client, spec.ToSpec(), cmd.ResourcePool, nil)
	if err != nil {
		return nil, err
	}

	info, err := task.WaitForResult(nil)
	if err != nil {
		return nil, err
	}

	return govmomi.NewVirtualMachine(info.Result.(types.ManagedObjectReference)), nil
}

func (cmd *vmdk) CloneVM(vm *govmomi.VirtualMachine, name string) (*govmomi.VirtualMachine, error) {
	folders, err := cmd.Datacenter.Folders(cmd.Client)
	if err != nil {
		return nil, err
	}

	spec := types.VirtualMachineCloneSpec{
		Config:   &types.VirtualMachineConfigSpec{},
		Location: types.VirtualMachineRelocateSpec{},
	}

	task, err := vm.Clone(cmd.Client, folders.VmFolder, name, spec)
	if err != nil {
		return nil, err
	}

	info, err := task.WaitForResult(nil)
	if err != nil {
		return nil, err
	}

	return govmomi.NewVirtualMachine(info.Result.(types.ManagedObjectReference)), nil
}

func (cmd *vmdk) DestroyVM(vm *govmomi.VirtualMachine) error {
	var mvm mo.VirtualMachine

	err := cmd.Client.Properties(vm.Reference(), []string{"config.hardware"}, &mvm)
	if err != nil {
		return err
	}

	spec := new(configSpec)
	spec.RemoveDisks(&mvm)

	task, err := vm.Reconfigure(cmd.Client, spec.ToSpec())
	if err != nil {
		return err
	}

	err = task.Wait()
	if err != nil {
		return err
	}

	task, err = vm.Destroy(cmd.Client)
	if err != nil {
		return err
	}

	err = task.Wait()
	if err != nil {
		return err
	}

	return nil
}

type configSpec types.VirtualMachineConfigSpec

func (c *configSpec) ToSpec() types.VirtualMachineConfigSpec {
	return types.VirtualMachineConfigSpec(*c)
}

func (c *configSpec) AddChange(d types.BaseVirtualDeviceConfigSpec) {
	c.DeviceChange = append(c.DeviceChange, d)
}

func (c *configSpec) AddDisk(ds *govmomi.Datastore, path string) {
	controller := &types.VirtualLsiLogicController{
		VirtualSCSIController: types.VirtualSCSIController{
			SharedBus: types.VirtualSCSISharingNoSharing,
			VirtualController: types.VirtualController{
				BusNumber: 0,
				VirtualDevice: types.VirtualDevice{
					Key: -1,
				},
			},
		},
	}

	controllerSpec := &types.VirtualDeviceConfigSpec{
		Device:    controller,
		Operation: types.VirtualDeviceConfigSpecOperationAdd,
	}

	c.AddChange(controllerSpec)

	disk := &types.VirtualDisk{
		VirtualDevice: types.VirtualDevice{
			Key:           -1,
			ControllerKey: -1,
			UnitNumber:    -1,
			Backing: &types.VirtualDiskFlatVer2BackingInfo{
				VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
					FileName: ds.Path(path),
				},
				DiskMode:        string(types.VirtualDiskModePersistent),
				ThinProvisioned: true,
			},
		},
	}

	diskSpec := &types.VirtualDeviceConfigSpec{
		Device:    disk,
		Operation: types.VirtualDeviceConfigSpecOperationAdd,
	}

	c.AddChange(diskSpec)
}

func (c *configSpec) RemoveDisks(vm *mo.VirtualMachine) {
	for _, d := range vm.Config.Hardware.Device {
		switch device := d.(type) {
		case *types.VirtualDisk:
			removeOp := &types.VirtualDeviceConfigSpec{
				Operation: types.VirtualDeviceConfigSpecOperationRemove,
				Device:    device,
			}

			c.AddChange(removeOp)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

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

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

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

package addressableservice

import (
	"context"
	"fmt"
	"reflect"

	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	corev1listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"knative.dev/pkg/apis"
	duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/tracker"
	"knative.dev/sample-controller/pkg/apis/samples/v1alpha1"
	clientset "knative.dev/sample-controller/pkg/client/clientset/versioned"
	listers "knative.dev/sample-controller/pkg/client/listers/samples/v1alpha1"
)

// Reconciler implements controller.Reconciler for AddressableService resources.
type Reconciler struct {
	// Client is used to write back status updates.
	Client clientset.Interface

	// Listers index properties about resources
	Lister        listers.AddressableServiceLister
	ServiceLister corev1listers.ServiceLister

	// The tracker builds an index of what resources are watching other
	// resources so that we can immediately react to changes to changes in
	// tracked resources.
	Tracker tracker.Interface

	// Recorder is an event recorder for recording Event resources to the
	// Kubernetes API.
	Recorder record.EventRecorder
}

// Check that our Reconciler implements controller.Reconciler
var _ controller.Reconciler = (*Reconciler)(nil)

// Reconcile implements controller.Reconciler
func (r *Reconciler) Reconcile(ctx context.Context, key string) error {
	logger := logging.FromContext(ctx)

	// Convert the namespace/name string into a distinct namespace and name
	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		logger.Errorf("invalid resource key: %s", key)
		return nil
	}

	// If our controller has configuration state, we'd "freeze" it and
	// attach the frozen configuration to the context.
	//    ctx = r.configStore.ToContext(ctx)

	// Get the resource with this namespace/name.
	original, err := r.Lister.AddressableServices(namespace).Get(name)
	if apierrs.IsNotFound(err) {
		// The resource may no longer exist, in which case we stop processing.
		logger.Errorf("resource %q no longer exists", key)
		return nil
	} else if err != nil {
		return err
	}
	// Don't modify the informers copy.
	resource := original.DeepCopy()

	// Reconcile this copy of the resource and then write back any status
	// updates regardless of whether the reconciliation errored out.
	reconcileErr := r.reconcile(ctx, resource)
	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
		// If we didn't change anything then don't call updateStatus.
		// This is important because the copy we loaded from the informer's
		// cache may be stale and we don't want to overwrite a prior update
		// to status with this stale state.
	} else if _, err = r.updateStatus(resource); err != nil {
		logger.Warnw("Failed to update resource status", zap.Error(err))
		r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
			"Failed to update status for %q: %v", resource.Name, err)
		return err
	}
	if reconcileErr != nil {
		r.Recorder.Event(resource, corev1.EventTypeWarning, "InternalError", reconcileErr.Error())
	}
	return reconcileErr
}

func (r *Reconciler) reconcile(ctx context.Context, asvc *v1alpha1.AddressableService) error {
	if asvc.GetDeletionTimestamp() != nil {
		// Check for a DeletionTimestamp.  If present, elide the normal reconcile logic.
		// When a controller needs finalizer handling, it would go here.
		return nil
	}
	asvc.Status.InitializeConditions()

	if err := r.reconcileService(ctx, asvc); err != nil {
		return err
	}

	asvc.Status.ObservedGeneration = asvc.Generation
	return nil
}

func (r *Reconciler) reconcileService(ctx context.Context, asvc *v1alpha1.AddressableService) error {
	logger := logging.FromContext(ctx)

	if err := r.Tracker.Track(corev1.ObjectReference{
		APIVersion: "v1",
		Kind:       "Service",
		Name:       asvc.Spec.ServiceName,
		Namespace:  asvc.Namespace,
	}, asvc); err != nil {
		logger.Errorf("Error tracking service %s: %v", asvc.Spec.ServiceName, err)
		return err
	}

	_, err := r.ServiceLister.Services(asvc.Namespace).Get(asvc.Spec.ServiceName)
	if apierrs.IsNotFound(err) {
		logger.Info("Service does not yet exist:", asvc.Spec.ServiceName)
		asvc.Status.MarkServiceUnavailable(asvc.Spec.ServiceName)
		return nil
	} else if err != nil {
		logger.Errorf("Error reconciling service %s: %v", asvc.Spec.ServiceName, err)
		return err
	}

	asvc.Status.MarkServiceAvailable()
	asvc.Status.Address = &duckv1beta1.Addressable{
		URL: &apis.URL{
			Scheme: "http",
			Host:   fmt.Sprintf("%s.%s.svc.cluster.local", asvc.Spec.ServiceName, asvc.Namespace),
		},
	}
	return nil
}

// Update the Status of the resource.  Caller is responsible for checking
// for semantic differences before calling.
func (r *Reconciler) updateStatus(desired *v1alpha1.AddressableService) (*v1alpha1.AddressableService, error) {
	actual, err := r.Lister.AddressableServices(desired.Namespace).Get(desired.Name)
	if err != nil {
		return nil, err
	}
	// If there's nothing to update, just return.
	if reflect.DeepEqual(actual.Status, desired.Status) {
		return actual, nil
	}
	// Don't modify the informers copy
	existing := actual.DeepCopy()
	existing.Status = desired.Status
	return r.Client.SamplesV1alpha1().AddressableServices(desired.Namespace).UpdateStatus(existing)
}
<commit_msg>Use the network package to more correctly construct the hostname. (#89)<commit_after>/*
Copyright 2019 The Knative Authors

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

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

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

package addressableservice

import (
	"context"
	"reflect"

	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	corev1listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"knative.dev/pkg/apis"
	duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/network"
	"knative.dev/pkg/tracker"
	"knative.dev/sample-controller/pkg/apis/samples/v1alpha1"
	clientset "knative.dev/sample-controller/pkg/client/clientset/versioned"
	listers "knative.dev/sample-controller/pkg/client/listers/samples/v1alpha1"
)

// Reconciler implements controller.Reconciler for AddressableService resources.
type Reconciler struct {
	// Client is used to write back status updates.
	Client clientset.Interface

	// Listers index properties about resources
	Lister        listers.AddressableServiceLister
	ServiceLister corev1listers.ServiceLister

	// The tracker builds an index of what resources are watching other
	// resources so that we can immediately react to changes to changes in
	// tracked resources.
	Tracker tracker.Interface

	// Recorder is an event recorder for recording Event resources to the
	// Kubernetes API.
	Recorder record.EventRecorder
}

// Check that our Reconciler implements controller.Reconciler
var _ controller.Reconciler = (*Reconciler)(nil)

// Reconcile implements controller.Reconciler
func (r *Reconciler) Reconcile(ctx context.Context, key string) error {
	logger := logging.FromContext(ctx)

	// Convert the namespace/name string into a distinct namespace and name
	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		logger.Errorf("invalid resource key: %s", key)
		return nil
	}

	// If our controller has configuration state, we'd "freeze" it and
	// attach the frozen configuration to the context.
	//    ctx = r.configStore.ToContext(ctx)

	// Get the resource with this namespace/name.
	original, err := r.Lister.AddressableServices(namespace).Get(name)
	if apierrs.IsNotFound(err) {
		// The resource may no longer exist, in which case we stop processing.
		logger.Errorf("resource %q no longer exists", key)
		return nil
	} else if err != nil {
		return err
	}
	// Don't modify the informers copy.
	resource := original.DeepCopy()

	// Reconcile this copy of the resource and then write back any status
	// updates regardless of whether the reconciliation errored out.
	reconcileErr := r.reconcile(ctx, resource)
	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
		// If we didn't change anything then don't call updateStatus.
		// This is important because the copy we loaded from the informer's
		// cache may be stale and we don't want to overwrite a prior update
		// to status with this stale state.
	} else if _, err = r.updateStatus(resource); err != nil {
		logger.Warnw("Failed to update resource status", zap.Error(err))
		r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
			"Failed to update status for %q: %v", resource.Name, err)
		return err
	}
	if reconcileErr != nil {
		r.Recorder.Event(resource, corev1.EventTypeWarning, "InternalError", reconcileErr.Error())
	}
	return reconcileErr
}

func (r *Reconciler) reconcile(ctx context.Context, asvc *v1alpha1.AddressableService) error {
	if asvc.GetDeletionTimestamp() != nil {
		// Check for a DeletionTimestamp.  If present, elide the normal reconcile logic.
		// When a controller needs finalizer handling, it would go here.
		return nil
	}
	asvc.Status.InitializeConditions()

	if err := r.reconcileService(ctx, asvc); err != nil {
		return err
	}

	asvc.Status.ObservedGeneration = asvc.Generation
	return nil
}

func (r *Reconciler) reconcileService(ctx context.Context, asvc *v1alpha1.AddressableService) error {
	logger := logging.FromContext(ctx)

	if err := r.Tracker.Track(corev1.ObjectReference{
		APIVersion: "v1",
		Kind:       "Service",
		Name:       asvc.Spec.ServiceName,
		Namespace:  asvc.Namespace,
	}, asvc); err != nil {
		logger.Errorf("Error tracking service %s: %v", asvc.Spec.ServiceName, err)
		return err
	}

	_, err := r.ServiceLister.Services(asvc.Namespace).Get(asvc.Spec.ServiceName)
	if apierrs.IsNotFound(err) {
		logger.Info("Service does not yet exist:", asvc.Spec.ServiceName)
		asvc.Status.MarkServiceUnavailable(asvc.Spec.ServiceName)
		return nil
	} else if err != nil {
		logger.Errorf("Error reconciling service %s: %v", asvc.Spec.ServiceName, err)
		return err
	}

	asvc.Status.MarkServiceAvailable()
	asvc.Status.Address = &duckv1beta1.Addressable{
		URL: &apis.URL{
			Scheme: "http",
			Host:   network.GetServiceHostname(asvc.Spec.ServiceName, asvc.Namespace),
		},
	}
	return nil
}

// Update the Status of the resource.  Caller is responsible for checking
// for semantic differences before calling.
func (r *Reconciler) updateStatus(desired *v1alpha1.AddressableService) (*v1alpha1.AddressableService, error) {
	actual, err := r.Lister.AddressableServices(desired.Namespace).Get(desired.Name)
	if err != nil {
		return nil, err
	}
	// If there's nothing to update, just return.
	if reflect.DeepEqual(actual.Status, desired.Status) {
		return actual, nil
	}
	// Don't modify the informers copy
	existing := actual.DeepCopy()
	existing.Status = desired.Status
	return r.Client.SamplesV1alpha1().AddressableServices(desired.Namespace).UpdateStatus(existing)
}
<|endoftext|>


<commit_before>package feed

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	. "github.com/korobool/btcticker/product"
)

var (
	fixerEurUri = "http://api.fixer.io/latest?base=EUR"
)

type FixerMsgLatest struct {
	Base  string
	Date  string
	Rates map[string]float64
}

type FixerFeed struct {
	BaseFeed
	client       *http.Client
	interrupt    chan struct{}
	wait         chan struct{}
	pollInterval time.Duration
	reqTiemout   time.Duration
}

func NewFixerFeed(agr *Aggregator) (Feed, error) {

	transport := &http.Transport{
		//TLSHandshakeTimeout
		//ResponseHeaderTimeout
		DisableKeepAlives: false,
	}

	client := &http.Client{Transport: transport}

	return &FixerFeed{
		BaseFeed: BaseFeed{
			Info:       FeedInfo{ProductEurUsd, "fixer"},
			aggregator: agr,
		},
		client:       client,
		interrupt:    make(chan struct{}),
		wait:         make(chan struct{}),
		pollInterval: 5 * time.Second,
		reqTiemout:   1 * time.Second,
	}, nil
}

func (f *FixerFeed) Run() error {
	f.aggregator.regFeed <- f.Info

	//f.client.Transport.Close()

	go func() {
		ticker := time.NewTicker(f.pollInterval)

		defer func() {
			ticker.Stop()
			f.aggregator.deregFeed <- f.Info
			close(f.wait)
		}()

		for {
			select {
			case <-ticker.C:
				rate, err := f.requestLatest(f.reqTiemout)
				if err != nil {
					log.Printf("fixer: %v", err)
					return
				}
				ts := time.Now().Unix()

				f.aggregator.tickMsgQueue <- &TickMsg{
					Info:     f.Info,
					Ts:       ts,
					TsBuy:    ts,
					BidPrice: rate,
				}
			case <-f.interrupt:
				log.Printf("fixer: push interrupted")
				return
			}
		}
	}()

	return nil
}

func (f *FixerFeed) Stop() error {
	close(f.interrupt)
	<-f.wait
	return nil
}

func (f *FixerFeed) Wait() chan struct{} {
	return f.wait
}

func (f *FixerFeed) GetInfo() FeedInfo {
	return f.Info
}

func (f *FixerFeed) GetName() string {
	return f.Info.Name
}

func (f *FixerFeed) requestLatest(timeout time.Duration) (float64, error) {

	var rate float64

	ctx, _ := context.WithTimeout(context.TODO(), timeout)
	req, err := http.NewRequest("GET", fixerEurUri, nil)
	if err != nil {
		return rate, err
	}

	resp, err := f.client.Do(req.WithContext(ctx))
	if err != nil {
		return rate, err
	}

	if resp.StatusCode != http.StatusOK {
		return rate, fmt.Errorf("status code: %v", resp.StatusCode)
	}

	defer resp.Body.Close()

	var latest FixerMsgLatest

	if err := json.NewDecoder(resp.Body).Decode(&latest); err != nil {
		return rate, err
	}
	rate, ok := latest.Rates["USD"]
	if !ok {
		return rate, fmt.Errorf("no such currency")
	}

	return rate, nil
}
<commit_msg>Refactor FixerFeed<commit_after>package feed

import (
	"fmt"
	"time"

	. "github.com/korobool/btcticker/product"
)

var (
	fixerEurUri = "http://api.fixer.io/latest?base=EUR"
)

type FixerMsgLatest struct {
	Base  string             `json:"base"`
	Date  string             `json:"date"`
	Rates map[string]float64 `json:"rates"`
}

type FixerFeed struct {
	RestFeed
}

func NewFixerFeed(agr *Aggregator) (Feed, error) {

	pollInterval := 5 * time.Second
	reqTimeout := 1 * time.Second

	info := FeedInfo{ProductEurUsd, "fixer"}
	rf, err := NewRestFeed(info, agr, pollInterval, reqTimeout)
	if err != nil {
		return nil, err
	}
	f := &FixerFeed{RestFeed: *rf}
	f.SetPollHandler(f.poll)

	return f, nil
}

func (f *FixerFeed) poll() (*TickMsg, error) {

	var msg FixerMsgLatest

	err := f.requestGet(fixerEurUri, &msg, f.reqTimeout)
	if err != nil {
		return nil, err
	}
	rate, ok := msg.Rates["USD"]
	if !ok {
		return nil, fmt.Errorf("no such currency")
	}

	ts := time.Now().Unix()

	return &TickMsg{
		Info:     f.GetInfo(),
		Ts:       ts,
		TsBuy:    ts,
		BidPrice: rate,
	}, nil
}
<|endoftext|>


<commit_before>package crawler

import (
	"github.com/ipfs-search/ipfs-search/indexer"
	"log"
)

// updateReferences updates references with name, parentHash and parentName. Returns true when updated
func (i *Indexable) updateReferences(references []indexer.Reference) ([]indexer.Reference, bool) {
	if references == nil {
		// Initialize empty references when none have been found
		references = []indexer.Reference{}
	}

	if i.ParentHash == "" {
		// No parent hash for item, not adding reference
		return references, false
	}

	for _, reference := range references {
		if reference.ParentHash == i.ParentHash {
			// Reference exists, not updating
			return references, false
		}
	}

	references = append(references, indexer.Reference{
		Name:       i.Name,
		ParentHash: i.ParentHash,
	})

	return references, true
}

func (i *Indexable) indexReferences() ([]indexer.Reference, bool, error) {
	var alreadyIndexed bool

	references, itemType, err := i.Indexer.GetReferences(i.Hash)
	if err != nil {
		return nil, false, err
	}

	// TODO: Handle this more explicitly, use and detect NotFound
	if references == nil {
		alreadyIndexed = false
	} else {
		alreadyIndexed = true
	}

	references, referencesUpdated := i.updateReferences(references)

	if alreadyIndexed {
		if referencesUpdated {
			log.Printf("Found %s, reference added: '%s' from %s", i.Hash, i.Name, i.ParentHash)

			properties := metadata{
				"references": references,
			}

			err := i.Indexer.IndexItem(itemType, i.Hash, properties)
			if err != nil {
				return nil, false, err
			}
		} else {
			log.Printf("Found %s, references not updated.", i.Hash)
		}
	} else if referencesUpdated {
		log.Printf("Adding %s, reference '%s' from %s", i.Hash, i.Name, i.ParentHash)
	}

	return references, alreadyIndexed, nil
}
<commit_msg>Added comments on references.<commit_after>package crawler

import (
	"github.com/ipfs-search/ipfs-search/indexer"
	"log"
)

// updateReferences updates references with name, parentHash and parentName. Returns true when updated
func (i *Indexable) updateReferences(references []indexer.Reference) ([]indexer.Reference, bool) {
	if references == nil {
		// Initialize empty references when none have been found
		references = []indexer.Reference{}
	}

	if i.ParentHash == "" {
		// No parent hash for item, not adding reference
		return references, false
	}

	for _, reference := range references {
		if reference.ParentHash == i.ParentHash {
			// Reference exists, not updating
			return references, false
		}
	}

	// New references found, updating references
	references = append(references, indexer.Reference{
		Name:       i.Name,
		ParentHash: i.ParentHash,
	})

	return references, true
}

// indexReferences retreives or creates references for this hashable,
// returning the resulting references and whether or not the item was
// previously present in the index.
func (i *Indexable) indexReferences() ([]indexer.Reference, bool, error) {
	var alreadyIndexed bool

	references, itemType, err := i.Indexer.GetReferences(i.Hash)
	if err != nil {
		return nil, false, err
	}

	// TODO: Handle this more explicitly, use and detect NotFound
	if references == nil {
		alreadyIndexed = false
	} else {
		alreadyIndexed = true
	}

	references, referencesUpdated := i.updateReferences(references)

	if alreadyIndexed {
		if referencesUpdated {
			log.Printf("Found %s, reference added: '%s' from %s", i.Hash, i.Name, i.ParentHash)

			properties := metadata{
				"references": references,
			}

			err := i.Indexer.IndexItem(itemType, i.Hash, properties)
			if err != nil {
				return nil, false, err
			}
		} else {
			log.Printf("Found %s, references not updated.", i.Hash)
		}
	} else if referencesUpdated {
		log.Printf("Adding %s, reference '%s' from %s", i.Hash, i.Name, i.ParentHash)
	}

	return references, alreadyIndexed, nil
}
<|endoftext|>


<commit_before>package dev

import (
	"fmt"
	"os"
	"os/signal"

	"github.com/jcelliott/lumber"
	"github.com/nanobox-io/nanobox-boxfile"

	container_generator "github.com/nanobox-io/nanobox/generators/containers"
	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/util"
)

// Run ...
func Run(appModel *models.App) error {

	// load the start commands from the boxfile
	starts := loadStarts(appModel)

	// run the start commands in from the boxfile
	// in the dev container
	if err := runStarts(starts); err != nil {
		return err
	}

	// catch signals and stop the run commands on signal
	sigs := make(chan os.Signal, 1)

	signal.Notify(sigs, os.Interrupt)
	defer signal.Stop(sigs)

	for range sigs {
		// if we get a interupt we will jut return here
		// causing the container to be destroyed and our
		// exec processes to die
		return nil
	}

	return nil
}

func loadStarts(appModel *models.App) map[string]string {
	boxfile := boxfile.New([]byte(appModel.DeployedBoxfile))
	starts := map[string]string{}

	// loop through the nodes and get there start commands
	for _, node := range boxfile.Nodes("code") {

		values := boxfile.Node(node).Value("start")

		switch values.(type) {
		case string:
			starts[node] = values.(string)
		case []interface{}:
			// if it is an array we need the keys to be
			// web.site.2 where 2 is the index of the element
			for index, iFace := range values.([]interface{}) {
				if str, ok := iFace.(string); ok {
					starts[fmt.Sprintf("%s.%d", node, index)] = str
				}
			}
		case map[interface{}]interface{}:
			for key, val := range values.(map[interface{}]interface{}) {
				k, keyOk := key.(string)
				v, valOk := val.(string)
				if keyOk && valOk {
					starts[fmt.Sprintf("%s.%s", node, k)] = v
				}
			}
		}
	}
	return starts
}

func runStarts(starts map[string]string) error {
	// loop through the starts and run them in go routines
	for key, start := range starts {
		go runStart(key, start)
	}
	return nil
}

func runStart(name, command string) error {

	// create the docker command
	cmd := []string{
		"-lc",
		fmt.Sprintf("cd /app/; %s", command),
	}

	lumber.Debug("run:runstarts: %+v", cmd)

	// TODO: dont just use os.Stdout but something from display
	// new print library
	// we will also want to use 'name' to create some prefix
	output, err := util.DockerExec(container_generator.DevName(), "gonano", "/bin/bash", cmd, os.Stdout)
	if err != nil {
		return fmt.Errorf("runstart error: %s, %s", output, err)
	}

	return nil
}
<commit_msg>update the dev run to accept maps from the boxfile<commit_after>package dev

import (
	"fmt"
	"os"
	"os/signal"

	"github.com/jcelliott/lumber"
	"github.com/nanobox-io/nanobox-boxfile"

	container_generator "github.com/nanobox-io/nanobox/generators/containers"
	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/util"
	"github.com/nanobox-io/nanobox/util/display"
)

// Run ...
func Run(appModel *models.App) error {

	// load the start commands from the boxfile
	starts := loadStarts(appModel)

	// run the start commands in from the boxfile
	// in the dev container
	if err := runStarts(starts); err != nil {
		return err
	}

	// catch signals and stop the run commands on signal
	sigs := make(chan os.Signal, 1)

	signal.Notify(sigs, os.Interrupt)
	defer signal.Stop(sigs)

	for range sigs {
		// if we get a interupt we will jut return here
		// causing the container to be destroyed and our
		// exec processes to die
		return nil
	}

	return nil
}

func loadStarts(appModel *models.App) map[string]string {
	boxfile := boxfile.New([]byte(appModel.DeployedBoxfile))
	starts := map[string]string{}
	// loop through the nodes and get there start commands
	for _, node := range boxfile.Nodes("code") {

		values := boxfile.Node(node).Value("start")

		switch values.(type) {
		case string:
			starts[node] = values.(string)
		case []interface{}:
			// if it is an array we need the keys to be
			// web.site.2 where 2 is the index of the element
			for index, iFace := range values.([]interface{}) {
				if str, ok := iFace.(string); ok {
					starts[fmt.Sprintf("%s.%d", node, index)] = str
				}
			}
		case map[interface{}]interface{}:
			for key, val := range values.(map[interface{}]interface{}) {
				k, keyOk := key.(string)
				v, valOk := val.(string)
				if keyOk && valOk {
					starts[fmt.Sprintf("%s.%s", node, k)] = v
				}
			}
		case map[string]interface{}:
			for key, val := range values.(map[string]interface{}) {
				v, valOk := val.(string)
				if valOk {
					starts[fmt.Sprintf("%s.%s", node, key)] = v
				}
			}
		}
	}
	return starts
}

func runStarts(starts map[string]string) error {
	// loop through the starts and run them in go routines
	for key, start := range starts {
		go runStart(key, start)
	}
	return nil
}

func runStart(name, command string) error {

	fmt.Printf("running '%s'\n", command)

	// create the docker command
	cmd := []string{
		"-lc",
		fmt.Sprintf("cd /app/; %s", command),
	}

	lumber.Debug("run:runstarts: %+v", cmd)

	streamer := display.NewPrefixedStreamer("info", fmt.Sprintf("[%s]", name))
	output, err := util.DockerExec(container_generator.DevName(), "gonano", "/bin/bash", cmd, streamer)
	if err != nil {
		return fmt.Errorf("runstart error: %s, %s", output, err)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
  "io"
  "net/http"

	"github.com/gorilla/mux"
)

func HelloHandler(writer http.ResponseWriter, request *http.Request) {
	vars := mux.Vars(request)
  if vars["name"] != "" {
    io.WriteString(writer, "hello " + vars["name"] + "!")
  } else {
    io.WriteString(writer, "hello!")
  }
}

func NoCacheDecorator(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.Header().Set("Pragma", "no-cache")
		w.Header().Set("Expires", "0")
    h.ServeHTTP(w, r)
  })
}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/hello", HelloHandler)
	router.HandleFunc("/hello/{name}", HelloHandler)
  staticHandler := http.StripPrefix("/", http.FileServer(http.Dir(".")))
  staticHandler = NoCacheDecorator(staticHandler)
	router.PathPrefix("/").Handler(staticHandler)
  http.ListenAndServe("localhost:1234", router)
}
<commit_msg>Tweaks to simple server<commit_after>package main

import (
	"io"
	"net/http"

	"github.com/gorilla/mux"
)

func HelloHandler(writer http.ResponseWriter, request *http.Request) {
	vars := mux.Vars(request)
	if vars["name"] != "" {
		io.WriteString(writer, "hello "+vars["name"]+"!")
	} else {
		io.WriteString(writer, "hello!")
	}
}

func NoCacheDecorator(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.Header().Set("Pragma", "no-cache")
		w.Header().Set("Expires", "0")
		h.ServeHTTP(w, r)
	})
}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/hello", HelloHandler)
	router.HandleFunc("/hello/{name}", HelloHandler)
	staticHandler := http.FileServer(http.Dir("."))
	staticHandler = http.StripPrefix("/static/", staticHandler)
	staticHandler = NoCacheDecorator(staticHandler)
	router.PathPrefix("/static/").Handler(staticHandler)
	http.ListenAndServe("localhost:1234", router)
}
<|endoftext|>


<commit_before>/*
 Commonly used types in a single place. Purely for organisation purposes.
*/

package main

// Thread stores the metadata and posts of a single thread
type Thread struct {
	ID       int             `json:"id",gorethink:"id"`
	IP       string          `json:"ip",gorethink:"ip"`
	Board    string          `json:"board",gorethink:"board"`
	Time     int             `json:"time",gorethink:"time"`
	BumpTime int             `json:"bumpTime",gorethink:"bumpTime"`
	Nonce    string          `json:"nonce",gorethink:"nonce"`
	Posts    map[string]Post `json:"posts",gorethink:"posts"`
	History  []Message       `json:"history",gorethink:"history"`
}

// Message is the universal transport container of all live updates through
// websockets
type Message struct {
	Type string `json:"type",gorethink:"type"`

	// If present, determines a priviledged access level, the client has to
	// have, to recieve this message
	Priv string `json:"priv,omitempty",gorethink:"priv,omitempty"`

	// The actual contents of the message. Very variadic, thus interface{}.
	Msg interface{} `json:"msg,omitempty",gorethink:"msg,omitempty"`
}

// Post is a generic post. Either OP or reply.
type Post struct {
	ID         int     `json:"id",gorethink:"id"`
	IP         string  `json:"ip",gorethink:"ip"`
	OP         int     `json:"op",gorethink:"op"`
	Time       int     `json:"time",gorethink:"time"`
	Nonce      string  `json:"nonce",gorethink:"nonce"`
	Editing    bool    `json:"editing,omitempty",gorethink:"editing,omitempty"`
	Body       string  `json:"body",gorethink:"body"`
	Deleted    bool    `json:"deleted",gorethink:"deleted"`
	ImgDeleted bool    `json:"imgDeleted",gorethink:"imgDeleted"`
	Image      Image   `json:"image,omitempty",gorethink:"image,omitempty"`
	Name       string  `json:"name,omitempty",gorethink:"name,omitempty"`
	Trip       string  `json:"trip,omitEmpty",gorethink:"trip,omitEmpty"`
	Email      string  `json:"email,omitempty",gorethink:"email,omitempty"`
	Auth       string  `json:"auth,omitempty",gorethink:"auth,omitempty"`
	Dice       Dice    `json:"dice,omitempty",gorethink:"dice,omitempty"`
	Links      LinkMap `json:"links,omitempty",gorethink:"links,omitempty"`
	Backlinks  LinkMap `json:"backlinks,omitempty",gorethink:"backlinks,omitempty"`
}

// Image contains a post's image and thumbanail data
type Image struct {
	Src     string `json:"src",gorethink:"src"`
	Thumb   string `json:"thumb,omitempty",gorethink:"thumb,omitempty"`
	Mid     string `json:"mid,omitempty",gorethink:"mid,omitempty"`
	Dims    [2]int `json:"dims",gorethink:"dims"`
	Ext     string `json:"ext",gorethink:"ext"`
	Size    int    `json:"size",gorethink:"size"`
	MD5     string
	SHA1    string
	Imgnm   string `json:"imagnm",gorethink:"imagnm"`
	Spoiler int    `json:"spoiler,omitempty",gorethink:"spoiler,omitempty"`
	APNG    bool   `json:"apng,omitempty",gorethink:"apng,omitempty"`
	Audio   bool   `json:"audio,omitempty",gorethink:"audio,omitempty"`
	Length  string `json:"lenght,omitempty",gorethink:"lenght,omitempty"`
}

// Dice stores # command information of the post in exectution order
type Dice []Roll

// Roll represents a single hash command. It always contains the Type field,
// which determines, which of the other fields are present.
type Roll struct {
	Type   string `json:"type",gorethink:"type"`
	Bool   bool   `json:"bool,omitempty",gorethink:"bool,omitempty"`
	Int    int    `json:"int,omitempty",gorethink:"int,omitempty"`
	Ints   []int  `json:"ints,omitempty",gorethink:"ints,omitempty"`
	String string `json:"string,omitempty",gorethink:"string,omitempty"`
}

// LinkMap contains a map of post numbers, this tread is linking, to
// corresponding Link tuples
type LinkMap map[string]Link

// Link is a one key-value pair map of the target post's parent board and parent
// thread
type Link map[string]int

// Ident is used to verify a client's access and write permissions
type Ident struct {
	// Indicates priveledged access rights for staff.
	Auth string
	Ban  bool
	IP   string
}
<commit_msg>Use hashes for post links<commit_after>/*
 Commonly used types in a single place. Purely for organisation purposes.
*/

package main

// Thread stores the metadata and posts of a single thread
type Thread struct {
	ID       int             `json:"id",gorethink:"id"`
	IP       string          `json:"ip",gorethink:"ip"`
	Board    string          `json:"board",gorethink:"board"`
	Time     int             `json:"time",gorethink:"time"`
	BumpTime int             `json:"bumpTime",gorethink:"bumpTime"`
	Nonce    string          `json:"nonce",gorethink:"nonce"`
	Posts    map[string]Post `json:"posts",gorethink:"posts"`
	History  []Message       `json:"history",gorethink:"history"`
}

// Message is the universal transport container of all live updates through
// websockets
type Message struct {
	Type string `json:"type",gorethink:"type"`

	// If present, determines a priviledged access level, the client has to
	// have, to recieve this message
	Priv string `json:"priv,omitempty",gorethink:"priv,omitempty"`

	// The actual contents of the message. Very variadic, thus interface{}.
	Msg interface{} `json:"msg,omitempty",gorethink:"msg,omitempty"`
}

// Post is a generic post. Either OP or reply.
type Post struct {
	ID         int     `json:"id",gorethink:"id"`
	IP         string  `json:"ip",gorethink:"ip"`
	OP         int     `json:"op",gorethink:"op"`
	Time       int     `json:"time",gorethink:"time"`
	Nonce      string  `json:"nonce",gorethink:"nonce"`
	Editing    bool    `json:"editing,omitempty",gorethink:"editing,omitempty"`
	Body       string  `json:"body",gorethink:"body"`
	Deleted    bool    `json:"deleted",gorethink:"deleted"`
	ImgDeleted bool    `json:"imgDeleted",gorethink:"imgDeleted"`
	Image      Image   `json:"image,omitempty",gorethink:"image,omitempty"`
	Name       string  `json:"name,omitempty",gorethink:"name,omitempty"`
	Trip       string  `json:"trip,omitEmpty",gorethink:"trip,omitEmpty"`
	Email      string  `json:"email,omitempty",gorethink:"email,omitempty"`
	Auth       string  `json:"auth,omitempty",gorethink:"auth,omitempty"`
	Dice       Dice    `json:"dice,omitempty",gorethink:"dice,omitempty"`
	Links      LinkMap `json:"links,omitempty",gorethink:"links,omitempty"`
	Backlinks  LinkMap `json:"backlinks,omitempty",gorethink:"backlinks,omitempty"`
}

// Image contains a post's image and thumbanail data
type Image struct {
	Src     string `json:"src",gorethink:"src"`
	Thumb   string `json:"thumb,omitempty",gorethink:"thumb,omitempty"`
	Mid     string `json:"mid,omitempty",gorethink:"mid,omitempty"`
	Dims    [2]int `json:"dims",gorethink:"dims"`
	Ext     string `json:"ext",gorethink:"ext"`
	Size    int    `json:"size",gorethink:"size"`
	MD5     string
	SHA1    string
	Imgnm   string `json:"imagnm",gorethink:"imagnm"`
	Spoiler int    `json:"spoiler,omitempty",gorethink:"spoiler,omitempty"`
	APNG    bool   `json:"apng,omitempty",gorethink:"apng,omitempty"`
	Audio   bool   `json:"audio,omitempty",gorethink:"audio,omitempty"`
	Length  string `json:"lenght,omitempty",gorethink:"lenght,omitempty"`
}

// Dice stores # command information of the post in exectution order
type Dice []Roll

// Roll represents a single hash command. It always contains the Type field,
// which determines, which of the other fields are present.
type Roll struct {
	Type   string `json:"type",gorethink:"type"`
	Bool   bool   `json:"bool,omitempty",gorethink:"bool,omitempty"`
	Int    int    `json:"int,omitempty",gorethink:"int,omitempty"`
	Ints   []int  `json:"ints,omitempty",gorethink:"ints,omitempty"`
	String string `json:"string,omitempty",gorethink:"string,omitempty"`
}

// LinkMap contains a map of post numbers, this tread is linking, to
// corresponding Link tuples
type LinkMap map[string]Link

// Link stores the target post's parent board and parent thread
type Link struct {
	Board string `json:"board",gorethink:"board"`
	ID    int    `json:"id",gorethink:"id"`
}

// Ident is used to verify a client's access and write permissions
type Ident struct {
	// Indicates priveledged access rights for staff.
	Auth string
	Ban  bool
	IP   string
}
<|endoftext|>