<commit_before>/*
Copyright 2018 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package executor

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"

	"github.com/GoogleContainerTools/kaniko/pkg/snapshot"

	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
	"github.com/google/go-containerregistry/pkg/v1"
	"github.com/google/go-containerregistry/pkg/v1/empty"
	"github.com/google/go-containerregistry/pkg/v1/mutate"
	"github.com/google/go-containerregistry/pkg/v1/remote"
	"github.com/google/go-containerregistry/pkg/v1/tarball"

	"io/ioutil"

	"github.com/GoogleContainerTools/kaniko/pkg/commands"
	"github.com/GoogleContainerTools/kaniko/pkg/constants"
	"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
	"github.com/GoogleContainerTools/kaniko/pkg/util"
	"github.com/docker/docker/builder/dockerfile/instructions"
	"github.com/sirupsen/logrus"
)

func DoBuild(dockerfilePath, srcContext, snapshotMode string, args []string) (name.Reference, v1.Image, error) {
	// Parse dockerfile and unpack base image to root
	d, err := ioutil.ReadFile(dockerfilePath)
	if err != nil {
		return nil, nil, err
	}

	stages, err := dockerfile.Parse(d)
	if err != nil {
		return nil, nil, err
	}
	dockerfile.ResolveStages(stages)

	hasher, err := getHasher(snapshotMode)
	if err != nil {
		return nil, nil, err
	}
	for index, stage := range stages {
		baseImage := stage.BaseName
		finalStage := index == len(stages)-1
		// Unpack file system to root
		logrus.Infof("Unpacking filesystem of %s...", baseImage)
		var sourceImage v1.Image
		var ref name.Reference
		if baseImage == constants.NoBaseImage {
			logrus.Info("No base image, nothing to extract")
			sourceImage = empty.Image
		} else {
			// Initialize source image
			ref, err = name.ParseReference(baseImage, name.WeakValidation)
			if err != nil {
				return nil, nil, err
			}
			auth, err := authn.DefaultKeychain.Resolve(ref.Context().Registry)
			if err != nil {
				return nil, nil, err
			}
			sourceImage, err = remote.Image(ref, auth, http.DefaultTransport)
			if err != nil {
				return nil, nil, err
			}
		}
		if err := util.GetFSFromImage(sourceImage); err != nil {
			return nil, nil, err
		}
		l := snapshot.NewLayeredMap(hasher)
		snapshotter := snapshot.NewSnapshotter(l, constants.RootDir)
		// Take initial snapshot
		if err := snapshotter.Init(); err != nil {
			return nil, nil, err
		}
		imageConfig, err := sourceImage.ConfigFile()
		if baseImage == constants.NoBaseImage {
			imageConfig.Config.Env = constants.ScratchEnvVars
		}
		if err != nil {
			return nil, nil, err
		}
		if err := resolveOnBuild(&stage, &imageConfig.Config); err != nil {
			return nil, nil, err
		}
		buildArgs := dockerfile.NewBuildArgs(args)
		for _, cmd := range stage.Commands {
			dockerCommand, err := commands.GetCommand(cmd, srcContext)
			if err != nil {
				return nil, nil, err
			}
			if dockerCommand == nil {
				continue
			}
			if err := dockerCommand.ExecuteCommand(&imageConfig.Config, buildArgs); err != nil {
				return nil, nil, err
			}
			if !finalStage {
				continue
			}
			// Now, we get the files to snapshot from this command and take the snapshot
			snapshotFiles := dockerCommand.FilesToSnapshot()
			contents, err := snapshotter.TakeSnapshot(snapshotFiles)
			if err != nil {
				return nil, nil, err
			}
			util.MoveVolumeWhitelistToWhitelist()
			if contents == nil {
				logrus.Info("No files were changed, appending empty layer to config.")
				continue
			}
			// Append the layer to the image
			opener := func() (io.ReadCloser, error) {
				return ioutil.NopCloser(bytes.NewReader(contents)), nil
			}
			layer, err := tarball.LayerFromOpener(opener)
			if err != nil {
				return nil, nil, err
			}
			sourceImage, err = mutate.Append(sourceImage,
				mutate.Addendum{
					Layer: layer,
					History: v1.History{
						Author:    constants.Author,
						CreatedBy: dockerCommand.CreatedBy(),
					},
				},
			)
			if err != nil {
				return nil, nil, err
			}
		}
		if finalStage {
			sourceImage, err = mutate.Config(sourceImage, imageConfig.Config)
			if err != nil {
				return nil, nil, err
			}
			return ref, sourceImage, nil
		}
		if err := saveStageDependencies(index, stages, buildArgs.Clone()); err != nil {
			return nil, nil, err
		}
		// Delete the filesystem
		if err := util.DeleteFilesystem(); err != nil {
			return nil, nil, err
		}
	}
	return nil, nil, err
}

func DoPush(ref name.Reference, image v1.Image, destinations []string, tarPath string) error {
	// continue pushing unless an error occurs
	for _, destination := range destinations {
		// Push the image
		destRef, err := name.NewTag(destination, name.WeakValidation)
		if err != nil {
			return err
		}

		if tarPath != "" {
			return tarball.WriteToFile(tarPath, destRef, image, nil)
		}

		wo := remote.WriteOptions{}
		if ref != nil {
			wo.MountPaths = []name.Repository{ref.Context()}
		}
		pushAuth, err := authn.DefaultKeychain.Resolve(destRef.Context().Registry)
		if err != nil {
			return err
		}

		err = remote.Write(destRef, image, pushAuth, http.DefaultTransport, wo)
		if err != nil {
			logrus.Error(fmt.Errorf("Failed to push to destination %s", destination))
			return err
		}
	}
	return nil
}
func saveStageDependencies(index int, stages []instructions.Stage, buildArgs *dockerfile.BuildArgs) error {
	// First, get the files in this stage later stages will need
	dependencies, err := dockerfile.Dependencies(index, stages, buildArgs)
	logrus.Infof("saving dependencies %s", dependencies)
	if err != nil {
		return err
	}
	// Then, create the directory they will exist in
	i := strconv.Itoa(index)
	dependencyDir := filepath.Join(constants.KanikoDir, i)
	if err := os.MkdirAll(dependencyDir, 0755); err != nil {
		return err
	}
	// Now, copy over dependencies to this dir
	for _, d := range dependencies {
		fi, err := os.Lstat(d)
		if err != nil {
			return err
		}
		dest := filepath.Join(dependencyDir, d)
		if fi.IsDir() {
			if err := util.CopyDir(d, dest); err != nil {
				return err
			}
		} else if fi.Mode()&os.ModeSymlink != 0 {
			if err := util.CopySymlink(d, dest); err != nil {
				return err
			}
		} else {
			if err := util.CopyFile(d, dest); err != nil {
				return err
			}
		}
	}
	return nil
}

func getHasher(snapshotMode string) (func(string) (string, error), error) {
	if snapshotMode == constants.SnapshotModeTime {
		logrus.Info("Only file modification time will be considered when snapshotting")
		return util.MtimeHasher(), nil
	}
	if snapshotMode == constants.SnapshotModeFull {
		return util.Hasher(), nil
	}
	return nil, fmt.Errorf("%s is not a valid snapshot mode", snapshotMode)
}

func resolveOnBuild(stage *instructions.Stage, config *v1.Config) error {
	if config.OnBuild == nil {
		return nil
	}
	// Otherwise, parse into commands
	cmds, err := dockerfile.ParseCommands(config.OnBuild)
	if err != nil {
		return err
	}
	// Append to the beginning of the commands in the stage
	stage.Commands = append(cmds, stage.Commands...)
	logrus.Infof("Executing %v build triggers", len(cmds))

	// Blank out the Onbuild command list for this image
	config.OnBuild = nil
	return nil
}
<commit_msg>Fix MountPaths breakage<commit_after>/*
Copyright 2018 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package executor

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"

	"github.com/GoogleContainerTools/kaniko/pkg/snapshot"

	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
	"github.com/google/go-containerregistry/pkg/v1"
	"github.com/google/go-containerregistry/pkg/v1/empty"
	"github.com/google/go-containerregistry/pkg/v1/mutate"
	"github.com/google/go-containerregistry/pkg/v1/remote"
	"github.com/google/go-containerregistry/pkg/v1/tarball"

	"io/ioutil"

	"github.com/GoogleContainerTools/kaniko/pkg/commands"
	"github.com/GoogleContainerTools/kaniko/pkg/constants"
	"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
	"github.com/GoogleContainerTools/kaniko/pkg/util"
	"github.com/docker/docker/builder/dockerfile/instructions"
	"github.com/sirupsen/logrus"
)

func DoBuild(dockerfilePath, srcContext, snapshotMode string, args []string) (name.Reference, v1.Image, error) {
	// Parse dockerfile and unpack base image to root
	d, err := ioutil.ReadFile(dockerfilePath)
	if err != nil {
		return nil, nil, err
	}

	stages, err := dockerfile.Parse(d)
	if err != nil {
		return nil, nil, err
	}
	dockerfile.ResolveStages(stages)

	hasher, err := getHasher(snapshotMode)
	if err != nil {
		return nil, nil, err
	}
	for index, stage := range stages {
		baseImage := stage.BaseName
		finalStage := index == len(stages)-1
		// Unpack file system to root
		logrus.Infof("Unpacking filesystem of %s...", baseImage)
		var sourceImage v1.Image
		var ref name.Reference
		if baseImage == constants.NoBaseImage {
			logrus.Info("No base image, nothing to extract")
			sourceImage = empty.Image
		} else {
			// Initialize source image
			ref, err = name.ParseReference(baseImage, name.WeakValidation)
			if err != nil {
				return nil, nil, err
			}
			auth, err := authn.DefaultKeychain.Resolve(ref.Context().Registry)
			if err != nil {
				return nil, nil, err
			}
			sourceImage, err = remote.Image(ref, auth, http.DefaultTransport)
			if err != nil {
				return nil, nil, err
			}
		}
		if err := util.GetFSFromImage(sourceImage); err != nil {
			return nil, nil, err
		}
		l := snapshot.NewLayeredMap(hasher)
		snapshotter := snapshot.NewSnapshotter(l, constants.RootDir)
		// Take initial snapshot
		if err := snapshotter.Init(); err != nil {
			return nil, nil, err
		}
		imageConfig, err := sourceImage.ConfigFile()
		if baseImage == constants.NoBaseImage {
			imageConfig.Config.Env = constants.ScratchEnvVars
		}
		if err != nil {
			return nil, nil, err
		}
		if err := resolveOnBuild(&stage, &imageConfig.Config); err != nil {
			return nil, nil, err
		}
		buildArgs := dockerfile.NewBuildArgs(args)
		for _, cmd := range stage.Commands {
			dockerCommand, err := commands.GetCommand(cmd, srcContext)
			if err != nil {
				return nil, nil, err
			}
			if dockerCommand == nil {
				continue
			}
			if err := dockerCommand.ExecuteCommand(&imageConfig.Config, buildArgs); err != nil {
				return nil, nil, err
			}
			if !finalStage {
				continue
			}
			// Now, we get the files to snapshot from this command and take the snapshot
			snapshotFiles := dockerCommand.FilesToSnapshot()
			contents, err := snapshotter.TakeSnapshot(snapshotFiles)
			if err != nil {
				return nil, nil, err
			}
			util.MoveVolumeWhitelistToWhitelist()
			if contents == nil {
				logrus.Info("No files were changed, appending empty layer to config.")
				continue
			}
			// Append the layer to the image
			opener := func() (io.ReadCloser, error) {
				return ioutil.NopCloser(bytes.NewReader(contents)), nil
			}
			layer, err := tarball.LayerFromOpener(opener)
			if err != nil {
				return nil, nil, err
			}
			sourceImage, err = mutate.Append(sourceImage,
				mutate.Addendum{
					Layer: layer,
					History: v1.History{
						Author:    constants.Author,
						CreatedBy: dockerCommand.CreatedBy(),
					},
				},
			)
			if err != nil {
				return nil, nil, err
			}
		}
		if finalStage {
			sourceImage, err = mutate.Config(sourceImage, imageConfig.Config)
			if err != nil {
				return nil, nil, err
			}
			return ref, sourceImage, nil
		}
		if err := saveStageDependencies(index, stages, buildArgs.Clone()); err != nil {
			return nil, nil, err
		}
		// Delete the filesystem
		if err := util.DeleteFilesystem(); err != nil {
			return nil, nil, err
		}
	}
	return nil, nil, err
}

func DoPush(ref name.Reference, image v1.Image, destinations []string, tarPath string) error {
	// continue pushing unless an error occurs
	for _, destination := range destinations {
		// Push the image
		destRef, err := name.NewTag(destination, name.WeakValidation)
		if err != nil {
			return err
		}

		if tarPath != "" {
			return tarball.WriteToFile(tarPath, destRef, image, nil)
		}

		pushAuth, err := authn.DefaultKeychain.Resolve(destRef.Context().Registry)
		if err != nil {
			return err
		}

		wo := remote.WriteOptions{}
		err = remote.Write(destRef, image, pushAuth, http.DefaultTransport, wo)
		if err != nil {
			logrus.Error(fmt.Errorf("Failed to push to destination %s", destination))
			return err
		}
	}
	return nil
}
func saveStageDependencies(index int, stages []instructions.Stage, buildArgs *dockerfile.BuildArgs) error {
	// First, get the files in this stage later stages will need
	dependencies, err := dockerfile.Dependencies(index, stages, buildArgs)
	logrus.Infof("saving dependencies %s", dependencies)
	if err != nil {
		return err
	}
	// Then, create the directory they will exist in
	i := strconv.Itoa(index)
	dependencyDir := filepath.Join(constants.KanikoDir, i)
	if err := os.MkdirAll(dependencyDir, 0755); err != nil {
		return err
	}
	// Now, copy over dependencies to this dir
	for _, d := range dependencies {
		fi, err := os.Lstat(d)
		if err != nil {
			return err
		}
		dest := filepath.Join(dependencyDir, d)
		if fi.IsDir() {
			if err := util.CopyDir(d, dest); err != nil {
				return err
			}
		} else if fi.Mode()&os.ModeSymlink != 0 {
			if err := util.CopySymlink(d, dest); err != nil {
				return err
			}
		} else {
			if err := util.CopyFile(d, dest); err != nil {
				return err
			}
		}
	}
	return nil
}

func getHasher(snapshotMode string) (func(string) (string, error), error) {
	if snapshotMode == constants.SnapshotModeTime {
		logrus.Info("Only file modification time will be considered when snapshotting")
		return util.MtimeHasher(), nil
	}
	if snapshotMode == constants.SnapshotModeFull {
		return util.Hasher(), nil
	}
	return nil, fmt.Errorf("%s is not a valid snapshot mode", snapshotMode)
}

func resolveOnBuild(stage *instructions.Stage, config *v1.Config) error {
	if config.OnBuild == nil {
		return nil
	}
	// Otherwise, parse into commands
	cmds, err := dockerfile.ParseCommands(config.OnBuild)
	if err != nil {
		return err
	}
	// Append to the beginning of the commands in the stage
	stage.Commands = append(cmds, stage.Commands...)
	logrus.Infof("Executing %v build triggers", len(cmds))

	// Blank out the Onbuild command list for this image
	config.OnBuild = nil
	return nil
}
<|endoftext|>


<commit_before>package extender

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/libopenstorage/stork/drivers/volume"
	storklog "github.com/libopenstorage/stork/pkg/log"
	log "github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
)

const (
	filter     = "filter"
	prioritize = "prioritize"
	// priorityScore Score by which each node is bumped if it has data for a volume
	priorityScore = 100
	// defaultScore Score assigned to a node which doesn't have data for any volume
	defaultScore = 10
)

// Extender Scheduler extender
type Extender struct {
	Driver  volume.Driver
	server  *http.Server
	lock    sync.Mutex
	started bool
}

// Start Starts the extender
func (e *Extender) Start() error {
	e.lock.Lock()
	defer e.lock.Unlock()

	if e.started {
		return fmt.Errorf("Extender has already been started")
	}

	// TODO: Make the listen port configurable
	e.server = &http.Server{Addr: ":8099"}

	http.HandleFunc("/", e.serveHTTP)
	go func() {
		if err := e.server.ListenAndServe(); err != nil {
			log.Panicf("Error starting extender server: %v", err)
		}
	}()
	e.started = true
	return nil
}

// Stop Stops the extender
func (e *Extender) Stop() error {
	e.lock.Lock()
	defer e.lock.Unlock()

	if !e.started {
		return fmt.Errorf("Extender has not been started")
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := e.server.Shutdown(ctx); err != nil {
		return err
	}
	e.started = false
	return nil
}

func (e *Extender) serveHTTP(w http.ResponseWriter, req *http.Request) {
	if strings.Contains(req.URL.Path, filter) {
		e.processFilterRequest(w, req)
	} else if strings.Contains(req.URL.Path, prioritize) {
		e.processPrioritizeRequest(w, req)
	} else {
		http.Error(w, "Unsupported request", http.StatusNotFound)
	}
}

func (e *Extender) processFilterRequest(w http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	defer func() {
		if err := req.Body.Close(); err != nil {
			log.Warnf("Error closing decoder")
		}
	}()
	encoder := json.NewEncoder(w)

	var args schedulerapi.ExtenderArgs
	if err := decoder.Decode(&args); err != nil {
		log.Errorf("Error decoding filter request: %v", err)
		http.Error(w, "Decode error", http.StatusBadRequest)
		return
	}

	pod := &args.Pod
	storklog.PodLog(pod).Debugf("Nodes in filter request:")
	for _, node := range args.Nodes.Items {
		storklog.PodLog(pod).Debugf("%+v", node.Status.Addresses)
	}

	filteredNodes := []v1.Node{}
	driverVolumes, err := e.Driver.GetPodVolumes(pod)

	if err != nil {
		storklog.PodLog(pod).Warnf("Error getting volumes for Pod for driver: %v", err)
		if _, ok := err.(*volume.ErrPVCPending); ok {
			http.Error(w, "Waiting for PVC to be bound", http.StatusBadRequest)
			return
		}
	} else if len(driverVolumes) > 0 {
		driverNodes, err := e.Driver.GetNodes()
		if err != nil {
			storklog.PodLog(pod).Errorf("Error getting list of driver nodes, returning all nodes")
		} else {
			for _, node := range args.Nodes.Items {
				for _, address := range node.Status.Addresses {
					if address.Type != v1.NodeHostName {
						continue
					}

					for _, driverNode := range driverNodes {
						if driverNode.Hostname == address.Address &&
							driverNode.Status == volume.NodeOnline {
							filteredNodes = append(filteredNodes, node)
						}
					}
				}
			}
		}
	}

	// If we filtered out all the nodes, or didn't find a PVC that
	// interested us, return all the nodes from the request
	if len(filteredNodes) == 0 {
		filteredNodes = args.Nodes.Items
	}

	storklog.PodLog(pod).Debugf("Nodes in filter response:")
	for _, node := range filteredNodes {
		log.Debugf("%+v", node.Status.Addresses)
	}
	response := &schedulerapi.ExtenderFilterResult{
		Nodes: &v1.NodeList{
			Items: filteredNodes,
		},
	}
	if err := encoder.Encode(response); err != nil {
		storklog.PodLog(pod).Fatalf("Error encoding filter response: %+v : %v", response, err)
	}
}

func (e *Extender) processPrioritizeRequest(w http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	defer func() {
		if err := req.Body.Close(); err != nil {
			log.Warnf("Error closing decoder")
		}
	}()
	encoder := json.NewEncoder(w)

	var args schedulerapi.ExtenderArgs
	if err := decoder.Decode(&args); err != nil {
		log.Errorf("Error decoding prioritize request: %v", err)
		http.Error(w, "Decode error", http.StatusBadRequest)
		return
	}

	pod := &args.Pod
	storklog.PodLog(pod).Debugf("Nodes in prioritize request:")
	for _, node := range args.Nodes.Items {
		storklog.PodLog(pod).Debugf("%+v", node.Status.Addresses)
	}
	respList := schedulerapi.HostPriorityList{}
	driverVolumes, err := e.Driver.GetPodVolumes(pod)

	priorityMap := make(map[string]int)
	if err != nil {
		storklog.PodLog(pod).Warnf("Error getting volumes for Pod for driver: %v", err)
		if _, ok := err.(*volume.ErrPVCPending); ok {
			http.Error(w, "Waiting for PVC to be bound", http.StatusBadRequest)
			return
		}
	} else if len(driverVolumes) > 0 {
		for _, volume := range driverVolumes {
			storklog.PodLog(pod).Debugf("Volume allocated on nodes:")
			for _, node := range volume.DataNodes {
				log.Debugf("%+v", node)
			}

			for _, datanode := range volume.DataNodes {
				for _, node := range args.Nodes.Items {
					for _, address := range node.Status.Addresses {
						if datanode == address.Address {
							// Increment score for every volume that is required by
							// the pod and is present on the node
							_, ok := priorityMap[node.Name]
							if !ok {
								priorityMap[node.Name] = priorityScore
							} else {
								priorityMap[node.Name] += priorityScore
							}
						}
					}
				}
			}
		}
	}

	// For any nodes that didn't have any volumes, assign it a
	// default score so that it doesn't get completelt ignored
	// by the scheduler
	for _, node := range args.Nodes.Items {
		score, ok := priorityMap[node.Name]
		if !ok {
			score = defaultScore
		}
		hostPriority := schedulerapi.HostPriority{Host: node.Name, Score: score}
		respList = append(respList, hostPriority)
	}

	storklog.PodLog(pod).Debugf("Nodes in response:")
	for _, node := range respList {
		storklog.PodLog(pod).Debugf("%+v", node)
	}

	if err := encoder.Encode(respList); err != nil {
		storklog.PodLog(pod).Fatalf("Failed to encode response: %v", err)
	}
	return
}
<commit_msg>Fix error check when stopping extender server<commit_after>package extender

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/libopenstorage/stork/drivers/volume"
	storklog "github.com/libopenstorage/stork/pkg/log"
	log "github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
)

const (
	filter     = "filter"
	prioritize = "prioritize"
	// priorityScore Score by which each node is bumped if it has data for a volume
	priorityScore = 100
	// defaultScore Score assigned to a node which doesn't have data for any volume
	defaultScore = 10
)

// Extender Scheduler extender
type Extender struct {
	Driver  volume.Driver
	server  *http.Server
	lock    sync.Mutex
	started bool
}

// Start Starts the extender
func (e *Extender) Start() error {
	e.lock.Lock()
	defer e.lock.Unlock()

	if e.started {
		return fmt.Errorf("Extender has already been started")
	}

	// TODO: Make the listen port configurable
	e.server = &http.Server{Addr: ":8099"}

	http.HandleFunc("/", e.serveHTTP)
	go func() {
		if err := e.server.ListenAndServe(); err != http.ErrServerClosed {
			log.Panicf("Error starting extender server: %v", err)
		}
	}()
	e.started = true
	return nil
}

// Stop Stops the extender
func (e *Extender) Stop() error {
	e.lock.Lock()
	defer e.lock.Unlock()

	if !e.started {
		return fmt.Errorf("Extender has not been started")
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := e.server.Shutdown(ctx); err != nil {
		return err
	}
	e.started = false
	return nil
}

func (e *Extender) serveHTTP(w http.ResponseWriter, req *http.Request) {
	if strings.Contains(req.URL.Path, filter) {
		e.processFilterRequest(w, req)
	} else if strings.Contains(req.URL.Path, prioritize) {
		e.processPrioritizeRequest(w, req)
	} else {
		http.Error(w, "Unsupported request", http.StatusNotFound)
	}
}

func (e *Extender) processFilterRequest(w http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	defer func() {
		if err := req.Body.Close(); err != nil {
			log.Warnf("Error closing decoder")
		}
	}()
	encoder := json.NewEncoder(w)

	var args schedulerapi.ExtenderArgs
	if err := decoder.Decode(&args); err != nil {
		log.Errorf("Error decoding filter request: %v", err)
		http.Error(w, "Decode error", http.StatusBadRequest)
		return
	}

	pod := &args.Pod
	storklog.PodLog(pod).Debugf("Nodes in filter request:")
	for _, node := range args.Nodes.Items {
		storklog.PodLog(pod).Debugf("%+v", node.Status.Addresses)
	}

	filteredNodes := []v1.Node{}
	driverVolumes, err := e.Driver.GetPodVolumes(pod)

	if err != nil {
		storklog.PodLog(pod).Warnf("Error getting volumes for Pod for driver: %v", err)
		if _, ok := err.(*volume.ErrPVCPending); ok {
			http.Error(w, "Waiting for PVC to be bound", http.StatusBadRequest)
			return
		}
	} else if len(driverVolumes) > 0 {
		driverNodes, err := e.Driver.GetNodes()
		if err != nil {
			storklog.PodLog(pod).Errorf("Error getting list of driver nodes, returning all nodes")
		} else {
			for _, node := range args.Nodes.Items {
				for _, address := range node.Status.Addresses {
					if address.Type != v1.NodeHostName {
						continue
					}

					for _, driverNode := range driverNodes {
						if driverNode.Hostname == address.Address &&
							driverNode.Status == volume.NodeOnline {
							filteredNodes = append(filteredNodes, node)
						}
					}
				}
			}
		}
	}

	// If we filtered out all the nodes, or didn't find a PVC that
	// interested us, return all the nodes from the request
	if len(filteredNodes) == 0 {
		filteredNodes = args.Nodes.Items
	}

	storklog.PodLog(pod).Debugf("Nodes in filter response:")
	for _, node := range filteredNodes {
		log.Debugf("%+v", node.Status.Addresses)
	}
	response := &schedulerapi.ExtenderFilterResult{
		Nodes: &v1.NodeList{
			Items: filteredNodes,
		},
	}
	if err := encoder.Encode(response); err != nil {
		storklog.PodLog(pod).Fatalf("Error encoding filter response: %+v : %v", response, err)
	}
}

func (e *Extender) processPrioritizeRequest(w http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	defer func() {
		if err := req.Body.Close(); err != nil {
			log.Warnf("Error closing decoder")
		}
	}()
	encoder := json.NewEncoder(w)

	var args schedulerapi.ExtenderArgs
	if err := decoder.Decode(&args); err != nil {
		log.Errorf("Error decoding prioritize request: %v", err)
		http.Error(w, "Decode error", http.StatusBadRequest)
		return
	}

	pod := &args.Pod
	storklog.PodLog(pod).Debugf("Nodes in prioritize request:")
	for _, node := range args.Nodes.Items {
		storklog.PodLog(pod).Debugf("%+v", node.Status.Addresses)
	}
	respList := schedulerapi.HostPriorityList{}
	driverVolumes, err := e.Driver.GetPodVolumes(pod)

	priorityMap := make(map[string]int)
	if err != nil {
		storklog.PodLog(pod).Warnf("Error getting volumes for Pod for driver: %v", err)
		if _, ok := err.(*volume.ErrPVCPending); ok {
			http.Error(w, "Waiting for PVC to be bound", http.StatusBadRequest)
			return
		}
	} else if len(driverVolumes) > 0 {
		for _, volume := range driverVolumes {
			storklog.PodLog(pod).Debugf("Volume allocated on nodes:")
			for _, node := range volume.DataNodes {
				log.Debugf("%+v", node)
			}

			for _, datanode := range volume.DataNodes {
				for _, node := range args.Nodes.Items {
					for _, address := range node.Status.Addresses {
						if datanode == address.Address {
							// Increment score for every volume that is required by
							// the pod and is present on the node
							_, ok := priorityMap[node.Name]
							if !ok {
								priorityMap[node.Name] = priorityScore
							} else {
								priorityMap[node.Name] += priorityScore
							}
						}
					}
				}
			}
		}
	}

	// For any nodes that didn't have any volumes, assign it a
	// default score so that it doesn't get completelt ignored
	// by the scheduler
	for _, node := range args.Nodes.Items {
		score, ok := priorityMap[node.Name]
		if !ok {
			score = defaultScore
		}
		hostPriority := schedulerapi.HostPriority{Host: node.Name, Score: score}
		respList = append(respList, hostPriority)
	}

	storklog.PodLog(pod).Debugf("Nodes in response:")
	for _, node := range respList {
		storklog.PodLog(pod).Debugf("%+v", node)
	}

	if err := encoder.Encode(respList); err != nil {
		storklog.PodLog(pod).Fatalf("Failed to encode response: %v", err)
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tpr

import (
	"fmt"
	"strings"
	"unicode"
)

const (
	groupName = "servicecatalog.k8s.io"
)

func withGroupName(name string) string {
	return fmt.Sprintf("%s.%s", name, groupName)
}

// Kind represents the kind of a third party resource. This type implements fmt.Stringer
type Kind string

// String is the fmt.Stringer interface implementation
func (k Kind) String() string {
	return string(k)
}

// TPRName returns the lowercase name, suitable for creating third party resources of this kind
func (k Kind) TPRName() string {
	// this code taken from code under
	// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/extending-api.md#expectations-about-third-party-objects
	var result string
	for ix := range k.String() {
		current := rune(k.String()[ix])
		if unicode.IsUpper(current) && ix > 0 {
			result = result + "-"
		}
		result = result + string(unicode.ToLower(current))
	}
	return result
}

// URLName returns the URL-worthy name this TPR kind. Examples:
//
//	Kind("ServiceClass").URLName() == "serviceclasses"
//	Kind("Broker").URLName() == "brokers"
func (k Kind) URLName() string {
	str := k.String()
	strLen := len(str)
	lastChar := str[strLen-1]
	var ret string
	if lastChar == 's' {
		ret = str + "es"
	} else {
		ret = str + "s"
	}
	return strings.ToLower(ret)
}

const (
	// ServiceBrokerKind is the name of a Service Broker resource, a Kubernetes third party resource.
	ServiceBrokerKind Kind = "Broker"

	// ServiceBrokerListKind is the name of a list of Service Broker resources
	ServiceBrokerListKind Kind = "BrokerList"

	// ServiceBindingKind is the name of a Service Binding resource, a Kubernetes third party resource.
	ServiceBindingKind Kind = "Binding"

	// ServiceBindingListKind is the name for lists of Service Bindings
	ServiceBindingListKind Kind = "BindingList"

	// ServiceClassKind is the name of a Service Class resource, a Kubernetes third party resource.
	ServiceClassKind Kind = "ServiceClass"

	// ServiceClassListKind is the name of a list of service class resources
	ServiceClassListKind Kind = "ServiceClassList"

	// ServiceInstanceKind is the name of a Service Instance resource, a Kubernetes third party resource.
	ServiceInstanceKind Kind = "Instance"

	// ServiceInstanceListKind is the name of a list of service instance resources
	ServiceInstanceListKind Kind = "InstanceList"
)
<commit_msg>adding important note to the URLName func<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tpr

import (
	"fmt"
	"strings"
	"unicode"
)

const (
	groupName = "servicecatalog.k8s.io"
)

func withGroupName(name string) string {
	return fmt.Sprintf("%s.%s", name, groupName)
}

// Kind represents the kind of a third party resource. This type implements fmt.Stringer
type Kind string

// String is the fmt.Stringer interface implementation
func (k Kind) String() string {
	return string(k)
}

// TPRName returns the lowercase name, suitable for creating third party resources of this kind
func (k Kind) TPRName() string {
	// this code taken from code under
	// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/extending-api.md#expectations-about-third-party-objects
	var result string
	for ix := range k.String() {
		current := rune(k.String()[ix])
		if unicode.IsUpper(current) && ix > 0 {
			result = result + "-"
		}
		result = result + string(unicode.ToLower(current))
	}
	return result
}

// URLName returns the URL-worthy name this TPR kind. Examples:
//
//	Kind("ServiceClass").URLName() == "serviceclasses"
//	Kind("Broker").URLName() == "brokers"
//
// Note that this function is incomplete - it is only guaranteed to properly pluralize our 4
// resource types ("Broker", "ServiceClass", "Instance", "Binding")
func (k Kind) URLName() string {
	str := k.String()
	strLen := len(str)
	lastChar := str[strLen-1]
	var ret string
	if lastChar == 's' {
		ret = str + "es"
	} else {
		ret = str + "s"
	}
	return strings.ToLower(ret)
}

const (
	// ServiceBrokerKind is the name of a Service Broker resource, a Kubernetes third party resource.
	ServiceBrokerKind Kind = "Broker"

	// ServiceBrokerListKind is the name of a list of Service Broker resources
	ServiceBrokerListKind Kind = "BrokerList"

	// ServiceBindingKind is the name of a Service Binding resource, a Kubernetes third party resource.
	ServiceBindingKind Kind = "Binding"

	// ServiceBindingListKind is the name for lists of Service Bindings
	ServiceBindingListKind Kind = "BindingList"

	// ServiceClassKind is the name of a Service Class resource, a Kubernetes third party resource.
	ServiceClassKind Kind = "ServiceClass"

	// ServiceClassListKind is the name of a list of service class resources
	ServiceClassListKind Kind = "ServiceClassList"

	// ServiceInstanceKind is the name of a Service Instance resource, a Kubernetes third party resource.
	ServiceInstanceKind Kind = "Instance"

	// ServiceInstanceListKind is the name of a list of service instance resources
	ServiceInstanceListKind Kind = "InstanceList"
)
<|endoftext|>


<commit_before>package next

import (
	"bufio"
	"encoding/binary"
	"errors"
	"io"
	"log"
	"net"
	"os"
	"reflect"
	"runtime"
)

type Tcp struct {
	Conn   map[int]*net.TCPConn
	Config *Config
	Logger *log.Logger
	routes *Routes
}

const (
	TcpHead = 0xAA
	TcpTail = 0x55
)

func NewTcp() *Tcp {
	tcp := &Tcp{
		Conn:   make(map[int]*net.TCPConn),
		Config: NewConfig(),
		Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
		routes: NewRoutes(),
	}

	// Load default config if exists
	file := "config.json"
	if fileExists(file) {
		tcp.Config.Load(file)
	}

	return tcp
}

// Pack is a utility function to read from the supplied Writer
// according to the Next protocol spec:
//
//    AA[x][x][x][x][x][x][x][x]55
//      |  (int32) || (binary)
//      |  4-byte  || N-byte
//      ------------------------...
//        size       data
func (t *Tcp) Pack(w io.Writer, data []byte) error {
	// Head
	_, err := w.Write([]byte{TcpHead})
	if err != nil {
		return errors.New("write head fail")
	}

	// Size
	err = binary.Write(w, binary.BigEndian, int32(len(data)))
	if err != nil {
		return errors.New("write size fail")
	}

	// Data
	_, err = w.Write(data)
	if err != nil {
		return errors.New("write data fail")
	}

	// Tail
	_, err = w.Write([]byte{TcpTail})
	if err != nil {
		return errors.New("write tail fail")
	}

	return nil
}

// Unpack is a utility function to read from the supplied Reader
// according to the Next protocol spec:
//
//    AA[x][x][x][x][x][x][x][x]55
//      |  (int32) || (binary)
//      |  4-byte  || N-byte
//      ------------------------...
//        size       data
func (t *Tcp) Unpack(r io.Reader) ([]byte, error) {
	// Check head
	head := make([]byte, 1)
	_, err := r.Read(head)
	if err != nil {
		return nil, err
	}
	if int32(head[0]) != TcpHead {
		return nil, errors.New("data head is error")
	}

	// message size
	var size int32
	err = binary.Read(r, binary.BigEndian, &size)
	if err != nil {
		return nil, err
	}

	// message binary data
	buf := make([]byte, size)
	_, err = io.ReadFull(r, buf)
	if err != nil {
		return nil, err
	}

	// Check head
	tail := make([]byte, 1)
	_, err = r.Read(tail)
	if err != nil {
		return nil, err
	}
	if int32(tail[0]) != TcpTail {
		return nil, errors.New("data tail is error")
	}

	return buf, nil
}

func (t *Tcp) Run(addr string) {
	tcpAddr, _ := net.ResolveTCPAddr("tcp", addr)
	tcpListener, _ := net.ListenTCP("tcp", tcpAddr)
	defer tcpListener.Close()

	t.Logger.Printf("next tcp serving %s\n", addr)
	for {
		tcpConn, err := tcpListener.AcceptTCP()
		if err != nil {
			continue
		}

		t.Logger.Printf("connected: %s\n", tcpConn.RemoteAddr().String())
		go t.Pipe(tcpConn)
	}
}

func (t *Tcp) Pipe(conn *net.TCPConn) {
	fd, err := t.Fd(conn)
	if err != nil {
		t.Logger.Printf("connect err: %s\n", err)
		return
	}
	defer func() {
		t.Logger.Printf("disconnected: %s\n", conn.RemoteAddr().String())
		conn.Close()
		delete(t.Conn, fd)
	}()

	// Save in map
	t.Conn[fd] = conn

	// Read data
	reader := bufio.NewReader(conn)
	for {
		body, err := t.Unpack(reader)
		if err == io.EOF {
			return
		}

		if err != nil {
			t.Logger.Print(err)
		} else {
			t.handler(conn, body)
		}
	}
}

// Get the integer Unix file descriptor referencing the open file
func (t *Tcp) Fd(conn *net.TCPConn) (int, error) {
	f, err := conn.File()
	if err != nil {
		return -1, err
	}

	return int(f.Fd()), nil
}

// Post adds a handler for the 'Via' TCP method for tcp.
func (t *Tcp) Via(route string, handler interface{}) {
	t.routes.Add(route, "VIA", handler)
}

// safelyCall invokes `function` in recover block
func (t *Tcp) safelyCall(function reflect.Value, args []reflect.Value) (resp []reflect.Value, e interface{}) {
	defer func() {
		if err := recover(); err != nil {
			if t.Config.Bool("panic") {
				// go back to panic
				panic(err)
			} else {
				e = err
				resp = nil
				t.Logger.Println("Handler crashed with error", err)
				for i := 1; ; i += 1 {
					_, file, line, ok := runtime.Caller(i)
					if !ok {
						break
					}
					t.Logger.Println(file, line)
				}
			}
		}
	}()
	return function.Call(args), nil
}

func (t *Tcp) handler(conn *net.TCPConn, body []byte) {
	ctx := TcpContext{
		Params: make(map[string]string),
		Tcp:    t,
		conn:   conn,
	}

	// Read json body
	json := NewJson()
	json.Load(body)
	requestPath := json.Get("method").MustString()

	route := t.routes.Match(requestPath, "VIA")
	if route == nil {
		ctx.WriteJSON("404", "Method not found")
		return
	}
	cr := route.cr

	var args []reflect.Value
	handlerType := route.handler.Type()
	if requiresContext(handlerType) {
		args = append(args, reflect.ValueOf(&ctx))
	}

	match := cr.FindStringSubmatch(requestPath)
	for _, arg := range match[1:] {
		args = append(args, reflect.ValueOf(arg))
	}

	_, err := t.safelyCall(route.handler, args)
	if err != nil {
		//there was an error or panic while calling the handler
		ctx.WriteJSON("500", "Server Error")
	}
}

// --------
// Tcp Context
// --------
type TcpContext struct {
	Params map[string]string
	Tcp    *Tcp
	conn   *net.TCPConn
}

// WriteJSON writes json data into the response object.
func (ctx *TcpContext) WriteJSON(code, msg string, data ...interface{}) {
	json := NewJson()
	if ctx.Tcp.Config.Bool("debug.profiler") {
		json.Set("debug", ctx.Params)
	}

	json.Set("code", code)
	json.Set("msg", msg)

	if len(data) > 0 {
		json.Set("data", data[0])
	}
	out, _ := json.Encode()

	ctx.Tcp.Pack(ctx.conn, out)
	ctx.conn.Write([]byte{'\n'})
}
<commit_msg>change protocl<commit_after>package next

import (
	"bufio"
	"encoding/binary"
	"errors"
	"io"
	"log"
	"net"
	"os"
	"reflect"
	"runtime"
)

type Tcp struct {
	Conn   map[int]*net.TCPConn
	Config *Config
	Logger *log.Logger
	routes *Routes
}

const (
	TcpHead = 0xAA
	TcpTail = 0x0A
)

func NewTcp() *Tcp {
	tcp := &Tcp{
		Conn:   make(map[int]*net.TCPConn),
		Config: NewConfig(),
		Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
		routes: NewRoutes(),
	}

	// Load default config if exists
	file := "config.json"
	if fileExists(file) {
		tcp.Config.Load(file)
	}

	return tcp
}

// Pack is a utility function to read from the supplied Writer
// according to the Next protocol spec:
//
//    AA[x][x][x][x][x][x][x][x]55
//      |  (int32) || (binary)
//      |  4-byte  || N-byte
//      ------------------------...
//        size       data
func (t *Tcp) Pack(w io.Writer, data []byte) error {
	// Head
	_, err := w.Write([]byte{TcpHead})
	if err != nil {
		return errors.New("write head fail")
	}

	// Size
	err = binary.Write(w, binary.BigEndian, int32(len(data)))
	if err != nil {
		return errors.New("write size fail")
	}

	// Data
	_, err = w.Write(data)
	if err != nil {
		return errors.New("write data fail")
	}

	// Tail
	_, err = w.Write([]byte{TcpTail})
	if err != nil {
		return errors.New("write tail fail")
	}

	return nil
}

// Unpack is a utility function to read from the supplied Reader
// according to the Next protocol spec:
//
//    AA[x][x][x][x][x][x][x][x]55
//      |  (int32) || (binary)
//      |  4-byte  || N-byte
//      ------------------------...
//        size       data
func (t *Tcp) Unpack(r io.Reader) ([]byte, error) {
	// Check head
	head := make([]byte, 1)
	_, err := r.Read(head)
	if err != nil {
		return nil, err
	}
	if int32(head[0]) != TcpHead {
		return nil, errors.New("data head is error")
	}

	// message size
	var size int32
	err = binary.Read(r, binary.BigEndian, &size)
	if err != nil {
		return nil, err
	}

	// message binary data
	buf := make([]byte, size)
	_, err = io.ReadFull(r, buf)
	if err != nil {
		return nil, err
	}

	// Check head
	tail := make([]byte, 1)
	_, err = r.Read(tail)
	if err != nil {
		return nil, err
	}
	if int32(tail[0]) != TcpTail {
		return nil, errors.New("data tail is error")
	}

	return buf, nil
}

func (t *Tcp) Run(addr string) {
	tcpAddr, _ := net.ResolveTCPAddr("tcp", addr)
	tcpListener, _ := net.ListenTCP("tcp", tcpAddr)
	defer tcpListener.Close()

	t.Logger.Printf("next tcp serving %s\n", addr)
	for {
		tcpConn, err := tcpListener.AcceptTCP()
		if err != nil {
			continue
		}

		t.Logger.Printf("connected: %s\n", tcpConn.RemoteAddr().String())
		go t.Pipe(tcpConn)
	}
}

func (t *Tcp) Pipe(conn *net.TCPConn) {
	fd, err := t.Fd(conn)
	if err != nil {
		t.Logger.Printf("connect err: %s\n", err)
		return
	}
	defer func() {
		t.Logger.Printf("disconnected: %s\n", conn.RemoteAddr().String())
		conn.Close()
		delete(t.Conn, fd)
	}()

	// Save in map
	t.Conn[fd] = conn

	// Read data
	reader := bufio.NewReader(conn)
	for {
		body, err := t.Unpack(reader)
		if err == io.EOF {
			return
		}

		if err != nil {
			t.Logger.Print(err)
		} else {
			t.handler(conn, body)
		}
	}
}

// Get the integer Unix file descriptor referencing the open file
func (t *Tcp) Fd(conn *net.TCPConn) (int, error) {
	f, err := conn.File()
	if err != nil {
		return -1, err
	}

	return int(f.Fd()), nil
}

// Post adds a handler for the 'Via' TCP method for tcp.
func (t *Tcp) Via(route string, handler interface{}) {
	t.routes.Add(route, "VIA", handler)
}

// safelyCall invokes `function` in recover block
func (t *Tcp) safelyCall(function reflect.Value, args []reflect.Value) (resp []reflect.Value, e interface{}) {
	defer func() {
		if err := recover(); err != nil {
			if t.Config.Bool("panic") {
				// go back to panic
				panic(err)
			} else {
				e = err
				resp = nil
				t.Logger.Println("Handler crashed with error", err)
				for i := 1; ; i += 1 {
					_, file, line, ok := runtime.Caller(i)
					if !ok {
						break
					}
					t.Logger.Println(file, line)
				}
			}
		}
	}()
	return function.Call(args), nil
}

func (t *Tcp) handler(conn *net.TCPConn, body []byte) {
	ctx := TcpContext{
		Params: make(map[string]string),
		Tcp:    t,
		conn:   conn,
	}

	// Read json body
	json := NewJson()
	json.Load(body)
	requestPath := json.Get("method").MustString()

	route := t.routes.Match(requestPath, "VIA")
	if route == nil {
		ctx.WriteJSON("404", "Method not found")
		return
	}
	cr := route.cr

	var args []reflect.Value
	handlerType := route.handler.Type()
	if requiresContext(handlerType) {
		args = append(args, reflect.ValueOf(&ctx))
	}

	match := cr.FindStringSubmatch(requestPath)
	for _, arg := range match[1:] {
		args = append(args, reflect.ValueOf(arg))
	}

	_, err := t.safelyCall(route.handler, args)
	if err != nil {
		//there was an error or panic while calling the handler
		ctx.WriteJSON("500", "Server Error")
	}
}

// --------
// Tcp Context
// --------
type TcpContext struct {
	Params map[string]string
	Tcp    *Tcp
	conn   *net.TCPConn
}

// WriteJSON writes json data into the response object.
func (ctx *TcpContext) WriteJSON(code, msg string, data ...interface{}) {
	json := NewJson()
	if ctx.Tcp.Config.Bool("debug.profiler") {
		json.Set("debug", ctx.Params)
	}

	json.Set("code", code)
	json.Set("msg", msg)

	if len(data) > 0 {
		json.Set("data", data[0])
	}
	out, _ := json.Encode()

	ctx.Tcp.Pack(ctx.conn, out)
}
<|endoftext|>


<commit_before>package tdx

import (
	"encoding/binary"
	"fmt"
	"os"
	"path"
	"time"
)

var tz = time.FixedZone("UTC+8", int(8*time.Hour/time.Second))

// Bar provides a generic representation of bar data.
type Bar interface {
	Time() time.Time
	Open() float32
	High() float32
	Low() float32
	Close() float32
	Volume() uint32
	Turnover() float32
}

// A dayBar is a single record in end-of-day bar (.day) files.
type dayBar struct {
	RawDate     uint32  // yyyymmdd
	RawOpen     uint32  // in cents
	RawHigh     uint32  // in cents
	RawLow      uint32  // in cents
	RawClose    uint32  // in cents
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *dayBar) Time() time.Time {
	mmdd := int(bar.RawDate % 10000)

	return time.Date(int(bar.RawDate/10000), time.Month(mmdd/100), mmdd%100, 15, 0, 0, 0, tz)
}

func (bar *dayBar) Open() float32 {
	return float32(bar.RawOpen) / 100
}

func (bar *dayBar) High() float32 {
	return float32(bar.RawHigh) / 100
}

func (bar *dayBar) Low() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *dayBar) Close() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *dayBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *dayBar) Turnover() float32 {
	return bar.RawTurnover
}

// A fiveBar in a single record in five-minute bar (.5) files.
type fiveBar struct {
	RawDate     uint16  // higher 5 bits: years since 2004, higher 11 bits: mmdd
	RawTime     uint16  // minutes since 00:00:00 (UTC+8)
	RawOpen     uint32  // in cents
	RawHigh     uint32  // in cents
	RawLow      uint32  // in cents
	RawClose    uint32  // in cents
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *fiveBar) Time() time.Time {
	mmdd := int(bar.RawDate & (^uint16(0) >> (16 - 11)))

	return time.Date(2004+int(bar.RawDate>>11), time.Month(mmdd/100), mmdd%100, int(bar.RawTime/60), int(bar.RawTime%60), 0, 0, tz)
}

func (bar *fiveBar) Open() float32 {
	return float32(bar.RawOpen) / 100
}

func (bar *fiveBar) High() float32 {
	return float32(bar.RawHigh) / 100
}

func (bar *fiveBar) Low() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *fiveBar) Close() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *fiveBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *fiveBar) Turnover() float32 {
	return bar.RawTurnover
}

// A lcnBar is a single record in minute bar (.lc5/.lc1) files.
type lcnBar struct {
	RawDate     uint16  // higher 5 bits: years since 2004, higher 11 bits: mmdd
	RawTime     uint16  // minutes since 00:00:00 (UTC+8)
	RawOpen     float32 // in yuan
	RawHigh     float32 // in yuan
	RawLow      float32 // in yuan
	RawClose    float32 // in yuan
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *lcnBar) Time() time.Time {
	mmdd := int(bar.RawDate & (^uint16(0) >> (16 - 11)))

	return time.Date(2004+int(bar.RawDate>>11), time.Month(mmdd/100), mmdd%100, int(bar.RawTime/60), int(bar.RawTime%60), 0, 0, tz)
}

func (bar *lcnBar) Open() float32 {
	return bar.RawOpen
}

func (bar *lcnBar) High() float32 {
	return bar.RawHigh
}

func (bar *lcnBar) Low() float32 {
	return bar.RawLow
}

func (bar *lcnBar) Close() float32 {
	return bar.RawClose
}

func (bar *lcnBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *lcnBar) Turnover() float32 {
	return bar.RawTurnover
}

// DecodeFile decodes a bar data file that has been encoded in any of
// the supported formats. It detects file format by the file extension.
func DecodeFile(filepath string) ([]Bar, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}

	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

	bars := make([]Bar, fi.Size()/32)
	for i := 0; i < len(bars); i++ {
		var bar Bar
		switch path.Ext(filepath) {
		case ".day":
			bar = new(dayBar)
		case ".5":
			bar = new(fiveBar)
		case ".lc1", ".lc5":
			bar = new(lcnBar)
		default:
			return nil, fmt.Errorf("unsupported file: %s", filepath)
		}

		err = binary.Read(f, binary.LittleEndian, bar)
		if err != nil {
			return nil, err
		}

		bars[i] = bar
	}

	return bars, nil
}
<commit_msg>Add metadata of the dataset<commit_after>package tdx

import (
	"encoding/binary"
	"fmt"
	"os"
	"path"
	"time"
)

var tz = time.FixedZone("UTC+8", int(8*time.Hour/time.Second))

// Bar provides a generic representation of bar data.
type Bar interface {
	Time() time.Time
	Open() float32
	High() float32
	Low() float32
	Close() float32
	Volume() uint32
	Turnover() float32
}

// A dayBar is a single record in end-of-day bar (.day) files.
type dayBar struct {
	RawDate     uint32  // yyyymmdd
	RawOpen     uint32  // in cents
	RawHigh     uint32  // in cents
	RawLow      uint32  // in cents
	RawClose    uint32  // in cents
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *dayBar) Time() time.Time {
	mmdd := int(bar.RawDate % 10000)

	return time.Date(int(bar.RawDate/10000), time.Month(mmdd/100), mmdd%100, 15, 0, 0, 0, tz)
}

func (bar *dayBar) Open() float32 {
	return float32(bar.RawOpen) / 100
}

func (bar *dayBar) High() float32 {
	return float32(bar.RawHigh) / 100
}

func (bar *dayBar) Low() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *dayBar) Close() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *dayBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *dayBar) Turnover() float32 {
	return bar.RawTurnover
}

// A fiveBar in a single record in five-minute bar (.5) files.
type fiveBar struct {
	RawDate     uint16  // higher 5 bits: years since 2004, higher 11 bits: mmdd
	RawTime     uint16  // minutes since 00:00:00 (UTC+8)
	RawOpen     uint32  // in cents
	RawHigh     uint32  // in cents
	RawLow      uint32  // in cents
	RawClose    uint32  // in cents
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *fiveBar) Time() time.Time {
	mmdd := int(bar.RawDate & (^uint16(0) >> (16 - 11)))

	return time.Date(2004+int(bar.RawDate>>11), time.Month(mmdd/100), mmdd%100, int(bar.RawTime/60), int(bar.RawTime%60), 0, 0, tz)
}

func (bar *fiveBar) Open() float32 {
	return float32(bar.RawOpen) / 100
}

func (bar *fiveBar) High() float32 {
	return float32(bar.RawHigh) / 100
}

func (bar *fiveBar) Low() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *fiveBar) Close() float32 {
	return float32(bar.RawLow) / 100
}

func (bar *fiveBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *fiveBar) Turnover() float32 {
	return bar.RawTurnover
}

// A lcnBar is a single record in minute bar (.lc5/.lc1) files.
type lcnBar struct {
	RawDate     uint16  // higher 5 bits: years since 2004, higher 11 bits: mmdd
	RawTime     uint16  // minutes since 00:00:00 (UTC+8)
	RawOpen     float32 // in yuan
	RawHigh     float32 // in yuan
	RawLow      float32 // in yuan
	RawClose    float32 // in yuan
	RawTurnover float32 // in yuan
	RawVolume   uint32  // in shares
	Reserved    [4]byte // unknown
}

func (bar *lcnBar) Time() time.Time {
	mmdd := int(bar.RawDate & (^uint16(0) >> (16 - 11)))

	return time.Date(2004+int(bar.RawDate>>11), time.Month(mmdd/100), mmdd%100, int(bar.RawTime/60), int(bar.RawTime%60), 0, 0, tz)
}

func (bar *lcnBar) Open() float32 {
	return bar.RawOpen
}

func (bar *lcnBar) High() float32 {
	return bar.RawHigh
}

func (bar *lcnBar) Low() float32 {
	return bar.RawLow
}

func (bar *lcnBar) Close() float32 {
	return bar.RawClose
}

func (bar *lcnBar) Volume() uint32 {
	return bar.RawVolume
}

func (bar *lcnBar) Turnover() float32 {
	return bar.RawTurnover
}

// Dataset is a collection of bars with metadata.
type Dataset struct {
	Market  string // Market identification code (ISO 10383)
	Symbol  string // Local exchange ticker symbol
	BarSize uint   // Length of the bar defined in minutes
	Bars    []Bar
}

// DecodeFile decodes a bar data file that has been encoded in any of
// the supported formats. It detects file format by the file extension.
func DecodeFile(filepath string) (*Dataset, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}

	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

	market, symbol, ext := path.Base(filepath)[0:2], path.Base(filepath)[2:8], path.Base(filepath)[8:]
	switch market {
	case "sh":
		market = "XSHG"
	case "sz":
		market = "XSHE"
	default:
		return nil, fmt.Errorf("unknown market: %s", market)
	}

	var barSize uint
	bars := make([]Bar, fi.Size()/32)
	for i := 0; i < len(bars); i++ {
		var bar Bar
		switch ext {
		case ".day":
			bar = new(dayBar)
			barSize = 1440
		case ".5":
			bar = new(fiveBar)
			barSize = 5
		case ".lc5":
			bar = new(lcnBar)
			barSize = 5
		case ".lc1":
			bar = new(lcnBar)
			barSize = 1
		default:
			return nil, fmt.Errorf("unsupported file: %s", filepath)
		}

		err = binary.Read(f, binary.LittleEndian, bar)
		if err != nil {
			return nil, err
		}

		bars[i] = bar
	}

	return &Dataset{market, symbol, barSize, bars}, nil
}
<|endoftext|>


<commit_before>package music
import (
    "errors"
    "net/http"
    "encoding/xml"
    "net/url"
    "time"
    "io/ioutil"
    "logger"
    "regexp"
    "strings"
    "strconv"
)

type RootResponse struct {
    XMLName xml.Name `xml:"metadata"`
    Info InfoRelease `xml:"release-list"`
    //Releases []Release `xml:"release-list>release"`
}

type InfoRelease struct {
    Count string `xml:"count,attr"`
    Releases []Release `xml:"release"`
}

type Release struct {
    //XMLName xml.Name `xml:"release"`
    //Field string `xml:"field"`
    Id string `xml:"id,attr"`
    Group ReleaseGroup `xml:"release-group"`
}

type ReleaseGroup struct {
    Id string `xml:"id,attr"`
}

func (rl RootResponse)GetCount()int{
    //logger.GetLogger().Info("=>",rl.Info)
    if val,err := strconv.ParseInt(rl.Info.Count,32,10) ; err == nil {
        return int(val)
    }
    return 0
}

func (rl RootResponse)GetUrl()(string,error){
    if len(rl.Info.Releases) == 0 {
        return "",errors.New("Impossible to get id")
    }
    // read 10 firsts
    for i:= 0 ; i < 10 && i < len(rl.Info.Releases) ; i++{
        r := rl.Info.Releases[i]
        if r.Group.Id != "" {
            // Check if exist
            urlImage := "http://coverartarchive.org/release-group/" + r.Group.Id + "/front-250"
            resp,_ := http.Get(urlImage)
            if resp != nil && resp.Body!=nil {
                defer resp.Body.Close()
            }
            if resp.StatusCode == 200 {
                return urlImage,nil
            }
        }
        if r.Id != "" {
            urlImage := "http://coverartarchive.org/release/" + r.Id + "/front-250"
            resp,_ := http.Get(urlImage)
            if resp != nil && resp.Body!=nil {
                defer resp.Body.Close()
            }
            if resp.StatusCode == 200 {
                return urlImage,nil
            }
        }
    }
    return "",errors.New("No id")
}

// @param threashold : max number of results
func extractInfo(data []byte,threashold int)(string,error){
    var r RootResponse
    xml.Unmarshal(data,&r)
    if threashold!=0 && r.GetCount() > threashold {
        return "",errors.New("Too much results")
    }
    return r.GetUrl()
}

// key if composed of artist-album
var coverCache = make(map[string]string)

// use to request music brainz one time max by second
var lastGet = 0

var waitMBTime = 2000

func checkLastGet(){
    defer func(){
        lastGet = time.Now().Nanosecond()
    }()
    if lastGet == 0 {
        return
    }
    if rest := waitMBTime * 1000000 - time.Now().Nanosecond() - lastGet ; rest > 0 {
        time.Sleep(time.Nanosecond * time.Duration(rest))
    }
}

var musicBrainzUrl = "http://musicbrainz.org/ws/2/release/?"
var totalMBRequest = 0
var retryMBRequest = 0

func doSearch(params string, threashold int)string{
    fParams := url.Values{"query":[]string{params}}.Encode()
    checkLastGet()
    totalMBRequest++
    //logger.GetLogger().Info("req",params,totalMBRequest,retryMBRequest)
    if resp,e := http.Get(musicBrainzUrl + fParams); e == nil {
        defer resp.Body.Close()
        // Quota exceed, relaunch after time
        if resp.StatusCode == 503 {
            retryMBRequest++
            logger.GetLogger().Error("Limit exceed, retry", totalMBRequest, retryMBRequest, params)
            time.Sleep(time.Duration(waitMBTime) * time.Millisecond)
            return doSearch(params,threashold)
        }
        // Check if release field are present, if not, limit
        d, _ := ioutil.ReadAll(resp.Body)
        if cover, err := extractInfo(d, threashold); err == nil {
            return cover
        }
    }
    return ""
}

// Get cover musicbrainz id and check if resource exist
// Rules : check :
// 1 : artist + album
// 2 : artist + song title (as release) for single case
// 3 : artist and half album
// 4 : only album with threashold
// 3 : artist only
func GetCover(artist,album,title string)string{
    if artist == "" && album == ""{
        return ""
    }

    // Take first artist (before ( , and &)
    formatArtist,_ := regexp.Compile("[a-zA-Z0-9\\. ]*")
    if values := formatArtist.FindAllString(artist,1) ; len(values) == 1 {
        artist = values[0]
    }
    album = strings.Replace(strings.ToLower(album)," ost","",-1)
    if p:= strings.Index(album,"cd") ; p!=-1 {
        album = album[:p]
    }
    key := artist + "-" + album
    if url,ok:= coverCache[key] ; ok {
        return url
    }

    // 1
    if cover := doSearch("artist:\"" + artist + "\" AND release:\"" + album + "\"",0) ; cover != "" {
        return cover
    }
    // 2
    if cover := doSearch("artist:\"" + artist + "\" AND release:\"" + title + "\"",0) ; cover != "" {
        return cover
    }
    // 3 remove end useless of album : stop at first carac != aZ09., stop before CD and OST
    patternAlbum,_ := regexp.Compile("[a-zA-Z0-9 \\.]*")
    if fAlbum := patternAlbum.FindAllString(album,1) ; len(fAlbum) == 1 && len(fAlbum[0])!= len(album){
        if cover := doSearch("artist:\"" + artist + "\" AND release:\"" + fAlbum[0] + "\"",0) ; cover != "" {
            return cover
        }
    }
    // 4
    if cover := doSearch("release:\"" + album + "\"",50) ; cover != "" {
        return cover
    }
    // 5
    if cover := doSearch("artist:\"" + artist + "\"",0) ; cover != "" {
        return cover
    }
    return ""
}
<commit_msg>Correct bug nil on cover<commit_after>package music
import (
    "errors"
    "net/http"
    "encoding/xml"
    "net/url"
    "time"
    "io/ioutil"
    "logger"
    "regexp"
    "strings"
    "strconv"
)

type RootResponse struct {
    XMLName xml.Name `xml:"metadata"`
    Info InfoRelease `xml:"release-list"`
    //Releases []Release `xml:"release-list>release"`
}

type InfoRelease struct {
    Count string `xml:"count,attr"`
    Releases []Release `xml:"release"`
}

type Release struct {
    //XMLName xml.Name `xml:"release"`
    //Field string `xml:"field"`
    Id string `xml:"id,attr"`
    Group ReleaseGroup `xml:"release-group"`
}

type ReleaseGroup struct {
    Id string `xml:"id,attr"`
}

func (rl RootResponse)GetCount()int{
    //logger.GetLogger().Info("=>",rl.Info)
    if val,err := strconv.ParseInt(rl.Info.Count,32,10) ; err == nil {
        return int(val)
    }
    return 0
}

func (rl RootResponse)GetUrl()(string,error){
    if len(rl.Info.Releases) == 0 {
        return "",errors.New("Impossible to get id")
    }
    // read 10 firsts
    for i:= 0 ; i < 10 && i < len(rl.Info.Releases) ; i++{
        r := rl.Info.Releases[i]
        if r.Group.Id != "" {
            // Check if exist
            urlImage := "http://coverartarchive.org/release-group/" + r.Group.Id + "/front-250"
            resp,_ := http.Get(urlImage)
            if resp != nil && resp.Body!=nil {
                defer resp.Body.Close()
            }
            if resp!=nil && resp.StatusCode == 200 {
                return urlImage,nil
            }
        }
        if r.Id != "" {
            urlImage := "http://coverartarchive.org/release/" + r.Id + "/front-250"
            resp,_ := http.Get(urlImage)
            if resp != nil && resp.Body!=nil {
                defer resp.Body.Close()
            }
            if resp!=nil && resp.StatusCode == 200 {
                return urlImage,nil
            }
        }
    }
    return "",errors.New("No id")
}

// @param threashold : max number of results
func extractInfo(data []byte,threashold int)(string,error){
    var r RootResponse
    xml.Unmarshal(data,&r)
    if threashold!=0 && r.GetCount() > threashold {
        return "",errors.New("Too much results")
    }
    return r.GetUrl()
}

// key if composed of artist-album
var coverCache = make(map[string]string)

// use to request music brainz one time max by second
var lastGet = 0

var waitMBTime = 2000

func checkLastGet(){
    defer func(){
        lastGet = time.Now().Nanosecond()
    }()
    if lastGet == 0 {
        return
    }
    if rest := waitMBTime * 1000000 - time.Now().Nanosecond() - lastGet ; rest > 0 {
        time.Sleep(time.Nanosecond * time.Duration(rest))
    }
}

var musicBrainzUrl = "http://musicbrainz.org/ws/2/release/?"
var totalMBRequest = 0
var retryMBRequest = 0

func doSearch(params string, threashold int)string{
    fParams := url.Values{"query":[]string{params}}.Encode()
    checkLastGet()
    totalMBRequest++
    //logger.GetLogger().Info("req",params,totalMBRequest,retryMBRequest)
    if resp,e := http.Get(musicBrainzUrl + fParams); e == nil {
        defer resp.Body.Close()
        // Quota exceed, relaunch after time
        if resp.StatusCode == 503 {
            retryMBRequest++
            logger.GetLogger().Error("Limit exceed, retry", totalMBRequest, retryMBRequest, params)
            time.Sleep(time.Duration(waitMBTime) * time.Millisecond)
            return doSearch(params,threashold)
        }
        // Check if release field are present, if not, limit
        d, _ := ioutil.ReadAll(resp.Body)
        if cover, err := extractInfo(d, threashold); err == nil {
            return cover
        }
    }
    return ""
}

// Get cover musicbrainz id and check if resource exist
// Rules : check :
// 1 : artist + album
// 2 : artist + song title (as release) for single case
// 3 : artist and half album
// 4 : only album with threashold
// 3 : artist only
func GetCover(artist,album,title string)string{
    if artist == "" && album == ""{
        return ""
    }

    // Take first artist (before ( , and &)
    formatArtist,_ := regexp.Compile("[a-zA-Z0-9\\. ]*")
    if values := formatArtist.FindAllString(artist,1) ; len(values) == 1 {
        artist = values[0]
    }
    album = strings.Replace(strings.ToLower(album)," ost","",-1)
    if p:= strings.Index(album,"cd") ; p!=-1 {
        album = album[:p]
    }
    key := artist + "-" + album
    if url,ok:= coverCache[key] ; ok {
        return url
    }

    // 1
    if cover := doSearch("artist:\"" + artist + "\" AND release:\"" + album + "\"",0) ; cover != "" {
        return cover
    }
    // 2
    if cover := doSearch("artist:\"" + artist + "\" AND release:\"" + title + "\"",0) ; cover != "" {
        return cover
    }
    // 3 remove end useless of album : stop at first carac != aZ09., stop before CD and OST
    patternAlbum,_ := regexp.Compile("[a-zA-Z0-9 \\.]*")
    if fAlbum := patternAlbum.FindAllString(album,1) ; len(fAlbum) == 1 && len(fAlbum[0])!= len(album){
        if cover := doSearch("artist:\"" + artist + "\" AND release:\"" + fAlbum[0] + "\"",0) ; cover != "" {
            return cover
        }
    }
    // 4
    if cover := doSearch("release:\"" + album + "\"",50) ; cover != "" {
        return cover
    }
    // 5
    if cover := doSearch("artist:\"" + artist + "\"",0) ; cover != "" {
        return cover
    }
    return ""
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"text/template"
	"github.com/gorilla/mux"
)

func hello(w http.ResponseWriter, r *http.Request) {
	templatePath := "index.html"
	template, _ := template.ParseFiles(templatePath)
	template.Execute(w, nil)
	fmt.Println("Hit index.html")
}

func game(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	gameId := vars["gameId"]
	fmt.Println(gameId)

	gameMap := make(map[string]string)
	gameMap["GameId"] = gameId

	templatePath := "game.html"
	template, _ := template.ParseFiles(templatePath)
	template.Execute(w, gameMap)
	fmt.Println("Hit game.html")
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", hello)
    r.HandleFunc("/game/{gameId}", game)
    r.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))
    http.Handle("/", r)
    http.ListenAndServe(":8000", nil)
}
<commit_msg>Added newgame http endpoint which basically just generates a UUID<commit_after>package main

import (
	"fmt"
	"net/http"
	"text/template"
	_ "io/ioutil"
	"io"
	"github.com/gorilla/mux"
	"github.com/twinj/uuid"
)

func hello(w http.ResponseWriter, r *http.Request) {
	templatePath := "index.html"
	template, _ := template.ParseFiles(templatePath)
	template.Execute(w, nil)
	fmt.Println("Hit index.html")
}

func game(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	gameId := vars["gameId"]
	fmt.Println(gameId)

	gameMap := make(map[string]string)
	gameMap["GameId"] = gameId

	templatePath := "game.html"
	template, _ := template.ParseFiles(templatePath)
	template.Execute(w, gameMap)
	fmt.Println("Hit game.html")
}

func newGame(w http.ResponseWriter, r *http.Request) {
	// The UUID for this game which will be used everywhere
	// as this game's identifier
	u := uuid.NewV4()
	io.WriteString(w, u.String())
}

func main() {
	// The default UUID format has gross curly brackets in it!
	uuid.SwitchFormat(uuid.CleanHyphen)

	// Setup all routes and serve
    r := mux.NewRouter()
    r.HandleFunc("/", hello)
    r.HandleFunc("/newgame", newGame)
    r.HandleFunc("/game/{gameId}", game)
    r.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))
    http.Handle("/", r)
    http.ListenAndServe(":8000", nil)
}

// Misc notes:
/*
	Games should probably be saved as individual json files, <UUID>.json
	Those files should have a history of all moves that were made in this game

	Maybe there should also be a copy of the board as it was at the very end?
	This SHOULD be figure-out-able given just the history of moves, but
	people making very primitive AIs shouldn't have to roll their own board
	generator.
*/
<|endoftext|>


