<commit_before>package client

import (
	"errors"
	"io"
	"sync"
	"sync/atomic"

	"github.com/joushou/qp"
)

var (
	// ErrTagInUse indicates that a tag is already being used for still active
	// request.
	ErrTagInUse = errors.New("tag already in use")

	// ErrNoSuchTag indicates that the tag in a response from a server was not
	// known to be associated with a pending request.
	ErrNoSuchTag = errors.New("tag does not exist")

	// ErrInvalidResponse indicates that the response type did not make sense
	// for the request.
	ErrInvalidResponse = errors.New("invalid response")

	// ErrTagPoolDepleted indicates that all possible tags are currently in
	// use.
	ErrTagPoolDepleted = errors.New("tag pool depleted")

	// ErrUnexpectedTagPoolDepleted indicates that all possible tags are
	// currently in use, but that this should not have been the case.
	ErrUnexpectedTagPoolDepleted = errors.New("unexpected tag pool depletion")

	// ErrStopped indicate that the client was stopped.
	ErrStopped = errors.New("stopped")
)

// Transport implements a 9P client exposing a blocking rpc-like Send call,
// while still processing responses out-of-order. A blocking Send can be
// unblocked by using Ditch.
//
// Apart from managing available tags, matching them on response and
// encoding/decoding messages, RawClient does not concern itself with any
// protocol details.
type Transport struct {
	encoder *qp.Encoder
	decoder *qp.Decoder

	// error stores any run-loop errors.
	error     error
	errorLock sync.Mutex
	errorCnt  uint32

	// queue stores a response channel per message tag.
	queue     map[qp.Tag]chan qp.Message
	queueLock sync.RWMutex
	nextTag   qp.Tag

	// msgsize is the maximum set message size.
	msgsize uint32
}

func (c *Transport) die(err error) error {
	c.errorLock.Lock()
	defer c.errorLock.Unlock()
	if c.error == nil {
		c.error = err
	}
	atomic.AddUint32(&c.errorCnt, 1)
	return c.error
}

// getChannel retrieves the response channel for a given tag.
func (c *Transport) getChannel(t qp.Tag) chan qp.Message {
	c.queueLock.RLock()
	defer c.queueLock.RUnlock()

	if m, exists := c.queue[t]; exists {
		return m
	}

	return nil
}

// killChannel closes a response channel and removes it from the map.
func (c *Transport) killChannel(t qp.Tag) error {
	c.queueLock.Lock()
	defer c.queueLock.Unlock()
	ch, exists := c.queue[t]
	if !exists {
		return ErrNoSuchTag
	}
	ch <- nil
	close(ch)
	delete(c.queue, t)
	return nil
}

// received is the callback for messages decoded from the io.ReadWriter.
func (c *Transport) received(m qp.Message) error {
	c.queueLock.Lock()
	defer c.queueLock.Unlock()
	t := m.GetTag()
	if ch, ok := c.queue[t]; ok {
		ch <- m
		close(ch)
		delete(c.queue, t)
		return nil
	}
	return ErrNoSuchTag
}

// Tag allocates and retrieves a tag. The tag MUST be used or ditched
// afterwards, in order not to leak tags.
func (c *Transport) Tag() (qp.Tag, error) {
	c.queueLock.Lock()
	defer c.queueLock.Unlock()

	// No need to loop 0xFFFF times to figure out that the thing is full.
	if len(c.queue) > 0xFFFF {
		// No tags available.
		return qp.NOTAG, ErrTagPoolDepleted
	} else if len(c.queue) == 0xFFFF {
		if _, exists := c.queue[qp.NOTAG]; !exists {
			// The only tag available is qp.NOTAG.
			return qp.NOTAG, ErrTagPoolDepleted
		}
	}

	// There is a tag available *somewhere*. Find it.
	var t qp.Tag
	var exists bool
	for i := 0; i < 0xFFFF; i++ {
		t = c.nextTag
		c.nextTag++
		if c.nextTag == qp.NOTAG {
			c.nextTag = 0
		}

		if _, exists = c.queue[t]; exists {
			c.queue[t] = make(chan qp.Message, 1)
			return t, nil
		}
	}

	return qp.NOTAG, ErrUnexpectedTagPoolDepleted
}

// TakeTag allocates a specific tag if available. The tag MUST be used or
// ditched afterwards, in order not to leak tags.
func (c *Transport) TakeTag(t qp.Tag) error {
	c.queueLock.Lock()
	defer c.queueLock.Unlock()

	if _, exists := c.queue[t]; exists {
		return ErrTagInUse
	}

	c.queue[t] = make(chan qp.Message, 1)
	return nil
}

// Send sends a message and retrieves the response.
func (c *Transport) Send(m qp.Message) (qp.Message, error) {
	t := m.GetTag()
	ch := c.getChannel(t)
	if ch == nil {
		return nil, ErrNoSuchTag
	}

	err := c.encoder.WriteMessage(m)
	if err != nil {
		return nil, c.die(err)
	}

	return <-ch, nil
}

// Ditch throws a pending request state away, and unblocks any Send on the tag.
// It does not send Tflush, and should only be used after such a message has
// been sent, or after a connection has been deemed dead.
func (c *Transport) Ditch(t qp.Tag) error {
	return c.killChannel(t)
}

// PendingTags return tags that are currently in use.
func (c *Transport) PendingTags() []qp.Tag {
	c.queueLock.RLock()
	defer c.queueLock.RUnlock()
	var t []qp.Tag
	for tag := range c.queue {
		t = append(t, tag)
	}
	return t
}

// SetProtocol changes the protocol used for message de/encoding. Calling this
// method is only safe if there is a guarantee that the client and server will
// not exchange any messages before the change has been complete, to ensure
// proper de/encoding of messages. Calling this method while the Decoder is
// decoding messages may results in a decoder error, terminating the run loop.
func (c *Transport) SetProtocol(p qp.Protocol) {
	// We take the writeLock to ensure that no one is trying to write
	// messages.
	c.encoder.Protocol = p
	c.decoder.Protocol = p
}

// SetReadWriter sets the io.ReadWriter used for message de/encoding. It is
// safe assuming the safe conditions for SetProtocol are present and the
// greedy decoding flag have not been enabled.
func (c *Transport) SetReadWriter(rw io.ReadWriter) {
	c.encoder.Writer = rw
	c.decoder.Reader = rw
}

// SetMessageSize sets the maximum message size. It does not reallocate the
// decoding buffer.
func (c *Transport) SetMessageSize(ms uint32) {
	c.msgsize = ms
	c.encoder.MessageSize = ms
	c.decoder.MessageSize = ms
	c.decoder.Reset()
}

// SetGreedyDecoding sets the greedy flag for the decoder. When set, changing
// the readwriter becomes unsafe.
func (c *Transport) SetGreedyDecoding(g bool) {
	c.decoder.Greedy = g
}

// MessageSize returns the maximum message size.
func (c *Transport) MessageSize() uint32 {
	return c.msgsize
}

// Serve executes the response parsing loop.
func (c *Transport) Serve() error {
	for atomic.LoadUint32(&c.errorCnt) == 0 {
		m, err := c.decoder.ReadMessage()
		if err != nil {
			return c.die(err)
		}
		if err = c.received(m); err != nil {
			return c.die(err)
		}
	}

	c.errorLock.Lock()
	defer c.errorLock.Unlock()
	return c.error
}

// Stop terminates the reading loop.
func (c *Transport) Stop() {
	c.die(nil)
}

// NewTransport creates a new client.
func NewTransport(rw io.ReadWriter) *Transport {
	c := &Transport{
		queue:   make(map[qp.Tag]chan qp.Message),
		msgsize: 16384,
	}

	c.encoder = &qp.Encoder{
		Protocol:    qp.NineP2000,
		Writer:      rw,
		MessageSize: 16384,
	}

	c.decoder = &qp.Decoder{
		Protocol:    qp.NineP2000,
		Reader:      rw,
		MessageSize: 16384,
	}

	return c
}
<commit_msg>Fix tag pool allocation error in transport<commit_after>package client

import (
	"errors"
	"io"
	"sync"
	"sync/atomic"

	"github.com/joushou/qp"
)

var (
	// ErrTagInUse indicates that a tag is already being used for still active
	// request.
	ErrTagInUse = errors.New("tag already in use")

	// ErrNoSuchTag indicates that the tag in a response from a server was not
	// known to be associated with a pending request.
	ErrNoSuchTag = errors.New("tag does not exist")

	// ErrInvalidResponse indicates that the response type did not make sense
	// for the request.
	ErrInvalidResponse = errors.New("invalid response")

	// ErrTagPoolDepleted indicates that all possible tags are currently in
	// use.
	ErrTagPoolDepleted = errors.New("tag pool depleted")

	// ErrUnexpectedTagPoolDepleted indicates that all possible tags are
	// currently in use, but that this should not have been the case.
	ErrUnexpectedTagPoolDepleted = errors.New("unexpected tag pool depletion")

	// ErrStopped indicate that the client was stopped.
	ErrStopped = errors.New("stopped")
)

// Transport implements a 9P client exposing a blocking rpc-like Send call,
// while still processing responses out-of-order. A blocking Send can be
// unblocked by using Ditch.
//
// Apart from managing available tags, matching them on response and
// encoding/decoding messages, RawClient does not concern itself with any
// protocol details.
type Transport struct {
	encoder *qp.Encoder
	decoder *qp.Decoder

	// error stores any run-loop errors.
	error     error
	errorLock sync.Mutex
	errorCnt  uint32

	// queue stores a response channel per message tag.
	queue     map[qp.Tag]chan qp.Message
	queueLock sync.RWMutex
	nextTag   qp.Tag

	// msgsize is the maximum set message size.
	msgsize uint32
}

func (c *Transport) die(err error) error {
	c.errorLock.Lock()
	defer c.errorLock.Unlock()
	if c.error == nil {
		c.error = err
	}
	atomic.AddUint32(&c.errorCnt, 1)
	return c.error
}

// getChannel retrieves the response channel for a given tag.
func (c *Transport) getChannel(t qp.Tag) chan qp.Message {
	c.queueLock.RLock()
	defer c.queueLock.RUnlock()

	if m, exists := c.queue[t]; exists {
		return m
	}

	return nil
}

// killChannel closes a response channel and removes it from the map.
func (c *Transport) killChannel(t qp.Tag) error {
	c.queueLock.Lock()
	defer c.queueLock.Unlock()
	ch, exists := c.queue[t]
	if !exists {
		return ErrNoSuchTag
	}
	ch <- nil
	close(ch)
	delete(c.queue, t)
	return nil
}

// received is the callback for messages decoded from the io.ReadWriter.
func (c *Transport) received(m qp.Message) error {
	c.queueLock.Lock()
	defer c.queueLock.Unlock()
	t := m.GetTag()
	if ch, ok := c.queue[t]; ok {
		ch <- m
		close(ch)
		delete(c.queue, t)
		return nil
	}
	return ErrNoSuchTag
}

// Tag allocates and retrieves a tag. The tag MUST be used or ditched
// afterwards, in order not to leak tags.
func (c *Transport) Tag() (qp.Tag, error) {
	c.queueLock.Lock()
	defer c.queueLock.Unlock()

	// No need to loop 0xFFFF times to figure out that the thing is full.
	if len(c.queue) > 0xFFFF {
		// No tags available.
		return qp.NOTAG, ErrTagPoolDepleted
	} else if len(c.queue) == 0xFFFF {
		if _, exists := c.queue[qp.NOTAG]; !exists {
			// The only tag available is qp.NOTAG.
			return qp.NOTAG, ErrTagPoolDepleted
		}
	}

	// There is a tag available *somewhere*. Find it.
	var t qp.Tag
	var exists bool
	for i := 0; i < 0xFFFF; i++ {
		t = c.nextTag
		c.nextTag++
		if c.nextTag == qp.NOTAG {
			c.nextTag = 0
		}

		if _, exists = c.queue[t]; !exists {
			c.queue[t] = make(chan qp.Message, 1)
			return t, nil
		}
	}

	return qp.NOTAG, ErrUnexpectedTagPoolDepleted
}

// TakeTag allocates a specific tag if available. The tag MUST be used or
// ditched afterwards, in order not to leak tags.
func (c *Transport) TakeTag(t qp.Tag) error {
	c.queueLock.Lock()
	defer c.queueLock.Unlock()

	if _, exists := c.queue[t]; exists {
		return ErrTagInUse
	}

	c.queue[t] = make(chan qp.Message, 1)
	return nil
}

