<commit_before>// Copyright 2021 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

// Package sdk contains a high-level API for embedding OPA inside of Go programs.
package sdk

import (
	"bytes"
	"context"
	"crypto/rand"
	"fmt"
	"sync"
	"time"

	"github.com/open-policy-agent/opa/ast"
	"github.com/open-policy-agent/opa/bundle"
	"github.com/open-policy-agent/opa/internal/ref"
	"github.com/open-policy-agent/opa/internal/runtime"
	"github.com/open-policy-agent/opa/internal/uuid"
	"github.com/open-policy-agent/opa/logging"
	"github.com/open-policy-agent/opa/metrics"
	"github.com/open-policy-agent/opa/plugins"
	"github.com/open-policy-agent/opa/plugins/discovery"
	"github.com/open-policy-agent/opa/plugins/logs"
	"github.com/open-policy-agent/opa/rego"
	"github.com/open-policy-agent/opa/server"
	"github.com/open-policy-agent/opa/storage"
	"github.com/open-policy-agent/opa/storage/inmem"
	"github.com/open-policy-agent/opa/topdown/cache"
)

// OPA represents an instance of the policy engine. OPA can be started with
// several options that control configuration, logging, and lifecycle.
type OPA struct {
	id      string
	state   *state
	mtx     sync.Mutex
	logger  logging.Logger
	console logging.Logger
	plugins map[string]plugins.Factory
	config  []byte
}

type state struct {
	manager                *plugins.Manager
	interQueryBuiltinCache cache.InterQueryCache
	queryCache             *queryCache
}

// New returns a new OPA object. This function should minimally be called with
// options that specify an OPA configuration file.
func New(ctx context.Context, opts Options) (*OPA, error) {

	id, err := uuid.New(rand.Reader)
	if err != nil {
		return nil, err
	}

	if err := opts.init(); err != nil {
		return nil, err
	}

	opa := &OPA{
		id: id,
		state: &state{
			queryCache: newQueryCache(),
		},
	}

	opa.config = opts.config
	opa.logger = opts.Logger
	opa.console = opts.ConsoleLogger
	opa.plugins = opts.Plugins

	return opa, opa.configure(ctx, opa.config, opts.Ready, opts.block)
}

// Configure updates the configuration of the OPA in-place. This function should
// be called in response to configuration updates in the environment. This
// function is atomic. If the configuration update cannot be successfully
// applied, the old configuration will remain intact.
func (opa *OPA) Configure(ctx context.Context, opts ConfigOptions) error {

	if err := opts.init(); err != nil {
		return err
	}

	// NOTE(tsandall): In future we could be more intelligent about
	// re-configuration and avoid expensive background processing.
	opa.mtx.Lock()
	equal := bytes.Equal(opts.config, opa.config)
	opa.mtx.Unlock()

	if equal {
		close(opts.Ready)
		return nil
	}

	return opa.configure(ctx, opts.config, opts.Ready, opts.block)
}

func (opa *OPA) configure(ctx context.Context, bs []byte, ready chan struct{}, block bool) error {
	info, err := runtime.Term(runtime.Params{Config: opa.config})
	if err != nil {
		return err
	}

	manager, err := plugins.New(
		bs,
		opa.id,
		inmem.New(),
		plugins.Info(info),
		plugins.Logger(opa.logger),
		plugins.ConsoleLogger(opa.console))
	if err != nil {
		return err
	}

	manager.RegisterCompilerTrigger(func(_ storage.Transaction) {
		opa.mtx.Lock()
		opa.state.queryCache.Clear()
		opa.mtx.Unlock()
	})

	manager.RegisterPluginStatusListener("sdk", func(status map[string]*plugins.Status) {

		select {
		case <-ready:
			return
		default:
		}
		// NOTE(tsandall): we do not include a special case for the discovery
		// plugin. If the discovery plugin is the only plugin and it goes ready,
		// then OPA will be considered ready. The discovery plugin only goes ready
		// _after_ it has successfully processed a discovery bundle. During
		// discovery bundle processing, other plugins will register so their states
		// will be accounted for. If a discovery bundle did not enable any other
		// plugins (bundles, etc.) the OPA will still be operational.
		for _, s := range status {
			if s.State != plugins.StateOK {
				return
			}
		}

		close(ready)
	})

	d, err := discovery.New(manager, discovery.Factories(opa.plugins))
	if err != nil {
		return err
	}

	manager.Register(discovery.Name, d)

	if err := manager.Start(ctx); err != nil {
		return err
	}

	if block {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ready:
		}
	}

	opa.mtx.Lock()
	defer opa.mtx.Unlock()

	// NOTE(tsandall): there is no return value from Stop() and it could block
	// on async operations (e.g., decision log uploading) so defer the call to
	// another goroutine.
	//
	// TODO(tsandall): if we need to block on operations like decision log
	// uploading, perhaps we could rely on a manual trigger.
	previousManager := opa.state.manager
	go func() {
		if previousManager != nil {
			previousManager.Stop(ctx)
		}
	}()

	opa.state.manager = manager
	opa.state.queryCache.Clear()
	opa.state.interQueryBuiltinCache = cache.NewInterQueryCache(manager.InterQueryBuiltinCacheConfig())
	opa.config = bs

	return nil
}

// Stop closes the OPA. The OPA cannot be restarted.
func (opa *OPA) Stop(ctx context.Context) {

	opa.mtx.Lock()
	mgr := opa.state.manager
	opa.mtx.Unlock()

	if mgr != nil {
		mgr.Stop(ctx)
	}
}

// Decision returns a named decision. This function is threadsafe.
func (opa *OPA) Decision(ctx context.Context, options DecisionOptions) (*DecisionResult, error) {

	m := metrics.New()
	m.Timer(metrics.SDKDecisionEval).Start()

	result, err := newDecisionResult()
	if err != nil {
		return nil, err
	}

	opa.mtx.Lock()
	s := *opa.state
	opa.mtx.Unlock()

	record := server.Info{
		DecisionID: result.ID,
		Timestamp:  options.Now,
		Path:       options.Path,
		Input:      &options.Input,
		Metrics:    m,
	}

	if record.Timestamp.IsZero() {
		record.Timestamp = time.Now().UTC()
	}

	if record.Path == "" {
		record.Path = *s.manager.Config.DefaultDecision
	}

	record.Txn, record.Error = s.manager.Store.NewTransaction(ctx, storage.TransactionParams{})

	if record.Error == nil {
		defer s.manager.Store.Abort(ctx, record.Txn)
		result.Result, record.InputAST, record.Bundles, record.Error = evaluate(ctx, evalArgs{
			runtime:         s.manager.Info,
			compiler:        s.manager.GetCompiler(),
			store:           s.manager.Store,
			txn:             record.Txn,
			queryCache:      s.queryCache,
			interQueryCache: s.interQueryBuiltinCache,
			now:             record.Timestamp,
			path:            record.Path,
			input:           *record.Input,
			m:               record.Metrics,
		})
		if record.Error == nil {
			record.Results = &result.Result
		}
	}

	m.Timer(metrics.SDKDecisionEval).Stop()

	if logger := logs.Lookup(s.manager); logger != nil {
		if err := logger.Log(ctx, &record); err != nil {
			return result, fmt.Errorf("decision log: %w", err)
		}
	}

	return result, record.Error
}

// DecisionOptions contains parameters for query evaluation.
type DecisionOptions struct {
	Now   time.Time   // specifies wallclock time used for time.now_ns(), decision log timestamp, etc.
	Path  string      // specifies name of policy decision to evaluate (e.g., example/allow)
	Input interface{} // specifies value of the input document to evaluate policy with
}

// DecisionResult contains the output of query evaluation.
type DecisionResult struct {
	ID     string      // provides a globally unique identifier for this decision (which is included in the decision log.)
	Result interface{} // provides the output of query evaluation.
}

func newDecisionResult() (*DecisionResult, error) {
	id, err := uuid.New(rand.Reader)
	if err != nil {
		return nil, err
	}
	result := &DecisionResult{ID: id}
	return result, nil
}

// Error represents an internal error in the SDK.
type Error struct {
	Code    string `json:"code"`
	Message string `json:"message,omitempty"`
}

func (err *Error) Error() string {
	return fmt.Sprintf("%v: %v", err.Code, err.Message)
}

const (
	// UndefinedErr indicates that the queried decision was undefined.
	UndefinedErr = "opa_undefined_error"
)

func undefinedDecisionErr(path string) *Error {
	return &Error{
		Code:    UndefinedErr,
		Message: fmt.Sprintf("%v decision was undefined", path),
	}
}

// IsUndefinedErr returns true of the err represents an undefined decision error.
func IsUndefinedErr(err error) bool {
	actual, ok := err.(*Error)
	return ok && actual.Code == UndefinedErr
}

type evalArgs struct {
	runtime         *ast.Term
	compiler        *ast.Compiler
	store           storage.Store
	txn             storage.Transaction
	queryCache      *queryCache
	interQueryCache cache.InterQueryCache
	now             time.Time
	path            string
	input           interface{}
	m               metrics.Metrics
}

func evaluate(ctx context.Context, args evalArgs) (interface{}, ast.Value, map[string]server.BundleInfo, error) {

	bundles, err := bundles(ctx, args.store, args.txn)
	if err != nil {
		return nil, nil, nil, err
	}

	r, err := ref.ParseDataPath(args.path)
	if err != nil {
		return nil, nil, bundles, err
	}

	pq, err := args.queryCache.Get(r.String(), func(query string) (*rego.PreparedEvalQuery, error) {
		pq, err := rego.New(
			rego.Time(args.now),
			rego.Metrics(args.m),
			rego.Query(query),
			rego.Compiler(args.compiler),
			rego.Store(args.store),
			rego.Transaction(args.txn),
			rego.Runtime(args.runtime)).PrepareForEval(ctx)
		if err != nil {
			return nil, err
		}
		return &pq, err
	})
	if err != nil {
		return nil, nil, bundles, err
	}

	inputAST, err := ast.InterfaceToValue(args.input)
	if err != nil {
		return nil, nil, bundles, err
	}

	rs, err := pq.Eval(
		ctx,
		rego.EvalTime(args.now),
		rego.EvalParsedInput(inputAST),
		rego.EvalTransaction(args.txn),
		rego.EvalMetrics(args.m),
		rego.EvalInterQueryBuiltinCache(args.interQueryCache),
	)
	if err != nil {
		return nil, inputAST, bundles, err
	} else if len(rs) == 0 {
		return nil, inputAST, bundles, undefinedDecisionErr(args.path)
	}

	return rs[0].Expressions[0].Value, inputAST, bundles, nil
}

type queryCache struct {
	sync.Mutex
	cache map[string]*rego.PreparedEvalQuery
}

func newQueryCache() *queryCache {
	return &queryCache{cache: map[string]*rego.PreparedEvalQuery{}}
}

func (qc *queryCache) Get(key string, orElse func(string) (*rego.PreparedEvalQuery, error)) (*rego.PreparedEvalQuery, error) {
	qc.Lock()
	defer qc.Unlock()

	result, ok := qc.cache[key]
	if ok {
		return result, nil
	}

	result, err := orElse(key)
	if err != nil {
		return nil, err
	}

	qc.cache[key] = result
	return result, nil
}

func (qc *queryCache) Clear() {
	qc.Lock()
	defer qc.Unlock()

	qc.cache = make(map[string]*rego.PreparedEvalQuery)
}

func bundles(ctx context.Context, store storage.Store, txn storage.Transaction) (map[string]server.BundleInfo, error) {
	bundles := map[string]server.BundleInfo{}
	names, err := bundle.ReadBundleNamesFromStore(ctx, store, txn)
	if err != nil && !storage.IsNotFound(err) {
		return nil, fmt.Errorf("failed to read bundle names: %w", err)
	}
	for _, name := range names {
		r, err := bundle.ReadBundleRevisionFromStore(ctx, store, txn, name)
		if err != nil {
			return nil, fmt.Errorf("failed to read bundle revisions: %w", err)
		}
		bundles[name] = server.BundleInfo{Revision: r}
	}
	return bundles, nil
}
<commit_msg>sdk: expose plugins on the sdk object<commit_after>// Copyright 2021 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

// Package sdk contains a high-level API for embedding OPA inside of Go programs.
package sdk

