<commit_before>package main

import (
	"flag"
	"log"
	"math/rand"
	"net/http"
	"sort"
	"strconv"
	"sync"
	"time"
)

type sid string
type pid string

type Server struct {
	sid     sid
	players map[pid]*Player
}

type Player struct {
	pid          pid
	sid          sid
	name         string
	color        uint32
	lastUpdate   time.Time
	lastX, lastY float64
	lastScore    int32
}

var mu sync.Mutex
var rnd = rand.New(rand.NewSource(time.Now().Unix()))
var players = map[pid]*Player{}
var servers = map[sid]*Server{}

func newId() string {
	return strconv.FormatInt(rnd.Int63(), 16)
}

type GetIdReq struct {
	Sid   string  `json:"sid"`
	Name  string  `json:"name"`
	Color uint32  `json:"color"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
}

type GetIdRsp struct {
	Id string `json:"id"`
}

func getId(w http.ResponseWriter, r *http.Request) {
	log.Println(r.URL)
	if SetCorsHeaders(w.Header(), r) {
		return
	}

	req := GetIdReq{}
	if !ReadJson(w, r, &req) {
		return
	}

	if len(req.Sid) == 0 {
		http.Error(w, "missing sId", http.StatusBadRequest)
		return
	}

	mu.Lock()
	defer mu.Unlock()

	sid := sid(req.Sid)
	id := pid(newId())
	server, ok := servers[sid]
	if !ok {
		server = &Server{
			sid:     sid,
			players: map[pid]*Player{},
		}
		servers[sid] = server
	}

	player := &Player{
		pid:        id,
		sid:        sid,
		name:       req.Name,
		color:      req.Color,
		lastX:      req.X,
		lastY:      req.Y,
		lastScore:  req.Score,
		lastUpdate: time.Now(),
	}
	players[id] = player
	server.players[id] = player

	JsonRespond(w, &GetIdRsp{
		Id: string(player.pid),
	})
}

type UpdateReq struct {
	Id    string  `json:"id"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
}

type PlayerStatus struct {
	Name  string  `json:"name"`
	Color uint32  `json:"color"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
	IsMe  bool    `json:"is_me"`
}

type UpdateRsp struct {
	Players []PlayerStatus `json:"players"`
}

func update(w http.ResponseWriter, r *http.Request) {
	log.Println(r.URL)
	if SetCorsHeaders(w.Header(), r) {
		return
	}

	req := UpdateReq{}
	if !ReadJson(w, r, &req) {
		return
	}

	if len(req.Id) == 0 {
		http.Error(w, "missing id", http.StatusBadRequest)
		return
	}

	mu.Lock()
	defer mu.Unlock()

	id := pid(req.Id)
	player, ok := players[id]
	if !ok {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	server, ok := servers[player.sid]
	if !ok {
		http.Error(w, "no server for that player", http.StatusInternalServerError)
		return
	}

	now := time.Now()
	player.lastScore = req.Score
	player.lastX = req.X
	player.lastY = req.Y
	player.lastUpdate = now

	results := []PlayerStatus{}
	playersToGc := []pid{}
	for _, player := range server.players {
		// GC players who haven't posted in 30 seconds.
		if player.lastUpdate.Before(now.Add(-30 * time.Second)) {
			playersToGc = append(playersToGc, player.pid)
			continue
		}
		// Omit players who haven't posted in 5 seconds or are dead.
		if player.lastScore == 0 || player.lastUpdate.Before(now.Add(-5*time.Second)) {
			continue
		}

		results = append(results, PlayerStatus{
			Name:  player.name,
			Color: player.color,
			X:     player.lastX,
			Y:     player.lastY,
			Score: player.lastScore,
			IsMe:  (player.pid == id),
		})
	}

	for _, gc := range playersToGc {
		delete(server.players, gc)
		delete(players, gc)
	}

	sort.Sort(sort.Reverse(byScore(results))) // by score descending
	JsonRespond(w, &UpdateRsp{
		Players: results,
	})
}

type byScore []PlayerStatus

func (p byScore) Len() int {
	return len(p)
}
func (p byScore) Less(i, j int) bool {
	pi := p[i]
	pj := p[j]
	if pi.Score < pj.Score {
		return true
	} else if pi.Score > pj.Score {
		return false
	}

	return pi.Name < pj.Name
}
func (p byScore) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func main() {
	addr := flag.String("addr", ":8080", "http service address")
	flag.Parse()
	http.HandleFunc("/getId", getId)
	http.HandleFunc("/update", update)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>logging update<commit_after>package main

import (
	"flag"
	"log"
	"math/rand"
	"net/http"
	"sort"
	"strconv"
	"sync"
	"time"
)

type sid string
type pid string

type Server struct {
	sid     sid
	players map[pid]*Player
}

type Player struct {
	pid          pid
	sid          sid
	name         string
	color        uint32
	lastUpdate   time.Time
	lastX, lastY float64
	lastScore    int32
}

var mu sync.Mutex
var rnd = rand.New(rand.NewSource(time.Now().Unix()))
var players = map[pid]*Player{}
var servers = map[sid]*Server{}

func newId() string {
	return strconv.FormatInt(rnd.Int63(), 16)
}

type GetIdReq struct {
	Sid   string  `json:"sid"`
	Name  string  `json:"name"`
	Color uint32  `json:"color"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
}

type GetIdRsp struct {
	Id string `json:"id"`
}

func getId(w http.ResponseWriter, r *http.Request) {
	if SetCorsHeaders(w.Header(), r) {
		return
	}

	req := GetIdReq{}
	if !ReadJson(w, r, &req) {
		return
	}

	if len(req.Sid) == 0 {
		http.Error(w, "missing sId", http.StatusBadRequest)
		return
	}

	mu.Lock()
	defer mu.Unlock()

	sid := sid(req.Sid)
	id := pid(newId())
	server, ok := servers[sid]
	if !ok {
		server = &Server{
			sid:     sid,
			players: map[pid]*Player{},
		}
		servers[sid] = server
	}

	player := &Player{
		pid:        id,
		sid:        sid,
		name:       req.Name,
		color:      req.Color,
		lastX:      req.X,
		lastY:      req.Y,
		lastScore:  req.Score,
		lastUpdate: time.Now(),
	}
	players[id] = player
	server.players[id] = player

	log.Printf("%s (%s) joined %s", player.name, player.pid, player.sid)

	JsonRespond(w, &GetIdRsp{
		Id: string(player.pid),
	})
}

type UpdateReq struct {
	Id    string  `json:"id"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
}

type PlayerStatus struct {
	Name  string  `json:"name"`
	Color uint32  `json:"color"`
	X     float64 `json:"x"`
	Y     float64 `json:"y"`
	Score int32   `json:"score"`
	IsMe  bool    `json:"is_me"`
}

type UpdateRsp struct {
	Players []PlayerStatus `json:"players"`
}

func update(w http.ResponseWriter, r *http.Request) {
	if SetCorsHeaders(w.Header(), r) {
		return
	}

	req := UpdateReq{}
	if !ReadJson(w, r, &req) {
		return
	}

	if len(req.Id) == 0 {
		http.Error(w, "missing id", http.StatusBadRequest)
		return
	}

	mu.Lock()
	defer mu.Unlock()

	id := pid(req.Id)
	player, ok := players[id]
	if !ok {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	server, ok := servers[player.sid]
	if !ok {
		http.Error(w, "no server for that player", http.StatusInternalServerError)
		return
	}

	now := time.Now()
	player.lastScore = req.Score
	player.lastX = req.X
	player.lastY = req.Y
	player.lastUpdate = now

	results := []PlayerStatus{}
	playersToGc := []pid{}
	for _, player := range server.players {
		// GC players who haven't posted in 30 seconds.
		if player.lastUpdate.Before(now.Add(-30 * time.Second)) {
			playersToGc = append(playersToGc, player.pid)
			continue
		}
		// Omit players who haven't posted in 5 seconds or are dead.
		if player.lastScore == 0 || player.lastUpdate.Before(now.Add(-5*time.Second)) {
			continue
		}

		results = append(results, PlayerStatus{
			Name:  player.name,
			Color: player.color,
			X:     player.lastX,
			Y:     player.lastY,
			Score: player.lastScore,
			IsMe:  (player.pid == id),
		})
	}

	for _, gc := range playersToGc {
		log.Printf("%s (%s) collected from %s", player.name, player.pid, player.sid)
		delete(server.players, gc)
		delete(players, gc)
	}

	sort.Sort(sort.Reverse(byScore(results))) // by score descending
	JsonRespond(w, &UpdateRsp{
		Players: results,
	})
}

type byScore []PlayerStatus

func (p byScore) Len() int {
	return len(p)
}
func (p byScore) Less(i, j int) bool {
	pi := p[i]
	pj := p[j]
	if pi.Score < pj.Score {
		return true
	} else if pi.Score > pj.Score {
		return false
	}

	return pi.Name < pj.Name
}
func (p byScore) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func main() {
	addr := flag.String("addr", ":8080", "http service address")
	flag.Parse()
	http.HandleFunc("/getId", getId)
	http.HandleFunc("/update", update)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


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

package bufcli

import (
	"context"
	"errors"
	"fmt"

	"github.com/bufbuild/buf/private/bufpkg/bufmodule/bufmoduleref"
	"github.com/bufbuild/buf/private/pkg/app"
	"github.com/bufbuild/buf/private/pkg/app/appflag"
	"github.com/bufbuild/buf/private/pkg/rpc"
)

const (
	// ExitCodeFileAnnotation is the exit code used when we print file annotations.
	//
	// We use a different exit code to be able to distinguish user-parsable errors from
	// system errors.
	ExitCodeFileAnnotation = 100
)

var (
	// ErrNoModuleName is used when the user does not specify a module name in their configuration file.
	ErrNoModuleName = errors.New(`please specify a module name in your configuration file with the "name" key`)

	// ErrNoConfigFile is used when the user tries to execute a command without a configuration file.
	ErrNoConfigFile = errors.New(`please define a configuration file in the current directory; you can create one by running "buf config init"`)

	// ErrFileAnnotation is used when we print file annotations and want to return an error.
	//
	// The app package works on the concept that an error results in a non-zero exit
	// code, and we already print the messages with PrintFileAnnotations, so we do
	// not want to print any additional error message.
	//
	// We also exit with 100 to be able to distinguish user-parsable errors from
	// system errors.
	ErrFileAnnotation = app.NewError(ExitCodeFileAnnotation, "")
)

// errInternal is returned when the user encounters an unexpected internal buf error.
type errInternal struct {
	cause error
}

// NewInternalError represents an internal error encountered by the buf CLI.
// These errors should not happen and therefore warrant a bug report.
func NewInternalError(err error) error {
	if isInternalError(err) {
		return err
	}
	return &errInternal{cause: err}
}

// isInternalError returns whether the error provided, or
// any error wrapped by that error, is an internal error.
func isInternalError(err error) bool {
	asErr := &errInternal{}
	return errors.As(err, &asErr)
}

func (e *errInternal) Error() string {
	message := "it looks like you have found a bug in buf. " +
		"Please file an issue at https://github.com/bufbuild/buf/issues/ " +
		"and provide the command you ran"
	if e.cause == nil {
		return message
	}
	return message + ", as well as the following message: " + e.cause.Error()
}

// NewErrorInterceptor returns a CLI interceptor that wraps Buf CLI errors.
func NewErrorInterceptor() appflag.Interceptor {
	return func(next func(context.Context, appflag.Container) error) func(context.Context, appflag.Container) error {
		return func(ctx context.Context, container appflag.Container) error {
			return wrapError(next(ctx, container))
		}
	}
}

// NewModuleRefError is used when the client fails to parse a module ref.
func NewModuleRefError(moduleRef string) error {
	return fmt.Errorf("could not parse %q as a module; please verify this is a valid reference", moduleRef)
}

// NewTooManyEmptyAnswersError is used when the user does not answer a prompt in
// the given number of attempts.
func NewTooManyEmptyAnswersError(attempts int) error {
	return fmt.Errorf("did not receive an answer in %d attempts", attempts)
}

// NewOrganizationNameAlreadyExistsError informs the user that an organization with
// that name already exists.
func NewOrganizationNameAlreadyExistsError(name string) error {
	return fmt.Errorf("an organization named %q already exists", name)
}

// NewRepositoryNameAlreadyExistsError informs the user that a repository
// with that name already exists.
func NewRepositoryNameAlreadyExistsError(name string) error {
	return fmt.Errorf("a repository named %q already exists", name)
}

// NewBranchOrTagNameAlreadyExistsError informs the user that a branch
// or tag with that name already exists.
func NewBranchOrTagNameAlreadyExistsError(name string) error {
	return fmt.Errorf("a branch or tag named %q already exists", name)
}

// NewOrganizationNotFoundError informs the user that an organization with
// that name does not exist.
func NewOrganizationNotFoundError(name string) error {
	return fmt.Errorf(`an organization named %q does not exist, use "buf beta registry organization create" to create one`, name)
}

// NewRepositoryNotFoundError informs the user that a repository with
// that name does not exist.
func NewRepositoryNotFoundError(name string) error {
	return fmt.Errorf(`a repository named %q does not exist, use "buf beta registry repository create" to create one`, name)
}

// NewModuleReferenceNotFoundError informs the user that a module
// reference does not exist.
func NewModuleReferenceNotFoundError(reference bufmoduleref.ModuleReference) error {
	return fmt.Errorf("%q does not exist", reference)
}

// NewTokenNotFoundError informs the user that a token with
// that identifier does not exist.
func NewTokenNotFoundError(tokenID string) error {
	return fmt.Errorf("a token with ID %q does not exist", tokenID)
}

func NewUnimplementedRemoteError(err error, remote string, moduleIdentity string) error {
	return fmt.Errorf("%w. Are you sure %q (derived from module name %q) is a Buf Schema Registry?", err, remote, moduleIdentity)
}

// NewPluginNotFoundError informs the user that a plugin with
// that owner and name does not exist.
func NewPluginNotFoundError(owner string, name string) error {
	return fmt.Errorf("the plugin %s/%s does not exist", owner, name)
}

// NewTemplateNotFoundError informs the user that a template with
// that owner and name does not exist.
func NewTemplateNotFoundError(owner string, name string) error {
	return fmt.Errorf("the template %s/%s does not exist", owner, name)
}

// wrapError is used when a CLI command fails, regardless of its error code.
// Note that this function will wrap the error so that the underlying error
// can be recovered via 'errors.Is'.
func wrapError(err error) error {
	if err == nil || (err.Error() == "" && !rpc.IsError(err)) {
		// If the error is nil or empty and not an rpc error, we return it as-is.
		// This is especially relevant for commands like lint and breaking.
		return err
	}
	rpcCode := rpc.GetErrorCode(err)
	switch {
	case rpcCode == rpc.ErrorCodeUnauthenticated, isEmptyUnknownError(err):
		return errors.New(`Failure: you are not authenticated. Create a new entry in your netrc, using a Buf API Key as the password. For details, visit https://docs.buf.build/bsr/authentication`)
	case rpcCode == rpc.ErrorCodeUnavailable:
		return fmt.Errorf(`Failure: the server hosted at that remote is unavailable: %w`, err)
	}
	return fmt.Errorf("Failure: %w", err)
}