<commit_before>package roster

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

	"github.com/netflix/hal-9001/hal"
)

type RosterUser struct {
	Broker    string    `json: broker` // broker name e.g. slack, hipchat
	Username  string    `json: username`
	Timestamp time.Time `json: timestamp`
	Channel   string    `json: channel`
}

const ROSTER_TABLE = `
CREATE TABLE IF NOT EXISTS roster (
	broker   VARCHAR(64) NOT NULL,
	username VARCHAR(64) NOT NULL,
	channel  VARCHAR(255) DEFAULT NULL,
	ts       TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
	PRIMARY KEY (broker, username, channel)
)`

func Register(gb *hal.GenericBroker) {
	// rostertracker gets all messages and keeps a database of when users
	// were last seen to support !last, and the web roster.
	roster := hal.Plugin{
		Name:   "roster_tracker",
		Func:   rostertracker,
		Regex:  "",
		Broker: gb,
	}
	roster.Register()

	// TODO: the reply is still slack-specific, fix that! (maybe?)
	rostercmd := hal.Plugin{
		Name:   "roster_command",
		Func:   rosterlast,
		Regex:  "!last",
		Broker: gb,
	}
	rostercmd.Register()

	hal.SqlInit(ROSTER_TABLE)

	http.HandleFunc("/v1/roster", webroster)
}