// Send sends a message and retrieves the response.
func (c *Transport) Send(m qp.Message) (qp.Message, error) {
	t := m.GetTag()
	ch := c.getChannel(t)
	if ch == nil {
		return nil, ErrNoSuchTag
	}

	err := c.encoder.WriteMessage(m)
	if err != nil {
		return nil, c.die(err)
	}

	return <-ch, nil
}

// Ditch throws a pending request state away, and unblocks any Send on the tag.
// It does not send Tflush, and should only be used after such a message has
// been sent, or after a connection has been deemed dead.
func (c *Transport) Ditch(t qp.Tag) error {
	return c.killChannel(t)
}

// PendingTags return tags that are currently in use.
func (c *Transport) PendingTags() []qp.Tag {
	c.queueLock.RLock()
	defer c.queueLock.RUnlock()
	var t []qp.Tag
	for tag := range c.queue {
		t = append(t, tag)
	}
	return t
}

// SetProtocol changes the protocol used for message de/encoding. Calling this
// method is only safe if there is a guarantee that the client and server will
// not exchange any messages before the change has been complete, to ensure
// proper de/encoding of messages. Calling this method while the Decoder is
// decoding messages may results in a decoder error, terminating the run loop.
func (c *Transport) SetProtocol(p qp.Protocol) {
	// We take the writeLock to ensure that no one is trying to write
	// messages.
	c.encoder.Protocol = p
	c.decoder.Protocol = p
}

// SetReadWriter sets the io.ReadWriter used for message de/encoding. It is
// safe assuming the safe conditions for SetProtocol are present and the
// greedy decoding flag have not been enabled.
func (c *Transport) SetReadWriter(rw io.ReadWriter) {
	c.encoder.Writer = rw
	c.decoder.Reader = rw
}

// SetMessageSize sets the maximum message size. It does not reallocate the
// decoding buffer.
func (c *Transport) SetMessageSize(ms uint32) {
	c.msgsize = ms
	c.encoder.MessageSize = ms
	c.decoder.MessageSize = ms
	c.decoder.Reset()
}

// SetGreedyDecoding sets the greedy flag for the decoder. When set, changing
// the readwriter becomes unsafe.
func (c *Transport) SetGreedyDecoding(g bool) {
	c.decoder.Greedy = g
}

// MessageSize returns the maximum message size.
func (c *Transport) MessageSize() uint32 {
	return c.msgsize
}

// Serve executes the response parsing loop.
func (c *Transport) Serve() error {
	for atomic.LoadUint32(&c.errorCnt) == 0 {
		m, err := c.decoder.ReadMessage()
		if err != nil {
			return c.die(err)
		}
		if err = c.received(m); err != nil {
			return c.die(err)
		}
	}

	c.errorLock.Lock()
	defer c.errorLock.Unlock()
	return c.error
}

// Stop terminates the reading loop.
func (c *Transport) Stop() {
	c.die(nil)
}

// NewTransport creates a new client.
func NewTransport(rw io.ReadWriter) *Transport {
	c := &Transport{
		queue:   make(map[qp.Tag]chan qp.Message),
		msgsize: 16384,
	}

	c.encoder = &qp.Encoder{
		Protocol:    qp.NineP2000,
		Writer:      rw,
		MessageSize: 16384,
	}

	c.decoder = &qp.Decoder{
		Protocol:    qp.NineP2000,
		Reader:      rw,
		MessageSize: 16384,
	}

	return c
}
<|endoftext|>


<commit_before>package util

