<commit_before>package graphql

import (
	"fmt"
	"math"
	"strconv"

	"github.com/sprucehealth/graphql/language/ast"
)

func coerceInt(value interface{}) interface{} {
	switch value := value.(type) {
	case bool:
		if value {
			return 1
		}
		return 0
	case int:
		return value
	case int8:
		return int(value)
	case int16:
		return int(value)
	case int32:
		return int(value)
	case int64:
		if value < int64(math.MinInt32) || value > int64(math.MaxInt32) {
			return nil
		}
		return int(value)
	case uint:
		return int(value)
	case uint8:
		return int(value)
	case uint16:
		return int(value)
	case uint32:
		if value > uint32(math.MaxInt32) {
			return nil
		}
		return int(value)
	case uint64:
		if value > uint64(math.MaxInt32) {
			return nil
		}
		return int(value)
	case float32:
		if value < float32(math.MinInt32) || value > float32(math.MaxInt32) {
			return nil
		}
		return int(value)
	case float64:
		if value < float64(math.MinInt64) || value > float64(math.MaxInt64) {
			return nil
		}
		return int(value)
	case string:
		val, err := strconv.ParseFloat(value, 0)
		if err != nil {
			return nil
		}
		return coerceInt(val)
	}

	// If the value cannot be transformed into an int, return nil instead of '0'
	// to denote 'no integer found'
	return nil
}

// Int is the GraphQL Integer type definition.
var Int *Scalar = NewScalar(ScalarConfig{
	Name: "Int",
	Description: "The `Int` scalar type represents non-fractional signed whole numeric " +
		"values. Int can represent values between -(2^53 - 1) and 2^53 - 1 since " +
		"represented in JSON as double-precision floating point numbers specified" +
		"by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).",
	Serialize:  coerceInt,
	ParseValue: coerceInt,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.IntValue:
			if intValue, err := strconv.Atoi(valueAST.Value); err == nil {
				return intValue
			}
		}
		return nil
	},
})

func coerceFloat64(value interface{}) interface{} {
	switch value := value.(type) {
	case bool:
		if value {
			return float64(1)
		}
		return float64(0)
	case int:
		return float64(value)
	case float32:
		return float64(value)
	case float64:
		return value
	case string:
		val, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return nil
		}
		return val
	}
	return float64(0)
}

// Float is the GraphQL float type definition.
var Float *Scalar = NewScalar(ScalarConfig{
	Name: "Float",
	Description: "The `Float` scalar type represents signed double-precision fractional " +
		"values as specified by " +
		"[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ",
	Serialize:  coerceFloat64,
	ParseValue: coerceFloat64,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.FloatValue:
			if floatValue, err := strconv.ParseFloat(valueAST.Value, 64); err == nil {
				return floatValue
			}
		case *ast.IntValue:
			if floatValue, err := strconv.ParseFloat(valueAST.Value, 64); err == nil {
				return floatValue
			}
		}
		return nil
	},
})

func coerceString(value interface{}) interface{} {
	return fmt.Sprintf("%v", value)
}

// String is the GraphQL string type definition
var String *Scalar = NewScalar(ScalarConfig{
	Name: "String",
	Description: "The `String` scalar type represents textual data, represented as UTF-8 " +
		"character sequences. The String type is most often used by GraphQL to " +
		"represent free-form human-readable text.",
	Serialize:  coerceString,
	ParseValue: coerceString,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.StringValue:
			return valueAST.Value
		}
		return nil
	},
})

func coerceBool(value interface{}) interface{} {
	switch value := value.(type) {
	case bool:
		return value
	case string:
		switch value {
		case "", "false":
			return false
		}
		return true
	case float64:
		if value != 0 {
			return true
		}
		return false
	case float32:
		if value != 0 {
			return true
		}
		return false
	case int:
		if value != 0 {
			return true
		}
		return false
	}
	return false
}

// Boolean is the GraphQL boolean type definition
var Boolean *Scalar = NewScalar(ScalarConfig{
	Name:        "Boolean",
	Description: "The `Boolean` scalar type represents `true` or `false`.",
	Serialize:   coerceBool,
	ParseValue:  coerceBool,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.BooleanValue:
			return valueAST.Value
		}
		return nil
	},
})

// ID is the GraphQL id type definition
var ID *Scalar = NewScalar(ScalarConfig{
	Name: "ID",
	Description: "The `ID` scalar type represents a unique identifier, often used to " +
		"refetch an object or as key for a cache. The ID type appears in a JSON " +
		"response as a String; however, it is not intended to be human-readable. " +
		"When expected as an input type, any string (such as `\"4\"`) or integer " +
		"(such as `4`) input value will be accepted as an ID.",
	Serialize:  coerceString,
	ParseValue: coerceString,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.IntValue:
			return valueAST.Value
		case *ast.StringValue:
			return valueAST.Value
		}
		return nil
	},
})
<commit_msg>Update Int type description to match implementation<commit_after>package graphql

import (
	"fmt"
	"math"
	"strconv"

	"github.com/sprucehealth/graphql/language/ast"
)

func coerceInt(value interface{}) interface{} {
	switch value := value.(type) {
	case bool:
		if value {
			return 1
		}
		return 0
	case int:
		return value
	case int8:
		return int(value)
	case int16:
		return int(value)
	case int32:
		return int(value)
	case int64:
		if value < int64(math.MinInt32) || value > int64(math.MaxInt32) {
			return nil
		}
		return int(value)
	case uint:
		return int(value)
	case uint8:
		return int(value)
	case uint16:
		return int(value)
	case uint32:
		if value > uint32(math.MaxInt32) {
			return nil
		}
		return int(value)
	case uint64:
		if value > uint64(math.MaxInt32) {
			return nil
		}
		return int(value)
	case float32:
		if value < float32(math.MinInt32) || value > float32(math.MaxInt32) {
			return nil
		}
		return int(value)
	case float64:
		if value < float64(math.MinInt64) || value > float64(math.MaxInt64) {
			return nil
		}
		return int(value)
	case string:
		val, err := strconv.ParseFloat(value, 0)
		if err != nil {
			return nil
		}
		return coerceInt(val)
	}

	// If the value cannot be transformed into an int, return nil instead of '0'
	// to denote 'no integer found'
	return nil
}

// Int is the GraphQL Integer type definition.
var Int *Scalar = NewScalar(ScalarConfig{
	Name: "Int",
	Description: "The `Int` scalar type represents non-fractional signed whole numeric " +
		"values. Int can represent values between -(2^31) and 2^31 - 1. ",
	Serialize:  coerceInt,
	ParseValue: coerceInt,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.IntValue:
			if intValue, err := strconv.Atoi(valueAST.Value); err == nil {
				return intValue
			}
		}
		return nil
	},
})

func coerceFloat64(value interface{}) interface{} {
	switch value := value.(type) {
	case bool:
		if value {
			return float64(1)
		}
		return float64(0)
	case int:
		return float64(value)
	case float32:
		return float64(value)
	case float64:
		return value
	case string:
		val, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return nil
		}
		return val
	}
	return float64(0)
}

// Float is the GraphQL float type definition.
var Float *Scalar = NewScalar(ScalarConfig{
	Name: "Float",
	Description: "The `Float` scalar type represents signed double-precision fractional " +
		"values as specified by " +
		"[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ",
	Serialize:  coerceFloat64,
	ParseValue: coerceFloat64,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.FloatValue:
			if floatValue, err := strconv.ParseFloat(valueAST.Value, 64); err == nil {
				return floatValue
			}
		case *ast.IntValue:
			if floatValue, err := strconv.ParseFloat(valueAST.Value, 64); err == nil {
				return floatValue
			}
		}
		return nil
	},
})

func coerceString(value interface{}) interface{} {
	return fmt.Sprintf("%v", value)
}

// String is the GraphQL string type definition
var String *Scalar = NewScalar(ScalarConfig{
	Name: "String",
	Description: "The `String` scalar type represents textual data, represented as UTF-8 " +
		"character sequences. The String type is most often used by GraphQL to " +
		"represent free-form human-readable text.",
	Serialize:  coerceString,
	ParseValue: coerceString,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.StringValue:
			return valueAST.Value
		}
		return nil
	},
})

func coerceBool(value interface{}) interface{} {
	switch value := value.(type) {
	case bool:
		return value
	case string:
		switch value {
		case "", "false":
			return false
		}
		return true
	case float64:
		if value != 0 {
			return true
		}
		return false
	case float32:
		if value != 0 {
			return true
		}
		return false
	case int:
		if value != 0 {
			return true
		}
		return false
	}
	return false
}

// Boolean is the GraphQL boolean type definition
var Boolean *Scalar = NewScalar(ScalarConfig{
	Name:        "Boolean",
	Description: "The `Boolean` scalar type represents `true` or `false`.",
	Serialize:   coerceBool,
	ParseValue:  coerceBool,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.BooleanValue:
			return valueAST.Value
		}
		return nil
	},
})

// ID is the GraphQL id type definition
var ID *Scalar = NewScalar(ScalarConfig{
	Name: "ID",
	Description: "The `ID` scalar type represents a unique identifier, often used to " +
		"refetch an object or as key for a cache. The ID type appears in a JSON " +
		"response as a String; however, it is not intended to be human-readable. " +
		"When expected as an input type, any string (such as `\"4\"`) or integer " +
		"(such as `4`) input value will be accepted as an ID.",
	Serialize:  coerceString,
	ParseValue: coerceString,
	ParseLiteral: func(valueAST ast.Value) interface{} {
		switch valueAST := valueAST.(type) {
		case *ast.IntValue:
			return valueAST.Value
		case *ast.StringValue:
			return valueAST.Value
		}
		return nil
	},
})
<|endoftext|>


<commit_before>// The quantile package implements the algorithm in the paper Effective
// Computation of Biased Quantiles over Data Streams with both invariants.
//
// This package is useful for calculating high-biased and targeted quantiles
// for large datasets within low memory and CPU bounds. You trade a small
// amount of accuracy in rank selection for efficiency.
//
// Multiple Stream's can be merged before a Query, allowing clients to be
// distributed across threads. See Stream.Merge and Stream.Samples.
//
// For more detailed information about the algorithm, see:
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
package quantile

import (
	"container/list"
	"math"
	"sort"
)

// Sample holds an observed value and meta information for compression. JSON
// tags have been added for convenience.
type Sample struct {
	Value float64 `json:",string"`
	Width float64 `json:",string"`
	Delta float64 `json:",string"`
}

// Samples represents a slice of samples. It implements sort.Interface.
type Samples []Sample

func (a Samples) Len() int {
	return len(a)
}

func (a Samples) Less(i, j int) bool {
	return a[i].Value < a[j].Value
}

func (a Samples) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

type invariant func(s *stream, r float64) float64

// NewBiased returns an initialized Stream for high-biased quantiles (e.g.
// 50th, 90th, 99th) not known a priori with ﬁner error guarantees for the
// higher ranks of the data distribution.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewBiased() *Stream {
	f := func(s *stream, r float64) float64 {
		return 2 * s.epsilon * r
	}
	return newStream(0.01, f)
}

// NewTargeted returns an initialized Stream concerned with a particular set of
// quantile values that are supplied a priori. Knowing these a priori reduces
// space and computation time.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(quantiles ...float64) *Stream {
	f := func(s *stream, r float64) float64 {
		var m float64 = math.MaxFloat64
		var f float64
		for _, q := range quantiles {
			if q*s.n <= r {
				f = (2 * s.epsilon * r) / q
			} else {
				f = (2 * s.epsilon * (s.n - r)) / (1 - q)
			}
			m = math.Min(m, f)
		}
		return m
	}
	return newStream(0.01, f)
}

// Stream calculates quantiles for a stream of float64s.
type Stream struct {
	*stream
	b Samples
}

func newStream(epsilon float64, ƒ invariant) *Stream {
	x := &stream{epsilon: epsilon, ƒ: ƒ, l: list.New()}
	return &Stream{x, make(Samples, 0, 500)}
}

// Insert inserts v into the stream.
func (s *Stream) Insert(v float64) {
	s.insert(Sample{Value: v, Width: 1})
}

func (s *Stream) insert(sample Sample) {
	s.b = append(s.b, sample)
	if len(s.b) == cap(s.b) {
		s.flush()
		s.compress()
	}
}

// Query returns the calculated qth percentiles value. If s was created with
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
// will return an unspecified result.
func (s *Stream) Query(q float64) float64 {
	if s.flushed() {
		// Fast path when there hasn't been enough data for a flush;
		// this also yeilds better accuracy for small sets of data.
		i := float64(len(s.b)) * q
		return s.b[int(i)].Value
	}
	s.flush()
	return s.stream.query(q)
}

