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

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"log"

	"code.google.com/p/go.crypto/openpgp"
	"code.google.com/p/go.crypto/openpgp/armor"
	"code.google.com/p/gopass"
)

var DefaultSecureRingPath = "~/.gnupg/secring.gpg"
var DefaultPrompt = "password: "

func main() {
	directoryRootPtr := flag.String("s", "", "Directory")
	grepStringPtr := flag.String("g", "", "Regex String")
	flag.Parse()

	if *directoryRootPtr == "" {
		Usage()
		fmt.Println("Root directory must be specified")
		os.Exit(1)
	}

	ctx := NewSecureContext(
		DefaultSecureRingPath,
		*directoryRootPtr,
	)

	err := ctx.ReadKeyRing()
	if err != nil {
		log.Fatal(err)
		return
	}

	_, err = ctx.GetPassword()
	if err != nil {
		log.Fatal(err)
		return
	}

	err = ctx.FindRegex(*grepStringPtr)
	if err != nil {
		log.Fatal(err)
		return
	}
}

type SecureContext struct {
	SecureRingPath string
	DirectoryRoot  string

	PrivateRing openpgp.EntityList
	Password    string

	SearchRegex *regexp.Regexp
}

func NewSecureContext(secureRingPath, directoryRoot string) *SecureContext {
	return &SecureContext{
		SecureRingPath: secureRingPath,
		DirectoryRoot:  directoryRoot,
	}
}

func (ctx *SecureContext) GetPassword() (string, error) {
	var err error
	ctx.Password, err = gopass.GetPass(DefaultPrompt)
	if err != nil {
		log.Fatal(err)
		return "", err
	}
	return ctx.Password, nil
}

func (ctx *SecureContext) ReadKeyRing() error {
	secringPath, _ := expandPath(ctx.SecureRingPath)
	privringFile, err := os.Open(secringPath)
	if err != nil {
		return err
	}
	defer privringFile.Close()

	ctx.PrivateRing, err = openpgp.ReadKeyRing(privringFile)
	if err != nil {
		return err
	}

	return nil
}

func (ctx *SecureContext) FindRegex(regexStr string) error {
	var regex *regexp.Regexp
	var err error

	if len(regexStr) > 0 {
		regex, err = regexp.Compile(regexStr)
		if err != nil {
			return err
		}
	}

	fileCallback := func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if fi.IsDir() {
			return nil
		}
		if filepath.Ext(fi.Name()) != ".gpg" {
			return nil
		}

		md, err := ctx.DecryptFile(path, regex)
		if err != nil {
			return err
		}

		if regex != nil {
			newWriteLine := func(path string) func(uint64, string) {
				first := true
				return func(lineNumber uint64, line string) {
					if first {
						fmt.Println(path)
						first = false
					}
					fmt.Printf("%v:%v\n", lineNumber, line)
				}
			}

			foundMatch := false
			lineNumber := uint64(0)
			writeLine := newWriteLine(path)
			scanner := bufio.NewScanner(md.UnverifiedBody)
			for scanner.Scan() {
				lineNumber++
				line := scanner.Text()
				if regex.Match([]byte(line)) {
					foundMatch = true
					writeLine(lineNumber, line)
				}
			}

			if foundMatch {
				fmt.Println()
			}
		} else {
			io.Copy(os.Stdout, md.UnverifiedBody)
		}

		return nil
	}

	filesPath := path.Join(ctx.DirectoryRoot, "files")
	if err = filepath.Walk(filesPath, fileCallback); err != nil {
		return err
	}
	return nil
}

func (ctx *SecureContext) GetKeyByEmail(emailAddress string) *openpgp.Entity {
	for _, entity := range ctx.PrivateRing {
		for _, ident := range entity.Identities {
			if ident.UserId.Email == emailAddress {
				return entity
			}
		}
	}
	return nil
}

func (ctx *SecureContext) DecryptFile(filePath string, regex *regexp.Regexp) (*openpgp.MessageDetails, error) {
	secfile, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer secfile.Close()

	block, err := armor.Decode(secfile)
	if err != nil {
		return nil, err
	}

	promptCallback := func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
		for _, k := range keys {
			err := k.PrivateKey.Decrypt([]byte(ctx.Password))
			if err != nil {
				return nil, err
			}
			return nil, nil
		}
		return nil, errors.New("invalid password or no private key")
	}

	return openpgp.ReadMessage(block.Body, ctx.PrivateRing, promptCallback, nil)
}

func expandPath(p string) (string, error) {
	if path.IsAbs(p) {
		return p, nil
	}
	if p[:2] == "~/" {
		usr, err := user.Current()
		if err != nil {
			return "", err
		}
		p = strings.Replace(p, "~", usr.HomeDir, 1)
	}
	return p, nil
}

var Usage = func() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	flag.PrintDefaults()
}
<commit_msg>fix copyright from my copy/paste of the license<commit_after>// Copyright 2014 Ryan Phillips. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"log"

	"code.google.com/p/go.crypto/openpgp"
	"code.google.com/p/go.crypto/openpgp/armor"
	"code.google.com/p/gopass"
)

var DefaultSecureRingPath = "~/.gnupg/secring.gpg"
var DefaultPrompt = "password: "

func main() {
	directoryRootPtr := flag.String("s", "", "Directory")
	grepStringPtr := flag.String("g", "", "Regex String")
	flag.Parse()

	if *directoryRootPtr == "" {
		Usage()
		fmt.Println("Root directory must be specified")
		os.Exit(1)
	}

	ctx := NewSecureContext(
		DefaultSecureRingPath,
		*directoryRootPtr,
	)

	err := ctx.ReadKeyRing()
	if err != nil {
		log.Fatal(err)
		return
	}

	_, err = ctx.GetPassword()
	if err != nil {
		log.Fatal(err)
		return
	}

	err = ctx.FindRegex(*grepStringPtr)
	if err != nil {
		log.Fatal(err)
		return
	}
}

type SecureContext struct {
	SecureRingPath string
	DirectoryRoot  string

	PrivateRing openpgp.EntityList
	Password    string

	SearchRegex *regexp.Regexp
}

func NewSecureContext(secureRingPath, directoryRoot string) *SecureContext {
	return &SecureContext{
		SecureRingPath: secureRingPath,
		DirectoryRoot:  directoryRoot,
	}
}

func (ctx *SecureContext) GetPassword() (string, error) {
	var err error
	ctx.Password, err = gopass.GetPass(DefaultPrompt)
	if err != nil {
		log.Fatal(err)
		return "", err
	}
	return ctx.Password, nil
}

func (ctx *SecureContext) ReadKeyRing() error {
	secringPath, _ := expandPath(ctx.SecureRingPath)
	privringFile, err := os.Open(secringPath)
	if err != nil {
		return err
	}
	defer privringFile.Close()

	ctx.PrivateRing, err = openpgp.ReadKeyRing(privringFile)
	if err != nil {
		return err
	}

	return nil
}

func (ctx *SecureContext) FindRegex(regexStr string) error {
	var regex *regexp.Regexp
	var err error

	if len(regexStr) > 0 {
		regex, err = regexp.Compile(regexStr)
		if err != nil {
			return err
		}
	}

	fileCallback := func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if fi.IsDir() {
			return nil
		}
		if filepath.Ext(fi.Name()) != ".gpg" {
			return nil
		}

		md, err := ctx.DecryptFile(path, regex)
		if err != nil {
			return err
		}

		if regex != nil {
			newWriteLine := func(path string) func(uint64, string) {
				first := true
				return func(lineNumber uint64, line string) {
					if first {
						fmt.Println(path)
						first = false
					}
					fmt.Printf("%v:%v\n", lineNumber, line)
				}
			}

			foundMatch := false
			lineNumber := uint64(0)
			writeLine := newWriteLine(path)
			scanner := bufio.NewScanner(md.UnverifiedBody)
			for scanner.Scan() {
				lineNumber++
				line := scanner.Text()
				if regex.Match([]byte(line)) {
					foundMatch = true
					writeLine(lineNumber, line)
				}
			}

			if foundMatch {
				fmt.Println()
			}
		} else {
			io.Copy(os.Stdout, md.UnverifiedBody)
		}

		return nil
	}

	filesPath := path.Join(ctx.DirectoryRoot, "files")
	if err = filepath.Walk(filesPath, fileCallback); err != nil {
		return err
	}
	return nil
}

func (ctx *SecureContext) GetKeyByEmail(emailAddress string) *openpgp.Entity {
	for _, entity := range ctx.PrivateRing {
		for _, ident := range entity.Identities {
			if ident.UserId.Email == emailAddress {
				return entity
			}
		}
	}
	return nil
}

func (ctx *SecureContext) DecryptFile(filePath string, regex *regexp.Regexp) (*openpgp.MessageDetails, error) {
	secfile, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer secfile.Close()

	block, err := armor.Decode(secfile)
	if err != nil {
		return nil, err
	}

	promptCallback := func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
		for _, k := range keys {
			err := k.PrivateKey.Decrypt([]byte(ctx.Password))
			if err != nil {
				return nil, err
			}
			return nil, nil
		}
		return nil, errors.New("invalid password or no private key")
	}

	return openpgp.ReadMessage(block.Body, ctx.PrivateRing, promptCallback, nil)
}

func expandPath(p string) (string, error) {
	if path.IsAbs(p) {
		return p, nil
	}
	if p[:2] == "~/" {
		usr, err := user.Current()
		if err != nil {
			return "", err
		}
		p = strings.Replace(p, "~", usr.HomeDir, 1)
	}
	return p, nil
}

var Usage = func() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	flag.PrintDefaults()
}
<|endoftext|>


<commit_before><commit_msg>added listener pool example<commit_after><|endoftext|>


<commit_before>package jsh

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/asaskevich/govalidator"
)

// Object represents the default JSON spec for objects
type Object struct {
	Type          string             `json:"type" valid:"alpha,required"`
	ID            string             `json:"id"`
	Attributes    json.RawMessage    `json:"attributes,omitempty"`
	Links         map[string]*Link   `json:"links,omitempty"`
	Relationships map[string]*Object `json:"relationships,omitempty"`
	// Status is the HTTP Status Code that should be associated with the object
	// when it is sent.
	Status int `json:"-"`
}

// NewObject prepares a new JSON Object for an API response. Whatever is provided
// as attributes will be marshalled to JSON.
func NewObject(id string, resourceType string, attributes interface{}) (*Object, *Error) {
	object := &Object{
		ID:            id,
		Type:          resourceType,
		Links:         map[string]*Link{},
		Relationships: map[string]*Object{},
	}

	rawJSON, err := json.MarshalIndent(attributes, "", " ")
	if err != nil {
		return nil, ISE(fmt.Sprintf("Error marshaling attrs while creating a new JSON Object: %s", err))
	}

	object.Attributes = rawJSON
	return object, nil
}

/*
Unmarshal puts an Object's Attributes into a more useful target resourceType defined
by the user. A correct object resourceType specified must also be provided otherwise
an error is returned to prevent hard to track down situations.

Optionally, used https://github.com/go-validator/validator for request input validation.
Simply define your struct with valid input tags:

	struct {
		Username string `json:"username" valid:"required,alphanum"`
	}

As the final action, the Unmarshal function will run govalidator on the unmarshal
result. If the validator fails, a Sendable error response of HTTP Status 422 will
be returned containing each validation error with a populated Error.Source.Pointer
specifying each struct attribute that failed. In this case, all you need to do is:

	errors := obj.Unmarshal("mytype", &myType)
	if errors != nil {
		// log errors via error.ISE
		jsh.Send(w, r, errors)
	}
*/
func (o *Object) Unmarshal(resourceType string, target interface{}) ErrorList {

	if resourceType != o.Type {
		return []*Error{ISE(fmt.Sprintf(
			"Expected type %s, when converting actual type: %s",
			resourceType,
			o.Type,
		))}
	}

	jsonErr := json.Unmarshal(o.Attributes, target)
	if jsonErr != nil {
		return []*Error{ISE(fmt.Sprintf(
			"For type '%s' unable to marshal: %s\nError:%s",
			resourceType,
			string(o.Attributes),
			jsonErr.Error(),
		))}
	}

	return validateInput(target)
}

/*
Marshal allows you to load a modified payload back into an object to preserve
all of the data it has.
*/
func (o *Object) Marshal(attributes interface{}) *Error {
	raw, err := json.MarshalIndent(attributes, "", " ")
	if err != nil {
		return ISE(fmt.Sprintf("Error marshaling attrs while creating a new JSON Object: %s", err))
	}

	o.Attributes = raw
	return nil
}

