<commit_before>package service

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"shipshape/util/file"
	strset "shipshape/util/strings"
	"third_party/kythe/go/rpc/client"
	"third_party/kythe/go/rpc/server"

	"code.google.com/p/goprotobuf/proto"

	notepb "shipshape/proto/note_proto"
	contextpb "shipshape/proto/shipshape_context_proto"
	rpcpb "shipshape/proto/shipshape_rpc_proto"
)

const (
	// How long to wait for an analyzer service to become healthy.
	analyzerHealthTimeout = 30 * time.Second
	configFilename        = ".shipshape"
)

var (
	clients = make(map[string]*client.Client)
)

type ShipshapeDriver struct {
	AnalyzerLocations []string
	// catMap is a mapping from analyzer locations to the categories they have available.
	// The range of catMap is the same as AnalyzerLocations
	catMap map[string]strset.Set
}

type dispatcherCategories struct {
	dispatcher string
	categories strset.Set
}

// NewDriver creates a new driver with with the analyzers at the
// specified locations. This func makes no rpcs.
func NewDriver(analyzerLocations []string) *ShipshapeDriver {
	var addrs []string

	for _, addr := range analyzerLocations {
		addrs = append(addrs, strings.TrimPrefix(addr, "http://"))
	}
	return &ShipshapeDriver{AnalyzerLocations: addrs}
}

// NewTestDriver is only for testing. It creates a ShipshapeDriver
// with the address to categories map preset.
func NewTestDriver(addrToCats map[string]strset.Set) *ShipshapeDriver {
	var addrs []string
	var catMap = make(map[string]strset.Set)
	for addr, cats := range addrToCats {
		trimmed := strings.TrimPrefix(addr, "http://")
		addrs = append(addrs, trimmed)
		catMap[trimmed] = cats
	}
	return &ShipshapeDriver{AnalyzerLocations: addrs, catMap: catMap}
}

// Run runs the analyzers that this driver knows about on the provided ShipshapeRequest,
// taking configuration into account.
func (td ShipshapeDriver) Run(ctx server.Context, in *rpcpb.ShipshapeRequest, out chan<- *rpcpb.ShipshapeResponse) error {
	var ars []*rpcpb.AnalyzeResponse
	// TODO(ciera): If we ever have a long-lived service, getting the categories
	// should be done once, not on each Run request!
	td.catMap = td.getAllCategories()

	// However we exit, send back the set of collected AnalyzeResponses
	defer func() {
		out <- &rpcpb.ShipshapeResponse{
			AnalyzeResponse: ars,
		}
	}()

	// Fill in the file_paths if they are empty in the context
	context := proto.Clone(in.ShipshapeContext).(*contextpb.ShipshapeContext)
	root := context.GetRepoRoot()
	if len(context.GetFilePath()) == 0 {
		log.Printf("No files, getting some")
		newpaths, err := collectAllFiles(root)
		if err != nil {
			ars = append(ars, generateFailure("Driver setup", fmt.Sprint(err)))
			return err
		}
		context.FilePath = newpaths
		log.Printf("Newly supplied files: %v", context.GetFilePath())
	}

	orgDir, restore, err := file.ChangeDir(root)
	if err != nil {
		log.Printf("Could not change into directory %s from base %s", root, orgDir)
		ars = append(ars, generateFailure("Driver setup", fmt.Sprint(err)))
		return err
	}
	defer func() {
		if err := restore(); err != nil {
			log.Printf("could not return back into %s from %s: %v", orgDir, root, err)
		}
	}()

	cfg, err := loadConfig(configFilename, *in.Event)
	if err != nil {
		// TODO(collinwinter): attach the error to the config file.
		ars = append(ars, generateFailure("Driver setup", err.Error()))
		return err
	}
	if cfg == nil {
		log.Printf("No analysis configuration file found, doing nothing")
		return nil
	}

	// If the user told us explicitly which categories to run, use that list
	// instead.
	// TODO(collinwinter): nail down exactly how triggering interacts with a config file.
	if len(in.TriggeredCategory) > 0 {
		cfg.categories = in.TriggeredCategory
	}
	if len(cfg.categories) == 0 {
		log.Printf("No categories configured to run, doing nothing")
		return nil
	}
	// TODO(collinwinter): detect if the user specified a category that doesn't exist.

	// If the only files we know to analyze are covered by the list of files
	// to ignore, don't waste time doing anything. We subsequently filter
	// analysis results down to only the relevant files.
	context.FilePath = filterPaths(cfg.ignore, context.FilePath)
	if len(context.FilePath) == 0 {
		log.Printf("No files to run on, doing nothing")
		return nil
	}

	log.Printf("Processing with config %v", cfg)

	ars = append(ars, td.callAllAnalyzers(cfg, context)...)
	return nil
}

// WaitForAnalyzers witll wait for all the given analyzers to become healthy
// That is, their service is up and ready to serve requests.
// Returns a mapping of which analyzers had which errors.
func WaitForAnalyzers(analyzerList []string) map[string]error {
	var wg sync.WaitGroup
	var health = make(map[string]error)

	for _, analyzerAddr := range analyzerList {
		wg.Add(1)
		go func(addr string) {
			httpClient := getHTTPClient(addr)
			err := httpClient.WaitUntilReady(analyzerHealthTimeout)
			health[addr] = err
			wg.Done()
		}(analyzerAddr)
	}
	wg.Wait()
	return health
}

// collectAllFiles returns a list of all files for the passed-in root
func collectAllFiles(root string) ([]string, error) {
	var paths []string
	walkpath := func(path string, f os.FileInfo, err error) error {
		if f == nil {
			return nil
		}
		// Skip directories starting with "."
		dot := strings.HasPrefix(f.Name(), ".")
		if f.IsDir() && dot {
			return filepath.SkipDir
		}
		if !f.IsDir() && !dot {
			relpath, err := filepath.Rel(root, path)
			if err != nil {
				return err
			}
			paths = append(paths, relpath)
		}
		return nil
	}
	if err := filepath.Walk(root, walkpath); err != nil {
		return nil, err
	}
	return paths, nil
}

// allCats returns the entire set of categories for the driver, across all analyzers
func (td ShipshapeDriver) allCats() strset.Set {
	var catSet = strset.New()
	for _, cats := range td.catMap {
		catSet.AddSet(cats)
	}
	return catSet
}

// filterPaths drops paths that fall under one of the given directories to ignore.
// All directory names are assumed to end with /.
func filterPaths(ignoreDirs []string, filePaths []string) []string {
	var keepPaths []string
nextFile:
	for _, file := range filePaths {
		for _, dir := range ignoreDirs {
			if strings.HasPrefix(file, dir) {
				continue nextFile
			}
		}
		keepPaths = append(keepPaths, file)
	}
	return keepPaths
}

// callAllAnalyzers loops through the analyzer services, determines whether analyze should be called
// on each, and then calls it with the appropriate set of files and categories.
// It takes the configuration and the original context, and returns a slice of AnalyzeResponses.
func (td ShipshapeDriver) callAllAnalyzers(cfg *config, context *contextpb.ShipshapeContext) []*rpcpb.AnalyzeResponse {
	desiredCats := strset.New(cfg.categories...)

	var ars []*rpcpb.AnalyzeResponse
	var chans []chan *rpcpb.AnalyzeResponse
	for analyzer, providedCats := range td.catMap {
		cats := providedCats.Intersect(desiredCats)

		log.Printf("Analyzer %s filtered to categories %v and files %v", analyzer, cats, context.FilePath)

		// If there are any categories to run on for this analyzer service,
		// go ahead and call analyze
		if len(cats) > 0 {
			c := make(chan *rpcpb.AnalyzeResponse)
			chans = append(chans, c)
			req := &rpcpb.AnalyzeRequest{
				ShipshapeContext: context,
				Category:         cats.ToSlice(),
			}
			go callAnalyze(analyzer, req, c)
		}
	}

	// Collect up all the responses where we actually called analyze
	for _, c := range chans {
		ar := <-c
		ars = append(ars, filterResults(context, ar))
	}
	return ars
}

// filterResults removes any notes where the category is nil, the category is not specified for
// the file path by the configuration, or there is no location with a source context.
// The config category and internal failure category cannot be turned off.
func filterResults(context *contextpb.ShipshapeContext, response *rpcpb.AnalyzeResponse) *rpcpb.AnalyzeResponse {
	files := strset.New(context.FilePath...)
	var keep []*notepb.Note
	for _, note := range response.Note {
		if note.Category != nil {
			if note.Location != nil && note.Location.SourceContext != nil && (note.Location.Path == nil || files.Contains(*note.Location.Path)) {
				keep = append(keep, note)
			}
		}
	}

	return &rpcpb.AnalyzeResponse{
		Note:    keep,
		Failure: response.Failure,
	}
}

// getAllCategories loops through the analyzers and gets the categories for each of them.
// It returns a mapping from the analyzer address to the set of categories it provides.
func (td ShipshapeDriver) getAllCategories() map[string]strset.Set {
	categories := make(map[string]strset.Set)
	var catChans []chan dispatcherCategories
	for _, analyzer := range td.AnalyzerLocations {
		c := make(chan dispatcherCategories)
		catChans = append(catChans, c)
		go callGetCategories(analyzer, c)
	}
	for _, c := range catChans {
		dispatcherCats := <-c
		categories[dispatcherCats.dispatcher] = dispatcherCats.categories
	}
	return categories
}

// callGetCategories requests the categories for the specified analyzer and puts them onto the
// channel provided. If anything goes wrong, it returns the empty set.
func callGetCategories(analyzer string, out chan<- dispatcherCategories) {
	httpClient := getHTTPClient(analyzer)
	var resp rpcpb.GetCategoryResponse
	var cats strset.Set
	err := httpClient.Call("/AnalyzerService/GetCategory", &rpcpb.GetCategoryRequest{}, &resp)
	if err != nil {
		log.Printf("Could not get categories from %s: %v", analyzer, err)
		cats = strset.New()
	} else {
		cats = strset.New(resp.Category...)
	}
	out <- dispatcherCategories{
		dispatcher: analyzer,
		categories: cats,
	}
}

// callAnalyze attempts to call analyze for the specified analyzer with the given request.
// If anything goes wrong, it puts an AnalysisFailure into the AnalyzeResponse.
func callAnalyze(analyzer string, req *rpcpb.AnalyzeRequest, out chan<- *rpcpb.AnalyzeResponse) {
	httpClient := getHTTPClient(analyzer)
	var resp rpcpb.AnalyzeResponse
	err := httpClient.Call("/AnalyzerService/Analyze", req, &resp)
	if err != nil {
		out <- &rpcpb.AnalyzeResponse{
			Failure: []*rpcpb.AnalysisFailure{
				&rpcpb.AnalysisFailure{
					FailureMessage: proto.String(fmt.Sprintf("Error from analyzer %s: %v", analyzer, err)),
				},
			},
		}
	} else {
		out <- &resp
	}
}

// generateFailure creates a response with an analysis failure containing the given
// category and message
func generateFailure(cat string, message string) *rpcpb.AnalyzeResponse {
	return &rpcpb.AnalyzeResponse{
		Failure: []*rpcpb.AnalysisFailure{
			&rpcpb.AnalysisFailure{
				Category:       proto.String(cat),
				FailureMessage: proto.String(message),
			},
		},
	}
}

// getHTTPClient provides a (cached) HTTPClient for the address specified.
func getHTTPClient(addr string) *client.Client {
	httpClient, exists := clients[addr]
	if !exists {
		clients[addr] = client.NewHTTPClient(addr)
		httpClient = clients[addr]
	}
	return httpClient
}
<commit_msg>Fixed some problems in how configuration is handled<commit_after>package service

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"shipshape/util/file"
	strset "shipshape/util/strings"
	"third_party/kythe/go/rpc/client"
	"third_party/kythe/go/rpc/server"

	"code.google.com/p/goprotobuf/proto"

	notepb "shipshape/proto/note_proto"
	contextpb "shipshape/proto/shipshape_context_proto"
	rpcpb "shipshape/proto/shipshape_rpc_proto"
)

const (
	// How long to wait for an analyzer service to become healthy.
	analyzerHealthTimeout = 30 * time.Second
	configFilename        = ".shipshape"
)

var (
	clients = make(map[string]*client.Client)
)

type ShipshapeDriver struct {
	AnalyzerLocations []string
	// catMap is a mapping from analyzer locations to the categories they have available.
	// The range of catMap is the same as AnalyzerLocations
	catMap map[string]strset.Set
}

type dispatcherCategories struct {
	dispatcher string
	categories strset.Set
}

// NewDriver creates a new driver with with the analyzers at the
// specified locations. This func makes no rpcs.
func NewDriver(analyzerLocations []string) *ShipshapeDriver {
	var addrs []string

	for _, addr := range analyzerLocations {
		addrs = append(addrs, strings.TrimPrefix(addr, "http://"))
	}
	return &ShipshapeDriver{AnalyzerLocations: addrs}
}