// Merge merges samples into the underlying streams samples. This is handy when
// merging multiple streams from separate threads, database shards, etc.
func (s *Stream) Merge(samples Samples) {
	s.stream.merge(samples)
}

// Reset reinitializes and clears the list reusing the samples buffer memory.
func (s *Stream) Reset() {
	s.stream.reset()
	s.b = s.b[:0]
}

// Samples returns stream samples held by s.
func (s *Stream) Samples() Samples {
	if !s.flushed() {
		return s.b
	}
	return s.stream.samples()
}

func (s *Stream) flush() {
	sort.Sort(s.b)
	s.stream.merge(s.b)
	s.b = s.b[:0]
}

func (s *Stream) flushed() bool {
	return s.stream.l.Len() == 0
}

type stream struct {
	epsilon float64
	n       float64
	l       *list.List
	ƒ       invariant
}

// SetEpsilon sets the error epsilon for the Stream. The default epsilon is
// 0.01 and is usually satisfactory.
// To learn more, see: http://www.cs.rutgers.edu/~muthu/bquant.pdf
func (s *stream) SetEpsilon(epsilon float64) {
	s.epsilon = epsilon
}

func (s *stream) reset() {
	s.l.Init()
	s.n = 0
}

func (s *stream) insert(v float64) {
	fn := s.mergeFunc()
	fn(v, 1)
}

func (s *stream) merge(samples Samples) {
	fn := s.mergeFunc()
	for _, s := range samples {
		fn(s.Value, s.Width)
	}
}

func (s *stream) mergeFunc() func(v, w float64) {
	// NOTE: I used a goto over defer because it bought me a few extra
	// nanoseconds. I know. I know.
	var r float64
	e := s.l.Front()
	return func(v, w float64) {
		for ; e != nil; e = e.Next() {
			c := e.Value.(*Sample)
			if c.Value > v {
				sm := &Sample{v, w, math.Floor(s.ƒ(s, r)) - 1}
				s.l.InsertBefore(sm, e)
				goto inserted
			}
			r += c.Width
		}
		s.l.PushBack(&Sample{v, w, 0})
	inserted:
		s.n += w
	}
}

// Count returns the total number of samples observed in the stream
// since initialization.
func (s *stream) Count() int {
	return int(s.n)
}

func (s *stream) query(q float64) float64 {
	e := s.l.Front()
	t := math.Ceil(q * s.n)
	t += math.Ceil(s.ƒ(s, t) / 2)
	p := e.Value.(*Sample)
	e = e.Next()
	r := float64(0)
	for e != nil {
		c := e.Value.(*Sample)
		if r+c.Width+c.Delta > t {
			return p.Value
		}
		r += p.Width
		p = c
		e = e.Next()
	}
	return p.Value
}

func (s *stream) compress() {
	if s.l.Len() < 2 {
		return
	}
	e := s.l.Back()
	x := e.Value.(*Sample)
	r := s.n - 1 - x.Width
	e = e.Prev()
	for e != nil {
		c := e.Value.(*Sample)
		if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
			x.Width += c.Width
			o := e
			e = e.Prev()
			s.l.Remove(o)
		} else {
			x = c
			e = e.Prev()
		}
		r -= c.Width
	}
}

func (s *stream) samples() Samples {
	samples := make(Samples, 0, s.l.Len())
	for e := s.l.Front(); e != nil; e = e.Next() {
		samples = append(samples, *e.Value.(*Sample))
	}
	return samples
}
<commit_msg>better words<commit_after>// Package quantile computes approximate quantiles over an unbounded data
// stream within low memory and CPU bounds.
//
// A small amount of accuracy is traded to achieve the above properties.
//
// Multiple streams can be merged before calling Query to generate a single set
// of results. This is meaningful when the streams represent the same type of
// data. See Merge and Samples.
//
// For more detailed information about the algorithm, see:
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
// The quantile package implements the algorithm in the paper Effective
// Computation of Biased Quantiles over Data Streams with both invariants.
package quantile

import (
	"container/list"
	"math"
	"sort"
)

// Sample holds an observed value and meta information for compression. JSON
// tags have been added for convenience.
type Sample struct {
	Value float64 `json:",string"`
	Width float64 `json:",string"`
	Delta float64 `json:",string"`
}

// Samples represents a slice of samples. It implements sort.Interface.
type Samples []Sample

func (a Samples) Len() int {
	return len(a)
}

func (a Samples) Less(i, j int) bool {
	return a[i].Value < a[j].Value
}

func (a Samples) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

type invariant func(s *stream, r float64) float64

// NewBiased returns an initialized Stream for high-biased quantiles (e.g.
// 50th, 90th, 99th) not known a priori with ﬁner error guarantees for the
// higher ranks of the data distribution.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewBiased() *Stream {
	f := func(s *stream, r float64) float64 {
		return 2 * s.epsilon * r
	}
	return newStream(0.01, f)
}

// NewTargeted returns an initialized Stream concerned with a particular set of
// quantile values that are supplied a priori. Knowing these a priori reduces
// space and computation time.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(quantiles ...float64) *Stream {
	f := func(s *stream, r float64) float64 {
		var m float64 = math.MaxFloat64
		var f float64
		for _, q := range quantiles {
			if q*s.n <= r {
				f = (2 * s.epsilon * r) / q
			} else {
				f = (2 * s.epsilon * (s.n - r)) / (1 - q)
			}
			m = math.Min(m, f)
		}
		return m
	}
	return newStream(0.01, f)
}

// Stream calculates quantiles for a stream of float64s.
type Stream struct {
	*stream
	b Samples
}

func newStream(epsilon float64, ƒ invariant) *Stream {
	x := &stream{epsilon: epsilon, ƒ: ƒ, l: list.New()}
	return &Stream{x, make(Samples, 0, 500)}
}

// Insert inserts v into the stream.
func (s *Stream) Insert(v float64) {
	s.insert(Sample{Value: v, Width: 1})
}

func (s *Stream) insert(sample Sample) {
	s.b = append(s.b, sample)
	if len(s.b) == cap(s.b) {
		s.flush()
		s.compress()
	}
}

// Query returns the calculated qth percentiles value. If s was created with
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
// will return an unspecified result.
func (s *Stream) Query(q float64) float64 {
	if s.flushed() {
		// Fast path when there hasn't been enough data for a flush;
		// this also yeilds better accuracy for small sets of data.
		i := float64(len(s.b)) * q
		return s.b[int(i)].Value
	}
	s.flush()
	return s.stream.query(q)
}

// Merge merges samples into the underlying streams samples. This is handy when
// merging multiple streams from separate threads, database shards, etc.
func (s *Stream) Merge(samples Samples) {
	s.stream.merge(samples)
}

// Reset reinitializes and clears the list reusing the samples buffer memory.
func (s *Stream) Reset() {
	s.stream.reset()
	s.b = s.b[:0]
}

// Samples returns stream samples held by s.
func (s *Stream) Samples() Samples {
	if !s.flushed() {
		return s.b
	}
	return s.stream.samples()
}

func (s *Stream) flush() {
	sort.Sort(s.b)
	s.stream.merge(s.b)
	s.b = s.b[:0]
}

func (s *Stream) flushed() bool {
	return s.stream.l.Len() == 0
}

type stream struct {
	epsilon float64
	n       float64
	l       *list.List
	ƒ       invariant
}

// SetEpsilon sets the error epsilon for the Stream. The default epsilon is
// 0.01 and is usually satisfactory.
// To learn more, see: http://www.cs.rutgers.edu/~muthu/bquant.pdf
func (s *stream) SetEpsilon(epsilon float64) {
	s.epsilon = epsilon
}

func (s *stream) reset() {
	s.l.Init()
	s.n = 0
}

func (s *stream) insert(v float64) {
	fn := s.mergeFunc()
	fn(v, 1)
}

func (s *stream) merge(samples Samples) {
	fn := s.mergeFunc()
	for _, s := range samples {
		fn(s.Value, s.Width)
	}
}

func (s *stream) mergeFunc() func(v, w float64) {
	// NOTE: I used a goto over defer because it bought me a few extra
	// nanoseconds. I know. I know.
	var r float64
	e := s.l.Front()
	return func(v, w float64) {
		for ; e != nil; e = e.Next() {
			c := e.Value.(*Sample)
			if c.Value > v {
				sm := &Sample{v, w, math.Floor(s.ƒ(s, r)) - 1}
				s.l.InsertBefore(sm, e)
				goto inserted
			}
			r += c.Width
		}
		s.l.PushBack(&Sample{v, w, 0})
	inserted:
		s.n += w
	}
}

// Count returns the total number of samples observed in the stream
// since initialization.
func (s *stream) Count() int {
	return int(s.n)
}

func (s *stream) query(q float64) float64 {
	e := s.l.Front()
	t := math.Ceil(q * s.n)
	t += math.Ceil(s.ƒ(s, t) / 2)
	p := e.Value.(*Sample)
	e = e.Next()
	r := float64(0)
	for e != nil {
		c := e.Value.(*Sample)
		if r+c.Width+c.Delta > t {
			return p.Value
		}
		r += p.Width
		p = c
		e = e.Next()
	}
	return p.Value
}

func (s *stream) compress() {
	if s.l.Len() < 2 {
		return
	}
	e := s.l.Back()
	x := e.Value.(*Sample)
	r := s.n - 1 - x.Width
	e = e.Prev()
	for e != nil {
		c := e.Value.(*Sample)
		if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
			x.Width += c.Width
			o := e
			e = e.Prev()
			s.l.Remove(o)
		} else {
			x = c
			e = e.Prev()
		}
		r -= c.Width
	}
}

func (s *stream) samples() Samples {
	samples := make(Samples, 0, s.l.Len())
	for e := s.l.Front(); e != nil; e = e.Next() {
		samples = append(samples, *e.Value.(*Sample))
	}
	return samples
}
<|endoftext|>


<commit_before>package main

// copy from go scanner

import (
	"fmt"
	"io"
	"strconv"
	"unicode"
	"unicode/utf8"
)

type Scanner struct {
	src []byte // source

	// scanning state
	ch       rune // current character
	offset   int  // character offset
	rdOffset int  // reading offset (position after current character)

	line int // current line

	err error

	// items for one command
	items []interface{}
	// handle array type
	arrayItems [][]interface{}
}

const bom = 0xFEFF // byte order mark, only permitted as very first character

// Read the next Unicode char into s.ch.
// s.ch < 0 means end-of-file.
//
func (s *Scanner) next() {
	if s.rdOffset < len(s.src) {
		s.offset = s.rdOffset
		if s.ch == '\n' {
			s.line++
		}
		r, w := rune(s.src[s.rdOffset]), 1
		switch {
		case r == 0:
			s.error(s.offset, "illegal character NUL")
		case r >= 0x80:
			// not ASCII
			r, w = utf8.DecodeRune(s.src[s.rdOffset:])
			if r == utf8.RuneError && w == 1 {
				s.error(s.offset, "illegal UTF-8 encoding")
			} else if r == bom && s.offset > 0 {
				s.error(s.offset, "illegal byte order mark")
			}
		}
		s.rdOffset += w
		s.ch = r
	} else {
		s.offset = len(s.src)
		if s.ch == '\n' {
			s.line++
		}
		s.ch = -1 // eof
	}
}

func (s *Scanner) Init(src []byte) {
	s.src = src

	s.ch = ' '
	s.offset = 0
	s.rdOffset = 0
	s.line = 1

	s.next()
	if s.ch == bom {
		s.next() // ignore BOM at file beginning
	}
}

func (s *Scanner) error(offs int, msg string) {
	if s.err == nil {
		s.err = fmt.Errorf("An error occurs at line %d, offset %d, err: %v", s.line, offs, msg)
	}
}

func (s *Scanner) scanComment() string {
	offs := s.offset - 1

	s.next()
	for s.ch != '\n' && s.ch >= 0 {
		s.next()
	}

	lit := s.src[offs:s.offset]

	return string(lit)
}

func isLetter(ch rune) bool {
	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}

func isDigit(ch rune) bool {
	return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
}

func (s *Scanner) scanIdentifier() string {
	offs := s.offset
	for isLetter(s.ch) || isDigit(s.ch) {
		s.next()
	}
	return string(s.src[offs:s.offset])
}

func digitVal(ch rune) int {
	switch {
	case '0' <= ch && ch <= '9':
		return int(ch - '0')
	case 'a' <= ch && ch <= 'f':
		return int(ch - 'a' + 10)
	case 'A' <= ch && ch <= 'F':
		return int(ch - 'A' + 10)
	}
	return 16 // larger than any legal digit val
}

func (s *Scanner) scanMantissa(base int) {
	for digitVal(s.ch) < base {
		s.next()
	}
}