import (
	"bytes"
	"context"
	"crypto/rand"
	"fmt"
	"sync"
	"time"

	"github.com/open-policy-agent/opa/ast"
	"github.com/open-policy-agent/opa/bundle"
	"github.com/open-policy-agent/opa/internal/ref"
	"github.com/open-policy-agent/opa/internal/runtime"
	"github.com/open-policy-agent/opa/internal/uuid"
	"github.com/open-policy-agent/opa/logging"
	"github.com/open-policy-agent/opa/metrics"
	"github.com/open-policy-agent/opa/plugins"
	"github.com/open-policy-agent/opa/plugins/discovery"
	"github.com/open-policy-agent/opa/plugins/logs"
	"github.com/open-policy-agent/opa/rego"
	"github.com/open-policy-agent/opa/server"
	"github.com/open-policy-agent/opa/storage"
	"github.com/open-policy-agent/opa/storage/inmem"
	"github.com/open-policy-agent/opa/topdown/cache"
)

// OPA represents an instance of the policy engine. OPA can be started with
// several options that control configuration, logging, and lifecycle.
type OPA struct {
	id      string
	state   *state
	mtx     sync.Mutex
	logger  logging.Logger
	console logging.Logger
	plugins map[string]plugins.Factory
	config  []byte
}

type state struct {
	manager                *plugins.Manager
	interQueryBuiltinCache cache.InterQueryCache
	queryCache             *queryCache
}

// New returns a new OPA object. This function should minimally be called with
// options that specify an OPA configuration file.
func New(ctx context.Context, opts Options) (*OPA, error) {

	id, err := uuid.New(rand.Reader)
	if err != nil {
		return nil, err
	}

	if err := opts.init(); err != nil {
		return nil, err
	}

	opa := &OPA{
		id: id,
		state: &state{
			queryCache: newQueryCache(),
		},
	}

	opa.config = opts.config
	opa.logger = opts.Logger
	opa.console = opts.ConsoleLogger
	opa.plugins = opts.Plugins

	return opa, opa.configure(ctx, opa.config, opts.Ready, opts.block)
}

// Plugin returns the named plugin. If the plugin does not exist, this function
// returns nil.
func (opa *OPA) Plugin(name string) plugins.Plugin {
	opa.mtx.Lock()
	defer opa.mtx.Unlock()
	return opa.state.manager.Plugin(name)
}

// Configure updates the configuration of the OPA in-place. This function should
// be called in response to configuration updates in the environment. This
// function is atomic. If the configuration update cannot be successfully
// applied, the old configuration will remain intact.
func (opa *OPA) Configure(ctx context.Context, opts ConfigOptions) error {

	if err := opts.init(); err != nil {
		return err
	}

	// NOTE(tsandall): In future we could be more intelligent about
	// re-configuration and avoid expensive background processing.
	opa.mtx.Lock()
	equal := bytes.Equal(opts.config, opa.config)
	opa.mtx.Unlock()

	if equal {
		close(opts.Ready)
		return nil
	}

	return opa.configure(ctx, opts.config, opts.Ready, opts.block)
}

func (opa *OPA) configure(ctx context.Context, bs []byte, ready chan struct{}, block bool) error {
	info, err := runtime.Term(runtime.Params{Config: opa.config})
	if err != nil {
		return err
	}

	manager, err := plugins.New(
		bs,
		opa.id,
		inmem.New(),
		plugins.Info(info),
		plugins.Logger(opa.logger),
		plugins.ConsoleLogger(opa.console))
	if err != nil {
		return err
	}

	manager.RegisterCompilerTrigger(func(_ storage.Transaction) {
		opa.mtx.Lock()
		opa.state.queryCache.Clear()
		opa.mtx.Unlock()
	})

	manager.RegisterPluginStatusListener("sdk", func(status map[string]*plugins.Status) {

		select {
		case <-ready:
			return
		default:
		}
		// NOTE(tsandall): we do not include a special case for the discovery
		// plugin. If the discovery plugin is the only plugin and it goes ready,
		// then OPA will be considered ready. The discovery plugin only goes ready
		// _after_ it has successfully processed a discovery bundle. During
		// discovery bundle processing, other plugins will register so their states
		// will be accounted for. If a discovery bundle did not enable any other
		// plugins (bundles, etc.) the OPA will still be operational.
		for _, s := range status {
			if s.State != plugins.StateOK {
				return
			}
		}

		close(ready)
	})

	d, err := discovery.New(manager, discovery.Factories(opa.plugins))
	if err != nil {
		return err
	}

	manager.Register(discovery.Name, d)

	if err := manager.Start(ctx); err != nil {
		return err
	}

	if block {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ready:
		}
	}

	opa.mtx.Lock()
	defer opa.mtx.Unlock()

	// NOTE(tsandall): there is no return value from Stop() and it could block
	// on async operations (e.g., decision log uploading) so defer the call to
	// another goroutine.
	//
	// TODO(tsandall): if we need to block on operations like decision log
	// uploading, perhaps we could rely on a manual trigger.
	previousManager := opa.state.manager
	go func() {
		if previousManager != nil {
			previousManager.Stop(ctx)
		}
	}()

	opa.state.manager = manager
	opa.state.queryCache.Clear()
	opa.state.interQueryBuiltinCache = cache.NewInterQueryCache(manager.InterQueryBuiltinCacheConfig())
	opa.config = bs

	return nil
}

// Stop closes the OPA. The OPA cannot be restarted.
func (opa *OPA) Stop(ctx context.Context) {

	opa.mtx.Lock()
	mgr := opa.state.manager
	opa.mtx.Unlock()

	if mgr != nil {
		mgr.Stop(ctx)
	}
}

// Decision returns a named decision. This function is threadsafe.
func (opa *OPA) Decision(ctx context.Context, options DecisionOptions) (*DecisionResult, error) {

	m := metrics.New()
	m.Timer(metrics.SDKDecisionEval).Start()

	result, err := newDecisionResult()
	if err != nil {
		return nil, err
	}

	opa.mtx.Lock()
	s := *opa.state
	opa.mtx.Unlock()

	record := server.Info{
		DecisionID: result.ID,
		Timestamp:  options.Now,
		Path:       options.Path,
		Input:      &options.Input,
		Metrics:    m,
	}

	if record.Timestamp.IsZero() {
		record.Timestamp = time.Now().UTC()
	}

	if record.Path == "" {
		record.Path = *s.manager.Config.DefaultDecision
	}

	record.Txn, record.Error = s.manager.Store.NewTransaction(ctx, storage.TransactionParams{})

	if record.Error == nil {
		defer s.manager.Store.Abort(ctx, record.Txn)
		result.Result, record.InputAST, record.Bundles, record.Error = evaluate(ctx, evalArgs{
			runtime:         s.manager.Info,
			compiler:        s.manager.GetCompiler(),
			store:           s.manager.Store,
			txn:             record.Txn,
			queryCache:      s.queryCache,
			interQueryCache: s.interQueryBuiltinCache,
			now:             record.Timestamp,
			path:            record.Path,
			input:           *record.Input,
			m:               record.Metrics,
		})
		if record.Error == nil {
			record.Results = &result.Result
		}
	}

	m.Timer(metrics.SDKDecisionEval).Stop()

	if logger := logs.Lookup(s.manager); logger != nil {
		if err := logger.Log(ctx, &record); err != nil {
			return result, fmt.Errorf("decision log: %w", err)
		}
	}

	return result, record.Error
}

// DecisionOptions contains parameters for query evaluation.
type DecisionOptions struct {
	Now   time.Time   // specifies wallclock time used for time.now_ns(), decision log timestamp, etc.
	Path  string      // specifies name of policy decision to evaluate (e.g., example/allow)
	Input interface{} // specifies value of the input document to evaluate policy with
}

// DecisionResult contains the output of query evaluation.
type DecisionResult struct {
	ID     string      // provides a globally unique identifier for this decision (which is included in the decision log.)
	Result interface{} // provides the output of query evaluation.
}

func newDecisionResult() (*DecisionResult, error) {
	id, err := uuid.New(rand.Reader)
	if err != nil {
		return nil, err
	}
	result := &DecisionResult{ID: id}
	return result, nil
}

// Error represents an internal error in the SDK.
type Error struct {
	Code    string `json:"code"`
	Message string `json:"message,omitempty"`
}

func (err *Error) Error() string {
	return fmt.Sprintf("%v: %v", err.Code, err.Message)
}

const (
	// UndefinedErr indicates that the queried decision was undefined.
	UndefinedErr = "opa_undefined_error"
)

func undefinedDecisionErr(path string) *Error {
	return &Error{
		Code:    UndefinedErr,
		Message: fmt.Sprintf("%v decision was undefined", path),
	}
}

// IsUndefinedErr returns true of the err represents an undefined decision error.
func IsUndefinedErr(err error) bool {
	actual, ok := err.(*Error)
	return ok && actual.Code == UndefinedErr
}

type evalArgs struct {
	runtime         *ast.Term
	compiler        *ast.Compiler
	store           storage.Store
	txn             storage.Transaction
	queryCache      *queryCache
	interQueryCache cache.InterQueryCache
	now             time.Time
	path            string
	input           interface{}
	m               metrics.Metrics
}

func evaluate(ctx context.Context, args evalArgs) (interface{}, ast.Value, map[string]server.BundleInfo, error) {

	bundles, err := bundles(ctx, args.store, args.txn)
	if err != nil {
		return nil, nil, nil, err
	}

	r, err := ref.ParseDataPath(args.path)
	if err != nil {
		return nil, nil, bundles, err
	}

	pq, err := args.queryCache.Get(r.String(), func(query string) (*rego.PreparedEvalQuery, error) {
		pq, err := rego.New(
			rego.Time(args.now),
			rego.Metrics(args.m),
			rego.Query(query),
			rego.Compiler(args.compiler),
			rego.Store(args.store),
			rego.Transaction(args.txn),
			rego.Runtime(args.runtime)).PrepareForEval(ctx)
		if err != nil {
			return nil, err
		}
		return &pq, err
	})
	if err != nil {
		return nil, nil, bundles, err
	}

	inputAST, err := ast.InterfaceToValue(args.input)
	if err != nil {
		return nil, nil, bundles, err
	}

	rs, err := pq.Eval(
		ctx,
		rego.EvalTime(args.now),
		rego.EvalParsedInput(inputAST),
		rego.EvalTransaction(args.txn),
		rego.EvalMetrics(args.m),
		rego.EvalInterQueryBuiltinCache(args.interQueryCache),
	)
	if err != nil {
		return nil, inputAST, bundles, err
	} else if len(rs) == 0 {
		return nil, inputAST, bundles, undefinedDecisionErr(args.path)
	}

	return rs[0].Expressions[0].Value, inputAST, bundles, nil
}

type queryCache struct {
	sync.Mutex
	cache map[string]*rego.PreparedEvalQuery
}

func newQueryCache() *queryCache {
	return &queryCache{cache: map[string]*rego.PreparedEvalQuery{}}
}

func (qc *queryCache) Get(key string, orElse func(string) (*rego.PreparedEvalQuery, error)) (*rego.PreparedEvalQuery, error) {
	qc.Lock()
	defer qc.Unlock()

	result, ok := qc.cache[key]
	if ok {
		return result, nil
	}

	result, err := orElse(key)
	if err != nil {
		return nil, err
	}

	qc.cache[key] = result
	return result, nil
}

func (qc *queryCache) Clear() {
	qc.Lock()
	defer qc.Unlock()

	qc.cache = make(map[string]*rego.PreparedEvalQuery)
}

func bundles(ctx context.Context, store storage.Store, txn storage.Transaction) (map[string]server.BundleInfo, error) {
	bundles := map[string]server.BundleInfo{}
	names, err := bundle.ReadBundleNamesFromStore(ctx, store, txn)
	if err != nil && !storage.IsNotFound(err) {
		return nil, fmt.Errorf("failed to read bundle names: %w", err)
	}
	for _, name := range names {
		r, err := bundle.ReadBundleRevisionFromStore(ctx, store, txn, name)
		if err != nil {
			return nil, fmt.Errorf("failed to read bundle revisions: %w", err)
		}
		bundles[name] = server.BundleInfo{Revision: r}
	}
	return bundles, nil
}
<|endoftext|>


<commit_before>package movie

import (
	"context"
	"time"

	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/google/uuid"
	"github.com/pkg/errors"
)

// Writer is used to create/update a Movie
type Writer interface {
	Add(ctx context.Context) error
	Update(ctx context.Context, id string) error
}