// isEmptyUnknownError returns true if the given
// error is non-nil, but has an empty message
// and an unknown error code.
//
// This is relevant for errors returned by
// envoyauthd when the client does not provide
// an authentication header.
func isEmptyUnknownError(err error) bool {
	if err == nil {
		return false
	}
	return err.Error() == "" && rpc.GetErrorCode(err) == rpc.ErrorCodeUnknown
}
<commit_msg>Update ErrNoConfigFile error message (#1057)<commit_after>// Copyright 2020-2022 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bufcli

import (
	"context"
	"errors"
	"fmt"

	"github.com/bufbuild/buf/private/bufpkg/bufmodule/bufmoduleref"
	"github.com/bufbuild/buf/private/pkg/app"
	"github.com/bufbuild/buf/private/pkg/app/appflag"
	"github.com/bufbuild/buf/private/pkg/rpc"
)

const (
	// ExitCodeFileAnnotation is the exit code used when we print file annotations.
	//
	// We use a different exit code to be able to distinguish user-parsable errors from
	// system errors.
	ExitCodeFileAnnotation = 100
)

var (
	// ErrNoModuleName is used when the user does not specify a module name in their configuration file.
	ErrNoModuleName = errors.New(`please specify a module name in your configuration file with the "name" key`)

	// ErrNoConfigFile is used when the user tries to execute a command without a configuration file.
	ErrNoConfigFile = errors.New(`please define a configuration file in the current directory; you can create one by running "buf mod init"`)

	// ErrFileAnnotation is used when we print file annotations and want to return an error.
	//
	// The app package works on the concept that an error results in a non-zero exit
	// code, and we already print the messages with PrintFileAnnotations, so we do
	// not want to print any additional error message.
	//
	// We also exit with 100 to be able to distinguish user-parsable errors from
	// system errors.
	ErrFileAnnotation = app.NewError(ExitCodeFileAnnotation, "")
)

// errInternal is returned when the user encounters an unexpected internal buf error.
type errInternal struct {
	cause error
}

// NewInternalError represents an internal error encountered by the buf CLI.
// These errors should not happen and therefore warrant a bug report.
func NewInternalError(err error) error {
	if isInternalError(err) {
		return err
	}
	return &errInternal{cause: err}
}

// isInternalError returns whether the error provided, or
// any error wrapped by that error, is an internal error.
func isInternalError(err error) bool {
	asErr := &errInternal{}
	return errors.As(err, &asErr)
}

func (e *errInternal) Error() string {
	message := "it looks like you have found a bug in buf. " +
		"Please file an issue at https://github.com/bufbuild/buf/issues/ " +
		"and provide the command you ran"
	if e.cause == nil {
		return message
	}
	return message + ", as well as the following message: " + e.cause.Error()
}

// NewErrorInterceptor returns a CLI interceptor that wraps Buf CLI errors.
func NewErrorInterceptor() appflag.Interceptor {
	return func(next func(context.Context, appflag.Container) error) func(context.Context, appflag.Container) error {
		return func(ctx context.Context, container appflag.Container) error {
			return wrapError(next(ctx, container))
		}
	}
}

// NewModuleRefError is used when the client fails to parse a module ref.
func NewModuleRefError(moduleRef string) error {
	return fmt.Errorf("could not parse %q as a module; please verify this is a valid reference", moduleRef)
}

// NewTooManyEmptyAnswersError is used when the user does not answer a prompt in
// the given number of attempts.
func NewTooManyEmptyAnswersError(attempts int) error {
	return fmt.Errorf("did not receive an answer in %d attempts", attempts)
}

// NewOrganizationNameAlreadyExistsError informs the user that an organization with
// that name already exists.
func NewOrganizationNameAlreadyExistsError(name string) error {
	return fmt.Errorf("an organization named %q already exists", name)
}

// NewRepositoryNameAlreadyExistsError informs the user that a repository
// with that name already exists.
func NewRepositoryNameAlreadyExistsError(name string) error {
	return fmt.Errorf("a repository named %q already exists", name)
}

// NewBranchOrTagNameAlreadyExistsError informs the user that a branch
// or tag with that name already exists.
func NewBranchOrTagNameAlreadyExistsError(name string) error {
	return fmt.Errorf("a branch or tag named %q already exists", name)
}

// NewOrganizationNotFoundError informs the user that an organization with
// that name does not exist.
func NewOrganizationNotFoundError(name string) error {
	return fmt.Errorf(`an organization named %q does not exist, use "buf beta registry organization create" to create one`, name)
}

// NewRepositoryNotFoundError informs the user that a repository with
// that name does not exist.
func NewRepositoryNotFoundError(name string) error {
	return fmt.Errorf(`a repository named %q does not exist, use "buf beta registry repository create" to create one`, name)
}

// NewModuleReferenceNotFoundError informs the user that a module
// reference does not exist.
func NewModuleReferenceNotFoundError(reference bufmoduleref.ModuleReference) error {
	return fmt.Errorf("%q does not exist", reference)
}

// NewTokenNotFoundError informs the user that a token with
// that identifier does not exist.
func NewTokenNotFoundError(tokenID string) error {
	return fmt.Errorf("a token with ID %q does not exist", tokenID)
}

func NewUnimplementedRemoteError(err error, remote string, moduleIdentity string) error {
	return fmt.Errorf("%w. Are you sure %q (derived from module name %q) is a Buf Schema Registry?", err, remote, moduleIdentity)
}

// NewPluginNotFoundError informs the user that a plugin with
// that owner and name does not exist.
func NewPluginNotFoundError(owner string, name string) error {
	return fmt.Errorf("the plugin %s/%s does not exist", owner, name)
}

// NewTemplateNotFoundError informs the user that a template with
// that owner and name does not exist.
func NewTemplateNotFoundError(owner string, name string) error {
	return fmt.Errorf("the template %s/%s does not exist", owner, name)
}

// wrapError is used when a CLI command fails, regardless of its error code.
// Note that this function will wrap the error so that the underlying error
// can be recovered via 'errors.Is'.
func wrapError(err error) error {
	if err == nil || (err.Error() == "" && !rpc.IsError(err)) {
		// If the error is nil or empty and not an rpc error, we return it as-is.
		// This is especially relevant for commands like lint and breaking.
		return err
	}
	rpcCode := rpc.GetErrorCode(err)
	switch {
	case rpcCode == rpc.ErrorCodeUnauthenticated, isEmptyUnknownError(err):
		return errors.New(`Failure: you are not authenticated. Create a new entry in your netrc, using a Buf API Key as the password. For details, visit https://docs.buf.build/bsr/authentication`)
	case rpcCode == rpc.ErrorCodeUnavailable:
		return fmt.Errorf(`Failure: the server hosted at that remote is unavailable: %w`, err)
	}
	return fmt.Errorf("Failure: %w", err)
}

// isEmptyUnknownError returns true if the given
// error is non-nil, but has an empty message
// and an unknown error code.
//
// This is relevant for errors returned by
// envoyauthd when the client does not provide
// an authentication header.
func isEmptyUnknownError(err error) bool {
	if err == nil {
		return false
	}
	return err.Error() == "" && rpc.GetErrorCode(err) == rpc.ErrorCodeUnknown
}
<|endoftext|>


<commit_before>package main

import (
    "jvmgo/rtda"
    "jvmgo/instructions"
)

// todo
func loop(thread *rtda.Thread) {
    bcr := instructions.NewBytecodeReader()
    for !thread.IsStackEmpty() {
        pc := thread.PC()
        frame := thread.CurrentFrame()
        code := frame.Method().Code()

        bcr.SetPC(pc)
        bcr.SetCode(code)
        inst := instructions.Decode(bcr)
        frame.SetNextPC(bcr.PC())

        inst.Execute(thread)
        thread.SetPC(frame.NextPC())
    }
}
<commit_msg>code refactor<commit_after>package main

import (
    "jvmgo/rtda"
    "jvmgo/instructions"
)

// todo
func loop(thread *rtda.Thread) {
    bcr := instructions.NewBytecodeReader()
    for !thread.IsStackEmpty() {
        frame := thread.CurrentFrame()

        bcr.SetPC(thread.PC())
        bcr.SetCode(frame.Method().Code())
        inst := instructions.Decode(bcr)
        frame.SetNextPC(bcr.PC())

        inst.Execute(thread)
        thread.SetPC(frame.NextPC())
    }
}
<|endoftext|>


<commit_before>package classfile

/*
RuntimeVisibleAnnotations_attribute {
    u2         attribute_name_index;
    u4         attribute_length;
    u2         num_annotations;
    annotation annotations[num_annotations];
}
*/
type RuntimeVisibleAnnotationsAttribute struct {
    annotations []*Annotation
}
func (self *RuntimeVisibleAnnotationsAttribute) readInfo(reader *ClassReader, cp *ConstantPool) {
    numAnnotations := reader.readUint16()
    self.annotations = make([]*Annotation, numAnnotations)
    for i := range self.annotations {
        self.annotations[i] = readAnnotation(reader)
    }
}

/*
annotation {
    u2 type_index;
    u2 num_element_value_pairs;
    {   u2            element_name_index;
        element_value value;
    } element_value_pairs[num_element_value_pairs];
}
*/
type Annotation struct {
    typeIndex           uint16
    elementValuePairs   []*ElementValuePair
}
func readAnnotation(reader *ClassReader) (*Annotation) {
    typeIndex := reader.readUint16()
    numElementValuePairs := reader.readUint16()
    elementValuePairs := make([]*ElementValuePair, numElementValuePairs)
    return &Annotation{typeIndex, elementValuePairs}
}

type ElementValuePair struct {
    //elementNameIndex    uint16
    //value               ElementValue
}

// element_value {
//     u1 tag;
//     union {
//         u2 const_value_index;

//         {   u2 type_name_index;
//             u2 const_name_index;
//         } enum_const_value;

//         u2 class_info_index;

//         annotation annotation_value;

//         {   u2            num_values;
//             element_value values[num_values];
//         } array_value;
//     } value;
// }

// type ElementValue struct {
//     private U1 tag;
//     // tag=B,C,D,F,I,J,S,Z,s
//     private U2CpIndex constValueIndex;
//     // tag=e
//     private EnumConstValue enumConstValue;
//     // tag=c
//     private U2CpIndex classInfoIndex;
//     // tag=@
//     private Annotation annotationValue;
//     // tag=[
//     private ArrayValue arrayValue;
// }
// type EnumConstValue extends ClassComponent {
//     private U2CpIndex typeNameIndex;
//     private U2CpIndex constNameIndex;
// }
// type ArrayValue extends  ClassComponent {
//     private U2 numValues;
//     private Table<ElementValue> values;
// }

    // @Override
    // protected void readContent(ClassReader reader) {
    //     numValues = reader.readU2();
    //     values = reader.readTable(ElementValue.class, numValues);
    // }

    // @Override
    // protected void readContent(ClassReader reader) {
    //     tag = reader.readU1();
    //     tag.setDesc((char) tag.getValue());
    //     switch (tag.getValue()) {
    //         case 'B':
    //         case 'C':
    //         case 'D':
    //         case 'F':
    //         case 'I':
    //         case 'J':
    //         case 'S':
    //         case 'Z':
    //         case 's':
    //             constValueIndex = reader.readU2CpIndex();
    //             break;
    //         case 'e':
    //             enumConstValue = new EnumConstValue();
    //             enumConstValue.read(reader);
    //             break;
    //         case 'c':
    //             classInfoIndex = reader.readU2CpIndex();
    //             break;
    //         case '@':
    //             annotationValue = new AnnotationInfo();
    //             annotationValue.read(reader);
    //             break;
    //         case '[':
    //             arrayValue = new ArrayValue();
    //             arrayValue.read(reader);
    //             break;
    //         default: throw new FileParseException("Invalid element_value tag: " + tag.getDesc());
    //     }
    // }<commit_msg>read annotations from class file<commit_after>package classfile

/*
RuntimeVisibleAnnotations_attribute {
    u2         attribute_name_index;
    u4         attribute_length;
    u2         num_annotations;
    annotation annotations[num_annotations];
}
*/
type RuntimeVisibleAnnotationsAttribute struct {
    annotations []*Annotation
}
func (self *RuntimeVisibleAnnotationsAttribute) readInfo(reader *ClassReader, cp *ConstantPool) {
    numAnnotations := reader.readUint16()
    self.annotations = make([]*Annotation, numAnnotations)
    for i := range self.annotations {
        self.annotations[i] = readAnnotation(reader)
    }
}

/*
annotation {
    u2 type_index;
    u2 num_element_value_pairs;
    {   u2            element_name_index;
        element_value value;
    } element_value_pairs[num_element_value_pairs];
}
*/
type Annotation struct {
    typeIndex           uint16
    elementValuePairs   []*ElementValuePair
}
func readAnnotation(reader *ClassReader) (*Annotation) {
    typeIndex := reader.readUint16()
    numElementValuePairs := reader.readUint16()
    elementValuePairs := make([]*ElementValuePair, numElementValuePairs)
    for i := range elementValuePairs {
        elementValuePairs[i] = readElementValuePair(reader)
    }
    return &Annotation{typeIndex, elementValuePairs}
}

type ElementValuePair struct {
    elementNameIndex    uint16
    value               *ElementValue
}
func readElementValuePair(reader *ClassReader) (*ElementValuePair) {
    elementNameIndex := reader.readUint16()
    value := readElementValue(reader)
    return &ElementValuePair{elementNameIndex, value}
}

/*
element_value {
    u1 tag;
    union {
        u2 const_value_index;

        {   u2 type_name_index;
            u2 const_name_index;
        } enum_const_value;

        u2 class_info_index;

        annotation annotation_value;

        {   u2            num_values;
            element_value values[num_values];
        } array_value;
    } value;
}
*/
type ElementValue struct {
    // private U1 tag;
    // // tag=B,C,D,F,I,J,S,Z,s
    // private U2CpIndex constValueIndex;
    // // tag=e
    // private EnumConstValue enumConstValue;
    // // tag=c
    // private U2CpIndex classInfoIndex;
    // // tag=@
    // private Annotation annotationValue;
    // // tag=[
    // private ArrayValue arrayValue;
}
func readElementValue(reader *ClassReader) (*ElementValue) {
    return nil
}

// type EnumConstValue extends ClassComponent {
//     private U2CpIndex typeNameIndex;
//     private U2CpIndex constNameIndex;
// }
// type ArrayValue extends  ClassComponent {
//     private U2 numValues;
//     private Table<ElementValue> values;
// }

    // @Override
    // protected void readContent(ClassReader reader) {
    //     numValues = reader.readU2();
    //     values = reader.readTable(ElementValue.class, numValues);
    // }

    // @Override
    // protected void readContent(ClassReader reader) {
    //     tag = reader.readU1();
    //     tag.setDesc((char) tag.getValue());
    //     switch (tag.getValue()) {
    //         case 'B':
    //         case 'C':
    //         case 'D':
    //         case 'F':
    //         case 'I':
    //         case 'J':
    //         case 'S':
    //         case 'Z':
    //         case 's':
    //             constValueIndex = reader.readU2CpIndex();
    //             break;
    //         case 'e':
    //             enumConstValue = new EnumConstValue();
    //             enumConstValue.read(reader);
    //             break;
    //         case 'c':
    //             classInfoIndex = reader.readU2CpIndex();
    //             break;
    //         case '@':
    //             annotationValue = new AnnotationInfo();
    //             annotationValue.read(reader);
    //             break;
    //         case '[':
    //             arrayValue = new ArrayValue();
    //             arrayValue.read(reader);
    //             break;
    //         default: throw new FileParseException("Invalid element_value tag: " + tag.getDesc());
    //     }
    // }<|endoftext|>


<commit_before>package ltsvlog_test

import (
	"errors"
	"io/ioutil"
	"os"
	"testing"
	"time"

	"github.com/hnakamur/ltsvlog"
)

func BenchmarkInfo(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		logger.Info().String("msg", "hello").String("key1", "value1").Log()
	}
}