import (
	"../worker"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

type Config struct {
	Rootdir string
	Workers []*worker.Worker
}

func NormalizePath(path string) (string, string) {
	clean_path := filepath.Clean(path)
	full_path, _ := filepath.Abs(clean_path)
	return filepath.Base(full_path), full_path
}

func GetFullPath(abs_filename, rootdir string) string {
	return filepath.Join(rootdir, strings.TrimPrefix(abs_filename, "//"))
}

func CleanupChild(cmd *exec.Cmd) {
	cmd.Process.Signal(os.Interrupt)
	cmd.Wait()
}

var Exiter func()
<commit_msg>client: set up default clean-up function<commit_after>package util

import (
	"../worker"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

type Config struct {
	Rootdir string
	Workers []*worker.Worker
}

func NormalizePath(path string) (string, string) {
	clean_path := filepath.Clean(path)
	full_path, _ := filepath.Abs(clean_path)
	return filepath.Base(full_path), full_path
}

func GetFullPath(abs_filename, rootdir string) string {
	return filepath.Join(rootdir, strings.TrimPrefix(abs_filename, "//"))
}

func CleanupChild(cmd *exec.Cmd) {
	cmd.Process.Signal(os.Interrupt)
	cmd.Wait()
}

var Exiter func() = func() {}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/url"
	"os"

	"github.com/google/gerritfmt"
	"github.com/google/gerritfmt/gerrit"
	"github.com/google/slothfs/cookie"
)

func main() {
	gerritURL := flag.String("gerrit", "", "URL to gerrit host")
	register := flag.Bool("register", false, "Register with the host")
	update := flag.Bool("update", false, "Update an existing checker on the host")
	list := flag.Bool("list", false, "List pending checks")
	agent := flag.String("agent", "fmtserver", "user-agent for the fmtserver.")
	cookieJar := flag.String("cookies", "", "comma separated paths to cURL-style cookie jar file.")
	authFile := flag.String("auth_file", "", "file containing user:password")
	repo := flag.String("repo", "", "the repository (project) name to apply the checker to.")
	language := flag.String("language", "", "the language that the checker should apply to.")
	flag.Parse()
	if *gerritURL == "" {
		log.Fatal("must set --gerrit")
	}

	u, err := url.Parse(*gerritURL)
	if err != nil {
		log.Fatalf("url.Parse: %v", err)
	}

	g := gerrit.New(*u)

	if nm := *cookieJar; nm != "" {
		jar, err := cookie.NewJar(nm)
		if err != nil {
			log.Fatalf("NewJar(%s): %v", nm, err)
		}
		if err := cookie.WatchJar(jar, nm); err != nil {
			log.Printf("WatchJar: %v", err)
			log.Println("continuing despite WatchJar failure", err)
		}
		g.Client.Jar = jar
	}
	g.UserAgent = *agent

	if *authFile == "" {
		log.Fatal("must set --auth_file")
	}
	if content, err := ioutil.ReadFile(*authFile); err != nil {
		log.Fatal(err)
	} else {
		auth := bytes.TrimSpace(content)
		encoded := make([]byte, base64.StdEncoding.EncodedLen(len(auth)))
		base64.StdEncoding.Encode(encoded, auth)
		g.BasicAuth = string(encoded)
	}

	// Do a GET first to complete any cookie dance, because POST aren't redirected properly.
	if _, err := g.GetPath("a/accounts/self"); err != nil {
		log.Fatalf("accounts/self: %v", err)
	}

	gc, err := NewGerritChecker(g)
	if err != nil {
		log.Fatal(err)
	}

	if *list {
		if out, err := gc.ListCheckers(); err != nil {
			log.Fatalf("List: %v", err)
		} else {
			for _, ch := range out {
				json, _ := json.Marshal(ch)
				os.Stdout.Write(json)
				os.Stdout.Write([]byte{'\n'})
			}
		}

		os.Exit(0)
	}

	if *register || *update {
		if *repo == "" {
			log.Fatalf("need to set --repo")
		}

		if *language == "" {
			log.Fatalf("must set --language.")
		}

		if !gerritfmt.IsSupported(*language) {
			log.Fatalf("language is not supported. Choices are %s", gerritfmt.SupportedLanguages())
		}

		ch, err := gc.PostChecker(*repo, *language, *update)
		if err != nil {
			log.Fatalf("CreateChecker: %v", err)
		}
		log.Printf("CreateChecker result: %v", ch)
		os.Exit(0)
	}

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

package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"

	"github.com/google/gerritfmt"
	"github.com/google/gerritfmt/gerrit"
	"github.com/google/slothfs/cookie"
)

func main() {
	gerritURL := flag.String("gerrit", "", "URL to gerrit host")
	register := flag.Bool("register", false, "Register with the host")
	update := flag.Bool("update", false, "Update an existing checker on the host")
	list := flag.Bool("list", false, "List pending checks")
	agent := flag.String("agent", "fmtserver", "user-agent for the fmtserver.")
	cookieJar := flag.String("cookies", "", "comma separated paths to cURL-style cookie jar file.")
	authFile := flag.String("auth_file", "", "file containing user:password")
	repo := flag.String("repo", "", "the repository (project) name to apply the checker to.")
	language := flag.String("language", "", "the language that the checker should apply to.")
	flag.Parse()
	if *gerritURL == "" {
		log.Fatal("must set --gerrit")
	}

	// Add path to self to $PATH, for easy deployment.
	if exe, err := os.Executable(); err == nil {
		os.Setenv("PATH", filepath.Dir(exe)+":"+os.Getenv("PATH"))
	}

	u, err := url.Parse(*gerritURL)
	if err != nil {
		log.Fatalf("url.Parse: %v", err)
	}

	g := gerrit.New(*u)

	if nm := *cookieJar; nm != "" {
		jar, err := cookie.NewJar(nm)
		if err != nil {
			log.Fatalf("NewJar(%s): %v", nm, err)
		}
		if err := cookie.WatchJar(jar, nm); err != nil {
			log.Printf("WatchJar: %v", err)
			log.Println("continuing despite WatchJar failure", err)
		}
		g.Client.Jar = jar
	}
	g.UserAgent = *agent

	if *authFile == "" {
		log.Fatal("must set --auth_file")
	}
	if content, err := ioutil.ReadFile(*authFile); err != nil {
		log.Fatal(err)
	} else {
		auth := bytes.TrimSpace(content)
		encoded := make([]byte, base64.StdEncoding.EncodedLen(len(auth)))
		base64.StdEncoding.Encode(encoded, auth)
		g.BasicAuth = string(encoded)
	}

	// Do a GET first to complete any cookie dance, because POST aren't redirected properly.
	if _, err := g.GetPath("a/accounts/self"); err != nil {
		log.Fatalf("accounts/self: %v", err)
	}

	gc, err := NewGerritChecker(g)
	if err != nil {
		log.Fatal(err)
	}

	if *list {
		if out, err := gc.ListCheckers(); err != nil {
			log.Fatalf("List: %v", err)
		} else {
			for _, ch := range out {
				json, _ := json.Marshal(ch)
				os.Stdout.Write(json)
				os.Stdout.Write([]byte{'\n'})
			}
		}

		os.Exit(0)
	}

	if *register || *update {
		if *repo == "" {
			log.Fatalf("need to set --repo")
		}

		if *language == "" {
			log.Fatalf("must set --language.")
		}

		if !gerritfmt.IsSupported(*language) {
			log.Fatalf("language is not supported. Choices are %s", gerritfmt.SupportedLanguages())
		}

		ch, err := gc.PostChecker(*repo, *language, *update)
		if err != nil {
			log.Fatalf("CreateChecker: %v", err)
		}
		log.Printf("CreateChecker result: %v", ch)
		os.Exit(0)
	}

	gc.Serve()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"strings"
	"syscall"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/chihaya/chihaya/frontend/http"
	"github.com/chihaya/chihaya/frontend/udp"
	"github.com/chihaya/chihaya/middleware"
	"github.com/chihaya/chihaya/pkg/log"
	"github.com/chihaya/chihaya/pkg/prometheus"
	"github.com/chihaya/chihaya/pkg/stop"
	"github.com/chihaya/chihaya/storage"
)

// Run represents the state of a running instance of Chihaya.
type Run struct {
	configFilePath string
	peerStore      storage.PeerStore
	logic          *middleware.Logic
	sg             *stop.Group
}

// NewRun runs an instance of Chihaya.
func NewRun(configFilePath string) (*Run, error) {
	r := &Run{
		configFilePath: configFilePath,
	}

	return r, r.Start(nil)
}

// Start begins an instance of Chihaya.
// It is optional to provide an instance of the peer store to avoid the
// creation of a new one.
func (r *Run) Start(ps storage.PeerStore) error {
	configFile, err := ParseConfigFile(r.configFilePath)
	if err != nil {
		return errors.New("failed to read config: " + err.Error())
	}
	cfg := configFile.Chihaya

	r.sg = stop.NewGroup()

	log.Info("starting Prometheus server", log.Fields{"addr": cfg.PrometheusAddr})
	r.sg.Add(prometheus.NewServer(cfg.PrometheusAddr))

	if ps == nil {
		log.Info("starting storage", log.Fields{"name": cfg.Storage.Name})
		ps, err = storage.NewPeerStore(cfg.Storage.Name, cfg.Storage.Config)
		if err != nil {
			return errors.New("failed to create storage: " + err.Error())
		}
		log.Info("started storage", ps)
	}
	r.peerStore = ps

	preHooks, err := middleware.HooksFromHookConfigs(cfg.PreHooks)
	if err != nil {
		return errors.New("failed to validate hook config: " + err.Error())
	}
	postHooks, err := middleware.HooksFromHookConfigs(cfg.PostHooks)
	if err != nil {
		return errors.New("failed to validate hook config: " + err.Error())
	}

	log.Info("starting tracker logic", log.Fields{
		"prehooks":  cfg.PreHookNames(),
		"posthooks": cfg.PostHookNames(),
	})
	r.logic = middleware.NewLogic(cfg.ResponseConfig, r.peerStore, preHooks, postHooks)

	if cfg.HTTPConfig.Addr != "" {
		log.Info("starting HTTP frontend", cfg.HTTPConfig)
		httpfe, err := http.NewFrontend(r.logic, cfg.HTTPConfig)
		if err != nil {
			return err
		}
		r.sg.Add(httpfe)
	}

	if cfg.UDPConfig.Addr != "" {
		log.Info("starting UDP frontend", cfg.UDPConfig)
		udpfe, err := udp.NewFrontend(r.logic, cfg.UDPConfig)
		if err != nil {
			return err
		}
		r.sg.Add(udpfe)
	}

	return nil
}

func combineErrors(prefix string, errs []error) error {
	var errStrs []string
	for _, err := range errs {
		errStrs = append(errStrs, err.Error())
	}

	return errors.New(prefix + ": " + strings.Join(errStrs, "; "))
}

// Stop shuts down an instance of Chihaya.
func (r *Run) Stop(keepPeerStore bool) (storage.PeerStore, error) {
	log.Debug("stopping frontends and prometheus endpoint")
	if errs := r.sg.Stop(); len(errs) != 0 {
		return nil, combineErrors("failed while shutting down frontends", errs)
	}

	log.Debug("stopping logic")
	if errs := r.logic.Stop(); len(errs) != 0 {
		return nil, combineErrors("failed while shutting down middleware", errs)
	}

	if !keepPeerStore {
		log.Debug("stopping peer store")
		if err, closed := <-r.peerStore.Stop(); !closed {
			return nil, err
		}
		r.peerStore = nil
	}

	return r.peerStore, nil
}

// RunCmdFunc implements a Cobra command that runs an instance of Chihaya and
// handles reloading and shutdown via process signals.
func RunCmdFunc(cmd *cobra.Command, args []string) error {
	configFilePath, err := cmd.Flags().GetString("config")
	if err != nil {
		return err
	}

	r, err := NewRun(configFilePath)
	if err != nil {
		return err
	}

	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	reload := makeReloadChan()

	for {
		select {
		case <-reload:
			log.Info("reloading; received SIGUSR1")
			peerStore, err := r.Stop(true)
			if err != nil {
				return err
			}

			if err := r.Start(peerStore); err != nil {
				return err
			}
		case <-quit:
			log.Info("shutting down; received SIGINT/SIGTERM")
			if _, err := r.Stop(false); err != nil {
				return err
			}

			return nil
		}
	}
}

// PreRunCmdFunc handles command line flags for the Run command.
func PreRunCmdFunc(cmd *cobra.Command, args []string) error {
	noColors, err := cmd.Flags().GetBool("nocolors")
	if err != nil {
		return err
	}
	if noColors {
		log.SetFormatter(&logrus.TextFormatter{DisableColors: true})
	}

	jsonLog, err := cmd.Flags().GetBool("json")
	if err != nil {
		return err
	}
	if jsonLog {
		log.SetFormatter(&logrus.JSONFormatter{})
		log.Info("enabled JSON logging")
	}

	debugLog, err := cmd.Flags().GetBool("debug")
	if err != nil {
		return err
	}
	if debugLog {
		log.SetDebug(true)
		log.Info("enabled debug logging")
	}

	cpuProfilePath, err := cmd.Flags().GetString("cpuprofile")
	if err != nil {
		return err
	}
	if cpuProfilePath != "" {
		f, err := os.Create(cpuProfilePath)
		if err != nil {
			return err
		}
		pprof.StartCPUProfile(f)
		log.Info("enabled CPU profiling", log.Fields{"path": cpuProfilePath})
	}

	return nil
}

// PostRunCmdFunc handles clean up of any state initialized by command line
// flags.
func PostRunCmdFunc(cmd *cobra.Command, args []string) error {
	// This can be called regardless because it noops when not profiling.
	pprof.StopCPUProfile()

	return nil
}

func main() {
	var rootCmd = &cobra.Command{
		Use:                "chihaya",
		Short:              "BitTorrent Tracker",
		Long:               "A customizable, multi-protocol BitTorrent Tracker",
		PersistentPreRunE:  PreRunCmdFunc,
		RunE:               RunCmdFunc,
		PersistentPostRunE: PostRunCmdFunc,
	}

	rootCmd.Flags().String("config", "/etc/chihaya.yaml", "location of configuration file")
	rootCmd.Flags().String("cpuprofile", "", "location to save a CPU profile")
	rootCmd.Flags().Bool("debug", false, "enable debug logging")
	rootCmd.Flags().Bool("json", false, "enable json logging")
	if runtime.GOOS == "windows" {
		rootCmd.Flags().Bool("nocolors", true, "disable log coloring")
	} else {
		rootCmd.Flags().Bool("nocolors", false, "disable log coloring")
	}

	if err := rootCmd.Execute(); err != nil {
		log.Fatal("failed when executing root cobra command: " + err.Error())
	}
}
<commit_msg>cmd/chihaya: add tracing flag<commit_after>package main

import (
	"errors"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"runtime/trace"
	"strings"
	"syscall"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/chihaya/chihaya/frontend/http"
	"github.com/chihaya/chihaya/frontend/udp"
	"github.com/chihaya/chihaya/middleware"
	"github.com/chihaya/chihaya/pkg/log"
	"github.com/chihaya/chihaya/pkg/prometheus"
	"github.com/chihaya/chihaya/pkg/stop"
	"github.com/chihaya/chihaya/storage"
)

// Run represents the state of a running instance of Chihaya.
type Run struct {
	configFilePath string
	peerStore      storage.PeerStore
	logic          *middleware.Logic
	sg             *stop.Group
}

// NewRun runs an instance of Chihaya.
func NewRun(configFilePath string) (*Run, error) {
	r := &Run{
		configFilePath: configFilePath,
	}

	return r, r.Start(nil)
}

// Start begins an instance of Chihaya.
// It is optional to provide an instance of the peer store to avoid the
// creation of a new one.
func (r *Run) Start(ps storage.PeerStore) error {
	configFile, err := ParseConfigFile(r.configFilePath)
	if err != nil {
		return errors.New("failed to read config: " + err.Error())
	}
	cfg := configFile.Chihaya

	r.sg = stop.NewGroup()

	log.Info("starting Prometheus server", log.Fields{"addr": cfg.PrometheusAddr})
	r.sg.Add(prometheus.NewServer(cfg.PrometheusAddr))

	if ps == nil {
		log.Info("starting storage", log.Fields{"name": cfg.Storage.Name})
		ps, err = storage.NewPeerStore(cfg.Storage.Name, cfg.Storage.Config)
		if err != nil {
			return errors.New("failed to create storage: " + err.Error())
		}
		log.Info("started storage", ps)
	}
	r.peerStore = ps

	preHooks, err := middleware.HooksFromHookConfigs(cfg.PreHooks)
	if err != nil {
		return errors.New("failed to validate hook config: " + err.Error())
	}
	postHooks, err := middleware.HooksFromHookConfigs(cfg.PostHooks)
	if err != nil {
		return errors.New("failed to validate hook config: " + err.Error())
	}

	log.Info("starting tracker logic", log.Fields{
		"prehooks":  cfg.PreHookNames(),
		"posthooks": cfg.PostHookNames(),
	})
	r.logic = middleware.NewLogic(cfg.ResponseConfig, r.peerStore, preHooks, postHooks)

	if cfg.HTTPConfig.Addr != "" {
		log.Info("starting HTTP frontend", cfg.HTTPConfig)
		httpfe, err := http.NewFrontend(r.logic, cfg.HTTPConfig)
		if err != nil {
			return err
		}
		r.sg.Add(httpfe)
	}

	if cfg.UDPConfig.Addr != "" {
		log.Info("starting UDP frontend", cfg.UDPConfig)
		udpfe, err := udp.NewFrontend(r.logic, cfg.UDPConfig)
		if err != nil {
			return err
		}
		r.sg.Add(udpfe)
	}

	return nil
}

func combineErrors(prefix string, errs []error) error {
	var errStrs []string
	for _, err := range errs {
		errStrs = append(errStrs, err.Error())
	}

	return errors.New(prefix + ": " + strings.Join(errStrs, "; "))
}

// Stop shuts down an instance of Chihaya.
func (r *Run) Stop(keepPeerStore bool) (storage.PeerStore, error) {
	log.Debug("stopping frontends and prometheus endpoint")
	if errs := r.sg.Stop(); len(errs) != 0 {
		return nil, combineErrors("failed while shutting down frontends", errs)
	}

	log.Debug("stopping logic")
	if errs := r.logic.Stop(); len(errs) != 0 {
		return nil, combineErrors("failed while shutting down middleware", errs)
	}

	if !keepPeerStore {
		log.Debug("stopping peer store")
		if err, closed := <-r.peerStore.Stop(); !closed {
			return nil, err
		}
		r.peerStore = nil
	}

	return r.peerStore, nil
}

// RunCmdFunc implements a Cobra command that runs an instance of Chihaya and
// handles reloading and shutdown via process signals.
func RunCmdFunc(cmd *cobra.Command, args []string) error {
	configFilePath, err := cmd.Flags().GetString("config")
	if err != nil {
		return err
	}

	r, err := NewRun(configFilePath)
	if err != nil {
		return err
	}

	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	reload := makeReloadChan()

	for {
		select {
		case <-reload:
			log.Info("reloading; received SIGUSR1")
			peerStore, err := r.Stop(true)
			if err != nil {
				return err
			}

			if err := r.Start(peerStore); err != nil {
				return err
			}
		case <-quit:
			log.Info("shutting down; received SIGINT/SIGTERM")
			if _, err := r.Stop(false); err != nil {
				return err
			}

			return nil
		}
	}
}

// PreRunCmdFunc handles command line flags for the Run command.
func PreRunCmdFunc(cmd *cobra.Command, args []string) error {
	noColors, err := cmd.Flags().GetBool("nocolors")
	if err != nil {
		return err
	}
	if noColors {
		log.SetFormatter(&logrus.TextFormatter{DisableColors: true})
	}

	jsonLog, err := cmd.Flags().GetBool("json")
	if err != nil {
		return err
	}
	if jsonLog {
		log.SetFormatter(&logrus.JSONFormatter{})
		log.Info("enabled JSON logging")
	}

	debugLog, err := cmd.Flags().GetBool("debug")
	if err != nil {
		return err
	}
	if debugLog {
		log.SetDebug(true)
		log.Info("enabled debug logging")
	}

	tracePath, err := cmd.Flags().GetString("trace")
	if err != nil {
		return err
	}
	if tracePath != "" {
		f, err := os.Create(tracePath)
		if err != nil {
			return err
		}
		trace.Start(f)
		log.Info("enabled tracing", log.Fields{"path": tracePath})
	}

	cpuProfilePath, err := cmd.Flags().GetString("cpuprofile")
	if err != nil {
		return err
	}
	if cpuProfilePath != "" {
		f, err := os.Create(cpuProfilePath)
		if err != nil {
			return err
		}
		pprof.StartCPUProfile(f)
		log.Info("enabled CPU profiling", log.Fields{"path": cpuProfilePath})
	}

	return nil
}

// PostRunCmdFunc handles clean up of any state initialized by command line
// flags.
func PostRunCmdFunc(cmd *cobra.Command, args []string) error {
	// These can be called regardless because it noops when not profiling.
	pprof.StopCPUProfile()
	trace.Stop()

	return nil
}

func main() {
	var rootCmd = &cobra.Command{
		Use:                "chihaya",
		Short:              "BitTorrent Tracker",
		Long:               "A customizable, multi-protocol BitTorrent Tracker",
		PersistentPreRunE:  PreRunCmdFunc,
		RunE:               RunCmdFunc,
		PersistentPostRunE: PostRunCmdFunc,
	}

	rootCmd.Flags().String("config", "/etc/chihaya.yaml", "location of configuration file")
	rootCmd.Flags().String("cpuprofile", "", "location to save a CPU profile")
	rootCmd.Flags().String("trace", "", "location to save a trace")
	rootCmd.Flags().Bool("debug", false, "enable debug logging")
	rootCmd.Flags().Bool("json", false, "enable json logging")
	if runtime.GOOS == "windows" {
		rootCmd.Flags().Bool("nocolors", true, "disable log coloring")
	} else {
		rootCmd.Flags().Bool("nocolors", false, "disable log coloring")
	}

	if err := rootCmd.Execute(); err != nil {
		log.Fatal("failed when executing root cobra command: " + err.Error())
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"github.com/dynport/urknall/assets"
	"github.com/dynport/urknall/utils"
	"os"
	"path/filepath"
	"strings"
)

// The "FileCommand" is used to write files to the host being provisioned. The go templating mechanism (see
// http://golang.org/pkg/text/template) is applied on the file's content using the package. Thereby it is possible to
// have dynamic content (based on the package's configuration) for the file content and at the same time store it in
// an asset (which is generated at compile time). Please note that the underlying actions will panic if either no path
// or content are given.
//
// BUG(gfrey): It's currently not possible to write files larger than 32k, as the underlying go.crypto/ssh package has a
// hard limit to this size. Fixing should be possible by adding a data channel to the "Command" interfaces methods
// output, that is connected to stdin of the SSH session.
type FileCommand struct {
	Path        string      // Path to the file to create.
	Content     string      // Content of the file to create.
	Owner       string      // Owner of the file to create (root per default).
	Permissions os.FileMode // Permissions of the file created (only changed from system default if set).
}

func (cmd *FileCommand) Render(i interface{}) {
	cmd.Content = utils.MustRenderTemplate(cmd.Content, i)
}

// Helper method to create a file at the given path with the given content, and with owner and permissions set
// accordingly. The "Owner" and "Permissions" options are optional in the sense that they are ignored if set to go's
// default value.
func WriteFile(path string, content string, owner string, permissions os.FileMode) *FileCommand {
	return &FileCommand{Path: path, Content: content, Owner: owner, Permissions: permissions}
}

// Helper method to write the asset with the given name to the location given, with owner and permissions set
// accordingly. If no asset with the given name exists the function will panic! The "Owner" and "Permissions" options
// are optional in the sense that they are ignored if set to go's default value.
func WriteAsset(path, assetName, owner string, permissions os.FileMode) *FileCommand {
	content, e := assets.Get(assetName)
	if e != nil {
		panic(e)
	}
	return WriteFile(path, string(content), owner, permissions)
}

func (fc *FileCommand) Docker() string {
	return "RUN " + fc.Shell()
}

var b64 = base64.StdEncoding

func (fc *FileCommand) Shell() string {
	buf := &bytes.Buffer{}

	if fc.Path == "" {
		panic("no path given")
	}

	if fc.Content == "" {
		panic("no content given")
	}

	// Zip the content.
	zipper := gzip.NewWriter(buf)
	zipper.Write([]byte(fc.Content))
	zipper.Flush()
	zipper.Close()

	// Encode the zipped content in Base64.
	encoded := b64.EncodeToString(buf.Bytes())

	// Compute sha256 hash of the encoded and zipped content.
	hash := sha256.New()
	hash.Write([]byte(fc.Content))

	// Create temporary filename (hash as filename).
	tmpPath := fmt.Sprintf("/tmp/wunderscale.%x", hash.Sum(nil))

	// Get directory part of target file.
	dir := filepath.Dir(fc.Path)

	// Create command, that will decode and unzip the content and write to the temporary file.
	cmd := ""
	cmd += fmt.Sprintf("mkdir -p %s", dir)
	cmd += fmt.Sprintf(" && echo %s | base64 -d | gunzip > %s", encoded, tmpPath)
	if fc.Owner != "" { // If owner given, change accordingly.
		cmd += fmt.Sprintf(" && chown %s %s", fc.Owner, tmpPath)
	}
	if fc.Permissions > 0 { // If mode given, change accordingly.
		cmd += fmt.Sprintf(" && chmod %o %s", fc.Permissions, tmpPath)
	}
	// Move the temporary file to the requested location.
	cmd += fmt.Sprintf(" && mv %s %s", tmpPath, fc.Path)
	return cmd
}

func (fc *FileCommand) Logging() string {
	sList := []string{"[FILE   ]"}

	if fc.Owner != "" && fc.Owner != "root" {
		sList = append(sList, fmt.Sprintf("[CHOWN:%s]", fc.Owner))
	}

	if fc.Permissions != 0 {
		sList = append(sList, fmt.Sprintf("[CHMOD:%.4o]", fc.Permissions))
	}

	sList = append(sList, " "+fc.Path)

	cLen := len(fc.Content)
	if cLen > 50 {
		cLen = 50
	}
	sList = append(sList, fmt.Sprintf(" << %s", strings.Replace(string(fc.Content[0:cLen]), "\n", "⁋", -1)))
	return strings.Join(sList, "")
}
<commit_msg>also render Path of FileCommand<commit_after>package cmd

import (
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"github.com/dynport/urknall/assets"
	"github.com/dynport/urknall/utils"
	"os"
	"path/filepath"
	"strings"
)

// The "FileCommand" is used to write files to the host being provisioned. The go templating mechanism (see
// http://golang.org/pkg/text/template) is applied on the file's content using the package. Thereby it is possible to
// have dynamic content (based on the package's configuration) for the file content and at the same time store it in
// an asset (which is generated at compile time). Please note that the underlying actions will panic if either no path
// or content are given.
//
// BUG(gfrey): It's currently not possible to write files larger than 32k, as the underlying go.crypto/ssh package has a
// hard limit to this size. Fixing should be possible by adding a data channel to the "Command" interfaces methods
// output, that is connected to stdin of the SSH session.
type FileCommand struct {
	Path        string      // Path to the file to create.
	Content     string      // Content of the file to create.
	Owner       string      // Owner of the file to create (root per default).
	Permissions os.FileMode // Permissions of the file created (only changed from system default if set).
}

func (cmd *FileCommand) Render(i interface{}) {
	cmd.Path = utils.MustRenderTemplate(cmd.Path, i)
	cmd.Content = utils.MustRenderTemplate(cmd.Content, i)
}

// Helper method to create a file at the given path with the given content, and with owner and permissions set
// accordingly. The "Owner" and "Permissions" options are optional in the sense that they are ignored if set to go's
// default value.
func WriteFile(path string, content string, owner string, permissions os.FileMode) *FileCommand {
	return &FileCommand{Path: path, Content: content, Owner: owner, Permissions: permissions}
}

// Helper method to write the asset with the given name to the location given, with owner and permissions set
// accordingly. If no asset with the given name exists the function will panic! The "Owner" and "Permissions" options
// are optional in the sense that they are ignored if set to go's default value.
func WriteAsset(path, assetName, owner string, permissions os.FileMode) *FileCommand {
	content, e := assets.Get(assetName)
	if e != nil {
		panic(e)
	}
	return WriteFile(path, string(content), owner, permissions)
}

func (fc *FileCommand) Docker() string {
	return "RUN " + fc.Shell()
}

var b64 = base64.StdEncoding

func (fc *FileCommand) Shell() string {
	buf := &bytes.Buffer{}

	if fc.Path == "" {
		panic("no path given")
	}

	if fc.Content == "" {
		panic("no content given")
	}

	// Zip the content.
	zipper := gzip.NewWriter(buf)
	zipper.Write([]byte(fc.Content))
	zipper.Flush()
	zipper.Close()

	// Encode the zipped content in Base64.
	encoded := b64.EncodeToString(buf.Bytes())

	// Compute sha256 hash of the encoded and zipped content.
	hash := sha256.New()
	hash.Write([]byte(fc.Content))

	// Create temporary filename (hash as filename).
	tmpPath := fmt.Sprintf("/tmp/wunderscale.%x", hash.Sum(nil))

	// Get directory part of target file.
	dir := filepath.Dir(fc.Path)

	// Create command, that will decode and unzip the content and write to the temporary file.
	cmd := ""
	cmd += fmt.Sprintf("mkdir -p %s", dir)
	cmd += fmt.Sprintf(" && echo %s | base64 -d | gunzip > %s", encoded, tmpPath)
	if fc.Owner != "" { // If owner given, change accordingly.
		cmd += fmt.Sprintf(" && chown %s %s", fc.Owner, tmpPath)
	}
	if fc.Permissions > 0 { // If mode given, change accordingly.
		cmd += fmt.Sprintf(" && chmod %o %s", fc.Permissions, tmpPath)
	}
	// Move the temporary file to the requested location.
	cmd += fmt.Sprintf(" && mv %s %s", tmpPath, fc.Path)
	return cmd
}

func (fc *FileCommand) Logging() string {
	sList := []string{"[FILE   ]"}

	if fc.Owner != "" && fc.Owner != "root" {
		sList = append(sList, fmt.Sprintf("[CHOWN:%s]", fc.Owner))
	}

	if fc.Permissions != 0 {
		sList = append(sList, fmt.Sprintf("[CHMOD:%.4o]", fc.Permissions))
	}

	sList = append(sList, " "+fc.Path)

	cLen := len(fc.Content)
	if cLen > 50 {
		cLen = 50
	}
	sList = append(sList, fmt.Sprintf(" << %s", strings.Replace(string(fc.Content[0:cLen]), "\n", "⁋", -1)))
	return strings.Join(sList, "")
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"math"

	"github.com/ready-steady/format/mat"
	"github.com/ready-steady/numeric/interpolation/adhier"
	"github.com/ready-steady/statistics"
	"github.com/ready-steady/statistics/test"

	"../internal"
)

func main() {
	internal.Run(command)
}

func command(config internal.Config, input *mat.File, _ *mat.File) error {
	if input == nil {
		return errors.New("an input file is required")
	}

	surrogate := new(adhier.Surrogate)
	if err := input.Get("surrogate", surrogate); err != nil {
		return err
	}

	observed := []float64{}
	if err := input.Get("observed", &observed); err != nil {
		return err
	}

	predicted := []float64{}
	if err := input.Get("predicted", &predicted); err != nil {
		return err
	}

	μ1 := statistics.Mean(observed)
	μ2 := statistics.Mean(predicted)

	σ1 := math.Sqrt(statistics.Variance(observed))
	σ2 := math.Sqrt(statistics.Variance(predicted))

	_, _, Δ := test.KolmogorovSmirnov(observed, predicted, 0)

	fmt.Printf("Inputs: %2d, outputs: %4d, level: %2d, nodes: %7d, μ %.2e, σ %.2e, Δ %.2e\n",
		surrogate.Inputs, surrogate.Outputs, surrogate.Level, surrogate.Nodes,
		math.Abs((μ1 - μ2) / μ1), math.Abs((σ1 - σ2) / σ1), Δ)

	return nil
}
<commit_msg>Go fmt!<commit_after>package main

import (
	"errors"
	"fmt"
	"math"

	"github.com/ready-steady/format/mat"
	"github.com/ready-steady/numeric/interpolation/adhier"
	"github.com/ready-steady/statistics"
	"github.com/ready-steady/statistics/test"

	"../internal"
)

func main() {
	internal.Run(command)
}

func command(config internal.Config, input *mat.File, _ *mat.File) error {
	if input == nil {
		return errors.New("an input file is required")
	}

	surrogate := new(adhier.Surrogate)
	if err := input.Get("surrogate", surrogate); err != nil {
		return err
	}

	observed := []float64{}
	if err := input.Get("observed", &observed); err != nil {
		return err
	}

	predicted := []float64{}
	if err := input.Get("predicted", &predicted); err != nil {
		return err
	}

	μ1 := statistics.Mean(observed)
	μ2 := statistics.Mean(predicted)

	σ1 := math.Sqrt(statistics.Variance(observed))
	σ2 := math.Sqrt(statistics.Variance(predicted))

	_, _, Δ := test.KolmogorovSmirnov(observed, predicted, 0)

	fmt.Printf("Inputs: %2d, outputs: %4d, level: %2d, nodes: %7d, μ %.2e, σ %.2e, Δ %.2e\n",
		surrogate.Inputs, surrogate.Outputs, surrogate.Level, surrogate.Nodes,
		math.Abs((μ1-μ2)/μ1), math.Abs((σ1-σ2)/σ1), Δ)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"
	"strings"

	"github.com/pkg/errors"

	transport "github.com/weaveworks/flux/http"
)

func run(args []string, stderr io.Writer) int {
	rootCmd := newRoot().Command()
	rootCmd.SetArgs(args)
	rootCmd.SetOutput(stderr)
	if cmd, err := rootCmd.ExecuteC(); err != nil {
		err = errors.Cause(err)
		switch err := err.(type) {
		case usageError:
			cmd.Println("")
			cmd.Println(cmd.UsageString())
		case *transport.APIError:
			switch {
			case err.IsMissing():
				cmd.Println(strings.Join([]string{
					"Error: Service API endpoint not found. This usually means that there is a mismatch between the client and the service. Please visit",
					"    https://github.com/weaveworks/flux/releases",
					"to download a new release of the client."}, "\n"))
			default:
				cmd.Println("Problem communicating with the service: ", err.Error())
				cmd.Printf("Run '%v --help' for usage.\n", cmd.CommandPath())
			}
		default:
			cmd.Println("Error: ", err.Error())
			cmd.Printf("Run '%v --help' for usage.\n", cmd.CommandPath())
		}
		return 1
	}
	return 0
}

func main() {
	os.Exit(run(os.Args, os.Stderr))
}
<commit_msg>Pass os.Args properly<commit_after>package main

import (
	"io"
	"os"
	"strings"

	"github.com/pkg/errors"

	transport "github.com/weaveworks/flux/http"
)

func run(args []string, stderr io.Writer) int {
	rootCmd := newRoot().Command()
	rootCmd.SetArgs(args)
	rootCmd.SetOutput(stderr)
	if cmd, err := rootCmd.ExecuteC(); err != nil {
		err = errors.Cause(err)
		switch err := err.(type) {
		case usageError:
			cmd.Println("")
			cmd.Println(cmd.UsageString())
		case *transport.APIError:
			switch {
			case err.IsMissing():
				cmd.Println(strings.Join([]string{
					"Error: Service API endpoint not found. This usually means that there is a mismatch between the client and the service. Please visit",
					"    https://github.com/weaveworks/flux/releases",
					"to download a new release of the client."}, "\n"))
			default:
				cmd.Println("Problem communicating with the service: ", err.Error())
				cmd.Printf("Run '%v --help' for usage.\n", cmd.CommandPath())
			}
		default:
			cmd.Println("Error: ", err.Error())
			cmd.Printf("Run '%v --help' for usage.\n", cmd.CommandPath())
		}
		return 1
	}
	return 0
}

func main() {
	os.Exit(run(os.Args[1:], os.Stderr))
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/spf13/cobra"
)

// mrCmd represents the mr command
var mergeRequestCmd = &cobra.Command{
	Use:   "merge-request [remote [branch]]",
	Short: mrCreateCmd.Short,
	Long:  mrCreateCmd.Long,
	Args:  mrCreateCmd.Args,
	Run:   runMRCreate,
}

func init() {
	RootCmd.AddCommand(mergeRequestCmd)
}
<commit_msg>[#137] issue deprecation notice in `lab merge-request`<commit_after>package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
)

// mrCmd represents the mr command
var mergeRequestCmd = &cobra.Command{
	Use:   "merge-request [remote [branch]]",
	Short: mrCreateCmd.Short,
	Long:  mrCreateCmd.Long,
	Args:  mrCreateCmd.Args,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("[WARN] `lab merge-request` will be deprecated by `lab mr create` before v1.0 ")
		runMRCreate(cmd, args)
	},
}

func init() {
	RootCmd.AddCommand(mergeRequestCmd)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/freman/go-aurora"

	"github.com/BurntSushi/toml"
	"github.com/matryer/try"
	"github.com/tarm/serial"
)

type result struct {
	Time                time.Time
	Address             byte
	BoosterTemperature  float32
	InverterTemperature float32
	Frequency           float32
	GridVoltage         float32
	GridCurrent         float32
	GridPower           float32
	GridRunTime         duration
	Input1Voltage       float32
	Input1Current       float32
	Input2Voltage       float32
	Input2Current       float32
	Joules              uint16
	DailyEnergy         uint32
	WeeklyEnergy        uint32
	MonthlyEnergy       uint32
	YearlyEnergy        uint32
	TotalEnergy         uint32
	TotalRunTime        duration
	SerialNumber        string
}

type results struct {
	sync.RWMutex
	Results map[byte]*result
}

type serialConfig struct {
	serial.Config
	ReadTimeout duration
}

type duration struct {
	time.Duration
}

func (o *serialConfig) Normalise() *serial.Config {
	o.Config.ReadTimeout = o.ReadTimeout.Duration
	return &o.Config
}

func (d *duration) UnmarshalText(text []byte) (err error) {
	d.Duration, err = time.ParseDuration(string(text))
	return
}

func (d duration) MarshalJSON() ([]byte, error) {
	return json.Marshal(int64(d.Duration.Seconds()))
}

func withDeadline(deadline time.Duration, f func() error) error {
	c := make(chan error, 1)
	defer close(c)
	go func() {
		c <- try.Do(func(attempt int) (bool, error) {
			time.Sleep(time.Duration(attempt) * time.Millisecond)
			err := f()
			return attempt < 3, err
		})
	}()
	select {
	case err := <-c:
		return err
	case <-time.After(deadline):
		return errors.New("Timeout while waiting for operation to complete")
	}
}

type configStruct struct {
	Name          string
	Comms         serialConfig
	UpdateRate    duration
	Deadline      duration
	UnitAddresses []byte
}

func main() {
	config := struct {
		UpdateRate duration
		Deadline   duration
		Listen     string
		Devices    []configStruct
	}{
		UpdateRate: duration{
			Duration: time.Minute,
		},
		Deadline: duration{
			Duration: 5 * time.Second,
		},
		Listen: ":8080",
	}

	fConfig := flag.String("config", "config.toml", "Path to the configuration file")
	flag.Parse()

	_, err := toml.DecodeFile(*fConfig, &config)
	if err != nil {
		log.Fatalf("Unable to parse configuration file due to %v.", err)
	}
	buffer := results{
		Results: map[byte]*result{},
	}

	for _, device := range config.Devices {
		go func(device configStruct) {
			deadline := device.Deadline.Duration
			if deadline == 0 {
				deadline = config.Deadline.Duration
			}

			updateRate := device.UpdateRate.Duration
			if updateRate == 0 {
				updateRate = config.UpdateRate.Duration
			}

			port, err := serial.OpenPort(device.Comms.Normalise())
			if err != nil {
				log.Fatalf("Unable to open serial port due to %v.", err)
			}

			inverter := &aurora.Inverter{
				Conn: port,
			}

			for _, address := range device.UnitAddresses {
				inverter.Address = address
				buffer.Results[address] = &result{
					Address: address,
				}

				err := withDeadline(deadline, func() (err error) {
					buffer.Results[address].SerialNumber, err = inverter.SerialNumber()
					return
				})

				if err != nil {
					log.Fatalf("Unable to communicate with inverter at address %d, error was %v", address, err)
				}
			}

			ticker := time.NewTicker(updateRate)
			now := time.Now()
			for {
				for _, address := range device.UnitAddresses {
					buffer.RLock()
					inverter.Address = address
					r := &result{
						Address:      address,
						SerialNumber: buffer.Results[address].SerialNumber,
						Time:         now,
					}
					buffer.RUnlock()

					err := withDeadline(deadline, func() error {
						var err error
						if r.BoosterTemperature, err = inverter.BoosterTemperature(); err != nil {
							return err
						}
						if r.InverterTemperature, err = inverter.InverterTemperature(); err != nil {
							return err
						}
						if r.Frequency, err = inverter.Frequency(); err != nil {
							return err
						}
						if r.GridVoltage, err = inverter.GridVoltage(); err != nil {
							return err
						}
						if r.GridCurrent, err = inverter.GridCurrent(); err != nil {
							return err
						}
						if r.GridPower, err = inverter.GridPower(); err != nil {
							return err
						}
						if r.GridRunTime.Duration, err = inverter.GridRunTime(); err != nil {
							return err
						}
						if r.Input1Voltage, err = inverter.Input1Voltage(); err != nil {
							return err
						}
						if r.Input1Current, err = inverter.Input1Current(); err != nil {
							return err
						}
						if r.Input2Voltage, err = inverter.Input2Voltage(); err != nil {
							return err
						}
						if r.Input2Current, err = inverter.Input2Current(); err != nil {
							return err
						}
						if r.Joules, err = inverter.Joules(); err != nil {
							return err
						}
						if r.DailyEnergy, err = inverter.DailyEnergy(); err != nil {
							return err
						}
						if r.WeeklyEnergy, err = inverter.WeeklyEnergy(); err != nil {
							return err
						}
						if r.MonthlyEnergy, err = inverter.MonthlyEnergy(); err != nil {
							return err
						}
						if r.YearlyEnergy, err = inverter.YearlyEnergy(); err != nil {
							return err
						}
						if r.TotalEnergy, err = inverter.TotalEnergy(); err != nil {
							return err
						}
						r.TotalRunTime.Duration, err = inverter.TotalRunTime()
						return err
					})

					if err == nil {
						buffer.Lock()
						buffer.Results[address] = r
						buffer.Unlock()
					}
				}
				now = <-ticker.C
			}
		}(device)
	}

	http.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) {
		buffer.RLock()
		defer buffer.RUnlock()
		js, err := json.Marshal(buffer.Results)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Content-Type", "application/json")
		w.Write(js)
	})

	log.Fatal(http.ListenAndServe(config.Listen, nil))
}
<commit_msg>Run goimports like a good boy<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/freman/go-aurora"

	"github.com/BurntSushi/toml"
	"github.com/matryer/try"
	"github.com/tarm/serial"
)