/*
Validate ensures that an object is JSON API compatible. Has a side effect of also
setting the Object's Status attribute to be used as the Response HTTP Code if one
has not already been set.
*/
func (o *Object) Validate(r *http.Request, response bool) *Error {

	if o.ID == "" {

		// don't error if the client is attempting to performing a POST request, in
		// which case, ID shouldn't actually be set
		if !response && r.Method != "POST" {
			return SpecificationError("ID must be set for Object response")
		}
	}

	if o.Type == "" {
		return SpecificationError("Type must be set for Object response")
	}

	switch r.Method {
	case "POST":
		acceptable := map[int]bool{201: true, 202: true, 204: true}

		if o.Status != 0 {
			if _, validCode := acceptable[o.Status]; !validCode {
				return SpecificationError("POST Status must be one of 201, 202, or 204.")
			}
			break
		}

		o.Status = http.StatusCreated
		break
	case "PATCH":
		acceptable := map[int]bool{200: true, 202: true, 204: true}

		if o.Status != 0 {
			if _, validCode := acceptable[o.Status]; !validCode {
				return SpecificationError("PATCH Status must be one of 200, 202, or 204.")
			}
			break
		}

		o.Status = http.StatusOK
		break
	case "GET":
		o.Status = http.StatusOK
		break
	// If we hit this it means someone is attempting to use an unsupported HTTP
	// method. Return a 406 error instead
	default:
		return SpecificationError(fmt.Sprintf(
			"The JSON Specification does not accept '%s' requests.",
			r.Method,
		))
	}

	return nil
}

// String prints a formatted string representation of the object
func (o *Object) String() string {
	raw, err := json.MarshalIndent(o, "", " ")
	if err != nil {
		return err.Error()
	}

	return string(raw)
}

// validateInput runs go-validator on each attribute on the struct and returns all
// errors that it picks up
func validateInput(target interface{}) ErrorList {

	_, validationError := govalidator.ValidateStruct(target)
	if validationError != nil {

		errorList, isType := validationError.(govalidator.Errors)
		if isType {

			errors := ErrorList{}
			for _, singleErr := range errorList.Errors() {

				// parse out validation error
				goValidErr, _ := singleErr.(govalidator.Error)
				inputErr := InputError(goValidErr.Err.Error(), goValidErr.Name)

				errors = append(errors, inputErr)
			}

			return errors
		}
	}

	return nil
}
<commit_msg>Removes alpha validation on object type.<commit_after>package jsh

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/asaskevich/govalidator"
)

// Object represents the default JSON spec for objects
type Object struct {
	Type          string             `json:"type" valid:"required"`
	ID            string             `json:"id"`
	Attributes    json.RawMessage    `json:"attributes,omitempty"`
	Links         map[string]*Link   `json:"links,omitempty"`
	Relationships map[string]*Object `json:"relationships,omitempty"`
	// Status is the HTTP Status Code that should be associated with the object
	// when it is sent.
	Status int `json:"-"`
}

// NewObject prepares a new JSON Object for an API response. Whatever is provided
// as attributes will be marshalled to JSON.
func NewObject(id string, resourceType string, attributes interface{}) (*Object, *Error) {
	object := &Object{
		ID:            id,
		Type:          resourceType,
		Links:         map[string]*Link{},
		Relationships: map[string]*Object{},
	}

	rawJSON, err := json.MarshalIndent(attributes, "", " ")
	if err != nil {
		return nil, ISE(fmt.Sprintf("Error marshaling attrs while creating a new JSON Object: %s", err))
	}

	object.Attributes = rawJSON
	return object, nil
}

/*
Unmarshal puts an Object's Attributes into a more useful target resourceType defined
by the user. A correct object resourceType specified must also be provided otherwise
an error is returned to prevent hard to track down situations.

Optionally, used https://github.com/go-validator/validator for request input validation.
Simply define your struct with valid input tags:

	struct {
		Username string `json:"username" valid:"required,alphanum"`
	}

As the final action, the Unmarshal function will run govalidator on the unmarshal
result. If the validator fails, a Sendable error response of HTTP Status 422 will
be returned containing each validation error with a populated Error.Source.Pointer
specifying each struct attribute that failed. In this case, all you need to do is:

	errors := obj.Unmarshal("mytype", &myType)
	if errors != nil {
		// log errors via error.ISE
		jsh.Send(w, r, errors)
	}
*/
func (o *Object) Unmarshal(resourceType string, target interface{}) ErrorList {

	if resourceType != o.Type {
		return []*Error{ISE(fmt.Sprintf(
			"Expected type %s, when converting actual type: %s",
			resourceType,
			o.Type,
		))}
	}

	jsonErr := json.Unmarshal(o.Attributes, target)
	if jsonErr != nil {
		return []*Error{ISE(fmt.Sprintf(
			"For type '%s' unable to marshal: %s\nError:%s",
			resourceType,
			string(o.Attributes),
			jsonErr.Error(),
		))}
	}

	return validateInput(target)
}

/*
Marshal allows you to load a modified payload back into an object to preserve
all of the data it has.
*/
func (o *Object) Marshal(attributes interface{}) *Error {
	raw, err := json.MarshalIndent(attributes, "", " ")
	if err != nil {
		return ISE(fmt.Sprintf("Error marshaling attrs while creating a new JSON Object: %s", err))
	}

	o.Attributes = raw
	return nil
}

/*
Validate ensures that an object is JSON API compatible. Has a side effect of also
setting the Object's Status attribute to be used as the Response HTTP Code if one
has not already been set.
*/
func (o *Object) Validate(r *http.Request, response bool) *Error {

	if o.ID == "" {

		// don't error if the client is attempting to performing a POST request, in
		// which case, ID shouldn't actually be set
		if !response && r.Method != "POST" {
			return SpecificationError("ID must be set for Object response")
		}
	}

	if o.Type == "" {
		return SpecificationError("Type must be set for Object response")
	}

	switch r.Method {
	case "POST":
		acceptable := map[int]bool{201: true, 202: true, 204: true}

		if o.Status != 0 {
			if _, validCode := acceptable[o.Status]; !validCode {
				return SpecificationError("POST Status must be one of 201, 202, or 204.")
			}
			break
		}

		o.Status = http.StatusCreated
		break
	case "PATCH":
		acceptable := map[int]bool{200: true, 202: true, 204: true}

		if o.Status != 0 {
			if _, validCode := acceptable[o.Status]; !validCode {
				return SpecificationError("PATCH Status must be one of 200, 202, or 204.")
			}
			break
		}

		o.Status = http.StatusOK
		break
	case "GET":
		o.Status = http.StatusOK
		break
	// If we hit this it means someone is attempting to use an unsupported HTTP
	// method. Return a 406 error instead
	default:
		return SpecificationError(fmt.Sprintf(
			"The JSON Specification does not accept '%s' requests.",
			r.Method,
		))
	}

	return nil
}

// String prints a formatted string representation of the object
func (o *Object) String() string {
	raw, err := json.MarshalIndent(o, "", " ")
	if err != nil {
		return err.Error()
	}

	return string(raw)
}