// rostertracker is called for every message. It grabs the user and current
// time and throws it into the db for later use.
func rostertracker(msg hal.Evt) {
	db := hal.SqlDB()

	sql := `INSERT INTO roster
						(broker, username, channel, ts)
			VALUES (?,?,?,?)
			ON DUPLICATE KEY
			UPDATE broker=?, username=?, channel=?, ts=?`

	params := []interface{}{
		msg.Broker.Name(), msg.From, msg.Channel, msg.Time,
		msg.Broker.Name(), msg.From, msg.Channel, msg.Time,
	}

	_, err := db.Exec(sql, params...)
	if err != nil {
		log.Printf("roster_tracker write failed: %s", err)
	}
}

// rosterlast is the response to !last that causes the bot to reply via DM
// to the user with a table of when users last posted a message to slack
// rather than relying on status, which is usually useless.
func rosterlast(msg hal.Evt) {
	log.Printf("rosterlast(%q)", msg.Body)

	rus, err := GetRoster()
	if err != nil {
		log.Printf("Error while retreiving roster: %s\n", err)
		return
	}

	log.Printf("rosterlast(%v): ", rus)

	// TODO: ASCII art instead of JSON
	js, err := json.MarshalIndent(rus, "", "    ")
	if err != nil {
		log.Printf("JSON marshaling failed: %s\n", err)
		return
	}

	msg.Replyf("```%s```", string(js))
}