// NewMovie initializes a Movie struct for use in Movie creation
func NewMovie(id uuid.UUID, extlID string, u *user.User) (*Movie, error) {
	switch {
	case id == uuid.Nil:
		return nil, errs.E(errs.Validation, errs.Parameter("ID"), errors.New(errs.MissingField("ID").Error()))
	case extlID == "":
		return nil, errs.E(errs.Validation, errs.Parameter("ID"), errors.New(errs.MissingField("ID").Error()))
	case !u.IsValid():
		return nil, errs.E(errs.Validation, errs.Parameter("User"), errors.New("User is invalid"))
	}

	now := time.Now()

	return &Movie{
		ID:         id,
		ExternalID: extlID,
		CreateUser: *u,
		CreateTime: now,
		UpdateUser: *u,
		UpdateTime: now,
	}, nil
}

// Movie holds details of a movie
type Movie struct {
	ID         uuid.UUID
	ExternalID string
	Title      string
	Rated      string
	Released   time.Time
	RunTime    int
	Director   string
	Writer     string
	CreateUser user.User
	CreateTime time.Time
	UpdateUser user.User
	UpdateTime time.Time
}

func (m *Movie) SetExternalID(id string) *Movie {
	m.ExternalID = id
	return m
}

func (m *Movie) SetTitle(t string) *Movie {
	m.Title = t
	return m
}

func (m *Movie) SetRated(r string) *Movie {
	m.Rated = r
	return m
}

func (m *Movie) SetReleased(r string) (*Movie, error) {
	t, err := time.Parse(time.RFC3339, r)
	if err != nil {
		return nil, errs.E(errs.Validation,
			errs.Code("invalid_date_format"),
			errs.Parameter("release_date"),
			errors.WithStack(err))
	}
	m.Released = t
	return m, nil
}

func (m *Movie) SetRunTime(rt int) *Movie {
	m.RunTime = rt
	return m
}

func (m *Movie) SetDirector(d string) *Movie {
	m.Director = d
	return m
}

func (m *Movie) SetWriter(w string) *Movie {
	m.Writer = w
	return m
}

func (m *Movie) SetUpdateUser(u *user.User) *Movie {
	m.UpdateUser = *u
	return m
}

func (m *Movie) SetUpdateTime() *Movie {
	m.UpdateTime = time.Now().UTC()
	return m
}

// IsValid performs validation of the struct
func (m *Movie) IsValid() error {
	switch {
	case m.Title == "":
		return errs.E(errs.Validation, errs.Parameter("title"), errs.MissingField("title"))
	case m.Rated == "":
		return errs.E(errs.Validation, errs.Parameter("rated"), errs.MissingField("Rated"))
	case m.Released.IsZero() == true:
		return errs.E(errs.Validation, errs.Parameter("release_date"), "Released must have a value")
	case m.RunTime <= 0:
		return errs.E(errs.Validation, errs.Parameter("run_time"), "Run time must be greater than zero")
	case m.Director == "":
		return errs.E(errs.Validation, errs.Parameter("director"), errs.MissingField("Director"))
	case m.Writer == "":
		return errs.E(errs.Validation, errs.Parameter("writer"), errs.MissingField("Writer"))
	}

	return nil
}
<commit_msg>use utc<commit_after>package movie

import (
	"context"
	"time"

	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/google/uuid"
	"github.com/pkg/errors"
)

// Writer is used to create/update a Movie
type Writer interface {
	Add(ctx context.Context) error
	Update(ctx context.Context, id string) error
}

// NewMovie initializes a Movie struct for use in Movie creation
func NewMovie(id uuid.UUID, extlID string, u *user.User) (*Movie, error) {
	switch {
	case id == uuid.Nil:
		return nil, errs.E(errs.Validation, errs.Parameter("ID"), errors.New(errs.MissingField("ID").Error()))
	case extlID == "":
		return nil, errs.E(errs.Validation, errs.Parameter("ID"), errors.New(errs.MissingField("ID").Error()))
	case !u.IsValid():
		return nil, errs.E(errs.Validation, errs.Parameter("User"), errors.New("User is invalid"))
	}

	now := time.Now().UTC()

	return &Movie{
		ID:         id,
		ExternalID: extlID,
		CreateUser: *u,
		CreateTime: now,
		UpdateUser: *u,
		UpdateTime: now,
	}, nil
}

// Movie holds details of a movie
type Movie struct {
	ID         uuid.UUID
	ExternalID string
	Title      string
	Rated      string
	Released   time.Time
	RunTime    int
	Director   string
	Writer     string
	CreateUser user.User
	CreateTime time.Time
	UpdateUser user.User
	UpdateTime time.Time
}

func (m *Movie) SetExternalID(id string) *Movie {
	m.ExternalID = id
	return m
}

func (m *Movie) SetTitle(t string) *Movie {
	m.Title = t
	return m
}

func (m *Movie) SetRated(r string) *Movie {
	m.Rated = r
	return m
}

func (m *Movie) SetReleased(r string) (*Movie, error) {
	t, err := time.Parse(time.RFC3339, r)
	if err != nil {
		return nil, errs.E(errs.Validation,
			errs.Code("invalid_date_format"),
			errs.Parameter("release_date"),
			errors.WithStack(err))
	}
	m.Released = t
	return m, nil
}

func (m *Movie) SetRunTime(rt int) *Movie {
	m.RunTime = rt
	return m
}

func (m *Movie) SetDirector(d string) *Movie {
	m.Director = d
	return m
}

func (m *Movie) SetWriter(w string) *Movie {
	m.Writer = w
	return m
}

func (m *Movie) SetUpdateUser(u *user.User) *Movie {
	m.UpdateUser = *u
	return m
}

func (m *Movie) SetUpdateTime() *Movie {
	m.UpdateTime = time.Now().UTC()
	return m
}