type result struct {
	Time                time.Time
	Address             byte
	BoosterTemperature  float32
	InverterTemperature float32
	Frequency           float32
	GridVoltage         float32
	GridCurrent         float32
	GridPower           float32
	GridRunTime         duration
	Input1Voltage       float32
	Input1Current       float32
	Input2Voltage       float32
	Input2Current       float32
	Joules              uint16
	DailyEnergy         uint32
	WeeklyEnergy        uint32
	MonthlyEnergy       uint32
	YearlyEnergy        uint32
	TotalEnergy         uint32
	TotalRunTime        duration
	SerialNumber        string
}

type results struct {
	sync.RWMutex
	Results map[byte]*result
}

type serialConfig struct {
	serial.Config
	ReadTimeout duration
}

type duration struct {
	time.Duration
}

func (o *serialConfig) Normalise() *serial.Config {
	o.Config.ReadTimeout = o.ReadTimeout.Duration
	return &o.Config
}

func (d *duration) UnmarshalText(text []byte) (err error) {
	d.Duration, err = time.ParseDuration(string(text))
	return
}

func (d duration) MarshalJSON() ([]byte, error) {
	return json.Marshal(int64(d.Duration.Seconds()))
}

func withDeadline(deadline time.Duration, f func() error) error {
	c := make(chan error, 1)
	defer close(c)
	go func() {
		c <- try.Do(func(attempt int) (bool, error) {
			time.Sleep(time.Duration(attempt) * time.Millisecond)
			err := f()
			return attempt < 3, err
		})
	}()
	select {
	case err := <-c:
		return err
	case <-time.After(deadline):
		return errors.New("Timeout while waiting for operation to complete")
	}
}