// NewTestDriver is only for testing. It creates a ShipshapeDriver
// with the address to categories map preset.
func NewTestDriver(addrToCats map[string]strset.Set) *ShipshapeDriver {
	var addrs []string
	var catMap = make(map[string]strset.Set)
	for addr, cats := range addrToCats {
		trimmed := strings.TrimPrefix(addr, "http://")
		addrs = append(addrs, trimmed)
		catMap[trimmed] = cats
	}
	return &ShipshapeDriver{AnalyzerLocations: addrs, catMap: catMap}
}

// Run runs the analyzers that this driver knows about on the provided ShipshapeRequest,
// taking configuration into account.
func (td ShipshapeDriver) Run(ctx server.Context, in *rpcpb.ShipshapeRequest, out chan<- *rpcpb.ShipshapeResponse) error {
	var ars []*rpcpb.AnalyzeResponse
	log.Printf("Received analysis request for event %v, categories %v, repo %v", *in.Event, in.TriggeredCategory, *in.ShipshapeContext.RepoRoot)
	// However we exit, send back the set of collected AnalyzeResponses
	defer func() {
		out <- &rpcpb.ShipshapeResponse{
			AnalyzeResponse: ars,
		}
	}()

	if in.ShipshapeContext.RepoRoot == nil {
		return fmt.Errorf("No repo root was set")
	}
	root := *in.ShipshapeContext.RepoRoot

	// cd into the root directory
	orgDir, restore, err := file.ChangeDir(root)
	if err != nil {
		log.Printf("Could not change into directory %s from base %s", root, orgDir)
		ars = append(ars, generateFailure("Driver setup", fmt.Sprint(err)))
		return err
	}
	defer func() {
		if err := restore(); err != nil {
			log.Printf("could not return back into %s from %s: %v", orgDir, root, err)
		}
	}()

	// load the config, if it exists
	cfg, err := loadConfig(configFilename, *in.Event)
	if err != nil {
		log.Print("error loading config")
		// TODO(collinwinter): attach the error to the config file.
		ars = append(ars, generateFailure("Driver setup", err.Error()))
		return err
	}

	// Use the triggered categories if specified
	var desiredCats strset.Set
	if len(in.TriggeredCategory) > 0 {
		desiredCats = strset.New(in.TriggeredCategory...)
	} else if cfg != nil {
		desiredCats = strset.New(cfg.categories...)
	} else {
		return fmt.Errorf("service needs to be called with triggered categories and/or a repo root with a valid %s file with the event %s", configFilename, *in.Event)
	}

	// Find out what categories we have available, and remove/warn on the missing ones
	td.catMap = td.getAllCategories()
	allCats := td.allCats()
	missingCats := strset.New().AddSet(desiredCats).RemoveSet(allCats)
	for missing := range missingCats {
		ars = append(ars, generateFailure(missing, fmt.Sprintf("The triggered category %q could not be found at the locations %v", missing, td.AnalyzerLocations)))
	}
	desiredCats = desiredCats.RemoveSet(missingCats)

	if len(desiredCats) == 0 {
		log.Printf("No categories configured to run, doing nothing")
		return nil
	}

	ignorePaths := []string{}
	if cfg != nil {
		ignorePaths = cfg.ignore
	}
	// Fill in the file_paths if they are empty in the context
	context := proto.Clone(in.ShipshapeContext).(*contextpb.ShipshapeContext)
	context.FilePath, err = retrieveAndFilterFiles(*context.RepoRoot, context.FilePath, ignorePaths)
	if err != nil {
		log.Print("Had problems accessing files: %v", err.Error())
		ars = append(ars, generateFailure("Driver setup", fmt.Sprint(err)))
		return err
	}
	if len(context.FilePath) == 0 {
		log.Printf("No files to run on, doing nothing")
		return nil
	}

	log.Print("Analyzing")
	ars = append(ars, td.callAllAnalyzers(desiredCats, context)...)
	log.Print("Analysis completed")
	return nil
}

// WaitForAnalyzers witll wait for all the given analyzers to become healthy
// That is, their service is up and ready to serve requests.
// Returns a mapping of which analyzers had which errors.
func WaitForAnalyzers(analyzerList []string) map[string]error {
	var wg sync.WaitGroup
	var health = make(map[string]error)

	for _, analyzerAddr := range analyzerList {
		wg.Add(1)
		go func(addr string) {
			httpClient := getHTTPClient(addr)
			err := httpClient.WaitUntilReady(analyzerHealthTimeout)
			health[addr] = err
			wg.Done()
		}(analyzerAddr)
	}
	wg.Wait()
	return health
}

// retrieveAndFilter files returns a list of files (initiated with files if that is non-empty,
// or from recursing on root if it is) and removes the ones in the ignore list.
func retrieveAndFilterFiles(root string, files []string, ignore []string) ([]string, error) {
	if len(files) == 0 {
		log.Printf("No files, getting some")
		var err error
		files, err = collectAllFiles(root)
		if err != nil {
			return nil, err
		}
	}

	return filterPaths(ignore, files), nil
}

// collectAllFiles returns a list of all files for the passed-in root
func collectAllFiles(root string) ([]string, error) {
	var paths []string
	walkpath := func(path string, f os.FileInfo, err error) error {
		if f == nil {
			return nil
		}
		// Skip directories starting with "."
		dot := strings.HasPrefix(f.Name(), ".")
		if f.IsDir() && dot {
			return filepath.SkipDir
		}
		if !f.IsDir() && !dot {
			relpath, err := filepath.Rel(root, path)
			if err != nil {
				return err
			}
			paths = append(paths, relpath)
		}
		return nil
	}
	if err := filepath.Walk(root, walkpath); err != nil {
		return nil, err
	}
	return paths, nil
}

// filterPaths drops paths that fall under one of the given directories to ignore.
// All directory names are assumed to end with /.
func filterPaths(ignoreDirs []string, filePaths []string) []string {
	var keepPaths []string
nextFile:
	for _, file := range filePaths {
		for _, dir := range ignoreDirs {
			if strings.HasPrefix(file, dir) {
				continue nextFile
			}
		}
		keepPaths = append(keepPaths, file)
	}
	return keepPaths
}

// callAllAnalyzers loops through the analyzer services, determines whether analyze should be called
// on each, and then calls it with the appropriate set of files and categories.
// It takes the configuration and the original context, and returns a slice of AnalyzeResponses.
func (td ShipshapeDriver) callAllAnalyzers(desiredCats strset.Set, context *contextpb.ShipshapeContext) []*rpcpb.AnalyzeResponse {
	var ars []*rpcpb.AnalyzeResponse
	var chans []chan *rpcpb.AnalyzeResponse
	for analyzer, providedCats := range td.catMap {
		cats := providedCats.Intersect(desiredCats)

		log.Printf("Analyzer %s filtered to categories %v and files %v", analyzer, cats, context.FilePath)

		// If there are any categories to run on for this analyzer service,
		// go ahead and call analyze
		if len(cats) > 0 {
			c := make(chan *rpcpb.AnalyzeResponse)
			chans = append(chans, c)
			req := &rpcpb.AnalyzeRequest{
				ShipshapeContext: context,
				Category:         cats.ToSlice(),
			}
			go callAnalyze(analyzer, req, c)
		}
	}

	// Collect up all the responses where we actually called analyze
	for _, c := range chans {
		ar := <-c
		ars = append(ars, filterResults(context, ar))
	}
	return ars
}

// filterResults removes any notes where the category is nil, the category is not specified for
// the file path by the configuration, or there is no location with a source context.
// The config category and internal failure category cannot be turned off.
func filterResults(context *contextpb.ShipshapeContext, response *rpcpb.AnalyzeResponse) *rpcpb.AnalyzeResponse {
	files := strset.New(context.FilePath...)
	var keep []*notepb.Note
	for _, note := range response.Note {
		if note.Category != nil {
			if note.Location != nil && note.Location.SourceContext != nil && (note.Location.Path == nil || files.Contains(*note.Location.Path)) {
				keep = append(keep, note)
			}
		}
	}

	return &rpcpb.AnalyzeResponse{
		Note:    keep,
		Failure: response.Failure,
	}
}

// allCats returns the entire set of categories for the driver, across all analyzers
func (td ShipshapeDriver) allCats() strset.Set {
	var catSet = strset.New()
	for _, cats := range td.catMap {
		catSet.AddSet(cats)
	}
	return catSet
}

// getAllCategories loops through the analyzers and gets the categories for each of them.
// It returns a mapping from the analyzer address to the set of categories it provides.
func (td ShipshapeDriver) getAllCategories() map[string]strset.Set {
	categories := make(map[string]strset.Set)
	var catChans []chan dispatcherCategories
	for _, analyzer := range td.AnalyzerLocations {
		c := make(chan dispatcherCategories)
		catChans = append(catChans, c)
		go callGetCategories(analyzer, c)
	}
	for _, c := range catChans {
		dispatcherCats := <-c
		categories[dispatcherCats.dispatcher] = dispatcherCats.categories
	}
	return categories
}

// callGetCategories requests the categories for the specified analyzer and puts them onto the
// channel provided. If anything goes wrong, it returns the empty set.
func callGetCategories(analyzer string, out chan<- dispatcherCategories) {
	httpClient := getHTTPClient(analyzer)
	var resp rpcpb.GetCategoryResponse
	var cats strset.Set
	err := httpClient.Call("/AnalyzerService/GetCategory", &rpcpb.GetCategoryRequest{}, &resp)
	if err != nil {
		log.Printf("Could not get categories from %s: %v", analyzer, err)
		cats = strset.New()
	} else {
		cats = strset.New(resp.Category...)
	}
	out <- dispatcherCategories{
		dispatcher: analyzer,
		categories: cats,
	}
}

// callAnalyze attempts to call analyze for the specified analyzer with the given request.
// If anything goes wrong, it puts an AnalysisFailure into the AnalyzeResponse.
func callAnalyze(analyzer string, req *rpcpb.AnalyzeRequest, out chan<- *rpcpb.AnalyzeResponse) {
	httpClient := getHTTPClient(analyzer)
	var resp rpcpb.AnalyzeResponse
	err := httpClient.Call("/AnalyzerService/Analyze", req, &resp)
	if err != nil {
		out <- &rpcpb.AnalyzeResponse{
			Failure: []*rpcpb.AnalysisFailure{
				&rpcpb.AnalysisFailure{
					FailureMessage: proto.String(fmt.Sprintf("Error from analyzer %s: %v", analyzer, err)),
				},
			},
		}
	} else {
		out <- &resp
	}
}

// generateFailure creates a response with an analysis failure containing the given
// category and message
func generateFailure(cat string, message string) *rpcpb.AnalyzeResponse {
	return &rpcpb.AnalyzeResponse{
		Failure: []*rpcpb.AnalysisFailure{
			&rpcpb.AnalysisFailure{
				Category:       proto.String(cat),
				FailureMessage: proto.String(message),
			},
		},
	}
}

// getHTTPClient provides a (cached) HTTPClient for the address specified.
func getHTTPClient(addr string) *client.Client {
	httpClient, exists := clients[addr]
	if !exists {
		clients[addr] = client.NewHTTPClient(addr)
		httpClient = clients[addr]
	}
	return httpClient
}
<|endoftext|>


<commit_before>package log

import (
	"io/ioutil"
	"log"

	"google.golang.org/grpc/grpclog"
)

func init() {
	grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
}
<commit_msg>Remove grpclogger from log package<commit_after><|endoftext|>


<commit_before>// Copyright Roger Booth (roger.booth@gmail.com)
// In the event that you find this code useful, feel free to provide attribution :)

package main

import (
	"container/ring"
	"fmt"
)

type Color string

var colors = [...]Color{"white", "blue", "red", "yellow", "orange", "green"}
var edgesForFace = map[Color][]Color{
	"white":  {"red", "green", "orange", "blue"},
	"blue":   {"white", "orange", "yellow", "red"},
	"red":    {"blue", "yellow", "green", "white"},
	"yellow": {"green", "red", "blue", "orange"},
	"orange": {"yellow", "blue", "white", "green"},
	"green":  {"orange", "white", "red", "yellow"},
}

// Based on the coordinate system I discovered in 1984
var edgePos = [...]int{0, 7, 6, 4, 3, 2, 6, 5, 4, 2, 1, 0}

type Face [8]Color

type Edge [12]*Color

type Cube struct {
	faceMap map[Color]*Face
	edgeMap map[Color]Edge
}

type Entanglement [8]*Cube

