<commit_before>package sqlr

import (
	"fmt"
	"reflect"

	"github.com/jjeffery/dataloader"
	"github.com/jjeffery/errors"
)

func makeQuery(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	for _, maker := range []func(reflect.Type, *Schema) (func(*Session) reflect.Value, error){
		selectFunc,
		getOneFunc,
		getManyFunc,
		loadOneFunc,
	} {
		f, err := maker(funcType, schema)
		if err != nil {
			return nil, err
		}
		if f != nil {
			return f, nil
		}
	}

	return nil, newError("cannot recognize function: %v", funcType.String())
}

// selectFunc returns a func implementation if the func is a select func.
// input args alternatives:
//   (query string, args ...interface{})
// output args alternatives:
//   ([]*Row, error)
//   (*Row, error)
// Returns nil if not a match, returns error if the function looks like a query
// but is not quite conformant.
func selectFunc(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	if funcType.NumIn() < 1 {
		return nil, nil // not a select function
	}
	if funcType.In(0) != wellKnownTypes.stringType {
		return nil, nil // not a select function
	}
	const invalidInputsMsg = "expect query function inputs to be like (query string, args ...interface{})"
	if funcType.NumIn() != 2 {
		return nil, newError(invalidInputsMsg)
	}
	if funcType.In(1) != wellKnownTypes.sliceOfInterfaceType {
		return nil, newError(invalidInputsMsg)
	}

	rowTypeName := "Row" // don't know the type yet
	invalidOutputsMsg := fmt.Sprintf("MakeQuery: expect query function outputs to be like ([]*%s, error) or (*%s, error)", rowTypeName, rowTypeName)
	if funcType.NumOut() != 2 {
		return nil, newError(invalidOutputsMsg)
	}
	rowType := funcType.Out(0)
	if rowType.Kind() == reflect.Slice {
		rowType = rowType.Elem()
	}
	if rowType.Kind() == reflect.Ptr {
		rowType = rowType.Elem()
	}
	if rowType.Kind() != reflect.Struct {
		return nil, newError("expected struct type, got %v", rowType.String())
	}
	tbl := schema.TableFor(rowType)
	if funcType.Out(1) != wellKnownTypes.errorType {
		return nil, newError(invalidOutputsMsg)
	}
	rowPtrType := reflect.PtrTo(rowType)
	if funcType.Out(0) == rowPtrType {
		return makeSelectRowFunc(funcType, tbl), nil
	}
	sliceOfRowPtrType := reflect.SliceOf(rowPtrType)
	if funcType.Out(0) == sliceOfRowPtrType {
		return makeSelectRowsFunc(funcType, tbl), nil
	}

	return nil, newError(invalidOutputsMsg)
}

func makeSelectRowsFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		return reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
			rowsPtrValue := reflect.New(reflect.SliceOf(reflect.PtrTo(tbl.RowType())))
			query := args[0].Interface().(string)
			queryArgs := args[1].Interface().([]interface{})
			_, err := sess.Select(rowsPtrValue.Interface(), query, queryArgs...)
			if err != nil {
				err = errors.Wrap(err, fmt.Sprintf("cannot query %s", tbl.plural())).With(
					"query", query,
					"args", queryArgs,
				)
			}
			rowsValue := rowsPtrValue.Elem()
			errValue := reflect.ValueOf(err)
			if err == nil {
				errValue = reflect.Zero(wellKnownTypes.errorType)
			}
			return []reflect.Value{
				rowsValue,
				errValue,
			}
		})
	}
}

func makeSelectRowFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		return reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
			rowPtrValue := reflect.New(tbl.RowType())
			query := args[0].Interface().(string)
			queryArgs := args[1].Interface().([]interface{})
			_, err := sess.Select(rowPtrValue.Interface(), query, queryArgs...)
			if err != nil {
				err = errors.Wrap(err, fmt.Sprintf("cannot query one %s", tbl.singular())).With(
					"query", query,
					"args", queryArgs,
				)
				rowPtrValue = reflect.Zero(reflect.PtrTo(tbl.RowType()))
			}

			return []reflect.Value{
				rowPtrValue,
				errorValueFor(err),
			}
		})
	}
}

func getOneFunc(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	if funcType.NumIn() != 1 {
		return nil, nil
	}
	if funcType.NumOut() != 2 {
		return nil, nil
	}
	if funcType.In(0).Kind() == reflect.Slice {
		return nil, nil
	}
	if funcType.Out(1) != wellKnownTypes.errorType {
		return nil, newError("expecting second return arg to be error")
	}
	rowType := funcType.Out(0)
	if rowType.Kind() != reflect.Ptr {
		return nil, newError("expecting first return arg to be a pointer to struct")
	}
	rowType = rowType.Elem()
	if rowType.Kind() != reflect.Struct {
		return nil, newError("expecting first return arg to be a pointer to struct")
	}
	tbl := schema.TableFor(rowType)
	if len(tbl.PrimaryKey()) == 0 {

	}

	pkCol, err := getPKCol(tbl)
	if err != nil {
		return nil, err
	}

	inType := funcType.In(0)
	if inType != pkCol.info.Field.Type {
		return nil, newError("looks like a get func, but %s has primary key type of %s", tbl.RowType().String(), pkCol.info.Field.Type.String())
	}

	return makeGetOneFunc(funcType, tbl), nil
}

func makeGetOneFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		return reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
			rowPtrValue := reflect.New(tbl.RowType())
			query := fmt.Sprintf("select {} from %s where {}", tbl.Name())
			queryArgs := []interface{}{args[0].Interface()}
			_, err := sess.Select(rowPtrValue.Interface(), query, queryArgs...)
			if err != nil {
				err = errors.Wrap(err, fmt.Sprintf("cannot get one %s", tbl.singular())).With(
					"query", query,
					"args", queryArgs,
				)
				rowPtrValue = reflect.Zero(reflect.PtrTo(tbl.RowType()))
			}
			return []reflect.Value{
				rowPtrValue,
				errorValueFor(err),
			}
		})
	}
}

func getManyFunc(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	if funcType.NumIn() != 1 {
		return nil, nil
	}
	if funcType.NumOut() != 2 {
		return nil, nil
	}
	if funcType.In(0).Kind() != reflect.Slice {
		return nil, nil
	}
	if funcType.Out(1) != wellKnownTypes.errorType {
		return nil, newError("expecting second return arg to be error")
	}
	rowType := funcType.Out(0)
	if rowType.Kind() != reflect.Slice {
		return nil, newError("expecting first return arg to be a slice of pointer to struct")
	}
	rowType = rowType.Elem()
	if rowType.Kind() != reflect.Ptr {
		return nil, newError("expecting first return arg to be a slice of pointer to struct")
	}
	rowType = rowType.Elem()
	if rowType.Kind() != reflect.Struct {
		return nil, newError("expecting first return arg to be a slice of pointer to struct")
	}
	tbl := schema.TableFor(rowType)
	pkCol, err := getPKCol(tbl)
	if err != nil {
		return nil, err
	}

	inType := funcType.In(0)
	if inType != reflect.SliceOf(pkCol.info.Field.Type) {
		return nil, newError("looks like a get func, but %s has primary key type of %s", tbl.RowType().String(), pkCol.info.Field.Type.String())
	}

	return makeGetManyFunc(funcType, tbl), nil
}

func makeGetManyFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		return reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
			var err error
			rowsPtrValue := reflect.New(reflect.SliceOf(reflect.PtrTo(tbl.RowType())))
			if len(args) > 0 {
				pkColName := tbl.PrimaryKey()[0].Name()
				query := fmt.Sprintf("select {} from %s where `%s` in (?)", tbl.Name(), pkColName)
				queryArgs := make([]interface{}, len(args))
				for i, arg := range args {
					queryArgs[i] = arg.Interface()
				}
				_, err = sess.Select(rowsPtrValue.Interface(), query, queryArgs...)
				if err != nil {
					err = errors.Wrap(err, "cannot get one %s", tbl.singular()).With(
						"query", query,
						"args", queryArgs,
					)
				}
			}
			rowsValue := rowsPtrValue.Elem()
			return []reflect.Value{
				rowsValue,
				errorValueFor(err),
			}
		})
	}
}

func loadOneFunc(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	if funcType.NumIn() != 1 {
		return nil, nil
	}
	if funcType.NumOut() != 1 {
		return nil, nil
	}
	if funcType.In(0).Kind() == reflect.Slice {
		return nil, nil
	}

	thunkType := funcType.Out(0)
	if thunkType.Kind() != reflect.Func {
		return nil, newError("looks like a load function, but does not return a thunk (function): %s", funcType.String())
	}
	if thunkType.NumIn() != 0 {
		return nil, newError("thunk function cannot accept arguments: %s", funcType.String())
	}
	invalidOutputsErr := newError("expect thunk to return a pointer to struct and an error)")
	if thunkType.NumOut() != 2 {
		return nil, invalidOutputsErr
	}
	if thunkType.Out(1) != wellKnownTypes.errorType {
		return nil, invalidOutputsErr
	}
	rowType := thunkType.Out(0)
	if rowType.Kind() != reflect.Ptr {
		return nil, invalidOutputsErr
	}
	rowType = rowType.Elem()
	if rowType.Kind() != reflect.Struct {
		return nil, invalidOutputsErr
	}
	tbl := schema.TableFor(rowType)

	pkCol, err := getPKCol(tbl)
	if err != nil {
		return nil, err
	}

	inType := funcType.In(0)
	if inType != pkCol.info.Field.Type {
		return nil, newError("looks like a load func, but %s has primary key type of %s", tbl.RowType().String(), pkCol.info.Field.Type.String())
	}

	return makeLoadOneFunc(funcType, tbl), nil
}

func makeLoadOneFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		pkCol := tbl.PrimaryKey()[0]
		queryFuncIn := []reflect.Type{reflect.SliceOf(pkCol.fieldType())}
		queryFuncOut := []reflect.Type{reflect.SliceOf(reflect.PtrTo(tbl.RowType())), wellKnownTypes.errorType}
		queryFuncType := reflect.FuncOf(queryFuncIn, queryFuncOut, false)
		queryFuncValue := makeGetManyFunc(queryFuncType, tbl)(sess)

		keyFuncIn := []reflect.Type{reflect.PtrTo(tbl.RowType())}
		keyFuncOut := []reflect.Type{pkCol.fieldType()}
		keyFuncType := reflect.FuncOf(keyFuncIn, keyFuncOut, false)
		keyFuncValue := reflect.MakeFunc(keyFuncType, func(args []reflect.Value) []reflect.Value {
			rowPtrValue := args[0]
			rowValue := rowPtrValue.Elem()
			keyValue := rowValue.FieldByIndex([]int(pkCol.fieldIndex()))
			return []reflect.Value{keyValue}
		})

		loadFuncPtrValue := reflect.New(funcType)
		dataloader.Make(loadFuncPtrValue.Interface(), queryFuncValue.Interface(), keyFuncValue.Interface())
		return loadFuncPtrValue.Elem()
	}
}

func getPKCol(tbl *Table) (*Column, error) {
	pkCols := tbl.PrimaryKey()
	if len(pkCols) > 1 {
		return nil, newError("compositeprimary key not supported: %s", tbl.RowType().String())
	}
	if len(pkCols) == 0 {
		return nil, newError("no primary key defined for %s (table %s)", tbl.RowType().String(), tbl.Name())
	}
	return pkCols[0], nil
}

type rowFuncError string

func newError(format string, args ...interface{}) rowFuncError {
	msg := fmt.Sprintf(format, args...)
	return rowFuncError(msg)
}

func (e rowFuncError) Error() string {
	return string(e)
}
<commit_msg>Fix reflection-based error handling.<commit_after>package sqlr

import (
	"fmt"
	"reflect"

	"github.com/jjeffery/dataloader"
	"github.com/jjeffery/errors"
)

func makeQuery(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	for _, maker := range []func(reflect.Type, *Schema) (func(*Session) reflect.Value, error){
		selectFunc,
		getOneFunc,
		getManyFunc,
		loadOneFunc,
	} {
		f, err := maker(funcType, schema)
		if err != nil {
			return nil, err
		}
		if f != nil {
			return f, nil
		}
	}

	return nil, newError("cannot recognize function: %v", funcType.String())
}