func webroster(w http.ResponseWriter, r *http.Request) {
	rus, err := GetRoster()
	if err != nil {
		http.Error(w, fmt.Sprintf("could not fetch roster: '%s'", err), 500)
		return
	}

	js, err := json.Marshal(rus)
	if err != nil {
		http.Error(w, fmt.Sprintf("could not marshal roster to json: '%s'", err), 500)
		return
	}

	w.Write(js)
}

func GetRoster() ([]*RosterUser, error) {
	db := hal.SqlDB()

	sql := `SELECT broker, username, channel, UNIX_TIMESTAMP(ts) AS ts
	FROM roster ORDER BY ts DESC`
	rows, err := db.Query(sql)
	if err != nil {
		log.Printf("Roster query failed: %s\n", err)
		return nil, err
	}
	defer rows.Close()

	rus := []*RosterUser{}

	for rows.Next() {
		ru := RosterUser{}

		var ts int64
		err = rows.Scan(&ru.Broker, &ru.Username, &ru.Channel, &ts)
		if err != nil {
			log.Printf("Row iteration failed: %s\n", err)
			return nil, err
		}

		ru.Timestamp = time.Unix(ts, 0)

		rus = append(rus, &ru)
	}

	return rus, nil
}
<commit_msg>whitespace & remove dead debug code & comments<commit_after>package roster

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

	"github.com/netflix/hal-9001/hal"
)