func BenchmarkInfoString(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		logger.Info().String("msg", "hello").String("key1", "value1").Log()
	}
}

func BenchmarkErrWithStackAndUTCTime(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	run := func() error {
		return ltsvlog.Err(errors.New("some error")).Stack("stack").UTCTime("errtime", time.Now()).String("key1", "value1")
	}

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		err = run()
		logger.Err(err)
	}
}

func BenchmarkErrWithStack(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	run := func() error {
		return ltsvlog.Err(errors.New("some error")).Stack("stack").String("key1", "value1")
	}

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		err = run()
		logger.Err(err)
	}
}

func BenchmarkErrWithUTCTime(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	run := func() error {
		return ltsvlog.Err(errors.New("some error")).UTCTime("errtime", time.Now()).String("key1", "value1")
	}

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		err = run()
		logger.Err(err)
	}
}
<commit_msg>Delete BenchmarkInfoString since it is the same as BenchmarkInfo<commit_after>package ltsvlog_test

import (
	"errors"
	"io/ioutil"
	"os"
	"testing"
	"time"

	"github.com/hnakamur/ltsvlog"
)

func BenchmarkInfo(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		logger.Info().String("msg", "hello").String("key1", "value1").Log()
	}
}

func BenchmarkErrWithStackAndUTCTime(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	run := func() error {
		return ltsvlog.Err(errors.New("some error")).Stack("stack").UTCTime("errtime", time.Now()).String("key1", "value1")
	}

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		err = run()
		logger.Err(err)
	}
}

func BenchmarkErrWithStack(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	run := func() error {
		return ltsvlog.Err(errors.New("some error")).Stack("stack").String("key1", "value1")
	}

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		err = run()
		logger.Err(err)
	}
}

