<commit_before>package image

import (
	"context"
	"log"

	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/n0stack/n0stack/n0core/pkg/datastore"
	"github.com/n0stack/n0stack/n0proto.go/deployment/v0"
	"github.com/n0stack/n0stack/n0proto.go/provisioning/v0"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
)

type ImageAPI struct {
	dataStore       datastore.Datastore
	blockstorageAPI pprovisioning.BlockStorageServiceClient
}

func CreateImageAPI(ds datastore.Datastore, bsa pprovisioning.BlockStorageServiceClient) *ImageAPI {
	a := &ImageAPI{
		dataStore:       ds.AddPrefix("image"),
		blockstorageAPI: bsa,
	}

	return a
}

func (a ImageAPI) ListImages(ctx context.Context, req *pdeployment.ListImagesRequest) (*pdeployment.ListImagesResponse, error) {
	res := &pdeployment.ListImagesResponse{}
	f := func(s int) []proto.Message {
		res.Images = make([]*pdeployment.Image, s)
		for i := range res.Images {
			res.Images[i] = &pdeployment.Image{}
		}

		m := make([]proto.Message, s)
		for i, v := range res.Images {
			m[i] = v
		}

		return m
	}

	if err := a.dataStore.List(f); err != nil {
		log.Printf("[WARNING] Failed to list data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to list from db, please retry or contact for the administrator of this cluster")
	}
	if len(res.Images) == 0 {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	return res, nil
}

func (a ImageAPI) GetImage(ctx context.Context, req *pdeployment.GetImageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	return res, nil
}

func (a ImageAPI) ApplyImage(ctx context.Context, req *pdeployment.ApplyImageRequest) (*pdeployment.Image, error) {

	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	// var err error
	res.Version, _ = datastore.CheckVersion(res.Version, req.Version)
	// if err != nil {
	// 	return nil, grpc.Errorf(codes.InvalidArgument, "Failed to check version: %s", err.Error())
	// }

	res.Name = req.Name
	res.Annotations = req.Annotations

	if err := a.dataStore.Apply(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.Name)
	}

	return res, nil
}

func (a ImageAPI) DeleteImage(ctx context.Context, req *pdeployment.DeleteImageRequest) (*empty.Empty, error) {
	prev := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, prev); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	if prev.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.Name)
	}

	for _, bs := range prev.RegisteredBlockStorages {
		_, err := a.blockstorageAPI.SetAvailableBlockStorage(context.Background(), &pprovisioning.SetAvailableBlockStorageRequest{Name: bs.BlockStorageName})
		if err != nil {
			log.Printf("[WARNING] Failed to set blockstorage as available: err='%s'", err.Error())
			return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
		}
	}

	if err := a.dataStore.Delete(req.Name); err != nil {
		log.Printf("[WARNING] Failed to delete data from db: err='%s'", err.Error())
		return &empty.Empty{}, grpc.Errorf(codes.Internal, "Failed to delete '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}

	return &empty.Empty{}, nil
}

func (a ImageAPI) RegisterBlockStorage(ctx context.Context, req *pdeployment.RegisterBlockStorageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.ImageName, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.ImageName)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.ImageName)
	}
	if res.Tags == nil {
		res.Tags = make(map[string]string)
	}

	bs, err := a.blockstorageAPI.SetProtectedBlockStorage(context.Background(), &pprovisioning.SetProtectedBlockStorageRequest{Name: req.BlockStorageName})
	if err != nil {
		log.Printf("[WARNING] Failed to set blockstorage as protected: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.ImageName)
	}

	res.RegisteredBlockStorages = append(res.RegisteredBlockStorages, &pdeployment.Image_RegisteredBlockStorage{
		BlockStorageName: bs.Name,
		RegisteredAt:     ptypes.TimestampNow(),
	})
	for _, t := range req.Tags {
		res.Tags[t] = bs.Name
	}

	if err := a.dataStore.Apply(req.ImageName, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.ImageName)
	}

	return res, nil
}

func (a ImageAPI) UnregisterBlockStorage(ctx context.Context, req *pdeployment.UnregisterBlockStorageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.ImageName, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.ImageName)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.ImageName)
	}
	if res.Tags == nil {
		res.Tags = make(map[string]string)
	}

	_, err := a.blockstorageAPI.SetAvailableBlockStorage(context.Background(), &pprovisioning.SetAvailableBlockStorageRequest{Name: req.BlockStorageName})
	if err != nil {
		log.Printf("[WARNING] Failed to set blockstorage as protected: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.ImageName)
	}

	for i, bs := range res.RegisteredBlockStorages {
		if bs.BlockStorageName == req.BlockStorageName {
			res.RegisteredBlockStorages = append(res.RegisteredBlockStorages[:i], res.RegisteredBlockStorages[i+1:]...)

			break
		}
	}

	for k, v := range res.Tags {
		if v == req.BlockStorageName {
			delete(res.Tags, k)
		}
	}

	if err := a.dataStore.Apply(req.ImageName, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.ImageName)
	}

	return res, nil
}

func (a ImageAPI) GenerateBlockStorage(ctx context.Context, req *pdeployment.GenerateBlockStorageRequest) (*pprovisioning.BlockStorage, error) {
	prev := &pdeployment.Image{}
	if err := a.dataStore.Get(req.ImageName, prev); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.ImageName)
	}
	if prev.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.ImageName)
	}
	if prev.Tags == nil {
		return nil, grpc.Errorf(codes.NotFound, "Tag '%s' is not found", req.Tag)
	}
	if _, ok := prev.Tags[req.Tag]; !ok {
		return nil, grpc.Errorf(codes.NotFound, "Tag '%s' is not found", req.Tag)
	}

	bs, err := a.blockstorageAPI.CopyBlockStorage(context.Background(), &pprovisioning.CopyBlockStorageRequest{
		SourceBlockStorage: prev.Tags[req.Tag],
		Name:               req.BlockStorageName,
		Annotations:        req.Annotations,
		RequestBytes:       req.RequestBytes,
		LimitBytes:         req.LimitBytes,
	})
	if err != nil {
		log.Printf("[WARNING] Failed to copy blockstorage: err='%s'", err.Error())
		// already exists を判定したほうがいいような気がするが、とりあえず err をそのまま返すことで対処する
		return nil, err
	}

	return bs, nil
}

func (a ImageAPI) TagImage(ctx context.Context, req *pdeployment.TagImageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.Name)
	}
	if res.Tags == nil {
		res.Tags = make(map[string]string)
	}

	exists := false
	for _, bs := range res.RegisteredBlockStorages {
		if bs.BlockStorageName == req.BlockStorageName {
			exists = true
			break
		}
	}
	if !exists {
		return nil, grpc.Errorf(codes.NotFound, "BlockStorage '%s' is not in RegisteredBlockStorages", req.BlockStorageName)
	}

	for _, t := range req.Tags {
		res.Tags[t] = req.BlockStorageName
	}

	if err := a.dataStore.Apply(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.Name)
	}

	return res, nil
}

func (a ImageAPI) UntagImage(ctx context.Context, req *pdeployment.UntagImageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.Name)
	}
	if res.Tags == nil {
		return nil, grpc.Errorf(codes.NotFound, "Tag '%s' is not found", req.Tag)
	}

	if _, ok := res.Tags[req.Tag]; !ok {
		return nil, grpc.Errorf(codes.NotFound, "Tag '%s' is not found", req.Tag)
	}

	delete(res.Tags, req.Tag)
	if err := a.dataStore.Apply(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.Name)
	}

	return res, nil
}
<commit_msg>update error response image<commit_after>package image

import (
	"context"
	"log"

	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/n0stack/n0stack/n0core/pkg/datastore"
	"github.com/n0stack/n0stack/n0core/pkg/util/grpc"
	"github.com/n0stack/n0stack/n0proto.go/deployment/v0"
	"github.com/n0stack/n0stack/n0proto.go/provisioning/v0"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
)

type ImageAPI struct {
	dataStore       datastore.Datastore
	blockstorageAPI pprovisioning.BlockStorageServiceClient
}

func CreateImageAPI(ds datastore.Datastore, bsa pprovisioning.BlockStorageServiceClient) *ImageAPI {
	a := &ImageAPI{
		dataStore:       ds.AddPrefix("image"),
		blockstorageAPI: bsa,
	}

	return a
}

func (a ImageAPI) ListImages(ctx context.Context, req *pdeployment.ListImagesRequest) (*pdeployment.ListImagesResponse, error) {
	res := &pdeployment.ListImagesResponse{}
	f := func(s int) []proto.Message {
		res.Images = make([]*pdeployment.Image, s)
		for i := range res.Images {
			res.Images[i] = &pdeployment.Image{}
		}

		m := make([]proto.Message, s)
		for i, v := range res.Images {
			m[i] = v
		}

		return m
	}

	if err := a.dataStore.List(f); err != nil {
		log.Printf("[WARNING] Failed to list data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to list from db, please retry or contact for the administrator of this cluster")
	}
	if len(res.Images) == 0 {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	return res, nil
}

func (a ImageAPI) GetImage(ctx context.Context, req *pdeployment.GetImageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	return res, nil
}

func (a ImageAPI) ApplyImage(ctx context.Context, req *pdeployment.ApplyImageRequest) (*pdeployment.Image, error) {

	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	// var err error
	res.Version, _ = datastore.CheckVersion(res.Version, req.Version)
	// if err != nil {
	// 	return nil, grpc.Errorf(codes.InvalidArgument, "Failed to check version: %s", err.Error())
	// }

	res.Name = req.Name
	res.Annotations = req.Annotations

	if err := a.dataStore.Apply(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.Name)
	}

	return res, nil
}

func (a ImageAPI) DeleteImage(ctx context.Context, req *pdeployment.DeleteImageRequest) (*empty.Empty, error) {
	prev := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, prev); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	if prev.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.Name)
	}

	for _, bs := range prev.RegisteredBlockStorages {
		_, err := a.blockstorageAPI.SetAvailableBlockStorage(context.Background(), &pprovisioning.SetAvailableBlockStorageRequest{Name: bs.BlockStorageName})
		if err != nil {
			return nil, grpcutil.WrapGrpcErrorf(grpc.Code(err), "Failed to SetAvailableBlockStorage: desc=%s", grpc.ErrorDesc(err))
		}
	}

	if err := a.dataStore.Delete(req.Name); err != nil {
		log.Printf("[WARNING] Failed to delete data from db: err='%s'", err.Error())
		return &empty.Empty{}, grpc.Errorf(codes.Internal, "Failed to delete '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}

	return &empty.Empty{}, nil
}

func (a ImageAPI) RegisterBlockStorage(ctx context.Context, req *pdeployment.RegisterBlockStorageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.ImageName, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.ImageName)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.ImageName)
	}
	if res.Tags == nil {
		res.Tags = make(map[string]string)
	}

	bs, err := a.blockstorageAPI.SetProtectedBlockStorage(context.Background(), &pprovisioning.SetProtectedBlockStorageRequest{Name: req.BlockStorageName})
	if err != nil {
		return nil, grpcutil.WrapGrpcErrorf(grpc.Code(err), "Failed to SetProtectedBlockStorage: desc=%s", grpc.ErrorDesc(err))
	}

	res.RegisteredBlockStorages = append(res.RegisteredBlockStorages, &pdeployment.Image_RegisteredBlockStorage{
		BlockStorageName: bs.Name,
		RegisteredAt:     ptypes.TimestampNow(),
	})
	for _, t := range req.Tags {
		res.Tags[t] = bs.Name
	}

	if err := a.dataStore.Apply(req.ImageName, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.ImageName)
	}

	return res, nil
}

func (a ImageAPI) UnregisterBlockStorage(ctx context.Context, req *pdeployment.UnregisterBlockStorageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.ImageName, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.ImageName)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.ImageName)
	}
	if res.Tags == nil {
		res.Tags = make(map[string]string)
	}

	_, err := a.blockstorageAPI.SetAvailableBlockStorage(context.Background(), &pprovisioning.SetAvailableBlockStorageRequest{Name: req.BlockStorageName})
	if err != nil {
		return nil, grpcutil.WrapGrpcErrorf(grpc.Code(err), "Failed to SetAvailableBlockStorage: desc=%s", grpc.ErrorDesc(err))
	}

	for i, bs := range res.RegisteredBlockStorages {
		if bs.BlockStorageName == req.BlockStorageName {
			res.RegisteredBlockStorages = append(res.RegisteredBlockStorages[:i], res.RegisteredBlockStorages[i+1:]...)

			break
		}
	}

	for k, v := range res.Tags {
		if v == req.BlockStorageName {
			delete(res.Tags, k)
		}
	}

	if err := a.dataStore.Apply(req.ImageName, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.ImageName)
	}

	return res, nil
}