func NewCube() (*Cube, error) {
	newFaceMap := make(map[Color]*Face)
	newEdgeMap := make(map[Color]Edge)
	for _, color := range colors {
		newFaceMap[color] = &Face{color, color, color, color, color, color, color, color}
	}
	i := 0
	for _, faceColor := range colors {
		var newEdge Edge
		for _, edgeColor := range edgesForFace[faceColor] {
		        //fmt.Println(faceColor)
		        //fmt.Println(i)
			newEdge[i] = &newFaceMap[edgeColor][edgePos[i]]
			newEdge[i+1] = &newFaceMap[edgeColor][edgePos[i+1]]
			newEdge[i+2] = &newFaceMap[edgeColor][edgePos[i+2]]
			i += 3
			if i == 12 {
			    i = 0
			}
		}
		newEdgeMap[faceColor] = newEdge
	}
	return &Cube{newFaceMap, newEdgeMap}, nil
}

func NewEntanglement() (*Entanglement, error) {
        var newEntanglement Entanglement
	for i:=0; i<8; i++{
		newEntanglement[i], _ = NewCube();
	}
	return &newEntanglement, nil
}

type ThreeDTransformer struct {
	faceRing *ring.Ring
	edgeRing *ring.Ring
}

func ThreeDRotate(ent *Entanglement, cubeId int, face Color, direction int) error {
        newFaceRing := ring.New(8)
        newEdgeRing := ring.New(12)
        trx := ThreeDTransformer{
		newFaceRing,newEdgeRing}
	for _, faceColor := range ent[cubeId].faceMap[face] {
		trx.faceRing.Value = faceColor
		trx.faceRing = trx.faceRing.Next()
	}
	for _, edgeColorPtr := range ent[cubeId].edgeMap[face] {
		trx.edgeRing.Value = *edgeColorPtr
		trx.edgeRing = trx.edgeRing.Next()
	}

	trx.faceRing = trx.faceRing.Move(2*direction)
	trx.edgeRing = trx.edgeRing.Move(3*direction)

	for i, _ := range ent[cubeId].faceMap[face] {
	        if v,ok := trx.faceRing.Value.(Color); ok {
		    ent[cubeId].faceMap[face][i] = v
		}
		trx.faceRing = trx.faceRing.Next()
	}
	for i, _ := range ent[cubeId].edgeMap[face] {
	        if v,ok := trx.edgeRing.Value.(Color); ok {
		    *ent[cubeId].edgeMap[face][i] = v
		}
		trx.edgeRing = trx.edgeRing.Next()
	}

        return nil
}

func main() {
	entanglement1,_ := NewEntanglement()
	fmt.Println(entanglement1[0].faceMap["red"][1])
	fmt.Println(entanglement1[0].faceMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][3])
	fmt.Println(*entanglement1[0].edgeMap["red"][8])
	fmt.Println(*entanglement1[0].edgeMap["red"][11])
	err := ThreeDRotate(entanglement1, 0, "red", 1)
	fmt.Println(err)
	fmt.Println(entanglement1[0].faceMap["red"][1])
	fmt.Println(entanglement1[0].faceMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][3])
	fmt.Println(*entanglement1[0].edgeMap["red"][8])
	fmt.Println(*entanglement1[0].edgeMap["red"][11])
<commit_msg>Implement entanglement using channels (incomplete)<commit_after>// Copyright Roger Booth (roger.booth@gmail.com)
// In the event that you find this code useful, feel free to provide attribution :)

package main

import (
	"container/ring"
	"fmt"
	"time"
)

type Color string

var colors = [...]Color{"white", "blue", "red", "yellow", "orange", "green"}
var intForColor = map[Color]int {
	"white": 0,
	"blue": 1,
	"red": 2,
	"yellow": 3,
	"orange": 4,
	"green": 5
}
var edgesForFace = map[Color][]Color{
	"white":  {"red", "green", "orange", "blue"},
	"blue":   {"white", "orange", "yellow", "red"},
	"red":    {"blue", "yellow", "green", "white"},
	"yellow": {"green", "red", "blue", "orange"},
	"orange": {"yellow", "blue", "white", "green"},
	"green":  {"orange", "white", "red", "yellow"},
}

// Based on the coordinate system I discovered in 1984
var edgePos = [...]int{0, 7, 6, 4, 3, 2, 6, 5, 4, 2, 1, 0}

// Pair cube faces by front-to-front projection
// Eight cubes X six faces
var straightProjection = [...][...]int {
	{1,3,4,1,4,3},
	{0,2,5,0,5,2},
	{3,1,6,3,6,1},
	{2,0,7,2,7,0},
	{5,7,0,5,0,7},
	{4,6,1,4,1,6},
	{7,5,2,7,2,5},
	{6,4,3,6,3,4}
}

type Face [8]Color

type Edge [12]*Color

type Cube struct {
	faceMap map[Color]*Face
	edgeMap map[Color]Edge
}

type Entanglement [8]*Cube

func NewCube() (*Cube, error) {
	newFaceMap := make(map[Color]*Face)
	newEdgeMap := make(map[Color]Edge)
	for _, color := range colors {
		newFaceMap[color] = &Face{color, color, color, color, color, color, color, color}
	}
	i := 0
	for _, faceColor := range colors {
		var newEdge Edge
		for _, edgeColor := range edgesForFace[faceColor] {
		        //fmt.Println(faceColor)
		        //fmt.Println(i)
			newEdge[i] = &newFaceMap[edgeColor][edgePos[i]]
			newEdge[i+1] = &newFaceMap[edgeColor][edgePos[i+1]]
			newEdge[i+2] = &newFaceMap[edgeColor][edgePos[i+2]]
			i += 3
			if i == 12 {
			    i = 0
			}
		}
		newEdgeMap[faceColor] = newEdge
	}
	return &Cube{newFaceMap, newEdgeMap}, nil
}

func NewEntanglement() (*Entanglement, error) {
        var newEntanglement Entanglement
	for i:=0; i<8; i++{
		newEntanglement[i], _ = NewCube();
	}
	return &newEntanglement, nil
}

type ThreeDTransformer struct {
	faceRing *ring.Ring
	edgeRing *ring.Ring
}

func ThreeDRotate(op ThreeDOperation) error {

        newFaceRing := ring.New(8)
        newEdgeRing := ring.New(12)
        trx := ThreeDTransformer{
		newFaceRing,newEdgeRing}
	for _, faceColor := range op.ent[op.cubeId].faceMap[op.face] {
		trx.faceRing.Value = faceColor
		trx.faceRing = trx.faceRing.Next()
	}
	for _, edgeColorPtr := range op.ent[op.cubeId].edgeMap[op.face] {
		trx.edgeRing.Value = *edgeColorPtr
		trx.edgeRing = trx.edgeRing.Next()
	}

	trx.faceRing = trx.faceRing.Move(2*op.direction)
	trx.edgeRing = trx.edgeRing.Move(3*op.direction)

	for i, _ := range ent[op.cubeId].faceMap[op.face] {
	        if v,ok := trx.faceRing.Value.(Color); ok {
		    op.ent[op.cubeId].faceMap[op.face][i] = v
		}
		trx.faceRing = trx.faceRing.Next()
	}
	for i, _ := range ent[op.cubeId].edgeMap[op.face] {
	        if v,ok := trx.edgeRing.Value.(Color); ok {
		    *op.ent[cubeId].edgeMap[face][i] = v
		}
		trx.edgeRing = trx.edgeRing.Next()
	}

    return nil
}

func Sister(cubeId int, face Color) (sisterCubeId int, sisterFace Color){
	sisterCubeId = straightProjection[cubeId][intForColor[Color]]
	sisterFace = face
}

type ThreeDOperation struct {
	ent *Entanglement
	cubeId int
	face Color
	direction int
	primary bool
}

func SplitMessage(op ThreeDOperation, opchan channel ThreeDOperation) error {
	sisterCubeId, sisterFace = Sister(op.CubeId, op.face)
	op.primary = false
	opchan <- op
	var sisterOp = ThreeDOperation {op.ent, sisterCubeId, sisterFace,op.direction, false}
	opchan <- sisterOp
}

func countDown(count chan int) {
	for i := 20 ; i >= 0 ; i-- {
		count <- i
		time.Sleep(1000000000)
	}
}

func selfDestruct() {
	fmt.Printf("Self destruct feature not yet implemented\n")
}

func main() {
	entanglement1,_ := NewEntanglement()
	operations := make(chan ThreeDOperation)
	count := make(chan int)
    go player1(operations)
	go player2(operations)
	go countDown(count)
	for {
		select {
			case o := <- operations:
			    if o.primary {
					SplitMessage(o, operations)
				} else {
					ThreeDRotate(o)
				}

			case i := <- count:
				if 0 == i {
					selfDestruct()
					return
				}
				fmt.Printf("%d seconds remaining\n", i)
		}
	}
}
<|endoftext|>


<commit_before>package geom

type MultiLineString struct {
	layout     Layout
	stride     int
	flatCoords []float64
	ends       []int
}

var _ T = &MultiLineString{}

func NewMultiLineString(layout Layout) *MultiLineString {
	return &MultiLineString{
		layout:     layout,
		stride:     layout.Stride(),
		flatCoords: nil,
		ends:       nil,
	}
}

func NewMultiLineStringFlat(layout Layout, flatCoords []float64, ends []int) *MultiLineString {
	return &MultiLineString{
		layout:     layout,
		stride:     layout.Stride(),
		flatCoords: flatCoords,
		ends:       ends,
	}
}

func (mls *MultiLineString) Bounds() *Bounds {
	return NewBounds().extendFlatCoords(mls.flatCoords, 0, len(mls.flatCoords), mls.stride)
}

func (mls *MultiLineString) Coords() [][][]float64 {
	return inflate2(mls.flatCoords, 0, mls.ends, mls.stride)
}

func (mls *MultiLineString) Ends() []int {
	return mls.ends
}

func (mls *MultiLineString) Endss() [][]int {
	return nil
}

func (mls *MultiLineString) FlatCoords() []float64 {
	return mls.flatCoords
}

func (mls *MultiLineString) Layout() Layout {
	return mls.layout
}

func (mls *MultiLineString) LineString(i int) *LineString {
	offset := 0
	if i > 0 {
		offset = mls.ends[i-1]
	}
	return &LineString{
		layout:     mls.layout,
		stride:     mls.stride,
		flatCoords: mls.flatCoords[offset:mls.ends[i]],
	}
}

func (mls *MultiLineString) NumLineStrings() int {
	return len(mls.ends)
}

func (mls *MultiLineString) Push(ls *LineString) error {
	if ls.layout != mls.layout {
		return ErrLayoutMismatch{Got: ls.layout, Want: mls.layout}
	}
	mls.flatCoords = append(mls.flatCoords, ls.flatCoords...)
	mls.ends = append(mls.ends, len(mls.flatCoords))
	return nil
}

func (mls *MultiLineString) SetCoords(coords2 [][][]float64) error {
	var err error
	if mls.flatCoords, mls.ends, err = deflate2(nil, nil, coords2, mls.stride); err != nil {
		return err
	}
	return nil
}

func (mls *MultiLineString) Stride() int {
	return mls.stride
}
<commit_msg>Add MultiLineString.MustSetCoords<commit_after>package geom

type MultiLineString struct {
	layout     Layout
	stride     int
	flatCoords []float64
	ends       []int
}

var _ T = &MultiLineString{}

func NewMultiLineString(layout Layout) *MultiLineString {
	return &MultiLineString{
		layout:     layout,
		stride:     layout.Stride(),
		flatCoords: nil,
		ends:       nil,
	}
}

func NewMultiLineStringFlat(layout Layout, flatCoords []float64, ends []int) *MultiLineString {
	return &MultiLineString{
		layout:     layout,
		stride:     layout.Stride(),
		flatCoords: flatCoords,
		ends:       ends,
	}
}

func (mls *MultiLineString) Bounds() *Bounds {
	return NewBounds().extendFlatCoords(mls.flatCoords, 0, len(mls.flatCoords), mls.stride)
}

func (mls *MultiLineString) Coords() [][][]float64 {
	return inflate2(mls.flatCoords, 0, mls.ends, mls.stride)
}

func (mls *MultiLineString) Ends() []int {
	return mls.ends
}

func (mls *MultiLineString) Endss() [][]int {
	return nil
}

func (mls *MultiLineString) FlatCoords() []float64 {
	return mls.flatCoords
}

func (mls *MultiLineString) Layout() Layout {
	return mls.layout
}

func (mls *MultiLineString) LineString(i int) *LineString {
	offset := 0
	if i > 0 {
		offset = mls.ends[i-1]
	}
	return &LineString{
		layout:     mls.layout,
		stride:     mls.stride,
		flatCoords: mls.flatCoords[offset:mls.ends[i]],
	}
}

func (mls *MultiLineString) MustSetCoords(coords2 [][][]float64) *MultiLineString {
	if err := mls.SetCoords(coords2); err != nil {
		panic(err)
	}
	return mls
}

func (mls *MultiLineString) NumLineStrings() int {
	return len(mls.ends)
}

func (mls *MultiLineString) Push(ls *LineString) error {
	if ls.layout != mls.layout {
		return ErrLayoutMismatch{Got: ls.layout, Want: mls.layout}
	}
	mls.flatCoords = append(mls.flatCoords, ls.flatCoords...)
	mls.ends = append(mls.ends, len(mls.flatCoords))
	return nil
}