type configStruct struct {
	Name          string
	Comms         serialConfig
	UpdateRate    duration
	Deadline      duration
	UnitAddresses []byte
}

func main() {
	config := struct {
		UpdateRate duration
		Deadline   duration
		Listen     string
		Devices    []configStruct
	}{
		UpdateRate: duration{
			Duration: time.Minute,
		},
		Deadline: duration{
			Duration: 5 * time.Second,
		},
		Listen: ":8080",
	}

	fConfig := flag.String("config", "config.toml", "Path to the configuration file")
	flag.Parse()

	_, err := toml.DecodeFile(*fConfig, &config)
	if err != nil {
		log.Fatalf("Unable to parse configuration file due to %v.", err)
	}
	buffer := results{
		Results: map[byte]*result{},
	}

	for _, device := range config.Devices {
		go func(device configStruct) {
			deadline := device.Deadline.Duration
			if deadline == 0 {
				deadline = config.Deadline.Duration
			}

			updateRate := device.UpdateRate.Duration
			if updateRate == 0 {
				updateRate = config.UpdateRate.Duration
			}

			port, err := serial.OpenPort(device.Comms.Normalise())
			if err != nil {
				log.Fatalf("Unable to open serial port due to %v.", err)
			}

			inverter := &aurora.Inverter{
				Conn: port,
			}

			for _, address := range device.UnitAddresses {
				inverter.Address = address
				buffer.Results[address] = &result{
					Address: address,
				}

				err := withDeadline(deadline, func() (err error) {
					buffer.Results[address].SerialNumber, err = inverter.SerialNumber()
					return
				})

				if err != nil {
					log.Fatalf("Unable to communicate with inverter at address %d, error was %v", address, err)
				}
			}

			ticker := time.NewTicker(updateRate)
			now := time.Now()
			for {
				for _, address := range device.UnitAddresses {
					buffer.RLock()
					inverter.Address = address
					r := &result{
						Address:      address,
						SerialNumber: buffer.Results[address].SerialNumber,
						Time:         now,
					}
					buffer.RUnlock()

					err := withDeadline(deadline, func() error {
						var err error
						if r.BoosterTemperature, err = inverter.BoosterTemperature(); err != nil {
							return err
						}
						if r.InverterTemperature, err = inverter.InverterTemperature(); err != nil {
							return err
						}
						if r.Frequency, err = inverter.Frequency(); err != nil {
							return err
						}
						if r.GridVoltage, err = inverter.GridVoltage(); err != nil {
							return err
						}
						if r.GridCurrent, err = inverter.GridCurrent(); err != nil {
							return err
						}
						if r.GridPower, err = inverter.GridPower(); err != nil {
							return err
						}
						if r.GridRunTime.Duration, err = inverter.GridRunTime(); err != nil {
							return err
						}
						if r.Input1Voltage, err = inverter.Input1Voltage(); err != nil {
							return err
						}
						if r.Input1Current, err = inverter.Input1Current(); err != nil {
							return err
						}
						if r.Input2Voltage, err = inverter.Input2Voltage(); err != nil {
							return err
						}
						if r.Input2Current, err = inverter.Input2Current(); err != nil {
							return err
						}
						if r.Joules, err = inverter.Joules(); err != nil {
							return err
						}
						if r.DailyEnergy, err = inverter.DailyEnergy(); err != nil {
							return err
						}
						if r.WeeklyEnergy, err = inverter.WeeklyEnergy(); err != nil {
							return err
						}
						if r.MonthlyEnergy, err = inverter.MonthlyEnergy(); err != nil {
							return err
						}
						if r.YearlyEnergy, err = inverter.YearlyEnergy(); err != nil {
							return err
						}
						if r.TotalEnergy, err = inverter.TotalEnergy(); err != nil {
							return err
						}
						r.TotalRunTime.Duration, err = inverter.TotalRunTime()
						return err
					})

					if err == nil {
						buffer.Lock()
						buffer.Results[address] = r
						buffer.Unlock()
					}
				}
				now = <-ticker.C
			}
		}(device)
	}

	http.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) {
		buffer.RLock()
		defer buffer.RUnlock()
		js, err := json.Marshal(buffer.Results)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Content-Type", "application/json")
		w.Write(js)
	})

	log.Fatal(http.ListenAndServe(config.Listen, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	"github.com/relab/raft"
	"github.com/relab/raft/commonpb"
	"github.com/relab/rkv/rkvpb"
)

// Service exposes a key-value store as a gRPC service.
type Service struct {
	raft   raft.Raft
	sem    chan struct{} // Forces requests to be handled in order. len(sema) = concurrent requests.
	leader chan struct{}
	logger logrus.FieldLogger
}

// NewService initializes and returns a new Service.
func NewService(logger logrus.FieldLogger, raft raft.Raft, leader chan struct{}) *Service {
	return &Service{
		raft:   raft,
		sem:    make(chan struct{}, 50000),
		leader: leader,
		logger: logger,
	}
}

// Register implements RKVServer.
func (s *Service) Register(ctx context.Context, req *rkvpb.RegisterRequest) (*rkvpb.RegisterResponse, error) {
	cmd := &rkvpb.Cmd{
		CmdType: rkvpb.Register,
	}

	b, err := cmd.Marshal()

	if err != nil {
		return nil, err
	}

	s.sem <- struct{}{}
	future, err := s.raft.ProposeCmd(ctx, b)
	<-s.sem

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}
		return &rkvpb.RegisterResponse{ClientID: res.Value.(uint64)}, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// Insert implements RKVServer.
func (s *Service) Insert(ctx context.Context, req *rkvpb.InsertRequest) (*rkvpb.InsertResponse, error) {
	reqb, err := req.Marshal()

	if err != nil {
		return nil, err
	}

	cmd := &rkvpb.Cmd{
		CmdType: rkvpb.Insert,
		Data:    reqb,
	}

	b, err := cmd.Marshal()

	if err != nil {
		return nil, err
	}

	s.sem <- struct{}{}
	future, err := s.raft.ProposeCmd(ctx, b)
	<-s.sem

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}
		return &rkvpb.InsertResponse{Ok: true}, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// Lookup implements RKVServer.
func (s *Service) Lookup(ctx context.Context, req *rkvpb.LookupRequest) (*rkvpb.LookupResponse, error) {
	reqb, err := req.Marshal()

	if err != nil {
		return nil, err
	}

	cmd := &rkvpb.Cmd{
		CmdType: rkvpb.Lookup,
		Data:    reqb,
	}

	b, err := cmd.Marshal()

	if err != nil {
		return nil, err
	}

	s.sem <- struct{}{}
	future, err := s.raft.ReadCmd(ctx, b)
	<-s.sem

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}
		return &rkvpb.LookupResponse{Value: res.Value.(string)}, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// Reconf implements RKVServer.
func (s *Service) Reconf(ctx context.Context, req *commonpb.ReconfRequest) (*commonpb.ReconfResponse, error) {
	s.sem <- struct{}{}
	future, err := s.raft.ProposeConf(ctx, req)
	<-s.sem

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}
		return res.Value.(*commonpb.ReconfResponse), nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// ReconfOnBecome implements RKVServer.
func (s *Service) ReconfOnBecome(ctx context.Context, req *commonpb.ReconfRequest) (*commonpb.ReconfResponse, error) {
	s.sem <- struct{}{}
	s.logger.Warnln("Waiting on becoming leader")
	select {
	case <-s.leader:
		s.logger.Warnln("Became leader, proceeding...")
	case <-ctx.Done():
		return nil, ctx.Err()
	}
	future, err := s.raft.ProposeConf(ctx, req)
	<-s.sem

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}

		go func() {
			if req.ReconfType == commonpb.ReconfRemove {
				req.ReconfType = commonpb.ReconfAdd
				req.ServerID = 4
				s.Reconf(context.Background(), req)
			}
		}()

		return res.Value.(*commonpb.ReconfResponse), nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}