// IsValid performs validation of the struct
func (m *Movie) IsValid() error {
	switch {
	case m.Title == "":
		return errs.E(errs.Validation, errs.Parameter("title"), errs.MissingField("title"))
	case m.Rated == "":
		return errs.E(errs.Validation, errs.Parameter("rated"), errs.MissingField("Rated"))
	case m.Released.IsZero() == true:
		return errs.E(errs.Validation, errs.Parameter("release_date"), "Released must have a value")
	case m.RunTime <= 0:
		return errs.E(errs.Validation, errs.Parameter("run_time"), "Run time must be greater than zero")
	case m.Director == "":
		return errs.E(errs.Validation, errs.Parameter("director"), errs.MissingField("Director"))
	case m.Writer == "":
		return errs.E(errs.Validation, errs.Parameter("writer"), errs.MissingField("Writer"))
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "math/rand"
    "time"
    "strconv"
    "runtime"
    "sync"
    "log"
)
var wg sync.WaitGroup

func main() {
  runtime.GOMAXPROCS(2)
  wg.Add(1)
  var mode, numTables, total, tries, avg int = 0, 0, 0, 0, 0
  fmt.Println("\n\tGame modes:\n1: Pick a random table\n2: Find the avg tries it takes to solve a puzzle\n\nPlease choose 1 or 2: ")
  fmt.Scanf("%d", &mode)
  fmt.Println("\n")
  if mode == 1 {
    picker()
  } else if mode == 2 {
    fmt.Print("How many tables would you like to calculate for: ")
    _, err := fmt.Scanf("%d", &numTables)
    if err != nil {
      log.Fatal(err)
    }
    fmt.Print("How many times would you like to run the experiment: ")
    _, err2 := fmt.Scanf("%d", &tries)
    if err2 != nil {
      log.Fatal(err2)
    }
    fmt.Println("\n")
    go calc()
    go printAvg(numTables, total, avg, tries)
    wg.Wait()
  } else {
    fmt.Println("Goodbye!")
  }
}

func printAvg(numTables int, total int, avg int, tries int) {
  defer wg.Done()
  for i := 1; i <= tries; i++ {
    total += avgFinder(numTables)
  }
  avg = total/tries
  fmt.Printf("\rThe avg tries it took to solve %v tables was %v\n\n", numTables, avg)
}

func calc() {
  for {
    for i := "Calculating";; i += "." {
      fmt.Printf("\r%s", i)
      time.Sleep(500 * time.Millisecond)
    }
  }
}

func printWelcome() {
  welcome := []string{"\nWelcome to Colin's Random Table Picker", "Tables are eliminated when number is drawn,", "And put back in the game when their number is drawn again.", "Last number left wins!", "Good Luck.\n"}
  for _, item := range welcome {
    fmt.Println(item)
    time.Sleep(400 * time.Millisecond)
  }
}

func picker() {
  var tables, comp, wait int
  var compString string
  printWelcome()
  for {
    fmt.Print("How many tables are playing: ")
    _, err := fmt.Scanf("%d", &tables)
    if err != nil {
      log.Fatal(err)
    }
    fmt.Print("How many ms would you like to wait between each iteration: ")
    _, err2 := fmt.Scanf("%d", &wait)
    if err2 != nil {
      log.Fatal(err2)
    }
    if tables > 0 {
      break
    }
  }
  tableArray := make([]string, tables)
  for i, _ := range tableArray {
    tableArray[i] = strconv.Itoa(i + 1)
  }
  var total int = 0
  for {
    r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    comp = r.Intn(tables) + 1;
    compString = strconv.Itoa(comp)
    var numX int = 0
    total++
    fmt.Println("Random number is:", compString)
    if tableArray[comp - 1] == compString {
      tableArray[comp - 1] = "x"
    } else {
      tableArray[comp - 1] = compString
    }
    fmt.Println(tableArray)
    for _, element := range tableArray {
      if element == "x" {
        numX++
      }
    }
    if numX >= tables - 1 {
      for _, element := range tableArray {
        if element != "x" {
          fmt.Println("The lucky winner is", element, "chosen after", total, "rounds.\n")
          break
        }
      }
      break
    }
    time.Sleep(time.Duration(wait) * time.Millisecond)
  }
}

func avgFinder(tables int) int{
  var comp, total int = 0, 0
  var compString string
  tableArray := make([]string, tables)
  for i, _ := range tableArray {
    tableArray[i] = strconv.Itoa(i + 1)
  }
  for {
    r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    comp = r.Intn(tables) + 1;
    compString = strconv.Itoa(comp)
    var numX int = 0
    total++
    if tableArray[comp - 1] == compString {
      tableArray[comp - 1] = "x"
    } else {
      tableArray[comp - 1] = compString
    }
    for _, element := range tableArray {
      if element == "x" {
        numX++
      }
    }
    if numX >= tables - 1 {
      break
    }
  }
  return total
}

/*
AVERAGES:
1:  0
2:  1
3:  2
4:  6
5:  11
6:  19
7:  34
8:  57
9:  95
10: 164
11: 283
12: 494
*/
<commit_msg>Changed output colors<commit_after>package main

import (
    "fmt"
    "math/rand"
    "time"
    "strconv"
    "runtime"
    "sync"
    "log"

    "github.com/fatih/color"
)
var wg sync.WaitGroup

var re *color.Color = color.New(color.FgRed)
var red *color.Color = re.Add(color.BgBlack)
var boldRed *color.Color = red.Add(color.Bold)

func main() {

  runtime.GOMAXPROCS(2)
  wg.Add(1)

  var mode, numTables, total, tries int = 0, 0, 0, 0
  var avg float32 = 0
  red.Println("\n\tGame modes:\n1: Pick a random table\n2: Find the avg tries it takes to solve a puzzle\n\nPlease choose 1 or 2: ")
  _, err := fmt.Scanf("%d", &mode)
  if err != nil {
    log.Fatal(err)
  }

  if mode == 1 {
    picker()
  } else if mode == 2 {
    red.Print("How many tables would you like to calculate for: ")
    _, err2 := fmt.Scanf("%d", &numTables)
    if err2 != nil {
      log.Fatal(err2)
    }
    red.Print("How many times would you like to run the experiment: ")
    _, err3 := fmt.Scanf("%d", &tries)
    if err3 != nil {
      log.Fatal(err3)
    }
    fmt.Println("\n")
    go calc()
    go printAvg(numTables, total, avg, tries)
    wg.Wait()
  } else {
    red.Println("Goodbye!")
  }
}

func printAvg(numTables int, total int, avg float32, tries int) {
  defer wg.Done()
  for i := 1; i <= tries; i++ {
    total += avgFinder(numTables)
  }
  //a := color.New(color.FgRed, color.Bold)
  avg = float32(total)/float32(tries)
  boldRed.Printf("\rThe avg tries it took to solve %v tables was %v                   \n\n", numTables, avg)
}

func calc() {
  for {
    for i := "Calculating";; i += "." {
      red.Printf("\r%s", i)
      time.Sleep(500 * time.Millisecond)
    }
  }
}

func printWelcome() {
  welcome := []string{"\nWelcome to Colin's Random Table Picker", "Tables are eliminated when number is drawn,", "And put back in the game when their number is drawn again.", "Last number left wins!", "Good Luck.\n"}
  for _, item := range welcome {
    red.Println(item)
    time.Sleep(400 * time.Millisecond)
  }
}

func picker() {
  var tables, comp, wait int
  var compString string
  printWelcome()
  for {
    red.Print("How many tables are playing: ")
    _, err := fmt.Scanf("%d", &tables)
    if err != nil {
      log.Fatal(err)
    }
    red.Print("How many ms would you like to wait between each iteration: ")
    _, err2 := fmt.Scanf("%d", &wait)
    if err2 != nil {
      log.Fatal(err2)
    }
    if tables > 0 {
      break
    }
  }
  tableArray := make([]string, tables)
  for i, _ := range tableArray {
    tableArray[i] = strconv.Itoa(i + 1)
  }
  var total int = 0
  for {
    r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    comp = r.Intn(tables) + 1;
    compString = strconv.Itoa(comp)
    var numX int = 0
    total++
    red.Println("Random number is:", compString)
    if tableArray[comp - 1] == compString {
      tableArray[comp - 1] = "x"
    } else {
      tableArray[comp - 1] = compString
    }
    red.Println(tableArray)
    for _, element := range tableArray {
      if element == "x" {
        numX++
      }
    }
    if numX >= tables - 1 {
      for _, element := range tableArray {
        if element != "x" {
          boldRed.Println("The lucky winner is", element, "chosen after", total, "rounds.\n")
          break
        }
      }
      break
    }
    time.Sleep(time.Duration(wait) * time.Millisecond)
  }
}

func avgFinder(tables int) int{
  var comp, total int = 0, 0
  var compString string
  tableArray := make([]string, tables)
  for i, _ := range tableArray {
    tableArray[i] = strconv.Itoa(i + 1)
  }
  for {
    r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    comp = r.Intn(tables) + 1;
    compString = strconv.Itoa(comp)
    var numX int = 0
    total++
    if tableArray[comp - 1] == compString {
      tableArray[comp - 1] = "x"
    } else {
      tableArray[comp - 1] = compString
    }
    for _, element := range tableArray {
      if element == "x" {
        numX++
      }
    }
    if numX >= tables - 1 {
      break
    }
  }
  return total
}

/*
AVERAGES:
1:  0
2:  1
3:  2
4:  6
5:  11
6:  19
7:  34
8:  57
9:  95
10: 164
11: 283
12: 494
*/
<|endoftext|>


<commit_before>package vcsstore

import (
	"crypto/sha1"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/sourcegraph/go-vcs/vcs"
)

var (
	// RepositoryPath is called to determine the directory, relative to a
	// Config's StorageDir, to which the repository should be cloned to. The
	// default implementation stores repositories in directories of the form
	// "vcs-type/escaped-clone-url".
	RepositoryPath = func(vcsType string, cloneURL *url.URL) string {
		return filepath.Join(vcsType, url.QueryEscape(cloneURL.String()))
	}
)

// HashedRepositoryPath may be assigned to RepositoryPath to use paths
// of the form "xx/yy/zzzzzzzz" where xx and yy are the first 4 characters of
// some hash of vcsType and cloneURL, and zzzzzzzz is the full hash (minus the
// first 4 characters).
func HashedRepositoryPath(vcsType string, cloneURL *url.URL) string {
	h := sha1.New()
	h.Write([]byte(vcsType))
	h.Write([]byte(cloneURL.String()))
	s := base64.URLEncoding.EncodeToString(h.Sum(nil))
	return fmt.Sprintf("%s/%s/%s/%s", vcsType, s[:2], s[2:4], s[4:])
}

type Service interface {
	// Open opens a repository. If it doesn't exist. an
	// os.ErrNotExist-satisfying error is returned. If opening succeeds, the
	// repository is returned.
	Open(vcs string, cloneURL *url.URL) (interface{}, error)

	// Clone clones the repository if a clone doesn't yet exist locally.
	// Otherwise, it opens the repository. If no errors occur, the repository is
	// returned.
	Clone(vcs string, cloneURL *url.URL) (interface{}, error)
}

type Config struct {
	// StorageDir is where cloned repositories are stored. If empty, the current
	// working directory is used.
	StorageDir string

	Log *log.Logger

	DebugLog *log.Logger
}

func NewService(c *Config) Service {
	if c == nil {
		c = &Config{
			StorageDir: ".",
			Log:        log.New(os.Stderr, "vcsstore: ", log.LstdFlags),
			DebugLog:   log.New(ioutil.Discard, "", 0),
		}
	}
	return &service{
		Config: *c,
		repoMu: make(map[repoKey]*sync.Mutex),
	}
}

type service struct {
	Config

	// repoMu prevents more than one goroutine from simultaneously cloning the
	// same repository. Because clones are atomic
	repoMu map[repoKey]*sync.Mutex

	// repoMuMu synchronizes access to repoMu.
	repoMuMu sync.Mutex
}

type repoKey struct {
	vcsType  string
	cloneURL string
}

// cloneDir validates vcsType and cloneURL. If they are valid, cloneDir returns
// the local directory that the repository should be cloned to (which it may
// already exist at). If invalid, cloneDir returns a non-nil error.
func (s *service) cloneDir(vcsType string, cloneURL *url.URL) (string, error) {
	if !isLowercaseLetter(vcsType) {
		return "", errors.New("invalid VCS type")
	}
	if cloneURL.Scheme == "" || cloneURL.Host == "" {
		return "", errors.New("invalid clone URL")
	}

	return filepath.Join(s.StorageDir, RepositoryPath(vcsType, cloneURL)), nil
}

func (s *service) Open(vcsType string, cloneURL *url.URL) (interface{}, error) {
	cloneDir, err := s.cloneDir(vcsType, cloneURL)
	if err != nil {
		return nil, err
	}
	return s.open(vcsType, cloneDir)
}

func (s *service) open(vcsType, cloneDir string) (interface{}, error) {
	if fi, err := os.Stat(cloneDir); err != nil {
		return nil, err
	} else if !fi.Mode().IsDir() {
		return nil, fmt.Errorf("clone path %q is not a directory", cloneDir)
	}
	return vcs.OpenMirror(vcsType, cloneDir)
}

func (s *service) Clone(vcsType string, cloneURL *url.URL) (interface{}, error) {
	cloneDir, err := s.cloneDir(vcsType, cloneURL)
	if err != nil {
		return nil, err
	}

	// See if the clone directory exists and return immediately (without
	// locking) if so.
	if r, err := s.open(vcsType, cloneDir); !os.IsNotExist(err) {
		if err == nil {
			s.DebugLog.Printf("Clone(%s, %s): repository already exists at %s", vcsType, cloneURL, cloneDir)
		} else {
			s.DebugLog.Printf("Clone(%s, %s): opening existing repository at %s failed: %s", vcsType, cloneURL, cloneDir, err)
		}
		return r, err
	}

	// The local clone directory doesn't exist, so we need to clone the repository.
	mu := s.Mutex(vcsType, cloneURL)
	mu.Lock()
	defer mu.Unlock()

	// Check again after obtaining the lock, so we don't clone multiple times.
	if r, err := s.open(vcsType, cloneDir); !os.IsNotExist(err) {
		if err == nil {
			s.DebugLog.Printf("Clone(%s, %s): after obtaining clone lock, repository already exists at %s", vcsType, cloneURL, cloneDir)
		} else {
			s.DebugLog.Printf("Clone(%s, %s): after obtaining clone lock, opening existing repository at %s failed: %s", vcsType, cloneURL, cloneDir, err)
		}
		return r, err
	}

	start := time.Now()
	msg := fmt.Sprintf("%s %s to %s", vcsType, cloneURL.String(), cloneDir)
	s.Log.Print("Cloning ", msg, "...")
	defer func() {
		s.Log.Print("Finished cloning ", msg, " in ", time.Since(start))
	}()

	// Atomically clone the repository. First, clone it to a temporary sibling
	// directory. Once the clone is complete, atomically
	// rename it to the intended cloneDir.
	parentDir := filepath.Dir(cloneDir)
	if err := os.MkdirAll(parentDir, 0700); err != nil {
		return nil, err
	}

	cloneTmpDir, err := ioutil.TempDir(parentDir, "_tmp_"+filepath.Base(cloneDir)+"-")
	if err != nil {
		return nil, err
	}
	s.DebugLog.Printf("Clone(%s, %s): cloning to temporary sibling dir %s", vcsType, cloneURL, cloneTmpDir)
	defer os.RemoveAll(cloneTmpDir)

	_, err = vcs.CloneMirror(vcsType, cloneURL.String(), cloneTmpDir)
	if err != nil {
		return nil, err
	}
	s.DebugLog.Printf("Clone(%s, %s): cloned to temporary sibling dir %s; now renaming to intended clone dir %s", vcsType, cloneURL, cloneTmpDir, cloneDir)

	if err := os.Rename(cloneTmpDir, cloneDir); err != nil {
		s.DebugLog.Printf("Clone(%s, %s): Rename(%s -> %s) failed: %s", vcsType, cloneURL, cloneTmpDir, cloneDir)
		return nil, err
	}

	return s.open(vcsType, cloneDir)
}

func (s *service) Mutex(vcsType string, cloneURL *url.URL) *sync.Mutex {
	s.repoMuMu.Lock()
	defer s.repoMuMu.Unlock()

	k := repoKey{vcsType, cloneURL.String()}
	if mu, ok := s.repoMu[k]; ok {
		return mu
	}
	s.repoMu[k] = &sync.Mutex{}
	return s.repoMu[k]
}

func isLowercaseLetter(s string) bool {
	return strings.IndexFunc(s, func(c rune) bool {
		return !(c >= 'a' && c <= 'z')
	}) == -1
}
<commit_msg>atomic note<commit_after>package vcsstore

import (
	"crypto/sha1"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/sourcegraph/go-vcs/vcs"
)

var (
	// RepositoryPath is called to determine the directory, relative to a
	// Config's StorageDir, to which the repository should be cloned to. The
	// default implementation stores repositories in directories of the form
	// "vcs-type/escaped-clone-url".
	RepositoryPath = func(vcsType string, cloneURL *url.URL) string {
		return filepath.Join(vcsType, url.QueryEscape(cloneURL.String()))
	}
)

// HashedRepositoryPath may be assigned to RepositoryPath to use paths
// of the form "xx/yy/zzzzzzzz" where xx and yy are the first 4 characters of
// some hash of vcsType and cloneURL, and zzzzzzzz is the full hash (minus the
// first 4 characters).
func HashedRepositoryPath(vcsType string, cloneURL *url.URL) string {
	h := sha1.New()
	h.Write([]byte(vcsType))
	h.Write([]byte(cloneURL.String()))
	s := base64.URLEncoding.EncodeToString(h.Sum(nil))
	return fmt.Sprintf("%s/%s/%s/%s", vcsType, s[:2], s[2:4], s[4:])
}

type Service interface {
	// Open opens a repository. If it doesn't exist. an
	// os.ErrNotExist-satisfying error is returned. If opening succeeds, the
	// repository is returned.
	Open(vcs string, cloneURL *url.URL) (interface{}, error)

	// Clone clones the repository if a clone doesn't yet exist locally.
	// Otherwise, it opens the repository. If no errors occur, the repository is
	// returned.
	Clone(vcs string, cloneURL *url.URL) (interface{}, error)
}

type Config struct {
	// StorageDir is where cloned repositories are stored. If empty, the current
	// working directory is used.
	StorageDir string

	Log *log.Logger

	DebugLog *log.Logger
}

func NewService(c *Config) Service {
	if c == nil {
		c = &Config{
			StorageDir: ".",
			Log:        log.New(os.Stderr, "vcsstore: ", log.LstdFlags),
			DebugLog:   log.New(ioutil.Discard, "", 0),
		}
	}
	return &service{
		Config: *c,
		repoMu: make(map[repoKey]*sync.Mutex),
	}
}

type service struct {
	Config

	// repoMu prevents more than one goroutine from simultaneously cloning the
	// same repository.
	repoMu map[repoKey]*sync.Mutex

	// repoMuMu synchronizes access to repoMu.
	repoMuMu sync.Mutex
}

type repoKey struct {
	vcsType  string
	cloneURL string
}

// cloneDir validates vcsType and cloneURL. If they are valid, cloneDir returns
// the local directory that the repository should be cloned to (which it may
// already exist at). If invalid, cloneDir returns a non-nil error.
func (s *service) cloneDir(vcsType string, cloneURL *url.URL) (string, error) {
	if !isLowercaseLetter(vcsType) {
		return "", errors.New("invalid VCS type")
	}
	if cloneURL.Scheme == "" || cloneURL.Host == "" {
		return "", errors.New("invalid clone URL")
	}

	return filepath.Join(s.StorageDir, RepositoryPath(vcsType, cloneURL)), nil
}

func (s *service) Open(vcsType string, cloneURL *url.URL) (interface{}, error) {
	cloneDir, err := s.cloneDir(vcsType, cloneURL)
	if err != nil {
		return nil, err
	}
	return s.open(vcsType, cloneDir)
}

func (s *service) open(vcsType, cloneDir string) (interface{}, error) {
	if fi, err := os.Stat(cloneDir); err != nil {
		return nil, err
	} else if !fi.Mode().IsDir() {
		return nil, fmt.Errorf("clone path %q is not a directory", cloneDir)
	}
	return vcs.OpenMirror(vcsType, cloneDir)
}

func (s *service) Clone(vcsType string, cloneURL *url.URL) (interface{}, error) {
	cloneDir, err := s.cloneDir(vcsType, cloneURL)
	if err != nil {
		return nil, err
	}

	// See if the clone directory exists and return immediately (without
	// locking) if so.
	if r, err := s.open(vcsType, cloneDir); !os.IsNotExist(err) {
		if err == nil {
			s.DebugLog.Printf("Clone(%s, %s): repository already exists at %s", vcsType, cloneURL, cloneDir)
		} else {
			s.DebugLog.Printf("Clone(%s, %s): opening existing repository at %s failed: %s", vcsType, cloneURL, cloneDir, err)
		}
		return r, err
	}

	// The local clone directory doesn't exist, so we need to clone the repository.
	mu := s.Mutex(vcsType, cloneURL)
	mu.Lock()
	defer mu.Unlock()

	// Check again after obtaining the lock, so we don't clone multiple times.
	if r, err := s.open(vcsType, cloneDir); !os.IsNotExist(err) {
		if err == nil {
			s.DebugLog.Printf("Clone(%s, %s): after obtaining clone lock, repository already exists at %s", vcsType, cloneURL, cloneDir)
		} else {
			s.DebugLog.Printf("Clone(%s, %s): after obtaining clone lock, opening existing repository at %s failed: %s", vcsType, cloneURL, cloneDir, err)
		}
		return r, err
	}

	start := time.Now()
	msg := fmt.Sprintf("%s %s to %s", vcsType, cloneURL.String(), cloneDir)
	s.Log.Print("Cloning ", msg, "...")
	defer func() {
		s.Log.Print("Finished cloning ", msg, " in ", time.Since(start))
	}()

	// "Atomically" clone the repository. First, clone it to a temporary sibling
	// directory. Once the clone is complete, "atomically"
	// rename it to the intended cloneDir.
	//
	// "Atomically" is in quotes because this operation is not really atomic. It
	// depends on the underlying FS. For now, for our purposes, it performs well
	// enough on local ext4 and on GlusterFS.
	parentDir := filepath.Dir(cloneDir)
	if err := os.MkdirAll(parentDir, 0700); err != nil {
		return nil, err
	}

	cloneTmpDir, err := ioutil.TempDir(parentDir, "_tmp_"+filepath.Base(cloneDir)+"-")
	if err != nil {
		return nil, err
	}
	s.DebugLog.Printf("Clone(%s, %s): cloning to temporary sibling dir %s", vcsType, cloneURL, cloneTmpDir)
	defer os.RemoveAll(cloneTmpDir)

	_, err = vcs.CloneMirror(vcsType, cloneURL.String(), cloneTmpDir)
	if err != nil {
		return nil, err
	}
	s.DebugLog.Printf("Clone(%s, %s): cloned to temporary sibling dir %s; now renaming to intended clone dir %s", vcsType, cloneURL, cloneTmpDir, cloneDir)

	if err := os.Rename(cloneTmpDir, cloneDir); err != nil {
		s.DebugLog.Printf("Clone(%s, %s): Rename(%s -> %s) failed: %s", vcsType, cloneURL, cloneTmpDir, cloneDir)
		return nil, err
	}

	return s.open(vcsType, cloneDir)
}

func (s *service) Mutex(vcsType string, cloneURL *url.URL) *sync.Mutex {
	s.repoMuMu.Lock()
	defer s.repoMuMu.Unlock()

	k := repoKey{vcsType, cloneURL.String()}
	if mu, ok := s.repoMu[k]; ok {
		return mu
	}
	s.repoMu[k] = &sync.Mutex{}
	return s.repoMu[k]
}

func isLowercaseLetter(s string) bool {
	return strings.IndexFunc(s, func(c rune) bool {
		return !(c >= 'a' && c <= 'z')
	}) == -1
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/any"
	empty "github.com/golang/protobuf/ptypes/empty"
	pb "github.com/iochti/point-service/proto"
	"golang.org/x/net/context"
)

// PointSvc implements the gRPCs methods described in proto/point.proto
type PointSvc struct {
	Db DataLayerInterface
}

// CreatePoint creates a point in the database
func (p *PointSvc) CreatePoint(ctx context.Context, in *pb.Point) (*empty.Empty, error) {
	fields, err := anyToInterface(in.GetFields())
	if err != nil {
		return nil, err
	}
	fmt.Println(fields)
	return nil, nil
}

// GetPointsByThing gets all points for a given user & thing
func (p *PointSvc) GetPointsByThing(in *pb.ThingId, stream pb.PointSvc_GetPointsByThingServer) error {
	return nil
}

// GetPointsByGroup get all points for a given group
func (p *PointSvc) GetPointsByGroup(in *pb.GroupId, stream pb.PointSvc_GetPointsByGroupServer) error {
	return nil

}

func anyToInterface(value map[string]*any.Any) (map[string]interface{}, error) {
	var res map[string]interface{}
	for k, v := range value {
		var x ptypes.DynamicAny
		if err := ptypes.UnmarshalAny(v, &x); err != nil {
			return res, err
		}
		res[k] = x.Message
	}
	return res, nil
}
<commit_msg>Added point creation<commit_after>package main

import (
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"

	"github.com/gogo/protobuf/proto"
	"github.com/golang/protobuf/ptypes/any"
	empty "github.com/golang/protobuf/ptypes/empty"
	"github.com/influxdata/influxdb/client/v2"
	pb "github.com/iochti/point-service/proto"
	"golang.org/x/net/context"
)

// PointSvc implements the gRPCs methods described in proto/point.proto
type PointSvc struct {
	Db DataLayerInterface
}

// CreatePoint creates a point in the database
func (p *PointSvc) CreatePoint(ctx context.Context, in *pb.Point) (*empty.Empty, error) {
	fields := make(map[string]interface{}, len(in.GetFields()))
	for k, v := range in.GetFields() {
		msg, err := AnyToMessageType(v)
		if err != nil {
			return nil, err
		}
		val := ExtractDatasFromMessage(v.GetTypeUrl(), msg)
		fields[k] = val
	}
	pt, err := client.NewPoint(in.GetUser(), in.GetTags(), fields, time.Now())
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, err.Error())
	}
	err = p.Db.CreatePoint(pt)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, err.Error())
	}
	return &empty.Empty{}, nil
}