func BenchmarkErrWithUTCTime(b *testing.B) {
	tmpfile, err := ioutil.TempFile("", "benchmark")
	if err != nil {
		b.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	run := func() error {
		return ltsvlog.Err(errors.New("some error")).UTCTime("errtime", time.Now()).String("key1", "value1")
	}

	logger := ltsvlog.NewLTSVLogger(tmpfile, false)
	for i := 0; i < b.N; i++ {
		err = run()
		logger.Err(err)
	}
}
<|endoftext|>


<commit_before>// Package proctl provides functions for attaching to and manipulating
// a process during the debug session.
package proctl

import (
	"bytes"
	"debug/elf"
	"debug/gosym"
	"encoding/binary"
	"fmt"
	"os"
	"syscall"

	"github.com/derekparker/dbg/dwarf/frame"
	"github.com/derekparker/dbg/dwarf/line"
)

// Struct representing a debugged process. Holds onto pid, register values,
// process struct and process state.
type DebuggedProcess struct {
	Pid          int
	Regs         *syscall.PtraceRegs
	Process      *os.Process
	ProcessState *os.ProcessState
	Executable   *elf.File
	Symbols      []elf.Symbol
	GoSymTable   *gosym.Table
	FrameEntries frame.FrameDescriptionEntries
	DebugLine    *line.DebugLineInfo
	BreakPoints  map[string]*BreakPoint
}

// Represents a single breakpoint. Stores information on the break
// point including the byte of data that originally was stored at that
// address.
type BreakPoint struct {
	FunctionName string
	File         string
	Line         int
	Addr         uint64
	OriginalData []byte
}

type BreakPointExistsError struct {
	file string
	line int
	addr uintptr
}

func (bpe BreakPointExistsError) Error() string {
	return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
}

// Returns a new DebuggedProcess struct with sensible defaults.
func NewDebugProcess(pid int) (*DebuggedProcess, error) {
	proc, err := os.FindProcess(pid)
	if err != nil {
		return nil, err
	}

	err = syscall.PtraceAttach(pid)
	if err != nil {
		return nil, err
	}

	ps, err := proc.Wait()
	if err != nil {
		return nil, err
	}

	debuggedProc := DebuggedProcess{
		Pid:          pid,
		Regs:         &syscall.PtraceRegs{},
		Process:      proc,
		ProcessState: ps,
		BreakPoints:  make(map[string]*BreakPoint),
	}

	err = debuggedProc.LoadInformation()
	if err != nil {
		return nil, err
	}

	return &debuggedProc, nil
}

// Finds the executable from /proc/<pid>/exe and then
// uses that to parse the Go symbol table.
func (dbp *DebuggedProcess) LoadInformation() error {
	err := dbp.findExecutable()
	if err != nil {
		return err
	}

	err = dbp.parseDebugFrame()
	if err != nil {
		return err
	}

	err = dbp.parseDebugLine()
	if err != nil {
		return err
	}

	err = dbp.obtainGoSymbols()
	if err != nil {
		return err
	}

	return nil
}

// Obtains register values from the debugged process.
func (dbp *DebuggedProcess) Registers() (*syscall.PtraceRegs, error) {
	err := syscall.PtraceGetRegs(dbp.Pid, dbp.Regs)
	if err != nil {
		return nil, fmt.Errorf("Registers():", err)
	}

	return dbp.Regs, nil
}

// Sets a breakpoint in the running process.
func (dbp *DebuggedProcess) Break(addr uintptr) (*BreakPoint, error) {
	var (
		int3         = []byte{0xCC}
		f, l, fn     = dbp.GoSymTable.PCToLine(uint64(addr))
		originalData = make([]byte, 1)
	)

	_, err := syscall.PtracePeekData(dbp.Pid, addr, originalData)
	if err != nil {
		return nil, err
	}

	if bytes.Equal(originalData, int3) {
		return nil, BreakPointExistsError{f, l, addr}
	}

	_, err = syscall.PtracePokeData(dbp.Pid, addr, int3)
	if err != nil {
		return nil, err
	}

	breakpoint := &BreakPoint{
		FunctionName: fn.Name,
		File:         f,
		Line:         l,
		Addr:         uint64(addr),
		OriginalData: originalData,
	}

	fname := fmt.Sprintf("%s:%d", f, l)
	dbp.BreakPoints[fname] = breakpoint

	return breakpoint, nil
}

// Clears a breakpoint.
func (dbp *DebuggedProcess) Clear(pc uint64) (*BreakPoint, error) {
	bp, ok := dbp.PCtoBP(pc)
	if !ok {
		return nil, fmt.Errorf("No breakpoint currently set for %s", bp.FunctionName)
	}

	_, err := syscall.PtracePokeData(dbp.Pid, uintptr(bp.Addr), bp.OriginalData)
	if err != nil {
		return nil, err
	}

	delete(dbp.BreakPoints, fmt.Sprintf("%s:%d", bp.File, bp.Line))

	return bp, nil
}

// Steps through process.
func (dbp *DebuggedProcess) Step() (err error) {
	regs, err := dbp.Registers()
	if err != nil {
		return err
	}

	bp, ok := dbp.PCtoBP(regs.PC() - 1)
	if ok {
		// Clear the breakpoint so that we can continue execution.
		_, err = dbp.Clear(bp.Addr)
		if err != nil {
			return err
		}

		// Reset program counter to our restored instruction.
		regs.SetPC(bp.Addr)
		err = syscall.PtraceSetRegs(dbp.Pid, regs)
		if err != nil {
			return err
		}

		// Restore breakpoint now that we have passed it.
		defer func() {
			_, err = dbp.Break(uintptr(bp.Addr))
		}()
	}

	err = dbp.handleResult(syscall.PtraceSingleStep(dbp.Pid))
	if err != nil {
		return fmt.Errorf("step failed: ", err.Error())
	}

	return nil
}

// Step over function calls.
func (dbp *DebuggedProcess) Next() error {
	addrs := make([]uint64, 0, 3)
	pc, err := dbp.CurrentPC()
	if err != nil {
		return err
	}

	pc-- // account for breakpoint instruction

	fde, err := dbp.FrameEntries.FDEForPC(pc)
	if err != nil {
		return err
	}

	loc := dbp.DebugLine.NextLocAfterPC(pc)
	if !fde.AddressRange.Cover(loc.Address) {
		// Next line is outside current frame, use return addr.
		addr := dbp.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc))
		loc = dbp.DebugLine.LocationInfoForPC(addr)
	}
	addrs = append(addrs, loc.Address)

	if loc.Delta < 0 {
		// We are likely in a loop, set breakpoints at entry and exit.
		entry := dbp.DebugLine.LoopEntryLocation(loc.Line)
		exit := dbp.DebugLine.LoopExitLocation(pc)
		addrs = append(addrs, entry.Address, exit.Address)
	}

	for _, addr := range addrs {
		if _, err := dbp.Break(uintptr(addr)); err != nil {
			if _, ok := err.(BreakPointExistsError); !ok {
				return err
			}
		}
	}

	return dbp.Continue()
}

// Continue process until next breakpoint.
func (dbp *DebuggedProcess) Continue() error {
	// Stepping first will ensure we are able to continue
	// past a breakpoint if that's currently where we are stopped.
	err := dbp.Step()
	if err != nil {
		return err
	}

	return dbp.handleResult(syscall.PtraceCont(dbp.Pid, 0))
}

func (dbp *DebuggedProcess) CurrentPC() (uint64, error) {
	regs, err := dbp.Registers()
	if err != nil {
		return 0, err
	}

	return regs.Rip, nil
}

func (dbp *DebuggedProcess) handleResult(err error) error {
	if err != nil {
		return err
	}

	ps, err := dbp.Process.Wait()
	if err != nil {
		return err
	}

	dbp.ProcessState = ps

	return nil
}

func (dbp *DebuggedProcess) findExecutable() error {
	procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)

	f, err := os.Open(procpath)
	if err != nil {
		return err
	}

	elffile, err := elf.NewFile(f)
	if err != nil {
		return err
	}

	dbp.Executable = elffile

	return nil
}

func (dbp *DebuggedProcess) parseDebugLine() error {
	debugLine, err := dbp.Executable.Section(".debug_line").Data()
	if err != nil {
		return err
	}

	dbp.DebugLine = line.Parse(debugLine)
	return nil
}

func (dbp *DebuggedProcess) parseDebugFrame() error {
	debugFrame, err := dbp.Executable.Section(".debug_frame").Data()
	if err != nil {
		return err
	}

	dbp.FrameEntries = frame.Parse(debugFrame)
	return nil
}

func (dbp *DebuggedProcess) obtainGoSymbols() error {
	symdat, err := dbp.Executable.Section(".gosymtab").Data()
	if err != nil {
		return err
	}

	pclndat, err := dbp.Executable.Section(".gopclntab").Data()
	if err != nil {
		return err
	}

	pcln := gosym.NewLineTable(pclndat, dbp.Executable.Section(".text").Addr)
	tab, err := gosym.NewTable(symdat, pcln)
	if err != nil {
		return err
	}

	dbp.GoSymTable = tab

	return nil
}

// Converts a program counter value into a breakpoint, if one was set
// for the function containing pc.
func (dbp *DebuggedProcess) PCtoBP(pc uint64) (*BreakPoint, bool) {
	f, l, _ := dbp.GoSymTable.PCToLine(pc)
	bp, ok := dbp.BreakPoints[fmt.Sprintf("%s:%d", f, l)]
	return bp, ok
}

// Takes an offset from RSP and returns the address of the
// instruction the currect function is going to return to.
func (dbp *DebuggedProcess) ReturnAddressFromOffset(offset int64) uint64 {
	regs, err := dbp.Registers()
	if err != nil {
		panic("Could not obtain register values")
	}

	retaddr := int64(regs.Rsp) + offset
	data := make([]byte, 8)
	syscall.PtracePeekText(dbp.Pid, uintptr(retaddr), data)
	return binary.LittleEndian.Uint64(data)
}
<commit_msg>Improve documentation of LoadInformation()<commit_after>// Package proctl provides functions for attaching to and manipulating
// a process during the debug session.
package proctl

import (
	"bytes"
	"debug/elf"
	"debug/gosym"
	"encoding/binary"
	"fmt"
	"os"
	"syscall"

	"github.com/derekparker/dbg/dwarf/frame"
	"github.com/derekparker/dbg/dwarf/line"
)

// Struct representing a debugged process. Holds onto pid, register values,
// process struct and process state.
type DebuggedProcess struct {
	Pid          int
	Regs         *syscall.PtraceRegs
	Process      *os.Process
	ProcessState *os.ProcessState
	Executable   *elf.File
	Symbols      []elf.Symbol
	GoSymTable   *gosym.Table
	FrameEntries frame.FrameDescriptionEntries
	DebugLine    *line.DebugLineInfo
	BreakPoints  map[string]*BreakPoint
}

// Represents a single breakpoint. Stores information on the break
// point including the byte of data that originally was stored at that
// address.
type BreakPoint struct {
	FunctionName string
	File         string
	Line         int
	Addr         uint64
	OriginalData []byte
}

type BreakPointExistsError struct {
	file string
	line int
	addr uintptr
}

func (bpe BreakPointExistsError) Error() string {
	return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
}

// Returns a new DebuggedProcess struct with sensible defaults.
func NewDebugProcess(pid int) (*DebuggedProcess, error) {
	proc, err := os.FindProcess(pid)
	if err != nil {
		return nil, err
	}

	err = syscall.PtraceAttach(pid)
	if err != nil {
		return nil, err
	}

	ps, err := proc.Wait()
	if err != nil {
		return nil, err
	}

	debuggedProc := DebuggedProcess{
		Pid:          pid,
		Regs:         &syscall.PtraceRegs{},
		Process:      proc,
		ProcessState: ps,
		BreakPoints:  make(map[string]*BreakPoint),
	}

	err = debuggedProc.LoadInformation()
	if err != nil {
		return nil, err
	}

	return &debuggedProc, nil
}

// Finds the executable from /proc/<pid>/exe and then
// uses that to parse the following information:
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Go symbol table.
func (dbp *DebuggedProcess) LoadInformation() error {
	err := dbp.findExecutable()
	if err != nil {
		return err
	}

	err = dbp.parseDebugFrame()
	if err != nil {
		return err
	}

	err = dbp.parseDebugLine()
	if err != nil {
		return err
	}

	err = dbp.obtainGoSymbols()
	if err != nil {
		return err
	}

	return nil
}

// Obtains register values from the debugged process.
func (dbp *DebuggedProcess) Registers() (*syscall.PtraceRegs, error) {
	err := syscall.PtraceGetRegs(dbp.Pid, dbp.Regs)
	if err != nil {
		return nil, fmt.Errorf("Registers():", err)
	}

	return dbp.Regs, nil
}

// Sets a breakpoint in the running process.
func (dbp *DebuggedProcess) Break(addr uintptr) (*BreakPoint, error) {
	var (
		int3         = []byte{0xCC}
		f, l, fn     = dbp.GoSymTable.PCToLine(uint64(addr))
		originalData = make([]byte, 1)
	)

	_, err := syscall.PtracePeekData(dbp.Pid, addr, originalData)
	if err != nil {
		return nil, err
	}

	if bytes.Equal(originalData, int3) {
		return nil, BreakPointExistsError{f, l, addr}
	}

	_, err = syscall.PtracePokeData(dbp.Pid, addr, int3)
	if err != nil {
		return nil, err
	}

	breakpoint := &BreakPoint{
		FunctionName: fn.Name,
		File:         f,
		Line:         l,
		Addr:         uint64(addr),
		OriginalData: originalData,
	}

	fname := fmt.Sprintf("%s:%d", f, l)
	dbp.BreakPoints[fname] = breakpoint

	return breakpoint, nil
}