type RosterUser struct {
	Broker    string    `json: broker` // broker name e.g. slack, hipchat
	Username  string    `json: username`
	Timestamp time.Time `json: timestamp`
	Channel   string    `json: channel`
}

const ROSTER_TABLE = `
CREATE TABLE IF NOT EXISTS roster (
	broker   VARCHAR(64) NOT NULL,
	username VARCHAR(64) NOT NULL,
	channel  VARCHAR(255) DEFAULT NULL,
	ts       TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
	PRIMARY KEY (broker, username, channel)
)`

func Register(gb *hal.GenericBroker) {
	// rostertracker gets all messages and keeps a database of when users
	// were last seen to support !last, and the web roster.
	roster := hal.Plugin{
		Name:   "roster_tracker",
		Func:   rostertracker,
		Regex:  "",
		Broker: gb,
	}
	roster.Register()

	rostercmd := hal.Plugin{
		Name:   "roster_command",
		Func:   rosterlast,
		Regex:  "!last",
		Broker: gb,
	}
	rostercmd.Register()

	hal.SqlInit(ROSTER_TABLE)

	http.HandleFunc("/v1/roster", webroster)
}

// rostertracker is called for every message. It grabs the user and current
// time and throws it into the db for later use.
func rostertracker(msg hal.Evt) {
	db := hal.SqlDB()

	sql := `INSERT INTO roster
	          (broker, username, channel, ts)
	        VALUES (?,?,?,?)
	        ON DUPLICATE KEY
	        UPDATE broker=?, username=?, channel=?, ts=?`

	params := []interface{}{
		msg.Broker.Name(), msg.From, msg.Channel, msg.Time,
		msg.Broker.Name(), msg.From, msg.Channel, msg.Time,
	}

	_, err := db.Exec(sql, params...)
	if err != nil {
		log.Printf("roster_tracker write failed: %s", err)
	}
}