// GetPointsByThing gets all points for a given user & thing
func (p *PointSvc) GetPointsByThing(in *pb.ThingId, stream pb.PointSvc_GetPointsByThingServer) error {
	return nil
}

// GetPointsByGroup get all points for a given group
func (p *PointSvc) GetPointsByGroup(in *pb.GroupId, stream pb.PointSvc_GetPointsByGroupServer) error {
	return nil

}

func AnyToMessageType(elmt *any.Any) (proto.Message, error) {
	var value proto.Message
	switch elmt.GetTypeUrl() {
	case "iochti.com/string":
		value = &pb.StringPoint{}
	case "iochti.com/float":
		value = &pb.FloatPoint{}
	case "iochti.com/int":
		value = &pb.IntegerPoint{}
	case "iochti.com/bool":
		value = &pb.BoolPoint{}
	case "iochti.com/duration":
		value = &pb.DurationPoint{}
	case "iochti.com/date-time":
		value = &pb.DateTimePoint{}
	}
	err := proto.Unmarshal(elmt.GetValue(), value)
	return value, err
}

func ExtractDatasFromMessage(typeString string, msg proto.Message) interface{} {
	switch typeString {
	case "iochti.com/string":
		return msg.(*pb.StringPoint).GetValue()
	case "iochti.com/float":
		return msg.(*pb.FloatPoint).GetValue()
	case "iochti.com/int":
		return msg.(*pb.IntegerPoint).GetValue()
	case "iochti.com/bool":
		return msg.(*pb.BoolPoint).GetValue()
	case "iochti.com/duration":
		return msg.(*pb.DurationPoint).GetValue()
	case "iochti.com/date-time":
		return msg.(*pb.DateTimePoint).GetValue()
	}
	return nil
}
<|endoftext|>


<commit_before>package release

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/juju/errgo"
	log "github.com/op/go-logging"

	"arvika.pulcy.com/pulcy/pulcy/docker"
	"arvika.pulcy.com/pulcy/pulcy/git"
	"arvika.pulcy.com/pulcy/pulcy/tunnel"
	"arvika.pulcy.com/pulcy/pulcy/util"
)

const (
	packageJsonFile   = "package.json"
	versionFile       = "VERSION"
	nameKey           = "name"
	versionKey        = "version"
	makefileFile      = "Makefile"
	gruntfileFile     = "Gruntfile.js"
	dockerfileFile    = "Dockerfile"
	defaultPerm       = 0664
	nodeModulesFolder = "node_modules"
)

type Flags struct {
	ReleaseType    string
	DockerRegistry string
}

type packageJson map[string]interface{}

func Release(log *log.Logger, flags *Flags) error {
	// Detect environment
	hasMakefile := false
	isDev := flags.ReleaseType == "dev"
	if _, err := os.Stat(makefileFile); err == nil {
		hasMakefile = true
		log.Info("Found %s", makefileFile)
	}

	hasGruntfile := false
	if _, err := os.Stat(gruntfileFile); err == nil {
		hasGruntfile = true
		log.Info("Found %s", gruntfileFile)
	}

	hasDockerfile := false
	if _, err := os.Stat(dockerfileFile); err == nil {
		hasDockerfile = true
		log.Info("Found %s", dockerfileFile)
	}

	// Read the current version and name
	info, err := GetProjectInfo()
	if err != nil {
		return err
	}

	log.Info("Found old version %s", info.Version)
	version, err := semver.NewVersion(info.Version)
	if err != nil {
		return err
	}

	// Check repository state
	if !isDev {
		if err := checkRepoClean(log); err != nil {
			return err
		}
	}

	// Bump version
	switch flags.ReleaseType {
	case "major":
		version.Major++
		version.Minor = 0
		version.Patch = 0
	case "minor":
		version.Minor++
		version.Patch = 0
	case "patch":
		version.Patch++
	case "dev":
		// Do not change version
	default:
		return errgo.Newf("Unknown release type %s", flags.ReleaseType)
	}
	version.Metadata = ""

	// Write new release version
	if !isDev {
		if err := writeVersion(log, version.String(), info.pkg, false); err != nil {
			return err
		}
	}

	// Open SSH tunnel
	if err := tunnel.OpenTunnel(log); err != nil {
		return err
	}

	// Build project
	if hasGruntfile && !info.NoGrunt {
		if _, err := os.Stat(nodeModulesFolder); os.IsNotExist(err) {
			log.Info("Folder %s not found", nodeModulesFolder)
			if err := util.ExecPrintError(log, "npm", "install"); err != nil {
				return err
			}
		}
		if err := util.ExecPrintError(log, "grunt", "build-release"); err != nil {
			return err
		}
	}
	if hasMakefile {
		// Clean first
		if !isDev {
			if err := util.ExecPrintError(log, "make", info.Targets.CleanTarget); err != nil {
				return err
			}
		}
		// Now build
		if err := util.ExecPrintError(log, "make"); err != nil {
			return err
		}
	}

	if hasDockerfile {
		// Build docker images
		tagVersion := version.String()
		if isDev {
			tagVersion = time.Now().Format("2006-01-02-15-04-05")
		}
		tag := fmt.Sprintf("%s:%s", info.Image, tagVersion)
		latestTag := fmt.Sprintf("%s:latest", info.Image)
		if err := util.ExecPrintError(log, "docker", "build", "--tag", tag, "."); err != nil {
			return err
		}
		if info.TagLatest {
			if err := util.ExecPrintError(log, "docker", "tag", tag, latestTag); err != nil {
				return err
			}
		}
		registry := flags.DockerRegistry
		if info.Registry != "" {
			registry = info.Registry
		}
		if registry != "" {
			// Push image to registry
			if err := docker.Push(log, tag, registry); err != nil {
				return err
			}
			if info.TagLatest {
				// Push latest image to registry
				if err := docker.Push(log, latestTag, registry); err != nil {
					return err
				}
			}
		}
	}

	// Build succeeded, re-write new release version and commit
	if !isDev {
		if err := writeVersion(log, version.String(), info.pkg, true); err != nil {
			return err
		}

		// Tag version
		if err := git.Tag(log, version.String()); err != nil {
			return err
		}

		// Update version to "+git" working version
		version.Metadata = "git"

		// Write new release version
		if err := writeVersion(log, version.String(), info.pkg, true); err != nil {
			return err
		}

		// Push changes
		if err := git.Push(log, "", false); err != nil {
			return err
		}

		// Push tags
		if err := git.Push(log, "", true); err != nil {
			return err
		}
	}

	return nil
}