// Clears a breakpoint.
func (dbp *DebuggedProcess) Clear(pc uint64) (*BreakPoint, error) {
	bp, ok := dbp.PCtoBP(pc)
	if !ok {
		return nil, fmt.Errorf("No breakpoint currently set for %s", bp.FunctionName)
	}

	_, err := syscall.PtracePokeData(dbp.Pid, uintptr(bp.Addr), bp.OriginalData)
	if err != nil {
		return nil, err
	}

	delete(dbp.BreakPoints, fmt.Sprintf("%s:%d", bp.File, bp.Line))

	return bp, nil
}

// Steps through process.
func (dbp *DebuggedProcess) Step() (err error) {
	regs, err := dbp.Registers()
	if err != nil {
		return err
	}

	bp, ok := dbp.PCtoBP(regs.PC() - 1)
	if ok {
		// Clear the breakpoint so that we can continue execution.
		_, err = dbp.Clear(bp.Addr)
		if err != nil {
			return err
		}

		// Reset program counter to our restored instruction.
		regs.SetPC(bp.Addr)
		err = syscall.PtraceSetRegs(dbp.Pid, regs)
		if err != nil {
			return err
		}

		// Restore breakpoint now that we have passed it.
		defer func() {
			_, err = dbp.Break(uintptr(bp.Addr))
		}()
	}

	err = dbp.handleResult(syscall.PtraceSingleStep(dbp.Pid))
	if err != nil {
		return fmt.Errorf("step failed: ", err.Error())
	}

	return nil
}

// Step over function calls.
func (dbp *DebuggedProcess) Next() error {
	addrs := make([]uint64, 0, 3)
	pc, err := dbp.CurrentPC()
	if err != nil {
		return err
	}

	pc-- // account for breakpoint instruction

	fde, err := dbp.FrameEntries.FDEForPC(pc)
	if err != nil {
		return err
	}

	loc := dbp.DebugLine.NextLocAfterPC(pc)
	if !fde.AddressRange.Cover(loc.Address) {
		// Next line is outside current frame, use return addr.
		addr := dbp.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc))
		loc = dbp.DebugLine.LocationInfoForPC(addr)
	}
	addrs = append(addrs, loc.Address)

	if loc.Delta < 0 {
		// We are likely in a loop, set breakpoints at entry and exit.
		entry := dbp.DebugLine.LoopEntryLocation(loc.Line)
		exit := dbp.DebugLine.LoopExitLocation(pc)
		addrs = append(addrs, entry.Address, exit.Address)
	}

	for _, addr := range addrs {
		if _, err := dbp.Break(uintptr(addr)); err != nil {
			if _, ok := err.(BreakPointExistsError); !ok {
				return err
			}
		}
	}

	return dbp.Continue()
}

// Continue process until next breakpoint.
func (dbp *DebuggedProcess) Continue() error {
	// Stepping first will ensure we are able to continue
	// past a breakpoint if that's currently where we are stopped.
	err := dbp.Step()
	if err != nil {
		return err
	}

	return dbp.handleResult(syscall.PtraceCont(dbp.Pid, 0))
}

func (dbp *DebuggedProcess) CurrentPC() (uint64, error) {
	regs, err := dbp.Registers()
	if err != nil {
		return 0, err
	}

	return regs.Rip, nil
}

func (dbp *DebuggedProcess) handleResult(err error) error {
	if err != nil {
		return err
	}

	ps, err := dbp.Process.Wait()
	if err != nil {
		return err
	}

	dbp.ProcessState = ps

	return nil
}

func (dbp *DebuggedProcess) findExecutable() error {
	procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)

	f, err := os.Open(procpath)
	if err != nil {
		return err
	}

	elffile, err := elf.NewFile(f)
	if err != nil {
		return err
	}

	dbp.Executable = elffile

	return nil
}

func (dbp *DebuggedProcess) parseDebugLine() error {
	debugLine, err := dbp.Executable.Section(".debug_line").Data()
	if err != nil {
		return err
	}

	dbp.DebugLine = line.Parse(debugLine)
	return nil
}

func (dbp *DebuggedProcess) parseDebugFrame() error {
	debugFrame, err := dbp.Executable.Section(".debug_frame").Data()
	if err != nil {
		return err
	}

	dbp.FrameEntries = frame.Parse(debugFrame)
	return nil
}

func (dbp *DebuggedProcess) obtainGoSymbols() error {
	symdat, err := dbp.Executable.Section(".gosymtab").Data()
	if err != nil {
		return err
	}

	pclndat, err := dbp.Executable.Section(".gopclntab").Data()
	if err != nil {
		return err
	}

	pcln := gosym.NewLineTable(pclndat, dbp.Executable.Section(".text").Addr)
	tab, err := gosym.NewTable(symdat, pcln)
	if err != nil {
		return err
	}

	dbp.GoSymTable = tab

	return nil
}

// Converts a program counter value into a breakpoint, if one was set
// for the function containing pc.
func (dbp *DebuggedProcess) PCtoBP(pc uint64) (*BreakPoint, bool) {
	f, l, _ := dbp.GoSymTable.PCToLine(pc)
	bp, ok := dbp.BreakPoints[fmt.Sprintf("%s:%d", f, l)]
	return bp, ok
}

// Takes an offset from RSP and returns the address of the
// instruction the currect function is going to return to.
func (dbp *DebuggedProcess) ReturnAddressFromOffset(offset int64) uint64 {
	regs, err := dbp.Registers()
	if err != nil {
		panic("Could not obtain register values")
	}

	retaddr := int64(regs.Rsp) + offset
	data := make([]byte, 8)
	syscall.PtracePeekText(dbp.Pid, uintptr(retaddr), data)
	return binary.LittleEndian.Uint64(data)
}
<|endoftext|>


<commit_before>// docker_tls_args: find the docker daemon's tls args
// This reimplements pgrep, because pgrep will only look in /proc, but not in
// $PROCFS.
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

func main() {
	procRoot := os.Getenv("PROCFS")
	if procRoot == "" {
		procRoot = "/proc"
	}

	dirEntries, err := ioutil.ReadDir(procRoot)
	checkErr(err)

	for _, dirEntry := range dirEntries {
		dirName := dirEntry.Name()
		if _, err := strconv.Atoi(dirName); err != nil {
			continue
		}

		if comm, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "comm")); err != nil || string(comm) != "docker\n" {
			continue
		}

		cmdline, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "cmdline"))
		if err != nil {
			continue
		}

		isDaemon := false
		tlsArgs := []string{}
		args := bytes.Split(cmdline, []byte{'\000'})
		for i := 0; i < len(args); i++ {
			arg := string(args[i])
			switch {
			case arg == "-d":
				isDaemon = true
				break
			case arg == "--tls", arg == "--tlsverify":
				tlsArgs = append(tlsArgs, arg)
			case strings.HasPrefix(arg, "--tls"):
				tlsArgs = append(tlsArgs, arg)
				if len(args) > i+1 &&
					!strings.Contains(arg, "=") &&
					!strings.HasPrefix(string(args[i+1]), "-") {
					tlsArgs = append(tlsArgs, string(args[i+1]))
					i++
				}
			}
		}
		if !isDaemon {
			continue
		}

		fmt.Println(strings.Join(tlsArgs, " "))
		os.Exit(0)
	}

	os.Exit(1)
}

func checkErr(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>docker_tls_args should look for '-d' or 'daemon' to handle docker 1.8 change<commit_after>// docker_tls_args: find the docker daemon's tls args
// This reimplements pgrep, because pgrep will only look in /proc, but not in
// $PROCFS.
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

func main() {
	procRoot := os.Getenv("PROCFS")
	if procRoot == "" {
		procRoot = "/proc"
	}

	dirEntries, err := ioutil.ReadDir(procRoot)
	checkErr(err)

	for _, dirEntry := range dirEntries {
		dirName := dirEntry.Name()
		if _, err := strconv.Atoi(dirName); err != nil {
			continue
		}

		if comm, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "comm")); err != nil || string(comm) != "docker\n" {
			continue
		}

		cmdline, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "cmdline"))
		if err != nil {
			continue
		}

		isDaemon := false
		tlsArgs := []string{}
		args := bytes.Split(cmdline, []byte{'\000'})
		for i := 0; i < len(args); i++ {
			arg := string(args[i])
			switch {
			case arg == "-d" || arg == "daemon":
				isDaemon = true
				break
			case arg == "--tls", arg == "--tlsverify":
				tlsArgs = append(tlsArgs, arg)
			case strings.HasPrefix(arg, "--tls"):
				tlsArgs = append(tlsArgs, arg)
				if len(args) > i+1 &&
					!strings.Contains(arg, "=") &&
					!strings.HasPrefix(string(args[i+1]), "-") {
					tlsArgs = append(tlsArgs, string(args[i+1]))
					i++
				}
			}
		}
		if !isDaemon {
			continue
		}

		fmt.Println(strings.Join(tlsArgs, " "))
		os.Exit(0)
	}

	os.Exit(1)
}

func checkErr(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package auth

import (
	"encoding/json"
	"fmt"
	"github.com/timeredbull/tsuru/api/repository/gitosis"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/errors"
	"io"
	"io/ioutil"
	"launchpad.net/mgo/bson"
	"net/http"
	"strings"
)

func CreateUser(w http.ResponseWriter, r *http.Request) error {
	var u User
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &u)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()}
	}
	err = u.Create()
	if err == nil {
		w.WriteHeader(http.StatusCreated)
		return nil
	}

	if u.Get() == nil {
		err = &errors.Http{Code: http.StatusConflict, Message: "This email is already registered"}
	}

	return err
}

func Login(w http.ResponseWriter, r *http.Request) error {
	var pass map[string]string
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &pass)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: "Invalid JSON"}
	}

	password, ok := pass["password"]
	if !ok {
		msg := "You must provide a password to login"
		return &errors.Http{Code: http.StatusBadRequest, Message: msg}
	}

	u := User{Email: r.URL.Query().Get(":email")}
	err = u.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "User not found"}
	}

	if u.Login(password) {
		t, _ := u.CreateToken()
		fmt.Fprintf(w, `{"token":"%s"}`, t.Token)
		return nil
	}

	msg := "Authentication failed, wrong password"
	return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
}

func CreateTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	var params map[string]string
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &params)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()}
	}
	name, ok := params["name"]
	if !ok {
		msg := "You must provide the team name"
		return &errors.Http{Code: http.StatusBadRequest, Message: msg}
	}
	team := &Team{Name: name, Users: []*User{u}}
	err = db.Session.Teams().Insert(team)
	if err != nil && strings.Contains(err.Error(), "duplicate key error") {
		return &errors.Http{Code: http.StatusConflict, Message: "This team already exists"}
	}
	return nil
}

func AddUserToTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	team, user := new(Team), new(User)
	selector := bson.M{"name": r.URL.Query().Get(":team")}
	err := db.Session.Teams().Find(selector).One(team)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if !team.ContainsUser(u) {
		msg := fmt.Sprintf("You are not authorized to add new users to the team %s", team.Name)
		return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
	}
	err = db.Session.Users().Find(bson.M{"email": r.URL.Query().Get(":user")}).One(user)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "User not found"}
	}
	err = team.AddUser(user)
	if err != nil {
		return &errors.Http{Code: http.StatusConflict, Message: err.Error()}
	}
	return db.Session.Teams().Update(selector, team)
}

func RemoveUserFromTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	team := new(Team)
	selector := bson.M{"name": r.URL.Query().Get(":team")}
	err := db.Session.Teams().Find(selector).One(team)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if !team.ContainsUser(u) {
		msg := fmt.Sprintf("You are not authorized to remove a member from the team %s", team.Name)
		return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
	}
	if len(team.Users) == 1 {
		msg := "You can not remove this user from this team, because it is the last user within the team, and a team can not be orphaned"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	user := User{Email: r.URL.Query().Get(":user")}
	err = team.RemoveUser(&user)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: err.Error()}
	}
	return db.Session.Teams().Update(selector, team)
}