// validateInput runs go-validator on each attribute on the struct and returns all
// errors that it picks up
func validateInput(target interface{}) ErrorList {

	_, validationError := govalidator.ValidateStruct(target)
	if validationError != nil {

		errorList, isType := validationError.(govalidator.Errors)
		if isType {

			errors := ErrorList{}
			for _, singleErr := range errorList.Errors() {

				// parse out validation error
				goValidErr, _ := singleErr.(govalidator.Error)
				inputErr := InputError(goValidErr.Err.Error(), goValidErr.Name)

				errors = append(errors, inputErr)
			}

			return errors
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	//"log"
	"sort"
	"strconv"
	"strings"

	"github.com/gonum/stat"
)

var (
	colors = []string{
		"rgba(31,119,180,1)",
		"rgba(255,127,14,1)",
		"rgba(44,160,44,1)",
		"rgba(214,39,40,1)",
		"rgba(148,103,189,1)",
		"rgba(140,86,75,1)",
		"rgba(227,119,194,1)",
	}
)

const (
	maxBallNum       = 59
	balls            = 7
	avgMarkerSize    = 8
	regressionLinear = "linear"
	regressionPoly   = "polynomial"
	graphTypeScatter = "scatter"
	graphTypeBar     = "bar"
	graphTypeLine    = "line"
)

type dataset struct {
	X           []string  `json:"x"`
	Y           []float64 `json:"y"`
	Z           []int     `json:"z"` // number
	Name        string    `json:"name"`
	Mode        string    `json:"mode"`
	Type        string    `json:"type"`
	Line        line      `json:"line"`
	Marker      marker    `json:"marker"`
	ConnectGaps bool      `json:"connectgaps"`
}

type datasetB struct {
	X           []string  `json:"x"`
	Y           []float64 `json:"y"`
	Z           []float64 `json:"z"` // number
	Name        string    `json:"name"`
	Mode        string    `json:"mode"`
	Type        string    `json:"type"`
	Line        line      `json:"line"`
	Marker      markerB   `json:"marker"`
	ConnectGaps bool      `json:"connectgaps"`
}

type marker struct {
	Colour  string  `json:"color"`
	Size    float64 `json:"size"`
	Line    line    `json:"line"`
	Opacity float64 `json:"opacity"`
	Symbol  string  `json:"symbol"`
}

type markerB struct {
	Colour   string    `json:"color"`
	Size     []float64 `json:"size"`
	SizeMode string    `json:"sizemode"`
	SizeRef  float64   `json:"sizeref"`
	Line     line      `json:"line"`
	Opacity  float64   `json:"opacity"`
	Symbol   string    `json:"symbol"`
}

type line struct {
	Width   float64 `json:"width"`
	Colour  string  `json:"color"`
	Shape   string  `json:"shape"`
	Dash    string  `json:"dash"`
	Opacity float64 `json:"opacity"`
}

func graphResultsTimeSeries(records <-chan dbRow, bestFit bool, t string) []dataset {
	data := make([]dataset, balls)

	i := 0
	for row := range records {
		for ball := 0; ball < balls; ball++ {
			if i == 0 {

				switch t {
				case graphTypeScatter:
					data[ball] = dataset{
						Mode: "markers",
						Marker: marker{
							Colour:  colors[ball],
							Size:    avgMarkerSize,
							Opacity: 1,
						},
					}

				case graphTypeLine:
					data[ball] = dataset{
						Mode: "lines",
						Line: line{
							Width: 1,
							Shape: "spline",
						},
					}
				} // END SWITCH

				data[ball].Name = label(ball)
			}
			data[ball].X = append(data[ball].X, fmt.Sprintf("%s:%d:%s", row.Date.Format(formatYYYYMMDD), row.Set, row.Machine))
			data[ball].Y = append(data[ball].Y, float64(row.Num[ball]))
		}

		i++
	}

	if bestFit && t == graphTypeScatter {
		data = append(data, regressionSets(data, regressionLinear)...)
	}

	return data
}

func graphResultsFreqDist(records <-chan dbRow, bestFit bool, t string) []dataset {
	data := make([]dataset, balls)

	i := 0
	for row := range records {
		for ball := 0; ball < balls; ball++ {
			if i == 0 {

				switch t {
				case graphTypeScatter:
					data[ball] = dataset{
						Mode: "markers",
						Marker: marker{
							Colour:  colors[ball],
							Size:    avgMarkerSize,
							Opacity: 1,
						},
						X: freqDistXLabels(),
						Y: make([]float64, maxBallNum),
					}

				case graphTypeBar:
					data[ball] = dataset{
						Type: graphTypeBar,
						X:    freqDistXLabels(),
						Y:    make([]float64, maxBallNum),
					}

				} // END SWITCH

				data[ball].Name = label(ball)
			}

			data[ball].Y[row.Num[ball]-1]++
		}
		i++
	}

	/*
		if bestFit && t != graphTypeBar {
			data = append(data, regressionSets(data, regressionPoly)...)
		}
	*/

	return data
}

func graphResultsRawScatter3D(records <-chan dbRow) []dataset {
	data := make([]dataset, balls)

	// x: machine
	// y: set
	// z: ball result

	i := 0
	for row := range records {
		for ball := 0; ball < balls; ball++ {
			if i == 0 {
				data[ball] = dataset{
					Mode: "markers",
					Type: "scatter3d",
					Marker: marker{
						Size:    avgMarkerSize / 2,
						Opacity: 0.9,
						Line:    line{Width: 0.1},
					},
				}

				data[ball].Name = label(ball)
			}
			data[ball].X = append(data[ball].X, row.Machine)
			data[ball].Y = append(data[ball].Y, float64(row.Set))
			data[ball].Z = append(data[ball].Z, row.Num[ball])
		}
		i++
	}

	return data
}

func graphMSFreqDistScatter3D(m map[string]int) []dataset {
	data := dataset{
		Type: "scatter3d",
		Mode: "markers",
		Marker: marker{
			Size:    avgMarkerSize,
			Opacity: 0.9,
			Line: line{
				Width: 0.1,
			},
		},
	}

	l := []string{}
	for k := range m {
		l = append(l, k)
	}
	sort.Strings(l)

	for _, k := range l {
		s := strings.Split(k, ":")
		y, _ := strconv.Atoi(s[1])

		data.X = append(data.X, s[0])       // MAchine
		data.Y = append(data.Y, float64(y)) // Set
		data.Z = append(data.Z, m[k])       // Frequency
	}

	return []dataset{data}
}

func graphMSFreqDistBubble(m map[string]int) []datasetB {
	data := datasetB{
		Type: "scatter",
		Mode: "markers",
		Marker: markerB{
			Opacity: 0.8,
			Line:    line{Width: 0.1},
		},
	}

	l := []string{}
	for k := range m {
		l = append(l, k)
	}
	sort.Strings(l)

	for _, k := range l {
		s := strings.Split(k, ":")
		z, _ := strconv.Atoi(s[1])

		data.X = append(data.X, s[0])
		data.Y = append(data.Y, float64(z))
		data.Marker.Size = append(data.Marker.Size, float64(m[k])*2)
	}

	return []datasetB{data}
}

func regressionSets(data []dataset, t string) []dataset {
	// Calculate and append regressionLinear regressions for each set
	r, rX := make([]dataset, balls), make([]float64, len(data[0].Y))

	// Generate numerical X axis data
	for i := range rX {
		rX[i] = float64(i) + 1
	}

	// Iterate existing sets and create new regression sets
	for i, set := range data {
		r[i] = dataset{
			Name: set.Name,
			Mode: "lines",
			Line: line{
				Dash:   "dot",
				Width:  2,
				Colour: colors[i],
			},
			ConnectGaps: true,
			X:           set.X,
		}

		switch t {
		case regressionLinear:
			r[i].Y = linRegYData(rX, set.Y, false)
		case regressionPoly:
			r[i].Y = polRegYData(rX, set.Y, false) // Not currently working because I suck at maths
		}
	}

	return r
}

func polRegYData(prX, prY []float64, subzero bool) []float64 {
	// @TODO: polynomial regression sets for non-linear best fits
	y := make([]float64, len(prX))
	//a, b := stat.LinearRegression(prX, prY, nil, false)
	//r2 := stat.RSquared(prX, prY, nil, a, b)
	//log.Println(r2)

	return y
}

func linRegYData(lrX, lrY []float64, subzero bool) []float64 {
	a, b := stat.LinearRegression(lrX, lrY, nil, false)

	y := make([]float64, len(lrX))
	for idx, x := range lrX {
		n := a + (b * x)
		if subzero {
			y[idx] = n
		} else if n < 0 {
			y[idx] = 0
		} else {
			y[idx] = n
		}

	}
	return y
}

func freqDistXLabels() []string {
	var x []string
	for i := 0; i < maxBallNum; i++ {
		x = append(x, fmt.Sprintf("%d", i+1))
	}
	return x
}

func label(ball int) string {
	if ball < 6 {
		return fmt.Sprintf("Ball %d", ball+1)
	}

	return "Bonus"
}
<commit_msg>still don't understand curved best fit lines<commit_after>package main

import (
	"fmt"
	//"log"
	"sort"
	"strconv"
	"strings"

	//"github.com/gonum/matrix/mat64"
	"github.com/gonum/stat"
)

var (
	colors = []string{
		"rgba(31,119,180,1)",
		"rgba(255,127,14,1)",
		"rgba(44,160,44,1)",
		"rgba(214,39,40,1)",
		"rgba(148,103,189,1)",
		"rgba(140,86,75,1)",
		"rgba(227,119,194,1)",
	}
)

const (
	maxBallNum       = 59
	balls            = 7
	avgMarkerSize    = 8
	regressionLinear = "linear"
	regressionPoly   = "polynomial"
	graphTypeScatter = "scatter"
	graphTypeBar     = "bar"
	graphTypeLine    = "line"
)

type dataset struct {
	X           []string  `json:"x"`
	Y           []float64 `json:"y"`
	Z           []int     `json:"z"` // number
	Name        string    `json:"name"`
	Mode        string    `json:"mode"`
	Type        string    `json:"type"`
	Line        line      `json:"line"`
	Marker      marker    `json:"marker"`
	ConnectGaps bool      `json:"connectgaps"`
}

type datasetB struct {
	X           []string  `json:"x"`
	Y           []float64 `json:"y"`
	Z           []float64 `json:"z"` // number
	Name        string    `json:"name"`
	Mode        string    `json:"mode"`
	Type        string    `json:"type"`
	Line        line      `json:"line"`
	Marker      markerB   `json:"marker"`
	ConnectGaps bool      `json:"connectgaps"`
}

type marker struct {
	Colour  string  `json:"color"`
	Size    float64 `json:"size"`
	Line    line    `json:"line"`
	Opacity float64 `json:"opacity"`
	Symbol  string  `json:"symbol"`
}

type markerB struct {
	Colour   string    `json:"color"`
	Size     []float64 `json:"size"`
	SizeMode string    `json:"sizemode"`
	SizeRef  float64   `json:"sizeref"`
	Line     line      `json:"line"`
	Opacity  float64   `json:"opacity"`
	Symbol   string    `json:"symbol"`
}

type line struct {
	Width   float64 `json:"width"`
	Colour  string  `json:"color"`
	Shape   string  `json:"shape"`
	Dash    string  `json:"dash"`
	Opacity float64 `json:"opacity"`
}

func graphResultsTimeSeries(records <-chan dbRow, bestFit bool, t string) []dataset {
	data := make([]dataset, balls)

	i := 0
	for row := range records {
		for ball := 0; ball < balls; ball++ {
			if i == 0 {

				switch t {
				case graphTypeScatter:
					data[ball] = dataset{
						Mode: "markers",
						Marker: marker{
							Colour:  colors[ball],
							Size:    avgMarkerSize,
							Opacity: 1,
						},
					}

				case graphTypeLine:
					data[ball] = dataset{
						Mode: "lines",
						Line: line{
							Width: 1,
							Shape: "spline",
						},
					}
				} // END SWITCH

				data[ball].Name = label(ball)
			}
			data[ball].X = append(data[ball].X, fmt.Sprintf("%s:%d:%s", row.Date.Format(formatYYYYMMDD), row.Set, row.Machine))
			data[ball].Y = append(data[ball].Y, float64(row.Num[ball]))
		}

		i++
	}

	if bestFit && t == graphTypeScatter {
		data = append(data, regressionSet(data, regressionLinear)...)
	}

	return data
}

func graphResultsFreqDist(records <-chan dbRow, bestFit bool, t string) []dataset {
	data := make([]dataset, balls)

	i := 0
	for row := range records {
		for ball := 0; ball < balls; ball++ {
			if i == 0 {

				switch t {
				case graphTypeScatter:
					data[ball] = dataset{
						Mode: "markers",
						Marker: marker{
							Colour:  colors[ball],
							Size:    avgMarkerSize,
							Opacity: 1,
						},
						X: freqDistXLabels(),
						Y: make([]float64, maxBallNum),
					}

				case graphTypeBar:
					data[ball] = dataset{
						Type: graphTypeBar,
						X:    freqDistXLabels(),
						Y:    make([]float64, maxBallNum),
					}

				} // END SWITCH

				data[ball].Name = label(ball)
			}

			data[ball].Y[row.Num[ball]-1]++
		}
		i++
	}

	if bestFit && t != graphTypeBar {
		data = append(data, regressionSet(data, regressionPoly)...)
	}

	return data
}

func graphResultsRawScatter3D(records <-chan dbRow) []dataset {
	data := make([]dataset, balls)

	// x: machine
	// y: set
	// z: ball result

	i := 0
	for row := range records {
		for ball := 0; ball < balls; ball++ {
			if i == 0 {
				data[ball] = dataset{
					Mode: "markers",
					Type: "scatter3d",
					Marker: marker{
						Size:    avgMarkerSize / 2,
						Opacity: 0.9,
						Line:    line{Width: 0.1},
					},
				}

				data[ball].Name = label(ball)
			}
			data[ball].X = append(data[ball].X, row.Machine)
			data[ball].Y = append(data[ball].Y, float64(row.Set))
			data[ball].Z = append(data[ball].Z, row.Num[ball])
		}
		i++
	}

	return data
}

func graphMSFreqDistScatter3D(m map[string]int) []dataset {
	data := dataset{
		Type: "scatter3d",
		Mode: "markers",
		Marker: marker{
			Size:    avgMarkerSize,
			Opacity: 0.9,
			Line: line{
				Width: 0.1,
			},
		},
	}

	l := []string{}
	for k := range m {
		l = append(l, k)
	}
	sort.Strings(l)

	for _, k := range l {
		s := strings.Split(k, ":")
		y, _ := strconv.Atoi(s[1])

		data.X = append(data.X, s[0])       // MAchine
		data.Y = append(data.Y, float64(y)) // Set
		data.Z = append(data.Z, m[k])       // Frequency
	}

	return []dataset{data}
}

func graphMSFreqDistBubble(m map[string]int) []datasetB {
	data := datasetB{
		Type: "scatter",
		Mode: "markers",
		Marker: markerB{
			Opacity: 0.8,
			Line:    line{Width: 0.1},
		},
	}

	l := []string{}
	for k := range m {
		l = append(l, k)
	}
	sort.Strings(l)

	for _, k := range l {
		s := strings.Split(k, ":")
		z, _ := strconv.Atoi(s[1])

		data.X = append(data.X, s[0])
		data.Y = append(data.Y, float64(z))
		data.Marker.Size = append(data.Marker.Size, float64(m[k])*2)
	}

	return []datasetB{data}
}

func regressionSet(data []dataset, t string) []dataset {
	// Calculate and append regressionLinear regressions for each set
	r, rX := make([]dataset, balls), make([]float64, len(data[0].Y))

	// Generate numerical X axis data
	for i := range rX {
		rX[i] = float64(i) + 1
	}

	// Iterate existing sets and create new regression sets
	for i, set := range data {
		r[i] = dataset{
			Name: set.Name,
			Mode: "lines",
			Line: line{
				Dash:   "dot",
				Width:  2,
				Colour: colors[i],
			},
			ConnectGaps: true,
			X:           set.X,
		}

		switch t {
		case regressionLinear:
			r[i].Y = linRegYData(rX, set.Y, false)
		case regressionPoly:
			r[i].Y = polRegYData(rX, set.Y, false) // Not currently working because I suck at maths
		}
	}

	return r
}

func polRegYData(prX, prY []float64, subzero bool) []float64 {
	// @TODO: polynomial regression sets for non-linear best fits
	y := make([]float64, len(prX))
	//a, b := stat.LinearRegression(prX, prY, nil, false)
	//r2 := stat.RSquared(prX, prY, nil, a, b)
	//log.Println(r2)

	return y
}

func linRegYData(lrX, lrY []float64, subzero bool) []float64 {
	a, b := stat.LinearRegression(lrX, lrY, nil, false)

	y := make([]float64, len(lrX))
	for idx, x := range lrX {
		n := a + (b * x)
		if subzero {
			y[idx] = n
		} else if n < 0 {
			y[idx] = 0
		} else {
			y[idx] = n
		}

	}
	return y
}

func freqDistXLabels() []string {
	var x []string
	for i := 0; i < maxBallNum; i++ {
		x = append(x, fmt.Sprintf("%d", i+1))
	}
	return x
}

func label(ball int) string {
	if ball < 6 {
		return fmt.Sprintf("Ball %d", ball+1)
	}

	return "Bonus"
}
<|endoftext|>


<commit_before>package chef

import "fmt"

type GroupService struct {
	client *Client
}

// Group represents the native Go version of the deserialized Group type
type Group struct {
	Name       string    `json:"name"`
	GroupName  string    `json:"groupname"`
	OrgName    string    `json:"orgname"`
    Actors     []string  `json:"actors"`
    Clients    []string  `json:"clients"`
    Groups     []string  `json:"groups"`
    Users      []string  `json:"users"`
}

type GroupResult struct {
	Uri string `json:"uri"`
}

// List lists the groups in the Chef server.
//
// Chef API docs: https://docs.chef.io/api_chef_server.html#groups
func (e *GroupService) List() (grouplist []string, err error) {
	err = e.client.magicRequestDecoder("GET", "groups", nil, &grouplist)
	return
}

// Get gets a group from the Chef server.
//
// Chef API docs: http://docs.opscode.com/api_chef_server.html#id28
func (e *GroupService) Get(name string) (group Group, err error) {
	url := fmt.Sprintf("groups/%s", name)
	err = e.client.magicRequestDecoder("GET", url, nil, &group)
	return
}

// Creates a Group on the chef server
//
// Chef API docs: https://docs.chef.io/api_chef_server.html#groups
func (e *GroupService) Create(group Group) (data *GroupResult, err error) {
	body, err := JSONReader(group)
	if err != nil {
		return
	}

	err = e.client.magicRequestDecoder("POST", "groups", body, &data)
	return
}

// Update a group on the Chef server.
//
// Chef API docs: https://docs.chef.io/api_chef_server.html#groups
func (e *GroupService) Update(g Group) (group Group, err error) {
	url := fmt.Sprintf("groups/%s", g.Name)
	body, err := JSONReader(g)
	if err != nil {
		return
	}

	err = e.client.magicRequestDecoder("PUT", url, body, &group)
	return
}

// Delete removes a group on the Chef server
//
// Chef API docs: https://docs.chef.io/api_chef_server.html#groups
func (e *GroupService) Delete(name string) (err error) {
	err = e.client.magicRequestDecoder("DELETE", "groups/"+name, nil, nil)
	return
}
<commit_msg>Change tabs to spaces.<commit_after>package chef

import "fmt"

type GroupService struct {
    client *Client
}

// Group represents the native Go version of the deserialized Group type
type Group struct {
    Name       string    `json:"name"`
    GroupName  string    `json:"groupname"`
    OrgName    string    `json:"orgname"`
    Actors     []string  `json:"actors"`
    Clients    []string  `json:"clients"`
    Groups     []string  `json:"groups"`
    Users      []string  `json:"users"`
}

type GroupResult struct {
    Uri string `json:"uri"`
}

// List lists the groups in the Chef server.
//
// Chef API docs: https://docs.chef.io/api_chef_server.html#groups
func (e *GroupService) List() (grouplist []string, err error) {
    err = e.client.magicRequestDecoder("GET", "groups", nil, &grouplist)
    return
}

// Get gets a group from the Chef server.
//
// Chef API docs: http://docs.opscode.com/api_chef_server.html#id28
func (e *GroupService) Get(name string) (group Group, err error) {
    url := fmt.Sprintf("groups/%s", name)
    err = e.client.magicRequestDecoder("GET", url, nil, &group)
    return
}

// Creates a Group on the chef server
//
// Chef API docs: https://docs.chef.io/api_chef_server.html#groups
func (e *GroupService) Create(group Group) (data *GroupResult, err error) {
    body, err := JSONReader(group)
    if err != nil {
        return
    }

    err = e.client.magicRequestDecoder("POST", "groups", body, &data)
    return
}

// Update a group on the Chef server.
//
// Chef API docs: https://docs.chef.io/api_chef_server.html#groups
func (e *GroupService) Update(g Group) (group Group, err error) {
    url := fmt.Sprintf("groups/%s", g.Name)
    body, err := JSONReader(g)
    if err != nil {
        return
    }

    err = e.client.magicRequestDecoder("PUT", url, body, &group)
    return
}

// Delete removes a group on the Chef server
//
// Chef API docs: https://docs.chef.io/api_chef_server.html#groups
func (e *GroupService) Delete(name string) (err error) {
    err = e.client.magicRequestDecoder("DELETE", "groups/"+name, nil, nil)
    return
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.

package main

import (
	"log"
	"strings"
	"strconv"
)

type Group struct {
	// The channel that this group resides in
	Channel *Channel

	// The name of this group
	Name string

	// The inherit flag means that this group will inherit group
	// members from its parent.
	Inherit bool

	// The inheritable flag means that subchannels can
	// inherit the members of this group.
	Inheritable bool

	// Group adds permissions to these users
	Add map[int]bool
	// Group removes permissions from these users
	Remove map[int]bool
	// Temporary add (authenticators)
	Temporary map[int]bool
}

// Create a new group for channel with name. Does not add it to the channels
// group list.
func NewGroup(channel *Channel, name string) *Group {
	grp := &Group{}
	grp.Channel = channel
	grp.Name = name
	grp.Add = make(map[int]bool)
	grp.Remove = make(map[int]bool)
	grp.Temporary = make(map[int]bool)
	return grp
}

// Check whether the Add set contains id.
func (group *Group) AddContains(id int) (ok bool) {
	_, ok = group.Add[id]
	return
}

// Get the list of user ids in the Add set.
func (group *Group) AddUsers() []int {
	users := []int{}
	for uid, _ := range group.Add {
		users = append(users, uid)
	}
	return users
}

// Check whether the Remove set contains id.
func (group *Group) RemoveContains(id int) (ok bool) {
	_, ok = group.Remove[id]
	return
}

// Get the list of user ids in the Remove set.
func (group *Group) RemoveUsers() []int {
	users := []int{}
	for uid, _ := range group.Remove {
		users = append(users, uid)
	}
	return users
}

// Check whether the Temporary set contains id.
func (group *Group) TemporaryContains(id int) (ok bool) {
	_, ok = group.Temporary[id]
	return
}

// Get the set of user id's from the group. This includes group
// members that have been inherited from an ancestor.
func (group *Group) Members() map[int]bool {
	groups := []*Group{}
	members := map[int]bool{}

	// The channel that the group is defined on.
	channel := group.Channel

	// Walk a group's channel tree, starting with the channel the group
	// is defined on, followed by its parent channels.
	iter := group.Channel
	for iter != nil {
		curgroup := iter.Groups[group.Name]
		if curgroup != nil {
			// If the group is not inheritable, and we're looking at an
			// ancestor group, we've looked in all the groups we should.
			if iter != channel && !curgroup.Inheritable {
				break
			}
			// Add the group to the list of groups to be considered
			groups = append([]*Group{curgroup}, groups...)
			// If this group does not inherit from groups in its ancestors, stop looking
			// for more ancestor groups.
			if !curgroup.Inherit {
				break
			}
		}
		iter = iter.parent
	}

	for _, curgroup := range groups {
		for uid, _ := range curgroup.Add {
			members[uid] = true
		}
		for uid, _ := range curgroup.Remove {
			members[uid] = false, false
		}
	}

	return members
}

// Checks whether a user is a member of the group as defined on channel.
// The channel current is the channel that group membership is currently being evaluated for.
// The channel aclchan is the channel that the group is defined on. This means that current inherits
// the group from an acl in aclchan.
//
// The channel aclchan will always be either equal to current, or be an ancestor.
func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *Client) (ok bool) {
	valid := true
	invert := false
	token := false
	hash := false

	// Returns the 'correct' return value considering the value
	// of the invert flag.
	defer func() {
		if valid && invert {
			ok = !ok
		}
	}()

	channel := current

	for {
		// Empty group name are not valid.
		if len(name) == 0 {
			valid = false
			return false
		}
		// Invert
		if name[0] == '!' {
			invert = true
			name = name[1:]
			continue
		}
		// Evaluate in ACL context (not current channel)
		if name[0] == '~' {
			channel = aclchan
			name = name[1:]
			continue
		}
		// Token
		if name[0] == '#' {
			token = true
			name = name[1:]
			continue
		}
		// Hash
		if name[0] == '$' {
			hash = true
			name = name[1:]
			continue
		}
		break
	}

	if token {
		// The user is part of this group if the remaining name is part of
		// his access token list. The name check is case-insensitive.
		for _, clientToken := range client.Tokens {
			if strings.ToLower(name) == strings.ToLower(clientToken) {
				return true
			}
		}
		return false
	} else if hash {
		// The user is part of this group if the remaining name matches his
		// cert hash.
		log.Printf("GroupMemberCheck: Implement hash matching")
		return false
	} else if name == "none" {
		// None
		return false
	} else if name == "all" {
		// Everyone
		return true
	} else if name == "auth" {
		// The user is part of the auth group is he is authenticated. That is,
		// his UserId is >= 0.
		return client.IsRegistered()
	} else if name == "strong" {
		// The user is part of the strong group if he is authenticated to the server
		// via a strong certificate (i.e. non-self-signed, trusted by the server's
		// trusted set of root CAs).
		log.Printf("GroupMemberCheck: Implement strong certificate matching")
		return false
	} else if name == "in" {
		// Is the user in the currently evaluated channel?
		return client.Channel == channel
	} else if name == "out" {
		// Is the user not in the currently evaluated channel?
		return client.Channel != channel
	} else if name == "sub" {
		// fixme(mkrautz): The sub group implementation below hasn't been thoroughly
		// tested yet. It might be a bit buggy!

		// Strip away the "sub," part of the name
		name = name[4:]

		mindesc := 1
		maxdesc := 1000
		minpath := 0

		// Parse the groupname to extract the values we should use
		// for minpath (first argument), mindesc (second argument),
		// and maxdesc (third argument).
		args := strings.Split(name, ",", 3)
		nargs := len(args)
		if nargs == 3 {
			if len(args[2]) > 0 {
				if result, err := strconv.Atoi(args[2]); err == nil {
					maxdesc = result
				}
			}
		}
		if nargs >= 2 {
			if len(args[1]) > 0 {
				if result, err := strconv.Atoi(args[1]); err == nil {
					mindesc = result
				}
			}
		}
		if nargs >= 1 {
			if len(args[0]) > 0 {
				if result, err := strconv.Atoi(args[0]); err == nil {
					minpath = result
				}
			}
		}

		// Build a chain of channels, starting from the client's current channel.
		playerChain := []*Channel{}
		iter := client.Channel
		for iter != nil {
			playerChain = append([]*Channel{iter}, playerChain...)
			iter = iter.parent
		}
		// Build a chain of channels, starting from the channel current. This is
		// the channel that group membership is checked against, notwithstanding
		// the ~ group operator.
		groupChain := []*Channel{}
		iter = current
		for iter != nil {
			groupChain = append([]*Channel{iter}, groupChain...)
			iter = iter.parent
		}

		// Helper function that finds the given channel in the channels slice.
		// Returns -1 if the given channel was not found in the slice.
		indexOf := func(channels []*Channel, channel *Channel) int {
			for i, iter := range channels {
				if iter == channel {
					return i
				}
			}
			return -1
		}

		// Find the index of channel that the group is currently being evaluated on.
		// This can be either aclchan or current depending on the ~ group operator.
		cofs := indexOf(groupChain, channel)
		if cofs == -1 {
			valid = false
			return false
		}

		// Add the first parameter of our sub group to cofs to get our 'base' channel.
		cofs += minpath
		// Check that the minpath parameter that was given is a valid index for groupChain.
		if cofs >= len(groupChain) {
			valid = false
			return false
		} else if cofs < 0 {
			cofs = 0
		}

		// If our 'base' channel is not in the playerChain, the group does not apply to the client.
		if indexOf(playerChain, groupChain[cofs]) == -1 {
			return false
		}

		// Down here, we're certain that the playerChain includes the base channel
		// *somewhere*. We must now determine if the path depth makes the user a
		// member of the group.
		mindepth := cofs + mindesc
		maxdepth := cofs + maxdesc
		pdepth := len(playerChain) - 1
		return pdepth >= mindepth && pdepth <= maxdepth

	} else {
		// Non-magic groups
		groups := []*Group{}

		iter := channel
		for iter != nil {
			if group, ok := iter.Groups[name]; ok {
				// Skip non-inheritable groups if we're in parents
				// of our evaluated channel.
				if iter != channel && !group.Inheritable {
					break
				}
				// Prepend group
				groups = append([]*Group{group}, groups...)
				// If this group does not inherit from groups in its ancestors, stop looking
				// for more ancestor groups.
				if !group.Inherit {
					break
				}
			}
			iter = iter.parent
		}

		isMember := false
		for _, group := range groups {
			if group.AddContains(client.UserId()) || group.TemporaryContains(client.UserId()) || group.TemporaryContains(-int(client.Session)) {
				isMember = true
			}
			if group.RemoveContains(client.UserId()) {
				isMember = false
			}
		}
		return isMember
	}

	return false
}

// Get the list of group names in a particular channel.
// This function walks the through the channel and all its
// parent channels to figure out all groups that affect
// the channel while considering group inheritance.
func (channel *Channel) GroupNames() map[string]bool {
	names := map[string]bool{}

	// Construct a list of channels. Fartherst away ancestors
	// are put in front of the list, allowing us to linearly
	// iterate the list to determine inheritance.
	channels := []*Channel{}
	iter := channel
	for iter != nil {
		channels = append([]*Channel{iter}, channels...)
		iter = iter.parent
	}

	// Walk through all channels and groups in them.
	for _, iter := range channels {
		for _, group := range iter.Groups {
			// A non-inheritable group in parent. Discard it.
			if channel != iter && !group.Inheritable {
				names[group.Name] = false, false
				// An inheritable group. Add it to the list.
			} else {
				names[group.Name] = true
			}
		}
	}
	return names
}
<commit_msg>GroupMemberCheck: Implement certhash matching.<commit_after>// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.

package main

import (
	"log"
	"strings"
	"strconv"
)

type Group struct {
	// The channel that this group resides in
	Channel *Channel

	// The name of this group
	Name string

	// The inherit flag means that this group will inherit group
	// members from its parent.
	Inherit bool

	// The inheritable flag means that subchannels can
	// inherit the members of this group.
	Inheritable bool

	// Group adds permissions to these users
	Add map[int]bool
	// Group removes permissions from these users
	Remove map[int]bool
	// Temporary add (authenticators)
	Temporary map[int]bool
}

// Create a new group for channel with name. Does not add it to the channels
// group list.
func NewGroup(channel *Channel, name string) *Group {
	grp := &Group{}
	grp.Channel = channel
	grp.Name = name
	grp.Add = make(map[int]bool)
	grp.Remove = make(map[int]bool)
	grp.Temporary = make(map[int]bool)
	return grp
}

// Check whether the Add set contains id.
func (group *Group) AddContains(id int) (ok bool) {
	_, ok = group.Add[id]
	return
}

// Get the list of user ids in the Add set.
func (group *Group) AddUsers() []int {
	users := []int{}
	for uid, _ := range group.Add {
		users = append(users, uid)
	}
	return users
}

// Check whether the Remove set contains id.
func (group *Group) RemoveContains(id int) (ok bool) {
	_, ok = group.Remove[id]
	return
}

// Get the list of user ids in the Remove set.
func (group *Group) RemoveUsers() []int {
	users := []int{}
	for uid, _ := range group.Remove {
		users = append(users, uid)
	}
	return users
}

// Check whether the Temporary set contains id.
func (group *Group) TemporaryContains(id int) (ok bool) {
	_, ok = group.Temporary[id]
	return
}

// Get the set of user id's from the group. This includes group
// members that have been inherited from an ancestor.
func (group *Group) Members() map[int]bool {
	groups := []*Group{}
	members := map[int]bool{}

	// The channel that the group is defined on.
	channel := group.Channel

	// Walk a group's channel tree, starting with the channel the group
	// is defined on, followed by its parent channels.
	iter := group.Channel
	for iter != nil {
		curgroup := iter.Groups[group.Name]
		if curgroup != nil {
			// If the group is not inheritable, and we're looking at an
			// ancestor group, we've looked in all the groups we should.
			if iter != channel && !curgroup.Inheritable {
				break
			}
			// Add the group to the list of groups to be considered
			groups = append([]*Group{curgroup}, groups...)
			// If this group does not inherit from groups in its ancestors, stop looking
			// for more ancestor groups.
			if !curgroup.Inherit {
				break
			}
		}
		iter = iter.parent
	}

	for _, curgroup := range groups {
		for uid, _ := range curgroup.Add {
			members[uid] = true
		}
		for uid, _ := range curgroup.Remove {
			members[uid] = false, false
		}
	}

	return members
}

// Checks whether a user is a member of the group as defined on channel.
// The channel current is the channel that group membership is currently being evaluated for.
// The channel aclchan is the channel that the group is defined on. This means that current inherits
// the group from an acl in aclchan.
//
// The channel aclchan will always be either equal to current, or be an ancestor.
func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *Client) (ok bool) {
	valid := true
	invert := false
	token := false
	hash := false

	// Returns the 'correct' return value considering the value
	// of the invert flag.
	defer func() {
		if valid && invert {
			ok = !ok
		}
	}()

	channel := current

	for {
		// Empty group name are not valid.
		if len(name) == 0 {
			valid = false
			return false
		}
		// Invert
		if name[0] == '!' {
			invert = true
			name = name[1:]
			continue
		}
		// Evaluate in ACL context (not current channel)
		if name[0] == '~' {
			channel = aclchan
			name = name[1:]
			continue
		}
		// Token
		if name[0] == '#' {
			token = true
			name = name[1:]
			continue
		}
		// Hash
		if name[0] == '$' {
			hash = true
			name = name[1:]
			continue
		}
		break
	}

	if token {
		// The user is part of this group if the remaining name is part of
		// his access token list. The name check is case-insensitive.
		for _, clientToken := range client.Tokens {
			if strings.ToLower(name) == strings.ToLower(clientToken) {
				return true
			}
		}
		return false
	} else if hash {
		// The client is part of this group if the remaining name matches the
		// client's cert hash.
		if strings.ToLower(name) == strings.ToLower(client.CertHash) {
			return true
		}
		return false
	} else if name == "none" {
		// None
		return false
	} else if name == "all" {
		// Everyone
		return true
	} else if name == "auth" {
		// The user is part of the auth group is he is authenticated. That is,
		// his UserId is >= 0.
		return client.IsRegistered()
	} else if name == "strong" {
		// The user is part of the strong group if he is authenticated to the server
		// via a strong certificate (i.e. non-self-signed, trusted by the server's
		// trusted set of root CAs).
		log.Printf("GroupMemberCheck: Implement strong certificate matching")
		return false
	} else if name == "in" {
		// Is the user in the currently evaluated channel?
		return client.Channel == channel
	} else if name == "out" {
		// Is the user not in the currently evaluated channel?
		return client.Channel != channel
	} else if name == "sub" {
		// fixme(mkrautz): The sub group implementation below hasn't been thoroughly
		// tested yet. It might be a bit buggy!

		// Strip away the "sub," part of the name
		name = name[4:]

		mindesc := 1
		maxdesc := 1000
		minpath := 0

		// Parse the groupname to extract the values we should use
		// for minpath (first argument), mindesc (second argument),
		// and maxdesc (third argument).
		args := strings.Split(name, ",", 3)
		nargs := len(args)
		if nargs == 3 {
			if len(args[2]) > 0 {
				if result, err := strconv.Atoi(args[2]); err == nil {
					maxdesc = result
				}
			}
		}
		if nargs >= 2 {
			if len(args[1]) > 0 {
				if result, err := strconv.Atoi(args[1]); err == nil {
					mindesc = result
				}
			}
		}
		if nargs >= 1 {
			if len(args[0]) > 0 {
				if result, err := strconv.Atoi(args[0]); err == nil {
					minpath = result
				}
			}
		}

		// Build a chain of channels, starting from the client's current channel.
		playerChain := []*Channel{}
		iter := client.Channel
		for iter != nil {
			playerChain = append([]*Channel{iter}, playerChain...)
			iter = iter.parent
		}
		// Build a chain of channels, starting from the channel current. This is
		// the channel that group membership is checked against, notwithstanding
		// the ~ group operator.
		groupChain := []*Channel{}
		iter = current
		for iter != nil {
			groupChain = append([]*Channel{iter}, groupChain...)
			iter = iter.parent
		}

		// Helper function that finds the given channel in the channels slice.
		// Returns -1 if the given channel was not found in the slice.
		indexOf := func(channels []*Channel, channel *Channel) int {
			for i, iter := range channels {
				if iter == channel {
					return i
				}
			}
			return -1
		}

		// Find the index of channel that the group is currently being evaluated on.
		// This can be either aclchan or current depending on the ~ group operator.
		cofs := indexOf(groupChain, channel)
		if cofs == -1 {
			valid = false
			return false
		}

		// Add the first parameter of our sub group to cofs to get our 'base' channel.
		cofs += minpath
		// Check that the minpath parameter that was given is a valid index for groupChain.
		if cofs >= len(groupChain) {
			valid = false
			return false
		} else if cofs < 0 {
			cofs = 0
		}

		// If our 'base' channel is not in the playerChain, the group does not apply to the client.
		if indexOf(playerChain, groupChain[cofs]) == -1 {
			return false
		}

		// Down here, we're certain that the playerChain includes the base channel
		// *somewhere*. We must now determine if the path depth makes the user a
		// member of the group.
		mindepth := cofs + mindesc
		maxdepth := cofs + maxdesc
		pdepth := len(playerChain) - 1
		return pdepth >= mindepth && pdepth <= maxdepth

	} else {
		// Non-magic groups
		groups := []*Group{}

		iter := channel
		for iter != nil {
			if group, ok := iter.Groups[name]; ok {
				// Skip non-inheritable groups if we're in parents
				// of our evaluated channel.
				if iter != channel && !group.Inheritable {
					break
				}
				// Prepend group
				groups = append([]*Group{group}, groups...)
				// If this group does not inherit from groups in its ancestors, stop looking
				// for more ancestor groups.
				if !group.Inherit {
					break
				}
			}
			iter = iter.parent
		}

		isMember := false
		for _, group := range groups {
			if group.AddContains(client.UserId()) || group.TemporaryContains(client.UserId()) || group.TemporaryContains(-int(client.Session)) {
				isMember = true
			}
			if group.RemoveContains(client.UserId()) {
				isMember = false
			}
		}
		return isMember
	}

	return false
}

// Get the list of group names in a particular channel.
// This function walks the through the channel and all its
// parent channels to figure out all groups that affect
// the channel while considering group inheritance.
func (channel *Channel) GroupNames() map[string]bool {
	names := map[string]bool{}

	// Construct a list of channels. Fartherst away ancestors
	// are put in front of the list, allowing us to linearly
	// iterate the list to determine inheritance.
	channels := []*Channel{}
	iter := channel
	for iter != nil {
		channels = append([]*Channel{iter}, channels...)
		iter = iter.parent
	}

	// Walk through all channels and groups in them.
	for _, iter := range channels {
		for _, group := range iter.Groups {
			// A non-inheritable group in parent. Discard it.
			if channel != iter && !group.Inheritable {
				names[group.Name] = false, false
				// An inheritable group. Add it to the list.
			} else {
				names[group.Name] = true
			}
		}
	}
	return names
}
<|endoftext|>


<commit_before>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvimutil

import (
	"fmt"
	"strings"

	"nvim-go/config"
	"nvim-go/pathutil"

	"github.com/neovim/go-client/nvim"
)

var pkgTerminal = "GoTerminal"

var bufName = "__GO_TERMINAL__"

// Terminal represents a Neovim terminal.
type Terminal struct {
	v *nvim.Nvim
	b *nvim.Batch

	cmd  []string
	mode string
	// Name terminal buffer name.
	Name string
	// Dir specifies the working directory of the command on terminal.
	Dir string
	// Size open the terminal window size.
	Size int

	cw nvim.Window

	*Buffer
}

// NewTerminal return the Neovim terminal buffer.
func NewTerminal(vim *nvim.Nvim, name string, command []string, mode string) *Terminal {
	return &Terminal{
		v:    vim,
		b:    vim.NewBatch(),
		Name: name,
		cmd:  command,
		mode: mode,
	}
}

// Create creats the new Neovim terminal buffer.
func (t *Terminal) Create() (err error) {
	t.cw, err = t.v.CurrentWindow()
	if err != nil {
		return err
	}

	t.Buffer = NewBuffer(t.v)

	switch {
	case t.mode == "split":
		t.Size = t.getWindowSize(config.TerminalHeight, t.v.WindowHeight)
		t.Buffer.Height = t.Size
	case t.mode == "vsplit":
		t.Size = t.getWindowSize(config.TerminalWidth, t.v.WindowWidth)
		t.Buffer.Width = t.Size
	default:
		// nothing to do
	}

	option := t.setTerminalOption()
	name := fmt.Sprintf("| terminal %s", strings.Join(t.cmd, " "))
	mode := fmt.Sprintf("%s %d%s", config.TerminalPosition, t.Size, t.mode)

	t.Buffer.Create(name, FiletypeTerminal, mode, option)
	t.Buffer.Name = t.Name
	t.Buffer.UpdateSyntax(FiletypeGoTerminal)

	defer t.switchFocus()()

	// Cleanup cursor highlighting
	// TODO(zchee): Can use p.ClearBufferHighlight?
	t.b.Command("highlight TermCursor gui=NONE guifg=NONE guibg=NONE")
	t.b.Command("highlight TermCursorNC gui=NONE guifg=NONE guibg=NONE")

	// Set autoclose buffer if the current buffer is only terminal
	// TODO(zchee): convert to rpc way
	t.b.Command("autocmd WinEnter <buffer> if winnr('$') == 1 | quit | endif")

	return t.b.Execute()
}

// Run runs the command in the terminal buffer.
func (t *Terminal) Run(cmd []string) error {
	if t.Dir != "" {
		defer pathutil.Chdir(t.v, t.Dir)()
	}

	if t.Buffer != nil && IsBufferValid(t.v, t.buffer) {
		defer t.switchFocus()()

		t.v.SetBufferOption(t.buffer, BufOptionModified, false)
		t.v.Call("termopen", nil, cmd)
		t.v.SetBufferName(t.buffer, t.Buffer.Name)
	} else {
		t.Create()
	}
	// Workaround for "autocmd BufEnter term://* startinsert"
	if config.TerminalStopInsert {
		t.v.Command("stopinsert")
	}

	return nil
}

// getWindowSize return the one third of window (height|width) size if cfg is 0
func (t *Terminal) getWindowSize(cfg int64, fn func(nvim.Window) (int, error)) int {
	if cfg == 0 {
		i, err := fn(t.cw)
		if err != nil {
			return 0
		}
		return i / 3
	}
	return int(cfg)
}

// TODO(zchee): flashing when switch the window.
func (t *Terminal) switchFocus() func() {
	t.v.SetCurrentWindow(t.Window)

	return func() {
		t.v.SetCurrentWindow(t.cw)
	}
}

func (t *Terminal) setTerminalOption() map[NvimOption]map[string]interface{} {
	option := make(map[NvimOption]map[string]interface{})
	bufoption := make(map[string]interface{})
	bufvar := make(map[string]interface{})
	windowoption := make(map[string]interface{})

	bufoption[BufOptionBufhidden] = BufhiddenDelete
	bufoption[BufOptionBuflisted] = false
	bufoption[BufOptionBuftype] = BuftypeNofile
	bufoption[BufOptionFiletype] = FiletypeTerminal
	bufoption[BufOptionModifiable] = false
	bufoption[BufOptionSwapfile] = false

	bufvar[BufVarColorcolumn] = ""

	windowoption[WinOptionList] = false
	windowoption[WinOptionNumber] = false
	windowoption[WinOptionRelativenumber] = false
	windowoption[WinOptionWinfixheight] = true

	option[BufferOption] = bufoption
	option[BufferVar] = bufvar
	option[WindowOption] = windowoption

	return option
}
<commit_msg>nvimutil/terminal: rename to {Nvim,Batch} & support scroll to end<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvimutil

import (
	"fmt"
	"strings"

	"nvim-go/config"
	"nvim-go/pathutil"

	"github.com/neovim/go-client/nvim"
	"github.com/pkg/errors"
)

var terminalBufferName = "__GO_TERMINAL__"

// Terminal represents a Neovim terminal.
type Terminal struct {
	cmd  []string
	mode string
	// Name terminal buffer name.
	Name string
	// Dir specifies the working directory of the command on terminal.
	Dir string
	// Size open the terminal window size.
	Size int

	Nvim  *nvim.Nvim
	Batch *nvim.Batch
	cw    nvim.Window

	*Buffer
}

// NewTerminal return the Neovim terminal buffer.
func NewTerminal(n *nvim.Nvim, name string, command []string, mode string) *Terminal {
	if name == "" {
		name = terminalBufferName
	}
	return &Terminal{
		cmd:   command,
		mode:  mode,
		Name:  name,
		Nvim:  n,
		Batch: n.NewBatch(),
	}
}

// Create creats the new Neovim terminal buffer.
func (t *Terminal) Create() (err error) {
	t.cw, err = t.Nvim.CurrentWindow()
	if err != nil {
		return err
	}

	t.Buffer = NewBuffer(t.Nvim)

	switch {
	case t.mode == "split":
		t.Size = t.getSplitWindowSize(config.TerminalHeight, t.Nvim.WindowHeight)
		t.Buffer.Height = t.Size
	case t.mode == "vsplit":
		t.Size = t.getSplitWindowSize(config.TerminalWidth, t.Nvim.WindowWidth)
		t.Buffer.Width = t.Size
	default:
		// nothing to do
	}

	option := t.setTerminalOption()
	name := fmt.Sprintf("| terminal %s", strings.Join(t.cmd, " "))
	mode := fmt.Sprintf("%s %d%s", config.TerminalPosition, t.Size, t.mode)

	t.Buffer.Create(name, FiletypeTerminal, mode, option)
	t.Buffer.Name = t.Name
	t.Buffer.UpdateSyntax(FiletypeGoTerminal)

	defer t.switchFocus()()

	// Cleanup cursor highlighting
	// TODO(zchee): Can use p.ClearBufferHighlight?
	t.Batch.Command("highlight TermCursor gui=NONE guifg=NONE guibg=NONE")
	t.Batch.Command("highlight TermCursorNC gui=NONE guifg=NONE guibg=NONE")

	// Set autoclose buffer if the current buffer is only terminal
	// TODO(zchee): convert to rpc way
	t.Batch.Command("autocmd WinEnter <buffer> if winnr('$') == 1 | quit | endif")

	return t.Batch.Execute()
}

// Run runs the command in the terminal buffer.
func (t *Terminal) Run(cmd []string) error {
	if t.Dir != "" {
		defer pathutil.Chdir(t.Nvim, t.Dir)()
	}

	if t.Buffer != nil && IsBufferValid(t.Nvim, t.buffer) {
		defer t.switchFocus()()

		t.Nvim.SetBufferOption(t.buffer, BufOptionModified, false)
		t.Nvim.Call("termopen", nil, cmd)
		t.Nvim.SetBufferName(t.buffer, t.Buffer.Name)
	} else {
		t.Create()
	}
	// Workaround for "autocmd BufEnter term://* startinsert"
	if config.TerminalStopInsert {
		t.Nvim.Command("stopinsert")
	}

	lines, err := t.Nvim.BufferLineCount(t.buffer)
	if err != nil {
		return errors.WithStack(err)
	}

	return errors.WithStack(t.Nvim.SetWindowCursor(t.Window, [2]int{lines, 0}))
}

// getSplitWindowSize return the one third of window (height|width) size if cfg is 0
func (t *Terminal) getSplitWindowSize(cfg int64, f func(nvim.Window) (int, error)) int {
	if cfg == 0 {
		i, err := f(t.cw)
		if err != nil {
			return 0
		}
		return i / 3
	}
	return int(cfg)
}

// TODO(zchee): flashing when switch the window.
func (t *Terminal) switchFocus() func() {
	t.Nvim.SetCurrentWindow(t.Window)

	return func() {
		t.Nvim.SetCurrentWindow(t.cw)
	}
}

func (t *Terminal) setTerminalOption() map[NvimOption]map[string]interface{} {
	option := make(map[NvimOption]map[string]interface{})
	bufoption := make(map[string]interface{})
	bufvar := make(map[string]interface{})
	windowoption := make(map[string]interface{})

	bufoption[BufOptionBufhidden] = BufhiddenDelete
	bufoption[BufOptionBuflisted] = false
	bufoption[BufOptionBuftype] = BuftypeNofile
	bufoption[BufOptionFiletype] = FiletypeTerminal
	bufoption[BufOptionModifiable] = false
	bufoption[BufOptionSwapfile] = false

	bufvar[BufVarColorcolumn] = ""

	windowoption[WinOptionList] = false
	windowoption[WinOptionNumber] = false
	windowoption[WinOptionRelativenumber] = false
	windowoption[WinOptionWinfixheight] = true

	option[BufferOption] = bufoption
	option[BufferVar] = bufvar
	option[WindowOption] = windowoption

	return option
}
<|endoftext|>


<commit_before><commit_msg>fix(slo): fix SessionIndex attribute in LogoutRequest (#245)<commit_after><|endoftext|>


<commit_before>package service_test

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/sclevine/agouti/core/internal/service"
	"github.com/sclevine/agouti/core/internal/session"
)

var _ = Describe("Service", func() {
	var (
		service *Service
		started bool
	)

	BeforeEach(func() {
		started = false

		fakeServer := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
			if started && request.URL.Path == "/status" {
				response.WriteHeader(200)
			} else {
				response.WriteHeader(400)
			}
		}))

		service = &Service{
			URL:     fakeServer.URL,
			Timeout: 1500 * time.Millisecond,
			Command: []string{"read"},
		}
	})

	Describe("#Start", func() {
		Context("when the service is started multiple times", func() {
			It("returns an error indicating that service is already running", func() {
				defer service.Stop()
				started = true
				Expect(service.Start()).To(Succeed())
				err := service.Start()
				Expect(err).To(MatchError("read is already running"))
			})
		})

		Context("when the binary is not available in PATH", func() {
			It("returns an error indicating the binary needs to be installed", func() {
				service.Command = []string{"not-in-path"}
				err := service.Start()
				Expect(err).To(MatchError("unable to run not-in-path: exec: \"not-in-path\": executable file not found in $PATH"))
			})
		})

		Context("when the service starts before the provided timeout", func() {
			It("does not return an error", func() {
				defer service.Stop()
				go func() {
					time.Sleep(200 * time.Millisecond)
					started = true
				}()
				err := service.Start()
				Expect(err).NotTo(HaveOccurred())
			})
		})

		Context("when the service does not start before the provided timeout", func() {
			It("returns an error", func() {
				defer service.Stop()
				go func() {
					time.Sleep(3000 * time.Millisecond)
					started = true
				}()
				err := service.Start()
				Expect(err).To(MatchError("read webdriver failed to start"))
			})
		})
	})

	Describe("#Stop", func() {
		It("stops a running server", func() {
			defer service.Stop()
			started = true
			service.Start()
			service.Stop()
			err := service.Start()
			Expect(err).NotTo(HaveOccurred())
		})
	})

	Describe("#CreateSession", func() {
		var capabilities *session.Capabilities

		BeforeEach(func() {
			capabilities = &session.Capabilities{BrowserName: "some-browser"}
		})

		Context("when the server is not running", func() {
			It("returns an error", func() {
				_, err := service.CreateSession(capabilities)
				Expect(err).To(MatchError("read not running"))
			})
		})

		Context("when the server is running", func() {
			It("attempts to open a session using the desired capabilties", func() {
				defer service.Stop()
				started = true
				service.Start()
				var requestBody string
				fakeServer := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
					requestBodyBytes, _ := ioutil.ReadAll(request.Body)
					requestBody = string(requestBodyBytes)
					response.Write([]byte(`{"sessionId": "some-id"}`))
				}))
				defer fakeServer.Close()
				service.URL = fakeServer.URL
				newSession, err := service.CreateSession(capabilities)
				Expect(err).NotTo(HaveOccurred())
				Expect(requestBody).To(Equal(`{"desiredCapabilities": {"browserName":"some-browser"}}`))
				Expect(newSession.URL).To(ContainSubstring("/session/some-id"))
			})

			Context("when opening a new session fails", func() {
				It("returns the session error", func() {
					defer service.Stop()
					started = true
					service.Start()
					service.URL = "%@#$%"
					_, err := service.CreateSession(capabilities)
					Expect(err.Error()).To(ContainSubstring(`parse %@: invalid URL escape "%@"`))
				})
			})
		})
	})
})
<commit_msg>Replace 'read' with 'cat' in service test for Travis CI<commit_after>package service_test

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/sclevine/agouti/core/internal/service"
	"github.com/sclevine/agouti/core/internal/session"
)