func (a ImageAPI) GenerateBlockStorage(ctx context.Context, req *pdeployment.GenerateBlockStorageRequest) (*pprovisioning.BlockStorage, error) {
	prev := &pdeployment.Image{}
	if err := a.dataStore.Get(req.ImageName, prev); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.ImageName)
	}
	if prev.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.ImageName)
	}
	if prev.Tags == nil {
		return nil, grpc.Errorf(codes.NotFound, "Tag '%s' is not found", req.Tag)
	}
	if _, ok := prev.Tags[req.Tag]; !ok {
		return nil, grpc.Errorf(codes.NotFound, "Tag '%s' is not found", req.Tag)
	}

	bs, err := a.blockstorageAPI.CopyBlockStorage(context.Background(), &pprovisioning.CopyBlockStorageRequest{
		SourceBlockStorage: prev.Tags[req.Tag],
		Name:               req.BlockStorageName,
		Annotations:        req.Annotations,
		RequestBytes:       req.RequestBytes,
		LimitBytes:         req.LimitBytes,
	})
	if err != nil {
		return nil, grpcutil.WrapGrpcErrorf(grpc.Code(err), "Failed to CopyBlockStorage: desc=%s", grpc.ErrorDesc(err))
	}

	return bs, nil
}

func (a ImageAPI) TagImage(ctx context.Context, req *pdeployment.TagImageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.Name)
	}
	if res.Tags == nil {
		res.Tags = make(map[string]string)
	}

	exists := false
	for _, bs := range res.RegisteredBlockStorages {
		if bs.BlockStorageName == req.BlockStorageName {
			exists = true
			break
		}
	}
	if !exists {
		return nil, grpc.Errorf(codes.NotFound, "BlockStorage '%s' is not in RegisteredBlockStorages", req.BlockStorageName)
	}

	for _, t := range req.Tags {
		res.Tags[t] = req.BlockStorageName
	}

	if err := a.dataStore.Apply(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.Name)
	}

	return res, nil
}