func (s *Scanner) scanNumber() interface{} {
	offs := s.offset

	isInteger := true

	if s.ch == '0' {
		// int or float
		offs := s.offset
		s.next()
		if s.ch == 'x' || s.ch == 'X' {
			// hexadecimal int
			s.next()
			s.scanMantissa(16)
			if s.offset-offs <= 2 {
				// only scanned "0x" or "0X"
				s.error(offs, "illegal hexadecimal number")
				return nil
			}
		} else {
			// octal int or float
			seenDecimalDigit := false
			s.scanMantissa(8)
			if s.ch == '8' || s.ch == '9' {
				// illegal octal int or float
				seenDecimalDigit = true
				s.scanMantissa(10)
			}
			if s.ch == '.' || s.ch == 'e' || s.ch == 'E' || s.ch == 'i' {
				goto fraction
			} else if seenDecimalDigit {
				// octal int
				s.error(offs, "illegal octal number")
				return nil
			}
		}
		goto exit
	}

	s.scanMantissa(10)

fraction:
	if s.ch == '.' {
		isInteger = false
		s.next()
		s.scanMantissa(10)
	}

	if s.ch == 'e' || s.ch == 'E' {
		isInteger = false
		s.next()
		if s.ch == '-' || s.ch == '+' {
			s.next()
		}
		s.scanMantissa(10)
	}

	if s.ch == 'i' {
		s.error(offs, fmt.Sprintf("illegal number, can not support image number"))
		return nil
	}

exit:
	var v interface{}
	var err error
	if isInteger {
		v, err = strconv.ParseInt(string(s.src[offs:s.offset]), 10, 64)
	} else {
		v, err = strconv.ParseFloat(string(s.src[offs:s.offset]), 64)
	}

	if err != nil {
		s.error(offs, fmt.Sprintf("illegal number, parse err: %v", err))
		return nil
	}

	return v
}

// scanEscape parses an escape sequence where rune is the accepted
// escaped quote. In case of a syntax error, it stops at the offending
// character (without consuming it) and returns false. Otherwise
// it returns true.
func (s *Scanner) scanEscape(quote rune) bool {
	offs := s.offset

	var n int
	var base, max uint32
	switch s.ch {
	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
		s.next()
		return true
	case '0', '1', '2', '3', '4', '5', '6', '7':
		n, base, max = 3, 8, 255
	case 'x':
		s.next()
		n, base, max = 2, 16, 255
	case 'u':
		s.next()
		n, base, max = 4, 16, unicode.MaxRune
	case 'U':
		s.next()
		n, base, max = 8, 16, unicode.MaxRune
	default:
		msg := "unknown escape sequence"
		if s.ch < 0 {
			msg = "escape sequence not terminated"
		}
		s.error(offs, msg)
		return false
	}

	var x uint32
	for n > 0 {
		d := uint32(digitVal(s.ch))
		if d >= base {
			msg := fmt.Sprintf("illegal character %#U in escape sequence", s.ch)
			if s.ch < 0 {
				msg = "escape sequence not terminated"
			}
			s.error(s.offset, msg)
			return false
		}
		x = x*base + d
		s.next()
		n--
	}

	if x > max || 0xD800 <= x && x < 0xE000 {
		s.error(offs, "escape sequence is invalid Unicode code point")
		return false
	}

	return true
}

func (s *Scanner) scanString() string {
	// '"' opening already consumed
	offs := s.offset - 1

	for {
		ch := s.ch
		if ch == '\n' || ch < 0 {
			s.error(offs, "string literal not terminated")
			break
		}
		s.next()
		if ch == '"' {
			break
		}
		if ch == '\\' {
			s.scanEscape('"')
		}
	}

	// remove quote
	return string(s.src[offs+1 : s.offset-1])
}

func (s *Scanner) skipWhitespace() {
	for s.ch == ' ' || s.ch == '\t' || s.ch == '\r' {
		s.next()
	}
}

func (s *Scanner) Err() error {
	return s.err
}

func (s *Scanner) inBracket() bool {
	return len(s.arrayItems) > 0
}

func (s *Scanner) ScanCommand() []interface{} {
	s.items = make([]interface{}, 0)
	s.arrayItems = make([][]interface{}, 0)

	s.scanCommand()
	return s.items
}

func (s *Scanner) scanCommand() {
	var v interface{}
	for {
		v = nil
		s.skipWhitespace()

		switch ch := s.ch; {
		case isLetter(ch):
			v = s.scanIdentifier()
		case '0' <= ch && ch <= '9':
			v = s.scanNumber()
		default:
			s.next()
			switch ch {
			case -1:
				// EOF
				s.err = io.EOF
				return
			case '\n':
				if len(s.items) > 0 {
					return
				}
			case '"':
				v = s.scanString()
			case '[':
				s.arrayItems = append(s.arrayItems, make([]interface{}, 0))
			case ']':
				if len(s.arrayItems) == 0 {
					s.error(s.offset, "invalid ], no corresponding [")
					return
				}
				// pop last array
				n := len(s.arrayItems) - 1
				v = s.arrayItems[n]
				s.arrayItems = s.arrayItems[0:n]
			case '#':
				s.scanComment()
			case ',':
				if !s.inBracket() {
					s.error(s.offset, fmt.Sprintf(", must in bracket for array type"))
					return
				}
			default:
				s.error(s.offset, fmt.Sprintf("illegal character %#U", ch))
				return
			}
		}

		if v != nil {
			if s.inBracket() {
				n := len(s.arrayItems) - 1
				b := s.arrayItems[n]
				b = append(b, v)
				s.arrayItems[n] = b
			} else {
				s.items = append(s.items, v)
			}
		}
	}
	return
}
<commit_msg>break scan if err<commit_after>package main

// copy from go scanner

import (
	"fmt"
	"io"
	"strconv"
	"unicode"
	"unicode/utf8"
)

type Scanner struct {
	src []byte // source

	// scanning state
	ch       rune // current character
	offset   int  // character offset
	rdOffset int  // reading offset (position after current character)

	line int // current line

	err error

	// items for one command
	items []interface{}
	// handle array type
	arrayItems [][]interface{}
}

const bom = 0xFEFF // byte order mark, only permitted as very first character

// Read the next Unicode char into s.ch.
// s.ch < 0 means end-of-file.
//
func (s *Scanner) next() {
	if s.rdOffset < len(s.src) {
		s.offset = s.rdOffset
		if s.ch == '\n' {
			s.line++
		}
		r, w := rune(s.src[s.rdOffset]), 1
		switch {
		case r == 0:
			s.error(s.offset, "illegal character NUL")
		case r >= 0x80:
			// not ASCII
			r, w = utf8.DecodeRune(s.src[s.rdOffset:])
			if r == utf8.RuneError && w == 1 {
				s.error(s.offset, "illegal UTF-8 encoding")
			} else if r == bom && s.offset > 0 {
				s.error(s.offset, "illegal byte order mark")
			}
		}
		s.rdOffset += w
		s.ch = r
	} else {
		s.offset = len(s.src)
		if s.ch == '\n' {
			s.line++
		}
		s.ch = -1 // eof
	}
}

func (s *Scanner) Init(src []byte) {
	s.src = src

	s.ch = ' '
	s.offset = 0
	s.rdOffset = 0
	s.line = 1

	s.next()
	if s.ch == bom {
		s.next() // ignore BOM at file beginning
	}
}

func (s *Scanner) error(offs int, msg string) {
	if s.err == nil {
		s.err = fmt.Errorf("An error occurs at line %d, offset %d, err: %v", s.line, offs, msg)
	}
}

func (s *Scanner) scanComment() string {
	offs := s.offset - 1

	s.next()
	for s.ch != '\n' && s.ch >= 0 {
		s.next()
	}

	lit := s.src[offs:s.offset]

	return string(lit)
}

func isLetter(ch rune) bool {
	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}

func isDigit(ch rune) bool {
	return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
}

func (s *Scanner) scanIdentifier() string {
	offs := s.offset
	for isLetter(s.ch) || isDigit(s.ch) {
		s.next()
	}
	return string(s.src[offs:s.offset])
}

func digitVal(ch rune) int {
	switch {
	case '0' <= ch && ch <= '9':
		return int(ch - '0')
	case 'a' <= ch && ch <= 'f':
		return int(ch - 'a' + 10)
	case 'A' <= ch && ch <= 'F':
		return int(ch - 'A' + 10)
	}
	return 16 // larger than any legal digit val
}

func (s *Scanner) scanMantissa(base int) {
	for digitVal(s.ch) < base {
		s.next()
	}
}

func (s *Scanner) scanNumber() interface{} {
	offs := s.offset

	isInteger := true

	if s.ch == '0' {
		// int or float
		offs := s.offset
		s.next()
		if s.ch == 'x' || s.ch == 'X' {
			// hexadecimal int
			s.next()
			s.scanMantissa(16)
			if s.offset-offs <= 2 {
				// only scanned "0x" or "0X"
				s.error(offs, "illegal hexadecimal number")
				return nil
			}
		} else {
			// octal int or float
			seenDecimalDigit := false
			s.scanMantissa(8)
			if s.ch == '8' || s.ch == '9' {
				// illegal octal int or float
				seenDecimalDigit = true
				s.scanMantissa(10)
			}
			if s.ch == '.' || s.ch == 'e' || s.ch == 'E' || s.ch == 'i' {
				goto fraction
			} else if seenDecimalDigit {
				// octal int
				s.error(offs, "illegal octal number")
				return nil
			}
		}
		goto exit
	}

	s.scanMantissa(10)

fraction:
	if s.ch == '.' {
		isInteger = false
		s.next()
		s.scanMantissa(10)
	}

	if s.ch == 'e' || s.ch == 'E' {
		isInteger = false
		s.next()
		if s.ch == '-' || s.ch == '+' {
			s.next()
		}
		s.scanMantissa(10)
	}

	if s.ch == 'i' {
		s.error(offs, fmt.Sprintf("illegal number, can not support image number"))
		return nil
	}

exit:
	var v interface{}
	var err error
	if isInteger {
		v, err = strconv.ParseInt(string(s.src[offs:s.offset]), 10, 64)
	} else {
		v, err = strconv.ParseFloat(string(s.src[offs:s.offset]), 64)
	}

	if err != nil {
		s.error(offs, fmt.Sprintf("illegal number, parse err: %v", err))
		return nil
	}

	return v
}

// scanEscape parses an escape sequence where rune is the accepted
// escaped quote. In case of a syntax error, it stops at the offending
// character (without consuming it) and returns false. Otherwise
// it returns true.
func (s *Scanner) scanEscape(quote rune) bool {
	offs := s.offset

	var n int
	var base, max uint32
	switch s.ch {
	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
		s.next()
		return true
	case '0', '1', '2', '3', '4', '5', '6', '7':
		n, base, max = 3, 8, 255
	case 'x':
		s.next()
		n, base, max = 2, 16, 255
	case 'u':
		s.next()
		n, base, max = 4, 16, unicode.MaxRune
	case 'U':
		s.next()
		n, base, max = 8, 16, unicode.MaxRune
	default:
		msg := "unknown escape sequence"
		if s.ch < 0 {
			msg = "escape sequence not terminated"
		}
		s.error(offs, msg)
		return false
	}

	var x uint32
	for n > 0 {
		d := uint32(digitVal(s.ch))
		if d >= base {
			msg := fmt.Sprintf("illegal character %#U in escape sequence", s.ch)
			if s.ch < 0 {
				msg = "escape sequence not terminated"
			}
			s.error(s.offset, msg)
			return false
		}
		x = x*base + d
		s.next()
		n--
	}

	if x > max || 0xD800 <= x && x < 0xE000 {
		s.error(offs, "escape sequence is invalid Unicode code point")
		return false
	}

	return true
}

func (s *Scanner) scanString() string {
	// '"' opening already consumed
	offs := s.offset - 1

	for {
		ch := s.ch
		if ch == '\n' || ch < 0 {
			s.error(offs, "string literal not terminated")
			break
		}
		s.next()
		if ch == '"' {
			break
		}
		if ch == '\\' {
			s.scanEscape('"')
		}
	}

	// remove quote
	return string(s.src[offs+1 : s.offset-1])
}

func (s *Scanner) skipWhitespace() {
	for s.ch == ' ' || s.ch == '\t' || s.ch == '\r' {
		s.next()
	}
}

func (s *Scanner) Err() error {
	return s.err
}

func (s *Scanner) inBracket() bool {
	return len(s.arrayItems) > 0
}

func (s *Scanner) ScanCommand() []interface{} {
	s.items = make([]interface{}, 0)
	s.arrayItems = make([][]interface{}, 0)

	s.scanCommand()
	return s.items
}