func getKeyFromBody(b io.Reader) (string, error) {
	var body map[string]string
	content, err := ioutil.ReadAll(b)
	if err != nil {
		return "", err
	}
	err = json.Unmarshal(content, &body)
	if err != nil {
		return "", &errors.Http{Code: http.StatusBadRequest, Message: "Invalid JSON"}
	}
	key, ok := body["key"]
	if !ok || key == "" {
		return "", &errors.Http{Code: http.StatusBadRequest, Message: "Missing key"}
	}
	return key, nil
}

func addKeyToUser(content string, u *User) error {
	key := Key{Content: content}
	if u.hasKey(key) {
		return &errors.Http{Code: http.StatusConflict, Message: "User has this key already"}
	}
	filename, err := gitosis.BuildAndStoreKeyFile(u.Email, content)
	if err != nil {
		return err
	}
	key.Name = strings.Replace(filename, ".pub", "", -1)
	err = u.addKey(key)
	return db.Session.Users().Update(bson.M{"email": u.Email}, u)
}

// AddKeyToUser adds a key to a user.
//
// This function is just an http wrapper around addKeyToUser. The latter function
// exists to be used in other places in the package without the http stuff (request and
// response).
func AddKeyToUser(w http.ResponseWriter, r *http.Request, u *User) error {
	key, err := getKeyFromBody(r.Body)
	if err != nil {
		return err
	}
	return addKeyToUser(key, u)
}

func removeKeyFromUser(content string, u *User) error {
	key, index := u.findKey(Key{Content: content})
	if index < 0 {
		return &errors.Http{Code: http.StatusNotFound, Message: "User does not have this key"}
	}
	u.removeKey(key)
	err := db.Session.Users().Update(bson.M{"email": u.Email}, u)
	if err != nil {
		return err
	}
	return gitosis.DeleteKeyFile(key.Name + ".pub")
}

// RemoveKeyFromUser removes a key from a user.
//
// This function is just an http wrapper around removeKeyFromUser. The latter function
// exists to be used in other places in the package without the http stuff (request and
// response).
func RemoveKeyFromUser(w http.ResponseWriter, r *http.Request, u *User) error {
	key, err := getKeyFromBody(r.Body)
	if err != nil {
		return err
	}
	return removeKeyFromUser(key, u)
}
<commit_msg>api/auth: refactoring CreateTeam handler<commit_after>package auth

import (
	"encoding/json"
	"fmt"
	"github.com/timeredbull/tsuru/api/repository/gitosis"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/errors"
	"io"
	"io/ioutil"
	"launchpad.net/mgo/bson"
	"net/http"
	"strings"
)

func CreateUser(w http.ResponseWriter, r *http.Request) error {
	var u User
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &u)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()}
	}
	err = u.Create()
	if err == nil {
		w.WriteHeader(http.StatusCreated)
		return nil
	}

	if u.Get() == nil {
		err = &errors.Http{Code: http.StatusConflict, Message: "This email is already registered"}
	}

	return err
}

func Login(w http.ResponseWriter, r *http.Request) error {
	var pass map[string]string
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &pass)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: "Invalid JSON"}
	}

	password, ok := pass["password"]
	if !ok {
		msg := "You must provide a password to login"
		return &errors.Http{Code: http.StatusBadRequest, Message: msg}
	}

	u := User{Email: r.URL.Query().Get(":email")}
	err = u.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "User not found"}
	}

	if u.Login(password) {
		t, _ := u.CreateToken()
		fmt.Fprintf(w, `{"token":"%s"}`, t.Token)
		return nil
	}

	msg := "Authentication failed, wrong password"
	return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
}

func createTeam(name string, u *User) error {
	team := &Team{Name: name, Users: []*User{u}}
	err := db.Session.Teams().Insert(team)
	if err != nil && strings.Contains(err.Error(), "duplicate key error") {
		return &errors.Http{Code: http.StatusConflict, Message: "This team already exists"}
	}
	return nil
}

func CreateTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	var params map[string]string
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, &params)
	if err != nil {
		return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()}
	}
	name, ok := params["name"]
	if !ok {
		msg := "You must provide the team name"
		return &errors.Http{Code: http.StatusBadRequest, Message: msg}
	}
	return createTeam(name, u)
}

func AddUserToTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	team, user := new(Team), new(User)
	selector := bson.M{"name": r.URL.Query().Get(":team")}
	err := db.Session.Teams().Find(selector).One(team)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if !team.ContainsUser(u) {
		msg := fmt.Sprintf("You are not authorized to add new users to the team %s", team.Name)
		return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
	}
	err = db.Session.Users().Find(bson.M{"email": r.URL.Query().Get(":user")}).One(user)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "User not found"}
	}
	err = team.AddUser(user)
	if err != nil {
		return &errors.Http{Code: http.StatusConflict, Message: err.Error()}
	}
	return db.Session.Teams().Update(selector, team)
}

func RemoveUserFromTeam(w http.ResponseWriter, r *http.Request, u *User) error {
	team := new(Team)
	selector := bson.M{"name": r.URL.Query().Get(":team")}
	err := db.Session.Teams().Find(selector).One(team)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if !team.ContainsUser(u) {
		msg := fmt.Sprintf("You are not authorized to remove a member from the team %s", team.Name)
		return &errors.Http{Code: http.StatusUnauthorized, Message: msg}
	}
	if len(team.Users) == 1 {
		msg := "You can not remove this user from this team, because it is the last user within the team, and a team can not be orphaned"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	user := User{Email: r.URL.Query().Get(":user")}
	err = team.RemoveUser(&user)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: err.Error()}
	}
	return db.Session.Teams().Update(selector, team)
}

func getKeyFromBody(b io.Reader) (string, error) {
	var body map[string]string
	content, err := ioutil.ReadAll(b)
	if err != nil {
		return "", err
	}
	err = json.Unmarshal(content, &body)
	if err != nil {
		return "", &errors.Http{Code: http.StatusBadRequest, Message: "Invalid JSON"}
	}
	key, ok := body["key"]
	if !ok || key == "" {
		return "", &errors.Http{Code: http.StatusBadRequest, Message: "Missing key"}
	}
	return key, nil
}

func addKeyToUser(content string, u *User) error {
	key := Key{Content: content}
	if u.hasKey(key) {
		return &errors.Http{Code: http.StatusConflict, Message: "User has this key already"}
	}
	filename, err := gitosis.BuildAndStoreKeyFile(u.Email, content)
	if err != nil {
		return err
	}
	key.Name = strings.Replace(filename, ".pub", "", -1)
	err = u.addKey(key)
	return db.Session.Users().Update(bson.M{"email": u.Email}, u)
}

// AddKeyToUser adds a key to a user.
//
// This function is just an http wrapper around addKeyToUser. The latter function
// exists to be used in other places in the package without the http stuff (request and
// response).
func AddKeyToUser(w http.ResponseWriter, r *http.Request, u *User) error {
	key, err := getKeyFromBody(r.Body)
	if err != nil {
		return err
	}
	return addKeyToUser(key, u)
}

func removeKeyFromUser(content string, u *User) error {
	key, index := u.findKey(Key{Content: content})
	if index < 0 {
		return &errors.Http{Code: http.StatusNotFound, Message: "User does not have this key"}
	}
	u.removeKey(key)
	err := db.Session.Users().Update(bson.M{"email": u.Email}, u)
	if err != nil {
		return err
	}
	return gitosis.DeleteKeyFile(key.Name + ".pub")
}

// RemoveKeyFromUser removes a key from a user.
//
// This function is just an http wrapper around removeKeyFromUser. The latter function
// exists to be used in other places in the package without the http stuff (request and
// response).
func RemoveKeyFromUser(w http.ResponseWriter, r *http.Request, u *User) error {
	key, err := getKeyFromBody(r.Body)
	if err != nil {
		return err
	}
	return removeKeyFromUser(key, u)
}
<|endoftext|>


<commit_before>package builds

type Status string

const (
	StatusStarted   Status = "started"
	StatusSucceeded Status = "succeeded"
	StatusFailed    Status = "failed"
	StatusErrored   Status = "errored"
)

type Build struct {
	Guid string `json:"guid"`

	Privileged bool `json:"privileged"`

	Config Config `json:"config"`

	LogsURL  string `json:"logs_url"`
	Callback string `json:"callback"`

	Inputs  []Input  `json:"inputs"`
	Outputs []Output `json:"outputs"`

	Status Status `json:"status"`
}

type Config struct {
	Image string `json:"image"`

	Env    [][2]string `json:"env"`
	Script string      `json:"script"`
}

type Input struct {
	Type   string `json:"type"`
	Source Source `json:"source,omitempty"`

	ConfigPath      string `json:"configPath"`
	DestinationPath string `json:"destinationPath"`
}

type Source []byte

func (source Source) MarshalJSON() ([]byte, error) {
	return []byte(source), nil
}

func (source *Source) UnmarshalJSON(data []byte) error {
	*source = append((*source)[0:0], data...)
	return nil
}

type Output struct {
	Type   string `json:"type"`
	Params Params `json:"params,omitempty"`

	Source Source `json:"source,omitempty"`

	SourcePath string `json:"sourcePath"`
}

type Params []byte

func (params Params) MarshalJSON() ([]byte, error) {
	return []byte(params), nil
}

func (params *Params) UnmarshalJSON(data []byte) error {
	*params = append((*params)[0:0], data...)
	return nil
}
<commit_msg>add name to input/output<commit_after>package builds

type Status string

const (
	StatusStarted   Status = "started"
	StatusSucceeded Status = "succeeded"
	StatusFailed    Status = "failed"
	StatusErrored   Status = "errored"
)

type Build struct {
	Guid string `json:"guid"`

	Privileged bool `json:"privileged"`

	Config Config `json:"config"`

	LogsURL  string `json:"logs_url"`
	Callback string `json:"callback"`

	Inputs  []Input  `json:"inputs"`
	Outputs []Output `json:"outputs"`

	Status Status `json:"status"`
}

type Config struct {
	Image string `json:"image"`

	Env    [][2]string `json:"env"`
	Script string      `json:"script"`
}

type Input struct {
	Name string `json:"name"`

	Type   string `json:"type"`
	Source Source `json:"source,omitempty"`

	ConfigPath      string `json:"configPath"`
	DestinationPath string `json:"destinationPath"`
}

type Source []byte

func (source Source) MarshalJSON() ([]byte, error) {
	return []byte(source), nil
}

func (source *Source) UnmarshalJSON(data []byte) error {
	*source = append((*source)[0:0], data...)
	return nil
}

type Output struct {
	Name string `json:"name"`

	Type   string `json:"type"`
	Params Params `json:"params,omitempty"`

	Source Source `json:"source,omitempty"`

	SourcePath string `json:"sourcePath"`
}

type Params []byte

func (params Params) MarshalJSON() ([]byte, error) {
	return []byte(params), nil
}

func (params *Params) UnmarshalJSON(data []byte) error {
	*params = append((*params)[0:0], data...)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	kingpin "gopkg.in/alecthomas/kingpin.v2"

	"github.com/boltdb/bolt"
	homedir "github.com/mitchellh/go-homedir"
)

var (
	aliasDescription string
)

func init() {
	action.Arg("name", "name for the alias action").
		Required().
		StringVar(&request.Alias)

	action.Arg("method", "the method to use for the action").
		Required().
		StringVar(&request.Method)

	action.Arg("path", "the path to perform the action on").
		Required().
		StringVar(&request.Path)

	action.Arg("description", "a short description of the alias, will be used in generated help documentation").
		StringVar(&aliasDescription)

	settings.Flags(action)

	dir, err := homedir.Dir()
	if err != nil {
		panic(err)
	}
	dbFile = fmt.Sprintf("%s/%s", dir, ".rest.db")

	// parse aliases and make them part of the command, aliases will show up on help,
	// aliases can be called directly as a subcommand of 'rest'
	if err := setAliases(); err != nil {
		panic(err)
	}

}