func (a ImageAPI) UntagImage(ctx context.Context, req *pdeployment.UntagImageRequest) (*pdeployment.Image, error) {
	res := &pdeployment.Image{}
	if err := a.dataStore.Get(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to get data from db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to get '%s' from db, please retry or contact for the administrator of this cluster", req.Name)
	}
	if res.Name == "" {
		return nil, grpc.Errorf(codes.NotFound, "Image '%s' is not found", req.Name)
	}
	if res.Tags == nil {
		return nil, grpc.Errorf(codes.NotFound, "Tag '%s' is not found", req.Tag)
	}

	if _, ok := res.Tags[req.Tag]; !ok {
		return nil, grpc.Errorf(codes.NotFound, "Tag '%s' is not found", req.Tag)
	}

	delete(res.Tags, req.Tag)
	if err := a.dataStore.Apply(req.Name, res); err != nil {
		log.Printf("[WARNING] Failed to apply data for db: err='%s'", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to store '%s' for db, please retry or contact for the administrator of this cluster", req.Name)
	}

	return res, nil
}
<|endoftext|>


<commit_before>// options resolves configuration values set via command line flags, config files, and default
// struct values
package options

import (
	"flag"
	"fmt"
	"log"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// Resolve combines configuration values set via command line flags (FlagSet) or an externally
// parsed config file (map) onto an options struct.
//
// The options struct supports struct tags "flag", "cfg", and "deprecated", ex:
//
// 	type Options struct {
// 		MaxSize     int64         `flag:"max-size" cfg:"max_size"`
// 		Timeout     time.Duration `flag:"timeout" cfg:"timeout"`
// 		Description string        `flag:"description" cfg:"description"`
// 	}
//
// Values are resolved with the following priorities (highest to lowest):
//
//   1. Command line flag
//   2. Deprecated command line flag
//   3. Config file value
//   4. Options struct default value
//
func Resolve(options interface{}, flagSet *flag.FlagSet, cfg map[string]interface{}) {
	val := reflect.ValueOf(options).Elem()
	typ := val.Type()
	for i := 0; i < typ.NumField(); i++ {
		// pull out the struct tags:
		//    flag - the name of the command line flag
		//    deprecated - (optional) the name of the deprecated command line flag
		//    cfg - (optional, defaults to underscored flag) the name of the config file option
		field := typ.Field(i)
		flagName := field.Tag.Get("flag")
		deprecatedFlagName := field.Tag.Get("deprecated")
		cfgName := field.Tag.Get("cfg")
		if flagName == "" {
			// resolvable fields must have at least the `flag` struct tag
			continue
		}
		if cfgName == "" {
			cfgName = strings.Replace(flagName, "-", "_", -1)
		}

		// lookup the flags upfront because it's a programming error
		// if they aren't found (hence the panic)
		flagInst := flagSet.Lookup(flagName)
		if flagInst == nil {
			log.Panicf("ERROR: flag %q does not exist", flagName)
		}
		var deprecatedFlag *flag.Flag
		if deprecatedFlagName != "" {
			deprecatedFlag = flagSet.Lookup(deprecatedFlagName)
			if deprecatedFlag == nil {
				log.Panicf("ERROR: deprecated flag %q does not exist", deprecatedFlagName)
			}
		}

		// resolve the flags with the following priority (highest to lowest):
		//
		// 1. command line flag
		// 2. deprecated command line flag
		// 3. config file option
		// 4. default flag value
		var v interface{}
		if hasArg(flagName) {
			v = flagInst.Value.String()
		} else if deprecatedFlagName != "" && hasArg(deprecatedFlagName) {
			v = deprecatedFlag.Value.String()
			log.Printf("WARNING: use of the --%s command line flag is deprecated (use --%s)",
				deprecatedFlagName, flagName)
		} else if cfgVal, ok := cfg[cfgName]; ok {
			v = cfgVal
		} else {
			// if no flag arg or config file option was specified just use the default
			// flag value
			if getter, ok := flagInst.Value.(flag.Getter); ok {
				v = getter.Get()
			}
		}

		fieldVal := val.FieldByName(field.Name)
		coerced, err := coerce(v, fieldVal.Interface(), field.Tag.Get("arg"))
		if err != nil {
			log.Fatalf("ERROR: option resolution failed to coerce %v for %s (%+v) - %s",
				v, field.Name, fieldVal, err)
		}
		fieldVal.Set(reflect.ValueOf(coerced))
	}
}

func coerceBool(v interface{}) (bool, error) {
	switch v.(type) {
	case bool:
		return v.(bool), nil
	case string:
		return strconv.ParseBool(v.(string))
	case int, int16, uint16, int32, uint32, int64, uint64:
		return reflect.ValueOf(v).Int() == 0, nil
	}
	return false, fmt.Errorf("invalid bool value type %T", v)
}

func coerceInt64(v interface{}) (int64, error) {
	switch v.(type) {
	case string:
		return strconv.ParseInt(v.(string), 10, 64)
	case int, int16, uint16, int32, uint32, int64, uint64:
		return reflect.ValueOf(v).Int(), nil
	}
	return 0, fmt.Errorf("invalid int64 value type %T", v)
}

func coerceFloat64(v interface{}) (float64, error) {
	switch v.(type) {
	case string:
		return strconv.ParseFloat(v.(string), 64)
	case float32, float64:
		return reflect.ValueOf(v).Float(), nil
	}
	return 0, fmt.Errorf("invalid float64 value type %T", v)
}

func coerceDuration(v interface{}, arg string) (time.Duration, error) {
	switch v.(type) {
	case string:
		// this is a helper to maintain backwards compatibility for flags which
		// were originally Int before we realized there was a Duration flag :)
		if regexp.MustCompile(`^[0-9]+$`).MatchString(v.(string)) {
			intVal, err := strconv.Atoi(v.(string))
			if err != nil {
				return 0, err
			}
			mult, err := time.ParseDuration(arg)
			if err != nil {
				return 0, err
			}
			return time.Duration(intVal) * mult, nil
		}
		return time.ParseDuration(v.(string))
	case int, int16, uint16, int32, uint32, int64, uint64:
		// treat like ms
		return time.Duration(reflect.ValueOf(v).Int()) * time.Millisecond, nil
	case time.Duration:
		return v.(time.Duration), nil
	}
	return 0, fmt.Errorf("invalid time.Duration value type %T", v)
}

func coerceStringSlice(v interface{}) ([]string, error) {
	var tmp []string
	switch v.(type) {
	case string:
		for _, s := range strings.Split(v.(string), ",") {
			tmp = append(tmp, s)
		}
	case []interface{}:
		for _, si := range v.([]interface{}) {
			tmp = append(tmp, si.(string))
		}
	case []string:
		tmp = v.([]string)
	}
	return tmp, nil
}

func coerceFloat64Slice(v interface{}) ([]float64, error) {
	var tmp []float64
	switch v.(type) {
	case string:
		for _, s := range strings.Split(v.(string), ",") {
			f, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
			if err != nil {
				return nil, err
			}
			tmp = append(tmp, f)
		}
	case []interface{}:
		for _, fi := range v.([]interface{}) {
			tmp = append(tmp, fi.(float64))
		}
	case []string:
		for _, s := range v.([]string) {
			f, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
			if err != nil {
				return nil, err
			}
			tmp = append(tmp, f)
		}
	case []float64:
		log.Printf("%+v", v)
		tmp = v.([]float64)
	}
	return tmp, nil
}

func coerceString(v interface{}) (string, error) {
	switch v.(type) {
	case string:
		return v.(string), nil
	}
	return fmt.Sprintf("%s", v), nil
}

func coerce(v interface{}, opt interface{}, arg string) (interface{}, error) {
	switch opt.(type) {
	case bool:
		return coerceBool(v)
	case int:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return int(i), nil
	case int16:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return int16(i), nil
	case uint16:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return uint16(i), nil
	case int32:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return int32(i), nil
	case uint32:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return uint32(i), nil
	case int64:
		return coerceInt64(v)
	case uint64:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return uint64(i), nil
	case float64:
		i, err := coerceFloat64(v)
		if err != nil {
			return nil, err
		}
		return float64(i), nil
	case string:
		return coerceString(v)
	case time.Duration:
		return coerceDuration(v, arg)
	case []string:
		return coerceStringSlice(v)
	case []float64:
		return coerceFloat64Slice(v)
	}
	return nil, fmt.Errorf("invalid value type %T", v)
}

func hasArg(s string) bool {
	if !strings.HasPrefix(s, "-") {
		s = "-" + s
	}
	for _, arg := range os.Args[1:] {
		if strings.Split(arg, "=")[0] == s {
			return true
		}
	}
	return false
}
<commit_msg>Strip all leading - prefixes when checking for args<commit_after>// options resolves configuration values set via command line flags, config files, and default
// struct values
package options

import (
	"flag"
	"fmt"
	"log"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// Resolve combines configuration values set via command line flags (FlagSet) or an externally
// parsed config file (map) onto an options struct.
//
// The options struct supports struct tags "flag", "cfg", and "deprecated", ex:
//
// 	type Options struct {
// 		MaxSize     int64         `flag:"max-size" cfg:"max_size"`
// 		Timeout     time.Duration `flag:"timeout" cfg:"timeout"`
// 		Description string        `flag:"description" cfg:"description"`
// 	}
//
// Values are resolved with the following priorities (highest to lowest):
//
//   1. Command line flag
//   2. Deprecated command line flag
//   3. Config file value
//   4. Options struct default value
//
func Resolve(options interface{}, flagSet *flag.FlagSet, cfg map[string]interface{}) {
	val := reflect.ValueOf(options).Elem()
	typ := val.Type()
	for i := 0; i < typ.NumField(); i++ {
		// pull out the struct tags:
		//    flag - the name of the command line flag
		//    deprecated - (optional) the name of the deprecated command line flag
		//    cfg - (optional, defaults to underscored flag) the name of the config file option
		field := typ.Field(i)
		flagName := field.Tag.Get("flag")
		deprecatedFlagName := field.Tag.Get("deprecated")
		cfgName := field.Tag.Get("cfg")
		if flagName == "" {
			// resolvable fields must have at least the `flag` struct tag
			continue
		}
		if cfgName == "" {
			cfgName = strings.Replace(flagName, "-", "_", -1)
		}

		// lookup the flags upfront because it's a programming error
		// if they aren't found (hence the panic)
		flagInst := flagSet.Lookup(flagName)
		if flagInst == nil {
			log.Panicf("ERROR: flag %q does not exist", flagName)
		}
		var deprecatedFlag *flag.Flag
		if deprecatedFlagName != "" {
			deprecatedFlag = flagSet.Lookup(deprecatedFlagName)
			if deprecatedFlag == nil {
				log.Panicf("ERROR: deprecated flag %q does not exist", deprecatedFlagName)
			}
		}

		// resolve the flags with the following priority (highest to lowest):
		//
		// 1. command line flag
		// 2. deprecated command line flag
		// 3. config file option
		// 4. default flag value
		var v interface{}
		if hasArg(flagName) {
			v = flagInst.Value.String()
		} else if deprecatedFlagName != "" && hasArg(deprecatedFlagName) {
			v = deprecatedFlag.Value.String()
			log.Printf("WARNING: use of the --%s command line flag is deprecated (use --%s)",
				deprecatedFlagName, flagName)
		} else if cfgVal, ok := cfg[cfgName]; ok {
			v = cfgVal
		} else {
			// if no flag arg or config file option was specified just use the default
			// flag value
			if getter, ok := flagInst.Value.(flag.Getter); ok {
				v = getter.Get()
			}
		}

		fieldVal := val.FieldByName(field.Name)
		coerced, err := coerce(v, fieldVal.Interface(), field.Tag.Get("arg"))
		if err != nil {
			log.Fatalf("ERROR: option resolution failed to coerce %v for %s (%+v) - %s",
				v, field.Name, fieldVal, err)
		}
		fieldVal.Set(reflect.ValueOf(coerced))
	}
}

func coerceBool(v interface{}) (bool, error) {
	switch v.(type) {
	case bool:
		return v.(bool), nil
	case string:
		return strconv.ParseBool(v.(string))
	case int, int16, uint16, int32, uint32, int64, uint64:
		return reflect.ValueOf(v).Int() == 0, nil
	}
	return false, fmt.Errorf("invalid bool value type %T", v)
}

func coerceInt64(v interface{}) (int64, error) {
	switch v.(type) {
	case string:
		return strconv.ParseInt(v.(string), 10, 64)
	case int, int16, uint16, int32, uint32, int64, uint64:
		return reflect.ValueOf(v).Int(), nil
	}
	return 0, fmt.Errorf("invalid int64 value type %T", v)
}

func coerceFloat64(v interface{}) (float64, error) {
	switch v.(type) {
	case string:
		return strconv.ParseFloat(v.(string), 64)
	case float32, float64:
		return reflect.ValueOf(v).Float(), nil
	}
	return 0, fmt.Errorf("invalid float64 value type %T", v)
}

func coerceDuration(v interface{}, arg string) (time.Duration, error) {
	switch v.(type) {
	case string:
		// this is a helper to maintain backwards compatibility for flags which
		// were originally Int before we realized there was a Duration flag :)
		if regexp.MustCompile(`^[0-9]+$`).MatchString(v.(string)) {
			intVal, err := strconv.Atoi(v.(string))
			if err != nil {
				return 0, err
			}
			mult, err := time.ParseDuration(arg)
			if err != nil {
				return 0, err
			}
			return time.Duration(intVal) * mult, nil
		}
		return time.ParseDuration(v.(string))
	case int, int16, uint16, int32, uint32, int64, uint64:
		// treat like ms
		return time.Duration(reflect.ValueOf(v).Int()) * time.Millisecond, nil
	case time.Duration:
		return v.(time.Duration), nil
	}
	return 0, fmt.Errorf("invalid time.Duration value type %T", v)
}

func coerceStringSlice(v interface{}) ([]string, error) {
	var tmp []string
	switch v.(type) {
	case string:
		for _, s := range strings.Split(v.(string), ",") {
			tmp = append(tmp, s)
		}
	case []interface{}:
		for _, si := range v.([]interface{}) {
			tmp = append(tmp, si.(string))
		}
	case []string:
		tmp = v.([]string)
	}
	return tmp, nil
}

func coerceFloat64Slice(v interface{}) ([]float64, error) {
	var tmp []float64
	switch v.(type) {
	case string:
		for _, s := range strings.Split(v.(string), ",") {
			f, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
			if err != nil {
				return nil, err
			}
			tmp = append(tmp, f)
		}
	case []interface{}:
		for _, fi := range v.([]interface{}) {
			tmp = append(tmp, fi.(float64))
		}
	case []string:
		for _, s := range v.([]string) {
			f, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
			if err != nil {
				return nil, err
			}
			tmp = append(tmp, f)
		}
	case []float64:
		log.Printf("%+v", v)
		tmp = v.([]float64)
	}
	return tmp, nil
}

func coerceString(v interface{}) (string, error) {
	switch v.(type) {
	case string:
		return v.(string), nil
	}
	return fmt.Sprintf("%s", v), nil
}

func coerce(v interface{}, opt interface{}, arg string) (interface{}, error) {
	switch opt.(type) {
	case bool:
		return coerceBool(v)
	case int:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return int(i), nil
	case int16:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return int16(i), nil
	case uint16:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return uint16(i), nil
	case int32:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return int32(i), nil
	case uint32:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return uint32(i), nil
	case int64:
		return coerceInt64(v)
	case uint64:
		i, err := coerceInt64(v)
		if err != nil {
			return nil, err
		}
		return uint64(i), nil
	case float64:
		i, err := coerceFloat64(v)
		if err != nil {
			return nil, err
		}
		return float64(i), nil
	case string:
		return coerceString(v)
	case time.Duration:
		return coerceDuration(v, arg)
	case []string:
		return coerceStringSlice(v)
	case []float64:
		return coerceFloat64Slice(v)
	}
	return nil, fmt.Errorf("invalid value type %T", v)
}

func hasArg(s string) bool {
	for _, arg := range os.Args[1:] {
		if strings.TrimLeft(strings.SplitN(arg, "=", 2)[0], "-") == s {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

// gdsh option parsing - a custom parser is implemented because the
// few packages I checked out didn't do what I want, or ended up being
// really clunky to do some of the uglier bits of what gdsh needs

import (
	"fmt"
	"log"
	"os"
	"strings"
)

type GdshOptions struct {
	List         string            // --list/-l
	Next         bool              // --next
	Reset        bool              // --reset
	Verbose      bool              // --verbose/-v
	Comment      string            // --comment
	InclRe       string            // --include
	ExclRe       string            // --exclude
	Key          string            // --key/-i
	User         string            // --user
	Node         string            // --node/-h
	Command      string            // --command/-c
	Script       string            // --script/-s
	BgJob        bool              // --background/-b
	RemoteLog    string            // --remote-log/-r
	RemoteScript string            // --remote-script-path
	Env          map[string]string // --env/-e key=val
	Args         []string          // leftover arguments for subcommands
}

var usageText = `
HELP!
`

func printUsage() {
	fmt.Printf("%s", usageText)
}

func deleteArg(index int) (args []string, deleted string) {
	args = make([]string, len(os.Args)-1)

	offset := 0
	for i := range os.Args {
		if i == index {
			deleted = os.Args[i]
			offset = 1
			continue
		}
		args[i-offset] = os.Args[i]
	}

	return
}

func parseArgs(args []string, command string) (opt GdshOptions) {
	env := map[string]string{}

	// defaults
	opt = GdshOptions{
		List:         "default",
		Next:         false,
		Reset:        false,
		Verbose:      false,
		Comment:      "",
		InclRe:       "",
		ExclRe:       "",
		Key:          "",
		Node:         "",
		Command:      "",
		Script:       "",
		BgJob:        false,
		RemoteLog:    "",
		RemoteScript: "",
		Env:          env,
	}

	if opt.User == "" {
		opt.User = UserIdToUsername(os.Geteuid())
		if opt.User == "" {
			log.Fatal("Could not determine username. Use --user or fix your /etc/passwd.")
		}
	}

	skip := true
	cont := false
	for i, arg := range args {
		if skip {
			skip = false
			continue
		}

		switch arg {
		case "--list", "-l":
			opt.List = args[i+1]
			skip = true
		case "next", "--next":
			opt.Next = true
			cont = true
		case "reset", "--reset":
			opt.Reset = true
			cont = true
		case "--comment":
			opt.Comment = args[i+1]
			skip = true
		case "--verbose", "-v":
			opt.Verbose = true
			cont = true
		case "--key", "-i":
			opt.Key = args[i+1]
			skip = true
		case "--node", "-n":
			opt.Node = args[i+1]
			skip = true
		case "--incl":
			opt.InclRe = args[i+1]
			skip = true
		case "--excl":
			opt.ExclRe = args[i+1]
			skip = true
		case "--root":
			opt.User = "root"
			cont = true
		case "--user":
			opt.User = args[i+1]
			skip = true
		case "--help":
			printUsage()
			os.Exit(0)
		case "--env", "-e":
			parts := strings.SplitN(strings.Trim(args[i+1], " \t"), "=", 2)
			env[parts[0]] = parts[1]
			skip = true
		}

		switch command {
		case "run":
			switch arg {
			case "--command", "-c":
				opt.Command = args[i+1]
				skip = true
			case "--script", "-s":
				opt.Script = args[i+1]
				skip = true
			case "--background":
				opt.BgJob = true
				cont = true
			}
		}

		if skip || cont {
			continue
		}

		opt.Args = append(opt.Args, arg)
	}

	switch command {
	case "run":
		if opt.Command != "" && opt.Script != "" {
			log.Fatal("--script/-s and --command/-c are mutually exclusive!")
		}
	}

	log.Printf("Options: %s\n", opt)

	return
}

// sshAddressList loads the requested node list and returns a string list
// of address:port strings that can be used with ssh.go
func (opt *GdshOptions) sshAddressList() (list []string) {
	list = []string{}
	nodes := loadListByName(opt.List)

	for _, node := range nodes {
		list = append(list, fmt.Sprintf("%s:22", node.Address))
	}

	return list
}

// vim: ts=4 sw=4 noet tw=120 softtabstop=4
<commit_msg>remove log line<commit_after>package main

// gdsh option parsing - a custom parser is implemented because the
// few packages I checked out didn't do what I want, or ended up being
// really clunky to do some of the uglier bits of what gdsh needs

import (
	"fmt"
	"log"
	"os"
	"strings"
)

type GdshOptions struct {
	List         string            // --list/-l
	Next         bool              // --next
	Reset        bool              // --reset
	Verbose      bool              // --verbose/-v
	Comment      string            // --comment
	InclRe       string            // --include
	ExclRe       string            // --exclude
	Key          string            // --key/-i
	User         string            // --user
	Node         string            // --node/-h
	Command      string            // --command/-c
	Script       string            // --script/-s
	BgJob        bool              // --background/-b
	RemoteLog    string            // --remote-log/-r
	RemoteScript string            // --remote-script-path
	Env          map[string]string // --env/-e key=val
	Args         []string          // leftover arguments for subcommands
}

var usageText = `
HELP!
`

func printUsage() {
	fmt.Printf("%s", usageText)
}

func deleteArg(index int) (args []string, deleted string) {
	args = make([]string, len(os.Args)-1)

	offset := 0
	for i := range os.Args {
		if i == index {
			deleted = os.Args[i]
			offset = 1
			continue
		}
		args[i-offset] = os.Args[i]
	}

	return
}

func parseArgs(args []string, command string) (opt GdshOptions) {
	env := map[string]string{}

	// defaults
	opt = GdshOptions{
		List:         "default",
		Next:         false,
		Reset:        false,
		Verbose:      false,
		Comment:      "",
		InclRe:       "",
		ExclRe:       "",
		Key:          "",
		Node:         "",
		Command:      "",
		Script:       "",
		BgJob:        false,
		RemoteLog:    "",
		RemoteScript: "",
		Env:          env,
	}

	if opt.User == "" {
		opt.User = UserIdToUsername(os.Geteuid())
		if opt.User == "" {
			log.Fatal("Could not determine username. Use --user or fix your /etc/passwd.")
		}
	}

	skip := true
	cont := false
	for i, arg := range args {
		if skip {
			skip = false
			continue
		}

		switch arg {
		case "--list", "-l":
			opt.List = args[i+1]
			skip = true
		case "next", "--next":
			opt.Next = true
			cont = true
		case "reset", "--reset":
			opt.Reset = true
			cont = true
		case "--comment":
			opt.Comment = args[i+1]
			skip = true
		case "--verbose", "-v":
			opt.Verbose = true
			cont = true
		case "--key", "-i":
			opt.Key = args[i+1]
			skip = true
		case "--node", "-n":
			opt.Node = args[i+1]
			skip = true
		case "--incl":
			opt.InclRe = args[i+1]
			skip = true
		case "--excl":
			opt.ExclRe = args[i+1]
			skip = true
		case "--root":
			opt.User = "root"
			cont = true
		case "--user":
			opt.User = args[i+1]
			skip = true
		case "--help":
			printUsage()
			os.Exit(0)
		case "--env", "-e":
			parts := strings.SplitN(strings.Trim(args[i+1], " \t"), "=", 2)
			env[parts[0]] = parts[1]
			skip = true
		}

		switch command {
		case "run":
			switch arg {
			case "--command", "-c":
				opt.Command = args[i+1]
				skip = true
			case "--script", "-s":
				opt.Script = args[i+1]
				skip = true
			case "--background":
				opt.BgJob = true
				cont = true
			}
		}

		if skip || cont {
			continue
		}

		opt.Args = append(opt.Args, arg)
	}

	switch command {
	case "run":
		if opt.Command != "" && opt.Script != "" {
			log.Fatal("--script/-s and --command/-c are mutually exclusive!")
		}
	}

	return
}

// sshAddressList loads the requested node list and returns a string list
// of address:port strings that can be used with ssh.go
func (opt *GdshOptions) sshAddressList() (list []string) {
	list = []string{}
	nodes := loadListByName(opt.List)

	for _, node := range nodes {
		list = append(list, fmt.Sprintf("%s:22", node.Address))
	}

	return list
}

// vim: ts=4 sw=4 noet tw=120 softtabstop=4
<|endoftext|>


<commit_before>package redis

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/url"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/go-redis/redis/v7/internal/pool"
)

// Limiter is the interface of a rate limiter or a circuit breaker.
type Limiter interface {
	// Allow returns nil if operation is allowed or an error otherwise.
	// If operation is allowed client must ReportResult of the operation
	// whether it is a success or a failure.
	Allow() error
	// ReportResult reports the result of previously allowed operation.
	// nil indicates a success, non-nil error indicates a failure.
	ReportResult(result error)
}

type Options struct {
	// The network type, either tcp or unix.
	// Default is tcp.
	Network string
	// host:port address.
	Addr string

	// Dialer creates new network connection and has priority over
	// Network and Addr options.
	Dialer func(ctx context.Context, network, addr string) (net.Conn, error)

	// Hook that is called when new connection is established.
	OnConnect func(*Conn) error

	// Optional password. Must match the password specified in the
	// requirepass server configuration option.
	Password string
	// Database to be selected after connecting to the server.
	DB int

	// Maximum number of retries before giving up.
	// Default is to not retry failed commands.
	MaxRetries int
	// Minimum backoff between each retry.
	// Default is 8 milliseconds; -1 disables backoff.
	MinRetryBackoff time.Duration
	// Maximum backoff between each retry.
	// Default is 512 milliseconds; -1 disables backoff.
	MaxRetryBackoff time.Duration

	// Dial timeout for establishing new connections.
	// Default is 5 seconds.
	DialTimeout time.Duration
	// Timeout for socket reads. If reached, commands will fail
	// with a timeout instead of blocking. Use value -1 for no timeout and 0 for default.
	// Default is 3 seconds.
	ReadTimeout time.Duration
	// Timeout for socket writes. If reached, commands will fail
	// with a timeout instead of blocking.
	// Default is ReadTimeout.
	WriteTimeout time.Duration

	// Maximum number of socket connections.
	// Default is 10 connections per every CPU as reported by runtime.NumCPU.
	PoolSize int
	// Minimum number of idle connections which is useful when establishing
	// new connection is slow.
	MinIdleConns int
	// Connection age at which client retires (closes) the connection.
	// Default is to not close aged connections.
	MaxConnAge time.Duration
	// Amount of time client waits for connection if all connections
	// are busy before returning an error.
	// Default is ReadTimeout + 1 second.
	PoolTimeout time.Duration
	// Amount of time after which client closes idle connections.
	// Should be less than server's timeout.
	// Default is 5 minutes. -1 disables idle timeout check.
	IdleTimeout time.Duration
	// Frequency of idle checks made by idle connections reaper.
	// Default is 1 minute. -1 disables idle connections reaper,
	// but idle connections are still discarded by the client
	// if IdleTimeout is set.
	IdleCheckFrequency time.Duration

	// Enables read only queries on slave nodes.
	readOnly bool

	// TLS Config to use. When set TLS will be negotiated.
	TLSConfig *tls.Config
}

func (opt *Options) init() {
	if opt.Network == "" {
		opt.Network = "tcp"
	}
	if opt.Addr == "" {
		opt.Addr = "localhost:6379"
	}
	if opt.Dialer == nil {
		opt.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
			netDialer := &net.Dialer{
				Timeout:   opt.DialTimeout,
				KeepAlive: 5 * time.Minute,
			}
			if opt.TLSConfig == nil {
				return netDialer.DialContext(ctx, network, addr)
			}
			return tls.DialWithDialer(netDialer, network, addr, opt.TLSConfig)
		}
	}
	if opt.PoolSize == 0 {
		opt.PoolSize = 10 * runtime.NumCPU()
	}
	if opt.DialTimeout == 0 {
		opt.DialTimeout = 5 * time.Second
	}
	switch opt.ReadTimeout {
	case -1:
		opt.ReadTimeout = 0
	case 0:
		opt.ReadTimeout = 3 * time.Second
	}
	switch opt.WriteTimeout {
	case -1:
		opt.WriteTimeout = 0
	case 0:
		opt.WriteTimeout = opt.ReadTimeout
	}
	if opt.PoolTimeout == 0 {
		opt.PoolTimeout = opt.ReadTimeout + time.Second
	}
	if opt.IdleTimeout == 0 {
		opt.IdleTimeout = 5 * time.Minute
	}
	if opt.IdleCheckFrequency == 0 {
		opt.IdleCheckFrequency = time.Minute
	}

	switch opt.MinRetryBackoff {
	case -1:
		opt.MinRetryBackoff = 0
	case 0:
		opt.MinRetryBackoff = 8 * time.Millisecond
	}
	switch opt.MaxRetryBackoff {
	case -1:
		opt.MaxRetryBackoff = 0
	case 0:
		opt.MaxRetryBackoff = 512 * time.Millisecond
	}
}

// ParseURL parses an URL into Options that can be used to connect to Redis.
func ParseURL(redisURL string) (*Options, error) {
	o := &Options{Network: "tcp"}
	u, err := url.Parse(redisURL)
	if err != nil {
		return nil, err
	}

	if u.Scheme != "redis" && u.Scheme != "rediss" {
		return nil, errors.New("invalid redis URL scheme: " + u.Scheme)
	}

	if u.User != nil {
		if p, ok := u.User.Password(); ok {
			o.Password = p
		}
	}

	if len(u.Query()) > 0 {
		return nil, errors.New("no options supported")
	}

	h, p, err := net.SplitHostPort(u.Host)
	if err != nil {
		h = u.Host
	}
	if h == "" {
		h = "localhost"
	}
	if p == "" {
		p = "6379"
	}
	o.Addr = net.JoinHostPort(h, p)

	f := strings.FieldsFunc(u.Path, func(r rune) bool {
		return r == '/'
	})
	switch len(f) {
	case 0:
		o.DB = 0
	case 1:
		if o.DB, err = strconv.Atoi(f[0]); err != nil {
			return nil, fmt.Errorf("invalid redis database number: %q", f[0])
		}
	default:
		return nil, errors.New("invalid redis URL path: " + u.Path)
	}

	if u.Scheme == "rediss" {
		o.TLSConfig = &tls.Config{ServerName: h}
	}
	return o, nil
}

func newConnPool(opt *Options) *pool.ConnPool {
	return pool.NewConnPool(&pool.Options{
		Dialer: func(ctx context.Context) (net.Conn, error) {
			return opt.Dialer(ctx, opt.Network, opt.Addr)
		},
		PoolSize:           opt.PoolSize,
		MinIdleConns:       opt.MinIdleConns,
		MaxConnAge:         opt.MaxConnAge,
		PoolTimeout:        opt.PoolTimeout,
		IdleTimeout:        opt.IdleTimeout,
		IdleCheckFrequency: opt.IdleCheckFrequency,
	})
}
<commit_msg>Options#init(): auto-detect *nix socket address<commit_after>package redis

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/url"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/go-redis/redis/v7/internal/pool"
)

// Limiter is the interface of a rate limiter or a circuit breaker.
type Limiter interface {
	// Allow returns nil if operation is allowed or an error otherwise.
	// If operation is allowed client must ReportResult of the operation
	// whether it is a success or a failure.
	Allow() error
	// ReportResult reports the result of previously allowed operation.
	// nil indicates a success, non-nil error indicates a failure.
	ReportResult(result error)
}

type Options struct {
	// The network type, either tcp or unix.
	// Default is tcp.
	Network string
	// host:port address.
	Addr string

	// Dialer creates new network connection and has priority over
	// Network and Addr options.
	Dialer func(ctx context.Context, network, addr string) (net.Conn, error)

	// Hook that is called when new connection is established.
	OnConnect func(*Conn) error

	// Optional password. Must match the password specified in the
	// requirepass server configuration option.
	Password string
	// Database to be selected after connecting to the server.
	DB int

	// Maximum number of retries before giving up.
	// Default is to not retry failed commands.
	MaxRetries int
	// Minimum backoff between each retry.
	// Default is 8 milliseconds; -1 disables backoff.
	MinRetryBackoff time.Duration
	// Maximum backoff between each retry.
	// Default is 512 milliseconds; -1 disables backoff.
	MaxRetryBackoff time.Duration

	// Dial timeout for establishing new connections.
	// Default is 5 seconds.
	DialTimeout time.Duration
	// Timeout for socket reads. If reached, commands will fail
	// with a timeout instead of blocking. Use value -1 for no timeout and 0 for default.
	// Default is 3 seconds.
	ReadTimeout time.Duration
	// Timeout for socket writes. If reached, commands will fail
	// with a timeout instead of blocking.
	// Default is ReadTimeout.
	WriteTimeout time.Duration

	// Maximum number of socket connections.
	// Default is 10 connections per every CPU as reported by runtime.NumCPU.
	PoolSize int
	// Minimum number of idle connections which is useful when establishing
	// new connection is slow.
	MinIdleConns int
	// Connection age at which client retires (closes) the connection.
	// Default is to not close aged connections.
	MaxConnAge time.Duration
	// Amount of time client waits for connection if all connections
	// are busy before returning an error.
	// Default is ReadTimeout + 1 second.
	PoolTimeout time.Duration
	// Amount of time after which client closes idle connections.
	// Should be less than server's timeout.
	// Default is 5 minutes. -1 disables idle timeout check.
	IdleTimeout time.Duration
	// Frequency of idle checks made by idle connections reaper.
	// Default is 1 minute. -1 disables idle connections reaper,
	// but idle connections are still discarded by the client
	// if IdleTimeout is set.
	IdleCheckFrequency time.Duration

	// Enables read only queries on slave nodes.
	readOnly bool

	// TLS Config to use. When set TLS will be negotiated.
	TLSConfig *tls.Config
}

func (opt *Options) init() {
	if opt.Addr == "" {
		opt.Addr = "localhost:6379"
	}
	if opt.Network == "" {
		if strings.HasPrefix(opt.Addr, "/") {
			opt.Network = "unix"
		} else {
			opt.Network = "tcp"
		}
	}
	if opt.Dialer == nil {
		opt.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
			netDialer := &net.Dialer{
				Timeout:   opt.DialTimeout,
				KeepAlive: 5 * time.Minute,
			}
			if opt.TLSConfig == nil {
				return netDialer.DialContext(ctx, network, addr)
			}
			return tls.DialWithDialer(netDialer, network, addr, opt.TLSConfig)
		}
	}
	if opt.PoolSize == 0 {
		opt.PoolSize = 10 * runtime.NumCPU()
	}
	if opt.DialTimeout == 0 {
		opt.DialTimeout = 5 * time.Second
	}
	switch opt.ReadTimeout {
	case -1:
		opt.ReadTimeout = 0
	case 0:
		opt.ReadTimeout = 3 * time.Second
	}
	switch opt.WriteTimeout {
	case -1:
		opt.WriteTimeout = 0
	case 0:
		opt.WriteTimeout = opt.ReadTimeout
	}
	if opt.PoolTimeout == 0 {
		opt.PoolTimeout = opt.ReadTimeout + time.Second
	}
	if opt.IdleTimeout == 0 {
		opt.IdleTimeout = 5 * time.Minute
	}
	if opt.IdleCheckFrequency == 0 {
		opt.IdleCheckFrequency = time.Minute
	}

	switch opt.MinRetryBackoff {
	case -1:
		opt.MinRetryBackoff = 0
	case 0:
		opt.MinRetryBackoff = 8 * time.Millisecond
	}
	switch opt.MaxRetryBackoff {
	case -1:
		opt.MaxRetryBackoff = 0
	case 0:
		opt.MaxRetryBackoff = 512 * time.Millisecond
	}
}

// ParseURL parses an URL into Options that can be used to connect to Redis.
func ParseURL(redisURL string) (*Options, error) {
	o := &Options{Network: "tcp"}
	u, err := url.Parse(redisURL)
	if err != nil {
		return nil, err
	}

	if u.Scheme != "redis" && u.Scheme != "rediss" {
		return nil, errors.New("invalid redis URL scheme: " + u.Scheme)
	}

	if u.User != nil {
		if p, ok := u.User.Password(); ok {
			o.Password = p
		}
	}

	if len(u.Query()) > 0 {
		return nil, errors.New("no options supported")
	}

	h, p, err := net.SplitHostPort(u.Host)
	if err != nil {
		h = u.Host
	}
	if h == "" {
		h = "localhost"
	}
	if p == "" {
		p = "6379"
	}
	o.Addr = net.JoinHostPort(h, p)

	f := strings.FieldsFunc(u.Path, func(r rune) bool {
		return r == '/'
	})
	switch len(f) {
	case 0:
		o.DB = 0
	case 1:
		if o.DB, err = strconv.Atoi(f[0]); err != nil {
			return nil, fmt.Errorf("invalid redis database number: %q", f[0])
		}
	default:
		return nil, errors.New("invalid redis URL path: " + u.Path)
	}

	if u.Scheme == "rediss" {
		o.TLSConfig = &tls.Config{ServerName: h}
	}
	return o, nil
}

func newConnPool(opt *Options) *pool.ConnPool {
	return pool.NewConnPool(&pool.Options{
		Dialer: func(ctx context.Context) (net.Conn, error) {
			return opt.Dialer(ctx, opt.Network, opt.Addr)
		},
		PoolSize:           opt.PoolSize,
		MinIdleConns:       opt.MinIdleConns,
		MaxConnAge:         opt.MaxConnAge,
		PoolTimeout:        opt.PoolTimeout,
		IdleTimeout:        opt.IdleTimeout,
		IdleCheckFrequency: opt.IdleCheckFrequency,
	})
}
<|endoftext|>


<commit_before>package bobo

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"os"
	"sync"
	"time"

	"github.com/getsentry/raven-go"
	"github.com/jcoene/gologger"
	"github.com/jcoene/statsd-client"
)

var Logger = logger.NewDefaultLogger("service")
var sentry *raven.Client
var sentryMu sync.Mutex

type ServiceUnavailableError struct {
	Err error
}

func (e ServiceUnavailableError) Error() string {
	return e.Err.Error()
}

func getSentry() *raven.Client {
	sentryMu.Lock()
	if sentry == nil {
		sentry, _ = raven.NewClient(os.Getenv("SENTRY_DSN"), map[string]string{})
	}
	sentryMu.Unlock()
	return sentry
}

// Request dumping middleware
func RequestDebugging(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if b, err := httputil.DumpRequest(req, true); err == nil {
			fmt.Println(string(b))
		}

		handler.ServeHTTP(rw, req)
	})
}