var _ = Describe("Service", func() {
	var (
		service *Service
		started bool
	)

	BeforeEach(func() {
		started = false

		fakeServer := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
			if started && request.URL.Path == "/status" {
				response.WriteHeader(200)
			} else {
				response.WriteHeader(400)
			}
		}))

		service = &Service{
			URL:     fakeServer.URL,
			Timeout: 1500 * time.Millisecond,
			Command: []string{"cat"},
		}
	})

	Describe("#Start", func() {
		Context("when the service is started multiple times", func() {
			It("returns an error indicating that service is already running", func() {
				defer service.Stop()
				started = true
				Expect(service.Start()).To(Succeed())
				err := service.Start()
				Expect(err).To(MatchError("cat is already running"))
			})
		})

		Context("when the binary is not available in PATH", func() {
			It("returns an error indicating the binary needs to be installed", func() {
				service.Command = []string{"not-in-path"}
				err := service.Start()
				Expect(err).To(MatchError("unable to run not-in-path: exec: \"not-in-path\": executable file not found in $PATH"))
			})
		})

		Context("when the service starts before the provided timeout", func() {
			It("does not return an error", func() {
				defer service.Stop()
				go func() {
					time.Sleep(200 * time.Millisecond)
					started = true
				}()
				err := service.Start()
				Expect(err).NotTo(HaveOccurred())
			})
		})

		Context("when the service does not start before the provided timeout", func() {
			It("returns an error", func() {
				defer service.Stop()
				go func() {
					time.Sleep(3000 * time.Millisecond)
					started = true
				}()
				err := service.Start()
				Expect(err).To(MatchError("cat webdriver failed to start"))
			})
		})
	})

	Describe("#Stop", func() {
		It("stops a running server", func() {
			defer service.Stop()
			started = true
			service.Start()
			service.Stop()
			err := service.Start()
			Expect(err).NotTo(HaveOccurred())
		})
	})

	Describe("#CreateSession", func() {
		var capabilities *session.Capabilities

		BeforeEach(func() {
			capabilities = &session.Capabilities{BrowserName: "some-browser"}
		})

		Context("when the server is not running", func() {
			It("returns an error", func() {
				_, err := service.CreateSession(capabilities)
				Expect(err).To(MatchError("cat not running"))
			})
		})

		Context("when the server is running", func() {
			It("attempts to open a session using the desired capabilties", func() {
				defer service.Stop()
				started = true
				service.Start()
				var requestBody string
				fakeServer := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
					requestBodyBytes, _ := ioutil.ReadAll(request.Body)
					requestBody = string(requestBodyBytes)
					response.Write([]byte(`{"sessionId": "some-id"}`))
				}))
				defer fakeServer.Close()
				service.URL = fakeServer.URL
				newSession, err := service.CreateSession(capabilities)
				Expect(err).NotTo(HaveOccurred())
				Expect(requestBody).To(Equal(`{"desiredCapabilities": {"browserName":"some-browser"}}`))
				Expect(newSession.URL).To(ContainSubstring("/session/some-id"))
			})

			Context("when opening a new session fails", func() {
				It("returns the session error", func() {
					defer service.Stop()
					started = true
					service.Start()
					service.URL = "%@#$%"
					_, err := service.CreateSession(capabilities)
					Expect(err.Error()).To(ContainSubstring(`parse %@: invalid URL escape "%@"`))
				})
			})
		})
	})
})
<|endoftext|>