func (mls *MultiLineString) SetCoords(coords2 [][][]float64) error {
	var err error
	if mls.flatCoords, mls.ends, err = deflate2(nil, nil, coords2, mls.stride); err != nil {
		return err
	}
	return nil
}

func (mls *MultiLineString) Stride() int {
	return mls.stride
}
<|endoftext|>


<commit_before>package endpoint

import (
	"github.com/julienschmidt/httprouter"
	"io/ioutil"
	"net/http"
)

func readBody() Middleware {
	fn := func(ctx Context, h httprouter.Handle) httprouter.Handle {
		return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {

			if r.Header.Get("Content-Type") != "application/json" {
				w.Write([]byte(`{"message":"content type must be application/json"}`))
				w.WriteHeader(http.StatusBadRequest)
				return
			}

			if r.ContentLength > 0 {
				if data, err := ioutil.ReadAll(r.Body); err == nil {
					ctx["data"] = data
					h(w, r, p)
					return
				}
			}

			w.Write([]byte(`{"message":"body is malformed or missing"}`))
			w.WriteHeader(http.StatusBadRequest)
			return
		}
	}

	return fn
}
<commit_msg>order of header/body write<commit_after>package endpoint

import (
	"github.com/julienschmidt/httprouter"
	"io/ioutil"
	"net/http"
)

func readBody() Middleware {
	fn := func(ctx Context, h httprouter.Handle) httprouter.Handle {
		return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {

			if r.Header.Get("Content-Type") != "application/json" {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte(`{"message":"content type must be application/json"}`))
				return
			}

			if r.ContentLength > 0 {
				if data, err := ioutil.ReadAll(r.Body); err == nil {
					ctx["data"] = data
					h(w, r, p)
					return
				}
			}

			w.Write([]byte(`{"message":"body is malformed or missing"}`))
			w.WriteHeader(http.StatusBadRequest)
			return
		}
	}

	return fn
}
<|endoftext|>


<commit_before>// +build linux

package fs

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/configs"
)

var (
	subsystems = map[string]subsystem{
		"devices":    &DevicesGroup{},
		"memory":     &MemoryGroup{},
		"cpu":        &CpuGroup{},
		"cpuset":     &CpusetGroup{},
		"cpuacct":    &CpuacctGroup{},
		"blkio":      &BlkioGroup{},
		"hugetlb":    &HugetlbGroup{},
		"net_cls":    &NetClsGroup{},
		"net_prio":   &NetPrioGroup{},
		"perf_event": &PerfEventGroup{},
		"freezer":    &FreezerGroup{},
	}
	CgroupProcesses  = "cgroup.procs"
	HugePageSizes, _ = cgroups.GetHugePageSize()
)

type subsystem interface {
	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
	GetStats(path string, stats *cgroups.Stats) error
	// Removes the cgroup represented by 'data'.
	Remove(*data) error
	// Creates and joins the cgroup represented by data.
	Apply(*data) error
	// Set the cgroup represented by cgroup.
	Set(path string, cgroup *configs.Cgroup) error
}

type Manager struct {
	mu      sync.Mutex
	Cgroups *configs.Cgroup
	Paths   map[string]string
}

// The absolute path to the root of the cgroup hierarchies.
var cgroupRootLock sync.Mutex
var cgroupRoot string

// Gets the cgroupRoot.
func getCgroupRoot() (string, error) {
	cgroupRootLock.Lock()
	defer cgroupRootLock.Unlock()

	if cgroupRoot != "" {
		return cgroupRoot, nil
	}

	root, err := cgroups.FindCgroupMountpointDir()
	if err != nil {
		return "", err
	}

	if _, err := os.Stat(root); err != nil {
		return "", err
	}

	cgroupRoot = root
	return cgroupRoot, nil
}

type data struct {
	root   string
	cgroup string
	c      *configs.Cgroup
	pid    int
}

func (m *Manager) Apply(pid int) error {
	if m.Cgroups == nil {
		return nil
	}

	var c = m.Cgroups

	d, err := getCgroupData(m.Cgroups, pid)
	if err != nil {
		return err
	}

	paths := make(map[string]string)
	defer func() {
		if err != nil {
			cgroups.RemovePaths(paths)
		}
	}()
	for name, sys := range subsystems {
		if err := sys.Apply(d); err != nil {
			return err
		}
		// TODO: Apply should, ideally, be reentrant or be broken up into a separate
		// create and join phase so that the cgroup hierarchy for a container can be
		// created then join consists of writing the process pids to cgroup.procs
		p, err := d.path(name)
		if err != nil {
			if cgroups.IsNotFound(err) {
				continue
			}
			return err
		}
		paths[name] = p
	}
	m.Paths = paths

	if paths["cpu"] != "" {
		if err := CheckCpushares(paths["cpu"], c.CpuShares); err != nil {
			return err
		}
	}

	return nil
}

func (m *Manager) Destroy() error {
	m.mu.Lock()
	defer m.mu.Unlock()
	if err := cgroups.RemovePaths(m.Paths); err != nil {
		return err
	}
	m.Paths = make(map[string]string)
	return nil
}

func (m *Manager) GetPaths() map[string]string {
	m.mu.Lock()
	paths := m.Paths
	m.mu.Unlock()
	return paths
}

func (m *Manager) GetStats() (*cgroups.Stats, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	stats := cgroups.NewStats()
	for name, path := range m.Paths {
		sys, ok := subsystems[name]
		if !ok || !cgroups.PathExists(path) {
			continue
		}
		if err := sys.GetStats(path, stats); err != nil {
			return nil, err
		}
	}

	return stats, nil
}

func (m *Manager) Set(container *configs.Config) error {
	for name, path := range m.Paths {
		sys, ok := subsystems[name]
		if !ok || !cgroups.PathExists(path) {
			continue
		}
		if err := sys.Set(path, container.Cgroups); err != nil {
			return err
		}
	}

	return nil
}

// Freeze toggles the container's freezer cgroup depending on the state
// provided
func (m *Manager) Freeze(state configs.FreezerState) error {
	d, err := getCgroupData(m.Cgroups, 0)
	if err != nil {
		return err
	}

	dir, err := d.path("freezer")
	if err != nil {
		return err
	}

	prevState := m.Cgroups.Freezer
	m.Cgroups.Freezer = state

	freezer := subsystems["freezer"]
	err = freezer.Set(dir, m.Cgroups)
	if err != nil {
		m.Cgroups.Freezer = prevState
		return err
	}

	return nil
}

func (m *Manager) GetPids() ([]int, error) {
	d, err := getCgroupData(m.Cgroups, 0)
	if err != nil {
		return nil, err
	}

	dir, err := d.path("devices")
	if err != nil {
		return nil, err
	}

	return cgroups.ReadProcsFile(dir)
}

func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
	root, err := getCgroupRoot()
	if err != nil {
		return nil, err
	}

	cgroup := c.Name
	if c.Parent != "" {
		cgroup = filepath.Join(c.Parent, cgroup)
	}

	return &data{
		root:   root,
		cgroup: cgroup,
		c:      c,
		pid:    pid,
	}, nil
}

func (raw *data) parent(subsystem, mountpoint, src string) (string, error) {
	initPath, err := cgroups.GetInitCgroupDir(subsystem)
	if err != nil {
		return "", err
	}
	relDir, err := filepath.Rel(src, initPath)
	if err != nil {
		return "", err
	}
	return filepath.Join(mountpoint, relDir), nil
}

func (raw *data) path(subsystem string) (string, error) {
	mnt, src, err := cgroups.FindCgroupMountpointAndSource(subsystem)
	// If we didn't mount the subsystem, there is no point we make the path.
	if err != nil {
		return "", err
	}

	// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
	if filepath.IsAbs(raw.cgroup) {
		return filepath.Join(raw.root, filepath.Base(mnt), raw.cgroup), nil
	}

	parent, err := raw.parent(subsystem, mnt, src)
	if err != nil {
		return "", err
	}

	return filepath.Join(parent, raw.cgroup), nil
}

func (raw *data) join(subsystem string) (string, error) {
	path, err := raw.path(subsystem)
	if err != nil {
		return "", err
	}
	if err := os.MkdirAll(path, 0755); err != nil {
		return "", err
	}
	if err := writeFile(path, CgroupProcesses, strconv.Itoa(raw.pid)); err != nil {
		return "", err
	}
	return path, nil
}

func writeFile(dir, file, data string) error {
	// Normally dir should not be empty, one case is that cgroup subsystem
	// is not mounted, we will get empty dir, and we want it fail here.
	if dir == "" {
		return fmt.Errorf("no such directory for %s.", file)
	}
	return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
}

func readFile(dir, file string) (string, error) {
	data, err := ioutil.ReadFile(filepath.Join(dir, file))
	return string(data), err
}

func removePath(p string, err error) error {
	if err != nil {
		return err
	}
	if p != "" {
		return os.RemoveAll(p)
	}
	return nil
}

func CheckCpushares(path string, c int64) error {
	var cpuShares int64

	if c == 0 {
		return nil
	}

	fd, err := os.Open(filepath.Join(path, "cpu.shares"))
	if err != nil {
		return err
	}
	defer fd.Close()

	_, err = fmt.Fscanf(fd, "%d", &cpuShares)
	if err != nil && err != io.EOF {
		return err
	}

	if c > cpuShares {
		return fmt.Errorf("The maximum allowed cpu-shares is %d", cpuShares)
	} else if c < cpuShares {
		return fmt.Errorf("The minimum allowed cpu-shares is %d", cpuShares)
	}

	return nil
}
<commit_msg>Always use cgroup root of current process<commit_after>// +build linux

package fs

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/configs"
)

var (
	subsystems = map[string]subsystem{
		"devices":    &DevicesGroup{},
		"memory":     &MemoryGroup{},
		"cpu":        &CpuGroup{},
		"cpuset":     &CpusetGroup{},
		"cpuacct":    &CpuacctGroup{},
		"blkio":      &BlkioGroup{},
		"hugetlb":    &HugetlbGroup{},
		"net_cls":    &NetClsGroup{},
		"net_prio":   &NetPrioGroup{},
		"perf_event": &PerfEventGroup{},
		"freezer":    &FreezerGroup{},
	}
	CgroupProcesses  = "cgroup.procs"
	HugePageSizes, _ = cgroups.GetHugePageSize()
)

type subsystem interface {
	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
	GetStats(path string, stats *cgroups.Stats) error
	// Removes the cgroup represented by 'data'.
	Remove(*data) error
	// Creates and joins the cgroup represented by data.
	Apply(*data) error
	// Set the cgroup represented by cgroup.
	Set(path string, cgroup *configs.Cgroup) error
}

type Manager struct {
	mu      sync.Mutex
	Cgroups *configs.Cgroup
	Paths   map[string]string
}

// The absolute path to the root of the cgroup hierarchies.
var cgroupRootLock sync.Mutex
var cgroupRoot string

// Gets the cgroupRoot.
func getCgroupRoot() (string, error) {
	cgroupRootLock.Lock()
	defer cgroupRootLock.Unlock()

	if cgroupRoot != "" {
		return cgroupRoot, nil
	}

	root, err := cgroups.FindCgroupMountpointDir()
	if err != nil {
		return "", err
	}

	if _, err := os.Stat(root); err != nil {
		return "", err
	}

	cgroupRoot = root
	return cgroupRoot, nil
}

type data struct {
	root   string
	cgroup string
	c      *configs.Cgroup
	pid    int
}

func (m *Manager) Apply(pid int) error {
	if m.Cgroups == nil {
		return nil
	}

	var c = m.Cgroups

	d, err := getCgroupData(m.Cgroups, pid)
	if err != nil {
		return err
	}

	paths := make(map[string]string)
	defer func() {
		if err != nil {
			cgroups.RemovePaths(paths)
		}
	}()
	for name, sys := range subsystems {
		if err := sys.Apply(d); err != nil {
			return err
		}
		// TODO: Apply should, ideally, be reentrant or be broken up into a separate
		// create and join phase so that the cgroup hierarchy for a container can be
		// created then join consists of writing the process pids to cgroup.procs
		p, err := d.path(name)
		if err != nil {
			if cgroups.IsNotFound(err) {
				continue
			}
			return err
		}
		paths[name] = p
	}
	m.Paths = paths

	if paths["cpu"] != "" {
		if err := CheckCpushares(paths["cpu"], c.CpuShares); err != nil {
			return err
		}
	}

	return nil
}

func (m *Manager) Destroy() error {
	m.mu.Lock()
	defer m.mu.Unlock()
	if err := cgroups.RemovePaths(m.Paths); err != nil {
		return err
	}
	m.Paths = make(map[string]string)
	return nil
}

func (m *Manager) GetPaths() map[string]string {
	m.mu.Lock()
	paths := m.Paths
	m.mu.Unlock()
	return paths
}