func setAliases() error {
	var err error
	db, err = bolt.Open(dbFile, 0600, nil)
	if err != nil {
		return err
	}
	defer db.Close()

	return db.View(func(tx *bolt.Tx) error {
		// if the services haven't been initialised there will be nothing here, so all
		// aliases should be ignored and failure to find a particular bucket is safe.
		info := tx.Bucket([]byte("info"))
		if info == nil {
			return nil
		}

		current := info.Get([]byte("current"))
		if current == nil {
			return nil
		}

		services := tx.Bucket([]byte("services"))
		if services == nil {
			return nil
		}

		sb := services.Bucket(current)
		if sb == nil {
			return nil
		}

		aliases := sb.Bucket([]byte("aliases"))
		if aliases == nil {
			return nil
		}

		return aliases.ForEach(func(k, _ []byte) error {
			b := aliases.Bucket(k)
			a := kingpin.Command(string(k), string(b.Get([]byte("description"))))

			// attached data arguments to post and put methods
			method := string(b.Get([]byte("method")))
			if method == "post" || method == "put" {
				a.Arg("data", "data to send in the request").
					StringVar(&request.Data)
			}

			requestFlags(a)

			return nil
		})
	})
}

func addAlias() error {
	return db.Update(func(tx *bolt.Tx) error {
		sb, err := request.ServiceBucket(tx)
		if err != nil {
			return err
		}

		ab, err := sb.CreateBucketIfNotExists([]byte("aliases"))
		if err != nil {
			return err
		}

		a, err := ab.CreateBucketIfNotExists([]byte(request.Alias))
		if err != nil {
			return err
		}

		if err := a.Put([]byte("method"), []byte(request.Method)); err != nil {
			return err
		}

		if err := a.Put([]byte("path"), []byte(request.Path)); err != nil {
			return err
		}

		if aliasDescription != "" {
			if err := a.Put([]byte("description"), []byte(aliasDescription)); err != nil {
				return err
			}

		}

		if err := settings.Write(a); err != nil {
			return err
		}

		return nil
	})
}

func Perform(name string) {
	request.Alias = name
	err := db.View(func(tx *bolt.Tx) error {
		sb, err := request.ServiceBucket(tx)
		if err != nil {
			return err
		}

		b := sb.Bucket([]byte("aliases"))
		if b == nil {
			return ErrNoAliases
		}

		a := b.Bucket([]byte(name))
		if a == nil {
			return ErrNoAlias{Alias: name}
		}

		method := string(a.Get([]byte("method")))
		path := string(a.Get([]byte("path")))

		request.Method = method
		request.Path = path

		return nil
	})

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	Do(request.Method)
}
<commit_msg>Make alias description a flag to prevent excessive rewriting it<commit_after>package main

import (
	"fmt"
	"os"

	kingpin "gopkg.in/alecthomas/kingpin.v2"

	"github.com/boltdb/bolt"
	homedir "github.com/mitchellh/go-homedir"
)

var (
	aliasDescription string
)

func init() {
	action.Arg("name", "name for the alias action").
		Required().
		StringVar(&request.Alias)

	action.Arg("method", "the method to use for the action").
		Required().
		StringVar(&request.Method)

	action.Arg("path", "the path to perform the action on").
		Required().
		StringVar(&request.Path)

	action.Flag("description", "a short description of the alias, will be used in generated help documentation").
		StringVar(&aliasDescription)

	settings.Flags(action)

	dir, err := homedir.Dir()
	if err != nil {
		panic(err)
	}
	dbFile = fmt.Sprintf("%s/%s", dir, ".rest.db")

	// parse aliases and make them part of the command, aliases will show up on help,
	// aliases can be called directly as a subcommand of 'rest'
	if err := setAliases(); err != nil {
		panic(err)
	}

}

func setAliases() error {
	var err error
	db, err = bolt.Open(dbFile, 0600, nil)
	if err != nil {
		return err
	}
	defer db.Close()

	return db.View(func(tx *bolt.Tx) error {
		// if the services haven't been initialised there will be nothing here, so all
		// aliases should be ignored and failure to find a particular bucket is safe.
		info := tx.Bucket([]byte("info"))
		if info == nil {
			return nil
		}

		current := info.Get([]byte("current"))
		if current == nil {
			return nil
		}

		services := tx.Bucket([]byte("services"))
		if services == nil {
			return nil
		}

		sb := services.Bucket(current)
		if sb == nil {
			return nil
		}

		aliases := sb.Bucket([]byte("aliases"))
		if aliases == nil {
			return nil
		}

		return aliases.ForEach(func(k, _ []byte) error {
			b := aliases.Bucket(k)
			a := kingpin.Command(string(k), string(b.Get([]byte("description"))))

			// attached data arguments to post and put methods
			method := string(b.Get([]byte("method")))
			if method == "post" || method == "put" {
				a.Arg("data", "data to send in the request").
					StringVar(&request.Data)
			}

			requestFlags(a)

			return nil
		})
	})
}

func addAlias() error {
	return db.Update(func(tx *bolt.Tx) error {
		sb, err := request.ServiceBucket(tx)
		if err != nil {
			return err
		}

		ab, err := sb.CreateBucketIfNotExists([]byte("aliases"))
		if err != nil {
			return err
		}

		a, err := ab.CreateBucketIfNotExists([]byte(request.Alias))
		if err != nil {
			return err
		}

		if err := a.Put([]byte("method"), []byte(request.Method)); err != nil {
			return err
		}

		if err := a.Put([]byte("path"), []byte(request.Path)); err != nil {
			return err
		}

		if aliasDescription != "" {
			if err := a.Put([]byte("description"), []byte(aliasDescription)); err != nil {
				return err
			}

		}

		if err := settings.Write(a); err != nil {
			return err
		}

		return nil
	})
}

func Perform(name string) {
	request.Alias = name
	err := db.View(func(tx *bolt.Tx) error {
		sb, err := request.ServiceBucket(tx)
		if err != nil {
			return err
		}

		b := sb.Bucket([]byte("aliases"))
		if b == nil {
			return ErrNoAliases
		}

		a := b.Bucket([]byte(name))
		if a == nil {
			return ErrNoAlias{Alias: name}
		}

		method := string(a.Get([]byte("method")))
		path := string(a.Get([]byte("path")))

		request.Method = method
		request.Path = path

		return nil
	})

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	Do(request.Method)
}
<|endoftext|>


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

package blob

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"

	"golang.org/x/net/context"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcsutil"
)

// A key placed in GCS object metadata by GCSStore containing the hex SHA-1
// expected for the object contents. This is of course redundant with the
// object name; we use it as a paranoid check against GCS returning the
// metadata or contents for the wrong object.
const metadataKey_SHA1 = "comeback_sha1"

// A key placed in GCS object metadata by GCSStore containing the CRC32C
// checksum expected for the object contents. If GCS reports a different
// checksum or returns contents with a different checksum, we know something
// screwy has happened.
//
// See here for more info: https://github.com/jacobsa/comeback/issues/18
const metadataKey_CRC32C = "comeback_crc32c"

// A key placed in GCS object metadata by GCSStore containing the hex MD5 sum
// expected for the object contents. If GCS reports a different MD5 sum or
// returns contents with a different MD5 sum, we know something screwy has
// happened.
//
// See here for more info: https://github.com/jacobsa/comeback/issues/18
const metadataKey_MD5 = "comeback_md5"

// Return a blob store that stores blobs in the supplied GCS bucket. GCS object
// names look like:
//
//     <prefix><score>
//
// where <score> is the result of calling Score.Hex.
//
// The blob store trusts that it has full ownership of this portion of the
// bucket's namespace -- if a score name exists, then it points to the correct
// data.
//
// The returned store does not support Flush or Contains; these methods must
// not be called.
func NewGCSStore(
	bucket gcs.Bucket,
	prefix string) (store *GCSStore) {
	store = &GCSStore{
		bucket:     bucket,
		namePrefix: prefix,
	}

	return
}

type GCSStore struct {
	bucket     gcs.Bucket
	namePrefix string
}