// rosterlast is the response to !last that causes the bot to reply via DM
// to the user with a table of when users last posted a message to slack
// rather than relying on status, which is usually useless.
func rosterlast(msg hal.Evt) {
	rus, err := GetRoster()
	if err != nil {
		log.Printf("Error while retreiving roster: %s\n", err)
		return
	}

	// TODO: ASCII art instead of JSON
	js, err := json.MarshalIndent(rus, "", "    ")
	if err != nil {
		log.Printf("JSON marshaling failed: %s\n", err)
		return
	}

	msg.Replyf("```%s```", string(js))
}

func webroster(w http.ResponseWriter, r *http.Request) {
	rus, err := GetRoster()
	if err != nil {
		http.Error(w, fmt.Sprintf("could not fetch roster: '%s'", err), 500)
		return
	}

	js, err := json.Marshal(rus)
	if err != nil {
		http.Error(w, fmt.Sprintf("could not marshal roster to json: '%s'", err), 500)
		return
	}

	w.Write(js)
}

func GetRoster() ([]*RosterUser, error) {
	db := hal.SqlDB()

	sql := `SELECT broker, username, channel,
	               UNIX_TIMESTAMP(ts) AS ts
	               FROM roster
	               ORDER BY ts DESC`

	rows, err := db.Query(sql)
	if err != nil {
		log.Printf("Roster query failed: %s\n", err)
		return nil, err
	}
	defer rows.Close()

	rus := []*RosterUser{}

	for rows.Next() {
		ru := RosterUser{}

		var ts int64
		err = rows.Scan(&ru.Broker, &ru.Username, &ru.Channel, &ts)
		if err != nil {
			log.Printf("Row iteration failed: %s\n", err)
			return nil, err
		}

		ru.Timestamp = time.Unix(ts, 0)

		rus = append(rus, &ru)
	}

	return rus, nil
}
<|endoftext|>