func (m *Manager) GetStats() (*cgroups.Stats, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	stats := cgroups.NewStats()
	for name, path := range m.Paths {
		sys, ok := subsystems[name]
		if !ok || !cgroups.PathExists(path) {
			continue
		}
		if err := sys.GetStats(path, stats); err != nil {
			return nil, err
		}
	}

	return stats, nil
}

func (m *Manager) Set(container *configs.Config) error {
	for name, path := range m.Paths {
		sys, ok := subsystems[name]
		if !ok || !cgroups.PathExists(path) {
			continue
		}
		if err := sys.Set(path, container.Cgroups); err != nil {
			return err
		}
	}

	return nil
}

// Freeze toggles the container's freezer cgroup depending on the state
// provided
func (m *Manager) Freeze(state configs.FreezerState) error {
	d, err := getCgroupData(m.Cgroups, 0)
	if err != nil {
		return err
	}

	dir, err := d.path("freezer")
	if err != nil {
		return err
	}

	prevState := m.Cgroups.Freezer
	m.Cgroups.Freezer = state

	freezer := subsystems["freezer"]
	err = freezer.Set(dir, m.Cgroups)
	if err != nil {
		m.Cgroups.Freezer = prevState
		return err
	}

	return nil
}

func (m *Manager) GetPids() ([]int, error) {
	d, err := getCgroupData(m.Cgroups, 0)
	if err != nil {
		return nil, err
	}

	dir, err := d.path("devices")
	if err != nil {
		return nil, err
	}

	return cgroups.ReadProcsFile(dir)
}

func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
	root, err := getCgroupRoot()
	if err != nil {
		return nil, err
	}

	cgroup := c.Name
	if c.Parent != "" {
		cgroup = filepath.Join(c.Parent, cgroup)
	}

	return &data{
		root:   root,
		cgroup: cgroup,
		c:      c,
		pid:    pid,
	}, nil
}

func (raw *data) parent(subsystem, mountpoint, src string) (string, error) {
	initPath, err := cgroups.GetThisCgroupDir(subsystem)
	if err != nil {
		return "", err
	}
	relDir, err := filepath.Rel(src, initPath)
	if err != nil {
		return "", err
	}
	return filepath.Join(mountpoint, relDir), nil
}

func (raw *data) path(subsystem string) (string, error) {
	mnt, src, err := cgroups.FindCgroupMountpointAndSource(subsystem)
	// If we didn't mount the subsystem, there is no point we make the path.
	if err != nil {
		return "", err
	}

	// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
	if filepath.IsAbs(raw.cgroup) {
		return filepath.Join(raw.root, filepath.Base(mnt), raw.cgroup), nil
	}

	parent, err := raw.parent(subsystem, mnt, src)
	if err != nil {
		return "", err
	}

	return filepath.Join(parent, raw.cgroup), nil
}

func (raw *data) join(subsystem string) (string, error) {
	path, err := raw.path(subsystem)
	if err != nil {
		return "", err
	}
	if err := os.MkdirAll(path, 0755); err != nil {
		return "", err
	}
	if err := writeFile(path, CgroupProcesses, strconv.Itoa(raw.pid)); err != nil {
		return "", err
	}
	return path, nil
}

func writeFile(dir, file, data string) error {
	// Normally dir should not be empty, one case is that cgroup subsystem
	// is not mounted, we will get empty dir, and we want it fail here.
	if dir == "" {
		return fmt.Errorf("no such directory for %s.", file)
	}
	return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
}

func readFile(dir, file string) (string, error) {
	data, err := ioutil.ReadFile(filepath.Join(dir, file))
	return string(data), err
}

func removePath(p string, err error) error {
	if err != nil {
		return err
	}
	if p != "" {
		return os.RemoveAll(p)
	}
	return nil
}

func CheckCpushares(path string, c int64) error {
	var cpuShares int64

	if c == 0 {
		return nil
	}

	fd, err := os.Open(filepath.Join(path, "cpu.shares"))
	if err != nil {
		return err
	}
	defer fd.Close()

	_, err = fmt.Fscanf(fd, "%d", &cpuShares)
	if err != nil && err != io.EOF {
		return err
	}

	if c > cpuShares {
		return fmt.Errorf("The maximum allowed cpu-shares is %d", cpuShares)
	} else if c < cpuShares {
		return fmt.Errorf("The minimum allowed cpu-shares is %d", cpuShares)
	}

	return nil
}
<|endoftext|>


<commit_before>package httpretry

import (
	"bytes"
	"io"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"
	"time"

	"github.com/cenkalti/backoff"
)

func TestRetry(t *testing.T) {
	t.Parallel()

	i := 0
	requests := []func(w http.ResponseWriter, r *http.Request){
		func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(time.Second)
			writeTestData(w, 404, "never reached")
		},
		func(w http.ResponseWriter, r *http.Request) {
			if v := r.Header.Get("Range"); v != "" {
				t.Errorf("Unexpected Range header on request %d: %s", v, i)
			}

			head := w.Header()
			head.Set("Test-Request", strconv.Itoa(i))
			head.Set("Content-Type", "text/plain")
			head.Set("Content-Length", "4")
			w.WriteHeader(500)
			w.Write([]byte("boom"))
		},
		func(w http.ResponseWriter, r *http.Request) {
			if v := r.Header.Get("Range"); v != "" {
				t.Errorf("Unexpected Range header on request %d: %s", v, i)
			}

			head := w.Header()
			head.Set("Test-Request", strconv.Itoa(i))
			head.Set("Accept-Ranges", "bytes")
			head.Set("Content-Type", "text/plain")
			head.Set("Content-Length", "5")
			w.WriteHeader(200)
		},
		func(w http.ResponseWriter, r *http.Request) {
			if v := r.Header.Get("Range"); v != "" {
				t.Errorf("Unexpected Range header on request %d: %s", v, i)
			}

			head := w.Header()
			head.Set("Test-Request", strconv.Itoa(i))
			head.Set("Accept-Ranges", "bytes")
			head.Set("Content-Type", "text/plain")
			head.Set("Content-Length", "5")
			w.WriteHeader(200)
			w.Write([]byte("ab"))
		},
		func(w http.ResponseWriter, r *http.Request) {
			if v := r.Header.Get("Range"); v != "bytes=2-4" {
				t.Errorf("Unexpected Range header on request %d: %s", v, i)
			}

			head := w.Header()
			head.Set("Test-Request", strconv.Itoa(i))
			head.Set("Content-Range", "bytes 2-4/4")
			head.Set("Accept-Ranges", "bytes")
			head.Set("Content-Type", "text/plain")
			head.Set("Content-Length", "3")
			w.WriteHeader(206)
			w.Write([]byte("cd"))
		},
		func(w http.ResponseWriter, r *http.Request) {
			if v := r.Header.Get("Range"); v != "bytes=4-4" {
				t.Errorf("Unexpected Range header on request %d: %s", v, i)
			}

			time.Sleep(time.Second)
			writeTestData(w, 404, "never reached")
		},
		func(w http.ResponseWriter, r *http.Request) {
			if v := r.Header.Get("Range"); v != "bytes=4-4" {
				t.Errorf("Unexpected Range header on request %d: %s", v, i)
			}

			head := w.Header()
			head.Set("Test-Request", strconv.Itoa(i))
			head.Set("Content-Type", "text/plain")
			head.Set("Content-Length", "4")
			w.WriteHeader(500)
			w.Write([]byte("boom"))
		},
		func(w http.ResponseWriter, r *http.Request) {
			if v := r.Header.Get("Range"); v != "bytes=4-4" {
				t.Errorf("Unexpected Range header on request %d: %s", v, i)
			}

			head := w.Header()
			head.Set("Test-Request", strconv.Itoa(i))
			head.Set("Content-Range", "bytes 4-4/4")
			head.Set("Accept-Ranges", "bytes")
			head.Set("Content-Type", "text/plain")
			head.Set("Content-Length", "1")
			w.WriteHeader(206)
			w.Write([]byte("e"))
		},
	}

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if i < len(requests) {
			requests[i](w, r)
			i += 1
		} else {
			head := w.Header()
			head.Set("Content-Type", "text/plain")
			head.Set("Content-Length", "7")
			w.WriteHeader(404)
			w.Write([]byte("missing"))
		}
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}

	code, head, reader := testGetter(t, req, 0, 0, 500, 200, 200, 206, 0, 500, 206)

	if code != 200 {
		t.Errorf("Unexpected status %d", code)
	}

	if ctype := head.Get("Content-Type"); ctype != "text/plain" {
		t.Errorf("Unexpected Content Type: %s", ctype)
	}

	buf := &bytes.Buffer{}
	written, err := io.Copy(buf, reader)
	if err != nil {
		t.Errorf("Copy error: %s", err)
	}

	if written != 5 {
		t.Errorf("Wrote %d", written)
	}

	if b := buf.String(); b != "abcde" {
		t.Errorf("Got %s", b)
	}

	if s := reader.Sha256(); s != "36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c" {
		t.Errorf("Bad SHA256: %s", s)
	}

	reader.Close()
}

func TestSingleSuccess(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		writeTestData(w, 200, "ok")
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}

	code, head, reader := testGetter(t, req)

	if code != 200 {
		t.Errorf("Unexpected status %d", code)
	}

	if ctype := head.Get("Content-Type"); ctype != "text/plain" {
		t.Errorf("Unexpected Content Type: %s", ctype)
	}

	buf := &bytes.Buffer{}
	written, err := io.Copy(buf, reader)
	if err != nil {
		t.Errorf("Copy error: %s", err)
	}

	if written != 2 {
		t.Errorf("Wrote %d", written)
	}

	if b := buf.String(); b != "ok" {
		t.Errorf("Got %s", b)
	}

	if s := reader.Sha256(); s != "2689367b205c16ce32ed4200942b8b8b1e262dfc70d9bc9fbc77c49699a4f1df" {
		t.Errorf("Bad SHA256: %s", s)
	}

	reader.Close()
}

func TestSkipRetryWithoutAcceptRange(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		head := w.Header()
		head.Set("Content-Type", "text/plain")
		head.Set("Content-Length", "2")
		w.WriteHeader(200)
		w.Write([]byte("o"))
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}

	code, head, reader := testGetter(t, req)

	if code != 200 {
		t.Errorf("Unexpected status %d", code)
	}

	if ctype := head.Get("Content-Type"); ctype != "text/plain" {
		t.Errorf("Unexpected Content Type: %s", ctype)
	}

	buf := &bytes.Buffer{}
	written, err := io.Copy(buf, reader)
	if err != nil {
		t.Errorf("Copy error: %s", err)
	}

	if written != 1 {
		t.Errorf("Wrote %d", written)
	}

	if b := buf.String(); b != "o" {
		t.Errorf("Got %s", b)
	}

	if s := reader.Sha256(); s != "65c74c15a686187bb6bbf9958f494fc6b80068034a659a9ad44991b08c58f2d2" {
		t.Errorf("Bad SHA256: %s", s)
	}

	reader.Close()
}

func TestSkipRetryWith400(t *testing.T) {
	t.Parallel()
	status := 200
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		writeTestData(w, status, "client error")
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}

	for status = 400; status < 500; status++ {
		code, head, reader := testGetter(t, req)

		if code != status {
			t.Errorf("Expected status %d, got %d", status, code)
		}

		if ctype := head.Get("Content-Type"); ctype != "text/plain" {
			t.Fatalf("Unexpected Content Type: %s", ctype)
		}

		buf := &bytes.Buffer{}
		written, err := io.Copy(buf, reader)
		if err != nil {
			t.Errorf("Copy error: %s", err)
		}

		if written != 12 {
			t.Errorf("Wrote %d", written)
		}

		if b := buf.String(); b != "client error" {
			t.Errorf("Got %s", b)
		}

		reader.Close()
	}
}

func writeTestData(w http.ResponseWriter, status int, body string) {
	by := []byte(body)
	head := w.Header()
	head.Set("Accept-Ranges", "bytes")
	head.Set("Content-Type", "text/plain")
	head.Set("Content-Length", strconv.Itoa(len(by)))
	w.WriteHeader(status)
	w.Write(by)
}

var zeroBackOff = &backoff.ZeroBackOff{}

func init() {
	tport := http.DefaultTransport.(*http.Transport)
	tport.ResponseHeaderTimeout = 500 * time.Millisecond
}

