<commit_before>/*
Copyright 2021 The Vitess 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 planbuilder

import (
	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/vtgate/engine"
	"vitess.io/vitess/go/vt/vtgate/evalengine"
	"vitess.io/vitess/go/vt/vtgate/planbuilder/abstract"
	"vitess.io/vitess/go/vt/vtgate/semantics"
	"vitess.io/vitess/go/vt/vtgate/vindexes"
)

type routeOp struct {
	source abstract.PhysicalOperator

	routeOpCode engine.RouteOpcode
	keyspace    *vindexes.Keyspace

	// here we store the possible vindexes we can use so that when we add predicates to the plan,
	// we can quickly check if the new predicates enables any new vindex options
	vindexPreds []*vindexPlusPredicates

	// the best option available is stored here
	selected *vindexOption

	// The following two fields are used when routing information_schema queries
	SysTableTableSchema []evalengine.Expr
	SysTableTableName   map[string]evalengine.Expr
}

var _ abstract.PhysicalOperator = (*routeOp)(nil)

// IPhysical implements the PhysicalOperator interface
func (*routeOp) IPhysical() {}

// TableID implements the Operator interface
func (r *routeOp) TableID() semantics.TableSet {
	return r.source.TableID()
}

// Cost implements the Operator interface
func (r *routeOp) Cost() int {
	switch r.routeOpCode {
	case // these op codes will never be compared with each other - they are assigned by a rule and not a comparison
		engine.SelectDBA,
		engine.SelectNext,
		engine.SelectNone,
		engine.SelectReference,
		engine.SelectUnsharded:
		return 0
	// TODO revisit these costs when more of the gen4 planner is done
	case engine.SelectEqualUnique:
		return 1
	case engine.SelectEqual:
		return 5
	case engine.SelectIN:
		return 10
	case engine.SelectMultiEqual:
		return 10
	case engine.SelectScatter:
		return 20
	}
	return 1
}

// Clone implements the PhysicalOperator interface
func (r *routeOp) Clone() abstract.PhysicalOperator {
	cloneRoute := *r
	cloneRoute.source = r.source.Clone()
	cloneRoute.vindexPreds = make([]*vindexPlusPredicates, len(r.vindexPreds))
	for i, pred := range r.vindexPreds {
		// we do this to create a copy of the struct
		p := *pred
		cloneRoute.vindexPreds[i] = &p
	}
	return &cloneRoute
}

func (r *routeOp) updateRoutingLogic(ctx *planningContext, expr sqlparser.Expr) error {
	if r.canImprove() {
		newVindexFound, err := r.searchForNewVindexes(ctx, expr)
		if err != nil {
			return err
		}

		// if we didn't open up any new vindex options, no need to enter here
		if newVindexFound {
			r.pickBestAvailableVindex()
		}
	}
	return nil
}

func (r *routeOp) searchForNewVindexes(ctx *planningContext, predicate sqlparser.Expr) (bool, error) {
	newVindexFound := false
	switch node := predicate.(type) {
	case *sqlparser.ComparisonExpr:
		found, exitEarly, err := r.planComparison(ctx, node)
		if err != nil || exitEarly {
			return false, err
		}
		newVindexFound = newVindexFound || found
	}
	return newVindexFound, nil
}

func (r *routeOp) planComparison(ctx *planningContext, node *sqlparser.ComparisonExpr) (bool, bool, error) {
	if sqlparser.IsNull(node.Left) || sqlparser.IsNull(node.Right) {
		// we are looking at ANDed predicates in the WHERE clause.
		// since we know that nothing returns true when compared to NULL,
		// so we can safely bail out here
		r.routeOpCode = engine.SelectNone
		return false, true, nil
	}

	switch node.Operator {
	case sqlparser.EqualOp:
		found := r.planEqualOp(ctx, node)
		return found, false, nil
	}
	return false, false, nil
}

func (r *routeOp) planEqualOp(ctx *planningContext, node *sqlparser.ComparisonExpr) bool {
	column, ok := node.Left.(*sqlparser.ColName)
	other := node.Right
	vdValue := other
	if !ok {
		column, ok = node.Right.(*sqlparser.ColName)
		if !ok {
			// either the LHS or RHS have to be a column to be useful for the vindex
			return false
		}
		vdValue = node.Left
	}
	val := r.makeEvalEngineExpr(ctx, vdValue)
	if val == nil {
		return false
	}

	return r.haveMatchingVindex(ctx, node, vdValue, column, val, equalOrEqualUnique, justTheVindex)
}

// makePlanValue transforms the given sqlparser.Expr into a sqltypes.PlanValue.
// If the given sqlparser.Expr is an argument and can be found in the r.argToReplaceBySelect then the
// method will stops and return nil values.
// Otherwise, the method will try to apply makePlanValue for any equality the sqlparser.Expr n has.
// The first PlanValue that is successfully produced will be returned.
func (r *routeOp) makeEvalEngineExpr(ctx *planningContext, n sqlparser.Expr) evalengine.Expr {
	for _, expr := range ctx.semTable.GetExprAndEqualities(n) {
		if subq, isSubq := expr.(*sqlparser.Subquery); isSubq {
			extractedSubquery := ctx.semTable.FindSubqueryReference(subq)
			if extractedSubquery == nil {
				continue
			}
			switch engine.PulloutOpcode(extractedSubquery.OpCode) {
			case engine.PulloutIn, engine.PulloutNotIn:
				expr = sqlparser.NewListArg(extractedSubquery.GetArgName())
			case engine.PulloutValue, engine.PulloutExists:
				expr = sqlparser.NewArgument(extractedSubquery.GetArgName())
			}
		}
		pv, _ := evalengine.Convert(expr, ctx.semTable)
		if pv != nil {
			return pv
		}
	}

	return nil
}

func (r *routeOp) hasVindex(column *sqlparser.ColName) bool {
	for _, v := range r.vindexPreds {
		for _, col := range v.colVindex.Columns {
			if column.Name.Equal(col) {
				return true
			}
		}
	}
	return false
}

func (r *routeOp) haveMatchingVindex(
	ctx *planningContext,
	node sqlparser.Expr,
	valueExpr sqlparser.Expr,
	column *sqlparser.ColName,
	value evalengine.Expr,
	opcode func(*vindexes.ColumnVindex) engine.RouteOpcode,
	vfunc func(*vindexes.ColumnVindex) vindexes.Vindex,
) bool {
	newVindexFound := false
	for _, v := range r.vindexPreds {
		// check that the
		if !ctx.semTable.DirectDeps(column).IsSolvedBy(v.tableID) {
			continue
		}
		// Ignore MultiColumn vindexes for finding matching Vindex.
		if _, isSingleCol := v.colVindex.Vindex.(vindexes.SingleColumn); !isSingleCol {
			continue
		}

		col := v.colVindex.Columns[0]
		if column.Name.Equal(col) {
			// single column vindex - just add the option
			routeOpcode := opcode(v.colVindex)
			vindex := vfunc(v.colVindex)
			v.options = append(v.options, &vindexOption{
				values:      []evalengine.Expr{value},
				valueExprs:  []sqlparser.Expr{valueExpr},
				predicates:  []sqlparser.Expr{node},
				opcode:      routeOpcode,
				foundVindex: vindex,
				cost:        costFor(vindex, routeOpcode),
				ready:       true,
			})
			newVindexFound = true
		}
	}
	return newVindexFound
}

// pickBestAvailableVindex goes over the available vindexes for this route and picks the best one available.
func (r *routeOp) pickBestAvailableVindex() {
	for _, v := range r.vindexPreds {
		option := v.bestOption()
		if option != nil && (r.selected == nil || less(option.cost, r.selected.cost)) {
			r.selected = option
			r.routeOpCode = option.opcode
		}
	}
}

// canImprove returns true if additional predicates could help improving this plan
func (r *routeOp) canImprove() bool {
	return r.routeOpCode != engine.SelectNone
}

// UnsolvedPredicates implements the Operator interface
func (r *routeOp) UnsolvedPredicates(semTable *semantics.SemTable) []sqlparser.Expr {
	return r.source.UnsolvedPredicates(semTable)
}

// CheckValid implements the Operator interface
func (r *routeOp) CheckValid() error {
	return r.source.CheckValid()
}

// Compact implements the Operator interface
func (r *routeOp) Compact(semTable *semantics.SemTable) (abstract.Operator, error) {
	return r, nil
}
<commit_msg>bug: fix compilation error after rebasing<commit_after>/*
Copyright 2021 The Vitess 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 planbuilder

import (
	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/vtgate/engine"
	"vitess.io/vitess/go/vt/vtgate/evalengine"
	"vitess.io/vitess/go/vt/vtgate/planbuilder/abstract"
	"vitess.io/vitess/go/vt/vtgate/semantics"
	"vitess.io/vitess/go/vt/vtgate/vindexes"
)

type routeOp struct {
	source abstract.PhysicalOperator

	routeOpCode engine.RouteOpcode
	keyspace    *vindexes.Keyspace

	// here we store the possible vindexes we can use so that when we add predicates to the plan,
	// we can quickly check if the new predicates enables any new vindex options
	vindexPreds []*vindexPlusPredicates

	// the best option available is stored here
	selected *vindexOption

	// The following two fields are used when routing information_schema queries
	SysTableTableSchema []evalengine.Expr
	SysTableTableName   map[string]evalengine.Expr
}

var _ abstract.PhysicalOperator = (*routeOp)(nil)

// IPhysical implements the PhysicalOperator interface
func (*routeOp) IPhysical() {}

// TableID implements the Operator interface
func (r *routeOp) TableID() semantics.TableSet {
	return r.source.TableID()
}

// Cost implements the Operator interface
func (r *routeOp) Cost() int {
	switch r.routeOpCode {
	case // these op codes will never be compared with each other - they are assigned by a rule and not a comparison
		engine.SelectDBA,
		engine.SelectNext,
		engine.SelectNone,
		engine.SelectReference,
		engine.SelectUnsharded:
		return 0
	// TODO revisit these costs when more of the gen4 planner is done
	case engine.SelectEqualUnique:
		return 1
	case engine.SelectEqual:
		return 5
	case engine.SelectIN:
		return 10
	case engine.SelectMultiEqual:
		return 10
	case engine.SelectScatter:
		return 20
	}
	return 1
}

// Clone implements the PhysicalOperator interface
func (r *routeOp) Clone() abstract.PhysicalOperator {
	cloneRoute := *r
	cloneRoute.source = r.source.Clone()
	cloneRoute.vindexPreds = make([]*vindexPlusPredicates, len(r.vindexPreds))
	for i, pred := range r.vindexPreds {
		// we do this to create a copy of the struct
		p := *pred
		cloneRoute.vindexPreds[i] = &p
	}
	return &cloneRoute
}

func (r *routeOp) updateRoutingLogic(ctx *planningContext, expr sqlparser.Expr) error {
	if r.canImprove() {
		newVindexFound, err := r.searchForNewVindexes(ctx, expr)
		if err != nil {
			return err
		}

		// if we didn't open up any new vindex options, no need to enter here
		if newVindexFound {
			r.pickBestAvailableVindex()
		}
	}
	return nil
}

func (r *routeOp) searchForNewVindexes(ctx *planningContext, predicate sqlparser.Expr) (bool, error) {
	newVindexFound := false
	switch node := predicate.(type) {
	case *sqlparser.ComparisonExpr:
		found, exitEarly, err := r.planComparison(ctx, node)
		if err != nil || exitEarly {
			return false, err
		}
		newVindexFound = newVindexFound || found
	}
	return newVindexFound, nil
}

func (r *routeOp) planComparison(ctx *planningContext, node *sqlparser.ComparisonExpr) (bool, bool, error) {
	if sqlparser.IsNull(node.Left) || sqlparser.IsNull(node.Right) {
		// we are looking at ANDed predicates in the WHERE clause.
		// since we know that nothing returns true when compared to NULL,
		// so we can safely bail out here
		r.routeOpCode = engine.SelectNone
		return false, true, nil
	}

	switch node.Operator {
	case sqlparser.EqualOp:
		found := r.planEqualOp(ctx, node)
		return found, false, nil
	}
	return false, false, nil
}

func (r *routeOp) planEqualOp(ctx *planningContext, node *sqlparser.ComparisonExpr) bool {
	column, ok := node.Left.(*sqlparser.ColName)
	other := node.Right
	vdValue := other
	if !ok {
		column, ok = node.Right.(*sqlparser.ColName)
		if !ok {
			// either the LHS or RHS have to be a column to be useful for the vindex
			return false
		}
		vdValue = node.Left
	}
	val := r.makeEvalEngineExpr(ctx, vdValue)
	if val == nil {
		return false
	}

	return r.haveMatchingVindex(ctx, node, vdValue, column, val, equalOrEqualUnique, justTheVindex)
}

// makePlanValue transforms the given sqlparser.Expr into a sqltypes.PlanValue.
// If the given sqlparser.Expr is an argument and can be found in the r.argToReplaceBySelect then the
// method will stops and return nil values.
// Otherwise, the method will try to apply makePlanValue for any equality the sqlparser.Expr n has.
// The first PlanValue that is successfully produced will be returned.
func (r *routeOp) makeEvalEngineExpr(ctx *planningContext, n sqlparser.Expr) evalengine.Expr {
	for _, expr := range ctx.semTable.GetExprAndEqualities(n) {
		if subq, isSubq := expr.(*sqlparser.Subquery); isSubq {
			extractedSubquery := ctx.semTable.FindSubqueryReference(subq)
			if extractedSubquery == nil {
				continue
			}
			switch engine.PulloutOpcode(extractedSubquery.OpCode) {
			case engine.PulloutIn, engine.PulloutNotIn:
				expr = sqlparser.NewListArg(extractedSubquery.GetArgName())
			case engine.PulloutValue, engine.PulloutExists:
				expr = sqlparser.NewArgument(extractedSubquery.GetArgName())
			}
		}
		pv, _ := evalengine.Convert(expr, ctx.semTable)
		if pv != nil {
			return pv
		}
	}

	return nil
}

func (r *routeOp) hasVindex(column *sqlparser.ColName) bool {
	for _, v := range r.vindexPreds {
		for _, col := range v.colVindex.Columns {
			if column.Name.Equal(col) {
				return true
			}
		}
	}
	return false
}

func (r *routeOp) haveMatchingVindex(
	ctx *planningContext,
	node sqlparser.Expr,
	valueExpr sqlparser.Expr,
	column *sqlparser.ColName,
	value evalengine.Expr,
	opcode func(*vindexes.ColumnVindex) engine.RouteOpcode,
	vfunc func(*vindexes.ColumnVindex) vindexes.Vindex,
) bool {
	newVindexFound := false
	for _, v := range r.vindexPreds {
		// check that the
		if !ctx.semTable.DirectDeps(column).IsSolvedBy(v.tableID) {
			continue
		}
		// Ignore MultiColumn vindexes for finding matching Vindex.
		if _, isSingleCol := v.colVindex.Vindex.(vindexes.SingleColumn); !isSingleCol {
			continue
		}

		col := v.colVindex.Columns[0]
		if column.Name.Equal(col) {
			// single column vindex - just add the option
			routeOpcode := opcode(v.colVindex)
			vindex := vfunc(v.colVindex)
			if vindex == nil {
				continue
			}
			v.options = append(v.options, &vindexOption{
				values:      []evalengine.Expr{value},
				valueExprs:  []sqlparser.Expr{valueExpr},
				predicates:  []sqlparser.Expr{node},
				opcode:      routeOpcode,
				foundVindex: vindex,
				cost:        costFor(v.colVindex, routeOpcode),
				ready:       true,
			})
			newVindexFound = true
		}
	}
	return newVindexFound
}

// pickBestAvailableVindex goes over the available vindexes for this route and picks the best one available.
func (r *routeOp) pickBestAvailableVindex() {
	for _, v := range r.vindexPreds {
		option := v.bestOption()
		if option != nil && (r.selected == nil || less(option.cost, r.selected.cost)) {
			r.selected = option
			r.routeOpCode = option.opcode
		}
	}
}

// canImprove returns true if additional predicates could help improving this plan
func (r *routeOp) canImprove() bool {
	return r.routeOpCode != engine.SelectNone
}

// UnsolvedPredicates implements the Operator interface
func (r *routeOp) UnsolvedPredicates(semTable *semantics.SemTable) []sqlparser.Expr {
	return r.source.UnsolvedPredicates(semTable)
}

// CheckValid implements the Operator interface
func (r *routeOp) CheckValid() error {
	return r.source.CheckValid()
}

// Compact implements the Operator interface
func (r *routeOp) Compact(semTable *semantics.SemTable) (abstract.Operator, error) {
	return r, nil
}
<|endoftext|>


<commit_before>package libkb

import (
	"encoding/json"
	"fmt"
	"github.com/keybase/go-jsonw"
	"io"
	"os"
)

type JsonFile struct {
	filename string
	which    string
	jw       *jsonw.Wrapper
	exists   bool
	dirty    bool
}

func NewJsonFile(filename, which string) *JsonFile {
	return &JsonFile{filename, which, jsonw.NewDictionary(), false, false}
}

func (f *JsonFile) Exists() bool { return f.exists }

func (f *JsonFile) Load(warnOnNotFound bool) error {
	G.Log.Debug(fmt.Sprintf("+ loading %s file: %s", f.which, f.filename))
	file, err := os.Open(f.filename)
	if err != nil {
		if os.IsNotExist(err) {
			msg := fmt.Sprintf("No %s file found; tried %s",
				f.which, f.filename)
			if warnOnNotFound {
				G.Log.Warning(msg)
			} else {
				G.Log.Debug(msg)
			}
			return nil
		} else if os.IsPermission(err) {
			G.Log.Warning(fmt.Sprintf("Permission denied opening %s file %s",
				f.which, f.filename))
			return nil
		} else {
			return err
		}
	}
	f.exists = true
	defer file.Close()
	decoder := json.NewDecoder(file)
	obj := make(map[string]interface{})
	// Treat empty files like an empty dictionary
	if err = decoder.Decode(&obj); err != nil && err != io.EOF {
		G.Log.Error(fmt.Sprintf("Error decoding %s file %s",
			f.which, f.filename))
		return err
	}
	f.jw = jsonw.NewWrapper(obj)
	G.Log.Debug(fmt.Sprintf("- successfully loaded %s file", f.which))
	return nil
}

func (f *JsonFile) MaybeSave(pretty bool, mode os.FileMode) (err error) {
	if f != nil && f.dirty {
		err = f.Save(pretty, mode)
	}
	return
}

func (f *JsonFile) Nuke() error {
	G.Log.Debug("+ nuke file %s", f.filename)

	err := os.Remove(f.filename)
	G.Log.Debug("- nuke file %s -> %s", f.filename, ErrToOk(err))

	return err
}

func (f *JsonFile) Save(pretty bool, mode os.FileMode) (err error) {
	G.Log.Debug(fmt.Sprintf("+ saving %s file %s", f.which, f.filename))

	err = MakeParentDirs(f.filename)
	if err != nil {
		G.Log.Error("Failed to make parent dirs for %s", f.filename)
		return err
	}

	var dat interface{}

	if f.jw == nil {
		// Make a default Dictionary if none already exists
		dat = make(map[string]interface{})
		G.Log.Warning("No value for %s file; assuming empty value (i.e., {})",
			f.which)
	} else {
		dat, err = f.jw.GetData()
		if err != nil {
			G.Log.Error("Failed to encode data for %s file", f.which)
			return err
		}
	}
	var writer *os.File
	flags := (os.O_WRONLY | os.O_CREATE | os.O_TRUNC)
	if mode == 0 {
		mode = PERM_FILE // By default, secrecy
	}
	writer, err = os.OpenFile(f.filename, flags, mode)
	if err != nil {
		G.Log.Error("Failed to open %s file %s for writing: %s",
			f.which, f.filename, err.Error())
		return err
	}
	defer writer.Close()

	if pretty {
		encoded, err := json.MarshalIndent(dat, "", "    ")
		if err == nil {
			_, err = writer.Write(encoded)
		}
	} else {
		encoder := json.NewEncoder(writer)
		err = encoder.Encode(dat)
	}

	if err != nil {
		G.Log.Error("Error encoding data to %s file %s: %s",
			f.which, f.filename, err.Error())
		return err
	}

	err = writer.Close()
	if err != nil {
		G.Log.Error("Error flushing %s file %s: %s",
			f.which, f.filename, err.Error())
		return err
	}

	G.Log.Notice(fmt.Sprintf("Wrote %s file to %s", f.which, f.filename))
	f.dirty = false

	G.Log.Debug(fmt.Sprintf("- saved %s file %s", f.which, f.filename))
	return
}
<commit_msg>so bserver can use JsonFile to parse json configuration file<commit_after>package libkb

import (
	"encoding/json"
	"fmt"
	"github.com/keybase/go-jsonw"
	"io"
	"os"
)

type JsonFile struct {
	filename string
	which    string
	jw       *jsonw.Wrapper
	exists   bool
	dirty    bool
}

func NewJsonFile(filename, which string) *JsonFile {
	return &JsonFile{filename, which, jsonw.NewDictionary(), false, false}
}

func (f *JsonFile) GetWrapper() (*jsonw.Wrapper) {
	return f.jw
}
func (f *JsonFile) Exists() bool { return f.exists }

func (f *JsonFile) Load(warnOnNotFound bool) error {
	G.Log.Debug(fmt.Sprintf("+ loading %s file: %s", f.which, f.filename))
	file, err := os.Open(f.filename)
	if err != nil {
		if os.IsNotExist(err) {
			msg := fmt.Sprintf("No %s file found; tried %s",
				f.which, f.filename)
			if warnOnNotFound {
				G.Log.Warning(msg)
			} else {
				G.Log.Debug(msg)
			}
			return nil
		} else if os.IsPermission(err) {
			G.Log.Warning(fmt.Sprintf("Permission denied opening %s file %s",
				f.which, f.filename))
			return nil
		} else {
			return err
		}
	}
	f.exists = true
	defer file.Close()
	decoder := json.NewDecoder(file)
	obj := make(map[string]interface{})
	// Treat empty files like an empty dictionary
	if err = decoder.Decode(&obj); err != nil && err != io.EOF {
		G.Log.Error(fmt.Sprintf("Error decoding %s file %s",
			f.which, f.filename))
		return err
	}
	f.jw = jsonw.NewWrapper(obj)
	G.Log.Debug(fmt.Sprintf("- successfully loaded %s file", f.which))
	return nil
}

func (f *JsonFile) MaybeSave(pretty bool, mode os.FileMode) (err error) {
	if f != nil && f.dirty {
		err = f.Save(pretty, mode)
	}
	return
}

func (f *JsonFile) Nuke() error {
	G.Log.Debug("+ nuke file %s", f.filename)

	err := os.Remove(f.filename)
	G.Log.Debug("- nuke file %s -> %s", f.filename, ErrToOk(err))

	return err
}

func (f *JsonFile) Save(pretty bool, mode os.FileMode) (err error) {
	G.Log.Debug(fmt.Sprintf("+ saving %s file %s", f.which, f.filename))

	err = MakeParentDirs(f.filename)
	if err != nil {
		G.Log.Error("Failed to make parent dirs for %s", f.filename)
		return err
	}

	var dat interface{}

	if f.jw == nil {
		// Make a default Dictionary if none already exists
		dat = make(map[string]interface{})
		G.Log.Warning("No value for %s file; assuming empty value (i.e., {})",
			f.which)
	} else {
		dat, err = f.jw.GetData()
		if err != nil {
			G.Log.Error("Failed to encode data for %s file", f.which)
			return err
		}
	}
	var writer *os.File
	flags := (os.O_WRONLY | os.O_CREATE | os.O_TRUNC)
	if mode == 0 {
		mode = PERM_FILE // By default, secrecy
	}
	writer, err = os.OpenFile(f.filename, flags, mode)
	if err != nil {
		G.Log.Error("Failed to open %s file %s for writing: %s",
			f.which, f.filename, err.Error())
		return err
	}
	defer writer.Close()

	if pretty {
		encoded, err := json.MarshalIndent(dat, "", "    ")
		if err == nil {
			_, err = writer.Write(encoded)
		}
	} else {
		encoder := json.NewEncoder(writer)
		err = encoder.Encode(dat)
	}

	if err != nil {
		G.Log.Error("Error encoding data to %s file %s: %s",
			f.which, f.filename, err.Error())
		return err
	}

	err = writer.Close()
	if err != nil {
		G.Log.Error("Error flushing %s file %s: %s",
			f.which, f.filename, err.Error())
		return err
	}

	G.Log.Notice(fmt.Sprintf("Wrote %s file to %s", f.which, f.filename))
	f.dirty = false

	G.Log.Debug(fmt.Sprintf("- saved %s file %s", f.which, f.filename))
	return
}
<|endoftext|>


<commit_before>package convox

import (
	"fmt"

	"github.com/hashicorp/terraform/helper/schema"
)

// ResourceConvoxSyslog describes the schema for a Convox Syslog Resource resource
func ResourceConvoxSyslog(clientUnpacker ClientUnpacker) *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"hostname": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},
			"scheme": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"private": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"url": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
		Create: ResourceConvoxSyslogCreateFactory(clientUnpacker),
		Read:   ResourceConvoxSyslogReadFactory(clientUnpacker),
		Update: ResourceConvoxSyslogUpdateFactory(clientUnpacker),
		Delete: ResourceConvoxSyslogDeleteFactory(clientUnpacker),
	}
}

func formURLString(d *schema.ResourceData) string {
	return fmt.Sprintf("%s://%s:%d", d.Get("scheme"), d.Get("hostname"), d.Get("port"))
}

// ResourceConvoxSyslogCreateFactory builds the Convox Syslog CreateFunc
func ResourceConvoxSyslogCreateFactory(clientUnpacker ClientUnpacker) schema.CreateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return fmt.Errorf("Error unpacking client in CreateFunc: %s", err.Error())
		}

		options := map[string]string{
			"name":    d.Get("name").(string),
			"Url":     formURLString(d),
			"Private": fmt.Sprintf("%v", d.Get("private")),
		}

		_, err = c.CreateResource("syslog", options)
		if err != nil {
			return fmt.Errorf("Error calling CreateResource: %s -- %v", err.Error(), options)
		}

		// TODO: probably need to wait here for the status to stabilize. (and in update)

		d.Set("url", options["Url"])

		return nil
	}
}

// ResourceConvoxSyslogReadFactory builds the ReadFunc for the Convox Syslog Resource
func ResourceConvoxSyslogReadFactory(clientUnpacker ClientUnpacker) schema.ReadFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		if d == nil {
			panic("d is required")
		}

		if meta == nil {
			panic("meta is required")
		}

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return err
		}

		name := d.Get("name").(string)
		convoxResource, err := c.GetResource(name)
		if err != nil {
			return err
		}

		d.Set("url", convoxResource.Exports["URL"])

		return nil
	}
}

// ResourceConvoxSyslogUpdateFactory creates the UpdateFunc for the Convox Syslog Resource
func ResourceConvoxSyslogUpdateFactory(clientUnpacker ClientUnpacker) schema.UpdateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		if d == nil {
			panic("d is required")
		}

		if meta == nil {
			panic("meta is required")
		}

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return err
		}

		options := map[string]string{
			"Url":     formURLString(d),
			"Private": fmt.Sprintf("%v", d.Get("private")),
		}

		_, err = c.UpdateResource(d.Get("name").(string), options)
		if err != nil {
			return err
		}

		d.Set("url", options["Url"])

		return nil
	}
}

// ResourceConvoxSyslogDeleteFactory builds the DeleteFunc for thw Convox Syslog Resource
func ResourceConvoxSyslogDeleteFactory(clientUnpacker ClientUnpacker) schema.DeleteFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		if d == nil {
			panic("d is required")
		}

		if meta == nil {
			panic("meta is required")
		}

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return err
		}

		_, err = c.DeleteResource(d.Get("name").(string))
		if err != nil {
			return err
		}

		return nil
	}
}
<commit_msg>and more error context<commit_after>package convox

import (
	"fmt"

	"github.com/hashicorp/terraform/helper/schema"
)

// ResourceConvoxSyslog describes the schema for a Convox Syslog Resource resource
func ResourceConvoxSyslog(clientUnpacker ClientUnpacker) *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"hostname": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},
			"scheme": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"private": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"url": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
		Create: ResourceConvoxSyslogCreateFactory(clientUnpacker),
		Read:   ResourceConvoxSyslogReadFactory(clientUnpacker),
		Update: ResourceConvoxSyslogUpdateFactory(clientUnpacker),
		Delete: ResourceConvoxSyslogDeleteFactory(clientUnpacker),
	}
}

func formURLString(d *schema.ResourceData) string {
	return fmt.Sprintf("%s://%s:%d", d.Get("scheme"), d.Get("hostname"), d.Get("port"))
}

// ResourceConvoxSyslogCreateFactory builds the Convox Syslog CreateFunc
func ResourceConvoxSyslogCreateFactory(clientUnpacker ClientUnpacker) schema.CreateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return fmt.Errorf("Error unpacking client in CreateFunc: %s", err.Error())
		}

		options := map[string]string{
			"name":    d.Get("name").(string),
			"Url":     formURLString(d),
			"Private": fmt.Sprintf("%v", d.Get("private")),
		}

		_, err = c.CreateResource("syslog", options)
		if err != nil {
			return fmt.Errorf("Error calling CreateResource: %s -- %v", err.Error(), options)
		}

		// TODO: probably need to wait here for the status to stabilize. (and in update)

		d.Set("url", options["Url"])

		return nil
	}
}

// ResourceConvoxSyslogReadFactory builds the ReadFunc for the Convox Syslog Resource
func ResourceConvoxSyslogReadFactory(clientUnpacker ClientUnpacker) schema.ReadFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		if d == nil {
			panic("d is required")
		}

		if meta == nil {
			panic("meta is required")
		}

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return fmt.Errorf("Error getting client in ReadFunc: %s", err.Error())
		}

		name := d.Get("name").(string)
		convoxResource, err := c.GetResource(name)
		if err != nil {
			return fmt.Errorf("Error calling GetResource: %s", err.Error())
		}

		d.Set("url", convoxResource.Exports["URL"])

		return nil
	}
}

// ResourceConvoxSyslogUpdateFactory creates the UpdateFunc for the Convox Syslog Resource
func ResourceConvoxSyslogUpdateFactory(clientUnpacker ClientUnpacker) schema.UpdateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		if d == nil {
			panic("d is required")
		}

		if meta == nil {
			panic("meta is required")
		}

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return err
		}

		options := map[string]string{
			"Url":     formURLString(d),
			"Private": fmt.Sprintf("%v", d.Get("private")),
		}

		_, err = c.UpdateResource(d.Get("name").(string), options)
		if err != nil {
			return fmt.Errorf("Error calling UpdateResource: %s -- %v", err.Error(), options)
		}

		d.Set("url", options["Url"])

		return nil
	}
}

// ResourceConvoxSyslogDeleteFactory builds the DeleteFunc for thw Convox Syslog Resource
func ResourceConvoxSyslogDeleteFactory(clientUnpacker ClientUnpacker) schema.DeleteFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		if d == nil {
			panic("d is required")
		}

		if meta == nil {
			panic("meta is required")
		}

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return err
		}

		_, err = c.DeleteResource(d.Get("name").(string))
		if err != nil {
			return fmt.Errorf("Error calling DeleteResource: %s", err.Error())
		}

		return nil
	}
}
<|endoftext|>


<commit_before>// +build linux

package libsysinfo

import (
	"io/ioutil"
	"os/exec"
	"strings"
)

const ()

var (
	cacheKeys = map[string]string{
		"HOSTNAME_FULL":        "fullhostname",
		"HOSTNAME":             "hostname",
		"DOMAIN_NAME":          "domainname",
		"LSB_FULL":             "lsbfull",
		"LSB_DIST_CODE_NAME":   "lsbdistcodename",
		"LSB_DIST_DESCRIPTION": "lsbdistdescrption",
		"LSB_DIST_ID":          "lsbdistid",
		"LSB_DIST_RELEASE":     "lsbdistrelease",
		"HOST_ID":              "hostid",
		"FILE_SYSTEMS":         "filesystems",
	}

	globalCache     = newCachedValues(len(cacheKeys))
	fileSystemCache []string

	ErrDomainNameNotFound = &LibSysInfoErr{"Domain name not found"}
)

type LibSysInfoErr struct {
	Msg string
}

func (e LibSysInfoErr) Error() string {
	return e.Msg
}

func Hostname() (string, error) {
	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys["HOSTNAME"],
		Fetcher:     getFullHostname,
		Processor:   processHostname,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func Domain() (string, error) {
	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys["DOMAIN_NAME"],
		Fetcher:     getFullHostname,
		Processor:   processDomainName,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func Fqdn() (string, error) {
	fqdn := func(fullHostname string) (string, error) {
		return fullHostname, nil
	}

	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys["FQDN"],
		Fetcher:     getFullHostname,
		Processor:   fqdn,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func LsbRelease() (map[string]string, error) {
	items := make(map[string]string, 4)

	v, err := lsbReleaseItem("LSB_DIST_CODE_NAME", "Codename")
	if err != nil {
		return items, err
	}
	items["codename"] = v

	v, err = lsbReleaseItem("LSB_DIST_DESCRIPTION", "Description")
	if err != nil {
		return items, err
	}
	items["description"] = v

	v, err = lsbReleaseItem("LSB_DIST_ID", "Distributor ID")
	if err != nil {
		return items, err
	}
	items["distributorid"] = v

	v, err = lsbReleaseItem("LSB_DIST_RELEASE", "Release")
	if err != nil {
		return items, err
	}
	items["release"] = v

	return items, nil
}

func HostId() (string, error) {
	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys["HOST_ID"],
		Fetcher:     getHostId,
		Processor:   processHostId,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func FileSystems() ([]string, error) {
	if len(fileSystemCache) > 0 {
		return fileSystemCache, nil
	}

	buff, err := getFileSystems()
	if err != nil {
		return fileSystemCache, err
	}

	return processFileSystems(buff), nil
}

func lsbReleaseItem(k string, lsbItem string) (string, error) {
	proc := func(lsb string) (string, error) {
		return processLsbItem(lsb, lsbItem)
	}

	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys[k],
		Fetcher:     getLsbRelease,
		Processor:   proc,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func processDomainName(fullHostname string) (string, error) {
	pos := strings.Index(fullHostname, ".")
	if pos == -1 {
		return "", ErrDomainNameNotFound
	}

	return fullHostname[pos+1:], nil
}

func processHostname(fullHostname string) (string, error) {
	pos := strings.Index(fullHostname, ".")
	if pos == -1 {
		return fullHostname, nil
	}

	return fullHostname[:pos], nil
}

func processLsbItem(lsb string, item string) (string, error) {
	var out string
	var tmp string

	for _, line := range strings.Split(lsb, "\n") {
		if len(line) <= 0 {
			continue
		}

		tmp = line[0:len(item)]
		if tmp == item {
			out = strings.TrimSpace(strings.TrimLeft(line, item+":"))
			break
		}
	}

	return strings.ToLower(out), nil
}

func processHostId(id string) (string, error) {
	return strings.Trim(id, "\n"), nil
}

func processFileSystems(buff string) []string {
	var tmp string
	var fileSystems []string
	var isNodev bool

	for _, line := range strings.Split(buff, "\n") {
		isNodev = len(line) <= 0 || line[0] == 'n'
		if isNodev {
			continue
		}

		tmp = strings.TrimSpace(line)
		if len(tmp) <= 0 {
			continue
		}

		fileSystems = append(fileSystems, tmp)
	}

	return fileSystems
}

func getFullHostname() (string, error) {
	cacheKey := cacheKeys["HOSTNAME_FULL"]

	hf, exists := globalCache.Exists(cacheKey)
	if exists {
		return hf, nil
	}

	out, err := exec.Command("hostname", "-f").Output()
	if err != nil {
		return string(out), err
	}

	hf = string(out)

	// removing \n
	pos := strings.Index(hf, "\n")
	if pos > -1 {
		hf = hf[:pos]
	}

	globalCache.Set(cacheKey, hf)

	return hf, nil
}

func getLsbRelease() (string, error) {
	cacheKey := cacheKeys["LSB_FULL"]

	lsb, exists := globalCache.Exists(cacheKey)
	if exists {
		return lsb, nil
	}

	out, err := exec.Command("lsb_release", "-a").Output()
	if err != nil {
		return string(out), err
	}

	lsb = string(out)

	globalCache.Set(cacheKey, lsb)

	return lsb, nil
}

func getHostId() (string, error) {
	// XXX : hostid will not be reused, no need to cache the full output
	out, err := exec.Command("hostid").Output()
	if err != nil {
		return string(out), err
	}

	return string(out), nil
}

func getFileSystems() (string, error) {
	buff, err := ioutil.ReadFile("/proc/filesystems")
	if err != nil {
		return "", nil
	}

	return string(buff), nil
}
<commit_msg>Added LsbReleaseInfo type<commit_after>// +build linux

package libsysinfo

import (
	"io/ioutil"
	"os/exec"
	"strings"
)

const ()

var (
	cacheKeys = map[string]string{
		"HOSTNAME_FULL":        "fullhostname",
		"HOSTNAME":             "hostname",
		"DOMAIN_NAME":          "domainname",
		"LSB_FULL":             "lsbfull",
		"LSB_DIST_CODE_NAME":   "lsbdistcodename",
		"LSB_DIST_DESCRIPTION": "lsbdistdescrption",
		"LSB_DIST_ID":          "lsbdistid",
		"LSB_DIST_RELEASE":     "lsbdistrelease",
		"HOST_ID":              "hostid",
		"FILE_SYSTEMS":         "filesystems",
	}

	globalCache     = newCachedValues(len(cacheKeys))
	fileSystemCache []string

	ErrDomainNameNotFound = &LibSysInfoErr{"Domain name not found"}
)

type LsbReleaseInfo struct {
	Codename      string
	Description   string
	DistributorId string
	Release       string
}

type LibSysInfoErr struct {
	Msg string
}

func (e LibSysInfoErr) Error() string {
	return e.Msg
}

func Hostname() (string, error) {
	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys["HOSTNAME"],
		Fetcher:     getFullHostname,
		Processor:   processHostname,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func Domain() (string, error) {
	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys["DOMAIN_NAME"],
		Fetcher:     getFullHostname,
		Processor:   processDomainName,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func Fqdn() (string, error) {
	fqdn := func(fullHostname string) (string, error) {
		return fullHostname, nil
	}

	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys["FQDN"],
		Fetcher:     getFullHostname,
		Processor:   fqdn,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func LsbRelease() (LsbReleaseInfo, error) {
	var lsbr LsbReleaseInfo

	v, err := lsbReleaseItem("LSB_DIST_CODE_NAME", "Codename")
	if err != nil {
		return lsbr, err
	}
	lsbr.Codename = v

	v, err = lsbReleaseItem("LSB_DIST_DESCRIPTION", "Description")
	if err != nil {
		return lsbr, err
	}
	lsbr.Description = v

	v, err = lsbReleaseItem("LSB_DIST_ID", "Distributor ID")
	if err != nil {
		return lsbr, err
	}
	lsbr.DistributorId = v

	v, err = lsbReleaseItem("LSB_DIST_RELEASE", "Release")
	if err != nil {
		return lsbr, err
	}
	lsbr.Release = v

	return lsbr, nil
}

func HostId() (string, error) {
	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys["HOST_ID"],
		Fetcher:     getHostId,
		Processor:   processHostId,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func FileSystems() ([]string, error) {
	if len(fileSystemCache) > 0 {
		return fileSystemCache, nil
	}

	buff, err := getFileSystems()
	if err != nil {
		return fileSystemCache, err
	}

	return processFileSystems(buff), nil
}

func lsbReleaseItem(k string, lsbItem string) (string, error) {
	proc := func(lsb string) (string, error) {
		return processLsbItem(lsb, lsbItem)
	}

	llv := &lazyLoadedValue{
		CacheKey:    cacheKeys[k],
		Fetcher:     getLsbRelease,
		Processor:   proc,
		CacheBucket: globalCache,
	}

	return llv.run()
}

func processDomainName(fullHostname string) (string, error) {
	pos := strings.Index(fullHostname, ".")
	if pos == -1 {
		return "", ErrDomainNameNotFound
	}

	return fullHostname[pos+1:], nil
}

func processHostname(fullHostname string) (string, error) {
	pos := strings.Index(fullHostname, ".")
	if pos == -1 {
		return fullHostname, nil
	}

	return fullHostname[:pos], nil
}

func processLsbItem(lsb string, item string) (string, error) {
	var out string
	var tmp string

	for _, line := range strings.Split(lsb, "\n") {
		if len(line) <= 0 {
			continue
		}

		tmp = line[0:len(item)]
		if tmp == item {
			out = strings.TrimSpace(strings.TrimLeft(line, item+":"))
			break
		}
	}

	return strings.ToLower(out), nil
}

func processHostId(id string) (string, error) {
	return strings.Trim(id, "\n"), nil
}

func processFileSystems(buff string) []string {
	var tmp string
	var fileSystems []string
	var isNodev bool

	for _, line := range strings.Split(buff, "\n") {
		isNodev = len(line) <= 0 || line[0] == 'n'
		if isNodev {
			continue
		}

		tmp = strings.TrimSpace(line)
		if len(tmp) <= 0 {
			continue
		}

		fileSystems = append(fileSystems, tmp)
	}

	return fileSystems
}

func getFullHostname() (string, error) {
	cacheKey := cacheKeys["HOSTNAME_FULL"]

	hf, exists := globalCache.Exists(cacheKey)
	if exists {
		return hf, nil
	}

	out, err := exec.Command("hostname", "-f").Output()
	if err != nil {
		return string(out), err
	}

	hf = string(out)

	// removing \n
	pos := strings.Index(hf, "\n")
	if pos > -1 {
		hf = hf[:pos]
	}

	globalCache.Set(cacheKey, hf)

	return hf, nil
}

func getLsbRelease() (string, error) {
	cacheKey := cacheKeys["LSB_FULL"]

	lsb, exists := globalCache.Exists(cacheKey)
	if exists {
		return lsb, nil
	}

	out, err := exec.Command("lsb_release", "-a").Output()
	if err != nil {
		return string(out), err
	}

	lsb = string(out)

	globalCache.Set(cacheKey, lsb)

	return lsb, nil
}

func getHostId() (string, error) {
	// XXX : hostid will not be reused, no need to cache the full output
	out, err := exec.Command("hostid").Output()
	if err != nil {
		return string(out), err
	}

	return string(out), nil
}

func getFileSystems() (string, error) {
	buff, err := ioutil.ReadFile("/proc/filesystems")
	if err != nil {
		return "", nil
	}

	return string(buff), nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Intel Corporation
//
// 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 main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"testing"
)

func fcntl(fd int, cmd int, arg int) (val int, err error) {
	r0, _, errno := syscall.Syscall(
		syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
	val = int(r0)
	if errno != 0 {
		err = errno
	}
	return
}

// FdLeakDetector detects fd leaks by letting its user take snapshots of the
// list of opened fds at key points in the profiled program and compare two
// snapshots.
type FdLeakDetector struct {
}

type FdInfo struct {
	Fd          int
	Flags       int
	CloseOnExec bool
	Text        string
}

func (info *FdInfo) dump(w io.Writer) {
	flags := make([]string, 5)

	if info.CloseOnExec {
		flags = append(flags, "cloexec")
	}

	// file status
	if info.Flags&syscall.O_APPEND != 0 {
		flags = append(flags, "append")
	}
	if info.Flags&syscall.O_NONBLOCK != 0 {
		flags = append(flags, "nonblock")
	}

	// acc mode
	if info.Flags&syscall.O_RDONLY != 0 {
		flags = append(flags, "read-only")
	}
	if info.Flags&syscall.O_RDWR != 0 {
		flags = append(flags, "read-write")
	}
	if info.Flags&syscall.O_WRONLY != 0 {
		flags = append(flags, "write-only")
	}
	if info.Flags&syscall.O_DSYNC != 0 {
		flags = append(flags, "dsync")
	}
	if info.Flags&syscall.O_RSYNC != 0 {
		flags = append(flags, "rsync")
	}
	if info.Flags&syscall.O_SYNC != 0 {
		flags = append(flags, "sync")
	}

	fmt.Fprintf(w, "  %d: %s (%s)\n", info.Fd, info.Text,
		strings.Join(flags, ""))
}

func (info *FdInfo) equal(other *FdInfo) bool {
	return reflect.DeepEqual(info, other)
}

type FdSnapshot struct {
	Fds []FdInfo
}

// ByFd implements sort.Interface for []FdInfo based on the Fd field.
type ByFd []FdInfo

func (a ByFd) Len() int           { return len(a) }
func (a ByFd) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByFd) Less(i, j int) bool { return a[i].Fd < a[j].Fd }

func newFdSnapshot() *FdSnapshot {
	return &FdSnapshot{}
}

func (snap *FdSnapshot) FdInfo(i int) *FdInfo {
	return &snap.Fds[i]
}

func (snap *FdSnapshot) dump(w io.Writer) {
	for _, info := range snap.Fds {
		info.dump(w)
	}
}

// NewFdLeadDetector creates a new FdLeakDetector
func NewFdLeadDetector() *FdLeakDetector {
	return &FdLeakDetector{}
}

const selfFdPath = "/proc/self/fd"

// Snapshot captures the list of opened file descriptors of the current
// process
func (d *FdLeakDetector) Snapshot() (snap *FdSnapshot, err error) {
	root, err := os.Open(selfFdPath)
	if err != nil {
		return nil, err
	}
	defer root.Close()

	infos, err := root.Readdir(-1)
	if err != nil {
		return nil, err
	}

	snap = newFdSnapshot()
	for _, info := range infos {
		fd, err := strconv.Atoi(info.Name())
		if err != nil {
			return nil, err
		}

		// Don't capture the fd used to list /proc/self/fd
		if fd == int(root.Fd()) {
			continue
		}

		flags, err := fcntl(fd, syscall.F_GETFL, 0)
		if err != nil {
			return nil, err
		}

		fdFlags, err := fcntl(fd, syscall.F_GETFD, 0)
		if err != nil {
			return nil, err
		}

		// readlink on /proc/self/fd gives nice textual information
		// about the fd
		rl, err := os.Readlink(fmt.Sprintf("%s/%d", selfFdPath, fd))
		if err != nil {
			return nil, err
		}

		snap.Fds = append(snap.Fds, FdInfo{
			Fd:          fd,
			Flags:       flags,
			CloseOnExec: fdFlags&syscall.FD_CLOEXEC != 0,
			Text:        rl,
		})
	}

	sort.Sort(ByFd(snap.Fds))

	return
}

// Compare writes to w the differences between the a and b snaphots.
// true will be returned if the two snapshots are equal, false otherwise.
func (d *FdLeakDetector) Compare(w io.Writer, a, b *FdSnapshot) bool {
	equal := true

	i := 0
	j := 0

	for i < len(a.Fds) && j < len(b.Fds) {
		aInfo := a.FdInfo(i)
		bInfo := b.FdInfo(j)

		if aInfo.Fd == bInfo.Fd {
			// File descriptor found in both snapshots
			equal = aInfo.equal(bInfo)
			i++
			j++

			if !equal {
				fmt.Fprintf(w, "- fd %d\n", aInfo.Fd)
				aInfo.dump(w)
				fmt.Fprintf(w, "+ fd %d\n", bInfo.Fd)
				bInfo.dump(w)
			}
		} else if aInfo.Fd < bInfo.Fd {
			// File descriptor present in a but not in b
			equal = false
			i++

			fmt.Fprintf(w, "- fd %d\n", aInfo.Fd)
			aInfo.dump(w)
		} else {
			// File descriptor present in b but not in a
			equal = false
			j++

			fmt.Fprintf(w, "+ fd %d\n", bInfo.Fd)
			bInfo.dump(w)
		}
	}

	for ; i < len(a.Fds); i++ {
		// File descriptor present in a but not in b
		equal = false
		aInfo := a.FdInfo(i)

		fmt.Fprintf(w, "- fd %d\n", aInfo.Fd)
		aInfo.dump(w)
	}

	for ; j < len(b.Fds); j++ {
		// File descriptor present in b but not in a
		equal = false
		bInfo := b.FdInfo(j)

		fmt.Fprintf(w, "+ fd %d\n", bInfo.Fd)
		bInfo.dump(w)
	}

	return equal
}

func TestFdDetectorNoLeak(t *testing.T) {
	detector := NewFdLeadDetector()

	old, err := detector.Snapshot()
	if err != nil {
		t.Error(err)
	}

	new, err := detector.Snapshot()
	if err != nil {
		t.Error(err)
	}

	buffer := bytes.NewBuffer(nil)
	equal := detector.Compare(buffer, old, new)
	if buffer.Len() != 0 {
		fmt.Print(buffer.String())
		t.Fatal()
	}
	if !equal {
		fmt.Print(buffer.String())
		t.Fatal()
	}
}

func TestFdDetectorLeak(t *testing.T) {
	detector := NewFdLeadDetector()

	old, err := detector.Snapshot()
	if err != nil {
		t.Error(err)
	}

	_, err = os.Open("/dev/null")
	if err != nil {
		t.Error(err)
	}

	new, err := detector.Snapshot()
	if err != nil {
		t.Error(err)
	}

	buffer := bytes.NewBuffer(nil)
	equal := detector.Compare(buffer, old, new)
	if equal {
		fmt.Print(buffer.String())
		t.Fatal()
	}
}

func TestFdDetectorCompare(t *testing.T) {
	tests := []struct {
		old, new *FdSnapshot
		equal    bool
	}{
		// Same fds
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			equal: true,
		},

		// Same fd number, different flags
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDONLY,
						Text:  "/foo",
					},
				},
			},
			equal: false,
		},

		// Same fd number, different close on exec status
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:          1,
						Flags:       syscall.O_RDWR,
						CloseOnExec: true,
						Text:        "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			equal: false,
		},

		// Same fd number, different readlink
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/bar",
					},
				},
			},
			equal: false,
		},

		// old has more fds
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
				},
			},
			equal: false,
		},

		// new has more fds
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			equal: false,
		},
	}

	detector := NewFdLeadDetector()

	for i, test := range tests {
		buffer := bytes.NewBuffer(nil)

		equal := detector.Compare(buffer, test.old, test.new)
		if equal != test.equal {
			test.old.dump(os.Stderr)
			test.new.dump(os.Stderr)
			fmt.Print(buffer.String())
			t.Fatal(fmt.Sprintf("Failed test #%d", i))
		}
	}
}
<commit_msg>fdleak: Don't let Compare() return true when it shouldn't<commit_after>// Copyright (c) 2017 Intel Corporation
//
// 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 main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"testing"
)

func fcntl(fd int, cmd int, arg int) (val int, err error) {
	r0, _, errno := syscall.Syscall(
		syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
	val = int(r0)
	if errno != 0 {
		err = errno
	}
	return
}

// FdLeakDetector detects fd leaks by letting its user take snapshots of the
// list of opened fds at key points in the profiled program and compare two
// snapshots.
type FdLeakDetector struct {
}

type FdInfo struct {
	Fd          int
	Flags       int
	CloseOnExec bool
	Text        string
}

func (info *FdInfo) dump(w io.Writer) {
	flags := make([]string, 5)

	if info.CloseOnExec {
		flags = append(flags, "cloexec")
	}

	// file status
	if info.Flags&syscall.O_APPEND != 0 {
		flags = append(flags, "append")
	}
	if info.Flags&syscall.O_NONBLOCK != 0 {
		flags = append(flags, "nonblock")
	}

	// acc mode
	if info.Flags&syscall.O_RDONLY != 0 {
		flags = append(flags, "read-only")
	}
	if info.Flags&syscall.O_RDWR != 0 {
		flags = append(flags, "read-write")
	}
	if info.Flags&syscall.O_WRONLY != 0 {
		flags = append(flags, "write-only")
	}
	if info.Flags&syscall.O_DSYNC != 0 {
		flags = append(flags, "dsync")
	}
	if info.Flags&syscall.O_RSYNC != 0 {
		flags = append(flags, "rsync")
	}
	if info.Flags&syscall.O_SYNC != 0 {
		flags = append(flags, "sync")
	}

	fmt.Fprintf(w, "  %d: %s (%s)\n", info.Fd, info.Text,
		strings.Join(flags, ""))
}

func (info *FdInfo) equal(other *FdInfo) bool {
	return reflect.DeepEqual(info, other)
}

type FdSnapshot struct {
	Fds []FdInfo
}

// ByFd implements sort.Interface for []FdInfo based on the Fd field.
type ByFd []FdInfo

func (a ByFd) Len() int           { return len(a) }
func (a ByFd) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByFd) Less(i, j int) bool { return a[i].Fd < a[j].Fd }

func newFdSnapshot() *FdSnapshot {
	return &FdSnapshot{}
}

func (snap *FdSnapshot) FdInfo(i int) *FdInfo {
	return &snap.Fds[i]
}

func (snap *FdSnapshot) dump(w io.Writer) {
	for _, info := range snap.Fds {
		info.dump(w)
	}
}

// NewFdLeadDetector creates a new FdLeakDetector
func NewFdLeadDetector() *FdLeakDetector {
	return &FdLeakDetector{}
}

const selfFdPath = "/proc/self/fd"

// Snapshot captures the list of opened file descriptors of the current
// process
func (d *FdLeakDetector) Snapshot() (snap *FdSnapshot, err error) {
	root, err := os.Open(selfFdPath)
	if err != nil {
		return nil, err
	}
	defer root.Close()

	infos, err := root.Readdir(-1)
	if err != nil {
		return nil, err
	}

	snap = newFdSnapshot()
	for _, info := range infos {
		fd, err := strconv.Atoi(info.Name())
		if err != nil {
			return nil, err
		}

		// Don't capture the fd used to list /proc/self/fd
		if fd == int(root.Fd()) {
			continue
		}

		flags, err := fcntl(fd, syscall.F_GETFL, 0)
		if err != nil {
			return nil, err
		}

		fdFlags, err := fcntl(fd, syscall.F_GETFD, 0)
		if err != nil {
			return nil, err
		}

		// readlink on /proc/self/fd gives nice textual information
		// about the fd
		rl, err := os.Readlink(fmt.Sprintf("%s/%d", selfFdPath, fd))
		if err != nil {
			return nil, err
		}

		snap.Fds = append(snap.Fds, FdInfo{
			Fd:          fd,
			Flags:       flags,
			CloseOnExec: fdFlags&syscall.FD_CLOEXEC != 0,
			Text:        rl,
		})
	}

	sort.Sort(ByFd(snap.Fds))

	return
}

// Compare writes to w the differences between the a and b snaphots.
// true will be returned if the two snapshots are equal, false otherwise.
func (d *FdLeakDetector) Compare(w io.Writer, a, b *FdSnapshot) bool {
	equal := true

	i := 0
	j := 0

	for i < len(a.Fds) && j < len(b.Fds) {
		aInfo := a.FdInfo(i)
		bInfo := b.FdInfo(j)

		if aInfo.Fd == bInfo.Fd {
			// File descriptor found in both snapshots
			equal = equal && aInfo.equal(bInfo)
			i++
			j++

			if !equal {
				fmt.Fprintf(w, "- fd %d\n", aInfo.Fd)
				aInfo.dump(w)
				fmt.Fprintf(w, "+ fd %d\n", bInfo.Fd)
				bInfo.dump(w)
			}
		} else if aInfo.Fd < bInfo.Fd {
			// File descriptor present in a but not in b
			equal = false
			i++

			fmt.Fprintf(w, "- fd %d\n", aInfo.Fd)
			aInfo.dump(w)
		} else {
			// File descriptor present in b but not in a
			equal = false
			j++

			fmt.Fprintf(w, "+ fd %d\n", bInfo.Fd)
			bInfo.dump(w)
		}
	}

	for ; i < len(a.Fds); i++ {
		// File descriptor present in a but not in b
		equal = false
		aInfo := a.FdInfo(i)

		fmt.Fprintf(w, "- fd %d\n", aInfo.Fd)
		aInfo.dump(w)
	}

	for ; j < len(b.Fds); j++ {
		// File descriptor present in b but not in a
		equal = false
		bInfo := b.FdInfo(j)

		fmt.Fprintf(w, "+ fd %d\n", bInfo.Fd)
		bInfo.dump(w)
	}

	return equal
}

func TestFdDetectorNoLeak(t *testing.T) {
	detector := NewFdLeadDetector()

	old, err := detector.Snapshot()
	if err != nil {
		t.Error(err)
	}

	new, err := detector.Snapshot()
	if err != nil {
		t.Error(err)
	}

	buffer := bytes.NewBuffer(nil)
	equal := detector.Compare(buffer, old, new)
	if buffer.Len() != 0 {
		fmt.Print(buffer.String())
		t.Fatal()
	}
	if !equal {
		fmt.Print(buffer.String())
		t.Fatal()
	}
}

func TestFdDetectorLeak(t *testing.T) {
	detector := NewFdLeadDetector()

	old, err := detector.Snapshot()
	if err != nil {
		t.Error(err)
	}

	_, err = os.Open("/dev/null")
	if err != nil {
		t.Error(err)
	}

	new, err := detector.Snapshot()
	if err != nil {
		t.Error(err)
	}

	buffer := bytes.NewBuffer(nil)
	equal := detector.Compare(buffer, old, new)
	if equal {
		fmt.Print(buffer.String())
		t.Fatal()
	}
}

func TestFdDetectorCompare(t *testing.T) {
	tests := []struct {
		old, new *FdSnapshot
		equal    bool
	}{
		// Same fds
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			equal: true,
		},

		// Same fd number, different flags
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDONLY,
						Text:  "/foo",
					},
				},
			},
			equal: false,
		},

		// Same fd number, different close on exec status
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:          1,
						Flags:       syscall.O_RDWR,
						CloseOnExec: true,
						Text:        "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			equal: false,
		},

		// Same fd number, different readlink
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/bar",
					},
				},
			},
			equal: false,
		},

		// old has more fds
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
				},
			},
			equal: false,
		},

		// new has more fds
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			equal: false,
		},

		// Same number of fds in the two snapshots, different fds at
		// index 0 but same fds at index 1. We used to have a bug
		// saying the two snapshots were identical.
		{
			old: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDONLY,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			new: &FdSnapshot{
				Fds: []FdInfo{
					{
						Fd:    0,
						Flags: syscall.O_RDWR,
					},
					{
						Fd:    1,
						Flags: syscall.O_RDWR,
						Text:  "/foo",
					},
				},
			},
			equal: false,
		},
	}

	detector := NewFdLeadDetector()

	for i, test := range tests {
		buffer := bytes.NewBuffer(nil)

		equal := detector.Compare(buffer, test.old, test.new)
		if equal != test.equal {
			test.old.dump(os.Stderr)
			test.new.dump(os.Stderr)
			fmt.Print(buffer.String())
			t.Fatal(fmt.Sprintf("Failed test #%d", i))
		}
	}
}
<|endoftext|>


<commit_before>package glukit

import (
	"appengine"
	"appengine/user"
	"github.com/alexandre-normand/glukit/app/store"
	"github.com/alexandre-normand/osin"
	"net/http"
)

func initOauthProvider(writer http.ResponseWriter, request *http.Request) {
	sconfig := osin.NewServerConfig()
	sconfig.AllowedAuthorizeTypes = osin.AllowedAuthorizeType{osin.CODE, osin.TOKEN}
	sconfig.AllowedAccessTypes = osin.AllowedAccessType{osin.AUTHORIZATION_CODE,
		osin.REFRESH_TOKEN, osin.CLIENT_CREDENTIALS}
	server := osin.NewServer(sconfig, store.NewOsinAppEngineStoreWithRequest(request))
	r.HandleFunc("/authorize", func(w http.ResponseWriter, req *http.Request) {
		c := appengine.NewContext(req)
		user := user.Current(c)
		resp := server.NewResponse()
		c.Debugf("Processing authorization request: %v", r)
		if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
			// Nothing to do since the page is already login restricted by gae app configuration

			ar.Authorized = true
			ar.UserData = user.Email
			server.FinishAuthorizeRequest(resp, req, ar)
		}
		if resp.IsError && resp.InternalError != nil {
			c.Debugf("ERROR: %s\n", resp.InternalError)
		}
		osin.OutputJSON(resp, w, req)
	})

	// Access token endpoint
	r.HandleFunc("/token", func(w http.ResponseWriter, req *http.Request) {
		c := appengine.NewContext(req)
		user := user.Current(c)
		resp := server.NewResponse()
		c.Debugf("Processing token request: %v", req)
		if ar := server.HandleAccessRequest(resp, req); ar != nil {
			ar.Authorized = true
			ar.UserData = user.Email
			server.FinishAccessRequest(resp, req, ar)
		}
		osin.OutputJSON(resp, w, req)
	})
	context := appengine.NewContext(request)
	context.Debugf("Oauth server loaded: [%v]", server)
}
<commit_msg>Enable GET requests on /token.<commit_after>package glukit

import (
	"appengine"
	"appengine/user"
	"github.com/alexandre-normand/glukit/app/store"
	"github.com/alexandre-normand/osin"
	"net/http"
)

func initOauthProvider(writer http.ResponseWriter, request *http.Request) {
	sconfig := osin.NewServerConfig()
	sconfig.AllowedAuthorizeTypes = osin.AllowedAuthorizeType{osin.CODE, osin.TOKEN}
	sconfig.AllowedAccessTypes = osin.AllowedAccessType{osin.AUTHORIZATION_CODE,
		osin.REFRESH_TOKEN, osin.CLIENT_CREDENTIALS}
	sconfig.AllowGetAccessRequest = true
	server := osin.NewServer(sconfig, store.NewOsinAppEngineStoreWithRequest(request))
	r.HandleFunc("/authorize", func(w http.ResponseWriter, req *http.Request) {
		c := appengine.NewContext(req)
		user := user.Current(c)
		resp := server.NewResponse()
		c.Debugf("Processing authorization request: %v", r)
		if ar := server.HandleAuthorizeRequest(resp, req); ar != nil {
			// Nothing to do since the page is already login restricted by gae app configuration

			ar.Authorized = true
			ar.UserData = user.Email
			server.FinishAuthorizeRequest(resp, req, ar)
		}
		if resp.IsError && resp.InternalError != nil {
			c.Debugf("ERROR: %s\n", resp.InternalError)
		}
		osin.OutputJSON(resp, w, req)
	})

	// Access token endpoint
	r.HandleFunc("/token", func(w http.ResponseWriter, req *http.Request) {
		c := appengine.NewContext(req)
		user := user.Current(c)
		resp := server.NewResponse()
		c.Debugf("Processing token request: %v", req)
		if ar := server.HandleAccessRequest(resp, req); ar != nil {
			ar.Authorized = true
			ar.UserData = user.Email
			server.FinishAccessRequest(resp, req, ar)
		}
		osin.OutputJSON(resp, w, req)
	})
	context := appengine.NewContext(request)
	context.Debugf("Oauth server loaded: [%v]", server)
}
<|endoftext|>


<commit_before>// Package webobs provides creation of websocket handlers at runtime,
// allowing easy communication between go program and web browsers
package webobs

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"os"
	"sync"
)

type Message struct {
	// Tag categories the message
	Tag string
	// Data contains data, may be marshalled to JSON
	Data []byte
}

type clientobs struct {
	readCh  chan Message
	writeCh chan []byte
	ws      *websocket.Conn
	tag     string
	id      int
}

type Server struct {
	clients   map[string][]*clientobs
	clientRCh chan Message
	// WriteCh is used whenever the application wish to send messages
	// the web observers
	WriteCh   chan Message
	listeners map[string][]Listener
	mutex     *sync.Mutex
}

// Listener is the callback type when receiving messages from web observers
type Listener func(tag string, data []byte)

// SetChannel adds a websocket handle and http handle to serve rendered
// html pages and scripts
func (s *Server) SetChannel(tag string, l Listener, path string) {
	s.setHandler(tag, path)
	if l != nil {
		s.mutex.Lock()
		if _, ok := s.listeners[tag]; !ok {
			s.listeners[tag] = make([]Listener, 0)
		}
		s.listeners[tag] = append(s.listeners[tag], l)
		s.mutex.Unlock()
	}
}

func (s *Server) addNewclientobs(tag string, ws *websocket.Conn) *clientobs {

	s.mutex.Lock()
	if _, ok := s.clients[tag]; !ok {
		s.clients[tag] = make([]*clientobs, 0)
	}
	s.mutex.Unlock()

	cid := len(s.clients[tag])
	wc := make(chan []byte)
	c := clientobs{readCh: s.clientRCh, writeCh: wc, ws: ws, tag: tag, id: cid}
	s.mutex.Lock()
	s.clients[tag] = append(s.clients[tag], &c)
	s.mutex.Unlock()
	return &c
}

func (s *Server) removeclientobs(tag string, id int) {
	s.mutex.Lock()
	if _, ok := s.clients[tag]; !ok {
		return
	}
	s.mutex.Unlock()
	var idx int = -1
	for pos, c := range s.clients[tag] {
		if c.id == id {
			idx = pos
			break
		}
	}
	if idx == -1 {
		return
	}
	s.mutex.Lock()
	cl := s.clients[tag]
	s.clients[tag] = append(cl[:idx], cl[idx+1:]...)
	s.mutex.Unlock()
}

func (s *Server) listen() {
	// listen to clients
	go func() {
		for {
			select {
			case cmsg := <-s.clientRCh:
				fmt.Println("got message from client", cmsg)
				// check listener
				if _, ok := s.listeners[cmsg.Tag]; ok {
					for _, listener := range s.listeners[cmsg.Tag] {
						listener(cmsg.Tag, cmsg.Data)
					}
				}
			}
		}
	}()

	// listen to app
	func() {
		for {
			select {
			case amsg := <-s.WriteCh:
				fmt.Println("got message from app", amsg)
				if _, ok := s.clients[amsg.Tag]; ok {
					for _, cch := range s.clients[amsg.Tag] {
						cch.writeCh <- amsg.Data
					}
				}
			}
		}
	}()
}

func registerWS(tag string, ws *websocket.Conn, s *Server) {
	fmt.Println("register", tag)

	c := s.addNewclientobs(tag, ws)
	defer func() {
		s.removeclientobs(tag, c.id)
	}()

	go c.listenWrite()
	c.listenRead()
}

func (c *clientobs) listenRead() {
	for {
		select {
		default:
			var data []byte
			err := websocket.Message.Receive(c.ws, &data)
			if err == io.EOF {
				// close client
				return
			} else if err != nil {

			} else {
				c.readCh <- Message{Tag: c.tag, Data: data}
			}
		}
	}
}

func (c *clientobs) listenWrite() {
	for {
		select {
		case amsg := <-c.writeCh:
			err := websocket.Message.Send(c.ws, string(amsg))
			if err != nil {
				fmt.Println("send to client pb", amsg, err)
			}
		}
	}
}

func (s *Server) setHandler(tag string, scriptpath string) {
	tagws := tag + "_ws"
	clientscriptpath := "/" + tag + "_res/"

	http.Handle(
		clientscriptpath,
		http.StripPrefix(
			clientscriptpath,
			http.FileServer(http.Dir(scriptpath))))

	http.Handle("/"+tagws, websocket.Handler(
		func(ws *websocket.Conn) {
			registerWS(tag, ws, s)
		}))

	tmpl := scriptpath + "/" + tag + ".html"
	if _, err := os.Stat(tmpl); os.IsNotExist(err) {
		http.HandleFunc("/"+tag, func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, "
%s
"+
				""+
				"",
				tag, tagws, clientscriptpath, tag+".js")
		})
	} else {
		http.HandleFunc("/"+tag, func(w http.ResponseWriter, r *http.Request) {
			t, _ := template.ParseFiles(tmpl)
			p := struct{
				Title string
				Tagws string
				ScriptPath string
				ScriptName string
			}{
				tag,
				tagws,
				clientscriptpath,
				tag+".js",
			}
			t.Execute(w, p)
		})
	}
}

func newServer() *Server {
	cs := make(map[string][]*clientobs)
	crc := make(chan Message)
	wc := make(chan Message)
	l := make(map[string][]Listener)
	mx := &sync.Mutex{}
	return &Server{clients: cs, clientRCh: crc, WriteCh: wc, listeners: l, mutex: mx}
}

// StartServer creates and starts a http server with no handles,
// it awaits for channels to be set.
func StartServer(port string) *Server {
	s := newServer()

	go func() {
		err := http.ListenAndServe(port, nil)
		if err != nil {
			panic("ListenAndServe: " + err.Error())
		}
	}()

	go s.listen()

	return s
}
<commit_msg>more params to template and less logs<commit_after>// Package webobs provides creation of websocket handlers at runtime,
// allowing easy communication between go program and web browsers
package webobs

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"os"
	"sync"
)

type Message struct {
	// Tag categories the message
	Tag string
	// Data contains data, may be marshalled to JSON
	Data []byte
}

type clientobs struct {
	readCh  chan Message
	writeCh chan []byte
	ws      *websocket.Conn
	tag     string
	id      int
}

type Server struct {
	clients   map[string][]*clientobs
	clientRCh chan Message
	// WriteCh is used whenever the application wish to send messages
	// the web observers
	WriteCh   chan Message
	listeners map[string][]Listener
	mutex     *sync.Mutex
}

// Listener is the callback type when receiving messages from web observers
type Listener func(tag string, data []byte)

// SetChannel adds a websocket handle and http handle to serve rendered
// html pages and scripts
func (s *Server) SetChannel(tag string, l Listener, path string) {
	s.setHandler(tag, path)
	if l != nil {
		s.mutex.Lock()
		if _, ok := s.listeners[tag]; !ok {
			s.listeners[tag] = make([]Listener, 0)
		}
		s.listeners[tag] = append(s.listeners[tag], l)
		s.mutex.Unlock()
	}
}

func (s *Server) addNewclientobs(tag string, ws *websocket.Conn) *clientobs {

	s.mutex.Lock()
	if _, ok := s.clients[tag]; !ok {
		s.clients[tag] = make([]*clientobs, 0)
	}
	s.mutex.Unlock()

	cid := len(s.clients[tag])
	wc := make(chan []byte)
	c := clientobs{readCh: s.clientRCh, writeCh: wc, ws: ws, tag: tag, id: cid}
	s.mutex.Lock()
	s.clients[tag] = append(s.clients[tag], &c)
	s.mutex.Unlock()
	return &c
}

func (s *Server) removeclientobs(tag string, id int) {
	s.mutex.Lock()
	if _, ok := s.clients[tag]; !ok {
		return
	}
	s.mutex.Unlock()
	var idx int = -1
	for pos, c := range s.clients[tag] {
		if c.id == id {
			idx = pos
			break
		}
	}
	if idx == -1 {
		return
	}
	s.mutex.Lock()
	cl := s.clients[tag]
	s.clients[tag] = append(cl[:idx], cl[idx+1:]...)
	s.mutex.Unlock()
}

func (s *Server) listen() {
	// listen to clients
	go func() {
		for {
			select {
			case cmsg := <-s.clientRCh:
				//fmt.Println("got message from client", cmsg)
				// check listener
				if _, ok := s.listeners[cmsg.Tag]; ok {
					for _, listener := range s.listeners[cmsg.Tag] {
						listener(cmsg.Tag, cmsg.Data)
					}
				}
			}
		}
	}()

	// listen to app
	func() {
		for {
			select {
			case amsg := <-s.WriteCh:
				//fmt.Println("got message from app", amsg)
				if _, ok := s.clients[amsg.Tag]; ok {
					for _, cch := range s.clients[amsg.Tag] {
						cch.writeCh <- amsg.Data
					}
				}
			}
		}
	}()
}

func registerWS(tag string, ws *websocket.Conn, s *Server) {
	fmt.Println("register", tag)

	c := s.addNewclientobs(tag, ws)
	defer func() {
		s.removeclientobs(tag, c.id)
	}()

	go c.listenWrite()
	c.listenRead()
}

func (c *clientobs) listenRead() {
	for {
		select {
		default:
			var data []byte
			err := websocket.Message.Receive(c.ws, &data)
			if err == io.EOF {
				// close client
				return
			} else if err != nil {

			} else {
				c.readCh <- Message{Tag: c.tag, Data: data}
			}
		}
	}
}

func (c *clientobs) listenWrite() {
	for {
		select {
		case amsg := <-c.writeCh:
			err := websocket.Message.Send(c.ws, string(amsg))
			if err != nil {
				fmt.Println("send to client pb", amsg, err)
			}
		}
	}
}

func (s *Server) setHandler(tag string, scriptpath string) {
	tagws := tag + "_ws"
	clientscriptpath := "/" + tag + "_res/"

	http.Handle(
		clientscriptpath,
		http.StripPrefix(
			clientscriptpath,
			http.FileServer(http.Dir(scriptpath))))

	http.Handle("/"+tagws, websocket.Handler(
		func(ws *websocket.Conn) {
			registerWS(tag, ws, s)
		}))

	tmpl := scriptpath + "/" + tag + ".html"
	if _, err := os.Stat(tmpl); os.IsNotExist(err) {
		http.HandleFunc("/"+tag, func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, "
%s
"+
				""+
				"",
				tag, tagws, clientscriptpath, tag+".js")
		})
	} else {
		http.HandleFunc("/"+tag, func(w http.ResponseWriter, r *http.Request) {
			t, _ := template.ParseFiles(tmpl)
			p := struct{
				Title string
				Tagws string
				ScriptPath string
				ScriptName string
			}{
				tag,
				tagws,
				clientscriptpath,
				tag+".js",
			}
			t.Execute(w, p)
		})
	}
}

func newServer() *Server {
	cs := make(map[string][]*clientobs)
	crc := make(chan Message)
	wc := make(chan Message)
	l := make(map[string][]Listener)
	mx := &sync.Mutex{}
	return &Server{clients: cs, clientRCh: crc, WriteCh: wc, listeners: l, mutex: mx}
}

// StartServer creates and starts a http server with no handles,
// it awaits for channels to be set.
func StartServer(port string) *Server {
	s := newServer()

	go func() {
		err := http.ListenAndServe(port, nil)
		if err != nil {
			panic("ListenAndServe: " + err.Error())
		}
	}()

	go s.listen()

	return s
}
<|endoftext|>


<commit_before>package function_test

import (
	"errors"
	"testing"

	"github.com/apex/apex/function"
	"github.com/apex/apex/mock"
	"github.com/apex/log"
	"github.com/apex/log/handlers/discard"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
)

func init() {
	log.SetHandler(discard.New())
}

func TestFunction_Open_requireConfigValues(t *testing.T) {
	fn := &function.Function{
		Path: "../fixtures/function/invalidName",
		Log:  log.Log,
	}
	nameErr := fn.Open()

	fn = &function.Function{
		Path: "../fixtures/function/invalidRuntime",
		Log:  log.Log,
	}
	runtimeErr := fn.Open()

	fn = &function.Function{
		Path: "../fixtures/function/invalidMemory",
		Log:  log.Log,
	}
	memoryErr := fn.Open()

	fn = &function.Function{
		Path: "../fixtures/function/invalidTimeout",
		Log:  log.Log,
	}
	timeoutErr := fn.Open()

	fn = &function.Function{
		Path: "../fixtures/function/invalidRole",
		Log:  log.Log,
	}
	roleErr := fn.Open()

	assert.Contains(t, nameErr.Error(), "Name: zero value")
	assert.Contains(t, runtimeErr.Error(), "Runtime: zero value")
	assert.Contains(t, memoryErr.Error(), "Memory: zero value")
	assert.Contains(t, timeoutErr.Error(), "Timeout: zero value")
	assert.Contains(t, roleErr.Error(), "Role: zero value")
}

func TestFunction_Delete_success(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().DeleteFunction(&lambda.DeleteFunctionInput{
		FunctionName: aws.String("testfn"),
	}).Return(&lambda.DeleteFunctionOutput{}, nil)

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Delete()

	assert.Nil(t, err)
}

func TestFunction_Delete_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().DeleteFunction(gomock.Any()).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Delete()

	assert.EqualError(t, err, "API err")
}

func TestFunction_Rollback_GetAlias_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(&lambda.GetAliasInput{
		FunctionName: aws.String("testfn"),
		Name:         aws.String("current"),
	}).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.EqualError(t, err, "API err")
}

func TestFunction_Rollback_ListVersions_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: aws.String("1")}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(&lambda.ListVersionsByFunctionInput{
		FunctionName: aws.String("testfn"),
	}).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.EqualError(t, err, "API err")
}

func TestFunction_Rollback_fewVersions(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: aws.String("1")}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(gomock.Any()).Return(&lambda.ListVersionsByFunctionOutput{
		Versions: []*lambda.FunctionConfiguration{&lambda.FunctionConfiguration{Version: aws.String("$LATEST")}},
	}, nil)

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.EqualError(t, err, "Can't rollback. Only one version deployed.")
}

func TestFunction_Rollback_previousVersion(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	deployedVersions := []*lambda.FunctionConfiguration{
		&lambda.FunctionConfiguration{Version: aws.String("$LATEST")},
		&lambda.FunctionConfiguration{Version: aws.String("1")},
		&lambda.FunctionConfiguration{Version: aws.String("2")},
	}
	currentVersion := aws.String("2")
	afterRollbackVersion := aws.String("1")
	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: currentVersion}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(gomock.Any()).Return(&lambda.ListVersionsByFunctionOutput{Versions: deployedVersions}, nil)
	serviceMock.EXPECT().UpdateAlias(&lambda.UpdateAliasInput{
		FunctionName:    aws.String("testfn"),
		Name:            aws.String("current"),
		FunctionVersion: afterRollbackVersion,
	})

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.Nil(t, err)
}

func TestFunction_Rollback_latestVersion(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	deployedVersions := []*lambda.FunctionConfiguration{
		&lambda.FunctionConfiguration{Version: aws.String("$LATEST")},
		&lambda.FunctionConfiguration{Version: aws.String("1")},
		&lambda.FunctionConfiguration{Version: aws.String("2")},
	}
	currentVersion := aws.String("1")
	afterRollbackVersion := aws.String("2")
	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: currentVersion}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(gomock.Any()).Return(&lambda.ListVersionsByFunctionOutput{Versions: deployedVersions}, nil)
	serviceMock.EXPECT().UpdateAlias(&lambda.UpdateAliasInput{
		FunctionName:    aws.String("testfn"),
		Name:            aws.String("current"),
		FunctionVersion: afterRollbackVersion,
	})

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.Nil(t, err)
}

func TestFunction_Rollback_UpdateAlias_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: aws.String("1")}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(gomock.Any()).Return(&lambda.ListVersionsByFunctionOutput{
		Versions: []*lambda.FunctionConfiguration{
			&lambda.FunctionConfiguration{Version: aws.String("$LATEST")},
			&lambda.FunctionConfiguration{Version: aws.String("1")},
			&lambda.FunctionConfiguration{Version: aws.String("2")},
		},
	}, nil)
	serviceMock.EXPECT().UpdateAlias(gomock.Any()).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.EqualError(t, err, "API err")
}

func TestFunction_RollbackVersion_GetAlias_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(&lambda.GetAliasInput{
		FunctionName: aws.String("testfn"),
		Name:         aws.String(function.CurrentAlias),
	}).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.RollbackVersion("1")

	assert.EqualError(t, err, "API err")
}

func TestFunction_RollbackVersion_sameVersion(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	currentVersion := aws.String("2")
	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: currentVersion}, nil)

	fn := &function.Function{
		Service: serviceMock,
		Log:     log.Log,
	}
	err := fn.RollbackVersion("2")

	assert.EqualError(t, err, "Specified version currently deployed.")
}

func TestFunction_RollbackVersion_success(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	specifiedVersion := "3"
	currentVersion := "2"
	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: &currentVersion}, nil)
	serviceMock.EXPECT().UpdateAlias(&lambda.UpdateAliasInput{
		FunctionName:    aws.String("testfn"),
		Name:            aws.String(function.CurrentAlias),
		FunctionVersion: &specifiedVersion,
	})

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.RollbackVersion("3")

	assert.Nil(t, err)
}
<commit_msg>Missing Open() param<commit_after>package function_test

import (
	"errors"
	"testing"

	"github.com/apex/apex/function"
	"github.com/apex/apex/mock"
	"github.com/apex/log"
	"github.com/apex/log/handlers/discard"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
)

func init() {
	log.SetHandler(discard.New())
}

func TestFunction_Open_requireConfigValues(t *testing.T) {
	fn := &function.Function{
		Path: "../fixtures/function/invalidName",
		Log:  log.Log,
	}
	nameErr := fn.Open("")

	fn = &function.Function{
		Path: "../fixtures/function/invalidRuntime",
		Log:  log.Log,
	}
	runtimeErr := fn.Open("")

	fn = &function.Function{
		Path: "../fixtures/function/invalidMemory",
		Log:  log.Log,
	}
	memoryErr := fn.Open("")

	fn = &function.Function{
		Path: "../fixtures/function/invalidTimeout",
		Log:  log.Log,
	}
	timeoutErr := fn.Open("")

	fn = &function.Function{
		Path: "../fixtures/function/invalidRole",
		Log:  log.Log,
	}
	roleErr := fn.Open("")

	assert.Contains(t, nameErr.Error(), "Name: zero value")
	assert.Contains(t, runtimeErr.Error(), "Runtime: zero value")
	assert.Contains(t, memoryErr.Error(), "Memory: zero value")
	assert.Contains(t, timeoutErr.Error(), "Timeout: zero value")
	assert.Contains(t, roleErr.Error(), "Role: zero value")
}

func TestFunction_Delete_success(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().DeleteFunction(&lambda.DeleteFunctionInput{
		FunctionName: aws.String("testfn"),
	}).Return(&lambda.DeleteFunctionOutput{}, nil)

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Delete()

	assert.Nil(t, err)
}

func TestFunction_Delete_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().DeleteFunction(gomock.Any()).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Delete()

	assert.EqualError(t, err, "API err")
}

func TestFunction_Rollback_GetAlias_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(&lambda.GetAliasInput{
		FunctionName: aws.String("testfn"),
		Name:         aws.String("current"),
	}).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.EqualError(t, err, "API err")
}

func TestFunction_Rollback_ListVersions_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: aws.String("1")}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(&lambda.ListVersionsByFunctionInput{
		FunctionName: aws.String("testfn"),
	}).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.EqualError(t, err, "API err")
}

func TestFunction_Rollback_fewVersions(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: aws.String("1")}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(gomock.Any()).Return(&lambda.ListVersionsByFunctionOutput{
		Versions: []*lambda.FunctionConfiguration{&lambda.FunctionConfiguration{Version: aws.String("$LATEST")}},
	}, nil)

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.EqualError(t, err, "Can't rollback. Only one version deployed.")
}

func TestFunction_Rollback_previousVersion(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	deployedVersions := []*lambda.FunctionConfiguration{
		&lambda.FunctionConfiguration{Version: aws.String("$LATEST")},
		&lambda.FunctionConfiguration{Version: aws.String("1")},
		&lambda.FunctionConfiguration{Version: aws.String("2")},
	}
	currentVersion := aws.String("2")
	afterRollbackVersion := aws.String("1")
	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: currentVersion}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(gomock.Any()).Return(&lambda.ListVersionsByFunctionOutput{Versions: deployedVersions}, nil)
	serviceMock.EXPECT().UpdateAlias(&lambda.UpdateAliasInput{
		FunctionName:    aws.String("testfn"),
		Name:            aws.String("current"),
		FunctionVersion: afterRollbackVersion,
	})

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.Nil(t, err)
}

func TestFunction_Rollback_latestVersion(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	deployedVersions := []*lambda.FunctionConfiguration{
		&lambda.FunctionConfiguration{Version: aws.String("$LATEST")},
		&lambda.FunctionConfiguration{Version: aws.String("1")},
		&lambda.FunctionConfiguration{Version: aws.String("2")},
	}
	currentVersion := aws.String("1")
	afterRollbackVersion := aws.String("2")
	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: currentVersion}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(gomock.Any()).Return(&lambda.ListVersionsByFunctionOutput{Versions: deployedVersions}, nil)
	serviceMock.EXPECT().UpdateAlias(&lambda.UpdateAliasInput{
		FunctionName:    aws.String("testfn"),
		Name:            aws.String("current"),
		FunctionVersion: afterRollbackVersion,
	})

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.Nil(t, err)
}

func TestFunction_Rollback_UpdateAlias_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: aws.String("1")}, nil)
	serviceMock.EXPECT().ListVersionsByFunction(gomock.Any()).Return(&lambda.ListVersionsByFunctionOutput{
		Versions: []*lambda.FunctionConfiguration{
			&lambda.FunctionConfiguration{Version: aws.String("$LATEST")},
			&lambda.FunctionConfiguration{Version: aws.String("1")},
			&lambda.FunctionConfiguration{Version: aws.String("2")},
		},
	}, nil)
	serviceMock.EXPECT().UpdateAlias(gomock.Any()).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.Rollback()

	assert.EqualError(t, err, "API err")
}

func TestFunction_RollbackVersion_GetAlias_failed(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	serviceMock.EXPECT().GetAlias(&lambda.GetAliasInput{
		FunctionName: aws.String("testfn"),
		Name:         aws.String(function.CurrentAlias),
	}).Return(nil, errors.New("API err"))

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.RollbackVersion("1")

	assert.EqualError(t, err, "API err")
}

func TestFunction_RollbackVersion_sameVersion(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	currentVersion := aws.String("2")
	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: currentVersion}, nil)

	fn := &function.Function{
		Service: serviceMock,
		Log:     log.Log,
	}
	err := fn.RollbackVersion("2")

	assert.EqualError(t, err, "Specified version currently deployed.")
}

func TestFunction_RollbackVersion_success(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	serviceMock := mock_lambdaiface.NewMockLambdaAPI(mockCtrl)

	specifiedVersion := "3"
	currentVersion := "2"
	serviceMock.EXPECT().GetAlias(gomock.Any()).Return(&lambda.AliasConfiguration{FunctionVersion: &currentVersion}, nil)
	serviceMock.EXPECT().UpdateAlias(&lambda.UpdateAliasInput{
		FunctionName:    aws.String("testfn"),
		Name:            aws.String(function.CurrentAlias),
		FunctionVersion: &specifiedVersion,
	})

	fn := &function.Function{
		LambdaName: "testfn",
		Service:    serviceMock,
		Log:        log.Log,
	}
	err := fn.RollbackVersion("3")

	assert.Nil(t, err)
}
<|endoftext|>


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

package util

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"text/template"
)

const (
	fleetAPIPort = 54728
	cloudConfig  = `#cloud-config

write_files:
 - path: /opt/fleet/fleet.conf
   content: |
    verbosity=2
    etcd_servers=[{{printf "%q" .EtcdEndpoint}}]
    etcd_key_prefix={{.EtcdKeyPrefix}}
    public_ip={{.IP}}

ssh_authorized_keys:
 - {{printf "%q" .PublicKey}}

coreos:
 units:
  - name: 00-local.network
    content: |
      [Match]
      Name=host0
      [Network]
      Address={{.IP}}/16
  - name: fleet.socket
    command: start
    content: |
     [Socket]
     ListenStream={{printf "%d" .FleetAPIPort}}
  - name: fleet.service
    command: start
    content: |
     [Service]
     ExecStart=/opt/fleet/fleetd -config /opt/fleet/fleet.conf
`
)

var (
	fleetdBinPath  string
	publicKeyPath  string
	configTemplate *template.Template
)

type configValues struct {
	IP            string
	PublicKey     string
	Fleetd        string
	EtcdEndpoint  string
	EtcdKeyPrefix string
	FleetAPIPort  int
}

func init() {
	fleetdBinPath = os.Getenv("FLEETD_BIN")
	if fleetdBinPath == "" {
		fmt.Println("FLEETD_BIN environment variable must be set")
		os.Exit(1)
	} else if _, err := os.Stat(fleetdBinPath); err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(1)
	}
	publicKeyPath = path.Join("fixtures", "id_rsa.pub")
	if _, err := os.Stat(publicKeyPath); err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(1)
	}

	// sanity check that go's use of tabs didn't leak into the yaml
	if strings.ContainsRune(cloudConfig, '\t') {
		panic("Aagh, no! Someone got tabs in the YAML!")
	}
	configTemplate = template.Must(template.New("cc").Parse(cloudConfig))
}

func BuildCloudConfig(dst io.Writer, ip, etcdEndpoint, etcdKeyPrefix string) error {
	key, err := ioutil.ReadFile(publicKeyPath)
	if err != nil {
		return err
	}

	values := configValues{
		IP:            ip,
		PublicKey:     string(key),
		EtcdEndpoint:  etcdEndpoint,
		EtcdKeyPrefix: etcdKeyPrefix,
		FleetAPIPort:  fleetAPIPort,
	}

	return configTemplate.Execute(dst, &values)
}
<commit_msg>functional/util: remove unnecessary imports<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"text/template"
)

const (
	fleetAPIPort = 54728
	cloudConfig  = `#cloud-config

write_files:
 - path: /opt/fleet/fleet.conf
   content: |
    verbosity=2
    etcd_servers=[{{printf "%q" .EtcdEndpoint}}]
    etcd_key_prefix={{.EtcdKeyPrefix}}
    public_ip={{.IP}}

ssh_authorized_keys:
 - {{printf "%q" .PublicKey}}

coreos:
 units:
  - name: 00-local.network
    content: |
      [Match]
      Name=host0
      [Network]
      Address={{.IP}}/16
  - name: fleet.socket
    command: start
    content: |
     [Socket]
     ListenStream={{printf "%d" .FleetAPIPort}}
  - name: fleet.service
    command: start
    content: |
     [Service]
     ExecStart=/opt/fleet/fleetd -config /opt/fleet/fleet.conf
`
)

var (
	fleetdBinPath  string
	publicKeyPath  string
	configTemplate *template.Template
)

type configValues struct {
	IP            string
	PublicKey     string
	Fleetd        string
	EtcdEndpoint  string
	EtcdKeyPrefix string
	FleetAPIPort  int
}

func init() {
	fleetdBinPath = os.Getenv("FLEETD_BIN")
	if fleetdBinPath == "" {
		fmt.Println("FLEETD_BIN environment variable must be set")
		os.Exit(1)
	} else if _, err := os.Stat(fleetdBinPath); err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(1)
	}
	publicKeyPath = path.Join("fixtures", "id_rsa.pub")
	if _, err := os.Stat(publicKeyPath); err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(1)
	}

	// sanity check that go's use of tabs didn't leak into the yaml
	if strings.ContainsRune(cloudConfig, '\t') {
		panic("Aagh, no! Someone got tabs in the YAML!")
	}
	configTemplate = template.Must(template.New("cc").Parse(cloudConfig))
}

func BuildCloudConfig(dst io.Writer, ip, etcdEndpoint, etcdKeyPrefix string) error {
	key, err := ioutil.ReadFile(publicKeyPath)
	if err != nil {
		return err
	}

	values := configValues{
		IP:            ip,
		PublicKey:     string(key),
		EtcdEndpoint:  etcdEndpoint,
		EtcdKeyPrefix: etcdKeyPrefix,
		FleetAPIPort:  fleetAPIPort,
	}

	return configTemplate.Execute(dst, &values)
}
<|endoftext|>