<commit_msg>rkvd/service.go: Schedule reconfigurations first<commit_after>package main

import (
	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	"github.com/relab/raft"
	"github.com/relab/raft/commonpb"
	"github.com/relab/rkv/rkvpb"
)

// Service exposes a key-value store as a gRPC service.
type Service struct {
	raft   raft.Raft
	sem    chan struct{} // Forces requests to be handled in order. len(sema) = concurrent requests.
	leader chan struct{}
	logger logrus.FieldLogger
}

// NewService initializes and returns a new Service.
func NewService(logger logrus.FieldLogger, raft raft.Raft, leader chan struct{}) *Service {
	return &Service{
		raft:   raft,
		sem:    make(chan struct{}, 50000),
		leader: leader,
		logger: logger,
	}
}

// Register implements RKVServer.
func (s *Service) Register(ctx context.Context, req *rkvpb.RegisterRequest) (*rkvpb.RegisterResponse, error) {
	cmd := &rkvpb.Cmd{
		CmdType: rkvpb.Register,
	}

	b, err := cmd.Marshal()

	if err != nil {
		return nil, err
	}

	s.sem <- struct{}{}
	future, err := s.raft.ProposeCmd(ctx, b)
	<-s.sem

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}
		return &rkvpb.RegisterResponse{ClientID: res.Value.(uint64)}, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// Insert implements RKVServer.
func (s *Service) Insert(ctx context.Context, req *rkvpb.InsertRequest) (*rkvpb.InsertResponse, error) {
	reqb, err := req.Marshal()

	if err != nil {
		return nil, err
	}

	cmd := &rkvpb.Cmd{
		CmdType: rkvpb.Insert,
		Data:    reqb,
	}

	b, err := cmd.Marshal()

	if err != nil {
		return nil, err
	}

	s.sem <- struct{}{}
	future, err := s.raft.ProposeCmd(ctx, b)
	<-s.sem

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}
		return &rkvpb.InsertResponse{Ok: true}, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// Lookup implements RKVServer.
func (s *Service) Lookup(ctx context.Context, req *rkvpb.LookupRequest) (*rkvpb.LookupResponse, error) {
	reqb, err := req.Marshal()

	if err != nil {
		return nil, err
	}

	cmd := &rkvpb.Cmd{
		CmdType: rkvpb.Lookup,
		Data:    reqb,
	}

	b, err := cmd.Marshal()

	if err != nil {
		return nil, err
	}

	s.sem <- struct{}{}
	future, err := s.raft.ReadCmd(ctx, b)
	<-s.sem

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}
		return &rkvpb.LookupResponse{Value: res.Value.(string)}, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// Reconf implements RKVServer.
func (s *Service) Reconf(ctx context.Context, req *commonpb.ReconfRequest) (*commonpb.ReconfResponse, error) {
	future, err := s.raft.ProposeConf(ctx, req)

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}
		return res.Value.(*commonpb.ReconfResponse), nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// ReconfOnBecome implements RKVServer.