func (s *Scanner) scanCommand() {
	var v interface{}
	for {
		v = nil
		s.skipWhitespace()

		switch ch := s.ch; {
		case isLetter(ch):
			v = s.scanIdentifier()
		case '0' <= ch && ch <= '9':
			v = s.scanNumber()
		default:
			s.next()
			switch ch {
			case -1:
				// EOF
				s.err = io.EOF
				return
			case '\n':
				if len(s.items) > 0 {
					return
				}
			case '"':
				v = s.scanString()
			case '[':
				s.arrayItems = append(s.arrayItems, make([]interface{}, 0))
			case ']':
				if len(s.arrayItems) == 0 {
					s.error(s.offset, "invalid ], no corresponding [")
					return
				}
				// pop last array
				n := len(s.arrayItems) - 1
				v = s.arrayItems[n]
				s.arrayItems = s.arrayItems[0:n]
			case '#':
				s.scanComment()
			case ',':
				if !s.inBracket() {
					s.error(s.offset, fmt.Sprintf(", must in bracket for array type"))
					return
				}
			default:
				s.error(s.offset, fmt.Sprintf("illegal character %#U", ch))
				return
			}
		}

		if s.err != nil {
			break
		}

		if v != nil {
			if s.inBracket() {
				n := len(s.arrayItems) - 1
				b := s.arrayItems[n]
				b = append(b, v)
				s.arrayItems[n] = b
			} else {
				s.items = append(s.items, v)
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package missinggo

import (
	"compress/gzip"
	"io"
	"net/http"
	"strings"
)

type gzipResponseWriter struct {
	io.Writer
	http.ResponseWriter
	haveWritten bool
}

func (w *gzipResponseWriter) Write(b []byte) (int, error) {
	if w.haveWritten {
		goto write
	}
	w.haveWritten = true
	if w.Header().Get("Content-Type") != "" {
		goto write
	}
	if type_ := http.DetectContentType(b); type_ != "application/octet-stream" {
		w.Header().Set("Content-Type", type_)
	}
write:
	return w.Writer.Write(b)
}

// Gzips response body if the request says it'll allow it.
func GzipHTTPHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			h.ServeHTTP(w, r)
			return
		}
		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		h.ServeHTTP(&gzipResponseWriter{
			Writer:         gz,
			ResponseWriter: w,
		}, r)
	})
}
<commit_msg>GzipHTTPHandler: Only gzip if a Content-Encoding isn't already present<commit_after>package missinggo

import (
	"compress/gzip"
	"io"
	"net/http"
	"strings"
)

type gzipResponseWriter struct {
	io.Writer
	http.ResponseWriter
	haveWritten bool
}

func (w *gzipResponseWriter) Write(b []byte) (int, error) {
	if w.haveWritten {
		goto write
	}
	w.haveWritten = true
	if w.Header().Get("Content-Type") != "" {
		goto write
	}
	if type_ := http.DetectContentType(b); type_ != "application/octet-stream" {
		w.Header().Set("Content-Type", type_)
	}
write:
	return w.Writer.Write(b)
}

// Gzips response body if the request says it'll allow it.
func GzipHTTPHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") || w.Header().Get("Content-Encoding") != "" || w.Header().Get("Vary") != "" {
			h.ServeHTTP(w, r)
			return
		}
		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		h.ServeHTTP(&gzipResponseWriter{
			Writer:         gz,
			ResponseWriter: w,
		}, r)
	})
}
<|endoftext|>


<commit_before>package admission

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"reflect"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apiserver/pkg/admission"
	"k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle"
	mutatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
	validatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
	"k8s.io/apiserver/pkg/apis/apiserver"
	"k8s.io/kubernetes/plugin/pkg/admission/noderestriction"
	expandpvcadmission "k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/resize"
	storageclassdefaultadmission "k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault"

	configv1 "github.com/openshift/api/config/v1"
	oadmission "github.com/openshift/origin/pkg/cmd/server/admission"
	configapi "github.com/openshift/origin/pkg/cmd/server/apis/config"
	configapilatest "github.com/openshift/origin/pkg/cmd/server/apis/config/latest"
	"github.com/openshift/origin/pkg/image/apiserver/admission/apis/imagepolicy"
	imageadmission "github.com/openshift/origin/pkg/image/apiserver/admission/limitrange"
	ingressadmission "github.com/openshift/origin/pkg/network/apiserver/admission"
	overrideapi "github.com/openshift/origin/pkg/quota/apiserver/admission/apis/clusterresourceoverride"
	"github.com/openshift/origin/pkg/security/apiserver/admission/sccadmission"
	"github.com/openshift/origin/pkg/service/admission/externalipranger"
	"github.com/openshift/origin/pkg/service/admission/restrictedendpoints"
)

var (
	// these are admission plugins that cannot be applied until after the kubeapiserver starts.
	// TODO if nothing comes to mind in 3.10, kill this
	SkipRunLevelZeroPlugins = sets.NewString()
	// these are admission plugins that cannot be applied until after the openshiftapiserver apiserver starts.
	SkipRunLevelOnePlugins = sets.NewString(
		"ProjectRequestLimit",
		"openshift.io/RestrictSubjectBindings",
		"openshift.io/ClusterResourceQuota",
		imagepolicy.PluginName,
		overrideapi.PluginName,
		"OriginPodNodeEnvironment",
		"RunOnceDuration",
		sccadmission.PluginName,
		"SCCExecRestrictions",
	)

	// openshiftAdmissionControlPlugins gives the in-order default admission chain for openshift resources.
	openshiftAdmissionControlPlugins = []string{
		lifecycle.PluginName,
		"ProjectRequestLimit",
		"openshift.io/JenkinsBootstrapper",
		"openshift.io/BuildConfigSecretInjector",
		"BuildByStrategy",
		imageadmission.PluginName,
		"PodNodeConstraints",
		"OwnerReferencesPermissionEnforcement",
		"Initializers",
		"MutatingAdmissionWebhook",
		"ValidatingAdmissionWebhook",
		"ResourceQuota",
	}

	// KubeAdmissionPlugins gives the in-order default admission chain for kube resources.
	KubeAdmissionPlugins = []string{
		"AlwaysAdmit",
		"NamespaceAutoProvision",
		"NamespaceExists",
		lifecycle.PluginName,
		"EventRateLimit",
		"openshift.io/RestrictSubjectBindings",
		"RunOnceDuration",
		"PodNodeConstraints",
		"OriginPodNodeEnvironment",
		"PodNodeSelector",
		overrideapi.PluginName,
		externalipranger.ExternalIPPluginName,
		restrictedendpoints.RestrictedEndpointsPluginName,
		imagepolicy.PluginName,
		"ImagePolicyWebhook",
		"PodPreset",
		"LimitRanger",
		"ServiceAccount",
		noderestriction.PluginName,
		"SecurityContextDeny",
		sccadmission.PluginName,
		"PodSecurityPolicy",
		"DenyEscalatingExec",
		"DenyExecOnPrivileged",
		storageclassdefaultadmission.PluginName,
		expandpvcadmission.PluginName,
		"AlwaysPullImages",
		"LimitPodHardAntiAffinityTopology",
		"SCCExecRestrictions",
		"PersistentVolumeLabel",
		"OwnerReferencesPermissionEnforcement",
		ingressadmission.IngressAdmission,
		"Priority",
		"ExtendedResourceToleration",
		"DefaultTolerationSeconds",
		"StorageObjectInUseProtection",
		"Initializers",
		mutatingwebhook.PluginName,
		validatingwebhook.PluginName,
		"PodTolerationRestriction",
		"AlwaysDeny",
		// NOTE: ResourceQuota and ClusterResourceQuota must be the last 2 plugins.
		// DO NOT ADD ANY PLUGINS AFTER THIS LINE!
		"ResourceQuota",
		"openshift.io/ClusterResourceQuota",
	}

	// combinedAdmissionControlPlugins gives the in-order default admission chain for all resources resources.
	// When possible, this list is used.  The set of openshift+kube chains must exactly match this set.  In addition,
	// the order specified in the openshift and kube chains must match the order here.
	CombinedAdmissionControlPlugins = []string{
		"AlwaysAdmit",
		"NamespaceAutoProvision",
		"NamespaceExists",
		lifecycle.PluginName,
		"EventRateLimit",
		"ProjectRequestLimit",
		"openshift.io/RestrictSubjectBindings",
		"openshift.io/JenkinsBootstrapper",
		"openshift.io/BuildConfigSecretInjector",
		"BuildByStrategy",
		imageadmission.PluginName,
		"RunOnceDuration",
		"PodNodeConstraints",
		"OriginPodNodeEnvironment",
		"PodNodeSelector",
		overrideapi.PluginName,
		externalipranger.ExternalIPPluginName,
		restrictedendpoints.RestrictedEndpointsPluginName,
		imagepolicy.PluginName,
		"ImagePolicyWebhook",
		"PodPreset",
		"LimitRanger",
		"ServiceAccount",
		noderestriction.PluginName,
		"SecurityContextDeny",
		sccadmission.PluginName,
		"PodSecurityPolicy",
		"DenyEscalatingExec",
		"DenyExecOnPrivileged",
		storageclassdefaultadmission.PluginName,
		expandpvcadmission.PluginName,
		"AlwaysPullImages",
		"LimitPodHardAntiAffinityTopology",
		"SCCExecRestrictions",
		"PersistentVolumeLabel",
		"OwnerReferencesPermissionEnforcement",
		ingressadmission.IngressAdmission,
		"Priority",
		"ExtendedResourceToleration",
		"DefaultTolerationSeconds",
		"StorageObjectInUseProtection",
		"Initializers",
		mutatingwebhook.PluginName,
		validatingwebhook.PluginName,
		"PodTolerationRestriction",
		"AlwaysDeny",
		// NOTE: ResourceQuota and ClusterResourceQuota must be the last 2 plugins.
		// DO NOT ADD ANY PLUGINS AFTER THIS LINE!
		"ResourceQuota",
		"openshift.io/ClusterResourceQuota",
	}
)

// fixupAdmissionPlugins fixes the input plugins to handle deprecation and duplicates.
func fixupAdmissionPlugins(plugins []string) []string {
	result := replace(plugins, "openshift.io/OriginResourceQuota", "ResourceQuota")
	result = dedupe(result)
	return result
}

func NewAdmissionChains(
	admissionConfigFiles []string,
	pluginConfig map[string]configv1.AdmissionPluginConfig,
	admissionInitializer admission.PluginInitializer,
	admissionDecorator admission.Decorator,
) (admission.Interface, error) {
	admissionPluginConfigFilename := ""
	if len(admissionConfigFiles) > 0 {
		admissionPluginConfigFilename = admissionConfigFiles[0]

	} else {
		upstreamAdmissionConfig, err := ConvertOpenshiftAdmissionConfigToKubeAdmissionConfig(pluginConfig)
		if err != nil {
			return nil, err
		}
		configBytes, err := configapilatest.WriteYAML(upstreamAdmissionConfig)
		if err != nil {
			return nil, err
		}

		tempFile, err := ioutil.TempFile("", "master-config.yaml")
		if err != nil {
			return nil, err
		}
		defer os.Remove(tempFile.Name())
		if _, err := tempFile.Write(configBytes); err != nil {
			return nil, err
		}
		tempFile.Close()
		admissionPluginConfigFilename = tempFile.Name()
	}

	admissionPluginNames := openshiftAdmissionControlPlugins
	admissionPluginNames = fixupAdmissionPlugins(admissionPluginNames)

	admissionChain, err := newAdmissionChainFunc(admissionPluginNames, admissionPluginConfigFilename, admissionInitializer, admissionDecorator)

	if err != nil {
		return nil, err
	}

	return admissionChain, err
}

// newAdmissionChainFunc is for unit testing only.  You should NEVER OVERRIDE THIS outside of a unit test.
var newAdmissionChainFunc = newAdmissionChain

func newAdmissionChain(pluginNames []string, admissionConfigFilename string, admissionInitializer admission.PluginInitializer, admissionDecorator admission.Decorator) (admission.Interface, error) {
	plugins := []admission.Interface{}
	for _, pluginName := range pluginNames {
		var (
			plugin admission.Interface
		)

		// TODO this needs to be refactored to use the admission scheme we created upstream.  I think this holds us for the rebase.
		pluginsConfigProvider, err := admission.ReadAdmissionConfiguration([]string{pluginName}, admissionConfigFilename, configapi.Scheme)
		if err != nil {
			return nil, err
		}
		plugin, err = OriginAdmissionPlugins.NewFromPlugins([]string{pluginName}, pluginsConfigProvider, admissionInitializer, admissionDecorator)
		if err != nil {
			// should have been caught with validation
			return nil, err
		}
		if plugin == nil {
			continue
		}

		plugins = append(plugins, plugin)

	}

	// ensure that plugins have been properly initialized
	if err := oadmission.Validate(plugins); err != nil {
		return nil, err
	}

	return admission.NewChainHandler(plugins...), nil
}