<commit_before>package stripe

import "encoding/json"

// IssuingDisputeReason is the list of possible values for status on an issuing dispute.
type IssuingDisputeReason string

// List of values that IssuingDisputeReason can take.
const (
	IssuingDisputeReasonFraudulent IssuingDisputeReason = "fraudulent"
	IssuingDisputeReasonOther      IssuingDisputeReason = "other"
)

// IssuingDisputeStatus is the list of possible values for status on an issuing dispute.
type IssuingDisputeStatus string

// List of values that IssuingDisputeStatus can take.
const (
	IssuingDisputeStatusLost        IssuingDisputeStatus = "lost"
	IssuingDisputeStatusUnderReview IssuingDisputeStatus = "under_review"
	IssuingDisputeStatusUnsubmitted IssuingDisputeStatus = "unsubmitted"
	IssuingDisputeStatusWon         IssuingDisputeStatus = "won"
)

// IssuingDisputeEvidenceFraudulentParams is the subset of parameters that can be sent as evidence for an issuing dispute
// with the reason set as fraudulent.
type IssuingDisputeEvidenceFraudulentParams struct {
	DisputeExplanation *string `form:"dispute_explanation"`
	UncategorizedFile  *string `form:"uncategorized_file"`
}

// IssuingDisputeEvidenceOtherParams is the subset of parameters that can be sent as evidence for an issuing dispute
// with the reason set as other.
type IssuingDisputeEvidenceOtherParams struct {
	DisputeExplanation *string `form:"dispute_explanation"`
	UncategorizedFile  *string `form:"uncategorized_file"`
}

// IssuingDisputeEvidenceParams is the set of parameters that can be sent as evidence for an issuing dispute.
type IssuingDisputeEvidenceParams struct {
	Fraudulent *IssuingDisputeEvidenceFraudulentParams `form:"fraudulent"`
	Other      *IssuingDisputeEvidenceOtherParams      `form:"other"`
}

// IssuingDisputeParams is the set of parameters that can be used when creating or updating an issuing dispute.
type IssuingDisputeParams struct {
	Params              `form:"*"`
	Amount              *int64                        `form:"amount"`
	Evidence            *IssuingDisputeEvidenceParams `form:"evidence"`
	Reason              *string                       `form:"reason"`
	DisputedTransaction *string                       `form:"disputed_transaction"`
}

// IssuingDisputeListParams is the set of parameters that can be used when listing issuing dispute.
type IssuingDisputeListParams struct {
	ListParams          `form:"*"`
	Created             *int64            `form:"created"`
	CreatedRange        *RangeQueryParams `form:"created"`
	DisputedTransaction *string           `form:"disputed_transaction"`
	Transaction         *string           `form:"transaction"`
}

// IssuingDisputeEvidenceFraudulent is the resource representing the evidence hash on an issuing dispute
// with the reason set as fraudulent.
type IssuingDisputeEvidenceFraudulent struct {
	DisputeExplanation string `json:"dispute_explanation"`
	UncategorizedFile  *File  `json:"uncategorized_file"`
}

// IssuingDisputeEvidenceOther is the resource representing the evidence hash on an issuing dispute
// with the reason set as other.
type IssuingDisputeEvidenceOther struct {
	DisputeExplanation string `json:"dispute_explanation"`
	UncategorizedFile  *File  `json:"uncategorized_file"`
}

// IssuingDisputeEvidence is the resource representing evidence on an issuing dispute.
type IssuingDisputeEvidence struct {
	Fraudulent *IssuingDisputeEvidenceFraudulent `json:"fraudulent"`
	Other      *IssuingDisputeEvidenceOther      `json:"other"`
}

// IssuingDispute is the resource representing an issuing dispute.
type IssuingDispute struct {
	Amount      int64                   `json:"amount"`
	Created     int64                   `json:"created"`
	Currency    Currency                `json:"currency"`
	Evidence    *IssuingDisputeEvidence `json:"evidence"`
	ID          string                  `json:"id"`
	Livemode    bool                    `json:"livemode"`
	Metadata    map[string]string       `json:"metadata"`
	Object      string                  `json:"object"`
	Reason      IssuingDisputeReason    `json:"reason"`
	Status      IssuingDisputeStatus    `json:"status"`
	Transaction *IssuingTransaction     `json:"transaction"`
}

// IssuingDisputeList is a list of issuing disputes as retrieved from a list endpoint.
type IssuingDisputeList struct {
	ListMeta
	Data []*IssuingDispute `json:"data"`
}

// UnmarshalJSON handles deserialization of an IssuingDispute.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (i *IssuingDispute) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		i.ID = id
		return nil
	}

	type issuingDispute IssuingDispute
	var v issuingDispute
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*i = IssuingDispute(v)
	return nil
}
<commit_msg>Add new enum values for `IssuingDisputeReason`<commit_after>package stripe

import "encoding/json"

// IssuingDisputeReason is the list of possible values for status on an issuing dispute.
type IssuingDisputeReason string

// List of values that IssuingDisputeReason can take.
const (
	IssuingDisputeReasonDuplicate          IssuingDisputeReason = "duplicate"
	IssuingDisputeReasonFraudulent         IssuingDisputeReason = "fraudulent"
	IssuingDisputeReasonOther              IssuingDisputeReason = "other"
	IssuingDisputeReasonProductNotReceived IssuingDisputeReason = "product_not_received"
)

// IssuingDisputeStatus is the list of possible values for status on an issuing dispute.
type IssuingDisputeStatus string

// List of values that IssuingDisputeStatus can take.
const (
	IssuingDisputeStatusLost        IssuingDisputeStatus = "lost"
	IssuingDisputeStatusUnderReview IssuingDisputeStatus = "under_review"
	IssuingDisputeStatusUnsubmitted IssuingDisputeStatus = "unsubmitted"
	IssuingDisputeStatusWon         IssuingDisputeStatus = "won"
)

// IssuingDisputeEvidenceFraudulentParams is the subset of parameters that can be sent as evidence for an issuing dispute
// with the reason set as fraudulent.
type IssuingDisputeEvidenceFraudulentParams struct {
	DisputeExplanation *string `form:"dispute_explanation"`
	UncategorizedFile  *string `form:"uncategorized_file"`
}

// IssuingDisputeEvidenceOtherParams is the subset of parameters that can be sent as evidence for an issuing dispute
// with the reason set as other.
type IssuingDisputeEvidenceOtherParams struct {
	DisputeExplanation *string `form:"dispute_explanation"`
	UncategorizedFile  *string `form:"uncategorized_file"`
}

// IssuingDisputeEvidenceParams is the set of parameters that can be sent as evidence for an issuing dispute.
type IssuingDisputeEvidenceParams struct {
	Fraudulent *IssuingDisputeEvidenceFraudulentParams `form:"fraudulent"`
	Other      *IssuingDisputeEvidenceOtherParams      `form:"other"`
}

// IssuingDisputeParams is the set of parameters that can be used when creating or updating an issuing dispute.
type IssuingDisputeParams struct {
	Params              `form:"*"`
	Amount              *int64                        `form:"amount"`
	Evidence            *IssuingDisputeEvidenceParams `form:"evidence"`
	Reason              *string                       `form:"reason"`
	DisputedTransaction *string                       `form:"disputed_transaction"`
}

// IssuingDisputeListParams is the set of parameters that can be used when listing issuing dispute.
type IssuingDisputeListParams struct {
	ListParams          `form:"*"`
	Created             *int64            `form:"created"`
	CreatedRange        *RangeQueryParams `form:"created"`
	DisputedTransaction *string           `form:"disputed_transaction"`
	Transaction         *string           `form:"transaction"`
}

// IssuingDisputeEvidenceFraudulent is the resource representing the evidence hash on an issuing dispute
// with the reason set as fraudulent.
type IssuingDisputeEvidenceFraudulent struct {
	DisputeExplanation string `json:"dispute_explanation"`
	UncategorizedFile  *File  `json:"uncategorized_file"`
}

// IssuingDisputeEvidenceOther is the resource representing the evidence hash on an issuing dispute
// with the reason set as other.
type IssuingDisputeEvidenceOther struct {
	DisputeExplanation string `json:"dispute_explanation"`
	UncategorizedFile  *File  `json:"uncategorized_file"`
}

// IssuingDisputeEvidence is the resource representing evidence on an issuing dispute.
type IssuingDisputeEvidence struct {
	Fraudulent *IssuingDisputeEvidenceFraudulent `json:"fraudulent"`
	Other      *IssuingDisputeEvidenceOther      `json:"other"`
}

// IssuingDispute is the resource representing an issuing dispute.
type IssuingDispute struct {
	Amount      int64                   `json:"amount"`
	Created     int64                   `json:"created"`
	Currency    Currency                `json:"currency"`
	Evidence    *IssuingDisputeEvidence `json:"evidence"`
	ID          string                  `json:"id"`
	Livemode    bool                    `json:"livemode"`
	Metadata    map[string]string       `json:"metadata"`
	Object      string                  `json:"object"`
	Reason      IssuingDisputeReason    `json:"reason"`
	Status      IssuingDisputeStatus    `json:"status"`
	Transaction *IssuingTransaction     `json:"transaction"`
}

// IssuingDisputeList is a list of issuing disputes as retrieved from a list endpoint.
type IssuingDisputeList struct {
	ListMeta
	Data []*IssuingDispute `json:"data"`
}

// UnmarshalJSON handles deserialization of an IssuingDispute.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (i *IssuingDispute) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		i.ID = id
		return nil
	}

	type issuingDispute IssuingDispute
	var v issuingDispute
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*i = IssuingDispute(v)
	return nil
}
<|endoftext|>


<commit_before><commit_msg>added queries from Don's cheat sheet<commit_after><|endoftext|>


<commit_before>package s3utils

import (
	"bytes"
	"io"
	"log"
	"path"
	"strings"

	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
)

const (
	// AWS says that parts must be at least 5MB, it's unclear if that means 5 *
	// 10^6 or 5 2^10 so we went with the larger.
	minPart = 5242880 // 5MB
)

// An s3 input looks like: s3://bucket/dir
// Where dir can be a path

// getBucket extracts the bucket from an s3 input
func GetBucket(input string) (string, error) {
	return strings.Split(strings.TrimPrefix(input, "s3://"), "/")[0], nil
}

// getPath extracts the path from an s3 input
func GetPath(input string) (string, error) {
	return path.Join(strings.Split(strings.TrimPrefix(input, "s3://"), "/")[1:]...), nil
}

func NewBucket(uri string) (*s3.Bucket, error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		log.Print(err)
		return nil, err
	}
	client := s3.New(auth, aws.USWest)
	bucket, err := GetBucket(uri)
	if err != nil {
		return nil, err
	}

	return client.Bucket(bucket), nil
}