func testGetter(t *testing.T, req *http.Request, expectedCodes ...int) (int, http.Header, *HttpGetter) {
	g := Getter(req)
	expectedAttempts := len(expectedCodes)
	if expectedAttempts > 0 {
		i := 0
		g.OnResponse(func(res *http.Response, err error) {
			if i < len(expectedCodes) {
				exp := expectedCodes[i]
				if exp == 0 {
					if err == nil {
						t.Errorf("Expected error for request %d", i)
					}
				} else {
					if res != nil && exp != res.StatusCode {
						t.Errorf("Request %d expected code %d, got %d", i, exp, res.StatusCode)
					}
				}
			} else {
				t.Errorf("Request %d was unexpected", i)
			}
			i += 1
		})

		g.OnClose(func(g *HttpGetter) {
			if g.Attempts != expectedAttempts {
				t.Errorf("Expected %d attempts, got %d", expectedAttempts, g.Attempts)
			}
		})
	}

	g.SetBackOff(zeroBackOff)
	s, h := g.Do()
	return s, h, g
}
<commit_msg>update retry test to be less brittle<commit_after>package httpretry

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/cenkalti/backoff"
)

func TestRetry(t *testing.T) {
	t.Parallel()

	numRequests := 0
	mutex := &sync.Mutex{}
	content := []byte("0123456789")
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mutex.Lock()
		numRequests += 1
		mutex.Unlock()

		status := 200

		i := 0
		v := r.Header.Get("Range")
		if strings.HasPrefix(v, "bytes=") {
			i, _ = strconv.Atoi(strings.SplitN(v, "-", 2)[0][6:])
		}

		head := w.Header()
		head.Set("Accept-Ranges", "bytes")
		head.Set("Content-Type", "text/plain")

		if i > 0 {
			head.Set("Content-Range", fmt.Sprintf("bytes %d-4/4", i))
			status = 206
		}

		if numRequests%2 == 0 {
			head.Set("Content-Length", "4")
			w.WriteHeader(500)
			w.Write([]byte("BOOM"))
			return
		}

		head.Set("Content-Length", fmt.Sprintf("%d", len(content)-i))
		w.WriteHeader(status)
		w.Write(content[i : i+1])
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}

	reader := testGetter(t, req)
	seenCodes := trackSeenCodes(reader)
	code, head := reader.Do()

	if code != 200 {
		t.Errorf("Unexpected status %d", code)
	}

	if ctype := head.Get("Content-Type"); ctype != "text/plain" {
		t.Errorf("Unexpected Content Type: %s", ctype)
	}

	buf := &bytes.Buffer{}
	written, err := io.Copy(buf, reader)
	if err != nil {
		t.Errorf("Copy error: %s", err)
	}

	if written != 10 {
		t.Errorf("Wrote %d", written)
	}

	if b := buf.String(); b != "0123456789" {
		t.Errorf("Got %s", b)
	}

	if s := reader.Sha256(); s != "84d89877f0d4041efb6bf91a16f0248f2fd573e6af05c19f96bedb9f882f7882" {
		t.Errorf("Bad SHA256: %s", s)
	}

	if numRequests < 2 {
		t.Errorf("Only made %d request(s)?", numRequests)
	}

	assertSeenCodes(t, seenCodes, 200, 206, 500)

	t.Logf("requests made: %d", numRequests)

	reader.Close()
}

func TestSingleSuccess(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		writeTestData(w, 200, "ok")
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}

	reader := testGetter(t, req)
	code, head := reader.Do()

	if code != 200 {
		t.Errorf("Unexpected status %d", code)
	}

	if ctype := head.Get("Content-Type"); ctype != "text/plain" {
		t.Errorf("Unexpected Content Type: %s", ctype)
	}

	buf := &bytes.Buffer{}
	written, err := io.Copy(buf, reader)
	if err != nil {
		t.Errorf("Copy error: %s", err)
	}

	if written != 2 {
		t.Errorf("Wrote %d", written)
	}

	if b := buf.String(); b != "ok" {
		t.Errorf("Got %s", b)
	}

	if s := reader.Sha256(); s != "2689367b205c16ce32ed4200942b8b8b1e262dfc70d9bc9fbc77c49699a4f1df" {
		t.Errorf("Bad SHA256: %s", s)
	}

	reader.Close()
}

func TestSkipRetryWithoutAcceptRange(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		head := w.Header()
		head.Set("Content-Type", "text/plain")
		head.Set("Content-Length", "2")
		w.WriteHeader(200)
		w.Write([]byte("o"))
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}

	reader := testGetter(t, req)
	code, head := reader.Do()

	if code != 200 {
		t.Errorf("Unexpected status %d", code)
	}

	if ctype := head.Get("Content-Type"); ctype != "text/plain" {
		t.Errorf("Unexpected Content Type: %s", ctype)
	}

	buf := &bytes.Buffer{}
	written, err := io.Copy(buf, reader)
	if err != nil {
		t.Errorf("Copy error: %s", err)
	}

	if written != 1 {
		t.Errorf("Wrote %d", written)
	}

	if b := buf.String(); b != "o" {
		t.Errorf("Got %s", b)
	}

	if s := reader.Sha256(); s != "65c74c15a686187bb6bbf9958f494fc6b80068034a659a9ad44991b08c58f2d2" {
		t.Errorf("Bad SHA256: %s", s)
	}

	reader.Close()
}

func TestSkipRetryWith400(t *testing.T) {
	t.Parallel()
	status := 200
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		writeTestData(w, status, "client error")
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}

	for status = 400; status < 500; status++ {
		reader := testGetter(t, req)
		code, head := reader.Do()

		if code != status {
			t.Errorf("Expected status %d, got %d", status, code)
		}

		if ctype := head.Get("Content-Type"); ctype != "text/plain" {
			t.Fatalf("Unexpected Content Type: %s", ctype)
		}

		buf := &bytes.Buffer{}
		written, err := io.Copy(buf, reader)
		if err != nil {
			t.Errorf("Copy error: %s", err)
		}

		if written != 12 {
			t.Errorf("Wrote %d", written)
		}

		if b := buf.String(); b != "client error" {
			t.Errorf("Got %s", b)
		}

		reader.Close()
	}
}

func writeTestData(w http.ResponseWriter, status int, body string) {
	by := []byte(body)
	head := w.Header()
	head.Set("Accept-Ranges", "bytes")
	head.Set("Content-Type", "text/plain")
	head.Set("Content-Length", strconv.Itoa(len(by)))
	w.WriteHeader(status)
	w.Write(by)
}

var zeroBackOff = &backoff.ZeroBackOff{}

func init() {
	tport := http.DefaultTransport.(*http.Transport)
	tport.ResponseHeaderTimeout = 500 * time.Millisecond
}

func trackSeenCodes(g *HttpGetter) map[int]bool {
	seenCodes := make(map[int]bool)
	mu := &sync.Mutex{}
	g.OnResponse(func(res *http.Response, err error) {
		mu.Lock()
		if res == nil {
			seenCodes[0] = true
		} else {
			seenCodes[res.StatusCode] = true
		}
		mu.Unlock()
	})
	return seenCodes
}

func assertSeenCodes(t *testing.T, seenCodes map[int]bool, expectedCodes ...int) {
	for _, code := range expectedCodes {
		if !seenCodes[code] {
			t.Errorf("Expected to see response %d", code)
		}
	}
}

func testGetter(t *testing.T, req *http.Request, expectedCodes ...int) *HttpGetter {
	g := Getter(req)
	g.SetBackOff(zeroBackOff)
	return g
}
<|endoftext|>


<commit_before>package logstash

import (
	"encoding/json"
	"errors"
	"log"
	"net"

	"github.com/gliderlabs/logspout/router"
	"github.com/rancher/go-rancher-metadata/metadata"
)

const (
	metadataUrl = "http://rancher-metadata/2015-07-25"
)

func init() {
	router.AdapterFactories.Register(NewLogstashAdapter, "logstash")
}

// LogstashAdapter is an adapter that streams UDP JSON to Logstash.
type LogstashAdapter struct {
	conn  net.Conn
	route *router.Route
}

// NewLogstashAdapter creates a LogstashAdapter with UDP as the default transport.
func NewLogstashAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}

	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &LogstashAdapter{
		route: route,
		conn:  conn,
	}, nil
}

// Stream implements the router.LogAdapter interface.
func (a *LogstashAdapter) Stream(logstream chan *router.Message) {
	racherMetaData := metadata.NewClient(metadataUrl)
	stackname := ""
	for m := range logstream {
	    stack, err := racherMetaData.GetSelfStack()
		if err != nil {
			log.Println("Error reading metadata version: ", err)
        }else {
			stackname = stack.Name
		}
		msg := LogstashMessage{
			Message: m.Data,
			Docker: DockerInfo{
				Name:     m.Container.Name,
				ID:       m.Container.ID,
				Image:    m.Container.Config.Image,
				Hostname: m.Container.Config.Hostname,
			},
			Rancher: RancherInfo{
				StackName: stackname,
			},
		}
		js, err := json.Marshal(msg)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
		_, err = a.conn.Write(js)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
	}
}

type DockerInfo struct {
	Name     string `json:"name"`
	ID       string `json:"id"`
	Image    string `json:"image"`
	Hostname string `json:"hostname"`
}

type RancherInfo struct {
    Stackname string `json:"stackname"`
}

// LogstashMessage is a simple JSON input to Logstash.
type LogstashMessage struct {
	Message string     `json:"message"`
	Docker  DockerInfo `json:"docker"`
	Rancher RancherInfo `json:"rancher"`
}
<commit_msg>gdhsahsa<commit_after>package logstash

import (
	"encoding/json"
	"errors"
	"log"
	"net"

	"github.com/gliderlabs/logspout/router"
	"github.com/rancher/go-rancher-metadata/metadata"
)

const (
	metadataUrl = "http://rancher-metadata/2015-07-25"
)

func init() {
	router.AdapterFactories.Register(NewLogstashAdapter, "logstash")
}

// LogstashAdapter is an adapter that streams UDP JSON to Logstash.
type LogstashAdapter struct {
	conn  net.Conn
	route *router.Route
}

// NewLogstashAdapter creates a LogstashAdapter with UDP as the default transport.
func NewLogstashAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}

	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &LogstashAdapter{
		route: route,
		conn:  conn,
	}, nil
}

// Stream implements the router.LogAdapter interface.
func (a *LogstashAdapter) Stream(logstream chan *router.Message) {
	racherMetaData := metadata.NewClient(metadataUrl)
	stackname := ""
	for m := range logstream {
	    stack, err := racherMetaData.GetSelfStack()
		if err != nil {
			log.Println("Error reading metadata version: ", err)
        }else {
			stackname = stack.Name
		}
		msg := LogstashMessage{
			Message: m.Data,
			Docker: DockerInfo{
				Name:     m.Container.Name,
				ID:       m.Container.ID,
				Image:    m.Container.Config.Image,
				Hostname: m.Container.Config.Hostname,
			},
			Rancher: RancherInfo{
				StackName: stackname,
			},
		}
		js, err := json.Marshal(msg)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
		_, err = a.conn.Write(js)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
	}
}

type DockerInfo struct {
	Name     string `json:"name"`
	ID       string `json:"id"`
	Image    string `json:"image"`
	Hostname string `json:"hostname"`
}

type RancherInfo struct {
    StackName string `json:"stackname"`
}

// LogstashMessage is a simple JSON input to Logstash.
type LogstashMessage struct {
	Message string     `json:"message"`
	Docker  DockerInfo `json:"docker"`
	Rancher RancherInfo `json:"rancher"`
}
<|endoftext|>


<commit_before><commit_msg>Initial version and application name<commit_after><|endoftext|>


<commit_before>// Copyright 2010 The 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 gf256 implements arithmetic over the Galois Field GF(256).
package gf256

import "strconv"

type Field struct {
	log [256]byte  // log[0] is unused
	exp [510]byte
}

func NewField(poly int) *Field {
	if poly < 0x100 || poly >= 0x200 {
		panic("gf256: invalid polynomial: " + strconv.Itoa(poly))
	}
	var f Field
	x := 1
	for i := 0; i < 255; i++ {
		if x == 1 && i != 0 {
			panic("gf256: reducible polynomial: " + strconv.Itoa(poly))
		}
		f.exp[i] = byte(x)
		f.exp[i+255] = byte(x)
		f.log[x] = byte(i)
		x *= 2
		if x >= 0x100 {
			x ^= poly
		}
	}
	f.log[0] = 255
	return &f
}

// Add returns the sum of x and y in the field.
func (f *Field) Add(x, y byte) byte {
	return x ^ y
}

// Exp returns the base 2 exponential of e in the field.
// If e < 0, Exp returns 0.
func (f *Field) Exp(e int) byte {
	if e < 0 {
		return 0
	}
	return f.exp[e%255]
}

// Log returns the base 2 logarithm of x in the field.
// If x == 0, Log returns -1.
func (f *Field) Log(x byte) int {
	if x == 0 {
		return -1
	}
	return int(f.log[x])
}

// Inv returns the multiplicative inverse of x in the field.
// If x == 0, Inv returns 0.
func (f *Field) Inv(x byte) byte {
	if x == 0 {
		return 0
	}
	return f.exp[255-f.log[x]]
}

// Mul returns the product of x and y in the field.
func (f *Field) Mul(x, y byte) byte {
	if x == 0 || y == 0 {
		return 0
	}
	return f.exp[int(f.log[x])+int(f.log[y])]
}