func (s *Service) ReconfOnBecome(ctx context.Context, req *commonpb.ReconfRequest) (*commonpb.ReconfResponse, error) {
	s.logger.Warnln("Waiting on becoming leader")
	select {
	case <-s.leader:
		s.logger.Warnln("Became leader, proceeding...")
	case <-ctx.Done():
		return nil, ctx.Err()
	}
	future, err := s.raft.ProposeConf(ctx, req)

	if err != nil {
		return nil, err
	}

	select {
	case res := <-future.ResultCh():
		if err, ok := res.Value.(error); ok {
			return nil, err
		}

		go func() {
			if req.ReconfType == commonpb.ReconfRemove {
				req.ReconfType = commonpb.ReconfAdd
				req.ServerID = 4
				s.Reconf(context.Background(), req)
			}
		}()

		return res.Value.(*commonpb.ReconfResponse), nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}
<|endoftext|>


<commit_before>package git_comment

import (
  "github.com/stvp/assert"
  "github.com/wayn3h0/go-uuid"
  "testing"
)

func TestNewCommentAuthor(t *testing.T) {
  author, _ := CreatePerson("Sam Wafers <sam@example.com>")
  comment, err := NewComment("", "123", nil, author)
  assert.Nil(t, err)
  assert.NotNil(t, comment)
  assert.Equal(t, comment.Author, author)
  assert.Equal(t, comment.Amender, author)
}

func TestNewCommentAmender(t *testing.T) {
  author, _ := CreatePerson("Sam Wafers <sam@example.com>")
  comment, err := NewComment("", "123", nil, author)
  assert.Nil(t, err)
  assert.NotNil(t, comment)
  assert.Equal(t, comment.Amender, author)
}

func TestNewCommentTime(t *testing.T) {
  comment, err := NewComment("", "123", nil, nil)
  assert.Nil(t, err)
  assert.NotNil(t, comment)
  assert.NotNil(t, comment.CreateTime)
  assert.Equal(t, comment.AmendTime, comment.CreateTime)
}

func TestNewCommentCommit(t *testing.T) {
  comment, err := NewComment("", "abcdefg", nil, nil)
  assert.Nil(t, err)
  assert.NotNil(t, comment)
  assert.Equal(t, comment.Commit, "abcdefg")
}

func TestNewCommentContent(t *testing.T) {
  comment, err := NewComment("Season the chex mix", "abcdefg", nil, nil)
  assert.Nil(t, err)
  assert.NotNil(t, comment)
  assert.Equal(t, comment.Content, "Season the chex mix")
}

func TestNewCommentID(t *testing.T) {
  comment, err := NewComment("", "abcdefg", nil, nil)
  assert.Nil(t, err)
  assert.NotNil(t, comment)
  assert.NotNil(t, comment.ID)
  identifier, uErr := uuid.Parse(comment.ID)
  assert.Nil(t, uErr)
  assert.NotNil(t, identifier)
}

func TestNewCommentDeleted(t *testing.T) {
  comment, err := NewComment("", "abcdefg", nil, nil)
  assert.Nil(t, err)
  assert.NotNil(t, comment)
  assert.False(t, comment.Deleted)
}

func TestNewCommentFileRef(t *testing.T) {
  ref := &FileRef{"src/example.c",12}
  comment, err := NewComment("", "abcdefg", ref, nil)
  assert.Nil(t, err)
  assert.NotNil(t, comment)
  assert.Equal(t, comment.FileRef, ref)
}
<commit_msg>Add content tests<commit_after>package git_comment

import (
	"github.com/stvp/assert"
	"github.com/wayn3h0/go-uuid"
	"strings"
	"testing"
)

func TestNewCommentAuthor(t *testing.T) {
	author, _ := CreatePerson("Sam Wafers <sam@example.com>")
	comment, err := NewComment("", "123", nil, author)
	assert.Nil(t, err)
	assert.NotNil(t, comment)
	assert.Equal(t, comment.Author, author)
	assert.Equal(t, comment.Amender, author)
}

func TestNewCommentAmender(t *testing.T) {
	author, _ := CreatePerson("Sam Wafers <sam@example.com>")
	comment, err := NewComment("", "123", nil, author)
	assert.Nil(t, err)
	assert.NotNil(t, comment)
	assert.Equal(t, comment.Amender, author)
}

func TestNewCommentTime(t *testing.T) {
	comment, err := NewComment("", "123", nil, nil)
	assert.Nil(t, err)
	assert.NotNil(t, comment)
	assert.NotNil(t, comment.CreateTime)
	assert.Equal(t, comment.AmendTime, comment.CreateTime)
}

func TestNewCommentCommit(t *testing.T) {
	comment, err := NewComment("", "abcdefg", nil, nil)
	assert.Nil(t, err)
	assert.NotNil(t, comment)
	assert.Equal(t, comment.Commit, "abcdefg")
}

func TestNewCommentContent(t *testing.T) {
	comment, err := NewComment("Season the chex mix", "abcdefg", nil, nil)
	assert.Nil(t, err)
	assert.NotNil(t, comment)
	assert.Equal(t, comment.Content, "Season the chex mix")
}

func TestNewCommentID(t *testing.T) {
	comment, err := NewComment("", "abcdefg", nil, nil)
	assert.Nil(t, err)
	assert.NotNil(t, comment)
	assert.NotNil(t, comment.ID)
	identifier, uErr := uuid.Parse(comment.ID)
	assert.Nil(t, uErr)
	assert.NotNil(t, identifier)
}

func TestNewCommentDeleted(t *testing.T) {
	comment, err := NewComment("", "abcdefg", nil, nil)
	assert.Nil(t, err)
	assert.NotNil(t, comment)
	assert.False(t, comment.Deleted)
}

func TestNewCommentFileRef(t *testing.T) {
	ref := &FileRef{"src/example.c", 12}
	comment, err := NewComment("", "abcdefg", ref, nil)
	assert.Nil(t, err)
	assert.NotNil(t, comment)
	assert.Equal(t, comment.FileRef, ref)
}

func TestObjectContentFull(t *testing.T) {
	ref := &FileRef{"src/example.c", 12}
	author := &Person{"Selina Kyle", "cat@example.com"}
	comment, _ := NewComment("This line is too long", "acdacdacd", ref, author)
	assert.NotNil(t, comment)
	lines := strings.Split(comment.ObjectContent(), "\n")
	assert.Equal(t, len(lines), 8)
	assert.Equal(t, lines[0], "commit acdacdacd")
	assert.Equal(t, lines[2], "Selina Kyle <cat@example.com>")
	assert.Equal(t, lines[4], "Selina Kyle <cat@example.com>")
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"path/filepath"

	"github.com/hashicorp/hcl/v2"
	imgds "github.com/hashicorp/packer/datasource/hcp-packer-image"
	iterds "github.com/hashicorp/packer/datasource/hcp-packer-iteration"
	"github.com/hashicorp/packer/hcl2template"
	"github.com/hashicorp/packer/internal/registry"
	"github.com/hashicorp/packer/internal/registry/env"
	"github.com/hashicorp/packer/packer"

	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/gocty"
)

type HCPConfigMode int

const (
	// HCPConfigMode types
	HCPConfigUnset HCPConfigMode = iota
	HCPConfigEnabled
	HCPEnvEnabled
)

const (
	// Known HCP Packer Image Datasource, whose id is the SourceImageId for some build.
	hcpImageDatasourceType     string = "hcp-packer-image"
	hcpIterationDatasourceType string = "hcp-packer-iteration"
	buildLabel                 string = "build"
)

// TrySetupHCP attempts to configure the HCP-related structures if
// Packer has been configured to publish to a HCP Packer registry.
func TrySetupHCP(cfg packer.Handler) hcl.Diagnostics {
	switch cfg := cfg.(type) {
	case *hcl2template.PackerConfig:
		return setupRegistryForPackerConfig(cfg)
	case *CoreWrapper:
		return setupRegistryForPackerCore(cfg)
	}

	return hcl.Diagnostics{
		&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "unknown Handler type",
			Detail: "TrySetupHCP called with an unknown Handler. " +
				"This is a Packer bug and should be brought to the attention " +
				"of the Packer team, please consider opening an issue for this.",
		},
	}
}

func setupRegistryForPackerConfig(pc *hcl2template.PackerConfig) hcl.Diagnostics {

	// HCP_PACKER_REGISTRY is explicitly turned off
	if env.IsHCPDisabled() {
		return nil
	}

	mode := HCPConfigUnset

	// TODO move into ConfigCheck
	for _, build := range pc.Builds {
		if build.HCPPackerRegistry != nil {
			mode = HCPConfigEnabled
		}
	}

	// HCP_PACKER_BUCKET_NAME is set or  HCP_PACKER_REGISTRY not toggled off
	if mode == HCPConfigUnset && (env.HasPackerRegistryBucket() || env.IsHCPExplicitelyEnabled()) {
		mode = HCPEnvEnabled
	}

	if mode == HCPConfigUnset {
		return nil
	}

	var diags hcl.Diagnostics
	if len(pc.Builds) > 1 {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Multiple " + buildLabel + " blocks",
			Detail: fmt.Sprintf("For Packer Registry enabled builds, only one " + buildLabel +
				" block can be defined. Please remove any additional " + buildLabel +
				" block(s). If this " + buildLabel + " is not meant for the Packer registry please " +
				"clear any HCP_PACKER_* environment variables."),
		})

		return diags
	}

	WithHCLBucketConfigurtationOpts := func(bb *hcl2template.BuildBlock) func(*registry.Bucket) {
		return func(bucket *registry.Bucket) {
			bb.HCPPackerRegistry.WriteToBucketConfig(bucket)
			// If at this point the bucket.Slug is still empty,
			// last try is to use the build.Name if present
			if bucket.Slug == "" && bb.Name != "" {
				bucket.Slug = bb.Name
			}

			// If the description is empty, use the one from the build block
			if bucket.Description == "" && bb.Description != "" {
				bucket.Description = bb.Description
			}
		}
	}

	build := pc.Builds[0]
	pc.Bucket, diags = createConfiguredBucket(
		pc.Basedir,
		WithPackerEnvConfigurationOpts,
		WithHCLBucketConfigurtationOpts(build),
	)

	if diags.HasErrors() {
		return diags
	}

	for _, source := range build.Sources {
		pc.Bucket.RegisterBuildForComponent(source.String())
	}

	/// Lets Move this
	vals, dsDiags := pc.Datasources.Values()
	if dsDiags != nil {
		diags = append(diags, dsDiags...)
	}

	imageDS, imageOK := vals[hcpImageDatasourceType]
	iterDS, iterOK := vals[hcpIterationDatasourceType]

	// If we don't have any image or iteration defined, we can return directly
	if !imageOK && !iterOK {
		return diags
	}

	iterations := map[string]iterds.DatasourceOutput{}

	var err error
	if iterOK {
		hcpData := map[string]cty.Value{}
		err = gocty.FromCtyValue(iterDS, &hcpData)
		if err != nil {
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid HCP datasources",
				Detail:   fmt.Sprintf("Failed to decode hcp_packer_iteration datasources: %s", err),
			})
			return diags
		}

		for k, v := range hcpData {
			iterVals := v.AsValueMap()
			dso := iterValueToDSOutput(iterVals)
			iterations[k] = dso
		}
	}

	images := map[string]imgds.DatasourceOutput{}

	if imageOK {
		hcpData := map[string]cty.Value{}
		err = gocty.FromCtyValue(imageDS, &hcpData)
		if err != nil {
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid HCP datasources",
				Detail:   fmt.Sprintf("Failed to decode hcp_packer_image datasources: %s", err),
			})
			return diags
		}

		for k, v := range hcpData {
			imageVals := v.AsValueMap()
			dso := imageValueToDSOutput(imageVals)
			images[k] = dso
		}
	}

	for _, img := range images {
		sourceIteration := registry.ParentIteration{}

		sourceIteration.IterationID = img.IterationID

		if img.ChannelID != "" {
			sourceIteration.ChannelID = img.ChannelID
		} else {
			for _, it := range iterations {
				if it.ID == img.IterationID {
					sourceIteration.ChannelID = it.ChannelID
					break
				}
			}
		}

		pc.Bucket.SourceImagesToParentIterations[img.ID] = sourceIteration
	}

	return diags
}

func setupRegistryForPackerCore(cfg *CoreWrapper) hcl.Diagnostics {
	if env.IsHCPDisabled() {
		return nil
	}

	if !env.HasPackerRegistryBucket() && !env.IsHCPExplicitelyEnabled() {
		return nil
	}

	core := cfg.Core
	bucket, diags := createConfiguredBucket(
		filepath.Dir(core.Template.Path),
		WithPackerEnvConfigurationOpts,
	)
	if diags.HasErrors() {
		return diags
	}

	core.Bucket = bucket
	for _, b := range core.Template.Builders {
		// Get all builds slated within config ignoring any only or exclude flags.
		core.Bucket.RegisterBuildForComponent(b.Name)
	}

	return diags
}

func createConfiguredBucket(templateDir string, opts ...func(*registry.Bucket)) (*registry.Bucket, hcl.Diagnostics) {
	var diags hcl.Diagnostics

	if !env.HasHCPCredentials() {
		diags = append(diags, &hcl.Diagnostic{
			Summary: "HCP authentication information required",
			Detail: fmt.Sprintf("The client authentication requires both %s and %s environment "+
				"variables to be set for authenticating with HCP.",
				env.HCPClientID,
				env.HCPClientSecret),
			Severity: hcl.DiagError,
		})
	}

	bucket, err := registry.NewBucketWithIteration(registry.IterationOptions{
		TemplateBaseDir: templateDir,
	})

	// This error needs to be reworded.
	if err != nil {
		diags = append(diags, &hcl.Diagnostic{
			Summary:  "Unable to create a valid bucket object for HCP Packer Registry",
			Detail:   fmt.Sprintf("%s", err),
			Severity: hcl.DiagError,
		})

		return nil, diags
	}

	for _, opt := range opts {
		opt(bucket)
	}

	if bucket.Slug == "" {
		diags = append(diags, &hcl.Diagnostic{
			Summary:  "bucket name cannot be empty",
			Detail:   "empty bucket name, please set it with the HCP_PACKER_BUCKET_NAME environment variable, or in a `hcp_packer_registry` block",
			Severity: hcl.DiagError,
		})

		return nil, diags
	}

	return bucket, diags
}

func WithPackerEnvConfigurationOpts(pc *registry.Bucket) {
	// Add default values for Packer settings configured via EnvVars.
	// TODO look to break this up to be more explicit on what is loaded here.
	pc.LoadDefaultSettingsFromEnv()
}

// load handler
// validate HCP Mode is on
// Validate we have all the bits to connect
// Build a connected client