// PutMulti is like a smart bucket.Put in that it will automatically do a
// multiput if the input reader has enough data that it makes sense to do so.
func PutMulti(bucket *s3.Bucket, path string, r io.Reader, contType string, perm s3.ACL) error {
	// A pointer to a Multi transaction, if this is non nil it means we're
	// putting the data in parts.
	var multi *s3.Multi = nil
	var parts []s3.Part
	for i := 0; ; i++ {
		var data []byte
		n, err := io.ReadAtLeast(r, data, minPart)
		if err != nil && err != io.ErrUnexpectedEOF && err != io.EOF {
			// EOF means that r was empty
			// ErrUnexpectedEOF means that r had less than minPart bytes in it
			// Note that we can hit these errors after having read several 5MB
			// chunks from r already
			return err
		}
		if n >= minPart && multi == nil {
			// We have enough data to do a MultiPut but we haven't started on yet.
			// That means it's time to start one
			multi, err = bucket.Multi(path, contType, perm)
			if err != nil {
				return err
			}
		}
		// Now we upload the actual data
		if multi == nil {
			// We're not doing a multi transaction,
			return bucket.Put(path, data, contType, perm)
		} else {
			part, err := multi.PutPart(i, bytes.NewReader(data))
			if err != nil {
				return err
			}
			parts = append(parts, part)
		}
		if n < minPart {
			// That means this was the last batch of data, time to break out of
			// this loop
			break
		}
	}
	if multi != nil {
		if err := multi.Complete(parts); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Adds a ForEachFile method to s3utils.<commit_after>package s3utils

import (
	"bytes"
	"io"
	"log"
	"path"
	"strings"

	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
)

const (
	// AWS says that parts must be at least 5MB, it's unclear if that means 5 *
	// 10^6 or 5 2^10 so we went with the larger.
	minPart = 5242880 // 5MB
)

// An s3 input looks like: s3://bucket/dir
// Where dir can be a path

// getBucket extracts the bucket from an s3 input
func GetBucket(input string) (string, error) {
	return strings.Split(strings.TrimPrefix(input, "s3://"), "/")[0], nil
}

// getPath extracts the path from an s3 input
func GetPath(input string) (string, error) {
	return path.Join(strings.Split(strings.TrimPrefix(input, "s3://"), "/")[1:]...), nil
}

func NewBucket(uri string) (*s3.Bucket, error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		log.Print(err)
		return nil, err
	}
	client := s3.New(auth, aws.USWest)
	bucket, err := GetBucket(uri)
	if err != nil {
		return nil, err
	}

	return client.Bucket(bucket), nil
}

// PutMulti is like a smart bucket.Put in that it will automatically do a
// multiput if the input reader has enough data that it makes sense to do so.
func PutMulti(bucket *s3.Bucket, path string, r io.Reader, contType string, perm s3.ACL) error {
	// A pointer to a Multi transaction, if this is non nil it means we're
	// putting the data in parts.
	var multi *s3.Multi = nil
	var parts []s3.Part
	for i := 0; ; i++ {
		var data []byte
		n, err := io.ReadAtLeast(r, data, minPart)
		if err != nil && err != io.ErrUnexpectedEOF && err != io.EOF {
			// EOF means that r was empty
			// ErrUnexpectedEOF means that r had less than minPart bytes in it
			// Note that we can hit these errors after having read several 5MB
			// chunks from r already
			return err
		}
		if n >= minPart && multi == nil {
			// We have enough data to do a MultiPut but we haven't started on yet.
			// That means it's time to start one
			multi, err = bucket.Multi(path, contType, perm)
			if err != nil {
				return err
			}
		}
		// Now we upload the actual data
		if multi == nil {
			// We're not doing a multi transaction,
			return bucket.Put(path, data, contType, perm)
		} else {
			part, err := multi.PutPart(i, bytes.NewReader(data))
			if err != nil {
				return err
			}
			parts = append(parts, part)
		}
		if n < minPart {
			// That means this was the last batch of data, time to break out of
			// this loop
			break
		}
	}
	if multi != nil {
		if err := multi.Complete(parts); err != nil {
			return err
		}
	}
	return nil
}

// Files calls `cont` on each file found at `uri`.
func ForEachFile(uri string, cont func(file string) error) error {
	bucket, err := NewBucket(uri)
	if err != nil {
		return err
	}
	inPath, err := GetPath(uri)
	if err != nil {
		return err
	}
	nextMarker := ""
	for {
		lr, err := bucket.List(inPath, "", nextMarker, 0)
		if err != nil {
			return err
		}
		for _, key := range lr.Contents {
			err := cont(key.Key)
			if err != nil {
				return err
			}
		}
		if !lr.IsTruncated {
			// We've exhausted the output
			break
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package types

import (
	"bytes"
	"fmt"

	"github.com/tendermint/go-crypto"
	cmn "github.com/tendermint/tmlibs/common"
)

// Volatile state for each Validator
// NOTE: The Accum is not included in Validator.Hash();
// make sure to update that method if changes are made here
type Validator struct {
	Address     Address       `json:"address"`
	PubKey      crypto.PubKey `json:"pub_key"`
	VotingPower int64         `json:"voting_power"`

	Accum int64 `json:"accum"`
}

func NewValidator(pubKey crypto.PubKey, votingPower int64) *Validator {
	return &Validator{
		Address:     pubKey.Address(),
		PubKey:      pubKey,
		VotingPower: votingPower,
		Accum:       0,
	}
}

// Creates a new copy of the validator so we can mutate accum.
// Panics if the validator is nil.
func (v *Validator) Copy() *Validator {
	vCopy := *v
	return &vCopy
}

// Returns the one with higher Accum.
func (v *Validator) CompareAccum(other *Validator) *Validator {
	if v == nil {
		return other
	}
	if v.Accum > other.Accum {
		return v
	} else if v.Accum < other.Accum {
		return other
	} else {
		if bytes.Compare(v.Address, other.Address) < 0 {
			return v
		} else if bytes.Compare(v.Address, other.Address) > 0 {
			return other
		} else {
			cmn.PanicSanity("Cannot compare identical validators")
			return nil
		}
	}
}

func (v *Validator) String() string {
	if v == nil {
		return "nil-Validator"
	}
	return fmt.Sprintf("Validator{%v %v VP:%v A:%v}",
		v.Address,
		v.PubKey,
		v.VotingPower,
		v.Accum)
}

// Hash computes the unique ID of a validator with a given voting power.
// It excludes the Accum value, which changes with every round.
func (v *Validator) Hash() []byte {
	return tmHash(struct {
		Address     Address
		PubKey      crypto.PubKey
		VotingPower int64
	}{
		v.Address,
		v.PubKey,
		v.VotingPower,
	})
}

//--------------------------------------------------------------------------------
// For testing...

// RandValidator returns a randomized validator, useful for testing.
// UNSTABLE
func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidatorFS) {
	_, tempFilePath := cmn.Tempfile("priv_validator_")
	privVal := GenPrivValidatorFS(tempFilePath)
	votePower := minPower
	if randPower {
		votePower += int64(cmn.RandUint32())
	}
	val := NewValidator(privVal.GetPubKey(), votePower)
	return val, privVal
}
<commit_msg>Remove unnecessary bytes.Compare() call<commit_after>package types

import (
	"bytes"
	"fmt"

	"github.com/tendermint/go-crypto"
	cmn "github.com/tendermint/tmlibs/common"
)

// Volatile state for each Validator
// NOTE: The Accum is not included in Validator.Hash();
// make sure to update that method if changes are made here
type Validator struct {
	Address     Address       `json:"address"`
	PubKey      crypto.PubKey `json:"pub_key"`
	VotingPower int64         `json:"voting_power"`

	Accum int64 `json:"accum"`
}

func NewValidator(pubKey crypto.PubKey, votingPower int64) *Validator {
	return &Validator{
		Address:     pubKey.Address(),
		PubKey:      pubKey,
		VotingPower: votingPower,
		Accum:       0,
	}
}

// Creates a new copy of the validator so we can mutate accum.
// Panics if the validator is nil.
func (v *Validator) Copy() *Validator {
	vCopy := *v
	return &vCopy
}

// Returns the one with higher Accum.
func (v *Validator) CompareAccum(other *Validator) *Validator {
	if v == nil {
		return other
	}
	if v.Accum > other.Accum {
		return v
	} else if v.Accum < other.Accum {
		return other
	} else {
		result := bytes.Compare(v.Address, other.Address)
		if result < 0 {
			return v
		} else if result > 0 {
			return other
		} else {
			cmn.PanicSanity("Cannot compare identical validators")
			return nil
		}
	}
}

func (v *Validator) String() string {
	if v == nil {
		return "nil-Validator"
	}
	return fmt.Sprintf("Validator{%v %v VP:%v A:%v}",
		v.Address,
		v.PubKey,
		v.VotingPower,
		v.Accum)
}

// Hash computes the unique ID of a validator with a given voting power.
// It excludes the Accum value, which changes with every round.
func (v *Validator) Hash() []byte {
	return tmHash(struct {
		Address     Address
		PubKey      crypto.PubKey
		VotingPower int64
	}{
		v.Address,
		v.PubKey,
		v.VotingPower,
	})
}

//--------------------------------------------------------------------------------
// For testing...

// RandValidator returns a randomized validator, useful for testing.
// UNSTABLE
func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidatorFS) {
	_, tempFilePath := cmn.Tempfile("priv_validator_")
	privVal := GenPrivValidatorFS(tempFilePath)
	votePower := minPower
	if randPower {
		votePower += int64(cmn.RandUint32())
	}
	val := NewValidator(privVal.GetPubKey(), votePower)
	return val, privVal
}
<|endoftext|>


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

package zoekt

// FormatVersion is a version number. It is increased every time the
// on-disk index format is changed.
// 5: subrepositories.
// 6: remove size prefix for posting varint list.
// 7: move subrepos into Repository struct.
// 8: move repoMetaData out of indexMetadata
// 9: use bigendian uint64 for trigrams.
// 10: sections for rune offsets.
// 11: file ends in rune offsets.
const IndexFormatVersion = 11

// FeatureVersion is increased if a feature is added that requires reindexing data.
const FeatureVersion = 1

type indexTOC struct {
	fileContents compoundSection
	fileNames    compoundSection
	fileSections compoundSection
	postings     compoundSection
	newlines     compoundSection
	ngramText    simpleSection
	runeOffsets  simpleSection
	fileEndRunes simpleSection

	branchMasks simpleSection
	subRepos    simpleSection

	nameNgramText   simpleSection
	namePostings    compoundSection
	nameRuneOffsets simpleSection
	metaData        simpleSection
	repoMetaData    simpleSection
	nameEndRunes    simpleSection
}

func (t *indexTOC) sections() []section {
	return []section{
		// This must be first, so it can be reliably read across
		// file format versions.
		&t.metaData,
		&t.repoMetaData,
		&t.fileContents,
		&t.fileNames,
		&t.fileSections,
		&t.newlines,
		&t.ngramText,
		&t.postings,
		&t.nameNgramText,
		&t.namePostings,
		&t.branchMasks,
		&t.subRepos,
		&t.runeOffsets,
		&t.nameRuneOffsets,
		&t.fileEndRunes,
		&t.nameEndRunes,
	}
}
<commit_msg>Fix copyright year<commit_after>// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package zoekt

// FormatVersion is a version number. It is increased every time the
// on-disk index format is changed.
// 5: subrepositories.
// 6: remove size prefix for posting varint list.
// 7: move subrepos into Repository struct.
// 8: move repoMetaData out of indexMetadata
// 9: use bigendian uint64 for trigrams.
// 10: sections for rune offsets.
// 11: file ends in rune offsets.
const IndexFormatVersion = 11

// FeatureVersion is increased if a feature is added that requires reindexing data.
const FeatureVersion = 1

type indexTOC struct {
	fileContents compoundSection
	fileNames    compoundSection
	fileSections compoundSection
	postings     compoundSection
	newlines     compoundSection
	ngramText    simpleSection
	runeOffsets  simpleSection
	fileEndRunes simpleSection

	branchMasks simpleSection
	subRepos    simpleSection

	nameNgramText   simpleSection
	namePostings    compoundSection
	nameRuneOffsets simpleSection
	metaData        simpleSection
	repoMetaData    simpleSection
	nameEndRunes    simpleSection
}

func (t *indexTOC) sections() []section {
	return []section{
		// This must be first, so it can be reliably read across
		// file format versions.
		&t.metaData,
		&t.repoMetaData,
		&t.fileContents,
		&t.fileNames,
		&t.fileSections,
		&t.newlines,
		&t.ngramText,
		&t.postings,
		&t.nameNgramText,
		&t.namePostings,
		&t.branchMasks,
		&t.subRepos,
		&t.runeOffsets,
		&t.nameRuneOffsets,
		&t.fileEndRunes,
		&t.nameEndRunes,
	}
}
<|endoftext|>


<commit_before><commit_msg>Fix flaky test for backup<commit_after><|endoftext|>