// selectFunc returns a func implementation if the func is a select func.
// input args alternatives:
//   (query string, args ...interface{})
// output args alternatives:
//   ([]*Row, error)
//   (*Row, error)
// Returns nil if not a match, returns error if the function looks like a query
// but is not quite conformant.
func selectFunc(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	if funcType.NumIn() < 1 {
		return nil, nil // not a select function
	}
	if funcType.In(0) != wellKnownTypes.stringType {
		return nil, nil // not a select function
	}
	const invalidInputsMsg = "expect query function inputs to be like (query string, args ...interface{})"
	if funcType.NumIn() != 2 {
		return nil, newError(invalidInputsMsg)
	}
	if funcType.In(1) != wellKnownTypes.sliceOfInterfaceType {
		return nil, newError(invalidInputsMsg)
	}

	rowTypeName := "Row" // don't know the type yet
	invalidOutputsMsg := fmt.Sprintf("MakeQuery: expect query function outputs to be like ([]*%s, error) or (*%s, error)", rowTypeName, rowTypeName)
	if funcType.NumOut() != 2 {
		return nil, newError(invalidOutputsMsg)
	}
	rowType := funcType.Out(0)
	if rowType.Kind() == reflect.Slice {
		rowType = rowType.Elem()
	}
	if rowType.Kind() == reflect.Ptr {
		rowType = rowType.Elem()
	}
	if rowType.Kind() != reflect.Struct {
		return nil, newError("expected struct type, got %v", rowType.String())
	}
	tbl := schema.TableFor(rowType)
	if funcType.Out(1) != wellKnownTypes.errorType {
		return nil, newError(invalidOutputsMsg)
	}
	rowPtrType := reflect.PtrTo(rowType)
	if funcType.Out(0) == rowPtrType {
		return makeSelectRowFunc(funcType, tbl), nil
	}
	sliceOfRowPtrType := reflect.SliceOf(rowPtrType)
	if funcType.Out(0) == sliceOfRowPtrType {
		return makeSelectRowsFunc(funcType, tbl), nil
	}

	return nil, newError(invalidOutputsMsg)
}

func makeSelectRowsFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		return reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
			rowsPtrValue := reflect.New(reflect.SliceOf(reflect.PtrTo(tbl.RowType())))
			query := args[0].Interface().(string)
			queryArgs := args[1].Interface().([]interface{})
			_, err := sess.Select(rowsPtrValue.Interface(), query, queryArgs...)
			if err != nil {
				err = errors.Wrap(err, fmt.Sprintf("cannot query %s", tbl.plural())).With(
					"query", query,
					"args", queryArgs,
				)
			}
			rowsValue := rowsPtrValue.Elem()
			errValue := errorValueFor(err)
			return []reflect.Value{
				rowsValue,
				errValue,
			}
		})
	}
}

func makeSelectRowFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		return reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
			rowPtrValue := reflect.New(tbl.RowType())
			query := args[0].Interface().(string)
			queryArgs := args[1].Interface().([]interface{})
			_, err := sess.Select(rowPtrValue.Interface(), query, queryArgs...)
			if err != nil {
				err = errors.Wrap(err, fmt.Sprintf("cannot query one %s", tbl.singular())).With(
					"query", query,
					"args", queryArgs,
				)
				rowPtrValue = reflect.Zero(reflect.PtrTo(tbl.RowType()))
			}

			return []reflect.Value{
				rowPtrValue,
				errorValueFor(err),
			}
		})
	}
}

func getOneFunc(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	if funcType.NumIn() != 1 {
		return nil, nil
	}
	if funcType.NumOut() != 2 {
		return nil, nil
	}
	if funcType.In(0).Kind() == reflect.Slice {
		return nil, nil
	}
	if funcType.Out(1) != wellKnownTypes.errorType {
		return nil, newError("expecting second return arg to be error")
	}
	rowType := funcType.Out(0)
	if rowType.Kind() != reflect.Ptr {
		return nil, newError("expecting first return arg to be a pointer to struct")
	}
	rowType = rowType.Elem()
	if rowType.Kind() != reflect.Struct {
		return nil, newError("expecting first return arg to be a pointer to struct")
	}
	tbl := schema.TableFor(rowType)
	if len(tbl.PrimaryKey()) == 0 {

	}

	pkCol, err := getPKCol(tbl)
	if err != nil {
		return nil, err
	}

	inType := funcType.In(0)
	if inType != pkCol.info.Field.Type {
		return nil, newError("looks like a get func, but %s has primary key type of %s", tbl.RowType().String(), pkCol.info.Field.Type.String())
	}

	return makeGetOneFunc(funcType, tbl), nil
}

func makeGetOneFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		return reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
			rowPtrValue := reflect.New(tbl.RowType())
			query := fmt.Sprintf("select {} from %s where {}", tbl.Name())
			queryArgs := []interface{}{args[0].Interface()}
			_, err := sess.Select(rowPtrValue.Interface(), query, queryArgs...)
			if err != nil {
				err = errors.Wrap(err, fmt.Sprintf("cannot get one %s", tbl.singular())).With(
					"query", query,
					"args", queryArgs,
				)
				rowPtrValue = reflect.Zero(reflect.PtrTo(tbl.RowType()))
			}
			return []reflect.Value{
				rowPtrValue,
				errorValueFor(err),
			}
		})
	}
}

func getManyFunc(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	if funcType.NumIn() != 1 {
		return nil, nil
	}
	if funcType.NumOut() != 2 {
		return nil, nil
	}
	if funcType.In(0).Kind() != reflect.Slice {
		return nil, nil
	}
	if funcType.Out(1) != wellKnownTypes.errorType {
		return nil, newError("expecting second return arg to be error")
	}
	rowType := funcType.Out(0)
	if rowType.Kind() != reflect.Slice {
		return nil, newError("expecting first return arg to be a slice of pointer to struct")
	}
	rowType = rowType.Elem()
	if rowType.Kind() != reflect.Ptr {
		return nil, newError("expecting first return arg to be a slice of pointer to struct")
	}
	rowType = rowType.Elem()
	if rowType.Kind() != reflect.Struct {
		return nil, newError("expecting first return arg to be a slice of pointer to struct")
	}
	tbl := schema.TableFor(rowType)
	pkCol, err := getPKCol(tbl)
	if err != nil {
		return nil, err
	}

	inType := funcType.In(0)
	if inType != reflect.SliceOf(pkCol.info.Field.Type) {
		return nil, newError("looks like a get func, but %s has primary key type of %s", tbl.RowType().String(), pkCol.info.Field.Type.String())
	}

	return makeGetManyFunc(funcType, tbl), nil
}

func makeGetManyFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		return reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
			var err error
			rowsPtrValue := reflect.New(reflect.SliceOf(reflect.PtrTo(tbl.RowType())))
			if len(args) > 0 {
				pkColName := tbl.PrimaryKey()[0].Name()
				query := fmt.Sprintf("select {} from %s where `%s` in (?)", tbl.Name(), pkColName)
				queryArgs := make([]interface{}, len(args))
				for i, arg := range args {
					queryArgs[i] = arg.Interface()
				}
				_, err = sess.Select(rowsPtrValue.Interface(), query, queryArgs...)
				if err != nil {
					err = errors.Wrap(err, "cannot get one %s", tbl.singular()).With(
						"query", query,
						"args", queryArgs,
					)
				}
			}
			rowsValue := rowsPtrValue.Elem()
			return []reflect.Value{
				rowsValue,
				errorValueFor(err),
			}
		})
	}
}

func loadOneFunc(funcType reflect.Type, schema *Schema) (func(*Session) reflect.Value, error) {
	if funcType.NumIn() != 1 {
		return nil, nil
	}
	if funcType.NumOut() != 1 {
		return nil, nil
	}
	if funcType.In(0).Kind() == reflect.Slice {
		return nil, nil
	}

	thunkType := funcType.Out(0)
	if thunkType.Kind() != reflect.Func {
		return nil, newError("looks like a load function, but does not return a thunk (function): %s", funcType.String())
	}
	if thunkType.NumIn() != 0 {
		return nil, newError("thunk function cannot accept arguments: %s", funcType.String())
	}
	invalidOutputsErr := newError("expect thunk to return a pointer to struct and an error)")
	if thunkType.NumOut() != 2 {
		return nil, invalidOutputsErr
	}
	if thunkType.Out(1) != wellKnownTypes.errorType {
		return nil, invalidOutputsErr
	}
	rowType := thunkType.Out(0)
	if rowType.Kind() != reflect.Ptr {
		return nil, invalidOutputsErr
	}
	rowType = rowType.Elem()
	if rowType.Kind() != reflect.Struct {
		return nil, invalidOutputsErr
	}
	tbl := schema.TableFor(rowType)

	pkCol, err := getPKCol(tbl)
	if err != nil {
		return nil, err
	}

	inType := funcType.In(0)
	if inType != pkCol.info.Field.Type {
		return nil, newError("looks like a load func, but %s has primary key type of %s", tbl.RowType().String(), pkCol.info.Field.Type.String())
	}

	return makeLoadOneFunc(funcType, tbl), nil
}

func makeLoadOneFunc(funcType reflect.Type, tbl *Table) func(*Session) reflect.Value {
	return func(sess *Session) reflect.Value {
		pkCol := tbl.PrimaryKey()[0]
		queryFuncIn := []reflect.Type{reflect.SliceOf(pkCol.fieldType())}
		queryFuncOut := []reflect.Type{reflect.SliceOf(reflect.PtrTo(tbl.RowType())), wellKnownTypes.errorType}
		queryFuncType := reflect.FuncOf(queryFuncIn, queryFuncOut, false)
		queryFuncValue := makeGetManyFunc(queryFuncType, tbl)(sess)

		keyFuncIn := []reflect.Type{reflect.PtrTo(tbl.RowType())}
		keyFuncOut := []reflect.Type{pkCol.fieldType()}
		keyFuncType := reflect.FuncOf(keyFuncIn, keyFuncOut, false)
		keyFuncValue := reflect.MakeFunc(keyFuncType, func(args []reflect.Value) []reflect.Value {
			rowPtrValue := args[0]
			rowValue := rowPtrValue.Elem()
			keyValue := rowValue.FieldByIndex([]int(pkCol.fieldIndex()))
			return []reflect.Value{keyValue}
		})

		loadFuncPtrValue := reflect.New(funcType)
		dataloader.Make(loadFuncPtrValue.Interface(), queryFuncValue.Interface(), keyFuncValue.Interface())
		return loadFuncPtrValue.Elem()
	}
}

func getPKCol(tbl *Table) (*Column, error) {
	pkCols := tbl.PrimaryKey()
	if len(pkCols) > 1 {
		return nil, newError("compositeprimary key not supported: %s", tbl.RowType().String())
	}
	if len(pkCols) == 0 {
		return nil, newError("no primary key defined for %s (table %s)", tbl.RowType().String(), tbl.Name())
	}
	return pkCols[0], nil
}

type rowFuncError string

func newError(format string, args ...interface{}) rowFuncError {
	msg := fmt.Sprintf(format, args...)
	return rowFuncError(msg)
}

func (e rowFuncError) Error() string {
	return string(e)
}
<|endoftext|>


<commit_before>package sql

import (
	"database/sql"
	"fmt"

	"github.com/jmoiron/sqlx"
	"github.com/osuripple/cheesegull"
)

func (p *provider) Beatmaps(beatmapIDs ...int) ([]cheesegull.Beatmap, error) {
	query, params, err := sqlx.In("SELECT "+`
		id as beatmap_id, parent_id, diff_name, file_md5, mode, bpm, ar, od, cs, hp,
		total_length, hit_length, playcount, passcount, max_combo,
		difficulty_rating`+" FROM beatmaps WHERE id IN (?)", beatmapIDs)
	if err != nil {
		return nil, err
	}
	b := make([]cheesegull.Beatmap, 0, len(beatmapIDs))
	err = p.db.Select(&b, query, params...)
	return b, err
}