// Panic recovery middleware
func Recovery(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		defer func() {
			reason := ""
			switch v := recover().(type) {
			case nil:
				return
			case error:
				reason = v.Error()
			default:
				reason = fmt.Sprint(v)
			}

			getSentry().CaptureMessage(reason, map[string]string{}, raven.NewHttp(req))
			ServerError(fmt.Errorf(reason)).ServeHTTP(rw, req)
		}()

		handler.ServeHTTP(rw, req)
	})
}

// Request logging middleware
func Logging(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		t := time.Now()
		Logger.Info("method=%s path=%s action=start", req.Method, req.URL.RequestURI())
		handler.ServeHTTP(rw, req)
		resp := rw.(Response)
		Logger.Info("method=%s path=%s result=%d duration=%v", req.Method, req.URL.RequestURI(), resp.Status(), time.Since(t))
	})
}

// Turns a transport-agnostic service method into an instrumented JSON endpoint.
func JSON(name string, fn func(*http.Request) (interface{}, bool, error)) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		t := time.Now()
		obj, found, err := fn(req)
		if err != nil {
			switch err.(type) {
			case ValidationErrors:
				WriteJSON(400, &Errors{err.(ValidationErrors)}).ServeHTTP(rw, req)
				return
			case TimeoutError, ServiceUnavailableError:
				statsd.Count(fmt.Sprintf("service.%s.timeout", name), 1)
				WriteJSON(503, &Error{err.Error()}).ServeHTTP(rw, req)
			default:
				getSentry().CaptureError(err, map[string]string{}, raven.NewHttp(req))
				statsd.Count(fmt.Sprintf("service.%s.error", name), 1)
				ServerError(err).ServeHTTP(rw, req)
			}
			return
		}

		if !found {
			statsd.Count(fmt.Sprintf("service.%s.notfound", name), 1)
			NotFound(nil).ServeHTTP(rw, req)
			return
		}

		statsd.Count(fmt.Sprintf("service.%s.success", name), 1)
		statsd.MeasureDur(fmt.Sprintf("service.%s.runtime", name), time.Since(t))
		WriteJSON(200, obj).ServeHTTP(rw, req)
	})
}
<commit_msg>Revert "Log query parameters"<commit_after>package bobo

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"os"
	"sync"
	"time"

	"github.com/getsentry/raven-go"
	"github.com/jcoene/gologger"
	"github.com/jcoene/statsd-client"
)