// replace returns a slice where each instance of the input that is x is replaced with y
func replace(input []string, x, y string) []string {
	result := []string{}
	for i := range input {
		if input[i] == x {
			result = append(result, y)
		} else {
			result = append(result, input[i])
		}
	}
	return result
}

// dedupe removes duplicate items from the input list.
// the last instance of a duplicate is kept in the input list.
func dedupe(input []string) []string {
	items := sets.NewString()
	result := []string{}
	for i := len(input) - 1; i >= 0; i-- {
		if items.Has(input[i]) {
			continue
		}
		items.Insert(input[i])
		result = append([]string{input[i]}, result...)
	}
	return result
}

func init() {
	// add a filter that will remove DefaultAdmissionConfig
	admission.FactoryFilterFn = filterEnableAdmissionConfigs
}

func filterEnableAdmissionConfigs(delegate admission.Factory) admission.Factory {
	return func(config io.Reader) (admission.Interface, error) {
		config1, config2, err := splitStream(config)
		if err != nil {
			return nil, err
		}
		// if the config isn't a DefaultAdmissionConfig, then assume we're enabled (we were called after all)
		// if the config *is* a DefaultAdmissionConfig and it explicitly said
		obj, err := configapilatest.ReadYAML(config1)
		// if we can't read it, let the plugin deal with it
		if err != nil {
			return delegate(config2)
		}
		// if nothing was there, let the plugin deal with it
		if obj == nil {
			return delegate(config2)
		}
		// if it wasn't a DefaultAdmissionConfig object, let the plugin deal with it
		if _, ok := obj.(*configapi.DefaultAdmissionConfig); !ok {
			return delegate(config2)
		}

		// if it was a DefaultAdmissionConfig, then it must have said "enabled" and it wasn't really meant for the
		// admission plugin
		return delegate(nil)
	}
}

// splitStream reads the stream bytes and constructs two copies of it.
func splitStream(config io.Reader) (io.Reader, io.Reader, error) {
	if config == nil || reflect.ValueOf(config).IsNil() {
		return nil, nil, nil
	}

	configBytes, err := ioutil.ReadAll(config)
	if err != nil {
		return nil, nil, err
	}

	return bytes.NewBuffer(configBytes), bytes.NewBuffer(configBytes), nil
}

func ConvertOpenshiftAdmissionConfigToKubeAdmissionConfig(in map[string]configv1.AdmissionPluginConfig) (*apiserver.AdmissionConfiguration, error) {
	ret := &apiserver.AdmissionConfiguration{}

	for _, pluginName := range sets.StringKeySet(in).List() {
		openshiftConfig := in[pluginName]

		kubeConfig := apiserver.AdmissionPluginConfiguration{
			Name: pluginName,
			Path: openshiftConfig.Location,
		}

		kubeConfig.Configuration = &runtime.Unknown{
			Raw: openshiftConfig.Configuration.Raw,
		}
		ret.Plugins = append(ret.Plugins, kubeConfig)
	}

	return ret, nil
}
<commit_msg>Add openshift.io/ClusterResourceQuota to openshift api server<commit_after>package admission

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"reflect"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apiserver/pkg/admission"
	"k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle"
	mutatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
	validatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
	"k8s.io/apiserver/pkg/apis/apiserver"
	"k8s.io/kubernetes/plugin/pkg/admission/noderestriction"
	expandpvcadmission "k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/resize"
	storageclassdefaultadmission "k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault"

	configv1 "github.com/openshift/api/config/v1"
	oadmission "github.com/openshift/origin/pkg/cmd/server/admission"
	configapi "github.com/openshift/origin/pkg/cmd/server/apis/config"
	configapilatest "github.com/openshift/origin/pkg/cmd/server/apis/config/latest"
	"github.com/openshift/origin/pkg/image/apiserver/admission/apis/imagepolicy"
	imageadmission "github.com/openshift/origin/pkg/image/apiserver/admission/limitrange"
	ingressadmission "github.com/openshift/origin/pkg/network/apiserver/admission"
	overrideapi "github.com/openshift/origin/pkg/quota/apiserver/admission/apis/clusterresourceoverride"
	"github.com/openshift/origin/pkg/security/apiserver/admission/sccadmission"
	"github.com/openshift/origin/pkg/service/admission/externalipranger"
	"github.com/openshift/origin/pkg/service/admission/restrictedendpoints"
)

var (
	// these are admission plugins that cannot be applied until after the kubeapiserver starts.
	// TODO if nothing comes to mind in 3.10, kill this
	SkipRunLevelZeroPlugins = sets.NewString()
	// these are admission plugins that cannot be applied until after the openshiftapiserver apiserver starts.
	SkipRunLevelOnePlugins = sets.NewString(
		"ProjectRequestLimit",
		"openshift.io/RestrictSubjectBindings",
		"openshift.io/ClusterResourceQuota",
		imagepolicy.PluginName,
		overrideapi.PluginName,
		"OriginPodNodeEnvironment",
		"RunOnceDuration",
		sccadmission.PluginName,
		"SCCExecRestrictions",
	)

	// openshiftAdmissionControlPlugins gives the in-order default admission chain for openshift resources.
	openshiftAdmissionControlPlugins = []string{
		lifecycle.PluginName,
		"ProjectRequestLimit",
		"openshift.io/JenkinsBootstrapper",
		"openshift.io/BuildConfigSecretInjector",
		"BuildByStrategy",
		imageadmission.PluginName,
		"PodNodeConstraints",
		"OwnerReferencesPermissionEnforcement",
		"Initializers",
		"MutatingAdmissionWebhook",
		"ValidatingAdmissionWebhook",
		"ResourceQuota",
		"openshift.io/ClusterResourceQuota",
	}

	// KubeAdmissionPlugins gives the in-order default admission chain for kube resources.
	KubeAdmissionPlugins = []string{
		"AlwaysAdmit",
		"NamespaceAutoProvision",
		"NamespaceExists",
		lifecycle.PluginName,
		"EventRateLimit",
		"openshift.io/RestrictSubjectBindings",
		"RunOnceDuration",
		"PodNodeConstraints",
		"OriginPodNodeEnvironment",
		"PodNodeSelector",
		overrideapi.PluginName,
		externalipranger.ExternalIPPluginName,
		restrictedendpoints.RestrictedEndpointsPluginName,
		imagepolicy.PluginName,
		"ImagePolicyWebhook",
		"PodPreset",
		"LimitRanger",
		"ServiceAccount",
		noderestriction.PluginName,
		"SecurityContextDeny",
		sccadmission.PluginName,
		"PodSecurityPolicy",
		"DenyEscalatingExec",
		"DenyExecOnPrivileged",
		storageclassdefaultadmission.PluginName,
		expandpvcadmission.PluginName,
		"AlwaysPullImages",
		"LimitPodHardAntiAffinityTopology",
		"SCCExecRestrictions",
		"PersistentVolumeLabel",
		"OwnerReferencesPermissionEnforcement",
		ingressadmission.IngressAdmission,
		"Priority",
		"ExtendedResourceToleration",
		"DefaultTolerationSeconds",
		"StorageObjectInUseProtection",
		"Initializers",
		mutatingwebhook.PluginName,
		validatingwebhook.PluginName,
		"PodTolerationRestriction",
		"AlwaysDeny",
		// NOTE: ResourceQuota and ClusterResourceQuota must be the last 2 plugins.
		// DO NOT ADD ANY PLUGINS AFTER THIS LINE!
		"ResourceQuota",
		"openshift.io/ClusterResourceQuota",
	}

	// combinedAdmissionControlPlugins gives the in-order default admission chain for all resources resources.
	// When possible, this list is used.  The set of openshift+kube chains must exactly match this set.  In addition,
	// the order specified in the openshift and kube chains must match the order here.
	CombinedAdmissionControlPlugins = []string{
		"AlwaysAdmit",
		"NamespaceAutoProvision",
		"NamespaceExists",
		lifecycle.PluginName,
		"EventRateLimit",
		"ProjectRequestLimit",
		"openshift.io/RestrictSubjectBindings",
		"openshift.io/JenkinsBootstrapper",
		"openshift.io/BuildConfigSecretInjector",
		"BuildByStrategy",
		imageadmission.PluginName,
		"RunOnceDuration",
		"PodNodeConstraints",
		"OriginPodNodeEnvironment",
		"PodNodeSelector",
		overrideapi.PluginName,
		externalipranger.ExternalIPPluginName,
		restrictedendpoints.RestrictedEndpointsPluginName,
		imagepolicy.PluginName,
		"ImagePolicyWebhook",
		"PodPreset",
		"LimitRanger",
		"ServiceAccount",
		noderestriction.PluginName,
		"SecurityContextDeny",
		sccadmission.PluginName,
		"PodSecurityPolicy",
		"DenyEscalatingExec",
		"DenyExecOnPrivileged",
		storageclassdefaultadmission.PluginName,
		expandpvcadmission.PluginName,
		"AlwaysPullImages",
		"LimitPodHardAntiAffinityTopology",
		"SCCExecRestrictions",
		"PersistentVolumeLabel",
		"OwnerReferencesPermissionEnforcement",
		ingressadmission.IngressAdmission,
		"Priority",
		"ExtendedResourceToleration",
		"DefaultTolerationSeconds",
		"StorageObjectInUseProtection",
		"Initializers",
		mutatingwebhook.PluginName,
		validatingwebhook.PluginName,
		"PodTolerationRestriction",
		"AlwaysDeny",
		// NOTE: ResourceQuota and ClusterResourceQuota must be the last 2 plugins.
		// DO NOT ADD ANY PLUGINS AFTER THIS LINE!
		"ResourceQuota",
		"openshift.io/ClusterResourceQuota",
	}
)

// fixupAdmissionPlugins fixes the input plugins to handle deprecation and duplicates.
func fixupAdmissionPlugins(plugins []string) []string {
	result := replace(plugins, "openshift.io/OriginResourceQuota", "ResourceQuota")
	result = dedupe(result)
	return result
}

func NewAdmissionChains(
	admissionConfigFiles []string,
	pluginConfig map[string]configv1.AdmissionPluginConfig,
	admissionInitializer admission.PluginInitializer,
	admissionDecorator admission.Decorator,
) (admission.Interface, error) {
	admissionPluginConfigFilename := ""
	if len(admissionConfigFiles) > 0 {
		admissionPluginConfigFilename = admissionConfigFiles[0]

	} else {
		upstreamAdmissionConfig, err := ConvertOpenshiftAdmissionConfigToKubeAdmissionConfig(pluginConfig)
		if err != nil {
			return nil, err
		}
		configBytes, err := configapilatest.WriteYAML(upstreamAdmissionConfig)
		if err != nil {
			return nil, err
		}

		tempFile, err := ioutil.TempFile("", "master-config.yaml")
		if err != nil {
			return nil, err
		}
		defer os.Remove(tempFile.Name())
		if _, err := tempFile.Write(configBytes); err != nil {
			return nil, err
		}
		tempFile.Close()
		admissionPluginConfigFilename = tempFile.Name()
	}

	admissionPluginNames := openshiftAdmissionControlPlugins
	admissionPluginNames = fixupAdmissionPlugins(admissionPluginNames)

	admissionChain, err := newAdmissionChainFunc(admissionPluginNames, admissionPluginConfigFilename, admissionInitializer, admissionDecorator)

	if err != nil {
		return nil, err
	}

	return admissionChain, err
}

// newAdmissionChainFunc is for unit testing only.  You should NEVER OVERRIDE THIS outside of a unit test.
var newAdmissionChainFunc = newAdmissionChain

func newAdmissionChain(pluginNames []string, admissionConfigFilename string, admissionInitializer admission.PluginInitializer, admissionDecorator admission.Decorator) (admission.Interface, error) {
	plugins := []admission.Interface{}
	for _, pluginName := range pluginNames {
		var (
			plugin admission.Interface
		)

		// TODO this needs to be refactored to use the admission scheme we created upstream.  I think this holds us for the rebase.
		pluginsConfigProvider, err := admission.ReadAdmissionConfiguration([]string{pluginName}, admissionConfigFilename, configapi.Scheme)
		if err != nil {
			return nil, err
		}
		plugin, err = OriginAdmissionPlugins.NewFromPlugins([]string{pluginName}, pluginsConfigProvider, admissionInitializer, admissionDecorator)
		if err != nil {
			// should have been caught with validation
			return nil, err
		}
		if plugin == nil {
			continue
		}

		plugins = append(plugins, plugin)

	}

	// ensure that plugins have been properly initialized
	if err := oadmission.Validate(plugins); err != nil {
		return nil, err
	}

	return admission.NewChainHandler(plugins...), nil
}