// Update the version of the given package (if any) and an existing VERSION file (if any)
// Commit changes afterwards
func writeVersion(log *log.Logger, version string, pkg packageJson, commit bool) error {
	files := []string{}
	if pkg != nil {
		pkg[versionKey] = version
		data, err := json.MarshalIndent(pkg, "", "  ")
		if err != nil {
			return err
		}
		if err := ioutil.WriteFile(packageJsonFile, data, defaultPerm); err != nil {
			return err
		}
		files = append(files, packageJsonFile)
	}
	if _, err := os.Stat(versionFile); err == nil {
		if err := ioutil.WriteFile(versionFile, []byte(version), defaultPerm); err != nil {
			return err
		}
		files = append(files, versionFile)
	}

	if commit {
		if err := git.Add(log, files...); err != nil {
			return err
		}
		msg := fmt.Sprintf("Updated version to %s", version)
		if err := git.Commit(log, msg); err != nil {
			return err
		}
	}

	return nil
}

// Are the no uncommited changes in this repo?
func checkRepoClean(log *log.Logger) error {
	if st, err := git.Status(log, true); err != nil {
		return err
	} else if st != "" {
		return errgo.New("There are uncommited changes")
	}
	if err := git.Fetch(log, "origin"); err != nil {
		return err
	}
	if diff, err := git.Diff(log, "master", "origin/master"); err != nil {
		return err
	} else if diff != "" {
		return errgo.New("Master is not in sync with origin")
	}

	return nil
}
<commit_msg>tag -f<commit_after>package release

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/juju/errgo"
	log "github.com/op/go-logging"

	"arvika.pulcy.com/pulcy/pulcy/docker"
	"arvika.pulcy.com/pulcy/pulcy/git"
	"arvika.pulcy.com/pulcy/pulcy/tunnel"
	"arvika.pulcy.com/pulcy/pulcy/util"
)

const (
	packageJsonFile   = "package.json"
	versionFile       = "VERSION"
	nameKey           = "name"
	versionKey        = "version"
	makefileFile      = "Makefile"
	gruntfileFile     = "Gruntfile.js"
	dockerfileFile    = "Dockerfile"
	defaultPerm       = 0664
	nodeModulesFolder = "node_modules"
)

type Flags struct {
	ReleaseType    string
	DockerRegistry string
}

type packageJson map[string]interface{}

func Release(log *log.Logger, flags *Flags) error {
	// Detect environment
	hasMakefile := false
	isDev := flags.ReleaseType == "dev"
	if _, err := os.Stat(makefileFile); err == nil {
		hasMakefile = true
		log.Info("Found %s", makefileFile)
	}

	hasGruntfile := false
	if _, err := os.Stat(gruntfileFile); err == nil {
		hasGruntfile = true
		log.Info("Found %s", gruntfileFile)
	}

	hasDockerfile := false
	if _, err := os.Stat(dockerfileFile); err == nil {
		hasDockerfile = true
		log.Info("Found %s", dockerfileFile)
	}

	// Read the current version and name
	info, err := GetProjectInfo()
	if err != nil {
		return err
	}

	log.Info("Found old version %s", info.Version)
	version, err := semver.NewVersion(info.Version)
	if err != nil {
		return err
	}

	// Check repository state
	if !isDev {
		if err := checkRepoClean(log); err != nil {
			return err
		}
	}

	// Bump version
	switch flags.ReleaseType {
	case "major":
		version.Major++
		version.Minor = 0
		version.Patch = 0
	case "minor":
		version.Minor++
		version.Patch = 0
	case "patch":
		version.Patch++
	case "dev":
		// Do not change version
	default:
		return errgo.Newf("Unknown release type %s", flags.ReleaseType)
	}
	version.Metadata = ""

	// Write new release version
	if !isDev {
		if err := writeVersion(log, version.String(), info.pkg, false); err != nil {
			return err
		}
	}

	// Open SSH tunnel
	if err := tunnel.OpenTunnel(log); err != nil {
		return err
	}

	// Build project
	if hasGruntfile && !info.NoGrunt {
		if _, err := os.Stat(nodeModulesFolder); os.IsNotExist(err) {
			log.Info("Folder %s not found", nodeModulesFolder)
			if err := util.ExecPrintError(log, "npm", "install"); err != nil {
				return err
			}
		}
		if err := util.ExecPrintError(log, "grunt", "build-release"); err != nil {
			return err
		}
	}
	if hasMakefile {
		// Clean first
		if !isDev {
			if err := util.ExecPrintError(log, "make", info.Targets.CleanTarget); err != nil {
				return err
			}
		}
		// Now build
		if err := util.ExecPrintError(log, "make"); err != nil {
			return err
		}
	}

	if hasDockerfile {
		// Build docker images
		tagVersion := version.String()
		if isDev {
			tagVersion = time.Now().Format("2006-01-02-15-04-05")
		}
		tag := fmt.Sprintf("%s:%s", info.Image, tagVersion)
		latestTag := fmt.Sprintf("%s:latest", info.Image)
		if err := util.ExecPrintError(log, "docker", "build", "--tag", tag, "."); err != nil {
			return err
		}
		if info.TagLatest {
			if err := util.ExecPrintError(log, "docker", "tag", "-f", tag, latestTag); err != nil {
				return err
			}
		}
		registry := flags.DockerRegistry
		if info.Registry != "" {
			registry = info.Registry
		}
		if registry != "" {
			// Push image to registry
			if err := docker.Push(log, tag, registry); err != nil {
				return err
			}
			if info.TagLatest {
				// Push latest image to registry
				if err := docker.Push(log, latestTag, registry); err != nil {
					return err
				}
			}
		}
	}

	// Build succeeded, re-write new release version and commit
	if !isDev {
		if err := writeVersion(log, version.String(), info.pkg, true); err != nil {
			return err
		}

		// Tag version
		if err := git.Tag(log, version.String()); err != nil {
			return err
		}

		// Update version to "+git" working version
		version.Metadata = "git"

		// Write new release version
		if err := writeVersion(log, version.String(), info.pkg, true); err != nil {
			return err
		}

		// Push changes
		if err := git.Push(log, "", false); err != nil {
			return err
		}

		// Push tags
		if err := git.Push(log, "", true); err != nil {
			return err
		}
	}

	return nil
}

// Update the version of the given package (if any) and an existing VERSION file (if any)
// Commit changes afterwards
func writeVersion(log *log.Logger, version string, pkg packageJson, commit bool) error {
	files := []string{}
	if pkg != nil {
		pkg[versionKey] = version
		data, err := json.MarshalIndent(pkg, "", "  ")
		if err != nil {
			return err
		}
		if err := ioutil.WriteFile(packageJsonFile, data, defaultPerm); err != nil {
			return err
		}
		files = append(files, packageJsonFile)
	}
	if _, err := os.Stat(versionFile); err == nil {
		if err := ioutil.WriteFile(versionFile, []byte(version), defaultPerm); err != nil {
			return err
		}
		files = append(files, versionFile)
	}

	if commit {
		if err := git.Add(log, files...); err != nil {
			return err
		}
		msg := fmt.Sprintf("Updated version to %s", version)
		if err := git.Commit(log, msg); err != nil {
			return err
		}
	}

	return nil
}

// Are the no uncommited changes in this repo?
func checkRepoClean(log *log.Logger) error {
	if st, err := git.Status(log, true); err != nil {
		return err
	} else if st != "" {
		return errgo.New("There are uncommited changes")
	}
	if err := git.Fetch(log, "origin"); err != nil {
		return err
	}
	if diff, err := git.Diff(log, "master", "origin/master"); err != nil {
		return err
	} else if diff != "" {
		return errgo.New("Master is not in sync with origin")
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package node

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	jose "github.com/square/go-jose"
	"k8s.io/apimachinery/pkg/util/wait"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)

// the amount of time to wait between each request to the discovery API
const discoveryRetryTimeout = 5 * time.Second

func RetrieveTrustedClusterInfo(d *kubeadmapi.TokenDiscovery) (*kubeadmapi.ClusterInfo, error) {
	requestURL := fmt.Sprintf("http://%s/cluster-info/v1/?token-id=%s", d.Addresses[0], d.ID)
	req, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to consturct an HTTP request [%v]", err)
	}

	fmt.Printf("[discovery] Created cluster info discovery client, requesting info from %q\n", requestURL)

	var res *http.Response
	wait.PollInfinite(discoveryRetryTimeout, func() (bool, error) {
		res, err = http.DefaultClient.Do(req)
		if err != nil {
			fmt.Printf("[discovery] Failed to request cluster info, will try again: [%s]\n", err)
			return false, nil
		}
		return true, nil
	})

	buf := new(bytes.Buffer)
	io.Copy(buf, res.Body)
	res.Body.Close()

	object, err := jose.ParseSigned(buf.String())
	if err != nil {
		return nil, fmt.Errorf("failed to parse response as JWS object [%v]", err)
	}

	fmt.Println("[discovery] Cluster info object received, verifying signature using given token")

	output, err := object.Verify([]byte(d.Secret))
	if err != nil {
		return nil, fmt.Errorf("failed to verify JWS signature of received cluster info object [%v]", err)
	}

	clusterInfo := kubeadmapi.ClusterInfo{}

	if err := json.Unmarshal(output, &clusterInfo); err != nil {
		return nil, fmt.Errorf("failed to decode received cluster info object [%v]", err)
	}

	if len(clusterInfo.CertificateAuthorities) == 0 || len(clusterInfo.Endpoints) == 0 {
		return nil, fmt.Errorf("cluster info object is invalid - no endpoint(s) and/or root CA certificate(s) found")
	}

	// TODO(phase1+) print summary info about the CA certificate, along with the the checksum signature
	// we also need an ability for the user to configure the client to validate received CA cert against a checksum
	fmt.Printf("[discovery] Cluster info signature and contents are valid, will use API endpoints %v\n", clusterInfo.Endpoints)
	return &clusterInfo, nil
}
<commit_msg>Typo fix.<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package node

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	jose "github.com/square/go-jose"
	"k8s.io/apimachinery/pkg/util/wait"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)

// the amount of time to wait between each request to the discovery API
const discoveryRetryTimeout = 5 * time.Second