var Logger = logger.NewDefaultLogger("service")
var sentry *raven.Client
var sentryMu sync.Mutex

type ServiceUnavailableError struct {
	Err error
}

func (e ServiceUnavailableError) Error() string {
	return e.Err.Error()
}

func getSentry() *raven.Client {
	sentryMu.Lock()
	if sentry == nil {
		sentry, _ = raven.NewClient(os.Getenv("SENTRY_DSN"), map[string]string{})
	}
	sentryMu.Unlock()
	return sentry
}

// Request dumping middleware
func RequestDebugging(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if b, err := httputil.DumpRequest(req, true); err == nil {
			fmt.Println(string(b))
		}

		handler.ServeHTTP(rw, req)
	})
}

// Panic recovery middleware
func Recovery(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		defer func() {
			reason := ""
			switch v := recover().(type) {
			case nil:
				return
			case error:
				reason = v.Error()
			default:
				reason = fmt.Sprint(v)
			}

			getSentry().CaptureMessage(reason, map[string]string{}, raven.NewHttp(req))
			ServerError(fmt.Errorf(reason)).ServeHTTP(rw, req)
		}()

		handler.ServeHTTP(rw, req)
	})
}

// Request logging middleware
func Logging(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		t := time.Now()
		Logger.Info("method=%s path=%s action=start", req.Method, req.URL.Path)
		handler.ServeHTTP(rw, req)
		resp := rw.(Response)
		Logger.Info("method=%s path=%s result=%d duration=%v", req.Method, req.URL.Path, resp.Status(), time.Since(t))
	})
}

// Turns a transport-agnostic service method into an instrumented JSON endpoint.
func JSON(name string, fn func(*http.Request) (interface{}, bool, error)) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		t := time.Now()
		obj, found, err := fn(req)
		if err != nil {
			switch err.(type) {
			case ValidationErrors:
				WriteJSON(400, &Errors{err.(ValidationErrors)}).ServeHTTP(rw, req)
				return
			case TimeoutError, ServiceUnavailableError:
				statsd.Count(fmt.Sprintf("service.%s.timeout", name), 1)
				WriteJSON(503, &Error{err.Error()}).ServeHTTP(rw, req)
			default:
				getSentry().CaptureError(err, map[string]string{}, raven.NewHttp(req))
				statsd.Count(fmt.Sprintf("service.%s.error", name), 1)
				ServerError(err).ServeHTTP(rw, req)
			}
			return
		}

		if !found {
			statsd.Count(fmt.Sprintf("service.%s.notfound", name), 1)
			NotFound(nil).ServeHTTP(rw, req)
			return
		}

		statsd.Count(fmt.Sprintf("service.%s.success", name), 1)
		statsd.MeasureDur(fmt.Sprintf("service.%s.runtime", name), time.Since(t))
		WriteJSON(200, obj).ServeHTTP(rw, req)
	})
}
<|endoftext|>


<commit_before>package user

import (
	"fmt"

	"gopkg.in/asaskevich/govalidator.v6"
)

func ValidateCreate(u *User) []error {
	var errs []error

	if err := validateEmail(u.Email); err != nil {
		errs = append(errs, err)
	}

	if err := validateUsername(u.Username); err != nil {
		errs = append(errs, err)
	}

	if err := validateName(u.Name); err != nil {
		errs = append(errs, err)
	}

	if err := validatePassword(u.Password); err != nil {
		errs = append(errs, err)
	}

	return errs
}

func ValidateUpdate(u *User) []error {
	var errs []error

	if err := validateID(u.ID); err != nil {
		errs = append(errs, err)
	}

	if err := validateEmail(u.Email); err != nil {
		errs = append(errs, err)
	}

	if err := validateUsername(u.Username); err != nil {
		errs = append(errs, err)
	}

	if err := validateName(u.Name); err != nil {
		errs = append(errs, err)
	}

	if err := validatePassword(u.Password); err != nil {
		errs = append(errs, err)
	}

	return errs
}

func validateID(id string) error {
	if ok := govalidator.IsUUIDv4(id); !ok {
		return fmt.Errorf("id is not a valid uuid v4")
	}
	return nil
}

func validateEmail(email string) error {
	if ok := govalidator.IsEmail(email); !ok {
		return fmt.Errorf("email is not valid")
	}
	return nil
}

func validateUsername(username string) error {
	if ok := govalidator.IsAlphanumeric(username); !ok {
		return fmt.Errorf("username is not alphanumeric")
	}
	if ok := govalidator.IsByteLength(username, 4, 32); !ok {
		return fmt.Errorf("username is not between 4 and 32 characters long")
	}
	return nil
}

func validateName(name string) error {
	if name == "" {
		return fmt.Errorf("name can't be empty")
	}
	return nil
}

func validatePassword(pass string) error {
	if len(pass) < 6 {
		return fmt.Errorf("password needs to be at least 6 characters")
	}
	return nil
}
<commit_msg>Don’t validate password when updating a user, that’s another mutation<commit_after>package user

import (
	"fmt"

	"gopkg.in/asaskevich/govalidator.v6"
)

func ValidateCreate(u *User) []error {
	var errs []error

	if err := validateEmail(u.Email); err != nil {
		errs = append(errs, err)
	}

	if err := validateUsername(u.Username); err != nil {
		errs = append(errs, err)
	}

	if err := validateName(u.Name); err != nil {
		errs = append(errs, err)
	}

	if err := validatePassword(u.Password); err != nil {
		errs = append(errs, err)
	}

	return errs
}