// replace returns a slice where each instance of the input that is x is replaced with y
func replace(input []string, x, y string) []string {
	result := []string{}
	for i := range input {
		if input[i] == x {
			result = append(result, y)
		} else {
			result = append(result, input[i])
		}
	}
	return result
}

// dedupe removes duplicate items from the input list.
// the last instance of a duplicate is kept in the input list.
func dedupe(input []string) []string {
	items := sets.NewString()
	result := []string{}
	for i := len(input) - 1; i >= 0; i-- {
		if items.Has(input[i]) {
			continue
		}
		items.Insert(input[i])
		result = append([]string{input[i]}, result...)
	}
	return result
}

func init() {
	// add a filter that will remove DefaultAdmissionConfig
	admission.FactoryFilterFn = filterEnableAdmissionConfigs
}

func filterEnableAdmissionConfigs(delegate admission.Factory) admission.Factory {
	return func(config io.Reader) (admission.Interface, error) {
		config1, config2, err := splitStream(config)
		if err != nil {
			return nil, err
		}
		// if the config isn't a DefaultAdmissionConfig, then assume we're enabled (we were called after all)
		// if the config *is* a DefaultAdmissionConfig and it explicitly said
		obj, err := configapilatest.ReadYAML(config1)
		// if we can't read it, let the plugin deal with it
		if err != nil {
			return delegate(config2)
		}
		// if nothing was there, let the plugin deal with it
		if obj == nil {
			return delegate(config2)
		}
		// if it wasn't a DefaultAdmissionConfig object, let the plugin deal with it
		if _, ok := obj.(*configapi.DefaultAdmissionConfig); !ok {
			return delegate(config2)
		}

		// if it was a DefaultAdmissionConfig, then it must have said "enabled" and it wasn't really meant for the
		// admission plugin
		return delegate(nil)
	}
}

// splitStream reads the stream bytes and constructs two copies of it.
func splitStream(config io.Reader) (io.Reader, io.Reader, error) {
	if config == nil || reflect.ValueOf(config).IsNil() {
		return nil, nil, nil
	}

	configBytes, err := ioutil.ReadAll(config)
	if err != nil {
		return nil, nil, err
	}

	return bytes.NewBuffer(configBytes), bytes.NewBuffer(configBytes), nil
}

func ConvertOpenshiftAdmissionConfigToKubeAdmissionConfig(in map[string]configv1.AdmissionPluginConfig) (*apiserver.AdmissionConfiguration, error) {
	ret := &apiserver.AdmissionConfiguration{}

	for _, pluginName := range sets.StringKeySet(in).List() {
		openshiftConfig := in[pluginName]

		kubeConfig := apiserver.AdmissionPluginConfiguration{
			Name: pluginName,
			Path: openshiftConfig.Location,
		}

		kubeConfig.Configuration = &runtime.Unknown{
			Raw: openshiftConfig.Configuration.Raw,
		}
		ret.Plugins = append(ret.Plugins, kubeConfig)
	}

	return ret, nil
}
<|endoftext|>


<commit_before>package reseed

import (
	"bytes"
	"crypto/tls"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/PuerkitoBio/throttled"
	"github.com/PuerkitoBio/throttled/store"
	"github.com/gorilla/handlers"
	"github.com/justinas/alice"
)

const (
	I2P_USER_AGENT = "Wget/1.11.4"
)

type Server struct {
	*http.Server
	Reseeder Reseeder
}

func NewServer(prefix string, trustProxy bool) *Server {
	config := &tls.Config{
		MinVersion:               tls.VersionTLS10,
		PreferServerCipherSuites: true,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
			// tls.TLS_RSA_WITH_RC4_128_SHA,
			tls.TLS_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
			tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
		},
	}
	h := &http.Server{TLSConfig: config}
	server := Server{h, nil}

	th := throttled.RateLimit(throttled.PerHour(120), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(10000))

	middlewareChain := alice.New()
	if trustProxy {
		middlewareChain = middlewareChain.Append(proxiedMiddleware)
	}

	errorHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Connection", "close")
		w.WriteHeader(http.StatusNotFound)
		if _, err := w.Write(nil); nil != err {
			log.Println(err)
		}
	})

	mux := http.NewServeMux()
	mux.Handle("/", middlewareChain.Append(loggingMiddleware).Then(errorHandler))
	mux.Handle(prefix+"/i2pseeds.su3", middlewareChain.Append(loggingMiddleware, verifyMiddleware, th.Throttle).Then(http.HandlerFunc(server.reseedHandler)))
	server.Handler = mux

	return &server
}

func (s *Server) reseedHandler(w http.ResponseWriter, r *http.Request) {
	peer := Peer(r.RemoteAddr)

	su3Bytes, err := s.Reseeder.PeerSu3Bytes(peer)
	if nil != err {
		http.Error(w, "500 Unable to get SU3", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Disposition", "attachment; filename=i2pseeds.su3")
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Length", strconv.FormatInt(int64(len(su3Bytes)), 10))

	io.Copy(w, bytes.NewReader(su3Bytes))
}

func loggingMiddleware(next http.Handler) http.Handler {
	return handlers.CombinedLoggingHandler(os.Stdout, next)
}

func verifyMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if I2P_USER_AGENT != r.UserAgent() {
			http.Error(w, "403 Forbidden", http.StatusForbidden)
			return
		}

		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func proxiedMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if prior, ok := r.Header["X-Forwarded-For"]; ok {
			r.RemoteAddr = prior[0]
		}

		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<commit_msg>disable keep alives<commit_after>package reseed

import (
	"bytes"
	"crypto/tls"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/PuerkitoBio/throttled"
	"github.com/PuerkitoBio/throttled/store"
	"github.com/gorilla/handlers"
	"github.com/justinas/alice"
)

const (
	I2P_USER_AGENT = "Wget/1.11.4"
)

type Server struct {
	*http.Server
	Reseeder Reseeder
}

func NewServer(prefix string, trustProxy bool) *Server {
	config := &tls.Config{
		MinVersion:               tls.VersionTLS10,
		PreferServerCipherSuites: true,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
			// tls.TLS_RSA_WITH_RC4_128_SHA,
			tls.TLS_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
			tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
		},
	}
	h := &http.Server{TLSConfig: config}
	server := Server{h, nil}

	th := throttled.RateLimit(throttled.PerHour(120), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(10000))

	middlewareChain := alice.New()
	if trustProxy {
		middlewareChain = middlewareChain.Append(proxiedMiddleware)
	}

	errorHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
		if _, err := w.Write(nil); nil != err {
			log.Println(err)
		}
	})

	mux := http.NewServeMux()
	mux.Handle("/", middlewareChain.Append(disableKeepAliveMiddleware, loggingMiddleware).Then(errorHandler))
	mux.Handle(prefix+"/i2pseeds.su3", middlewareChain.Append(disableKeepAliveMiddleware, loggingMiddleware, verifyMiddleware, th.Throttle).Then(http.HandlerFunc(server.reseedHandler)))
	server.Handler = mux

	return &server
}

func (s *Server) reseedHandler(w http.ResponseWriter, r *http.Request) {
	peer := Peer(r.RemoteAddr)

	su3Bytes, err := s.Reseeder.PeerSu3Bytes(peer)
	if nil != err {
		http.Error(w, "500 Unable to get SU3", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Disposition", "attachment; filename=i2pseeds.su3")
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Length", strconv.FormatInt(int64(len(su3Bytes)), 10))

	io.Copy(w, bytes.NewReader(su3Bytes))
}

func disableKeepAliveMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Connection", "close")
		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func loggingMiddleware(next http.Handler) http.Handler {
	return handlers.CombinedLoggingHandler(os.Stdout, next)
}

func verifyMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if I2P_USER_AGENT != r.UserAgent() {
			http.Error(w, "403 Forbidden", http.StatusForbidden)
			return
		}

		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func proxiedMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if prior, ok := r.Header["X-Forwarded-For"]; ok {
			r.RemoteAddr = prior[0]
		}

		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<|endoftext|>


<commit_before>package scm

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/libgit2/git2go"
)

// RootPath discovers the base directory for a git repo
func RootPath(path ...string) (string, error) {
	var (
		wd  string
		p   string
		err error
	)
	if len(path) > 0 {
		wd = path[0]
	} else {
		wd, err = os.Getwd()
		if err != nil {
			return "", err
		}
	}
	p, err = git.Discover(wd, false, []string{})
	if err != nil {
		return "", err
	}
	return filepath.ToSlash(filepath.Dir(filepath.Dir(p))), nil
}

// CommitIDs returns commit SHA1 IDs starting from the head up to the limit
func CommitIDs(limit int, wd ...string) ([]string, error) {
	var (
		repo *git.Repository
		cnt  int
		w    *git.RevWalk
		err  error
	)
	commits := []string{}

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}

	if err != nil {
		return commits, err
	}
	defer repo.Free()

	w, err = repo.Walk()
	if err != nil {
		return commits, err
	}
	defer w.Free()

	err = w.PushHead()
	if err != nil {
		return commits, err
	}

	err = w.Iterate(
		func(commit *git.Commit) bool {
			if limit == cnt {
				return false
			}
			commits = append(commits, commit.Object.Id().String())
			cnt++
			return true
		})

	if err != nil {
		return commits, err
	}

	return commits, nil
}

// Commit contains commit details
type Commit struct {
	ID      string
	OID     *git.Oid
	Summary string
	Message string
	Author  string
	Email   string
	When    time.Time
	Files   []string
}

// HeadCommit returns the latest commit
func HeadCommit(wd ...string) (Commit, error) {
	var (
		repo *git.Repository
		err  error
	)
	commit := Commit{}

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}
	if err != nil {
		return commit, err
	}
	defer repo.Free()

	headCommit, err := lookupHeadCommit(repo)
	if err != nil {
		if err == ErrHeadUnborn {
			return commit, nil
		}
		return commit, err
	}
	defer headCommit.Free()

	headTree, err := headCommit.Tree()
	if err != nil {
		return commit, err
	}
	defer headTree.Free()

	files := []string{}
	if headCommit.ParentCount() > 0 {
		parentTree, err := headCommit.Parent(0).Tree()
		if err != nil {
			return commit, err
		}
		defer parentTree.Free()

		options, err := git.DefaultDiffOptions()
		if err != nil {
			return commit, err
		}

		diff, err := headCommit.Owner().DiffTreeToTree(parentTree, headTree, &options)
		if err != nil {
			return commit, err
		}
		defer diff.Free()

		err = diff.ForEach(
			func(file git.DiffDelta, progress float64) (git.DiffForEachHunkCallback, error) {

				files = append(files, filepath.ToSlash(file.NewFile.Path))

				return func(hunk git.DiffHunk) (git.DiffForEachLineCallback, error) {
					return func(line git.DiffLine) error {
						return nil
					}, nil
				}, nil
			}, git.DiffDetailFiles)

		if err != nil {
			return commit, err
		}

	} else {

		path := ""
		err := headTree.Walk(
			func(s string, entry *git.TreeEntry) int {
				switch entry.Filemode {
				case git.FilemodeTree:
					path = filepath.ToSlash(entry.Name)
				default:
					files = append(files, filepath.Join(path, entry.Name))
				}
				return 0
			})

		if err != nil {
			return commit, err
		}
	}

	commit = Commit{
		ID:      headCommit.Object.Id().String(),
		OID:     headCommit.Object.Id(),
		Summary: headCommit.Summary(),
		Message: headCommit.Message(),
		Author:  headCommit.Author().Name,
		Email:   headCommit.Author().Email,
		When:    headCommit.Author().When,
		Files:   files}

	return commit, nil
}

// CreateNote creates a git note associated with the head commit
func CreateNote(noteTxt string, nameSpace string, wd ...string) error {
	var (
		repo *git.Repository
		err  error
	)

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}
	if err != nil {
		return err
	}
	defer repo.Free()

	headCommit, err := lookupHeadCommit(repo)
	if err != nil {
		return err
	}

	sig := &git.Signature{
		Name:  headCommit.Author().Name,
		Email: headCommit.Author().Email,
		When:  headCommit.Author().When,
	}

	_, err = repo.Notes.Create("refs/notes/"+nameSpace, sig, sig, headCommit.Id(), noteTxt, false)
	if err != nil {
		return err
	}

	return nil
}

// CommitNote contains a git note's details
type CommitNote struct {
	ID      string
	OID     *git.Oid
	Summary string
	Message string
	Author  string
	Email   string
	When    time.Time
	Note    string
}