<commit_before><commit_msg>correct output is porduced, not sure why i get index out of bounds exception<commit_after><|endoftext|>


<commit_before><commit_msg>add err log<commit_after><|endoftext|>


<commit_before><commit_msg>Add change to read certificate without restarting the server.<commit_after><|endoftext|>


<commit_before>package cmd

import (
	"context"

	"github.com/jessevdk/go-flags"

	"github.com/mitchellh/cli"
	"github.com/nerdalize/nerd/svc"
	"github.com/pkg/errors"
)

//DatasetUpload command
type DatasetUpload struct {
	KubeOpts
	TransferOpts
	Name string `long:"name" short:"n" description:"assign a name to the dataset"`

	*command
}

//DatasetUploadFactory creates the command
func DatasetUploadFactory(ui cli.Ui) cli.CommandFactory {
	cmd := &DatasetUpload{}
	cmd.command = createCommand(ui, cmd.Execute, cmd.Description, cmd.Usage, cmd, flags.PassAfterNonOption)
	return func() (cli.Command, error) {
		return cmd, nil
	}
}

//Execute runs the command
func (cmd *DatasetUpload) Execute(args []string) (err error) {
	if len(args) < 1 {
		return errShowUsage(MessageNotEnoughArguments)
	}

	deps, err := NewDeps(cmd.Logger(), cmd.KubeOpts)
	if err != nil {
		return renderConfigError(err, "failed to configure")
	}

	trans, err := cmd.TransferOpts.Transfer()
	if err != nil {
		return errors.Wrap(err, "failed configure transfer")
	}

	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, cmd.Timeout)
	defer cancel()

	ref, err := trans.Upload(ctx, args[0])
	if err != nil {
		return errors.Wrap(err, "failed to perform upload")
	}

	in := &svc.CreateDatasetInput{
		Name:   cmd.Name,
		Bucket: ref.Bucket,
		Key:    ref.Key,
	}

	kube := svc.NewKube(deps)
	out, err := kube.CreateDataset(ctx, in)
	if err != nil {
		return renderServiceError(err, "failed to upload dataset")
	}

	cmd.out.Infof("Upload dataset: '%s'", out.Name)
	cmd.out.Infof("To see available datasets, use: 'nerd dataset list'")
	return nil
}