// An RSEncoder implements Reed-Solomon encoding
// over a given field using a given number of error correction bytes.
type RSEncoder struct {
	f    *Field
	c    int
	lgen []byte
	p    []byte
}

func (f *Field) lgen(e int) []byte {
	// p = 1
	p := make([]byte, e+1)
	p[e] = 1

	for i := 0; i < e; i++ {
		// p *= (x + Exp(i))
		// p[j] = p[j]*Exp(i) + p[j+1].
		c := f.Exp(i)
		for j := 0; j < e; j++ {
			p[j] = f.Mul(p[j], c) ^ p[j+1]
		}
		p[e] = f.Mul(p[e], c)
	}

	// replace p with log p.
	for i, c := range p {
		if c == 0 {
			p[i] = 255
		} else {
			p[i] = byte(f.Log(c))
		}
	}
	return p
}

// NewRSEncoder returns a new Reed-Solomon encoder
// over the given field and number of error correction bytes.
func NewRSEncoder(f *Field, c int) *RSEncoder {
	return &RSEncoder{f: f, c: c, lgen: f.lgen(c)}
}

// ECC writes to check the error correcting code bytes
// for data using the given Reed-Solomon parameters.
func (rs *RSEncoder) ECC(data []byte, check []byte) {
	if len(check) < rs.c {
		panic("gf256.RSEncoder: invalid check byte length")
	}
	if rs.c == 0 {
		return
	}

	// The check bytes are the remainder after dividing
	// data padded with c zeros by the generator polynomial.

	// p = data padded with c zeros.
	var p []byte
	n := len(data) + rs.c
	if len(rs.p) >= n {
		p = rs.p
	} else {
		p = make([]byte, n)
	}
	copy(p, data)
	for i := len(data); i < len(p); i++ {
		p[i] = 0
	}

	// Divide p by gen, leaving the remainder in p[len(data):].
	// p[0] is the most significant term in p, and
	// gen[0] is the most significant term in the generator,
	// which is always 1.
	// To avoid repeated work, we store various values as
	// lv, not v, where lv = log[v].
	f := rs.f
	lgen := rs.lgen[1:]
	for i := 0; i < len(data); i++ {
		c := p[i]
		if c == 0 {
			continue
		}
		q := p[i+1:]
		exp := f.exp[f.log[c]:]
		for j, lg := range lgen {
			if lg != 255 {  // lgen uses 255 for log 0
				q[j] ^= exp[lg]
			}
		}
	}
	copy(check, p[len(data):])
	rs.p = p
}
<commit_msg>gofmt<commit_after>// Copyright 2010 The 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 gf256 implements arithmetic over the Galois Field GF(256).
package gf256

import "strconv"

type Field struct {
	log [256]byte // log[0] is unused
	exp [510]byte
}

func NewField(poly int) *Field {
	if poly < 0x100 || poly >= 0x200 {
		panic("gf256: invalid polynomial: " + strconv.Itoa(poly))
	}
	var f Field
	x := 1
	for i := 0; i < 255; i++ {
		if x == 1 && i != 0 {
			panic("gf256: reducible polynomial: " + strconv.Itoa(poly))
		}
		f.exp[i] = byte(x)
		f.exp[i+255] = byte(x)
		f.log[x] = byte(i)
		x *= 2
		if x >= 0x100 {
			x ^= poly
		}
	}
	f.log[0] = 255
	return &f
}

// Add returns the sum of x and y in the field.
func (f *Field) Add(x, y byte) byte {
	return x ^ y
}

// Exp returns the base 2 exponential of e in the field.
// If e < 0, Exp returns 0.
func (f *Field) Exp(e int) byte {
	if e < 0 {
		return 0
	}
	return f.exp[e%255]
}

// Log returns the base 2 logarithm of x in the field.
// If x == 0, Log returns -1.
func (f *Field) Log(x byte) int {
	if x == 0 {
		return -1
	}
	return int(f.log[x])
}

// Inv returns the multiplicative inverse of x in the field.
// If x == 0, Inv returns 0.
func (f *Field) Inv(x byte) byte {
	if x == 0 {
		return 0
	}
	return f.exp[255-f.log[x]]
}

// Mul returns the product of x and y in the field.
func (f *Field) Mul(x, y byte) byte {
	if x == 0 || y == 0 {
		return 0
	}
	return f.exp[int(f.log[x])+int(f.log[y])]
}

// An RSEncoder implements Reed-Solomon encoding
// over a given field using a given number of error correction bytes.
type RSEncoder struct {
	f    *Field
	c    int
	lgen []byte
	p    []byte
}

func (f *Field) lgen(e int) []byte {
	// p = 1
	p := make([]byte, e+1)
	p[e] = 1

	for i := 0; i < e; i++ {
		// p *= (x + Exp(i))
		// p[j] = p[j]*Exp(i) + p[j+1].
		c := f.Exp(i)
		for j := 0; j < e; j++ {
			p[j] = f.Mul(p[j], c) ^ p[j+1]
		}
		p[e] = f.Mul(p[e], c)
	}

	// replace p with log p.
	for i, c := range p {
		if c == 0 {
			p[i] = 255
		} else {
			p[i] = byte(f.Log(c))
		}
	}
	return p
}

// NewRSEncoder returns a new Reed-Solomon encoder
// over the given field and number of error correction bytes.
func NewRSEncoder(f *Field, c int) *RSEncoder {
	return &RSEncoder{f: f, c: c, lgen: f.lgen(c)}
}

// ECC writes to check the error correcting code bytes
// for data using the given Reed-Solomon parameters.
func (rs *RSEncoder) ECC(data []byte, check []byte) {
	if len(check) < rs.c {
		panic("gf256.RSEncoder: invalid check byte length")
	}
	if rs.c == 0 {
		return
	}

	// The check bytes are the remainder after dividing
	// data padded with c zeros by the generator polynomial.

	// p = data padded with c zeros.
	var p []byte
	n := len(data) + rs.c
	if len(rs.p) >= n {
		p = rs.p
	} else {
		p = make([]byte, n)
	}
	copy(p, data)
	for i := len(data); i < len(p); i++ {
		p[i] = 0
	}

	// Divide p by gen, leaving the remainder in p[len(data):].
	// p[0] is the most significant term in p, and
	// gen[0] is the most significant term in the generator,
	// which is always 1.
	// To avoid repeated work, we store various values as
	// lv, not v, where lv = log[v].
	f := rs.f
	lgen := rs.lgen[1:]
	for i := 0; i < len(data); i++ {
		c := p[i]
		if c == 0 {
			continue
		}
		q := p[i+1:]
		exp := f.exp[f.log[c]:]
		for j, lg := range lgen {
			if lg != 255 { // lgen uses 255 for log 0
				q[j] ^= exp[lg]
			}
		}
	}
	copy(check, p[len(data):])
	rs.p = p
}
<|endoftext|>


<commit_before>package main

import irc "github.com/fluffle/goirc/client"
import "math/rand"
import "regexp"

const channelName = "#poc_beta_test"

// Guilhem Bot core
func handleMessageGuilhem(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Mais ! C'est de la merde !",
		"On doit le réecrire en GO ça !",
		"Mais c'est pas du tout ça le problème, en fait […]",
		"C'est fait roulé sous les aisselles",
		"I have no *FRAKING* idea !",
		"NAN ! Je suis en copie, je suis perché !",
		"je veux pas devenir un SPOF",
	}

	personalAnswers := []string{
		"C'est moi le plus fort, je suis SURPUISSANT !",
		"Je parle fort… J'ai du charisme, c'est tout !",
		"Yves, Yves, YVES ! Laisse nous parler",
		"J'ai un super nom pour le deuxième DC ! WATTOO WATTOO",
		"Attends, y'a mon nom sur un t-shirt s'il-te-plaît !",
		"J'me vois pas me priver de bonheur parce que les autres veulent une vie de merde",
		//"'Tain IRC ! Protocole de merde, je vais installer un XM Peuh Peuh",
	}

	openstackRegexp, _ := regexp.Compile("openstack|keystone|deploy|datacenter")
	technicalPoint := openstackRegexp.FindString(line.Text())
	randomLine := rand.Intn(20)

	if technicalPoint != "" {
		conn.Privmsg(
			channelName,
			technicalAnswers[rand.Intn(len(technicalAnswers))],
		)
	} else if randomLine < 3 {
		conn.Privmsg(
			channelName,
			personalAnswers[rand.Intn(len(personalAnswers))],
		)
	}
}

func main() {
	// Gentux (for concurency tests)
	//go connect(channelName, "GentuxBot", handleMessageGuilhem)
	// Guilhem
	connect(channelName, "GuilhemBot", handleMessageGuilhem)
}
<commit_msg>Add personal answer<commit_after>package main

import irc "github.com/fluffle/goirc/client"
import "math/rand"
import "regexp"

const channelName = "#poc_beta_test"

// Guilhem Bot core
func handleMessageGuilhem(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Mais ! C'est de la merde !",
		"On doit le réecrire en GO ça !",
		"Mais c'est pas du tout ça le problème, en fait […]",
		"C'est fait roulé sous les aisselles",
		"I have no *FRAKING* idea !",
		"NAN ! Je suis en copie, je suis perché !",
		"je veux pas devenir un SPOF",
	}

	personalAnswers := []string{
		"C'est moi le plus fort, je suis SURPUISSANT !",
		"Je parle fort… J'ai du charisme, c'est tout !",
		"Yves, Yves, YVES ! Laisse nous parler",
		"J'ai un super nom pour le deuxième DC ! WATTOO WATTOO",
		"Attends, y'a mon nom sur un t-shirt s'il-te-plaît !",
		"J'me vois pas me priver de bonheur parce que les autres veulent une vie de merde",
		//"'Tain IRC ! Protocole de merde, je vais installer un XM Peuh Peuh",
		//"'Tain IRC ! Protocole de merde ! Je vais mettre en place un XM Peuh Peuh",
		"Il faut que je me sente... Important",
	}

	openstackRegexp, _ := regexp.Compile("openstack|keystone|deploy|datacenter")
	technicalPoint := openstackRegexp.FindString(line.Text())
	randomLine := rand.Intn(20)

	if technicalPoint != "" {
		conn.Privmsg(
			channelName,
			technicalAnswers[rand.Intn(len(technicalAnswers))],
		)
	} else if randomLine < 3 {
		conn.Privmsg(
			channelName,
			personalAnswers[rand.Intn(len(personalAnswers))],
		)
	}
}

func main() {
	// Gentux (for concurency tests)
	//go connect(channelName, "GentuxBot", handleMessageGuilhem)
	// Guilhem
	connect(channelName, "GuilhemBot", handleMessageGuilhem)
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/csv"
	"github.com/gin-gonic/gin"
	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
	"log"
	"time"
)

type LtarMetObservation struct {
	LTARSiteAcronym       string
	StationId             int
	Datetime              time.Time
	RecordType            string
	Air_temp107_avg       sql.NullFloat64
	Wind_speed_wvt        sql.NullFloat64
	Wind_direction_d1_wvt        sql.NullFloat64
	Relative_humidity_avg sql.NullFloat64
	Rain_mm               sql.NullFloat64
	AirPressure           sql.NullFloat64
	PAR                   sql.NullFloat64
	ShortWaveIn           sql.NullFloat64
	LongWaveIn            sql.NullFloat64
	BatteryVoltage        sql.NullFloat64
	LoggerTemperature     sql.NullFloat64
}

func (d *LtarMetObservation) to_csv() []string {
	values := []string{
		"KBS",
		"000",
		d.Datetime.Format(time.RFC3339Nano),
		"L",
		floatToString(d.Air_temp107_avg),
		floatToString(d.Wind_speed_wvt),
		floatToString(d.Wind_direction_d1_wvt),
		floatToString(d.Relative_humidity_avg),
		floatToString(d.Rain_mm),
		"",
		"",
		"",
		"",
		floatToString(d.BatteryVoltage),
		"",
	}
	return values
}

func (d *LtarMetObservation) header() []string {
	values := []string{
		"#LTARSiteAcronym",
		"StationId",
		"DateTime",
		"RecordType",
		"AirTemperature",
		"WindSpeed",
		"WindDirection",
		"RelativeHumidity",
		"Precipitation",
		"AirPressure",
		"PAR",
		"ShortWaveIn",
		"LongWaveIn",
		"BatteryVoltage",
		"LoggerTemperatuure",
	}
	return values
}

func (d *LtarMetObservation) units() []string {
	values := []string{
		"#",
		"",
		"",
		"",
		"C",
		"m/s",
		"degree",
		"%",
		"mm",
		"kPa",
		"",
		"",
		"",
		"V",
		"C",
	}
	return values
}