// ReadNote returns a commit note for the SHA1 commit id
func ReadNote(commitID string, nameSpace string, wd ...string) (CommitNote, error) {
	var (
		err    error
		repo   *git.Repository
		commit *git.Commit
		n      *git.Note
	)

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}

	if err != nil {
		return CommitNote{}, err
	}

	defer func() {
		if commit != nil {
			commit.Free()
		}
		if n != nil {
			n.Free()
		}
		repo.Free()
	}()

	id, err := git.NewOid(commitID)
	if err != nil {
		return CommitNote{}, err
	}

	commit, err = repo.LookupCommit(id)
	if err != nil {
		return CommitNote{}, err
	}

	var noteTxt string
	n, err = repo.Notes.Read("refs/notes/"+nameSpace, id)
	if err != nil {
		noteTxt = ""
	} else {
		noteTxt = n.Message()
	}

	return CommitNote{
		ID:      commit.Object.Id().String(),
		OID:     commit.Object.Id(),
		Summary: commit.Summary(),
		Message: commit.Message(),
		Author:  commit.Author().Name,
		Email:   commit.Author().Email,
		When:    commit.Author().When,
		Note:    noteTxt,
	}, nil
}

// Config persists git configuration settings
func Config(settings map[string]string, wd ...string) error {
	var (
		err  error
		repo *git.Repository
		cfg  *git.Config
	)

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}

	cfg, err = repo.Config()
	defer cfg.Free()

	for k, v := range settings {
		err = cfg.SetString(k, v)
		if err != nil {
			return err
		}
	}
	return nil
}

// SetHooks creates git hooks
func SetHooks(hooks map[string]string, wd ...string) error {
	for hook, command := range hooks {
		var (
			p   string
			err error
		)

		if len(wd) > 0 {
			p = wd[0]
		} else {
			p, err = os.Getwd()
			if err != nil {
				return err
			}
		}
		fp := filepath.Join(p, ".git", "hooks", hook)

		var output string
		if _, err := os.Stat(fp); !os.IsNotExist(err) {
			b, err := ioutil.ReadFile(fp)
			if err != nil {
				return err
			}
			output = string(b)

			if strings.Contains(output, command+"\n") {
				// if file already exists this will make sure it's executable
				if err := os.Chmod(fp, 0755); err != nil {
					return err
				}
				return nil
			}
		}

		if err = ioutil.WriteFile(
			fp, []byte(fmt.Sprintf("%s\n%s\n", output, command)), 0755); err != nil {
			return err
		}
		// if file already exists this will make sure it's executable
		if err := os.Chmod(fp, 0755); err != nil {
			return err
		}
	}

	return nil
}

// Ignore persists paths/files to ignore for a git repo
func Ignore(ignore string, wd ...string) error {
	var (
		p   string
		err error
	)

	if len(wd) > 0 {
		p = wd[0]
	} else {
		p, err = os.Getwd()
		if err != nil {
			return err
		}
	}
	fp := filepath.Join(p, ".gitignore")

	var output string
	if _, err := os.Stat(fp); !os.IsNotExist(err) {
		b, err := ioutil.ReadFile(fp)
		if err != nil {
			return err
		}
		output = string(b)

		if strings.Contains(output, ignore+"\n") {
			return nil
		}
	}

	if err = ioutil.WriteFile(
		fp, []byte(fmt.Sprintf("%s\n%s\n", output, ignore)), 0644); err != nil {
		return err
	}
	return nil
}

func openRepository(wd ...string) (*git.Repository, error) {
	var (
		p   string
		err error
	)

	if len(wd) > 0 {
		p, err = RootPath(wd[0])
	} else {
		p, err = RootPath()
	}
	if err != nil {
		return nil, err
	}

	repo, err := git.OpenRepository(p)
	return repo, err
}

var (
	// ErrHeadUnborn is raised when there are no commits yet in the git repo
	ErrHeadUnborn = errors.New("Head commit not found")
)

func lookupHeadCommit(repo *git.Repository) (*git.Commit, error) {

	headUnborn, err := repo.IsHeadUnborn()
	if err != nil {
		return nil, err
	}
	if headUnborn {
		return nil, ErrHeadUnborn
	}

	headRef, err := repo.Head()
	if err != nil {
		return nil, err
	}
	defer headRef.Free()

	commit, err := repo.LookupCommit(headRef.Target())
	if err != nil {
		return nil, err
	}

	return commit, nil
}

// Status contains the git file statuses
type Status struct {
	Files []fileStatus
}

// NewStatus create a Status struct for a git repo
func NewStatus(wd ...string) (Status, error) {
	var (
		repo *git.Repository
		err  error
	)
	status := Status{}

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}
	if err != nil {
		return status, err
	}
	defer repo.Free()

	//TODO: research what status options to set
	opts := &git.StatusOptions{}
	opts.Show = git.StatusShowIndexAndWorkdir
	opts.Flags = git.StatusOptIncludeUntracked | git.StatusOptRenamesHeadToIndex | git.StatusOptSortCaseSensitively
	statusList, err := repo.StatusList(opts)

	if err != nil {
		return status, err
	}
	defer statusList.Free()

	cnt, err := statusList.EntryCount()
	if err != nil {
		return status, err
	}

	for i := 0; i < cnt; i++ {
		entry, err := statusList.ByIndex(i)
		if err != nil {
			return status, err
		}
		status.AddFile(entry)
	}

	return status, nil
}

// AddFile adds a StatusEntry for each file in working and staging directories
func (s *Status) AddFile(e git.StatusEntry) {
	var path string
	if e.Status == git.StatusIndexNew ||
		e.Status == git.StatusIndexModified ||
		e.Status == git.StatusIndexDeleted ||
		e.Status == git.StatusIndexRenamed ||
		e.Status == git.StatusIndexTypeChange {
		path = filepath.ToSlash(e.HeadToIndex.NewFile.Path)
	} else {
		path = filepath.ToSlash(e.IndexToWorkdir.NewFile.Path)
	}
	s.Files = append(s.Files, fileStatus{Path: path, Status: e.Status})
}

// HasStaged returns true if there are any files in staging
func (s *Status) HasStaged() bool {
	for _, f := range s.Files {
		if f.InStaging() {
			return true
		}
	}
	return false
}

// IsModified returns true if the file is modified in either working or staging
func (s *Status) IsModified(path string, staging bool) bool {
	path = filepath.ToSlash(path)
	for _, f := range s.Files {
		if path == f.Path && f.InStaging() == staging {
			return f.IsModified()
		}
	}
	return false
}

// IsTracked returns true if file is tracked by the git repo
func (s *Status) IsTracked(path string) bool {
	path = filepath.ToSlash(path)
	for _, f := range s.Files {
		if path == f.Path {
			return f.IsTracked()
		}
	}
	return false
}

type fileStatus struct {
	Status git.Status
	Path   string
}

// InStaging returns true if the file is in staging
func (f fileStatus) InStaging() bool {
	return f.Status == git.StatusIndexNew ||
		f.Status == git.StatusIndexModified ||
		f.Status == git.StatusIndexDeleted ||
		f.Status == git.StatusIndexRenamed ||
		f.Status == git.StatusIndexTypeChange
}

// InWorking returns true if the file is in working
func (f fileStatus) InWorking() bool {
	return f.Status == git.StatusWtModified ||
		f.Status == git.StatusWtDeleted ||
		f.Status == git.StatusWtRenamed ||
		f.Status == git.StatusWtTypeChange
}

// IsTracked returns true if the file is tracked by git
func (f fileStatus) IsTracked() bool {
	return f.Status != git.StatusIgnored &&
		f.Status != git.StatusWtNew
}

// IsModified returns true if the file has been modified
func (f fileStatus) IsModified() bool {
	return f.InStaging() || f.InWorking()
}
<commit_msg>Add shebang to post commit hook<commit_after>package scm

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/libgit2/git2go"
)

// RootPath discovers the base directory for a git repo
func RootPath(path ...string) (string, error) {
	var (
		wd  string
		p   string
		err error
	)
	if len(path) > 0 {
		wd = path[0]
	} else {
		wd, err = os.Getwd()
		if err != nil {
			return "", err
		}
	}
	p, err = git.Discover(wd, false, []string{})
	if err != nil {
		return "", err
	}
	return filepath.ToSlash(filepath.Dir(filepath.Dir(p))), nil
}

// CommitIDs returns commit SHA1 IDs starting from the head up to the limit
func CommitIDs(limit int, wd ...string) ([]string, error) {
	var (
		repo *git.Repository
		cnt  int
		w    *git.RevWalk
		err  error
	)
	commits := []string{}

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}

	if err != nil {
		return commits, err
	}
	defer repo.Free()

	w, err = repo.Walk()
	if err != nil {
		return commits, err
	}
	defer w.Free()

	err = w.PushHead()
	if err != nil {
		return commits, err
	}

	err = w.Iterate(
		func(commit *git.Commit) bool {
			if limit == cnt {
				return false
			}
			commits = append(commits, commit.Object.Id().String())
			cnt++
			return true
		})

	if err != nil {
		return commits, err
	}

	return commits, nil
}

// Commit contains commit details
type Commit struct {
	ID      string
	OID     *git.Oid
	Summary string
	Message string
	Author  string
	Email   string
	When    time.Time
	Files   []string
}

// HeadCommit returns the latest commit
func HeadCommit(wd ...string) (Commit, error) {
	var (
		repo *git.Repository
		err  error
	)
	commit := Commit{}

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}
	if err != nil {
		return commit, err
	}
	defer repo.Free()

	headCommit, err := lookupHeadCommit(repo)
	if err != nil {
		if err == ErrHeadUnborn {
			return commit, nil
		}
		return commit, err
	}
	defer headCommit.Free()

	headTree, err := headCommit.Tree()
	if err != nil {
		return commit, err
	}
	defer headTree.Free()

	files := []string{}
	if headCommit.ParentCount() > 0 {
		parentTree, err := headCommit.Parent(0).Tree()
		if err != nil {
			return commit, err
		}
		defer parentTree.Free()

		options, err := git.DefaultDiffOptions()
		if err != nil {
			return commit, err
		}

		diff, err := headCommit.Owner().DiffTreeToTree(parentTree, headTree, &options)
		if err != nil {
			return commit, err
		}
		defer diff.Free()

		err = diff.ForEach(
			func(file git.DiffDelta, progress float64) (git.DiffForEachHunkCallback, error) {

				files = append(files, filepath.ToSlash(file.NewFile.Path))

				return func(hunk git.DiffHunk) (git.DiffForEachLineCallback, error) {
					return func(line git.DiffLine) error {
						return nil
					}, nil
				}, nil
			}, git.DiffDetailFiles)

		if err != nil {
			return commit, err
		}

	} else {

		path := ""
		err := headTree.Walk(
			func(s string, entry *git.TreeEntry) int {
				switch entry.Filemode {
				case git.FilemodeTree:
					path = filepath.ToSlash(entry.Name)
				default:
					files = append(files, filepath.Join(path, entry.Name))
				}
				return 0
			})

		if err != nil {
			return commit, err
		}
	}

	commit = Commit{
		ID:      headCommit.Object.Id().String(),
		OID:     headCommit.Object.Id(),
		Summary: headCommit.Summary(),
		Message: headCommit.Message(),
		Author:  headCommit.Author().Name,
		Email:   headCommit.Author().Email,
		When:    headCommit.Author().When,
		Files:   files}

	return commit, nil
}

// CreateNote creates a git note associated with the head commit
func CreateNote(noteTxt string, nameSpace string, wd ...string) error {
	var (
		repo *git.Repository
		err  error
	)

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}
	if err != nil {
		return err
	}
	defer repo.Free()

	headCommit, err := lookupHeadCommit(repo)
	if err != nil {
		return err
	}

	sig := &git.Signature{
		Name:  headCommit.Author().Name,
		Email: headCommit.Author().Email,
		When:  headCommit.Author().When,
	}

	_, err = repo.Notes.Create("refs/notes/"+nameSpace, sig, sig, headCommit.Id(), noteTxt, false)
	if err != nil {
		return err
	}

	return nil
}

// CommitNote contains a git note's details
type CommitNote struct {
	ID      string
	OID     *git.Oid
	Summary string
	Message string
	Author  string
	Email   string
	When    time.Time
	Note    string
}

// ReadNote returns a commit note for the SHA1 commit id
func ReadNote(commitID string, nameSpace string, wd ...string) (CommitNote, error) {
	var (
		err    error
		repo   *git.Repository
		commit *git.Commit
		n      *git.Note
	)

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}

	if err != nil {
		return CommitNote{}, err
	}

	defer func() {
		if commit != nil {
			commit.Free()
		}
		if n != nil {
			n.Free()
		}
		repo.Free()
	}()

	id, err := git.NewOid(commitID)
	if err != nil {
		return CommitNote{}, err
	}

	commit, err = repo.LookupCommit(id)
	if err != nil {
		return CommitNote{}, err
	}

	var noteTxt string
	n, err = repo.Notes.Read("refs/notes/"+nameSpace, id)
	if err != nil {
		noteTxt = ""
	} else {
		noteTxt = n.Message()
	}

	return CommitNote{
		ID:      commit.Object.Id().String(),
		OID:     commit.Object.Id(),
		Summary: commit.Summary(),
		Message: commit.Message(),
		Author:  commit.Author().Name,
		Email:   commit.Author().Email,
		When:    commit.Author().When,
		Note:    noteTxt,
	}, nil
}