const setsFields = `
	s.set_id, s.ranked_status, s.approved_date, s.last_update, s.last_checked,
	s.artist, s.title, s.creator, s.source, s.tags, s.has_video, s.genre,
	s.language, s.favourites,

	b.id as beatmap_id, b.parent_id, b.diff_name, b.file_md5, b.mode, b.bpm, b.ar, b.od, b.cs, b.hp,
	b.total_length, b.hit_length, b.playcount, b.passcount, b.max_combo,
	b.difficulty_rating
FROM beatmaps b
INNER JOIN sets s ON b.parent_id = s.set_id
`

func (p *provider) BeatmapSets(sets ...int) ([]cheesegull.BeatmapSet, error) {
	q, a, err := sqlx.In("SELECT "+setsFields+" WHERE s.set_id IN (?)", sets)
	if err != nil {
		return nil, err
	}
	rows, err := p.db.Query(q, a...)
	if err != nil {
		return nil, err
	}
	return p.sets(rows)
}

var sortingSystems = [...]string{
	// SortLastChecked
	"SELECT " + setsFields + " ORDER BY s.last_checked DESC LIMIT %d, %d",
}

func (p *provider) ChunkOfSets(offset, chunk, sortSystem int) ([]cheesegull.BeatmapSet, error) {
	q := fmt.Sprintf(sortingSystems[sortSystem], offset, chunk)
	rows, err := p.db.Query(q)
	if err != nil {
		return nil, err
	}
	return p.sets(rows)
}

const searchQuery = "SELECT " + setsFields +
	"WHERE MATCH (s.artist, s.title, s.creator, s.source, s.tags) " +
	"AGAINST (? IN NATURAL LANGUAGE MODE)"

func _and(needAnd bool) string {
	if needAnd {
		return " AND "
	}
	return " WHERE "
}

func (p *provider) SearchSets(opts cheesegull.SearchOptions) ([]cheesegull.BeatmapSet, error) {
	queryBase := "SELECT " + setsFields
	params := make([]interface{}, 0, 1)
	needAnd := false

	if len(opts.Mode) > 0 {
		queryBase += _and(needAnd) + "b.mode IN (?)"
		params = append(params, opts.Mode)
		needAnd = true
	}
	if len(opts.Status) > 0 {
		queryBase += _and(needAnd) + "s.ranked_status IN (?)"
		params = append(params, opts.Status)
		needAnd = true
	}
	if opts.Query != "" {
		queryBase += _and(needAnd) +
			"MATCH (s.artist, s.title, s.creator, s.source, s.tags) AGAINST (? IN NATURAL LANGUAGE MODE)"
		params = append(params, opts.Query)
	}

	queryBase += fmt.Sprintf(" ORDER BY s.last_update DESC LIMIT %d, %d", opts.Offset, opts.Amount)

	queryBase, params, err := sqlx.In(queryBase, params...)

	if err != nil {
		return nil, err
	}

	rows, err := p.db.Query(queryBase, params...)
	if err != nil {
		return nil, err
	}
	return p.sets(rows)
}

func (p *provider) sets(rows *sql.Rows) ([]cheesegull.BeatmapSet, error) {
	var bms []cheesegull.BeatmapSet
RowLoop:
	for rows.Next() {
		var (
			b cheesegull.Beatmap
			s cheesegull.BeatmapSet
		)
		err := rows.Scan(
			&s.SetID, &s.RankedStatus, &s.ApprovedDate, &s.LastUpdate,
			&s.LastChecked, &s.Artist, &s.Title, &s.Creator, &s.Source, &s.Tags,
			&s.HasVideo, &s.Genre, &s.Language, &s.Favourites,

			&b.BeatmapID, &b.ParentSetID, &b.DiffName, &b.FileMD5, &b.Mode, &b.BPM, &b.AR,
			&b.OD, &b.CS, &b.HP, &b.TotalLength, &b.TotalLength, &b.Playcount,
			&b.Passcount, &b.MaxCombo, &b.DifficultyRating,
		)
		if err != nil {
			return nil, err
		}
		b.ParentSetID = s.SetID
		// We are looping because we must base ourselves on the assumption
		// that the DB returns rows randomly sorted.
		for i, s2 := range bms {
			if s2.SetID != s.SetID {
				continue
			}
			bms[i].ChildrenBeatmaps = append(bms[i].ChildrenBeatmaps, b.BeatmapID)
			bms[i].ChildrenBeatmaps2 = append(bms[i].ChildrenBeatmaps2, b)
			continue RowLoop
		}
		s.ChildrenBeatmaps = []int{b.BeatmapID}
		s.ChildrenBeatmaps2 = []cheesegull.Beatmap{b}
		bms = append(bms, s)
	}
	return bms, nil
}

func (p *provider) HighestBeatmapSetID() (i int, err error) {
	err = p.db.Get(&i, "SELECT set_id FROM sets ORDER BY set_id DESC LIMIT 1")
	if err == sql.ErrNoRows {
		err = nil
	}
	return
}

func (p *provider) CreateSet(s cheesegull.BeatmapSet) error {
	_, err := p.db.Exec(`REPLACE INTO sets(
		set_id, ranked_status, approved_date, last_update, last_checked,
		artist, title, creator, source, tags, has_video, genre,
		language, favourites
	) VALUES (
		?, ?, ?, ?, ?,
		?, ?, ?, ?, ?, ?, ?,
		?, ?
	)`,
		s.SetID, s.RankedStatus, s.ApprovedDate, s.LastUpdate, s.LastChecked,
		s.Artist, s.Title, s.Creator, s.Source, s.Tags, s.HasVideo, s.Genre,
		s.Language, s.Favourites,
	)
	if len(s.ChildrenBeatmaps2) > 0 {
		err := p.CreateBeatmaps(s.ChildrenBeatmaps2...)
		if err != nil {
			return err
		}
	}
	return err
}

func (p *provider) CreateBeatmaps(bms ...cheesegull.Beatmap) error {
	if len(bms) == 0 {
		return nil
	}
	q := `REPLACE INTO beatmaps(
		id, parent_id, diff_name, file_md5, mode, bpm, ar, od, cs, hp,
		total_length, hit_length, playcount, passcount, max_combo,
		difficulty_rating
	) VALUES `
	// There are 15 fields, so yeah
	args := make([]interface{}, 0, 15*len(bms))
	for i, bm := range bms {
		q += "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
		if i != len(bms)-1 {
			q += ", "
		}
		args = append(args,
			bm.BeatmapID, bm.ParentSetID, bm.DiffName, bm.FileMD5, bm.Mode,
			bm.BPM, bm.AR, bm.OD, bm.CS, bm.HP, bm.TotalLength, bm.HitLength,
			bm.Playcount, bm.Passcount, bm.MaxCombo, bm.DifficultyRating,
		)
	}
	_, err := p.db.Exec(q, args...)
	return err
}
<commit_msg>remove useless searchQuery from beatmaps<commit_after>package sql

import (
	"database/sql"
	"fmt"

	"github.com/jmoiron/sqlx"
	"github.com/osuripple/cheesegull"
)

func (p *provider) Beatmaps(beatmapIDs ...int) ([]cheesegull.Beatmap, error) {
	query, params, err := sqlx.In("SELECT "+`
		id as beatmap_id, parent_id, diff_name, file_md5, mode, bpm, ar, od, cs, hp,
		total_length, hit_length, playcount, passcount, max_combo,
		difficulty_rating`+" FROM beatmaps WHERE id IN (?)", beatmapIDs)
	if err != nil {
		return nil, err
	}
	b := make([]cheesegull.Beatmap, 0, len(beatmapIDs))
	err = p.db.Select(&b, query, params...)
	return b, err
}

const setsFields = `
	s.set_id, s.ranked_status, s.approved_date, s.last_update, s.last_checked,
	s.artist, s.title, s.creator, s.source, s.tags, s.has_video, s.genre,
	s.language, s.favourites,

	b.id as beatmap_id, b.parent_id, b.diff_name, b.file_md5, b.mode, b.bpm, b.ar, b.od, b.cs, b.hp,
	b.total_length, b.hit_length, b.playcount, b.passcount, b.max_combo,
	b.difficulty_rating
FROM beatmaps b
INNER JOIN sets s ON b.parent_id = s.set_id
`

func (p *provider) BeatmapSets(sets ...int) ([]cheesegull.BeatmapSet, error) {
	q, a, err := sqlx.In("SELECT "+setsFields+" WHERE s.set_id IN (?)", sets)
	if err != nil {
		return nil, err
	}
	rows, err := p.db.Query(q, a...)
	if err != nil {
		return nil, err
	}
	return p.sets(rows)
}

var sortingSystems = [...]string{
	// SortLastChecked
	"SELECT " + setsFields + " ORDER BY s.last_checked DESC LIMIT %d, %d",
}

func (p *provider) ChunkOfSets(offset, chunk, sortSystem int) ([]cheesegull.BeatmapSet, error) {
	q := fmt.Sprintf(sortingSystems[sortSystem], offset, chunk)
	rows, err := p.db.Query(q)
	if err != nil {
		return nil, err
	}
	return p.sets(rows)
}

func _and(needAnd bool) string {
	if needAnd {
		return " AND "
	}
	return " WHERE "
}

func (p *provider) SearchSets(opts cheesegull.SearchOptions) ([]cheesegull.BeatmapSet, error) {
	queryBase := "SELECT " + setsFields
	params := make([]interface{}, 0, 1)
	needAnd := false

	if len(opts.Mode) > 0 {
		queryBase += _and(needAnd) + "b.mode IN (?)"
		params = append(params, opts.Mode)
		needAnd = true
	}
	if len(opts.Status) > 0 {
		queryBase += _and(needAnd) + "s.ranked_status IN (?)"
		params = append(params, opts.Status)
		needAnd = true
	}
	if opts.Query != "" {
		queryBase += _and(needAnd) +
			"MATCH (s.artist, s.title, s.creator, s.source, s.tags) AGAINST (? IN NATURAL LANGUAGE MODE)"
		params = append(params, opts.Query)
	}

	queryBase += fmt.Sprintf(" ORDER BY s.last_update DESC LIMIT %d, %d", opts.Offset, opts.Amount)

	queryBase, params, err := sqlx.In(queryBase, params...)

	if err != nil {
		return nil, err
	}

	rows, err := p.db.Query(queryBase, params...)
	if err != nil {
		return nil, err
	}
	return p.sets(rows)
}

func (p *provider) sets(rows *sql.Rows) ([]cheesegull.BeatmapSet, error) {
	var bms []cheesegull.BeatmapSet
RowLoop:
	for rows.Next() {
		var (
			b cheesegull.Beatmap
			s cheesegull.BeatmapSet
		)
		err := rows.Scan(
			&s.SetID, &s.RankedStatus, &s.ApprovedDate, &s.LastUpdate,
			&s.LastChecked, &s.Artist, &s.Title, &s.Creator, &s.Source, &s.Tags,
			&s.HasVideo, &s.Genre, &s.Language, &s.Favourites,

			&b.BeatmapID, &b.ParentSetID, &b.DiffName, &b.FileMD5, &b.Mode, &b.BPM, &b.AR,
			&b.OD, &b.CS, &b.HP, &b.TotalLength, &b.TotalLength, &b.Playcount,
			&b.Passcount, &b.MaxCombo, &b.DifficultyRating,
		)
		if err != nil {
			return nil, err
		}
		b.ParentSetID = s.SetID
		// We are looping because we must base ourselves on the assumption
		// that the DB returns rows randomly sorted.
		for i, s2 := range bms {
			if s2.SetID != s.SetID {
				continue
			}
			bms[i].ChildrenBeatmaps = append(bms[i].ChildrenBeatmaps, b.BeatmapID)
			bms[i].ChildrenBeatmaps2 = append(bms[i].ChildrenBeatmaps2, b)
			continue RowLoop
		}
		s.ChildrenBeatmaps = []int{b.BeatmapID}
		s.ChildrenBeatmaps2 = []cheesegull.Beatmap{b}
		bms = append(bms, s)
	}
	return bms, nil
}