// Description returns long-form help text
func (cmd *DatasetUpload) Description() string { return cmd.Synopsis() }

// Synopsis returns a one-line
func (cmd *DatasetUpload) Synopsis() string { return "Upload a dataset to your compute cluster." }

// Usage shows usage
func (cmd *DatasetUpload) Usage() string {
	return "nerd dataset upload [--name=] ~/my-project/my-input-1"
}
<commit_msg>changed next step hint to refer to run<commit_after>package cmd

import (
	"context"

	"github.com/jessevdk/go-flags"

	"github.com/mitchellh/cli"
	"github.com/nerdalize/nerd/svc"
	"github.com/pkg/errors"
)

//DatasetUpload command
type DatasetUpload struct {
	KubeOpts
	TransferOpts
	Name string `long:"name" short:"n" description:"assign a name to the dataset"`

	*command
}

//DatasetUploadFactory creates the command
func DatasetUploadFactory(ui cli.Ui) cli.CommandFactory {
	cmd := &DatasetUpload{}
	cmd.command = createCommand(ui, cmd.Execute, cmd.Description, cmd.Usage, cmd, flags.PassAfterNonOption)
	return func() (cli.Command, error) {
		return cmd, nil
	}
}

//Execute runs the command
func (cmd *DatasetUpload) Execute(args []string) (err error) {
	if len(args) < 1 {
		return errShowUsage(MessageNotEnoughArguments)
	}

	deps, err := NewDeps(cmd.Logger(), cmd.KubeOpts)
	if err != nil {
		return renderConfigError(err, "failed to configure")
	}

	trans, err := cmd.TransferOpts.Transfer()
	if err != nil {
		return errors.Wrap(err, "failed configure transfer")
	}

	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, cmd.Timeout)
	defer cancel()

	ref, err := trans.Upload(ctx, args[0])
	if err != nil {
		return errors.Wrap(err, "failed to perform upload")
	}

	in := &svc.CreateDatasetInput{
		Name:   cmd.Name,
		Bucket: ref.Bucket,
		Key:    ref.Key,
	}

	kube := svc.NewKube(deps)
	out, err := kube.CreateDataset(ctx, in)
	if err != nil {
		return renderServiceError(err, "failed to upload dataset")
	}

	cmd.out.Infof("Upload dataset: '%s'", out.Name)
	cmd.out.Infof("To run a job with a dataset, use: 'nerd job run'")
	return nil
}