func imageValueToDSOutput(imageVal map[string]cty.Value) imgds.DatasourceOutput {
	dso := imgds.DatasourceOutput{}
	for k, v := range imageVal {
		switch k {
		case "id":
			dso.ID = v.AsString()
		case "region":
			dso.Region = v.AsString()
		case "labels":
			labels := map[string]string{}
			lbls := v.AsValueMap()
			for k, v := range lbls {
				labels[k] = v.AsString()
			}
			dso.Labels = labels
		case "packer_run_uuid":
			dso.PackerRunUUID = v.AsString()
		case "channel_id":
			dso.ChannelID = v.AsString()
		case "iteration_id":
			dso.IterationID = v.AsString()
		case "build_id":
			dso.BuildID = v.AsString()
		case "created_at":
			dso.CreatedAt = v.AsString()
		case "component_type":
			dso.ComponentType = v.AsString()
		case "cloud_provider":
			dso.CloudProvider = v.AsString()
		}
	}

	return dso
}

func iterValueToDSOutput(iterVal map[string]cty.Value) iterds.DatasourceOutput {
	dso := iterds.DatasourceOutput{}
	for k, v := range iterVal {
		switch k {
		case "author_id":
			dso.AuthorID = v.AsString()
		case "bucket_name":
			dso.BucketName = v.AsString()
		case "complete":
			// For all intents and purposes, cty.Value.True() acts
			// like a AsBool() would.
			dso.Complete = v.True()
		case "created_at":
			dso.CreatedAt = v.AsString()
		case "fingerprint":
			dso.Fingerprint = v.AsString()
		case "id":
			dso.ID = v.AsString()
		case "incremental_version":
			// Maybe when cty provides a good way to AsInt() a cty.Value
			// we can consider implementing this.
		case "updated_at":
			dso.UpdatedAt = v.AsString()
		case "channel_id":
			dso.ChannelID = v.AsString()
		}
	}
	return dso
}
<commit_msg>Move datasource data into its own bucketConfiguratioOpts func<commit_after>package command

import (
	"fmt"
	"path/filepath"

	"github.com/hashicorp/hcl/v2"
	imgds "github.com/hashicorp/packer/datasource/hcp-packer-image"
	iterds "github.com/hashicorp/packer/datasource/hcp-packer-iteration"
	"github.com/hashicorp/packer/hcl2template"
	"github.com/hashicorp/packer/internal/registry"
	"github.com/hashicorp/packer/internal/registry/env"
	"github.com/hashicorp/packer/packer"

	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/gocty"
)

type HCPConfigMode int

const (
	// HCPConfigMode types specify the mode in which HCP configuration
	// is defined for a given Packer build execution.
	HCPConfigUnset HCPConfigMode = iota
	HCPConfigEnabled
	HCPEnvEnabled
)

const (
	// Known HCP Packer Image Datasource, whose id is the SourceImageId for some build.
	hcpImageDatasourceType     string = "hcp-packer-image"
	hcpIterationDatasourceType string = "hcp-packer-iteration"
	buildLabel                 string = "build"
)

// TrySetupHCP attempts to configure the HCP-related structures if
// Packer has been configured to publish to a HCP Packer registry.
func TrySetupHCP(cfg packer.Handler) hcl.Diagnostics {
	switch cfg := cfg.(type) {
	case *hcl2template.PackerConfig:
		return setupRegistryForPackerConfig(cfg)
	case *CoreWrapper:
		return setupRegistryForPackerCore(cfg)
	}

	return hcl.Diagnostics{
		&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "unknown Handler type",
			Detail: "TrySetupHCP called with an unknown Handler. " +
				"This is a Packer bug and should be brought to the attention " +
				"of the Packer team, please consider opening an issue for this.",
		},
	}
}

func setupRegistryForPackerConfig(pc *hcl2template.PackerConfig) hcl.Diagnostics {

	// HCP_PACKER_REGISTRY is explicitly turned off
	if env.IsHCPDisabled() {
		return nil
	}

	mode := HCPConfigUnset

	for _, build := range pc.Builds {
		if build.HCPPackerRegistry != nil {
			mode = HCPConfigEnabled
		}
	}

	// HCP_PACKER_BUCKET_NAME is set or  HCP_PACKER_REGISTRY not toggled off
	if mode == HCPConfigUnset && (env.HasPackerRegistryBucket() || env.IsHCPExplicitelyEnabled()) {
		mode = HCPEnvEnabled
	}

	if mode == HCPConfigUnset {
		return nil
	}

	var diags hcl.Diagnostics
	if len(pc.Builds) > 1 {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Multiple " + buildLabel + " blocks",
			Detail: fmt.Sprintf("For Packer Registry enabled builds, only one " + buildLabel +
				" block can be defined. Please remove any additional " + buildLabel +
				" block(s). If this " + buildLabel + " is not meant for the Packer registry please " +
				"clear any HCP_PACKER_* environment variables."),
		})

		return diags
	}

	withHCLBucketConfigurtation := func(bb *hcl2template.BuildBlock) bucketConfigurationOpts {
		return func(bucket *registry.Bucket) hcl.Diagnostics {
			bb.HCPPackerRegistry.WriteToBucketConfig(bucket)
			// If at this point the bucket.Slug is still empty,
			// last try is to use the build.Name if present
			if bucket.Slug == "" && bb.Name != "" {
				bucket.Slug = bb.Name
			}

			// If the description is empty, use the one from the build block
			if bucket.Description == "" && bb.Description != "" {
				bucket.Description = bb.Description
			}
			return nil
		}
	}

	// Capture Datasource configuration data
	vals, dsDiags := pc.Datasources.Values()
	if dsDiags != nil {
		diags = append(diags, dsDiags...)
	}

	build := pc.Builds[0]
	pc.Bucket, diags = createConfiguredBucket(
		pc.Basedir,
		withPackerEnvConfiguration,
		withHCLBucketConfigurtation(build),
		withDatasourceConfiguration(vals),
	)

	if diags.HasErrors() {
		return diags
	}

	for _, source := range build.Sources {
		pc.Bucket.RegisterBuildForComponent(source.String())
	}

	return diags
}

func setupRegistryForPackerCore(cfg *CoreWrapper) hcl.Diagnostics {
	if env.IsHCPDisabled() {
		return nil
	}

	if !env.HasPackerRegistryBucket() && !env.IsHCPExplicitelyEnabled() {
		return nil
	}

	bucket, diags := createConfiguredBucket(
		filepath.Dir(cfg.Core.Template.Path),
		withPackerEnvConfiguration,
	)

	if diags.HasErrors() {
		return diags
	}

	cfg.Core.Bucket = bucket
	for _, b := range cfg.Core.Template.Builders {
		// Get all builds slated within config ignoring any only or exclude flags.
		cfg.Core.Bucket.RegisterBuildForComponent(b.Name)
	}

	return diags
}

type bucketConfigurationOpts func(*registry.Bucket) hcl.Diagnostics

// createConfiguredBucket returns a bucket that can be used for connecting to the HCP Packer registry.
// Configuration for the bucket is obtained from the base iteration setting and any addition configuration
// options passed in as opts. All errors during configuration are collected and returned as Diagnostics.
func createConfiguredBucket(templateDir string, opts ...bucketConfigurationOpts) (*registry.Bucket, hcl.Diagnostics) {
	var diags hcl.Diagnostics

	if !env.HasHCPCredentials() {
		diags = append(diags, &hcl.Diagnostic{
			Summary: "HCP authentication information required",
			Detail: fmt.Sprintf("The client authentication requires both %s and %s environment "+
				"variables to be set for authenticating with HCP.",
				env.HCPClientID,
				env.HCPClientSecret),
			Severity: hcl.DiagError,
		})
	}

	bucket, err := registry.NewBucketWithIteration(registry.IterationOptions{
		TemplateBaseDir: templateDir,
	})

	// This error needs to be reworded.
	if err != nil {
		diags = append(diags, &hcl.Diagnostic{
			Summary:  "Unable to create a valid bucket object for HCP Packer Registry",
			Detail:   fmt.Sprintf("%s", err),
			Severity: hcl.DiagError,
		})

		return nil, diags
	}

	for _, opt := range opts {
		if optDiags := opt(bucket); optDiags.HasErrors() {
			diags = append(diags, optDiags...)
		}
	}

	if bucket.Slug == "" {
		diags = append(diags, &hcl.Diagnostic{
			Summary:  "bucket name cannot be empty",
			Detail:   "empty bucket name, please set it with the HCP_PACKER_BUCKET_NAME environment variable, or in a `hcp_packer_registry` block",
			Severity: hcl.DiagError,
		})

		return nil, diags
	}

	return bucket, diags
}

func withPackerEnvConfiguration(bucket *registry.Bucket) hcl.Diagnostics {
	// Add default values for Packer settings configured via EnvVars.
	// TODO look to break this up to be more explicit on what is loaded here.
	bucket.LoadDefaultSettingsFromEnv()

	return nil
}

func imageValueToDSOutput(imageVal map[string]cty.Value) imgds.DatasourceOutput {
	dso := imgds.DatasourceOutput{}
	for k, v := range imageVal {
		switch k {
		case "id":
			dso.ID = v.AsString()
		case "region":
			dso.Region = v.AsString()
		case "labels":
			labels := map[string]string{}
			lbls := v.AsValueMap()
			for k, v := range lbls {
				labels[k] = v.AsString()
			}
			dso.Labels = labels
		case "packer_run_uuid":
			dso.PackerRunUUID = v.AsString()
		case "channel_id":
			dso.ChannelID = v.AsString()
		case "iteration_id":
			dso.IterationID = v.AsString()
		case "build_id":
			dso.BuildID = v.AsString()
		case "created_at":
			dso.CreatedAt = v.AsString()
		case "component_type":
			dso.ComponentType = v.AsString()
		case "cloud_provider":
			dso.CloudProvider = v.AsString()
		}
	}

	return dso
}

func iterValueToDSOutput(iterVal map[string]cty.Value) iterds.DatasourceOutput {
	dso := iterds.DatasourceOutput{}
	for k, v := range iterVal {
		switch k {
		case "author_id":
			dso.AuthorID = v.AsString()
		case "bucket_name":
			dso.BucketName = v.AsString()
		case "complete":
			// For all intents and purposes, cty.Value.True() acts
			// like a AsBool() would.
			dso.Complete = v.True()
		case "created_at":
			dso.CreatedAt = v.AsString()
		case "fingerprint":
			dso.Fingerprint = v.AsString()
		case "id":
			dso.ID = v.AsString()
		case "incremental_version":
			// Maybe when cty provides a good way to AsInt() a cty.Value
			// we can consider implementing this.
		case "updated_at":
			dso.UpdatedAt = v.AsString()
		case "channel_id":
			dso.ChannelID = v.AsString()
		}
	}
	return dso
}

func withDatasourceConfiguration(vals map[string]cty.Value) bucketConfigurationOpts {
	return func(bucket *registry.Bucket) hcl.Diagnostics {
		var diags hcl.Diagnostics

		imageDS, imageOK := vals[hcpImageDatasourceType]
		iterDS, iterOK := vals[hcpIterationDatasourceType]

		if !imageOK && !iterOK {
			return nil
		}

		iterations := map[string]iterds.DatasourceOutput{}

		var err error
		if iterOK {
			hcpData := map[string]cty.Value{}
			err = gocty.FromCtyValue(iterDS, &hcpData)
			if err != nil {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid HCP datasources",
					Detail:   fmt.Sprintf("Failed to decode hcp_packer_iteration datasources: %s", err),
				})
				return diags
			}

			for k, v := range hcpData {
				iterVals := v.AsValueMap()
				dso := iterValueToDSOutput(iterVals)
				iterations[k] = dso
			}
		}

		images := map[string]imgds.DatasourceOutput{}

		if imageOK {
			hcpData := map[string]cty.Value{}
			err = gocty.FromCtyValue(imageDS, &hcpData)
			if err != nil {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid HCP datasources",
					Detail:   fmt.Sprintf("Failed to decode hcp_packer_image datasources: %s", err),
				})
				return diags
			}

			for k, v := range hcpData {
				imageVals := v.AsValueMap()
				dso := imageValueToDSOutput(imageVals)
				images[k] = dso
			}
		}

		for _, img := range images {
			sourceIteration := registry.ParentIteration{}

			sourceIteration.IterationID = img.IterationID

			if img.ChannelID != "" {
				sourceIteration.ChannelID = img.ChannelID
			} else {
				for _, it := range iterations {
					if it.ID == img.IterationID {
						sourceIteration.ChannelID = it.ChannelID
						break
					}
				}
			}

			bucket.SourceImagesToParentIterations[img.ID] = sourceIteration
		}

		return diags
	}
}
<|endoftext|>