func RetrieveTrustedClusterInfo(d *kubeadmapi.TokenDiscovery) (*kubeadmapi.ClusterInfo, error) {
	requestURL := fmt.Sprintf("http://%s/cluster-info/v1/?token-id=%s", d.Addresses[0], d.ID)
	req, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to construct an HTTP request [%v]", err)
	}

	fmt.Printf("[discovery] Created cluster info discovery client, requesting info from %q\n", requestURL)

	var res *http.Response
	wait.PollInfinite(discoveryRetryTimeout, func() (bool, error) {
		res, err = http.DefaultClient.Do(req)
		if err != nil {
			fmt.Printf("[discovery] Failed to request cluster info, will try again: [%s]\n", err)
			return false, nil
		}
		return true, nil
	})

	buf := new(bytes.Buffer)
	io.Copy(buf, res.Body)
	res.Body.Close()

	object, err := jose.ParseSigned(buf.String())
	if err != nil {
		return nil, fmt.Errorf("failed to parse response as JWS object [%v]", err)
	}

	fmt.Println("[discovery] Cluster info object received, verifying signature using given token")

	output, err := object.Verify([]byte(d.Secret))
	if err != nil {
		return nil, fmt.Errorf("failed to verify JWS signature of received cluster info object [%v]", err)
	}

	clusterInfo := kubeadmapi.ClusterInfo{}

	if err := json.Unmarshal(output, &clusterInfo); err != nil {
		return nil, fmt.Errorf("failed to decode received cluster info object [%v]", err)
	}

	if len(clusterInfo.CertificateAuthorities) == 0 || len(clusterInfo.Endpoints) == 0 {
		return nil, fmt.Errorf("cluster info object is invalid - no endpoint(s) and/or root CA certificate(s) found")
	}

	// TODO(phase1+) print summary info about the CA certificate, along with the the checksum signature
	// we also need an ability for the user to configure the client to validate received CA cert against a checksum
	fmt.Printf("[discovery] Cluster info signature and contents are valid, will use API endpoints %v\n", clusterInfo.Endpoints)
	return &clusterInfo, nil
}
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
    "github.com/hyperledger/fabric/core/chaincode/shim"
)

/*
 * Clears an index of type 'map[string]string' on the ledger
 */
func clearCarIndex(indexStr string, stub shim.ChaincodeStubInterface) error {
    index := make(map[string]string)

    jsonAsBytes, err := json.Marshal(index)
    if err != nil {
        return err
    }

    return stub.PutState(indexStr, jsonAsBytes)
}

/*
 * Clears an index of type 'map[string][]byte' on the ledger
 */
func clearKeyIndex(indexStr string, stub shim.ChaincodeStubInterface) error {
    index := make(map[string][]byte)

    jsonAsBytes, err := json.Marshal(index)
    if err != nil {
        return err
    }

    return stub.PutState(indexStr, jsonAsBytes)
}<commit_msg>remove clearKeyIndex<commit_after>package main

import (
    "encoding/json"
    "github.com/hyperledger/fabric/core/chaincode/shim"
)

/*
 * Clears an index of type 'map[string]string' on the ledger
 */
func clearCarIndex(indexStr string, stub shim.ChaincodeStubInterface) error {
    index := make(map[string]string)

    jsonAsBytes, err := json.Marshal(index)
    if err != nil {
        return err
    }

    return stub.PutState(indexStr, jsonAsBytes)
}<|endoftext|>


<commit_before>package queue

import (
	"fmt"

	"github.com/garyburd/redigo/redis"
)

var RedisAddressWithPort string

const batchSize = 10
const timeout = 5

func FetchBatch() (result []string) {
	c, err := redis.Dial("tcp", RedisAddressWithPort)
	if err != nil {
		panic(err)
	}
	defer c.Close()

	for i := 0; i < batchSize; i++ {
		reply, err := redis.Values(c.Do("BRPOP", "jobs", timeout))
		if err != nil {
			break
		}
		item := string(reply[1].([]byte))
		result = append(result, item)
	}

	return
}

func WriteResult(result string) {
	fmt.Println("Writing result " + result)
	c, err := redis.Dial("tcp", RedisAddressWithPort)
	if err != nil {
		panic(err)
	}
	defer c.Close()

	_, err = redis.String(c.Do("LPUSH", "results", result))
	if err != nil {
		// never mind
	}

	return
}

func AddJob(url string) {
	c, err := redis.Dial("tcp", RedisAddressWithPort)
	if err != nil {
		panic(err)
	}
	defer c.Close()

	c.Do("LPUSH", "jobs", url)
}
<commit_msg>Add queue.FetchResults<commit_after>package queue

import (
	"fmt"

	"github.com/garyburd/redigo/redis"
)

var RedisAddressWithPort string

const batchSize = 10
const timeout = 5

func FetchBatch() (result []string) {
	return listFetch("jobs")
}

func FetchResults() (results []string) {
	return listFetch("results")
}

func WriteResult(result string) {
	fmt.Println("Writing result " + result)
	c, err := redis.Dial("tcp", RedisAddressWithPort)
	if err != nil {
		panic(err)
	}
	defer c.Close()

	_, err = redis.String(c.Do("LPUSH", "results", result))
	if err != nil {
		// never mind
	}

	return
}

func AddJob(job string) {
	c, err := redis.Dial("tcp", RedisAddressWithPort)
	if err != nil {
		panic(err)
	}
	defer c.Close()

	c.Do("LPUSH", "jobs", job)
	fmt.Println("Job added:", job)
}

func listFetch(listName string) (results []string) {
	c, err := redis.Dial("tcp", RedisAddressWithPort)
	if err != nil {
		panic(err)
	}
	defer c.Close()

	for i := 0; i < batchSize; i++ {
		reply, err := redis.Values(c.Do("BRPOP", listName, timeout))
		if err != nil {
			break
		}
		item := string(reply[1].([]byte))
		results = append(results, item)
	}

	return results
}
<|endoftext|>


<commit_before>package queue

import (
	"github.com/nathan-osman/go-cannon/util"

	"io"
	"log"
	"time"
)

// Mail queue managing the sending of messages to hosts.
type Queue struct {
	storage    *Storage
	hosts      map[string]*Host
	newMessage *util.NonBlockingChan
	stop       chan bool
}

// Deliver the specified message to the appropriate host queue.
func (q *Queue) deliverMessage(host, id string) {
	if _, ok := q.hosts[host]; !ok {
		q.hosts[host] = NewHost(host, q.storage)
	}
	q.hosts[host].Deliver(id)
}

// Check for inactive host queues and shut them down.
func (q *Queue) checkForInactiveQueues() {
	for n, h := range q.hosts {
		if h.Idle() > time.Minute {
			h.Stop()
			delete(q.hosts, n)
		}
	}
}

// Receive new messages and deliver them to the specified host queue.
func (q *Queue) run() {
	defer close(q.stop)
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()
loop:
	for {
		select {
		case i := <-q.newMessage.Recv:
			m := i.(*Message)
			if id, err := q.storage.NewMessage(m); err == nil {
				q.deliverMessage(m.Host, id)
			} else {
				log.Print(err)
			}
		case <-ticker.C:
			q.checkForInactiveQueues()
		case <-q.stop:
			break loop
		}
	}
	log.Println("shutting down host queues")
	for h := range q.hosts {
		q.hosts[h].Stop()
	}
}

// Create a new message queue. Any undelivered messages on disk will be added
// to the appropriate queue.
func NewQueue(directory string) (*Queue, error) {
	if s, messages, err := NewStorage(directory); err == nil {
		q := &Queue{
			storage:    s,
			hosts:      make(map[string]*Host),
			newMessage: util.NewNonBlockingChan(),
			stop:       make(chan bool),
		}
		for _, id := range messages {
			if m, err := q.storage.GetMessage(id); err == nil {
				q.deliverMessage(m.Host, id)
			}
		}
		go q.run()
		return q, nil
	} else {
		return nil, err
	}
}

// Create a new message body.
func (q *Queue) NewBody() (io.WriteCloser, string, error) {
	return q.storage.NewBody()
}

// Deliver the specified message to the appropriate host queue.
func (q *Queue) Deliver(m *Message) {
	q.newMessage.Send <- m
}

// Stop all active host queues.
func (q *Queue) Stop() {
	q.stop <- true
	<-q.stop
}
<commit_msg>Modified Queue to accept a Storage pointer instead of a directory.<commit_after>package queue

import (
	"github.com/nathan-osman/go-cannon/util"

	"log"
	"time"
)

// Mail queue managing the sending of messages to hosts.
type Queue struct {
	storage    *Storage
	hosts      map[string]*Host
	newMessage *util.NonBlockingChan
	stop       chan bool
}

// Deliver the specified message to the appropriate host queue.
func (q *Queue) deliverMessage(host, id string) {
	if _, ok := q.hosts[host]; !ok {
		q.hosts[host] = NewHost(host, q.storage)
	}
	q.hosts[host].Deliver(id)
}

// Check for inactive host queues and shut them down.
func (q *Queue) checkForInactiveQueues() {
	for n, h := range q.hosts {
		if h.Idle() > time.Minute {
			h.Stop()
			delete(q.hosts, n)
		}
	}
}

// Receive new messages and deliver them to the specified host queue.
func (q *Queue) run() {
	defer close(q.stop)
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()
loop:
	for {
		select {
		case i := <-q.newMessage.Recv:
			m := i.(*Message)
			if id, err := q.storage.NewMessage(m); err == nil {
				q.deliverMessage(m.Host, id)
			} else {
				log.Print(err)
			}
		case <-ticker.C:
			q.checkForInactiveQueues()
		case <-q.stop:
			break loop
		}
	}
	log.Println("shutting down host queues")
	for h := range q.hosts {
		q.hosts[h].Stop()
	}
}

// Create a new message queue. Any undelivered messages on disk will be added
// to the appropriate queue.
func NewQueue(s *Storage) (*Queue, error) {
	q := &Queue{
		storage:    s,
		hosts:      make(map[string]*Host),
		newMessage: util.NewNonBlockingChan(),
		stop:       make(chan bool),
	}
	if messages, err := s.LoadMessages(); err == nil {
		for _, id := range messages {
			if m, err := q.storage.GetMessage(id); err == nil {
				q.deliverMessage(m.Host, id)
			}
		}
	} else {
		return nil, err
	}
	go q.run()
	return q, nil
}

// Deliver the specified message to the appropriate host queue.
func (q *Queue) Deliver(m *Message) {
	q.newMessage.Send <- m
}

// Stop all active host queues.
func (q *Queue) Stop() {
	q.stop <- true
	<-q.stop
}
<|endoftext|>


<commit_before>package stageCmd

import (
	// Stdlib
	"errors"
	"fmt"
	"os"

	// Internal
	"github.com/salsaflow/salsaflow/action"
	"github.com/salsaflow/salsaflow/app"
	"github.com/salsaflow/salsaflow/changes"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/git"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/modules"
	"github.com/salsaflow/salsaflow/modules/common"
	"github.com/salsaflow/salsaflow/prompt"
	"github.com/salsaflow/salsaflow/releases"
	"github.com/salsaflow/salsaflow/version"

	// Other
	"gopkg.in/tchap/gocli.v2"
)

var Command = &gocli.Command{
	UsageLine: `
  stage`,
	Short: "stage current release",
	Long: `
  Stage the release that is currently running, i.e.

    1) Make sure the release can be staged (the stories are reviewed and tested)
    2) Reset the staging branch to point to the release branch.
    3) Delete the release branch.
    4) Bump the version for the staging branch.
    5) Push the changes.
	`,
	Action: run,
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 0 {
		cmd.Usage()
		os.Exit(2)
	}

	app.InitOrDie()

	defer prompt.RecoverCancel()

	if err := runMain(); err != nil {
		errs.Fatal(err)
	}
}