// Description returns long-form help text
func (cmd *DatasetUpload) Description() string { return cmd.Synopsis() }

// Synopsis returns a one-line
func (cmd *DatasetUpload) Synopsis() string { return "Upload a dataset to your compute cluster." }

// Usage shows usage
func (cmd *DatasetUpload) Usage() string {
	return "nerd dataset upload [--name=] ~/my-project/my-input-1"
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"path/filepath"

	"github.com/constabulary/gb"
	"github.com/constabulary/gb/cmd"
)

var (
	fs          = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	projectroot = os.Getenv("GB_PROJECT_DIR")
	args        []string
)

func init() {
	fs.Usage = func() {
		printUsage(os.Stderr)
		os.Exit(2)
	}
}

var commands = []*cmd.Command{
	cmdFetch,
	cmdUpdate,
	cmdList,
	cmdDelete,
	cmdPurge,
}

func main() {
	args := os.Args[1:]

	switch {
	case len(args) < 1, args[0] == "-h", args[0] == "-help":
		fs.Usage()
		os.Exit(1)
	case projectroot == "":
		gb.Fatalf("don't run this binary directly, it is meant to be run as 'gb vendor ...'")
	case args[0] == "help":
		help(args[1:])
		return
	default:
	}

	root, err := cmd.FindProjectroot(projectroot)
	if err != nil {
		gb.Fatalf("could not locate project root: %v", err)
	}
	project := gb.NewProject(root)
	gb.Debugf("project root %q", project.Projectdir())

	for _, command := range commands {
		if command.Name == args[0] && command.Runnable() {

			// add extra flags if necessary
			if command.AddFlags != nil {
				command.AddFlags(fs)
			}

			if command.FlagParse != nil {
				err = command.FlagParse(fs, args)
			} else {
				err = fs.Parse(args[1:])
			}
			if err != nil {
				gb.Fatalf("could not parse flags: %v", err)
			}
			args = fs.Args() // reset args to the leftovers from fs.Parse
			gb.Debugf("args: %v", args)

			ctx, err := project.NewContext(
				gb.GcToolchain(),
			)
			if err != nil {
				gb.Fatalf("unable to construct context: %v", err)
			}

			if err := command.Run(ctx, args); err != nil {
				gb.Fatalf("command %q failed: %v", command.Name, err)
			}
			return
		}
	}
	gb.Fatalf("unknown command %q ", args[0])
}

const manifestfile = "manifest"

func manifestFile(ctx *gb.Context) string {
	return filepath.Join(ctx.Projectdir(), "vendor", manifestfile)
}
<commit_msg>swap cases so gb-vendor help displays the help text<commit_after>package main

import (
	"flag"
	"os"
	"path/filepath"

	"github.com/constabulary/gb"
	"github.com/constabulary/gb/cmd"
)

var (
	fs          = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	projectroot = os.Getenv("GB_PROJECT_DIR")
	args        []string
)

func init() {
	fs.Usage = func() {
		printUsage(os.Stderr)
		os.Exit(2)
	}
}

var commands = []*cmd.Command{
	cmdFetch,
	cmdUpdate,
	cmdList,
	cmdDelete,
	cmdPurge,
}

func main() {
	args := os.Args[1:]

	switch {
	case len(args) < 1, args[0] == "-h", args[0] == "-help":
		fs.Usage()
		os.Exit(1)
	case args[0] == "help":
		help(args[1:])
		return
	case projectroot == "":
		gb.Fatalf("don't run this binary directly, it is meant to be run as 'gb vendor ...'")
	default:
	}

	root, err := cmd.FindProjectroot(projectroot)
	if err != nil {
		gb.Fatalf("could not locate project root: %v", err)
	}
	project := gb.NewProject(root)
	gb.Debugf("project root %q", project.Projectdir())

	for _, command := range commands {
		if command.Name == args[0] && command.Runnable() {

			// add extra flags if necessary
			if command.AddFlags != nil {
				command.AddFlags(fs)
			}

			if command.FlagParse != nil {
				err = command.FlagParse(fs, args)
			} else {
				err = fs.Parse(args[1:])
			}
			if err != nil {
				gb.Fatalf("could not parse flags: %v", err)
			}
			args = fs.Args() // reset args to the leftovers from fs.Parse
			gb.Debugf("args: %v", args)

			ctx, err := project.NewContext(
				gb.GcToolchain(),
			)
			if err != nil {
				gb.Fatalf("unable to construct context: %v", err)
			}

			if err := command.Run(ctx, args); err != nil {
				gb.Fatalf("command %q failed: %v", command.Name, err)
			}
			return
		}
	}
	gb.Fatalf("unknown command %q ", args[0])
}

const manifestfile = "manifest"

func manifestFile(ctx *gb.Context) string {
	return filepath.Join(ctx.Projectdir(), "vendor", manifestfile)
}
<|endoftext|>


<commit_before>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package handlers

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"strconv"
	"time"

	"os"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/gorilla/mux"
	"github.com/openfaas/faas/gateway/metrics"
	"github.com/openfaas/faas/gateway/requests"
	"github.com/prometheus/client_golang/prometheus"
)