// Parse and verify the internal consistency of the supplied object record in
// the same manner that a GCSStore configured with the supplied object name
// prefix would. Return the score of the blob that the object contains.
func ParseObjectRecord(
	o *gcs.Object,
	namePrefix string) (score Score, err error) {
	// Is the name of the appropriate form?
	if !strings.HasPrefix(o.Name, namePrefix) {
		err = fmt.Errorf("Unexpected object name: %q", o.Name)
		return
	}

	// Parse the hex score.
	hexScore := strings.TrimPrefix(o.Name, namePrefix)
	score, err = ParseHexScore(hexScore)
	if err != nil {
		err = fmt.Errorf(
			"Unexpected hex score %q for object %q: %v",
			hexScore,
			o.Name,
			err)
		return
	}

	// We expect the hex score to match the hex SHA-1 in the metadata.
	hexSHA1, ok := o.Metadata[metadataKey_SHA1]
	if !ok {
		err = fmt.Errorf(
			"Object %q is missing metadata key %q",
			o.Name,
			metadataKey_SHA1)
		return
	}

	if hexSHA1 != hexScore {
		err = fmt.Errorf(
			"Score/SHA-1 metadata mismatch for object %q: %q",
			o.Name,
			hexSHA1)
		return
	}

	// We expect the hex CRC32C in the object metadata to match what GCS says the
	// object's checksum is.
	hexCRC32C, ok := o.Metadata[metadataKey_CRC32C]
	if !ok {
		err = fmt.Errorf(
			"Object %q is missing metadata key %q",
			o.Name,
			metadataKey_CRC32C)
		return
	}

	crc32Uint64, err := strconv.ParseUint(hexCRC32C, 0, 32)
	if err != nil {
		err = fmt.Errorf(
			"Object %q has invalid hex CRC32C %q: %v",
			o.Name,
			hexCRC32C,
			err)
		return
	}

	if uint32(crc32Uint64) != o.CRC32C {
		err = fmt.Errorf(
			"CRC32C mismatch for object %q: %#08x vs. %#08x",
			o.Name,
			crc32Uint64,
			o.CRC32C)
		return
	}

	// We expect the hex MD5 in the object metadata to match what GCS says the
	// object's MD5 sum is.
	hexMD5, ok := o.Metadata[metadataKey_MD5]
	if !ok {
		err = fmt.Errorf(
			"Object %q is missing metadata key %q",
			o.Name,
			metadataKey_MD5)
		return
	}

	if hex.DecodedLen(len(hexMD5)) != md5.Size {
		err = fmt.Errorf(
			"Object %q has weird hex MD5 metadata: %q",
			o.Name,
			hexMD5)
		return
	}

	var md5 [md5.Size]byte
	_, err = hex.Decode(md5[:], []byte(hexMD5))
	if err != nil {
		err = fmt.Errorf(
			"Object %q has invalid hex MD5 in metadata: %q",
			o.Name,
			hexMD5)
		return
	}

	if md5 != o.MD5 {
		err = fmt.Errorf(
			"MD5 mismatch for object %q: %s vs. %s",
			o.Name,
			hex.EncodeToString(md5[:]),
			hex.EncodeToString(o.MD5[:]))
		return
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (s *GCSStore) makeName(score Score) (name string) {
	name = s.namePrefix + score.Hex()
	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (s *GCSStore) Store(blob []byte) (score Score, err error) {
	// Compute a score and an object name.
	score = ComputeScore(blob)
	name := s.makeName(score)

	// Create the object.
	crc32c := *gcsutil.CRC32C(blob)
	md5 := *gcsutil.MD5(blob)
	sha1 := sha1.Sum(blob)

	req := &gcs.CreateObjectRequest{
		Name:     name,
		Contents: bytes.NewReader(blob),
		CRC32C:   &crc32c,
		MD5:      &md5,

		Metadata: map[string]string{
			metadataKey_SHA1:   hex.EncodeToString(sha1[:]),
			metadataKey_CRC32C: fmt.Sprintf("%#08x", crc32c),
			metadataKey_MD5:    hex.EncodeToString(md5[:]),
		},
	}

	o, err := s.bucket.CreateObject(context.Background(), req)
	if err != nil {
		err = fmt.Errorf("CreateObject: %v", err)
		return
	}

	// Paranoid check: what we get back from GCS should match what we put in.
	if o.CRC32C != crc32c {
		panic(fmt.Sprintf(
			"CRC32C mismatch for object %q: 0x%08xv vs. 0x%08x",
			o.Name,
			o.CRC32C,
			crc32c))
	}

	if o.MD5 != md5 {
		panic(fmt.Sprintf(
			"MD5 mismatch for object %q: %s vs. %s",
			o.Name,
			hex.EncodeToString(o.MD5[:]),
			hex.EncodeToString(md5[:])))
	}

	return
}

func (s *GCSStore) Flush() (err error) {
	panic("GCSStore.Flush not supported; wiring code bug?")
}

func (s *GCSStore) Contains(score Score) (b bool) {
	panic("GCSStore.Contains not supported; wiring code bug?")
}

func (s *GCSStore) Load(score Score) (blob []byte, err error) {
	// Create a ReadCloser.
	req := &gcs.ReadObjectRequest{
		Name: s.makeName(score),
	}

	rc, err := s.bucket.NewReader(context.Background(), req)
	if err != nil {
		err = fmt.Errorf("NewReader: %v", err)
		return
	}

	// Read from it.
	blob, err = ioutil.ReadAll(rc)
	if err != nil {
		rc.Close()
		err = fmt.Errorf("ReadAll: %v", err)
		return
	}

	// Close it.
	err = rc.Close()
	if err != nil {
		err = fmt.Errorf("Close: %v", err)
		return
	}

	return
}

// List all of the blobs that are known to be durable in the bucket.
func (s *GCSStore) List() (scores []Score, err error) {
	req := &gcs.ListObjectsRequest{
		Prefix: s.namePrefix,
	}

	// List repeatedly until we're done.
	for {
		// Call the bucket.
		var listing *gcs.Listing
		listing, err = s.bucket.ListObjects(context.Background(), req)
		if err != nil {
			err = fmt.Errorf("ListObjects: %v", err)
			return
		}

		// Process results.
		for _, o := range listing.Objects {
			// Special case: listing "blobs/*" includes "blobs/" itself, which we
			// allow to exist for convenience of use with e.g. gcsfuse.
			if o.Name == s.namePrefix {
				continue
			}

			// Parse and verify the record.
			var score Score
			score, err = ParseObjectRecord(o, s.namePrefix)
			if err != nil {
				return
			}

			// Save the score.
			scores = append(scores, score)
		}

		// Continue?
		if listing.ContinuationToken == "" {
			break
		}

		req.ContinuationToken = listing.ContinuationToken
	}

	return
}
<commit_msg>Declared ListBlobObjects.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package blob

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"

	"golang.org/x/net/context"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcsutil"
)

// A key placed in GCS object metadata by GCSStore containing the hex SHA-1
// expected for the object contents. This is of course redundant with the
// object name; we use it as a paranoid check against GCS returning the
// metadata or contents for the wrong object.
const metadataKey_SHA1 = "comeback_sha1"

// A key placed in GCS object metadata by GCSStore containing the CRC32C
// checksum expected for the object contents. If GCS reports a different
// checksum or returns contents with a different checksum, we know something
// screwy has happened.
//
// See here for more info: https://github.com/jacobsa/comeback/issues/18
const metadataKey_CRC32C = "comeback_crc32c"

// A key placed in GCS object metadata by GCSStore containing the hex MD5 sum
// expected for the object contents. If GCS reports a different MD5 sum or
// returns contents with a different MD5 sum, we know something screwy has
// happened.
//
// See here for more info: https://github.com/jacobsa/comeback/issues/18
const metadataKey_MD5 = "comeback_md5"

// Return a blob store that stores blobs in the supplied GCS bucket. GCS object
// names look like:
//
//     <prefix><score>
//
// where <score> is the result of calling Score.Hex.
//
// The blob store trusts that it has full ownership of this portion of the
// bucket's namespace -- if a score name exists, then it points to the correct
// data.
//
// The returned store does not support Flush or Contains; these methods must
// not be called.
func NewGCSStore(
	bucket gcs.Bucket,
	prefix string) (store *GCSStore) {
	store = &GCSStore{
		bucket:     bucket,
		namePrefix: prefix,
	}

	return
}

type GCSStore struct {
	bucket     gcs.Bucket
	namePrefix string
}

// Parse and verify the internal consistency of the supplied object record in
// the same manner that a GCSStore configured with the supplied object name
// prefix would. Return the score of the blob that the object contains.
func ParseObjectRecord(
	o *gcs.Object,
	namePrefix string) (score Score, err error) {
	// Is the name of the appropriate form?
	if !strings.HasPrefix(o.Name, namePrefix) {
		err = fmt.Errorf("Unexpected object name: %q", o.Name)
		return
	}

	// Parse the hex score.
	hexScore := strings.TrimPrefix(o.Name, namePrefix)
	score, err = ParseHexScore(hexScore)
	if err != nil {
		err = fmt.Errorf(
			"Unexpected hex score %q for object %q: %v",
			hexScore,
			o.Name,
			err)
		return
	}

	// We expect the hex score to match the hex SHA-1 in the metadata.
	hexSHA1, ok := o.Metadata[metadataKey_SHA1]
	if !ok {
		err = fmt.Errorf(
			"Object %q is missing metadata key %q",
			o.Name,
			metadataKey_SHA1)
		return
	}

	if hexSHA1 != hexScore {
		err = fmt.Errorf(
			"Score/SHA-1 metadata mismatch for object %q: %q",
			o.Name,
			hexSHA1)
		return
	}

	// We expect the hex CRC32C in the object metadata to match what GCS says the
	// object's checksum is.
	hexCRC32C, ok := o.Metadata[metadataKey_CRC32C]
	if !ok {
		err = fmt.Errorf(
			"Object %q is missing metadata key %q",
			o.Name,
			metadataKey_CRC32C)
		return
	}

	crc32Uint64, err := strconv.ParseUint(hexCRC32C, 0, 32)
	if err != nil {
		err = fmt.Errorf(
			"Object %q has invalid hex CRC32C %q: %v",
			o.Name,
			hexCRC32C,
			err)
		return
	}

	if uint32(crc32Uint64) != o.CRC32C {
		err = fmt.Errorf(
			"CRC32C mismatch for object %q: %#08x vs. %#08x",
			o.Name,
			crc32Uint64,
			o.CRC32C)
		return
	}

	// We expect the hex MD5 in the object metadata to match what GCS says the
	// object's MD5 sum is.
	hexMD5, ok := o.Metadata[metadataKey_MD5]
	if !ok {
		err = fmt.Errorf(
			"Object %q is missing metadata key %q",
			o.Name,
			metadataKey_MD5)
		return
	}

	if hex.DecodedLen(len(hexMD5)) != md5.Size {
		err = fmt.Errorf(
			"Object %q has weird hex MD5 metadata: %q",
			o.Name,
			hexMD5)
		return
	}

	var md5 [md5.Size]byte
	_, err = hex.Decode(md5[:], []byte(hexMD5))
	if err != nil {
		err = fmt.Errorf(
			"Object %q has invalid hex MD5 in metadata: %q",
			o.Name,
			hexMD5)
		return
	}

	if md5 != o.MD5 {
		err = fmt.Errorf(
			"MD5 mismatch for object %q: %s vs. %s",
			o.Name,
			hex.EncodeToString(md5[:]),
			hex.EncodeToString(o.MD5[:]))
		return
	}

	return
}

// Write object records for all of the blob objects in the supplied bucket into
// the given channel, without closing it. The order of records is undefined.
// The caller will likely want to call ParseObjectRecord for each record.
func ListBlobObjects(
	ctx context.Context,
	bucket gcs.Bucket,
	objects chan<- *gcs.Object) (err error)

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (s *GCSStore) makeName(score Score) (name string) {
	name = s.namePrefix + score.Hex()
	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (s *GCSStore) Store(blob []byte) (score Score, err error) {
	// Compute a score and an object name.
	score = ComputeScore(blob)
	name := s.makeName(score)

	// Create the object.
	crc32c := *gcsutil.CRC32C(blob)
	md5 := *gcsutil.MD5(blob)
	sha1 := sha1.Sum(blob)

	req := &gcs.CreateObjectRequest{
		Name:     name,
		Contents: bytes.NewReader(blob),
		CRC32C:   &crc32c,
		MD5:      &md5,

		Metadata: map[string]string{
			metadataKey_SHA1:   hex.EncodeToString(sha1[:]),
			metadataKey_CRC32C: fmt.Sprintf("%#08x", crc32c),
			metadataKey_MD5:    hex.EncodeToString(md5[:]),
		},
	}

	o, err := s.bucket.CreateObject(context.Background(), req)
	if err != nil {
		err = fmt.Errorf("CreateObject: %v", err)
		return
	}

	// Paranoid check: what we get back from GCS should match what we put in.
	if o.CRC32C != crc32c {
		panic(fmt.Sprintf(
			"CRC32C mismatch for object %q: 0x%08xv vs. 0x%08x",
			o.Name,
			o.CRC32C,
			crc32c))
	}

	if o.MD5 != md5 {
		panic(fmt.Sprintf(
			"MD5 mismatch for object %q: %s vs. %s",
			o.Name,
			hex.EncodeToString(o.MD5[:]),
			hex.EncodeToString(md5[:])))
	}

	return
}

func (s *GCSStore) Flush() (err error) {
	panic("GCSStore.Flush not supported; wiring code bug?")
}

func (s *GCSStore) Contains(score Score) (b bool) {
	panic("GCSStore.Contains not supported; wiring code bug?")
}

func (s *GCSStore) Load(score Score) (blob []byte, err error) {
	// Create a ReadCloser.
	req := &gcs.ReadObjectRequest{
		Name: s.makeName(score),
	}

	rc, err := s.bucket.NewReader(context.Background(), req)
	if err != nil {
		err = fmt.Errorf("NewReader: %v", err)
		return
	}

	// Read from it.
	blob, err = ioutil.ReadAll(rc)
	if err != nil {
		rc.Close()
		err = fmt.Errorf("ReadAll: %v", err)
		return
	}

	// Close it.
	err = rc.Close()
	if err != nil {
		err = fmt.Errorf("Close: %v", err)
		return
	}

	return
}

// List all of the blobs that are known to be durable in the bucket.
func (s *GCSStore) List() (scores []Score, err error) {
	req := &gcs.ListObjectsRequest{
		Prefix: s.namePrefix,
	}

	// List repeatedly until we're done.
	for {
		// Call the bucket.
		var listing *gcs.Listing
		listing, err = s.bucket.ListObjects(context.Background(), req)
		if err != nil {
			err = fmt.Errorf("ListObjects: %v", err)
			return
		}

		// Process results.
		for _, o := range listing.Objects {
			// Special case: listing "blobs/*" includes "blobs/" itself, which we
			// allow to exist for convenience of use with e.g. gcsfuse.
			if o.Name == s.namePrefix {
				continue
			}

			// Parse and verify the record.
			var score Score
			score, err = ParseObjectRecord(o, s.namePrefix)
			if err != nil {
				return
			}

			// Save the score.
			scores = append(scores, score)
		}

		// Continue?
		if listing.ContinuationToken == "" {
			break
		}

		req.ContinuationToken = listing.ContinuationToken
	}

	return
}
<|endoftext|>