func runMain() (err error) {
	// Load git config.
	gitConfig, err := git.LoadConfig()
	if err != nil {
		return err
	}
	var (
		remoteName    = gitConfig.RemoteName()
		releaseBranch = gitConfig.ReleaseBranchName()
		stagingBranch = gitConfig.StagingBranchName()
	)

	// Instantiate the issue tracker.
	tracker, err := modules.GetIssueTracker()
	if err != nil {
		return err
	}

	// Get the current branch.
	task := "Get the current branch"
	currentBranch, err := git.CurrentBranch()
	if err != nil {
		return err
	}

	// Cannot be on the release branch, it will be deleted.
	task = "Make sure that the release branch is not checked out"
	if currentBranch == releaseBranch {
		return errs.NewError(
			task, errors.New("cannot stage the release while on the release branch"))
	}

	// Fetch the remote repository.
	task = "Fetch the remote repository"
	log.Run(task)
	if err := git.UpdateRemotes(remoteName); err != nil {
		return errs.NewError(task, err)
	}

	// Make sure the remote release branch exists.
	task = fmt.Sprintf("Make sure that branch '%v' exists in remote '%v'", releaseBranch, remoteName)
	log.Run(task)
	exists, err := git.RemoteBranchExists(releaseBranch, remoteName)
	if err != nil {
		return errs.NewError(task, err)
	}
	if !exists {
		return errs.NewError(task,
			fmt.Errorf("branch '%v' not found in remote '%v'", releaseBranch, remoteName))
	}

	// Make sure that the local release branch exists.
	task = fmt.Sprintf("Make sure that branch '%v' exists locally", releaseBranch)
	log.Run(task)
	if err := git.CreateTrackingBranchUnlessExists(releaseBranch, remoteName); err != nil {
		return errs.NewError(task, err)
	}

	// Make sure that the release branch is up to date.
	task = fmt.Sprintf("Make sure that branch '%v' is up to date", releaseBranch)
	log.Run(task)
	if err := git.EnsureBranchSynchronized(releaseBranch, remoteName); err != nil {
		return errs.NewError(task, err)
	}

	// Read the current release version.
	task = "Read the current release version"
	releaseVersion, err := version.GetByBranch(releaseBranch)
	if err != nil {
		return errs.NewError(task, err)
	}

	// Make sure the release is stageable.
	release, err := tracker.RunningRelease(releaseVersion)
	if err != nil {
		return err
	}
	if err := release.EnsureStageable(); err != nil {
		return err
	}

	// Make sure there are no commits being left behind,
	// e.g. make sure no commits are forgotten on the trunk branch,
	// i.e. make sure that everything necessary was cherry-picked.
	if err := checkCommits(tracker, release, releaseBranch); err != nil {
		return err
	}

	// Reset the staging branch to point to the newly created tag.
	task = fmt.Sprintf("Reset branch '%v' to point to branch '%v'", stagingBranch, releaseBranch)
	log.Run(task)
	act, err := git.CreateOrResetBranch(stagingBranch, releaseBranch)
	if err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, act)

	// Delete the local release branch.
	task = fmt.Sprintf("Delete branch '%v'", releaseBranch)
	log.Run(task)
	if err := git.Branch("-d", releaseBranch); err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, action.ActionFunc(func() error {
		task := fmt.Sprintf("Recreate branch '%v'", releaseBranch)

		// In case the release branch exists locally, do nothing.
		// This might look like an extra and useless check, but it looks like
		// the final git push at the end of the command function actually creates
		// the release branch locally when it is aborted from the pre-push hook.
		// Not sure why and how that is happening.
		exists, err := git.LocalBranchExists(releaseBranch)
		if err != nil {
			return errs.NewError(task, err)
		}
		if exists {
			return nil
		}

		// In case the branch indeed does not exist, create it.
		if err := git.Branch(releaseBranch, remoteName+"/"+releaseBranch); err != nil {
			return errs.NewError(task, err)
		}
		return nil
	}))

	// Update the version string on the staging branch.
	stagingVersion, err := releaseVersion.ToStageVersion()
	if err != nil {
		return err
	}

	task = fmt.Sprintf("Bump version (branch '%v' -> %v)", stagingBranch, stagingVersion)
	log.Run(task)
	act, err = version.SetForBranch(stagingVersion, stagingBranch)
	if err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, act)

	// Finalise the release in the code review tool.
	codeReviewTool, err := modules.GetCodeReviewTool()
	if err != nil {
		return err
	}
	act, err = codeReviewTool.FinaliseRelease(releaseVersion)
	if err != nil {
		return err
	}
	// No need to pass any task string, the module rollback functions
	// are expected to take care of printing messages on their own.
	defer action.RollbackOnError(&err, act)

	// Stage the release in the issue tracker.
	act, err = release.Stage()
	if err != nil {
		return err
	}
	defer action.RollbackOnError(&err, act)

	// Push to create the tag, reset client and delete release in the remote repository.
	task = "Push changes to the remote repository"
	log.Run(task)
	return git.Push(remoteName,
		"-f",                            // Use the Force, Luke.
		":"+releaseBranch,               // Delete the release branch.
		stagingBranch+":"+stagingBranch) // Push the staging branch.
}

func checkCommits(
	tracker common.IssueTracker,
	release common.RunningRelease,
	releaseBranch string,
) error {

	var task = "Make sure no changes are being left behind"
	log.Run(task)

	stories, err := release.Stories()
	if err != nil {
		return errs.NewError(task, err)
	}
	if len(stories) == 0 {
		return nil
	}

	groups, err := changes.StoryChanges(stories)
	if err != nil {
		return errs.NewError(task, err)
	}

	toCherryPick, err := releases.StoryChangesToCherryPick(groups)
	if err != nil {
		return errs.NewError(task, err)
	}

	// In case there are some changes being left behind,
	// ask the user to confirm whether to proceed or not.
	if len(toCherryPick) == 0 {
		return nil
	}

	fmt.Println(`
Some changes are being left behind!

In other words, some changes that are assigned to the current release
have not been cherry-picked onto the release branch yet.
	`)
	if err := changes.DumpStoryChanges(os.Stdout, toCherryPick, tracker, false); err != nil {
		panic(err)
	}
	fmt.Println()

	confirmed, err := prompt.Confirm("Are you sure you really want to stage the release?")
	if err != nil {
		return errs.NewError(task, err)
	}
	if !confirmed {
		prompt.PanicCancel()
	}
	fmt.Println()

	return nil
}
<commit_msg>release stage: Use git branch -D<commit_after>package stageCmd

import (
	// Stdlib
	"errors"
	"fmt"
	"os"

	// Internal
	"github.com/salsaflow/salsaflow/action"
	"github.com/salsaflow/salsaflow/app"
	"github.com/salsaflow/salsaflow/changes"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/git"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/modules"
	"github.com/salsaflow/salsaflow/modules/common"
	"github.com/salsaflow/salsaflow/prompt"
	"github.com/salsaflow/salsaflow/releases"
	"github.com/salsaflow/salsaflow/version"

	// Other
	"gopkg.in/tchap/gocli.v2"
)

var Command = &gocli.Command{
	UsageLine: `
  stage`,
	Short: "stage current release",
	Long: `
  Stage the release that is currently running, i.e.

    1) Make sure the release can be staged (the stories are reviewed and tested)
    2) Reset the staging branch to point to the release branch.
    3) Delete the release branch.
    4) Bump the version for the staging branch.
    5) Push the changes.
	`,
	Action: run,
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 0 {
		cmd.Usage()
		os.Exit(2)
	}

	app.InitOrDie()

	defer prompt.RecoverCancel()

	if err := runMain(); err != nil {
		errs.Fatal(err)
	}
}

func runMain() (err error) {
	// Load git config.
	gitConfig, err := git.LoadConfig()
	if err != nil {
		return err
	}
	var (
		remoteName    = gitConfig.RemoteName()
		releaseBranch = gitConfig.ReleaseBranchName()
		stagingBranch = gitConfig.StagingBranchName()
	)

	// Instantiate the issue tracker.
	tracker, err := modules.GetIssueTracker()
	if err != nil {
		return err
	}

	// Get the current branch.
	task := "Get the current branch"
	currentBranch, err := git.CurrentBranch()
	if err != nil {
		return err
	}

	// Cannot be on the release branch, it will be deleted.
	task = "Make sure that the release branch is not checked out"
	if currentBranch == releaseBranch {
		return errs.NewError(
			task, errors.New("cannot stage the release while on the release branch"))
	}

	// Fetch the remote repository.
	task = "Fetch the remote repository"
	log.Run(task)
	if err := git.UpdateRemotes(remoteName); err != nil {
		return errs.NewError(task, err)
	}

	// Make sure the remote release branch exists.
	task = fmt.Sprintf("Make sure that branch '%v' exists in remote '%v'", releaseBranch, remoteName)
	log.Run(task)
	exists, err := git.RemoteBranchExists(releaseBranch, remoteName)
	if err != nil {
		return errs.NewError(task, err)
	}
	if !exists {
		return errs.NewError(task,
			fmt.Errorf("branch '%v' not found in remote '%v'", releaseBranch, remoteName))
	}

	// Make sure that the local release branch exists.
	task = fmt.Sprintf("Make sure that branch '%v' exists locally", releaseBranch)
	log.Run(task)
	if err := git.CreateTrackingBranchUnlessExists(releaseBranch, remoteName); err != nil {
		return errs.NewError(task, err)
	}

	// Make sure that the release branch is up to date.
	task = fmt.Sprintf("Make sure that branch '%v' is up to date", releaseBranch)
	log.Run(task)
	if err := git.EnsureBranchSynchronized(releaseBranch, remoteName); err != nil {
		return errs.NewError(task, err)
	}

	// Read the current release version.
	task = "Read the current release version"
	releaseVersion, err := version.GetByBranch(releaseBranch)
	if err != nil {
		return errs.NewError(task, err)
	}

	// Make sure the release is stageable.
	release, err := tracker.RunningRelease(releaseVersion)
	if err != nil {
		return err
	}
	if err := release.EnsureStageable(); err != nil {
		return err
	}

	// Make sure there are no commits being left behind,
	// e.g. make sure no commits are forgotten on the trunk branch,
	// i.e. make sure that everything necessary was cherry-picked.
	if err := checkCommits(tracker, release, releaseBranch); err != nil {
		return err
	}

	// Reset the staging branch to point to the newly created tag.
	task = fmt.Sprintf("Reset branch '%v' to point to branch '%v'", stagingBranch, releaseBranch)
	log.Run(task)
	act, err := git.CreateOrResetBranch(stagingBranch, releaseBranch)
	if err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, act)

	// Delete the local release branch.
	task = fmt.Sprintf("Delete branch '%v'", releaseBranch)
	log.Run(task)
	if err := git.Branch("-D", releaseBranch); err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, action.ActionFunc(func() error {
		task := fmt.Sprintf("Recreate branch '%v'", releaseBranch)

		// In case the release branch exists locally, do nothing.
		// This might look like an extra and useless check, but it looks like
		// the final git push at the end of the command function actually creates
		// the release branch locally when it is aborted from the pre-push hook.
		// Not sure why and how that is happening.
		exists, err := git.LocalBranchExists(releaseBranch)
		if err != nil {
			return errs.NewError(task, err)
		}
		if exists {
			return nil
		}

		// In case the branch indeed does not exist, create it.
		if err := git.Branch(releaseBranch, remoteName+"/"+releaseBranch); err != nil {
			return errs.NewError(task, err)
		}
		return nil
	}))

	// Update the version string on the staging branch.
	stagingVersion, err := releaseVersion.ToStageVersion()
	if err != nil {
		return err
	}

	task = fmt.Sprintf("Bump version (branch '%v' -> %v)", stagingBranch, stagingVersion)
	log.Run(task)
	act, err = version.SetForBranch(stagingVersion, stagingBranch)
	if err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, act)

	// Finalise the release in the code review tool.
	codeReviewTool, err := modules.GetCodeReviewTool()
	if err != nil {
		return err
	}
	act, err = codeReviewTool.FinaliseRelease(releaseVersion)
	if err != nil {
		return err
	}
	// No need to pass any task string, the module rollback functions
	// are expected to take care of printing messages on their own.
	defer action.RollbackOnError(&err, act)

	// Stage the release in the issue tracker.
	act, err = release.Stage()
	if err != nil {
		return err
	}
	defer action.RollbackOnError(&err, act)

	// Push to create the tag, reset client and delete release in the remote repository.
	task = "Push changes to the remote repository"
	log.Run(task)
	return git.Push(remoteName,
		"-f",                            // Use the Force, Luke.
		":"+releaseBranch,               // Delete the release branch.
		stagingBranch+":"+stagingBranch) // Push the staging branch.
}

func checkCommits(
	tracker common.IssueTracker,
	release common.RunningRelease,
	releaseBranch string,
) error {

	var task = "Make sure no changes are being left behind"
	log.Run(task)

	stories, err := release.Stories()
	if err != nil {
		return errs.NewError(task, err)
	}
	if len(stories) == 0 {
		return nil
	}

	groups, err := changes.StoryChanges(stories)
	if err != nil {
		return errs.NewError(task, err)
	}

	toCherryPick, err := releases.StoryChangesToCherryPick(groups)
	if err != nil {
		return errs.NewError(task, err)
	}

	// In case there are some changes being left behind,
	// ask the user to confirm whether to proceed or not.
	if len(toCherryPick) == 0 {
		return nil
	}

	fmt.Println(`
Some changes are being left behind!

In other words, some changes that are assigned to the current release
have not been cherry-picked onto the release branch yet.
	`)
	if err := changes.DumpStoryChanges(os.Stdout, toCherryPick, tracker, false); err != nil {
		panic(err)
	}
	fmt.Println()

	confirmed, err := prompt.Confirm("Are you sure you really want to stage the release?")
	if err != nil {
		return errs.NewError(task, err)
	}
	if !confirmed {
		prompt.PanicCancel()
	}
	fmt.Println()

	return nil
}
<|endoftext|>