// MakeProxy creates a proxy for HTTP web requests which can be routed to a function.
func MakeProxy(metrics metrics.MetricOptions, wildcard bool, client *client.Client, logger *logrus.Logger) http.HandlerFunc {
	proxyClient := http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   3 * time.Second,
				KeepAlive: 0,
			}).DialContext,
			MaxIdleConns:          1,
			DisableKeepAlives:     true,
			IdleConnTimeout:       120 * time.Millisecond,
			ExpectContinueTimeout: 1500 * time.Millisecond,
		},
	}

	return func(w http.ResponseWriter, r *http.Request) {
		defer r.Body.Close()

		switch r.Method {
		case "POST", "GET":
			logger.Infoln(r.Header)

			xFunctionHeader := r.Header["X-Function"]
			if len(xFunctionHeader) > 0 {
				logger.Infoln(xFunctionHeader)
			}

			// getServiceName
			var serviceName string
			if wildcard {
				vars := mux.Vars(r)
				name := vars["name"]
				serviceName = name
			} else if len(xFunctionHeader) > 0 {
				serviceName = xFunctionHeader[0]
			}

			if len(serviceName) > 0 {
				lookupInvoke(w, r, metrics, serviceName, client, logger, &proxyClient)
			} else {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte("Provide an x-function header or valid route /function/function_name."))
			}
			break
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	}
}

func lookupInvoke(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, name string, c *client.Client, logger *logrus.Logger, proxyClient *http.Client) {
	exists, err := lookupSwarmService(name, c)

	if err != nil || exists == false {
		if err != nil {
			logger.Infof("Could not resolve service: %s error: %s.", name, err)
		}

		// TODO: Should record the 404/not found error in Prometheus.
		writeHead(name, metrics, http.StatusNotFound, w)
		w.Write([]byte(fmt.Sprintf("Cannot find service: %s.", name)))
	}

	if exists {
		defer trackTime(time.Now(), metrics, name)
		forwardReq := requests.NewForwardRequest(r.Method, *r.URL)

		requestBody, _ := ioutil.ReadAll(r.Body)
		invokeService(w, r, metrics, name, forwardReq, requestBody, logger, proxyClient)
	}
}

func lookupSwarmService(serviceName string, c *client.Client) (bool, error) {
	fmt.Printf("Resolving: '%s'\n", serviceName)
	serviceFilter := filters.NewArgs()
	serviceFilter.Add("name", serviceName)
	services, err := c.ServiceList(context.Background(), types.ServiceListOptions{Filters: serviceFilter})

	return len(services) > 0, err
}

func invokeService(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, service string, forwardReq requests.ForwardRequest, requestBody []byte, logger *logrus.Logger, proxyClient *http.Client) {
	stamp := strconv.FormatInt(time.Now().Unix(), 10)

	defer func(when time.Time) {
		seconds := time.Since(when).Seconds()

		fmt.Printf("[%s] took %f seconds\n", stamp, seconds)
		metrics.GatewayFunctionsHistogram.WithLabelValues(service).Observe(seconds)
	}(time.Now())

	//TODO: inject setting rather than looking up each time.
	var dnsrr bool
	if os.Getenv("dnsrr") == "true" {
		dnsrr = true
	}

	watchdogPort := 8080

	addr := service
	// Use DNS-RR via tasks.servicename if enabled as override, otherwise VIP.
	if dnsrr {
		entries, lookupErr := net.LookupIP(fmt.Sprintf("tasks.%s", service))
		if lookupErr == nil && len(entries) > 0 {
			index := randomInt(0, len(entries))
			addr = entries[index].String()
		}
	}

	url := forwardReq.ToURL(addr, watchdogPort)

	contentType := r.Header.Get("Content-Type")
	fmt.Printf("[%s] Forwarding request [%s] to: %s\n", stamp, contentType, url)

	request, err := http.NewRequest("POST", url, bytes.NewReader(requestBody))

	copyHeaders(&request.Header, &r.Header)

	defer request.Body.Close()

	response, err := proxyClient.Do(request)
	if err != nil {
		logger.Infoln(err)
		writeHead(service, metrics, http.StatusInternalServerError, w)
		buf := bytes.NewBufferString("Can't reach service: " + service)
		w.Write(buf.Bytes())
		return
	}

	responseBody, readErr := ioutil.ReadAll(response.Body)
	if readErr != nil {
		fmt.Println(readErr)

		writeHead(service, metrics, http.StatusInternalServerError, w)
		buf := bytes.NewBufferString("Error reading response from service: " + service)
		w.Write(buf.Bytes())
		return
	}

	clientHeader := w.Header()
	copyHeaders(&clientHeader, &response.Header)

	defaultHeader := "text/plain"

	w.Header().Set("Content-Type", GetContentType(response.Header, r.Header, defaultHeader))

	writeHead(service, metrics, response.StatusCode, w)
	w.Write(responseBody)
}

// GetContentType resolves the correct Content-Tyoe for a proxied function
func GetContentType(request http.Header, proxyResponse http.Header, defaultValue string) string {
	responseHeader := proxyResponse.Get("Content-Type")
	requestHeader := request.Get("Content-Type")

	var headerContentType string
	if len(responseHeader) > 0 {
		headerContentType = responseHeader
	} else if len(requestHeader) > 0 {
		headerContentType = requestHeader
	} else {
		headerContentType = defaultValue
	}

	return headerContentType
}

func copyHeaders(destination *http.Header, source *http.Header) {
	for k, vv := range *source {
		vvClone := make([]string, len(vv))
		copy(vvClone, vv)
		(*destination)[k] = vvClone
	}
}

func randomInt(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func writeHead(service string, metrics metrics.MetricOptions, code int, w http.ResponseWriter) {
	w.WriteHeader(code)

	trackInvocation(service, metrics, code)
}

func trackInvocation(service string, metrics metrics.MetricOptions, code int) {
	metrics.GatewayFunctionInvocation.With(prometheus.Labels{"function_name": service, "code": strconv.Itoa(code)}).Inc()
}

func trackTime(then time.Time, metrics metrics.MetricOptions, name string) {
	since := time.Since(then)
	metrics.GatewayFunctionsHistogram.WithLabelValues(name).Observe(since.Seconds())
}

func trackTimeExact(duration time.Duration, metrics metrics.MetricOptions, name string) {
	metrics.GatewayFunctionsHistogram.WithLabelValues(name).Observe(float64(duration))
}
<commit_msg>Pass on method<commit_after>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package handlers

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"strconv"
	"time"

	"os"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/gorilla/mux"
	"github.com/openfaas/faas/gateway/metrics"
	"github.com/openfaas/faas/gateway/requests"
	"github.com/prometheus/client_golang/prometheus"
)

// MakeProxy creates a proxy for HTTP web requests which can be routed to a function.
func MakeProxy(metrics metrics.MetricOptions, wildcard bool, client *client.Client, logger *logrus.Logger) http.HandlerFunc {
	proxyClient := http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   3 * time.Second,
				KeepAlive: 0,
			}).DialContext,
			MaxIdleConns:          1,
			DisableKeepAlives:     true,
			IdleConnTimeout:       120 * time.Millisecond,
			ExpectContinueTimeout: 1500 * time.Millisecond,
		},
	}

	return func(w http.ResponseWriter, r *http.Request) {
		defer r.Body.Close()

		switch r.Method {
		case "POST", "GET":
			logger.Infoln(r.Header)

			xFunctionHeader := r.Header["X-Function"]
			if len(xFunctionHeader) > 0 {
				logger.Infoln(xFunctionHeader)
			}

			// getServiceName
			var serviceName string
			if wildcard {
				vars := mux.Vars(r)
				name := vars["name"]
				serviceName = name
			} else if len(xFunctionHeader) > 0 {
				serviceName = xFunctionHeader[0]
			}

			if len(serviceName) > 0 {
				lookupInvoke(w, r, metrics, serviceName, client, logger, &proxyClient)
			} else {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte("Provide an x-function header or valid route /function/function_name."))
			}
			break
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	}
}

func lookupInvoke(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, name string, c *client.Client, logger *logrus.Logger, proxyClient *http.Client) {
	exists, err := lookupSwarmService(name, c)

	if err != nil || exists == false {
		if err != nil {
			logger.Infof("Could not resolve service: %s error: %s.", name, err)
		}

		// TODO: Should record the 404/not found error in Prometheus.
		writeHead(name, metrics, http.StatusNotFound, w)
		w.Write([]byte(fmt.Sprintf("Cannot find service: %s.", name)))
	}

	if exists {
		defer trackTime(time.Now(), metrics, name)
		forwardReq := requests.NewForwardRequest(r.Method, *r.URL)

		requestBody, _ := ioutil.ReadAll(r.Body)
		invokeService(w, r, metrics, name, forwardReq, requestBody, logger, proxyClient)
	}
}

func lookupSwarmService(serviceName string, c *client.Client) (bool, error) {
	fmt.Printf("Resolving: '%s'\n", serviceName)
	serviceFilter := filters.NewArgs()
	serviceFilter.Add("name", serviceName)
	services, err := c.ServiceList(context.Background(), types.ServiceListOptions{Filters: serviceFilter})

	return len(services) > 0, err
}

func invokeService(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, service string, forwardReq requests.ForwardRequest, requestBody []byte, logger *logrus.Logger, proxyClient *http.Client) {
	stamp := strconv.FormatInt(time.Now().Unix(), 10)

	defer func(when time.Time) {
		seconds := time.Since(when).Seconds()

		fmt.Printf("[%s] took %f seconds\n", stamp, seconds)
		metrics.GatewayFunctionsHistogram.WithLabelValues(service).Observe(seconds)
	}(time.Now())

	//TODO: inject setting rather than looking up each time.
	var dnsrr bool
	if os.Getenv("dnsrr") == "true" {
		dnsrr = true
	}

	watchdogPort := 8080

	addr := service
	// Use DNS-RR via tasks.servicename if enabled as override, otherwise VIP.
	if dnsrr {
		entries, lookupErr := net.LookupIP(fmt.Sprintf("tasks.%s", service))
		if lookupErr == nil && len(entries) > 0 {
			index := randomInt(0, len(entries))
			addr = entries[index].String()
		}
	}

	url := forwardReq.ToURL(addr, watchdogPort)

	contentType := r.Header.Get("Content-Type")
	fmt.Printf("[%s] Forwarding request [%s] to: %s\n", stamp, contentType, url)

	request, err := http.NewRequest(r.Method, url, bytes.NewReader(requestBody))

	copyHeaders(&request.Header, &r.Header)

	defer request.Body.Close()

	response, err := proxyClient.Do(request)
	if err != nil {
		logger.Infoln(err)
		writeHead(service, metrics, http.StatusInternalServerError, w)
		buf := bytes.NewBufferString("Can't reach service: " + service)
		w.Write(buf.Bytes())
		return
	}

	responseBody, readErr := ioutil.ReadAll(response.Body)
	if readErr != nil {
		fmt.Println(readErr)

		writeHead(service, metrics, http.StatusInternalServerError, w)
		buf := bytes.NewBufferString("Error reading response from service: " + service)
		w.Write(buf.Bytes())
		return
	}

	clientHeader := w.Header()
	copyHeaders(&clientHeader, &response.Header)

	defaultHeader := "text/plain"

	w.Header().Set("Content-Type", GetContentType(response.Header, r.Header, defaultHeader))

	writeHead(service, metrics, response.StatusCode, w)
	w.Write(responseBody)
}

// GetContentType resolves the correct Content-Tyoe for a proxied function
func GetContentType(request http.Header, proxyResponse http.Header, defaultValue string) string {
	responseHeader := proxyResponse.Get("Content-Type")
	requestHeader := request.Get("Content-Type")

	var headerContentType string
	if len(responseHeader) > 0 {
		headerContentType = responseHeader
	} else if len(requestHeader) > 0 {
		headerContentType = requestHeader
	} else {
		headerContentType = defaultValue
	}

	return headerContentType
}

func copyHeaders(destination *http.Header, source *http.Header) {
	for k, vv := range *source {
		vvClone := make([]string, len(vv))
		copy(vvClone, vv)
		(*destination)[k] = vvClone
	}
}

func randomInt(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func writeHead(service string, metrics metrics.MetricOptions, code int, w http.ResponseWriter) {
	w.WriteHeader(code)

	trackInvocation(service, metrics, code)
}

func trackInvocation(service string, metrics metrics.MetricOptions, code int) {
	metrics.GatewayFunctionInvocation.With(prometheus.Labels{"function_name": service, "code": strconv.Itoa(code)}).Inc()
}

func trackTime(then time.Time, metrics metrics.MetricOptions, name string) {
	since := time.Since(then)
	metrics.GatewayFunctionsHistogram.WithLabelValues(name).Observe(since.Seconds())
}

func trackTimeExact(duration time.Duration, metrics metrics.MetricOptions, name string) {
	metrics.GatewayFunctionsHistogram.WithLabelValues(name).Observe(float64(duration))
}
<|endoftext|>