func (p *provider) HighestBeatmapSetID() (i int, err error) {
	err = p.db.Get(&i, "SELECT set_id FROM sets ORDER BY set_id DESC LIMIT 1")
	if err == sql.ErrNoRows {
		err = nil
	}
	return
}

func (p *provider) CreateSet(s cheesegull.BeatmapSet) error {
	_, err := p.db.Exec(`REPLACE INTO sets(
		set_id, ranked_status, approved_date, last_update, last_checked,
		artist, title, creator, source, tags, has_video, genre,
		language, favourites
	) VALUES (
		?, ?, ?, ?, ?,
		?, ?, ?, ?, ?, ?, ?,
		?, ?
	)`,
		s.SetID, s.RankedStatus, s.ApprovedDate, s.LastUpdate, s.LastChecked,
		s.Artist, s.Title, s.Creator, s.Source, s.Tags, s.HasVideo, s.Genre,
		s.Language, s.Favourites,
	)
	if len(s.ChildrenBeatmaps2) > 0 {
		err := p.CreateBeatmaps(s.ChildrenBeatmaps2...)
		if err != nil {
			return err
		}
	}
	return err
}

func (p *provider) CreateBeatmaps(bms ...cheesegull.Beatmap) error {
	if len(bms) == 0 {
		return nil
	}
	q := `REPLACE INTO beatmaps(
		id, parent_id, diff_name, file_md5, mode, bpm, ar, od, cs, hp,
		total_length, hit_length, playcount, passcount, max_combo,
		difficulty_rating
	) VALUES `
	// There are 15 fields, so yeah
	args := make([]interface{}, 0, 15*len(bms))
	for i, bm := range bms {
		q += "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
		if i != len(bms)-1 {
			q += ", "
		}
		args = append(args,
			bm.BeatmapID, bm.ParentSetID, bm.DiffName, bm.FileMD5, bm.Mode,
			bm.BPM, bm.AR, bm.OD, bm.CS, bm.HP, bm.TotalLength, bm.HitLength,
			bm.Playcount, bm.Passcount, bm.MaxCombo, bm.DifficultyRating,
		)
	}
	_, err := p.db.Exec(q, args...)
	return err
}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
)

func (commands *CommandSet) HelpForDebug() {
	fmt.Println("bigv debug")
	fmt.Println()
	fmt.Println("Usage:")
	fmt.Println("    bigv debug GET <path>")
	fmt.Println()
	fmt.Println("GET sends an HTTP GET request with a valid authorization header to the given path on the BigV endpoint and pretty-prints the received json.")
	fmt.Println()
}

// TODO(telyn): does the URL really have to start with /?

// Debug makes an HTTP <method> request to the URL specified in the arguments.
// command syntax: debug <method> <url>
// URL probably needs to start with a /
func (commands *CommandSet) Debug(args []string) {
	if len(args) < 2 {
		commands.HelpForDebug()
		return
	}
	// TODO(telyn): add a flag to disable auth
	// TODO(telyn): add a flag to junk the token
	shouldAuth := true
	commands.EnsureAuth()
	commands.bigv.SetDebugLevel(1)

	// make sure the command is well-formed

	body, err := commands.bigv.RequestAndRead(shouldAuth, args[0], args[1], "")
	if err != nil {
		panic(err)
	}

	buf := new(bytes.Buffer)
	json.Indent(buf, body, "", "    ")
	fmt.Printf("%s", buf)
}
<commit_msg>Rearrange CommandSet.Debug and add debug config command<commit_after>package cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
)

func (commands *CommandSet) HelpForDebug() {
	fmt.Println("bigv debug")
	fmt.Println()
	fmt.Println("Usage:")
	fmt.Println("    bigv debug GET <path>")
	fmt.Println("    bigv debug DELETE <path>")
	fmt.Println()
	fmt.Println("GET sends an HTTP GET request with a valid authorization header to the given path on the BigV endpoint and pretty-prints the received json.")
	fmt.Println()
}

// TODO(telyn): does the URL really have to start with /?