func ValidateUpdate(u *User) []error {
	var errs []error

	if err := validateID(u.ID); err != nil {
		errs = append(errs, err)
	}

	if err := validateEmail(u.Email); err != nil {
		errs = append(errs, err)
	}

	if err := validateUsername(u.Username); err != nil {
		errs = append(errs, err)
	}

	if err := validateName(u.Name); err != nil {
		errs = append(errs, err)
	}

	return errs
}

func validateID(id string) error {
	if ok := govalidator.IsUUIDv4(id); !ok {
		return fmt.Errorf("id is not a valid uuid v4")
	}
	return nil
}

func validateEmail(email string) error {
	if ok := govalidator.IsEmail(email); !ok {
		return fmt.Errorf("email is not valid")
	}
	return nil
}

func validateUsername(username string) error {
	if ok := govalidator.IsAlphanumeric(username); !ok {
		return fmt.Errorf("username is not alphanumeric")
	}
	if ok := govalidator.IsByteLength(username, 4, 32); !ok {
		return fmt.Errorf("username is not between 4 and 32 characters long")
	}
	return nil
}

func validateName(name string) error {
	if name == "" {
		return fmt.Errorf("name can't be empty")
	}
	return nil
}

func validatePassword(pass string) error {
	if len(pass) < 6 {
		return fmt.Errorf("password needs to be at least 6 characters")
	}
	return nil
}
<|endoftext|>


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

package http

import (
	"bytes"
	"encoding/binary"
)

// The algorithm uses at most sniffLen bytes to make its decision.
const sniffLen = 512

// DetectContentType implements the algorithm described
// at https://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data. It considers at most the
// first 512 bytes of data. DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
func DetectContentType(data []byte) string {
	if len(data) > sniffLen {
		data = data[:sniffLen]
	}

	// Index of the first non-whitespace byte in data.
	firstNonWS := 0
	for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ {
	}

	for _, sig := range sniffSignatures {
		if ct := sig.match(data, firstNonWS); ct != "" {
			return ct
		}
	}

	return "application/octet-stream" // fallback
}

// isWS reports whether the provided byte is a whitespace byte (0xWS)
// as defined in https://mimesniff.spec.whatwg.org/#terminology.
func isWS(b byte) bool {
	switch b {
	case '\t', '\n', '\x0c', '\r', ' ':
		return true
	}
	return false
}

// isTT reports whether the provided byte is a tag-terminating byte (0xTT)
// as defined in https://mimesniff.spec.whatwg.org/#terminology.
func isTT(b byte) bool {
	switch b {
	case ' ', '>':
		return true
	}
	return false
}

type sniffSig interface {
	// match returns the MIME type of the data, or "" if unknown.
	match(data []byte, firstNonWS int) string
}

// Data matching the table in section 6.
var sniffSignatures = []sniffSig{
	htmlSig("<!DOCTYPE HTML"),
	htmlSig("<HTML"),
	htmlSig("<HEAD"),
	htmlSig("<SCRIPT"),
	htmlSig("<IFRAME"),
	htmlSig("<H1"),
	htmlSig("<DIV"),
	htmlSig("<FONT"),
	htmlSig("<TABLE"),
	htmlSig("<A"),
	htmlSig("<STYLE"),
	htmlSig("<TITLE"),
	htmlSig("<B"),
	htmlSig("<BODY"),
	htmlSig("<BR"),
	htmlSig("<P"),
	htmlSig("<!--"),
	&maskedSig{
		mask:   []byte("\xFF\xFF\xFF\xFF\xFF"),
		pat:    []byte("<?xml"),
		skipWS: true,
		ct:     "text/xml; charset=utf-8"},
	&exactSig{[]byte("%PDF-"), "application/pdf"},
	&exactSig{[]byte("%!PS-Adobe-"), "application/postscript"},

	// UTF BOMs.
	&maskedSig{
		mask: []byte("\xFF\xFF\x00\x00"),
		pat:  []byte("\xFE\xFF\x00\x00"),
		ct:   "text/plain; charset=utf-16be",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\x00\x00"),
		pat:  []byte("\xFF\xFE\x00\x00"),
		ct:   "text/plain; charset=utf-16le",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\x00"),
		pat:  []byte("\xEF\xBB\xBF\x00"),
		ct:   "text/plain; charset=utf-8",
	},

	// Image types
	// For posterity, we originally returned "image/vnd.microsoft.icon" from
	// https://tools.ietf.org/html/draft-ietf-websec-mime-sniff-03#section-7
	// https://codereview.appspot.com/4746042
	// but that has since been replaced with "image/x-icon" in Section 6.2
	// of https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
	&exactSig{[]byte("\x00\x00\x01\x00"), "image/x-icon"},
	&exactSig{[]byte("\x00\x00\x02\x00"), "image/x-icon"},
	&exactSig{[]byte("BM"), "image/bmp"},
	&exactSig{[]byte("GIF87a"), "image/gif"},
	&exactSig{[]byte("GIF89a"), "image/gif"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WEBPVP"),
		ct:   "image/webp",
	},
	&exactSig{[]byte("\x89PNG\x0D\x0A\x1A\x0A"), "image/png"},
	&exactSig{[]byte("\xFF\xD8\xFF"), "image/jpeg"},

	// Audio and Video types
	// Enforce the pattern match ordering as prescribed in
	// https://mimesniff.spec.whatwg.org/#matching-an-audio-or-video-type-pattern
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF"),
		pat:  []byte(".snd"),
		ct:   "audio/basic",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("FORM\x00\x00\x00\x00AIFF"),
		ct:   "audio/aiff",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF"),
		pat:  []byte("ID3"),
		ct:   "audio/mpeg",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("OggS\x00"),
		ct:   "application/ogg",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("MThd\x00\x00\x00\x06"),
		ct:   "audio/midi",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00AVI "),
		ct:   "video/avi",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WAVE"),
		ct:   "audio/wave",
	},
	// 6.2.0.2. video/mp4
	mp4Sig{},
	// 6.2.0.3. video/webm
	&exactSig{[]byte("\x1A\x45\xDF\xA3"), "video/webm"},

	// Font types
	&maskedSig{
		// 34 NULL bytes followed by the string "LP"
		pat: []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LP"),
		// 34 NULL bytes followed by \xF\xF
		mask: []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF"),
		ct:   "application/vnd.ms-fontobject",
	},
	&exactSig{[]byte("\x00\x01\x00\x00"), "font/ttf"},
	&exactSig{[]byte("OTTO"), "font/otf"},
	&exactSig{[]byte("ttcf"), "font/collection"},
	&exactSig{[]byte("wOFF"), "font/woff"},
	&exactSig{[]byte("wOF2"), "font/woff2"},

	// Archive types
	&exactSig{[]byte("\x1F\x8B\x08"), "application/x-gzip"},
	&exactSig{[]byte("PK\x03\x04"), "application/zip"},
	// RAR's signatures are incorrectly defined by the MIME spec as per
	//    https://github.com/whatwg/mimesniff/issues/63
	// However, RAR Labs correctly defines it at:
	//    https://www.rarlab.com/technote.htm#rarsign
	// so we use the definition from RAR Labs.
	// TODO: do whatever the spec ends up doing.
	&exactSig{[]byte("Rar!\x1A\x07\x00"), "application/x-rar-compressed"},     // RAR v1.5-v4.0
	&exactSig{[]byte("Rar!\x1A\x07\x01\x00"), "application/x-rar-compressed"}, // RAR v5+

	&exactSig{[]byte("\x00\x61\x73\x6D"), "application/wasm"},

	textSig{}, // should be last
}

type exactSig struct {
	sig []byte
	ct  string
}

func (e *exactSig) match(data []byte, firstNonWS int) string {
	if bytes.HasPrefix(data, e.sig) {
		return e.ct
	}
	return ""
}

type maskedSig struct {
	mask, pat []byte
	skipWS    bool
	ct        string
}

func (m *maskedSig) match(data []byte, firstNonWS int) string {
	// pattern matching algorithm section 6
	// https://mimesniff.spec.whatwg.org/#pattern-matching-algorithm

	if m.skipWS {
		data = data[firstNonWS:]
	}
	if len(m.pat) != len(m.mask) {
		return ""
	}
	if len(data) < len(m.pat) {
		return ""
	}
	for i, pb := range m.pat {
		maskedData := data[i] & m.mask[i]
		if maskedData != pb {
			return ""
		}
	}
	return m.ct
}

type htmlSig []byte

func (h htmlSig) match(data []byte, firstNonWS int) string {
	data = data[firstNonWS:]
	if len(data) < len(h)+1 {
		return ""
	}
	for i, b := range h {
		db := data[i]
		if 'A' <= b && b <= 'Z' {
			db &= 0xDF
		}
		if b != db {
			return ""
		}
	}
	// Next byte must be a tag-terminating byte(0xTT).
	if !isTT(data[len(h)]) {
		return ""
	}
	return "text/html; charset=utf-8"
}

var mp4ftype = []byte("ftyp")
var mp4 = []byte("mp4")

type mp4Sig struct{}

func (mp4Sig) match(data []byte, firstNonWS int) string {
	// https://mimesniff.spec.whatwg.org/#signature-for-mp4
	// c.f. section 6.2.1
	if len(data) < 12 {
		return ""
	}
	boxSize := int(binary.BigEndian.Uint32(data[:4]))
	if len(data) < boxSize || boxSize%4 != 0 {
		return ""
	}
	if !bytes.Equal(data[4:8], mp4ftype) {
		return ""
	}
	for st := 8; st < boxSize; st += 4 {
		if st == 12 {
			// Ignores the four bytes that correspond to the version number of the "major brand".
			continue
		}
		if bytes.Equal(data[st:st+3], mp4) {
			return "video/mp4"
		}
	}
	return ""
}

type textSig struct{}

func (textSig) match(data []byte, firstNonWS int) string {
	// c.f. section 5, step 4.
	for _, b := range data[firstNonWS:] {
		switch {
		case b <= 0x08,
			b == 0x0B,
			0x0E <= b && b <= 0x1A,
			0x1C <= b && b <= 0x1F:
			return ""
		}
	}
	return "text/plain; charset=utf-8"
}
<commit_msg>net/http: drop mimesniff for audio/basic<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http

import (
	"bytes"
	"encoding/binary"
)

// The algorithm uses at most sniffLen bytes to make its decision.
const sniffLen = 512

// DetectContentType implements the algorithm described
// at https://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data. It considers at most the
// first 512 bytes of data. DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
func DetectContentType(data []byte) string {
	if len(data) > sniffLen {
		data = data[:sniffLen]
	}

	// Index of the first non-whitespace byte in data.
	firstNonWS := 0
	for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ {
	}

	for _, sig := range sniffSignatures {
		if ct := sig.match(data, firstNonWS); ct != "" {
			return ct
		}
	}

	return "application/octet-stream" // fallback
}

// isWS reports whether the provided byte is a whitespace byte (0xWS)
// as defined in https://mimesniff.spec.whatwg.org/#terminology.
func isWS(b byte) bool {
	switch b {
	case '\t', '\n', '\x0c', '\r', ' ':
		return true
	}
	return false
}

// isTT reports whether the provided byte is a tag-terminating byte (0xTT)
// as defined in https://mimesniff.spec.whatwg.org/#terminology.
func isTT(b byte) bool {
	switch b {
	case ' ', '>':
		return true
	}
	return false
}

type sniffSig interface {
	// match returns the MIME type of the data, or "" if unknown.
	match(data []byte, firstNonWS int) string
}