// Config persists git configuration settings
func Config(settings map[string]string, wd ...string) error {
	var (
		err  error
		repo *git.Repository
		cfg  *git.Config
	)

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}

	cfg, err = repo.Config()
	defer cfg.Free()

	for k, v := range settings {
		err = cfg.SetString(k, v)
		if err != nil {
			return err
		}
	}
	return nil
}

// SetHooks creates git hooks
func SetHooks(hooks map[string]string, wd ...string) error {
	const shebang = "#!/bin/sh"
	for hook, command := range hooks {
		var (
			p   string
			err error
		)

		if len(wd) > 0 {
			p = wd[0]
		} else {
			p, err = os.Getwd()
			if err != nil {
				return err
			}
		}
		fp := filepath.Join(p, ".git", "hooks", hook)

		var output string
		if _, err := os.Stat(fp); !os.IsNotExist(err) {
			b, err := ioutil.ReadFile(fp)
			if err != nil {
				return err
			}
			output = string(b)
		}

		if !strings.Contains(output, shebang) {
			output = fmt.Sprintf("%s\n%s", shebang, output)
		}

		if !strings.Contains(output, command) {
			output = fmt.Sprintf("%s\n%s\n", output, command)
		}

		if err = ioutil.WriteFile(fp, []byte(output), 0755); err != nil {
			return err
		}

		if err := os.Chmod(fp, 0755); err != nil {
			return err
		}
	}

	return nil
}

// Ignore persists paths/files to ignore for a git repo
func Ignore(ignore string, wd ...string) error {
	var (
		p   string
		err error
	)

	if len(wd) > 0 {
		p = wd[0]
	} else {
		p, err = os.Getwd()
		if err != nil {
			return err
		}
	}
	fp := filepath.Join(p, ".gitignore")

	var output string
	if _, err := os.Stat(fp); !os.IsNotExist(err) {
		b, err := ioutil.ReadFile(fp)
		if err != nil {
			return err
		}
		output = string(b)

		if strings.Contains(output, ignore+"\n") {
			return nil
		}
	}

	if err = ioutil.WriteFile(
		fp, []byte(fmt.Sprintf("%s\n%s\n", output, ignore)), 0644); err != nil {
		return err
	}
	return nil
}

func openRepository(wd ...string) (*git.Repository, error) {
	var (
		p   string
		err error
	)

	if len(wd) > 0 {
		p, err = RootPath(wd[0])
	} else {
		p, err = RootPath()
	}
	if err != nil {
		return nil, err
	}

	repo, err := git.OpenRepository(p)
	return repo, err
}

var (
	// ErrHeadUnborn is raised when there are no commits yet in the git repo
	ErrHeadUnborn = errors.New("Head commit not found")
)

func lookupHeadCommit(repo *git.Repository) (*git.Commit, error) {

	headUnborn, err := repo.IsHeadUnborn()
	if err != nil {
		return nil, err
	}
	if headUnborn {
		return nil, ErrHeadUnborn
	}

	headRef, err := repo.Head()
	if err != nil {
		return nil, err
	}
	defer headRef.Free()

	commit, err := repo.LookupCommit(headRef.Target())
	if err != nil {
		return nil, err
	}

	return commit, nil
}

// Status contains the git file statuses
type Status struct {
	Files []fileStatus
}

// NewStatus create a Status struct for a git repo
func NewStatus(wd ...string) (Status, error) {
	var (
		repo *git.Repository
		err  error
	)
	status := Status{}

	if len(wd) > 0 {
		repo, err = openRepository(wd[0])
	} else {
		repo, err = openRepository()
	}
	if err != nil {
		return status, err
	}
	defer repo.Free()

	//TODO: research what status options to set
	opts := &git.StatusOptions{}
	opts.Show = git.StatusShowIndexAndWorkdir
	opts.Flags = git.StatusOptIncludeUntracked | git.StatusOptRenamesHeadToIndex | git.StatusOptSortCaseSensitively
	statusList, err := repo.StatusList(opts)

	if err != nil {
		return status, err
	}
	defer statusList.Free()

	cnt, err := statusList.EntryCount()
	if err != nil {
		return status, err
	}

	for i := 0; i < cnt; i++ {
		entry, err := statusList.ByIndex(i)
		if err != nil {
			return status, err
		}
		status.AddFile(entry)
	}

	return status, nil
}

// AddFile adds a StatusEntry for each file in working and staging directories
func (s *Status) AddFile(e git.StatusEntry) {
	var path string
	if e.Status == git.StatusIndexNew ||
		e.Status == git.StatusIndexModified ||
		e.Status == git.StatusIndexDeleted ||
		e.Status == git.StatusIndexRenamed ||
		e.Status == git.StatusIndexTypeChange {
		path = filepath.ToSlash(e.HeadToIndex.NewFile.Path)
	} else {
		path = filepath.ToSlash(e.IndexToWorkdir.NewFile.Path)
	}
	s.Files = append(s.Files, fileStatus{Path: path, Status: e.Status})
}

// HasStaged returns true if there are any files in staging
func (s *Status) HasStaged() bool {
	for _, f := range s.Files {
		if f.InStaging() {
			return true
		}
	}
	return false
}

// IsModified returns true if the file is modified in either working or staging
func (s *Status) IsModified(path string, staging bool) bool {
	path = filepath.ToSlash(path)
	for _, f := range s.Files {
		if path == f.Path && f.InStaging() == staging {
			return f.IsModified()
		}
	}
	return false
}

// IsTracked returns true if file is tracked by the git repo
func (s *Status) IsTracked(path string) bool {
	path = filepath.ToSlash(path)
	for _, f := range s.Files {
		if path == f.Path {
			return f.IsTracked()
		}
	}
	return false
}

type fileStatus struct {
	Status git.Status
	Path   string
}

// InStaging returns true if the file is in staging
func (f fileStatus) InStaging() bool {
	return f.Status == git.StatusIndexNew ||
		f.Status == git.StatusIndexModified ||
		f.Status == git.StatusIndexDeleted ||
		f.Status == git.StatusIndexRenamed ||
		f.Status == git.StatusIndexTypeChange
}

// InWorking returns true if the file is in working
func (f fileStatus) InWorking() bool {
	return f.Status == git.StatusWtModified ||
		f.Status == git.StatusWtDeleted ||
		f.Status == git.StatusWtRenamed ||
		f.Status == git.StatusWtTypeChange
}

// IsTracked returns true if the file is tracked by git
func (f fileStatus) IsTracked() bool {
	return f.Status != git.StatusIgnored &&
		f.Status != git.StatusWtNew
}

// IsModified returns true if the file has been modified
func (f fileStatus) IsModified() bool {
	return f.InStaging() || f.InWorking()
}
<|endoftext|>


<commit_before>package tests

import (
	"socialapi/models"
	"socialapi/rest"
	"socialapi/workers/common/tests"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
	"labix.org/v2/mgo/bson"
)

func TestmoderationSetting(t *testing.T) {
	var AccountOldId = bson.NewObjectId()
	Convey("while testing troll mode", t, func() {
		Convey("First Create User", func() {
			account := models.NewAccount()
			account.OldId = AccountOldId.Hex()
			account, err := rest.CreateAccount(account)
			tests.ResultedWithNoErrorCheck(account, err)

			Convey("then we should be able to mark as troll", func() {
				res := rest.MarkAsTroll(account)
				So(res, ShouldBeNil)
				Convey("shold be able to mark as troll twice", func() {
					res := rest.MarkAsTroll(account)
					So(res, ShouldBeNil)
				})
			})

			Convey("should be able to unmark as troll", func() {
				res := rest.UnMarkAsTroll(account)
				So(res, ShouldBeNil)
				Convey("should be able to unmark as troll twice", func() {
					res := rest.UnMarkAsTroll(account)
					So(res, ShouldBeNil)
				})
			})
		})
	})
}
<commit_msg>Socialapi: added integration tests for moderation feature<commit_after>package tests

import (
	"koding/db/mongodb/modelhelper"
	"math/rand"
	"socialapi/models"
	"socialapi/request"
	"socialapi/rest"
	"socialapi/workers/common/runner"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestModeration(t *testing.T) {
	r := runner.New("test-moderation")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	rand.Seed(time.Now().UTC().UnixNano())
	modelhelper.Initialize(r.Conf.Mongo)
	defer modelhelper.Close()

	Convey("While creating a link to a channel", t, func() {
		// create admin
		admin, err := models.CreateAccountInBothDbs()
		So(err, ShouldBeNil)
		So(admin, ShouldNotBeNil)

		// create another account
		acc2, err := models.CreateAccountInBothDbs()
		So(err, ShouldBeNil)
		So(acc2, ShouldNotBeNil)

		// create root channel with second acc
		root, err := rest.CreateChannelWithType(acc2.Id, models.Channel_TYPE_TOPIC)
		So(err, ShouldBeNil)
		So(root, ShouldNotBeNil)

		// create leaf channel with second acc
		leaf, err := rest.CreateChannelWithType(acc2.Id, models.Channel_TYPE_TOPIC)
		So(err, ShouldBeNil)
		So(leaf, ShouldNotBeNil)

		// create leaf2 channel with second acc
		leaf2, err := rest.CreateChannelWithType(acc2.Id, models.Channel_TYPE_TOPIC)
		So(err, ShouldBeNil)
		So(leaf2, ShouldNotBeNil)

		// fetch admin's session
		ses, err := models.FetchOrCreateSession(admin.Nick)
		So(err, ShouldBeNil)
		So(ses, ShouldNotBeNil)

		Convey("We should be able to create it first", func() {
			res, err := rest.CreateLink(root.Id, leaf.Id, ses.ClientId)
			So(err, ShouldBeNil)
			So(res, ShouldNotBeNil)

			Convey("We should get error if we try to create the same link again", func() {
				res, err := rest.CreateLink(root.Id, leaf.Id, ses.ClientId)
				So(err, ShouldNotBeNil)
				So(res, ShouldBeNil)
			})

			Convey("We should not be able to list with non set root id", func() {
				links, err := rest.GetLinks(0, request.NewQuery(), ses.ClientId)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, models.ErrChannelIsNotSet.Error())
				So(links, ShouldBeNil)
			})

			Convey("We should be able to list the linked channels", func() {
				res, err := rest.CreateLink(root.Id, leaf2.Id, ses.ClientId)
				So(err, ShouldBeNil)
				So(res, ShouldNotBeNil)

				links, err := rest.GetLinks(root.Id, request.NewQuery(), ses.ClientId)
				So(err, ShouldBeNil)
				So(links, ShouldNotBeNil)
				So(len(links), ShouldEqual, 2)
			})

			Convey("We should be able to unlink created link", func() {
				err = rest.UnLink(root.Id, leaf.Id, ses.ClientId)
				So(err, ShouldBeNil)
			})

			Convey("We should not be able to unlink with non-set root id", func() {
				err = rest.UnLink(0, rand.Int63(), ses.ClientId)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, models.ErrChannelIsNotSet.Error())
			})

			Convey("We should not be able to unlink with non-set leaf id", func() {
				err = rest.UnLink(rand.Int63(), 0, ses.ClientId)
				So(err, ShouldNotBeNil)
				So(err.Error(), ShouldContainSubstring, models.ErrLeafIsNotSet.Error())
			})

			Convey("We should not be able to unlink non existing leaf", func() {
				err = rest.UnLink(root.Id, rand.Int63(), ses.ClientId)
				So(err, ShouldNotBeNil)
			})

			Convey("We should not be able to unlink from non existing root", func() {
				err = rest.UnLink(rand.Int63(), leaf.Id, ses.ClientId)
				So(err, ShouldNotBeNil)
			})
		})

		Convey("We should be able to blacklist channel without any leaves", func() {
			So(rest.BlackList(root.Id, ses.ClientId), ShouldBeNil)
		})

		Convey("We should not be able to blacklist channel with leaves", func() {
			res, err := rest.CreateLink(root.Id, leaf.Id, ses.ClientId)
			So(err, ShouldBeNil)
			So(res, ShouldNotBeNil)

			err = rest.BlackList(root.Id, ses.ClientId)
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, models.ErrChannelHasLeaves.Error())
		})
	})
}
<|endoftext|>