func ltar_met_observations(db *sqlx.DB, c *gin.Context) {

	rows, err := db.Queryx(" select * from ( select Air_temp107_avg, Relative_humidity_avg ,Wind_speed_wvt, Wind_direction_d1_wvt, raingauge_hourly.rain_mm,Datetime, Battery_voltage_min as BatteryVoltage from weather.lter_hour_d join weather.raingauge_hourly on raingauge_hourly.hours = lter_hour_d.datetime where datetime < now() - interval '1 hour' order by datetime desc limit $1) t1 order by datetime", limit(c, 1154))

	if err != nil {
		log.Print("error in query")
		log.Fatal(err)
	}
	defer rows.Close()

	i := 0
	writer := csv.NewWriter(c.Writer)

	obs := LtarMetObservation{}
	writer.Write(obs.header())
	writer.Write(obs.units())
	for rows.Next() {
		if err := rows.StructScan(&obs); err != nil {
			log.Fatal(err)
		}

		obs.Relative_humidity_avg.Float64 = obs.Relative_humidity_avg.Float64 * 100

		writer.Write(obs.to_csv())

		if i%500 == 0 {
			writer.Flush()
		}
		i = i + 1

	}
	writer.Flush()
}
<commit_msg>take out units line for LTAR<commit_after>package main

import (
	"database/sql"
	"encoding/csv"
	"github.com/gin-gonic/gin"
	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
	"log"
	"time"
)

type LtarMetObservation struct {
	LTARSiteAcronym       string
	StationId             int
	Datetime              time.Time
	RecordType            string
	Air_temp107_avg       sql.NullFloat64
	Wind_speed_wvt        sql.NullFloat64
	Wind_direction_d1_wvt        sql.NullFloat64
	Relative_humidity_avg sql.NullFloat64
	Rain_mm               sql.NullFloat64
	AirPressure           sql.NullFloat64
	PAR                   sql.NullFloat64
	ShortWaveIn           sql.NullFloat64
	LongWaveIn            sql.NullFloat64
	BatteryVoltage        sql.NullFloat64
	LoggerTemperature     sql.NullFloat64
}

func (d *LtarMetObservation) to_csv() []string {
	values := []string{
		"KBS",
		"000",
		d.Datetime.Format(time.RFC3339Nano),
		"L",
		floatToString(d.Air_temp107_avg),
		floatToString(d.Wind_speed_wvt),
		floatToString(d.Wind_direction_d1_wvt),
		floatToString(d.Relative_humidity_avg),
		floatToString(d.Rain_mm),
		"",
		"",
		"",
		"",
		floatToString(d.BatteryVoltage),
		"",
	}
	return values
}

func (d *LtarMetObservation) header() []string {
	values := []string{
		"#LTARSiteAcronym",
		"StationId",
		"DateTime",
		"RecordType",
		"AirTemperature",
		"WindSpeed",
		"WindDirection",
		"RelativeHumidity",
		"Precipitation",
		"AirPressure",
		"PAR",
		"ShortWaveIn",
		"LongWaveIn",
		"BatteryVoltage",
		"LoggerTemperatuure",
	}
	return values
}

func ltar_met_observations(db *sqlx.DB, c *gin.Context) {

	rows, err := db.Queryx(" select * from ( select Air_temp107_avg, Relative_humidity_avg ,Wind_speed_wvt, Wind_direction_d1_wvt, raingauge_hourly.rain_mm,Datetime, Battery_voltage_min as BatteryVoltage from weather.lter_hour_d join weather.raingauge_hourly on raingauge_hourly.hours = lter_hour_d.datetime where datetime < now() - interval '1 hour' order by datetime desc limit $1) t1 order by datetime", limit(c, 1154))

	if err != nil {
		log.Print("error in query")
		log.Fatal(err)
	}
	defer rows.Close()

	i := 0
	writer := csv.NewWriter(c.Writer)

	obs := LtarMetObservation{}
	writer.Write(obs.header())
	for rows.Next() {
		if err := rows.StructScan(&obs); err != nil {
			log.Fatal(err)
		}

		obs.Relative_humidity_avg.Float64 = obs.Relative_humidity_avg.Float64 * 100

		writer.Write(obs.to_csv())

		if i%500 == 0 {
			writer.Flush()
		}
		i = i + 1

	}
	writer.Flush()
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-github 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 github

import (
	"fmt"
	"time"
)

// RepositoryCommit represents a commit in a repo.
// Note that it's wrapping a Commit, so author/committer information is in two places,
// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details".
type RepositoryCommit struct {
	SHA       *string  `json:"sha,omitempty"`
	Commit    *Commit  `json:"commit,omitempty"`
	Author    *User    `json:"author,omitempty"`
	Committer *User    `json:"committer,omitempty"`
	Parents   []Commit `json:"parents,omitempty"`
	Message   *string  `json:"message,omitempty"`

	// Details about how many changes were made in this commit. Only filled in during GetCommit!
	Stats *CommitStats `json:"stats,omitempty"`
	// Details about which files, and how this commit touched. Only filled in during GetCommit!
	Files []CommitFile `json:"files,omitempty"`
}

func (r RepositoryCommit) String() string {
	return Stringify(r)
}

// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit.
type CommitStats struct {
	Additions *int `json:"additions,omitempty"`
	Deletions *int `json:"deletions,omitempty"`
	Total     *int `json:"total,omitempty"`
}

func (c CommitStats) String() string {
	return Stringify(c)
}

// CommitFile represents a file modified in a commit.
type CommitFile struct {
	SHA       *string `json:"sha,omitempty"`
	Filename  *string `json:"filename,omitempty"`
	Additions *int    `json:"additions,omitempty"`
	Deletions *int    `json:"deletions,omitempty"`
	Changes   *int    `json:"changes,omitempty"`
	Status    *string `json:"status,omitempty"`
	Patch     *string `json:"patch,omitempty"`
}

func (c CommitFile) String() string {
	return Stringify(c)
}

// CommitsComparison is the result of comparing two commits.
// See CompareCommits() for details.
type CommitsComparison struct {
	BaseCommit *RepositoryCommit `json:"base_commit,omitempty"`

	// Head can be 'behind' or 'ahead'
	Status       *string `json:"status,omitempty"`
	AheadBy      *int    `json:"ahead_by,omitempty"`
	BehindBy     *int    `json:"behind_by,omitempty"`
	TotalCommits *int    `json:"total_commits,omitempty"`

	Commits []RepositoryCommit `json:"commits,omitempty"`

	Files []CommitFile `json:"files,omitempty"`
}

func (c CommitsComparison) String() string {
	return Stringify(c)
}

// CommitsListOptions specifies the optional parameters to the
// RepositoriesService.ListCommits method.
type CommitsListOptions struct {
	// SHA or branch to start listing Commits from.
	SHA string `url:"sha,omitempty"`

	// Path that should be touched by the returned Commits.
	Path string `url:"path,omitempty"`

	// Author of by which to filter Commits.
	Author string `url:"author,omitempty"`

	// Since when should Commits be included in the response.
	Since time.Time `url:"since,omitempty"`

	// Until when should Commits be included in the response.
	Until time.Time `url:"until,omitempty"`
}

// ListCommits lists the commits of a repository.
//
// GitHub API docs: http://developer.github.com/v3/repos/commits/#list
func (s *RepositoriesService) ListCommits(owner, repo string, opt *CommitsListOptions) ([]RepositoryCommit, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits", owner, repo)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	commits := new([]RepositoryCommit)
	resp, err := s.client.Do(req, commits)
	if err != nil {
		return nil, resp, err
	}

	return *commits, resp, err
}

// GetCommit fetches the specified commit, including all details about it.
// todo: support media formats - https://github.com/google/go-github/issues/6
//
// GitHub API docs: http://developer.github.com/v3/repos/commits/#get-a-single-commit
// See also: http://developer.github.com//v3/git/commits/#get-a-single-commit provides the same functionality
func (s *RepositoriesService) GetCommit(owner, repo, sha string) (*RepositoryCommit, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	commit := new(RepositoryCommit)
	resp, err := s.client.Do(req, commit)
	if err != nil {
		return nil, resp, err
	}

	return commit, resp, err
}

// CompareCommits compares a range of commits with each other.
// todo: support media formats - https://github.com/google/go-github/issues/6
//
// GitHub API docs: http://developer.github.com/v3/repos/commits/index.html#compare-two-commits
func (s *RepositoriesService) CompareCommits(owner, repo string, base, head string) (*CommitsComparison, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	comp := new(CommitsComparison)
	resp, err := s.client.Do(req, comp)
	if err != nil {
		return nil, resp, err
	}

	return comp, resp, err
}
<commit_msg>Add ListOptions to CommitListOptions<commit_after>// Copyright 2013 The go-github 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 github

import (
	"fmt"
	"time"
)

// RepositoryCommit represents a commit in a repo.
// Note that it's wrapping a Commit, so author/committer information is in two places,
// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details".
type RepositoryCommit struct {
	SHA       *string  `json:"sha,omitempty"`
	Commit    *Commit  `json:"commit,omitempty"`
	Author    *User    `json:"author,omitempty"`
	Committer *User    `json:"committer,omitempty"`
	Parents   []Commit `json:"parents,omitempty"`
	Message   *string  `json:"message,omitempty"`

	// Details about how many changes were made in this commit. Only filled in during GetCommit!
	Stats *CommitStats `json:"stats,omitempty"`
	// Details about which files, and how this commit touched. Only filled in during GetCommit!
	Files []CommitFile `json:"files,omitempty"`
}

func (r RepositoryCommit) String() string {
	return Stringify(r)
}

// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit.
type CommitStats struct {
	Additions *int `json:"additions,omitempty"`
	Deletions *int `json:"deletions,omitempty"`
	Total     *int `json:"total,omitempty"`
}

func (c CommitStats) String() string {
	return Stringify(c)
}

// CommitFile represents a file modified in a commit.
type CommitFile struct {
	SHA       *string `json:"sha,omitempty"`
	Filename  *string `json:"filename,omitempty"`
	Additions *int    `json:"additions,omitempty"`
	Deletions *int    `json:"deletions,omitempty"`
	Changes   *int    `json:"changes,omitempty"`
	Status    *string `json:"status,omitempty"`
	Patch     *string `json:"patch,omitempty"`
}

func (c CommitFile) String() string {
	return Stringify(c)
}

// CommitsComparison is the result of comparing two commits.
// See CompareCommits() for details.
type CommitsComparison struct {
	BaseCommit *RepositoryCommit `json:"base_commit,omitempty"`

	// Head can be 'behind' or 'ahead'
	Status       *string `json:"status,omitempty"`
	AheadBy      *int    `json:"ahead_by,omitempty"`
	BehindBy     *int    `json:"behind_by,omitempty"`
	TotalCommits *int    `json:"total_commits,omitempty"`

	Commits []RepositoryCommit `json:"commits,omitempty"`

	Files []CommitFile `json:"files,omitempty"`
}

func (c CommitsComparison) String() string {
	return Stringify(c)
}

// CommitsListOptions specifies the optional parameters to the
// RepositoriesService.ListCommits method.
type CommitsListOptions struct {
	// SHA or branch to start listing Commits from.
	SHA string `url:"sha,omitempty"`

	// Path that should be touched by the returned Commits.
	Path string `url:"path,omitempty"`

	// Author of by which to filter Commits.
	Author string `url:"author,omitempty"`

	// Since when should Commits be included in the response.
	Since time.Time `url:"since,omitempty"`

	// Until when should Commits be included in the response.
	Until time.Time `url:"until,omitempty"`

	ListOptions
}

// ListCommits lists the commits of a repository.
//
// GitHub API docs: http://developer.github.com/v3/repos/commits/#list
func (s *RepositoriesService) ListCommits(owner, repo string, opt *CommitsListOptions) ([]RepositoryCommit, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits", owner, repo)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	commits := new([]RepositoryCommit)
	resp, err := s.client.Do(req, commits)
	if err != nil {
		return nil, resp, err
	}

	return *commits, resp, err
}

// GetCommit fetches the specified commit, including all details about it.
// todo: support media formats - https://github.com/google/go-github/issues/6
//
// GitHub API docs: http://developer.github.com/v3/repos/commits/#get-a-single-commit
// See also: http://developer.github.com//v3/git/commits/#get-a-single-commit provides the same functionality
func (s *RepositoriesService) GetCommit(owner, repo, sha string) (*RepositoryCommit, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	commit := new(RepositoryCommit)
	resp, err := s.client.Do(req, commit)
	if err != nil {
		return nil, resp, err
	}

	return commit, resp, err
}

// CompareCommits compares a range of commits with each other.
// todo: support media formats - https://github.com/google/go-github/issues/6
//
// GitHub API docs: http://developer.github.com/v3/repos/commits/index.html#compare-two-commits
func (s *RepositoriesService) CompareCommits(owner, repo string, base, head string) (*CommitsComparison, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	comp := new(CommitsComparison)
	resp, err := s.client.Do(req, comp)
	if err != nil {
		return nil, resp, err
	}

	return comp, resp, err
}
<|endoftext|>