// Data matching the table in section 6.
var sniffSignatures = []sniffSig{
	htmlSig("<!DOCTYPE HTML"),
	htmlSig("<HTML"),
	htmlSig("<HEAD"),
	htmlSig("<SCRIPT"),
	htmlSig("<IFRAME"),
	htmlSig("<H1"),
	htmlSig("<DIV"),
	htmlSig("<FONT"),
	htmlSig("<TABLE"),
	htmlSig("<A"),
	htmlSig("<STYLE"),
	htmlSig("<TITLE"),
	htmlSig("<B"),
	htmlSig("<BODY"),
	htmlSig("<BR"),
	htmlSig("<P"),
	htmlSig("<!--"),
	&maskedSig{
		mask:   []byte("\xFF\xFF\xFF\xFF\xFF"),
		pat:    []byte("<?xml"),
		skipWS: true,
		ct:     "text/xml; charset=utf-8"},
	&exactSig{[]byte("%PDF-"), "application/pdf"},
	&exactSig{[]byte("%!PS-Adobe-"), "application/postscript"},

	// UTF BOMs.
	&maskedSig{
		mask: []byte("\xFF\xFF\x00\x00"),
		pat:  []byte("\xFE\xFF\x00\x00"),
		ct:   "text/plain; charset=utf-16be",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\x00\x00"),
		pat:  []byte("\xFF\xFE\x00\x00"),
		ct:   "text/plain; charset=utf-16le",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\x00"),
		pat:  []byte("\xEF\xBB\xBF\x00"),
		ct:   "text/plain; charset=utf-8",
	},

	// Image types
	// For posterity, we originally returned "image/vnd.microsoft.icon" from
	// https://tools.ietf.org/html/draft-ietf-websec-mime-sniff-03#section-7
	// https://codereview.appspot.com/4746042
	// but that has since been replaced with "image/x-icon" in Section 6.2
	// of https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
	&exactSig{[]byte("\x00\x00\x01\x00"), "image/x-icon"},
	&exactSig{[]byte("\x00\x00\x02\x00"), "image/x-icon"},
	&exactSig{[]byte("BM"), "image/bmp"},
	&exactSig{[]byte("GIF87a"), "image/gif"},
	&exactSig{[]byte("GIF89a"), "image/gif"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WEBPVP"),
		ct:   "image/webp",
	},
	&exactSig{[]byte("\x89PNG\x0D\x0A\x1A\x0A"), "image/png"},
	&exactSig{[]byte("\xFF\xD8\xFF"), "image/jpeg"},

	// Audio and Video types
	// Enforce the pattern match ordering as prescribed in
	// https://mimesniff.spec.whatwg.org/#matching-an-audio-or-video-type-pattern
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("FORM\x00\x00\x00\x00AIFF"),
		ct:   "audio/aiff",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF"),
		pat:  []byte("ID3"),
		ct:   "audio/mpeg",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("OggS\x00"),
		ct:   "application/ogg",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("MThd\x00\x00\x00\x06"),
		ct:   "audio/midi",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00AVI "),
		ct:   "video/avi",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WAVE"),
		ct:   "audio/wave",
	},
	// 6.2.0.2. video/mp4
	mp4Sig{},
	// 6.2.0.3. video/webm
	&exactSig{[]byte("\x1A\x45\xDF\xA3"), "video/webm"},

	// Font types
	&maskedSig{
		// 34 NULL bytes followed by the string "LP"
		pat: []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LP"),
		// 34 NULL bytes followed by \xF\xF
		mask: []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF"),
		ct:   "application/vnd.ms-fontobject",
	},
	&exactSig{[]byte("\x00\x01\x00\x00"), "font/ttf"},
	&exactSig{[]byte("OTTO"), "font/otf"},
	&exactSig{[]byte("ttcf"), "font/collection"},
	&exactSig{[]byte("wOFF"), "font/woff"},
	&exactSig{[]byte("wOF2"), "font/woff2"},

	// Archive types
	&exactSig{[]byte("\x1F\x8B\x08"), "application/x-gzip"},
	&exactSig{[]byte("PK\x03\x04"), "application/zip"},
	// RAR's signatures are incorrectly defined by the MIME spec as per
	//    https://github.com/whatwg/mimesniff/issues/63
	// However, RAR Labs correctly defines it at:
	//    https://www.rarlab.com/technote.htm#rarsign
	// so we use the definition from RAR Labs.
	// TODO: do whatever the spec ends up doing.
	&exactSig{[]byte("Rar!\x1A\x07\x00"), "application/x-rar-compressed"},     // RAR v1.5-v4.0
	&exactSig{[]byte("Rar!\x1A\x07\x01\x00"), "application/x-rar-compressed"}, // RAR v5+

	&exactSig{[]byte("\x00\x61\x73\x6D"), "application/wasm"},

	textSig{}, // should be last
}

type exactSig struct {
	sig []byte
	ct  string
}

func (e *exactSig) match(data []byte, firstNonWS int) string {
	if bytes.HasPrefix(data, e.sig) {
		return e.ct
	}
	return ""
}

type maskedSig struct {
	mask, pat []byte
	skipWS    bool
	ct        string
}

func (m *maskedSig) match(data []byte, firstNonWS int) string {
	// pattern matching algorithm section 6
	// https://mimesniff.spec.whatwg.org/#pattern-matching-algorithm

	if m.skipWS {
		data = data[firstNonWS:]
	}
	if len(m.pat) != len(m.mask) {
		return ""
	}
	if len(data) < len(m.pat) {
		return ""
	}
	for i, pb := range m.pat {
		maskedData := data[i] & m.mask[i]
		if maskedData != pb {
			return ""
		}
	}
	return m.ct
}

type htmlSig []byte

func (h htmlSig) match(data []byte, firstNonWS int) string {
	data = data[firstNonWS:]
	if len(data) < len(h)+1 {
		return ""
	}
	for i, b := range h {
		db := data[i]
		if 'A' <= b && b <= 'Z' {
			db &= 0xDF
		}
		if b != db {
			return ""
		}
	}
	// Next byte must be a tag-terminating byte(0xTT).
	if !isTT(data[len(h)]) {
		return ""
	}
	return "text/html; charset=utf-8"
}

var mp4ftype = []byte("ftyp")
var mp4 = []byte("mp4")

type mp4Sig struct{}

func (mp4Sig) match(data []byte, firstNonWS int) string {
	// https://mimesniff.spec.whatwg.org/#signature-for-mp4
	// c.f. section 6.2.1
	if len(data) < 12 {
		return ""
	}
	boxSize := int(binary.BigEndian.Uint32(data[:4]))
	if len(data) < boxSize || boxSize%4 != 0 {
		return ""
	}
	if !bytes.Equal(data[4:8], mp4ftype) {
		return ""
	}
	for st := 8; st < boxSize; st += 4 {
		if st == 12 {
			// Ignores the four bytes that correspond to the version number of the "major brand".
			continue
		}
		if bytes.Equal(data[st:st+3], mp4) {
			return "video/mp4"
		}
	}
	return ""
}

type textSig struct{}

func (textSig) match(data []byte, firstNonWS int) string {
	// c.f. section 5, step 4.
	for _, b := range data[firstNonWS:] {
		switch {
		case b <= 0x08,
			b == 0x0B,
			0x0E <= b && b <= 0x1A,
			0x1C <= b && b <= 0x1F:
			return ""
		}
	}
	return "text/plain; charset=utf-8"
}
<|endoftext|>


<commit_before>package doozer

import (
	"doozer/client"
	"doozer/store"
	"testing"
)

func Benchmark1DoozerClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	go Main("a", "", u, l, nil, 1e9, 2e9, 3e9)

	cl := client.New("foo", l.Addr().String())

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cl.Set("/test", store.Clobber, nil)
	}
}

func Benchmark1DoozerConClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	go Main("a", "", u, l, nil, 1e9, 2e9, 3e9)

	cl := client.New("foo", l.Addr().String())

	c := make(chan bool, b.N)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		go func() {
			cl.Set("/test", store.Clobber, nil)
			c <- true
		}()
	}
	for i := 0; i < b.N; i++ {
		<-c
	}
}

func Benchmark5DoozerClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	go Main("a", "", u, l, nil, 1e9, 2e9, 3e9)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil, 1e9, 2e9, 3e9)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil, 1e9, 2e9, 3e9)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil, 1e9, 2e9, 3e9)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil, 1e9, 2e9, 3e9)

	cl := client.New("foo", l.Addr().String())

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cl.Set("/test", store.Clobber, nil)
	}
}

func Benchmark5DoozerConClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	go Main("a", "", u, l, nil, 1e9, 2e9, 3e9)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil, 1e9, 2e9, 3e9)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil, 1e9, 2e9, 3e9)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil, 1e9, 2e9, 3e9)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil, 1e9, 2e9, 3e9)

	cl := client.New("foo", l.Addr().String())

	c := make(chan bool, b.N)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		go func() {
			cl.Set("/test", store.Clobber, nil)
			c <- true
		}()
	}
	for i := 0; i < b.N; i++ {
		<-c
	}
}
<commit_msg>doozer: benchmark tests produce more meaningful results<commit_after>package doozer

import (
	"doozer/client"
	"doozer/store"
	"testing"
)

func Benchmark1DoozerClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	go Main("a", "", u, l, nil, 1e9, 2e9, 3e9)

	cl := client.New("foo", l.Addr().String())

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cl.Set("/test", store.Clobber, nil)
	}
}

func Benchmark1DoozerConClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	go Main("a", "", u, l, nil, 1e9, 2e9, 3e9)

	cl := client.New("foo", l.Addr().String())

	c := make(chan bool, b.N)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		go func() {
			cl.Set("/test", store.Clobber, nil)
			c <- true
		}()
	}
	for i := 0; i < b.N; i++ {
		<-c
	}
}

func Benchmark5DoozerClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	l1 := mustListen()
	defer l1.Close()
	u1 := mustListenPacket(l1.Addr().String())
	defer u1.Close()
	l2 := mustListen()
	defer l2.Close()
	u2 := mustListenPacket(l2.Addr().String())
	defer u2.Close()
	l3 := mustListen()
	defer l3.Close()
	u3 := mustListenPacket(l3.Addr().String())
	defer u3.Close()
	l4 := mustListen()
	defer l4.Close()
	u4 := mustListenPacket(l4.Addr().String())
	defer u4.Close()

	go Main("a", "", u, l, nil, 1e9, 1e8, 3e9)
	go Main("a", a, u1, l1, nil, 1e9, 1e8, 3e9)
	go Main("a", a, u2, l2, nil, 1e9, 1e8, 3e9)
	go Main("a", a, u3, l3, nil, 1e9, 1e8, 3e9)
	go Main("a", a, u4, l4, nil, 1e9, 1e8, 3e9)

	cl := client.New("foo", l.Addr().String())
	cl.Set("/ctl/cal/1", store.Missing, nil)
	cl.Set("/ctl/cal/2", store.Missing, nil)
	cl.Set("/ctl/cal/3", store.Missing, nil)
	cl.Set("/ctl/cal/4", store.Missing, nil)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cl.Set("/test", store.Clobber, nil)
	}
}

func Benchmark5DoozerConClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	l1 := mustListen()
	defer l1.Close()
	u1 := mustListenPacket(l1.Addr().String())
	defer u1.Close()
	l2 := mustListen()
	defer l2.Close()
	u2 := mustListenPacket(l2.Addr().String())
	defer u2.Close()
	l3 := mustListen()
	defer l3.Close()
	u3 := mustListenPacket(l3.Addr().String())
	defer u3.Close()
	l4 := mustListen()
	defer l4.Close()
	u4 := mustListenPacket(l4.Addr().String())
	defer u4.Close()

	go Main("a", "", u, l, nil, 1e9, 1e8, 3e9)
	go Main("a", a, u1, l1, nil, 1e9, 1e8, 3e9)
	go Main("a", a, u2, l2, nil, 1e9, 1e8, 3e9)
	go Main("a", a, u3, l3, nil, 1e9, 1e8, 3e9)
	go Main("a", a, u4, l4, nil, 1e9, 1e8, 3e9)

	cl := client.New("foo", l.Addr().String())
	cl.Set("/ctl/cal/1", store.Missing, nil)
	cl.Set("/ctl/cal/2", store.Missing, nil)
	cl.Set("/ctl/cal/3", store.Missing, nil)
	cl.Set("/ctl/cal/4", store.Missing, nil)

	cls := []*client.Client{
		cl,
		client.New("foo", l1.Addr().String()),
		client.New("foo", l2.Addr().String()),
		client.New("foo", l3.Addr().String()),
		client.New("foo", l4.Addr().String()),
	}

	c := make(chan bool, b.N)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		i := i
		go func() {
			cls[i%len(cls)].Set("/test", store.Clobber, nil)
			c <- true
		}()
	}
	for i := 0; i < b.N; i++ {
		<-c
	}
}
<|endoftext|>


<commit_before>package main

import "reflect"
import "testing"

var permutationsTests = []struct {
	input    []int
	expected [][]int
}{
	{input: []int{}, expected: [][]int{}},
	{input: []int{0}, expected: [][]int{{0}}},
	{input: []int{0, 1}, expected: [][]int{{0, 1}, {1, 0}}},
	{
		input: []int{0, 1, 2},
		expected: [][]int{
			{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0},
		},
	},
}