// Debug makes an HTTP <method> request to the URL specified in the arguments.
// command syntax: debug <method> <url>
// URL probably needs to start with a /
func (commands *CommandSet) Debug(args []string) {
	if len(args) < 1 {
		commands.HelpForDebug()
		return
	}

	switch args[0] {
	case "GET", "PUT", "POST", "DELETE":
		// TODO(telyn): add a flag to disable auth
		// TODO(telyn): add a flag to junk the token
		shouldAuth := true
		commands.EnsureAuth()
		body, err := commands.bigv.RequestAndRead(shouldAuth, args[0], args[1], "")
		if err != nil {
			fmt.Printf("error (type %T): %s", err.Error())
			os.Exit(1)
		}

		buf := new(bytes.Buffer)
		json.Indent(buf, body, "", "    ")
		fmt.Printf("%s", buf)
	case "config":
		indented, err := json.MarshalIndent(commands.config.GetAll(), "", "    ")
		if err != nil {
			fmt.Printf("Your config is so weird it broke json.MarshalIndent")
			panic(err)
		}
		fmt.Printf("%s", indented)
	default:
		commands.HelpForDebug()
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 Anthony Spring <aspring@yieldbot.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package version

import (
	"fmt"
)

//AppVersionMajor is the major revision number
const AppVersionMajor = "0"

// AppVersionMinor is the minor revison number
const AppVersionMinor = "1"

// AppVersionPatch is the patch version
const AppVersionPatch = "20"

// AppVersionPre ...
const AppVersionPre = ""

// AppVersionBuild should be empty string when releasing
const AppVersionBuild = ""

// AppVersion generates a usable version string
func AppVersion() string {
	return fmt.Sprintf("%s.%s.%s%s%s", AppVersionMajor, AppVersionMinor, AppVersionPatch, AppVersionPre, AppVersionBuild)
}

func main() {
	fmt.Printf("%s", AppVersion())
}
<commit_msg>[sensupluginsslack]Version Bump<commit_after>// Copyright © 2017 Anthony Spring <aspring@yieldbot.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package version

import (
	"fmt"
)

//AppVersionMajor is the major revision number
const AppVersionMajor = "0"

// AppVersionMinor is the minor revison number
const AppVersionMinor = "2"

// AppVersionPatch is the patch version
const AppVersionPatch = "21"

// AppVersionPre ...
const AppVersionPre = ""

// AppVersionBuild should be empty string when releasing
const AppVersionBuild = ""

// AppVersion generates a usable version string
func AppVersion() string {
	return fmt.Sprintf("%s.%s.%s%s%s", AppVersionMajor, AppVersionMinor, AppVersionPatch, AppVersionPre, AppVersionBuild)
}

func main() {
	fmt.Printf("%s", AppVersion())
}
<|endoftext|>


<commit_before>package deployments

import (
	"fmt"
	"sort"
	"strings"
	"time"

	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/util/wait"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployutil "github.com/openshift/origin/pkg/deploy/util"
	exutil "github.com/openshift/origin/test/extended/util"
)

func deploymentStatuses(rcs []kapi.ReplicationController) []string {
	statuses := []string{}
	for _, rc := range rcs {
		statuses = append(statuses, string(deployutil.DeploymentStatusFor(&rc)))
	}
	return statuses
}

func deploymentPods(pods []kapi.Pod) (map[string][]*kapi.Pod, error) {
	deployers := make(map[string][]*kapi.Pod)
	for i := range pods {
		name, ok := pods[i].Labels[deployapi.DeployerPodForDeploymentLabel]
		if !ok {
			continue
		}
		deployers[name] = append(deployers[name], &pods[i])
	}
	return deployers, nil
}

var completedStatuses = sets.NewString(string(deployapi.DeploymentStatusComplete), string(deployapi.DeploymentStatusFailed))

func checkDeployerPodInvariants(deploymentName string, pods []*kapi.Pod) (isRunning, isCompleted bool, err error) {
	running := false
	completed := false
	succeeded := false
	hasDeployer := false

	// find deployment state
	for _, pod := range pods {
		switch {
		case strings.HasSuffix(pod.Name, "-deploy"):
			if hasDeployer {
				return false, false, fmt.Errorf("multiple deployer pods for %q", deploymentName)
			}
			hasDeployer = true

			switch pod.Status.Phase {
			case kapi.PodSucceeded:
				succeeded = true
				completed = true
			case kapi.PodFailed:
				completed = true
			default:
				running = true
			}
		case strings.HasSuffix(pod.Name, "-pre"), strings.HasSuffix(pod.Name, "-mid"), strings.HasSuffix(pod.Name, "-post"):
		default:
			return false, false, fmt.Errorf("deployer pod %q not recognized as being a valid deployment pod", pod.Name)
		}
	}

	// check hook pods
	for _, pod := range pods {
		switch {
		case strings.HasSuffix(pod.Name, "-pre"), strings.HasSuffix(pod.Name, "-mid"), strings.HasSuffix(pod.Name, "-post"):
			switch pod.Status.Phase {
			case kapi.PodSucceeded:
			case kapi.PodFailed:
				if succeeded {
					return false, false, fmt.Errorf("deployer hook pod %q failed but the deployment %q pod succeeded", pod.Name, deploymentName)
				}
			default:
				if completed {
					// TODO: we need to tighten guarantees around hook pods: https://github.com/openshift/origin/issues/8500
					//for i := range pods {
					//	e2e.Logf("deployment %q pod[%d]: %#v", deploymentName, i, pods[i])
					//}
					//return false, false, fmt.Errorf("deployer hook pod %q is still running but the deployment %q is complete", pod.Name, deploymentName)
					//e2e.Logf("deployer hook pod %q is still running but the deployment %q is complete", pod.Name, deploymentName)
				}
			}
		}
	}
	return running, completed, nil
}

func checkDeploymentInvariants(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) error {
	deployers, err := deploymentPods(pods)
	if err != nil {
		return err
	}
	if len(deployers) > len(rcs) {
		existing := sets.NewString()
		for k := range deployers {
			existing.Insert(k)
		}
		for _, rc := range rcs {
			if existing.Has(rc.Name) {
				existing.Delete(rc.Name)
			} else {
				e2e.Logf("ANOMALY: No deployer pod found for deployment %q", rc.Name)
			}
		}
		for k := range existing {
			// TODO: we are missing RCs? https://github.com/openshift/origin/pull/8483#issuecomment-209150611
			e2e.Logf("ANOMALY: Deployer pod found for %q but no RC exists", k)
			//return fmt.Errorf("more deployer pods found than deployments: %#v %#v", deployers, rcs)
		}
	}
	running := sets.NewString()
	completed := 0
	for k, v := range deployers {
		isRunning, isCompleted, err := checkDeployerPodInvariants(k, v)
		if err != nil {
			return err
		}
		if isCompleted {
			completed++
		}
		if isRunning {
			running.Insert(k)
		}
	}
	if running.Len() > 1 {
		return fmt.Errorf("found multiple running deployments: %v", running.List())
	}
	sawStatus := sets.NewString()
	statuses := []string{}
	for _, rc := range rcs {
		status := deployutil.DeploymentStatusFor(&rc)
		if sawStatus.Len() != 0 {
			switch status {
			case deployapi.DeploymentStatusComplete, deployapi.DeploymentStatusFailed:
				if sawStatus.Difference(completedStatuses).Len() != 0 {
					return fmt.Errorf("rc %s was %s, but earlier RCs were not completed: %v", rc.Name, status, statuses)
				}
			case deployapi.DeploymentStatusRunning, deployapi.DeploymentStatusPending:
				if sawStatus.Has(string(status)) {
					return fmt.Errorf("rc %s was %s, but so was an earlier RC: %v", rc.Name, status, statuses)
				}
				if sawStatus.Difference(completedStatuses).Len() != 0 {
					return fmt.Errorf("rc %s was %s, but earlier RCs were not completed: %v", rc.Name, status, statuses)
				}
			case deployapi.DeploymentStatusNew:
			default:
				return fmt.Errorf("rc %s has unexpected status %s: %v", rc.Name, status, statuses)
			}
		}
		sawStatus.Insert(string(status))
		statuses = append(statuses, string(status))
	}
	return nil
}

func deploymentReachedCompletion(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) (bool, error) {
	if len(rcs) == 0 {
		return false, nil
	}
	rc := rcs[len(rcs)-1]
	version := deployutil.DeploymentVersionFor(&rc)
	if version != dc.Status.LatestVersion {
		return false, nil
	}

	status := rc.Annotations[deployapi.DeploymentStatusAnnotation]
	if deployapi.DeploymentStatus(status) != deployapi.DeploymentStatusComplete {
		return false, nil
	}
	expectedReplicas := dc.Spec.Replicas
	if dc.Spec.Test {
		expectedReplicas = 0
	}
	if rc.Spec.Replicas != int32(expectedReplicas) {
		return false, fmt.Errorf("deployment is complete but doesn't have expected spec replicas: %d %d", rc.Spec.Replicas, expectedReplicas)
	}
	if rc.Status.Replicas != int32(expectedReplicas) {
		e2e.Logf("POSSIBLE_ANOMALY: deployment is complete but doesn't have expected status replicas: %d %d", rc.Status.Replicas, expectedReplicas)
		return false, nil
	}
	return true, nil
}

func deploymentRunning(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) (bool, error) {
	if len(rcs) == 0 {
		return false, nil
	}
	rc := rcs[len(rcs)-1]
	version := deployutil.DeploymentVersionFor(&rc)
	if version != dc.Status.LatestVersion {
		//e2e.Logf("deployment %s is not the latest version on DC: %d", rc.Name, version)
		return false, nil
	}

	status := rc.Annotations[deployapi.DeploymentStatusAnnotation]
	switch deployapi.DeploymentStatus(status) {
	case deployapi.DeploymentStatusFailed:
		if deployutil.IsDeploymentCancelled(&rc) {
			return true, nil
		}
		reason := deployutil.DeploymentStatusReasonFor(&rc)
		if reason == "deployer pod no longer exists" {
			return true, nil
		}
		return false, fmt.Errorf("deployment failed: %v", deployutil.DeploymentStatusReasonFor(&rc))
	case deployapi.DeploymentStatusRunning, deployapi.DeploymentStatusComplete:
		return true, nil
	default:
		return false, nil
	}
}

func deploymentPreHookRetried(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) (bool, error) {
	var preHook *kapi.Pod
	for i := range pods {
		pod := pods[i]
		if !strings.HasSuffix(pod.Name, "-pre") {
			continue
		}
		preHook = &pod
		break
	}

	if preHook == nil || len(preHook.Status.ContainerStatuses) == 0 {
		return false, nil
	}

	return preHook.Status.ContainerStatuses[0].RestartCount > 0, nil
}

func deploymentInfo(oc *exutil.CLI, name string) (*deployapi.DeploymentConfig, []kapi.ReplicationController, []kapi.Pod, error) {
	dc, err := oc.REST().DeploymentConfigs(oc.Namespace()).Get(name)
	if err != nil {
		return nil, nil, nil, err
	}

	// get pods before RCs, so we see more RCs than pods.
	pods, err := oc.KubeREST().Pods(oc.Namespace()).List(kapi.ListOptions{})
	if err != nil {
		return nil, nil, nil, err
	}

	rcs, err := oc.KubeREST().ReplicationControllers(oc.Namespace()).List(kapi.ListOptions{
		LabelSelector: deployutil.ConfigSelector(name),
	})
	if err != nil {
		return nil, nil, nil, err
	}
	sort.Sort(deployutil.ByLatestVersionAsc(rcs.Items))

	return dc, rcs.Items, pods.Items, nil
}

type deploymentConditionFunc func(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) (bool, error)

func waitForLatestCondition(oc *exutil.CLI, name string, timeout time.Duration, fn deploymentConditionFunc) error {
	return wait.Poll(200*time.Millisecond, timeout, func() (bool, error) {
		dc, rcs, pods, err := deploymentInfo(oc, name)
		if err != nil {
			return false, err
		}
		if err := checkDeploymentInvariants(dc, rcs, pods); err != nil {
			return false, err
		}
		return fn(dc, rcs, pods)
	})
}

// createFixture will create the provided fixture and return the resource and the
// name separately.
// TODO: Probably move to a more general location like test/extended/util/cli.go
func createFixture(oc *exutil.CLI, fixture string) (string, string, error) {
	resource, err := oc.Run("create").Args("-f", fixture, "-o", "name").Output()
	if err != nil {
		return "", "", err
	}
	parts := strings.Split(resource, "/")
	if len(parts) != 2 {
		return "", "", fmt.Errorf("expected type/name syntax, got: %s", resource)
	}
	return resource, parts[1], nil
}

func failureTrap(oc *exutil.CLI, name string, failed bool) {
	if !failed {
		return
	}

	dc, rcs, pods, err := deploymentInfo(oc, name)
	if err != nil {
		return
	}

	e2e.Logf("DC: %#v", dc)
	e2e.Logf("  RCs: %#v", rcs)
	p, _ := deploymentPods(pods)
	for k, v := range p {
		for _, pod := range v {
			e2e.Logf("  Deployer: %s %#v", k, pod)
		}
	}
}

func checkDeploymentConfigHasSynced(dc *deployapi.DeploymentConfig, _ []kapi.ReplicationController, _ []kapi.Pod) (bool, error) {
	return deployutil.HasSynced(dc), nil
}
<commit_msg>better debugging info for deployment flakes<commit_after>package deployments

import (
	"fmt"
	"sort"
	"strings"
	"time"

	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/util/wait"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployutil "github.com/openshift/origin/pkg/deploy/util"
	exutil "github.com/openshift/origin/test/extended/util"
)

func deploymentStatuses(rcs []kapi.ReplicationController) []string {
	statuses := []string{}
	for _, rc := range rcs {
		statuses = append(statuses, string(deployutil.DeploymentStatusFor(&rc)))
	}
	return statuses
}

func deploymentPods(pods []kapi.Pod) (map[string][]*kapi.Pod, error) {
	deployers := make(map[string][]*kapi.Pod)
	for i := range pods {
		name, ok := pods[i].Labels[deployapi.DeployerPodForDeploymentLabel]
		if !ok {
			continue
		}
		deployers[name] = append(deployers[name], &pods[i])
	}
	return deployers, nil
}

var completedStatuses = sets.NewString(string(deployapi.DeploymentStatusComplete), string(deployapi.DeploymentStatusFailed))

func checkDeployerPodInvariants(deploymentName string, pods []*kapi.Pod) (isRunning, isCompleted bool, err error) {
	running := false
	completed := false
	succeeded := false
	hasDeployer := false

	// find deployment state
	for _, pod := range pods {
		switch {
		case strings.HasSuffix(pod.Name, "-deploy"):
			if hasDeployer {
				return false, false, fmt.Errorf("multiple deployer pods for %q", deploymentName)
			}
			hasDeployer = true

			switch pod.Status.Phase {
			case kapi.PodSucceeded:
				succeeded = true
				completed = true
			case kapi.PodFailed:
				completed = true
			default:
				running = true
			}
		case strings.HasSuffix(pod.Name, "-pre"), strings.HasSuffix(pod.Name, "-mid"), strings.HasSuffix(pod.Name, "-post"):
		default:
			return false, false, fmt.Errorf("deployer pod %q not recognized as being a valid deployment pod", pod.Name)
		}
	}

	// check hook pods
	for _, pod := range pods {
		switch {
		case strings.HasSuffix(pod.Name, "-pre"), strings.HasSuffix(pod.Name, "-mid"), strings.HasSuffix(pod.Name, "-post"):
			switch pod.Status.Phase {
			case kapi.PodSucceeded:
			case kapi.PodFailed:
				if succeeded {
					return false, false, fmt.Errorf("deployer hook pod %q failed but the deployment %q pod succeeded", pod.Name, deploymentName)
				}
			default:
				if completed {
					// TODO: we need to tighten guarantees around hook pods: https://github.com/openshift/origin/issues/8500
					//for i := range pods {
					//	e2e.Logf("deployment %q pod[%d]: %#v", deploymentName, i, pods[i])
					//}
					//return false, false, fmt.Errorf("deployer hook pod %q is still running but the deployment %q is complete", pod.Name, deploymentName)
					//e2e.Logf("deployer hook pod %q is still running but the deployment %q is complete", pod.Name, deploymentName)
				}
			}
		}
	}
	return running, completed, nil
}

func checkDeploymentInvariants(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) error {
	deployers, err := deploymentPods(pods)
	if err != nil {
		return err
	}
	if len(deployers) > len(rcs) {
		existing := sets.NewString()
		for k := range deployers {
			existing.Insert(k)
		}
		for _, rc := range rcs {
			if existing.Has(rc.Name) {
				existing.Delete(rc.Name)
			} else {
				e2e.Logf("ANOMALY: No deployer pod found for deployment %q", rc.Name)
			}
		}
		for k := range existing {
			// TODO: we are missing RCs? https://github.com/openshift/origin/pull/8483#issuecomment-209150611
			e2e.Logf("ANOMALY: Deployer pod found for %q but no RC exists", k)
			//return fmt.Errorf("more deployer pods found than deployments: %#v %#v", deployers, rcs)
		}
	}
	running := sets.NewString()
	completed := 0
	for k, v := range deployers {
		isRunning, isCompleted, err := checkDeployerPodInvariants(k, v)
		if err != nil {
			return err
		}
		if isCompleted {
			completed++
		}
		if isRunning {
			running.Insert(k)
		}
	}
	if running.Len() > 1 {
		return fmt.Errorf("found multiple running deployments: %v", running.List())
	}
	sawStatus := sets.NewString()
	statuses := []string{}
	for _, rc := range rcs {
		status := deployutil.DeploymentStatusFor(&rc)
		if sawStatus.Len() != 0 {
			switch status {
			case deployapi.DeploymentStatusComplete, deployapi.DeploymentStatusFailed:
				if sawStatus.Difference(completedStatuses).Len() != 0 {
					return fmt.Errorf("rc %s was %s, but earlier RCs were not completed: %v", rc.Name, status, statuses)
				}
			case deployapi.DeploymentStatusRunning, deployapi.DeploymentStatusPending:
				if sawStatus.Has(string(status)) {
					return fmt.Errorf("rc %s was %s, but so was an earlier RC: %v", rc.Name, status, statuses)
				}
				if sawStatus.Difference(completedStatuses).Len() != 0 {
					return fmt.Errorf("rc %s was %s, but earlier RCs were not completed: %v", rc.Name, status, statuses)
				}
			case deployapi.DeploymentStatusNew:
			default:
				return fmt.Errorf("rc %s has unexpected status %s: %v", rc.Name, status, statuses)
			}
		}
		sawStatus.Insert(string(status))
		statuses = append(statuses, string(status))
	}
	return nil
}

func deploymentReachedCompletion(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) (bool, error) {
	if len(rcs) == 0 {
		return false, nil
	}
	rc := rcs[len(rcs)-1]
	version := deployutil.DeploymentVersionFor(&rc)
	if version != dc.Status.LatestVersion {
		return false, nil
	}

	status := rc.Annotations[deployapi.DeploymentStatusAnnotation]
	if deployapi.DeploymentStatus(status) != deployapi.DeploymentStatusComplete {
		return false, nil
	}
	expectedReplicas := dc.Spec.Replicas
	if dc.Spec.Test {
		expectedReplicas = 0
	}
	if rc.Spec.Replicas != int32(expectedReplicas) {
		return false, fmt.Errorf("deployment is complete but doesn't have expected spec replicas: %d %d", rc.Spec.Replicas, expectedReplicas)
	}
	if rc.Status.Replicas != int32(expectedReplicas) {
		e2e.Logf("POSSIBLE_ANOMALY: deployment is complete but doesn't have expected status replicas: %d %d", rc.Status.Replicas, expectedReplicas)
		return false, nil
	}
	return true, nil
}

func deploymentRunning(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) (bool, error) {
	if len(rcs) == 0 {
		return false, nil
	}
	rc := rcs[len(rcs)-1]
	version := deployutil.DeploymentVersionFor(&rc)
	if version != dc.Status.LatestVersion {
		//e2e.Logf("deployment %s is not the latest version on DC: %d", rc.Name, version)
		return false, nil
	}

	status := rc.Annotations[deployapi.DeploymentStatusAnnotation]
	switch deployapi.DeploymentStatus(status) {
	case deployapi.DeploymentStatusFailed:
		if deployutil.IsDeploymentCancelled(&rc) {
			return true, nil
		}
		reason := deployutil.DeploymentStatusReasonFor(&rc)
		if reason == "deployer pod no longer exists" {
			return true, nil
		}
		return false, fmt.Errorf("deployment failed: %v", deployutil.DeploymentStatusReasonFor(&rc))
	case deployapi.DeploymentStatusRunning, deployapi.DeploymentStatusComplete:
		return true, nil
	default:
		return false, nil
	}
}

func deploymentPreHookRetried(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) (bool, error) {
	var preHook *kapi.Pod
	for i := range pods {
		pod := pods[i]
		if !strings.HasSuffix(pod.Name, "-pre") {
			continue
		}
		preHook = &pod
		break
	}

	if preHook == nil || len(preHook.Status.ContainerStatuses) == 0 {
		return false, nil
	}

	return preHook.Status.ContainerStatuses[0].RestartCount > 0, nil
}

func deploymentInfo(oc *exutil.CLI, name string) (*deployapi.DeploymentConfig, []kapi.ReplicationController, []kapi.Pod, error) {
	dc, err := oc.REST().DeploymentConfigs(oc.Namespace()).Get(name)
	if err != nil {
		return nil, nil, nil, err
	}

	// get pods before RCs, so we see more RCs than pods.
	pods, err := oc.KubeREST().Pods(oc.Namespace()).List(kapi.ListOptions{})
	if err != nil {
		return nil, nil, nil, err
	}

	rcs, err := oc.KubeREST().ReplicationControllers(oc.Namespace()).List(kapi.ListOptions{
		LabelSelector: deployutil.ConfigSelector(name),
	})
	if err != nil {
		return nil, nil, nil, err
	}
	sort.Sort(deployutil.ByLatestVersionAsc(rcs.Items))

	return dc, rcs.Items, pods.Items, nil
}

type deploymentConditionFunc func(dc *deployapi.DeploymentConfig, rcs []kapi.ReplicationController, pods []kapi.Pod) (bool, error)

func waitForLatestCondition(oc *exutil.CLI, name string, timeout time.Duration, fn deploymentConditionFunc) error {
	return wait.Poll(200*time.Millisecond, timeout, func() (bool, error) {
		dc, rcs, pods, err := deploymentInfo(oc, name)
		if err != nil {
			return false, err
		}
		if err := checkDeploymentInvariants(dc, rcs, pods); err != nil {
			return false, err
		}
		return fn(dc, rcs, pods)
	})
}

// createFixture will create the provided fixture and return the resource and the
// name separately.
// TODO: Probably move to a more general location like test/extended/util/cli.go
func createFixture(oc *exutil.CLI, fixture string) (string, string, error) {
	resource, err := oc.Run("create").Args("-f", fixture, "-o", "name").Output()
	if err != nil {
		return "", "", err
	}
	parts := strings.Split(resource, "/")
	if len(parts) != 2 {
		return "", "", fmt.Errorf("expected type/name syntax, got: %s", resource)
	}
	return resource, parts[1], nil
}

func failureTrap(oc *exutil.CLI, name string, failed bool) {
	if !failed {
		return
	}
	out, err := oc.Run("get").Args("dc/"+name, "-o", "yaml").Output()
	if err != nil {
		e2e.Logf("Error getting Deployment Config %s: %v", name, err)
		return
	}
	e2e.Logf("\n%s\n", out)
	_, rcs, pods, err := deploymentInfo(oc, name)
	if err != nil {
		e2e.Logf("Error getting deployment %s info: %v", name, err)
		return
	}
	for _, r := range rcs {
		out, err := oc.Run("get").Args("rc/"+r.Name, "-o", "yaml").Output()
		if err != nil {
			e2e.Logf("Error getting replication controller %s info: %v", r.Name, err)
			return
		}
		e2e.Logf("\n%s\n", out)
	}
	p, _ := deploymentPods(pods)
	for _, v := range p {
		for _, pod := range v {
			out, err := oc.Run("get").Args("pod/"+pod.Name, "-o", "yaml").Output()
			if err != nil {
				e2e.Logf("Error getting pod %s: %v", pod.Name, err)
				return
			}
			e2e.Logf("\n%s\n", out)
			out, _ = oc.Run("logs").Args("pod/" + pod.Name).Output()
			e2e.Logf("--- pod %s logs\n%s---\n", pod.Name, out)
		}
	}
}

func checkDeploymentConfigHasSynced(dc *deployapi.DeploymentConfig, _ []kapi.ReplicationController, _ []kapi.Pod) (bool, error) {
	return deployutil.HasSynced(dc), nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/ghodss/yaml"
	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/coreos/dex/api"
	"github.com/coreos/dex/server"
	"github.com/coreos/dex/storage"
)

func commandServe() *cobra.Command {
	return &cobra.Command{
		Use:     "serve [ config file ]",
		Short:   "Connect to the storage and begin serving requests.",
		Long:    ``,
		Example: "dex serve config.yaml",
		RunE:    serve,
	}
}

func serve(cmd *cobra.Command, args []string) error {
	switch len(args) {
	default:
		return errors.New("surplus arguments")
	case 0:
		// TODO(ericchiang): Consider having a default config file location.
		return errors.New("no config file specified")
	case 1:
	}

	configFile := args[0]
	configData, err := ioutil.ReadFile(configFile)
	if err != nil {
		return fmt.Errorf("read config file %s: %v", configFile, err)
	}

	var c Config
	if err := yaml.Unmarshal(configData, &c); err != nil {
		return fmt.Errorf("parse config file %s: %v", configFile, err)
	}

	// Fast checks. Perform these first for a more responsive CLI.
	checks := []struct {
		bad    bool
		errMsg string
	}{
		{c.Issuer == "", "no issuer specified in config file"},
		{len(c.Connectors) == 0 && !c.EnablePasswordDB, "no connectors supplied in config file"},
		{!c.EnablePasswordDB && len(c.StaticPasswords) != 0, "cannot specify static passwords without enabling password db"},
		{c.Storage.Config == nil, "no storage suppied in config file"},
		{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS  address to listen on"},
		{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
		{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
		{c.GRPC.TLSCert != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
		{c.GRPC.TLSKey != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
		{(c.GRPC.TLSCert == "") != (c.GRPC.TLSKey == ""), "must specific both a gRPC TLS cert and key"},
		{c.GRPC.TLSCert == "" && c.GRPC.TLSClientCA != "", "cannot specify gRPC TLS client CA without a gRPC TLS cert"},
	}

	for _, check := range checks {
		if check.bad {
			return errors.New(check.errMsg)
		}
	}

	var grpcOptions []grpc.ServerOption
	if c.GRPC.TLSCert != "" {
		if c.GRPC.TLSClientCA != "" {
			// Parse certificates from certificate file and key file for server.
			cert, err := tls.LoadX509KeyPair(c.GRPC.TLSCert, c.GRPC.TLSKey)
			if err != nil {
				return fmt.Errorf("parsing certificate file: %v", err)
			}

			// Parse certificates from client CA file to a new CertPool.
			cPool := x509.NewCertPool()
			clientCert, err := ioutil.ReadFile(c.GRPC.TLSClientCA)
			if err != nil {
				return fmt.Errorf("reading from client CA file: %v", err)
			}
			if cPool.AppendCertsFromPEM(clientCert) != true {
				return errors.New("failed to parse client CA")
			}

			tlsConfig := tls.Config{
				Certificates: []tls.Certificate{cert},
				ClientAuth:   tls.RequireAndVerifyClientCert,
				ClientCAs:    cPool,
			}
			grpcOptions = append(grpcOptions, grpc.Creds(credentials.NewTLS(&tlsConfig)))
		} else {
			opt, err := credentials.NewServerTLSFromFile(c.GRPC.TLSCert, c.GRPC.TLSKey)
			if err != nil {
				return fmt.Errorf("load grpc certs: %v", err)
			}
			grpcOptions = append(grpcOptions, grpc.Creds(opt))
		}
	}

	logger, _ := newLogger(c.Logger.Level, c.Logger.Format)

	connectors := make([]server.Connector, len(c.Connectors))
	for i, conn := range c.Connectors {
		if conn.ID == "" {
			return fmt.Errorf("no ID field for connector %d", i)
		}
		if conn.Config == nil {
			return fmt.Errorf("no config field for connector %q", conn.ID)
		}
		connectorLogger := logger.WithField("connector", conn.Name)
		c, err := conn.Config.Open(connectorLogger)
		if err != nil {
			return fmt.Errorf("open %s: %v", conn.ID, err)
		}
		connectors[i] = server.Connector{
			ID:          conn.ID,
			DisplayName: conn.Name,
			Connector:   c,
		}
	}

	s, err := c.Storage.Config.Open(logger)
	if err != nil {
		return fmt.Errorf("initializing storage: %v", err)
	}
	if len(c.StaticClients) > 0 {
		s = storage.WithStaticClients(s, c.StaticClients)
	}
	if len(c.StaticPasswords) > 0 {
		passwords := make([]storage.Password, len(c.StaticPasswords))
		for i, p := range c.StaticPasswords {
			passwords[i] = storage.Password(p)
		}
		s = storage.WithStaticPasswords(s, passwords)
	}

	serverConfig := server.Config{
		SupportedResponseTypes: c.OAuth2.ResponseTypes,
		SkipApprovalScreen:     c.OAuth2.SkipApprovalScreen,
		Issuer:                 c.Issuer,
		Connectors:             connectors,
		Storage:                s,
		Web:                    c.Frontend,
		EnablePasswordDB:       c.EnablePasswordDB,
		Logger:                 logger,
	}
	if c.Expiry.SigningKeys != "" {
		signingKeys, err := time.ParseDuration(c.Expiry.SigningKeys)
		if err != nil {
			return fmt.Errorf("parsing signingKeys expiry: %v", err)
		}
		serverConfig.RotateKeysAfter = signingKeys
	}
	if c.Expiry.IDTokens != "" {
		idTokens, err := time.ParseDuration(c.Expiry.IDTokens)
		if err != nil {
			return fmt.Errorf("parsing idTokens expiry: %v", err)
		}
		serverConfig.IDTokensValidFor = idTokens
	}

	serv, err := server.NewServer(context.Background(), serverConfig)
	if err != nil {
		return fmt.Errorf("initializing server: %v", err)
	}
	errc := make(chan error, 3)
	if c.Web.HTTP != "" {
		logger.Infof("listening (http) on %s", c.Web.HTTP)
		go func() {
			errc <- http.ListenAndServe(c.Web.HTTP, serv)
		}()
	}
	if c.Web.HTTPS != "" {
		logger.Infof("listening (https) on %s", c.Web.HTTPS)
		go func() {
			errc <- http.ListenAndServeTLS(c.Web.HTTPS, c.Web.TLSCert, c.Web.TLSKey, serv)
		}()
	}
	if c.GRPC.Addr != "" {
		logger.Infof("listening (grpc) on %s", c.GRPC.Addr)
		go func() {
			errc <- func() error {
				list, err := net.Listen("tcp", c.GRPC.Addr)
				if err != nil {
					return fmt.Errorf("listen grpc: %v", err)
				}
				s := grpc.NewServer(grpcOptions...)
				api.RegisterDexServer(s, server.NewAPI(serverConfig.Storage, logger))
				return s.Serve(list)
			}()
		}()
	}

	return <-errc
}

func newLogger(level string, format string) (logrus.FieldLogger, error) {
	var logLevel logrus.Level
	switch strings.ToLower(level) {
	case "debug":
		logLevel = logrus.DebugLevel
	case "", "info":
		logLevel = logrus.InfoLevel
	case "error":
		logLevel = logrus.ErrorLevel
	default:
		return nil, fmt.Errorf("unsupported logLevel: %s", level)
	}

	var formatter logrus.Formatter
	switch strings.ToLower(format) {
	case "", "text":
		formatter = &logrus.TextFormatter{DisableColors: true}
	case "json":
		formatter = &logrus.JSONFormatter{}
	default:
		return nil, fmt.Errorf("unsupported logger format: %s", format)
	}

	return &logrus.Logger{
		Out:       os.Stderr,
		Formatter: formatter,
		Level:     logLevel,
	}, nil
}
<commit_msg>cmd/dex: provide better feedback when parsing config<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/ghodss/yaml"
	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/coreos/dex/api"
	"github.com/coreos/dex/server"
	"github.com/coreos/dex/storage"
)

func commandServe() *cobra.Command {
	return &cobra.Command{
		Use:     "serve [ config file ]",
		Short:   "Connect to the storage and begin serving requests.",
		Long:    ``,
		Example: "dex serve config.yaml",
		Run: func(cmd *cobra.Command, args []string) {
			if err := serve(cmd, args); err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(2)
			}
		},
	}
}

func serve(cmd *cobra.Command, args []string) error {
	switch len(args) {
	default:
		return errors.New("surplus arguments")
	case 0:
		// TODO(ericchiang): Consider having a default config file location.
		return errors.New("no arguments provided")
	case 1:
	}

	configFile := args[0]
	configData, err := ioutil.ReadFile(configFile)
	if err != nil {
		return fmt.Errorf("failed to read config file %s: %v", configFile, err)
	}

	var c Config
	if err := yaml.Unmarshal(configData, &c); err != nil {
		return fmt.Errorf("error parse config file %s: %v", configFile, err)
	}

	logger, err := newLogger(c.Logger.Level, c.Logger.Format)
	if err != nil {
		return fmt.Errorf("invalid config: %v", err)
	}
	if c.Logger.Level != "" {
		logger.Infof("config using log level: %s", c.Logger.Level)
	}

	// Fast checks. Perform these first for a more responsive CLI.
	checks := []struct {
		bad    bool
		errMsg string
	}{
		{c.Issuer == "", "no issuer specified in config file"},
		{len(c.Connectors) == 0 && !c.EnablePasswordDB, "no connectors supplied in config file"},
		{!c.EnablePasswordDB && len(c.StaticPasswords) != 0, "cannot specify static passwords without enabling password db"},
		{c.Storage.Config == nil, "no storage suppied in config file"},
		{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS  address to listen on"},
		{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
		{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
		{c.GRPC.TLSCert != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
		{c.GRPC.TLSKey != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
		{(c.GRPC.TLSCert == "") != (c.GRPC.TLSKey == ""), "must specific both a gRPC TLS cert and key"},
		{c.GRPC.TLSCert == "" && c.GRPC.TLSClientCA != "", "cannot specify gRPC TLS client CA without a gRPC TLS cert"},
	}

	for _, check := range checks {
		if check.bad {
			return fmt.Errorf("invalid config: %s", check.errMsg)
		}
	}

	logger.Infof("config issuer: %s", c.Issuer)

	var grpcOptions []grpc.ServerOption
	if c.GRPC.TLSCert != "" {
		if c.GRPC.TLSClientCA != "" {
			// Parse certificates from certificate file and key file for server.
			cert, err := tls.LoadX509KeyPair(c.GRPC.TLSCert, c.GRPC.TLSKey)
			if err != nil {
				return fmt.Errorf("invalid config: error parsing gRPC certificate file: %v", err)
			}

			// Parse certificates from client CA file to a new CertPool.
			cPool := x509.NewCertPool()
			clientCert, err := ioutil.ReadFile(c.GRPC.TLSClientCA)
			if err != nil {
				return fmt.Errorf("invalid config: reading from client CA file: %v", err)
			}
			if cPool.AppendCertsFromPEM(clientCert) != true {
				return errors.New("invalid config: failed to parse client CA")
			}

			tlsConfig := tls.Config{
				Certificates: []tls.Certificate{cert},
				ClientAuth:   tls.RequireAndVerifyClientCert,
				ClientCAs:    cPool,
			}
			grpcOptions = append(grpcOptions, grpc.Creds(credentials.NewTLS(&tlsConfig)))
		} else {
			opt, err := credentials.NewServerTLSFromFile(c.GRPC.TLSCert, c.GRPC.TLSKey)
			if err != nil {
				return fmt.Errorf("invalid config: load grpc certs: %v", err)
			}
			grpcOptions = append(grpcOptions, grpc.Creds(opt))
		}
	}

	connectors := make([]server.Connector, len(c.Connectors))
	for i, conn := range c.Connectors {
		if conn.ID == "" {
			return fmt.Errorf("invalid config: no ID field for connector %d", i)
		}
		if conn.Config == nil {
			return fmt.Errorf("invalid config: no config field for connector %q", conn.ID)
		}
		logger.Infof("config connector: %s", conn.ID)

		connectorLogger := logger.WithField("connector", conn.Name)
		c, err := conn.Config.Open(connectorLogger)
		if err != nil {
			return fmt.Errorf("failed to create connector %s: %v", conn.ID, err)
		}
		connectors[i] = server.Connector{
			ID:          conn.ID,
			DisplayName: conn.Name,
			Connector:   c,
		}
	}
	if c.EnablePasswordDB {
		logger.Infof("config connector: local passwords enabled")
	}

	s, err := c.Storage.Config.Open(logger)
	if err != nil {
		return fmt.Errorf("failed to initialize storage: %v", err)
	}
	logger.Infof("config storage: %s", c.Storage.Type)

	if len(c.StaticClients) > 0 {
		for _, client := range c.StaticClients {
			logger.Infof("config static client: %s", client.ID)
		}
		s = storage.WithStaticClients(s, c.StaticClients)
	}
	if len(c.StaticPasswords) > 0 {
		passwords := make([]storage.Password, len(c.StaticPasswords))
		for i, p := range c.StaticPasswords {
			passwords[i] = storage.Password(p)
		}
		s = storage.WithStaticPasswords(s, passwords)
	}

	if len(c.OAuth2.ResponseTypes) > 0 {
		logger.Infof("config response types accepted: %s", c.OAuth2.ResponseTypes)
	}
	if c.OAuth2.SkipApprovalScreen {
		logger.Infof("config skipping approval screen")
	}

	serverConfig := server.Config{
		SupportedResponseTypes: c.OAuth2.ResponseTypes,
		SkipApprovalScreen:     c.OAuth2.SkipApprovalScreen,
		Issuer:                 c.Issuer,
		Connectors:             connectors,
		Storage:                s,
		Web:                    c.Frontend,
		EnablePasswordDB:       c.EnablePasswordDB,
		Logger:                 logger,
	}
	if c.Expiry.SigningKeys != "" {
		signingKeys, err := time.ParseDuration(c.Expiry.SigningKeys)
		if err != nil {
			return fmt.Errorf("invalid config value %q for signing keys expiry: %v", c.Expiry.SigningKeys, err)
		}
		logger.Infof("config signing keys expire after: %v", signingKeys)
		serverConfig.RotateKeysAfter = signingKeys
	}
	if c.Expiry.IDTokens != "" {
		idTokens, err := time.ParseDuration(c.Expiry.IDTokens)
		if err != nil {
			return fmt.Errorf("invalid config value %q for id token expiry: %v", c.Expiry.IDTokens, err)
		}
		logger.Infof("config id tokens valid for: %v", idTokens)
		serverConfig.IDTokensValidFor = idTokens
	}

	serv, err := server.NewServer(context.Background(), serverConfig)
	if err != nil {
		return fmt.Errorf("failed to initialize server: %v", err)
	}

	errc := make(chan error, 3)
	if c.Web.HTTP != "" {
		logger.Infof("listening (http) on %s", c.Web.HTTP)
		go func() {
			err := http.ListenAndServe(c.Web.HTTP, serv)
			errc <- fmt.Errorf("listening on %s failed: %v", c.Web.HTTP, err)
		}()
	}
	if c.Web.HTTPS != "" {
		logger.Infof("listening (https) on %s", c.Web.HTTPS)
		go func() {
			err := http.ListenAndServeTLS(c.Web.HTTPS, c.Web.TLSCert, c.Web.TLSKey, serv)
			errc <- fmt.Errorf("listening on %s failed: %v", c.Web.HTTPS, err)
		}()
	}
	if c.GRPC.Addr != "" {
		logger.Infof("listening (grpc) on %s", c.GRPC.Addr)
		go func() {
			errc <- func() error {
				list, err := net.Listen("tcp", c.GRPC.Addr)
				if err != nil {
					return fmt.Errorf("listening on %s failed: %v", c.GRPC.Addr, err)
				}
				s := grpc.NewServer(grpcOptions...)
				api.RegisterDexServer(s, server.NewAPI(serverConfig.Storage, logger))
				err = s.Serve(list)
				return fmt.Errorf("listening on %s failed: %v", c.GRPC.Addr, err)
			}()
		}()
	}

	return <-errc
}

var (
	logLevels  = []string{"debug", "info", "error"}
	logFormats = []string{"json", "text"}
)

func newLogger(level string, format string) (logrus.FieldLogger, error) {
	var logLevel logrus.Level
	switch strings.ToLower(level) {
	case "debug":
		logLevel = logrus.DebugLevel
	case "", "info":
		logLevel = logrus.InfoLevel
	case "error":
		logLevel = logrus.ErrorLevel
	default:
		return nil, fmt.Errorf("log level is not one of the supported values (%s): %s", strings.Join(logLevels, ", "), level)
	}

	var formatter logrus.Formatter
	switch strings.ToLower(format) {
	case "", "text":
		formatter = &logrus.TextFormatter{DisableColors: true}
	case "json":
		formatter = &logrus.JSONFormatter{}
	default:
		return nil, fmt.Errorf("log format is not one of the supported values (%s): %s", strings.Join(logFormats, ", "), format)
	}

	return &logrus.Logger{
		Out:       os.Stderr,
		Formatter: formatter,
		Level:     logLevel,
	}, nil
}
<|endoftext|>


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

// The version package implements version parsing.
// It also acts as guardian of the current client Juju version number.
package version

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"labix.org/v2/mgo/bson"
)

// The presence and format of this constant is very important.
// The debian/rules build recipe uses this value for the version
// number of the release package.
const version = "1.11.5"

// CurrentNumber returns the version number.
func CurrentNumber() Number {
	return MustParse(version)
}

// CurrentSeries returns the current Ubuntu release name.
func CurrentSeries() string {
	return readSeries("/etc/lsb-release")
}

// CurrentArch returns the architecture of the machine.
func CurrentArch() string {
	return ubuntuArch(runtime.GOARCH)
}

// Current gives the current version of the system.  If the file
// "FORCE-VERSION" is present in the same directory as the running
// binary, it will override this.
var Current = Binary{
	Number: CurrentNumber(),
	Series: CurrentSeries(),
	Arch:   CurrentArch(),
}

func init() {
	toolsDir := filepath.Dir(os.Args[0])
	v, err := ioutil.ReadFile(filepath.Join(toolsDir, "FORCE-VERSION"))
	if err != nil {
		if os.IsNotExist(err) {
			return
		}
		panic(fmt.Errorf("version: cannot read forced version: %v", err))
	}
	Current.Number = MustParse(strings.TrimSpace(string(v)))
}

// Number represents a juju version.  When bugs are fixed the patch
// number is incremented; when new features are added the minor number
// is incremented and patch is reset; and when compatibility is broken
// the major version is incremented and minor and patch are reset.  The
// build number is automatically assigned and has no well defined
// sequence.  If the build number is greater than zero or the minor
// version is odd, it indicates that the release is still in
// development.
type Number struct {
	Major int
	Minor int
	Patch int
	Build int
}

// Zero is occasionally convenient and readable.
// Please don't change its value.
var Zero = Number{}

// Binary specifies a binary version of juju.
type Binary struct {
	Number
	Series string
	Arch   string
}

func (v Binary) String() string {
	return fmt.Sprintf("%v-%s-%s", v.Number, v.Series, v.Arch)
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Binary) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Binary) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := ParseBinary(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func (v Binary) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.String())
}

func (vp *Binary) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	v, err := ParseBinary(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

var (
	binaryPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$`)
	numberPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?$`)
)

// MustParse parses a version and panics if it does
// not parse correctly.
func MustParse(s string) Number {
	v, err := Parse(s)
	if err != nil {
		panic(err)
	}
	return v
}

// MustParseBinary parses a binary version and panics if it does
// not parse correctly.
func MustParseBinary(s string) Binary {
	v, err := ParseBinary(s)
	if err != nil {
		panic(err)
	}
	return v
}

// ParseBinary parses a binary version of the form "1.2.3-series-arch".
func ParseBinary(s string) (Binary, error) {
	m := binaryPat.FindStringSubmatch(s)
	if m == nil {
		return Binary{}, fmt.Errorf("invalid binary version %q", s)
	}
	var v Binary
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	v.Series = m[5]
	v.Arch = m[6]
	return v, nil
}

// Parse parses the version, which is of the form 1.2.3
// giving the major, minor and release versions
// respectively.
func Parse(s string) (Number, error) {
	m := numberPat.FindStringSubmatch(s)
	if m == nil {
		return Number{}, fmt.Errorf("invalid version %q", s)
	}
	var v Number
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	return v, nil
}

// atoi is the same as strconv.Atoi but assumes that
// the string has been verified to be a valid integer.
func atoi(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return n
}

func (v Number) String() string {
	s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
	if v.Build > 0 {
		s += fmt.Sprintf(".%d", v.Build)
	}
	return s
}

// Less returns whether v is semantically earlier in the
// version sequence than w.
func (v Number) Less(w Number) bool {
	switch {
	case v.Major != w.Major:
		return v.Major < w.Major
	case v.Minor != w.Minor:
		return v.Minor < w.Minor
	case v.Patch != w.Patch:
		return v.Patch < w.Patch
	case v.Build != w.Build:
		return v.Build < w.Build
	}
	return false
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Number) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Number) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := Parse(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func (v Number) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.String())
}

func (vp *Number) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	v, err := Parse(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func isOdd(x int) bool {
	return x%2 != 0
}

// IsDev returns whether the version represents a development
// version. A version with an odd-numbered minor component or
// a nonzero build component is considered to be a development
// version.
func (v Number) IsDev() bool {
	return isOdd(v.Minor) || v.Build > 0
}

func readSeries(releaseFile string) string {
	data, err := ioutil.ReadFile(releaseFile)
	if err != nil {
		return "unknown"
	}
	for _, line := range strings.Split(string(data), "\n") {
		const p = "DISTRIB_CODENAME="
		if strings.HasPrefix(line, p) {
			return strings.Trim(line[len(p):], "\t '\"")
		}
	}
	return "unknown"
}

func ubuntuArch(arch string) string {
	if arch == "386" {
		arch = "i386"
	}
	return arch
}
<commit_msg>Set development version to 1.13.0<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// The version package implements version parsing.
// It also acts as guardian of the current client Juju version number.
package version

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"labix.org/v2/mgo/bson"
)

// The presence and format of this constant is very important.
// The debian/rules build recipe uses this value for the version
// number of the release package.
const version = "1.13.0"

// CurrentNumber returns the version number.
func CurrentNumber() Number {
	return MustParse(version)
}

// CurrentSeries returns the current Ubuntu release name.
func CurrentSeries() string {
	return readSeries("/etc/lsb-release")
}

// CurrentArch returns the architecture of the machine.
func CurrentArch() string {
	return ubuntuArch(runtime.GOARCH)
}

// Current gives the current version of the system.  If the file
// "FORCE-VERSION" is present in the same directory as the running
// binary, it will override this.
var Current = Binary{
	Number: CurrentNumber(),
	Series: CurrentSeries(),
	Arch:   CurrentArch(),
}

func init() {
	toolsDir := filepath.Dir(os.Args[0])
	v, err := ioutil.ReadFile(filepath.Join(toolsDir, "FORCE-VERSION"))
	if err != nil {
		if os.IsNotExist(err) {
			return
		}
		panic(fmt.Errorf("version: cannot read forced version: %v", err))
	}
	Current.Number = MustParse(strings.TrimSpace(string(v)))
}

// Number represents a juju version.  When bugs are fixed the patch
// number is incremented; when new features are added the minor number
// is incremented and patch is reset; and when compatibility is broken
// the major version is incremented and minor and patch are reset.  The
// build number is automatically assigned and has no well defined
// sequence.  If the build number is greater than zero or the minor
// version is odd, it indicates that the release is still in
// development.
type Number struct {
	Major int
	Minor int
	Patch int
	Build int
}

// Zero is occasionally convenient and readable.
// Please don't change its value.
var Zero = Number{}

// Binary specifies a binary version of juju.
type Binary struct {
	Number
	Series string
	Arch   string
}

func (v Binary) String() string {
	return fmt.Sprintf("%v-%s-%s", v.Number, v.Series, v.Arch)
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Binary) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Binary) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := ParseBinary(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func (v Binary) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.String())
}

func (vp *Binary) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	v, err := ParseBinary(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

var (
	binaryPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$`)
	numberPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?$`)
)

// MustParse parses a version and panics if it does
// not parse correctly.
func MustParse(s string) Number {
	v, err := Parse(s)
	if err != nil {
		panic(err)
	}
	return v
}

// MustParseBinary parses a binary version and panics if it does
// not parse correctly.
func MustParseBinary(s string) Binary {
	v, err := ParseBinary(s)
	if err != nil {
		panic(err)
	}
	return v
}

// ParseBinary parses a binary version of the form "1.2.3-series-arch".
func ParseBinary(s string) (Binary, error) {
	m := binaryPat.FindStringSubmatch(s)
	if m == nil {
		return Binary{}, fmt.Errorf("invalid binary version %q", s)
	}
	var v Binary
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	v.Series = m[5]
	v.Arch = m[6]
	return v, nil
}

// Parse parses the version, which is of the form 1.2.3
// giving the major, minor and release versions
// respectively.
func Parse(s string) (Number, error) {
	m := numberPat.FindStringSubmatch(s)
	if m == nil {
		return Number{}, fmt.Errorf("invalid version %q", s)
	}
	var v Number
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	return v, nil
}

// atoi is the same as strconv.Atoi but assumes that
// the string has been verified to be a valid integer.
func atoi(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return n
}

func (v Number) String() string {
	s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
	if v.Build > 0 {
		s += fmt.Sprintf(".%d", v.Build)
	}
	return s
}

// Less returns whether v is semantically earlier in the
// version sequence than w.
func (v Number) Less(w Number) bool {
	switch {
	case v.Major != w.Major:
		return v.Major < w.Major
	case v.Minor != w.Minor:
		return v.Minor < w.Minor
	case v.Patch != w.Patch:
		return v.Patch < w.Patch
	case v.Build != w.Build:
		return v.Build < w.Build
	}
	return false
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Number) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Number) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := Parse(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func (v Number) MarshalJSON() ([]byte, error) {
	return json.Marshal(v.String())
}

func (vp *Number) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	v, err := Parse(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func isOdd(x int) bool {
	return x%2 != 0
}

// IsDev returns whether the version represents a development
// version. A version with an odd-numbered minor component or
// a nonzero build component is considered to be a development
// version.
func (v Number) IsDev() bool {
	return isOdd(v.Minor) || v.Build > 0
}

func readSeries(releaseFile string) string {
	data, err := ioutil.ReadFile(releaseFile)
	if err != nil {
		return "unknown"
	}
	for _, line := range strings.Split(string(data), "\n") {
		const p = "DISTRIB_CODENAME="
		if strings.HasPrefix(line, p) {
			return strings.Trim(line[len(p):], "\t '\"")
		}
	}
	return "unknown"
}

func ubuntuArch(arch string) string {
	if arch == "386" {
		arch = "i386"
	}
	return arch
}
<|endoftext|>


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

package engi

import "math"

// A rather basic camera
type Camera struct {
	DeadzoneSize Point
	pos, to      Point
	tracking     *Entity // The entity that is currently being followed
}

func (cam *Camera) FollowEntity(entity *Entity) {
	cam.tracking = entity
	var space *SpaceComponent
	if !cam.tracking.GetComponent(&space) {
		return
	}

	cam.to = space.Position
	cam.centerCam(Width(), Height(), 1, space)
}

func (cam *Camera) Update(dt float32) {
	if cam.tracking != nil {
		var space *SpaceComponent
		if !cam.tracking.GetComponent(&space) {
			return
		}
		cam.centerCam(Width(), Height(), 0.09, space)
	}
}

func (cam *Camera) centerCam(width, height, lerp float32, space *SpaceComponent) {
	cam.to.X += ((space.Position.X + space.Width/2) - (cam.to.X + width/2)) * lerp
	cam.to.Y += ((space.Position.Y + space.Height/2) - (cam.to.Y + height/2)) * lerp

	dWidth := cam.DeadzoneSize.X
	dHeight := cam.DeadzoneSize.Y

	if dWidth == 0 {
		dWidth = 200
	}

	if dHeight == 0 {
		dHeight = 200
	}
	min, max := Point{}, Point{}

	min.X, min.Y = cam.to.X-(dWidth/2), cam.to.Y-(dHeight/2)
	max.X, max.Y = min.X+dWidth, min.Y+dHeight

	if cam.pos.X < min.X || cam.pos.X > min.Y {
		cam.pos.X = Clamp(floorFloat32(min.X), WorldBounds.Min.X, WorldBounds.Max.X-width)
	}

	if cam.pos.Y < max.X || cam.pos.Y > max.Y {
		cam.pos.Y = Clamp(floorFloat32(min.Y), WorldBounds.Min.Y, WorldBounds.Max.Y-height)
	}
}

func floorFloat32(i float32) float32 {
	return float32(math.Floor(float64(i)))
}
<commit_msg>camera: fix camera logic<commit_after>// Copyright 2014 Harrison Shoebridge. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package engi

import "math"

// A rather basic camera
type Camera struct {
	DeadzoneSize Point
	pos, to      Point
	tracking     *Entity // The entity that is currently being followed
}

func (cam *Camera) FollowEntity(entity *Entity) {
	cam.tracking = entity
	var space *SpaceComponent
	if !cam.tracking.GetComponent(&space) {
		return
	}

	cam.to = space.Position
	cam.centerCam(Width(), Height(), 1, space)
}

func (cam *Camera) Update(dt float32) {
	if cam.tracking != nil {
		var space *SpaceComponent
		if !cam.tracking.GetComponent(&space) {
			return
		}
		cam.centerCam(Width(), Height(), 0.09, space)
	}
}

func (cam *Camera) centerCam(width, height, lerp float32, space *SpaceComponent) {
	cam.to.X += ((space.Position.X + space.Width/2) - (cam.to.X + width/2)) * lerp
	cam.to.Y += ((space.Position.Y + space.Height/2) - (cam.to.Y + height/2)) * lerp

	dWidth := cam.DeadzoneSize.X
	dHeight := cam.DeadzoneSize.Y

	if dWidth == 0 {
		dWidth = 200
	}

	if dHeight == 0 {
		dHeight = 200
	}

	min, max := Point{}, Point{}

	min.X, min.Y = cam.to.X-(dWidth/2), cam.to.Y-(dHeight/2)
	max.X, max.Y = min.X+dWidth, min.Y+dHeight

	if cam.pos.X < min.X {
		cam.pos.X = Clamp(floorFloat32(min.X), WorldBounds.Min.X, WorldBounds.Max.X-width)
	} else if cam.pos.X > max.X {
		cam.pos.X = Clamp(floorFloat32(max.X), WorldBounds.Min.X, WorldBounds.Max.X-width)
	}

	if cam.pos.Y < min.X {
		cam.pos.Y = Clamp(floorFloat32(min.Y), WorldBounds.Min.Y, WorldBounds.Max.Y-height)
	} else if cam.pos.Y > max.Y {
		cam.pos.Y = Clamp(floorFloat32(max.Y), WorldBounds.Min.Y, WorldBounds.Max.Y-height)
	}
}

func floorFloat32(i float32) float32 {
	return float32(math.Floor(float64(i)))
}
<|endoftext|>