func TestPermutations(t *testing.T) {
	for _, tt := range permutationsTests {
		actual := permutations(tt.input)
		if !reflect.DeepEqual(actual, tt.expected) {
			t.Errorf("%v: Expected %v but got %v", tt.input, tt.expected, actual)
		}
	}
}

func BenchmarkPermutations(b *testing.B) {
	nums := []int{0, 1, 2, 3, 4, 5}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		permutations(nums)
	}
}

var factoradicTests = []struct {
	input    int
	expected []int
}{
	{input: -1, expected: nil},
	{input: 0, expected: []int{}},
	{input: 1, expected: []int{1}},
	{input: 2, expected: []int{1, 0}},
	{input: 3, expected: []int{1, 1}},
	{input: 4, expected: []int{2, 0}},
	{input: 5, expected: []int{2, 1}},
	{input: 6, expected: []int{1, 0, 0}},
	{input: 7, expected: []int{1, 0, 1}},
	{input: 8, expected: []int{1, 1, 0}},
	{input: 9, expected: []int{1, 1, 1}},
	{input: 10, expected: []int{1, 2, 0}},
	{input: 11, expected: []int{1, 2, 1}},
	{input: 12, expected: []int{2, 0, 0}},
	{input: 13, expected: []int{2, 0, 1}},
	{input: 14, expected: []int{2, 1, 0}},
	{input: 15, expected: []int{2, 1, 1}},
	{input: 16, expected: []int{2, 2, 0}},
	{input: 17, expected: []int{2, 2, 1}},
	{input: 18, expected: []int{3, 0, 0}},
	{input: 19, expected: []int{3, 0, 1}},
	{input: 20, expected: []int{3, 1, 0}},
	{input: 21, expected: []int{3, 1, 1}},
	{input: 22, expected: []int{3, 2, 0}},
	{input: 23, expected: []int{3, 2, 1}},
}

func TestFactoradic(t *testing.T) {
	for _, tt := range factoradicTests {
		actual := factoradic(tt.input)
		if !reflect.DeepEqual(actual, tt.expected) {
			t.Errorf("%d: Expected %d but got %d", tt.input, tt.expected, actual)
		}
	}
}

var p024Tests = []struct {
	inNums   []int
	inN      int
	expected int
}{
	{inNums: []int{}, inN: 0, expected: -1},
	{inNums: []int{}, inN: 1, expected: -1},
	{inNums: []int{0}, inN: 0, expected: -1},
	{inNums: []int{0}, inN: 1, expected: 0},
	{inNums: []int{0}, inN: 2, expected: -1},
	{inNums: []int{1, 2}, inN: 1, expected: 12},
	{inNums: []int{1, 2}, inN: 2, expected: 21},
	{inNums: []int{0, 1, 2}, inN: 0, expected: -1},
	{inNums: []int{0, 1, 2}, inN: 1, expected: 12},
	{inNums: []int{0, 1, 2}, inN: 2, expected: 21},
	{inNums: []int{0, 1, 2}, inN: 3, expected: 102},
	{inNums: []int{0, 1, 2}, inN: 4, expected: 120},
	{inNums: []int{0, 1, 2}, inN: 5, expected: 201},
	{inNums: []int{0, 1, 2}, inN: 6, expected: 210},
	{inNums: []int{0, 1, 2}, inN: 7, expected: -1},
}

func TestP024A(t *testing.T) {
	for _, tt := range p024Tests {
		actual := p024A(tt.inNums, tt.inN)
		if !reflect.DeepEqual(actual, tt.expected) {
			t.Errorf("(%v,%d): Expected %d but got %d", tt.inNums, tt.inN, tt.expected, actual)
		}
	}
}

func TestP024B(t *testing.T) {
	for _, tt := range p024Tests {
		actual := p024B(tt.inNums, tt.inN)
		if !reflect.DeepEqual(actual, tt.expected) {
			t.Errorf("(%v,%d): Expected %d but got %d", tt.inNums, tt.inN, tt.expected, actual)
		}
	}
}

func BenchmarkP024A(b *testing.B) {
	nums := []int{0, 1, 2, 3, 4, 5, 6, 7}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		p024A(nums, 30000)
	}
}

func BenchmarkP024B(b *testing.B) {
	nums := []int{0, 1, 2, 3, 4, 5, 6, 7}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		p024B(nums, 30000)
	}
}

func ExampleP024() {
	main()

	// Output:
	// P024A([0-7],30000): 57364210
	// P024B: 2783915460
}
<commit_msg>テストを修正<commit_after>package main

import "reflect"
import "testing"

var permutationsTests = []struct {
	input    []int
	expected [][]int
}{
	{input: []int{}, expected: [][]int{}},
	{input: []int{0}, expected: [][]int{{0}}},
	{input: []int{0, 1}, expected: [][]int{{0, 1}, {1, 0}}},
	{
		input: []int{0, 1, 2},
		expected: [][]int{
			{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0},
		},
	},
}

func TestPermutations(t *testing.T) {
	for _, tt := range permutationsTests {
		actual := permutations(tt.input)
		if !reflect.DeepEqual(actual, tt.expected) {
			t.Errorf("%v: Expected %v but got %v", tt.input, tt.expected, actual)
		}
	}
}

func BenchmarkPermutations(b *testing.B) {
	nums := []int{0, 1, 2, 3, 4, 5}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		permutations(nums)
	}
}

var factoradicTests = []struct {
	input    int
	expected []int
}{
	{input: -1, expected: nil},
	{input: 0, expected: []int{}},
	{input: 1, expected: []int{1}},
	{input: 2, expected: []int{1, 0}},
	{input: 3, expected: []int{1, 1}},
	{input: 4, expected: []int{2, 0}},
	{input: 5, expected: []int{2, 1}},
	{input: 6, expected: []int{1, 0, 0}},
	{input: 7, expected: []int{1, 0, 1}},
	{input: 8, expected: []int{1, 1, 0}},
	{input: 9, expected: []int{1, 1, 1}},
	{input: 10, expected: []int{1, 2, 0}},
	{input: 11, expected: []int{1, 2, 1}},
	{input: 12, expected: []int{2, 0, 0}},
	{input: 13, expected: []int{2, 0, 1}},
	{input: 14, expected: []int{2, 1, 0}},
	{input: 15, expected: []int{2, 1, 1}},
	{input: 16, expected: []int{2, 2, 0}},
	{input: 17, expected: []int{2, 2, 1}},
	{input: 18, expected: []int{3, 0, 0}},
	{input: 19, expected: []int{3, 0, 1}},
	{input: 20, expected: []int{3, 1, 0}},
	{input: 21, expected: []int{3, 1, 1}},
	{input: 22, expected: []int{3, 2, 0}},
	{input: 23, expected: []int{3, 2, 1}},
}

func TestFactoradic(t *testing.T) {
	for _, tt := range factoradicTests {
		actual := factoradic(tt.input)
		if !reflect.DeepEqual(actual, tt.expected) {
			t.Errorf("%d: Expected %d but got %d", tt.input, tt.expected, actual)
		}
	}
}

var p024Tests = []struct {
	inNums   []int
	inN      int
	expected int
}{
	{inNums: []int{}, inN: 0, expected: -1},
	{inNums: []int{}, inN: 1, expected: -1},
	{inNums: []int{0}, inN: 0, expected: -1},
	{inNums: []int{0}, inN: 1, expected: 0},
	{inNums: []int{0}, inN: 2, expected: -1},
	{inNums: []int{1, 2}, inN: 1, expected: 12},
	{inNums: []int{1, 2}, inN: 2, expected: 21},
	{inNums: []int{0, 1, 2}, inN: 0, expected: -1},
	{inNums: []int{0, 1, 2}, inN: 1, expected: 12},
	{inNums: []int{0, 1, 2}, inN: 2, expected: 21},
	{inNums: []int{0, 1, 2}, inN: 3, expected: 102},
	{inNums: []int{0, 1, 2}, inN: 4, expected: 120},
	{inNums: []int{0, 1, 2}, inN: 5, expected: 201},
	{inNums: []int{0, 1, 2}, inN: 6, expected: 210},
	{inNums: []int{0, 1, 2}, inN: 7, expected: -1},
}

func TestP024A(t *testing.T) {
	for _, tt := range p024Tests {
		actual := p024A(tt.inNums, tt.inN)
		if actual != tt.expected {
			t.Errorf("(%v,%d): Expected %d but got %d", tt.inNums, tt.inN, tt.expected, actual)
		}
	}
}

func TestP024B(t *testing.T) {
	for _, tt := range p024Tests {
		actual := p024B(tt.inNums, tt.inN)
		if actual != tt.expected {
			t.Errorf("(%v,%d): Expected %d but got %d", tt.inNums, tt.inN, tt.expected, actual)
		}
	}
}

func BenchmarkP024A(b *testing.B) {
	nums := []int{0, 1, 2, 3, 4, 5, 6, 7}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		p024A(nums, 30000)
	}
}

func BenchmarkP024B(b *testing.B) {
	nums := []int{0, 1, 2, 3, 4, 5, 6, 7}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		p024B(nums, 30000)
	}
}

func ExampleP024() {
	main()

	// Output:
	// P024A([0-7],30000): 57364210
	// P024B: 2783915460
}
<|endoftext|>


<commit_before>// 1 september 2013
package main

import (
	"io"
	"encoding/binary"
	"image/color"
)

// palette format is simple
// first longword is size of palette data in longwords
// this is immediately followed by the palette itself

type GBAColor uint16

const (
	redShift = 0
	redMask = (0x1F << redShift)
	greenShift = 5
	greenMask = (0x1F << greenShift)
	blueShift = 10
	blueMask = (0x1F << blueShift)
	componentScale = 11
)

func (c GBAColor) RGBA() (r, g, b, a uint32) {
	red := (c & redMask) >> redShift
	green := (c & greenMask) >> greenShift
	blue := (c & blueMask) >> blueShift
	return uint32(red) << componentScale,
		uint32(green) << componentScale,
		uint32(blue) << componentScale,
		0xFFFF
}

func ReadGBAColor(r io.Reader) (c GBAColor, err error) {
	err = binary.Read(r, binary.LittleEndian, &c)
	return c, err			// TODO wrap potential error with more text?
}

func ReadPalette(r io.Reader) (p color.Palette, err error) {
	var n uint32

	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return nil, err		// TODO wrap with more text?
	}
	n <<= 1				// longwords -> words for color count
	p = make(color.Palette, n)
	for i := 0; i < n; i++ {
		c, err := ReadGBAColor(r)
		if err != nil {
			return nil, err		// TODO wrap with more text?
		}
		p[i] = c
	}
	return p, nil
}
<commit_msg>Whoops, read the DMA spec wrong; fixed palette format errors.<commit_after>// 1 september 2013
package main

import (
	"io"
	"encoding/binary"
	"image/color"
)

// palette format is simple
// first longword is size of palette data in bytes
// this is immediately followed by the palette itself

type GBAColor uint16

const (
	redShift = 0
	redMask = (0x1F << redShift)
	greenShift = 5
	greenMask = (0x1F << greenShift)
	blueShift = 10
	blueMask = (0x1F << blueShift)
	componentScale = 11
)

func (c GBAColor) RGBA() (r, g, b, a uint32) {
	red := (c & redMask) >> redShift
	green := (c & greenMask) >> greenShift
	blue := (c & blueMask) >> blueShift
	return uint32(red) << componentScale,
		uint32(green) << componentScale,
		uint32(blue) << componentScale,
		0xFFFF
}

func ReadGBAColor(r io.Reader) (c GBAColor, err error) {
	err = binary.Read(r, binary.LittleEndian, &c)
	return c, err			// TODO wrap potential error with more text?
}

func ReadPalette(r io.Reader) (p color.Palette, err error) {
	var n uint32

	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return nil, err		// TODO wrap with more text?
	}
	n >>= 1				// bytes -> words for color count
	p = make(color.Palette, n)
	for i := 0; i < n; i++ {
		c, err := ReadGBAColor(r)
		if err != nil {
			return nil, err		// TODO wrap with more text?
		}
		p[i] = c
	}
	return p, nil
}
<|endoftext|>