<commit_before>package shards

// These are the endpoints required for
// cheshire sharding to work.
const (
	//router table get endpoint
	// response format
	// {
	//  "strest" :{...}
	//  "router_table" : <the router table>
	// }
	ROUTERTABLE_GET = "/__c/rt/get"

	// Sets the router table on this server
	// @method POST
	// @param router_table The router table
	ROUTERTABLE_SET = "/__c/rt/set"

	PARTITION_LOCK   = "/__c/pt/lock"
	PARTITION_UNLOCK = "/__c/pt/unlock"

	// Delete a partition from this server
	PARTITION_DELETE = "/__c/pt/delete"

	// Is a ping endpoint to check for liveness and
	// to check the revision of the router table.
	// response format
	// {
	//  "strest" :{...}
	//  "ts" : <ISOFORMATED TIMESTAMP>
	//  "rt_revision" : <router table revision>
	// }
	CHECKIN = "/__c/checkin"

	// Creates a stream of data for the given partition
	// @param partition the int partition
	// data is in the key "data"
	DATA_PULL = "/__c/data/pull"

	// Push data at the given partition
	// the pushed data should overwrite any data that exists at the given key
	// @method PUT
	// @param data the data object (usually a json packet)
	// @param partition the partition
	DATA_PUSH = "/__c/data/push"
)

//These are the required return error codes for various situations
const (
	// return when the requester has an old router table
	E_ROUTER_TABLE_OLD = 432

	// requester has a newer router table then us, request they update ours
	E_SEND_ROUTER_TABLE = 433

	// the requested partition is locked.  requester should try back in a bit
	E_PARTITION_LOCKED = 434

	// The requested partition does not live on this shard
	E_NOT_MY_PARTITION = 435
)

// Param Names
const (
	//The partition val (an integer from 0 to TotalPartitions)
	P_PARTITION = "_p"

	// The version of the router table
	P_REVISION = "_v"

	//The query type.
	// This defines how the request can be handled by the router.
	// Possible values:
	// single : return a single result (the first response received)
	// all : (default) return values for all servers, will make an effort to retry on failure, but will generally return error results.
	// all_q : return values for all servers (queue requests if needed, retry until response).  This would typically be for posting
	P_QUERY_TYPE = "_qt"
)
<commit_msg>add another query type (unimplemented)<commit_after>package shards

// These are the endpoints required for
// cheshire sharding to work.
const (
	//router table get endpoint
	// response format
	// {
	//  "strest" :{...}
	//  "router_table" : <the router table>
	// }
	ROUTERTABLE_GET = "/__c/rt/get"

	// Sets the router table on this server
	// @method POST
	// @param router_table The router table
	ROUTERTABLE_SET = "/__c/rt/set"

	PARTITION_LOCK   = "/__c/pt/lock"
	PARTITION_UNLOCK = "/__c/pt/unlock"

	// Delete a partition from this server
	PARTITION_DELETE = "/__c/pt/delete"

	// Is a ping endpoint to check for liveness and
	// to check the revision of the router table.
	// response format
	// {
	//  "strest" :{...}
	//  "ts" : <ISOFORMATED TIMESTAMP>
	//  "rt_revision" : <router table revision>
	// }
	CHECKIN = "/__c/checkin"

	// Creates a stream of data for the given partition
	// @param partition the int partition
	// data is in the key "data"
	DATA_PULL = "/__c/data/pull"

	// Push data at the given partition
	// the pushed data should overwrite any data that exists at the given key
	// @method PUT
	// @param data the data object (usually a json packet)
	// @param partition the partition
	DATA_PUSH = "/__c/data/push"
)

//These are the required return error codes for various situations
const (
	// return when the requester has an old router table
	E_ROUTER_TABLE_OLD = 432

	// requester has a newer router table then us, request they update ours
	E_SEND_ROUTER_TABLE = 433

	// the requested partition is locked.  requester should try back in a bit
	E_PARTITION_LOCKED = 434

	// The requested partition does not live on this shard
	E_NOT_MY_PARTITION = 435
)

// Param Names
const (
	//The partition val (an integer from 0 to TotalPartitions)
	P_PARTITION = "_p"

	// The version of the router table
	P_REVISION = "_v"

	//The query type.
	// This defines how the request can be handled by the router.
	// Possible values:
	// single : return a single result (the first response received)
	// all : (default) return values for all servers, will make an effort to retry on failure, but will generally return error results.
	// all_q : return values for all servers (queue requests if needed, retry until response).  This would typically be for posting
	// none_q : returns success immediately, queues the request and make best effort to ensure it is delivered (TODO)
	P_QUERY_TYPE = "_qt"
)
<|endoftext|>


<commit_before>// Copyright 2017, OpenCensus 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 stats

import (
	"bytes"
	"fmt"
	"reflect"
	"sync/atomic"
	"time"

	"go.opencensus.io/tag"
)

// View allows users to filter and aggregate the recorded events
// over a time window. Each view has to be registered to enable
// data retrieval. Use NewView to initiate new views.
// Unregister views once you don't want to collect any more events.
type View struct {
	name        string // name of View. Must be unique.
	description string

	// tagKeys to perform the aggregation on.
	tagKeys []tag.Key

	// Examples of measures are cpu:tickCount, diskio:time...
	m Measure

	// start is time when view collection was started originally.
	start time.Time

	forced     uint32 // 1 if view should collect data if no one is subscribed, use atomic to access
	subscribed uint32 // 1 if someone is subscribed and data need to be exported, use atomic to access

	collector *collector
}

// NewView creates a new view with the given name and description.
// View names need to be unique globally in the entire system.
//
// Data collection will only filter measurements recorded by the given keys.
// Collected data will be processed by the given aggregation algorithm for
// the given time window.
//
// Views need to be registered via RegisterView, or subscribed to, or need to be force
// collected to retrieve collection data. Once the view is no longer required,
// view can be unregistered.
func NewView(name, description string, keys []tag.Key, measure Measure, agg Aggregation, window Window) *View {
	return &View{
		name:        name,
		description: description,
		tagKeys:     keys,
		m:           measure,
		start:       time.Time{},
		collector:   &collector{make(map[string]aggregator), agg, window},
	}
}

// Name returns the name of the view.
func (v *View) Name() string {
	return v.name
}

// Description returns the name of the view.
func (v *View) Description() string {
	return v.description
}

func (v *View) startForcedCollection() {
	atomic.StoreUint32(&v.forced, 1)
}

func (v *View) stopForcedCollection() {
	atomic.StoreUint32(&v.forced, 0)
}

func (v *View) subscribe() {
	atomic.StoreUint32(&v.subscribed, 1)
}

func (v *View) unsubscribe() {
	atomic.StoreUint32(&v.subscribed, 0)
}

// isCollecting returns true if the view is exporting data
// by subscription or enabled for force collection.
func (v *View) isCollecting() bool {
	return atomic.LoadUint32(&v.subscribed) == 1 || atomic.LoadUint32(&v.forced) == 1
}

// isSubscribed returns true if the view is exporting
// data by subscription.
func (v *View) isSubscribed() bool {
	return atomic.LoadUint32(&v.subscribed) == 1
}

func (v *View) clearRows() {
	v.collector.clearRows()
}

// Window returns the timing window being used to collect
// metrics from this view.
func (v *View) Window() Window {
	return v.collector.w
}

// Aggregation returns the data aggregation method used to aggregate
// the measurements collected by this view.
func (v *View) Aggregation() Aggregation {
	return v.collector.a
}

// Measure returns the measure the view is collecting measurements for.
func (v *View) Measure() Measure {
	return v.m
}

func (v *View) collectedRows(now time.Time) []*Row {
	return v.collector.collectedRows(v.tagKeys, now)
}

func (v *View) addSample(m *tag.Map, val interface{}, now time.Time) {
	if !v.isCollecting() {
		return
	}
	sig := string(tag.EncodeOrderedTags(m, v.tagKeys))
	v.collector.addSample(sig, val, now)
}

// A ViewData is a set of rows about usage of the single measure associated
// with the given view during a particular window. Each row is specific to a
// unique set of tags.
type ViewData struct {
	View       *View
	Start, End time.Time
	Rows       []*Row
}

// Row is the collected value for a specific set of key value pairs a.k.a tags.
type Row struct {
	Tags             []tag.Tag
	AggregationValue AggregationValue
}

func (r *Row) String() string {
	var buffer bytes.Buffer
	buffer.WriteString("{ ")
	buffer.WriteString("{ ")
	for _, t := range r.Tags {
		buffer.WriteString(fmt.Sprintf("{%v %v}", t.Key.Name(), t.Key.ValueToString(t.Value)))
	}
	buffer.WriteString(" }")
	buffer.WriteString(fmt.Sprintf("%v", r.AggregationValue))
	buffer.WriteString(" }")
	return buffer.String()
}

// Equal returns true if both Rows are equal. Tags are expected to be ordered
// by the key name. Even both rows have the same tags but the tags appear in
// different orders it will return false.
func (r *Row) Equal(other *Row) bool {
	if r == other {
		return true
	}

	return reflect.DeepEqual(r.Tags, other.Tags) && r.AggregationValue.equal(other.AggregationValue)
}

// ContainsRow returns true if rows contain r.
func ContainsRow(rows []*Row, r *Row) bool {
	for _, x := range rows {
		if r.Equal(x) {
			return true
		}
	}
	return false
}

// EqualRows returns true if rows1 and rows2 contain exactly the same data.
func EqualRows(rows1, rows2 []*Row) bool {
	if len(rows1) != len(rows2) {
		return false
	}
	for _, r1 := range rows1 {
		if !ContainsRow(rows2, r1) {
			return false
		}
	}
	return true
}
<commit_msg>Remove EqualRows (#101)<commit_after>// Copyright 2017, OpenCensus 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 stats

import (
	"bytes"
	"fmt"
	"reflect"
	"sync/atomic"
	"time"

	"go.opencensus.io/tag"
)

// View allows users to filter and aggregate the recorded events
// over a time window. Each view has to be registered to enable
// data retrieval. Use NewView to initiate new views.
// Unregister views once you don't want to collect any more events.
type View struct {
	name        string // name of View. Must be unique.
	description string

	// tagKeys to perform the aggregation on.
	tagKeys []tag.Key

	// Examples of measures are cpu:tickCount, diskio:time...
	m Measure

	// start is time when view collection was started originally.
	start time.Time

	forced     uint32 // 1 if view should collect data if no one is subscribed, use atomic to access
	subscribed uint32 // 1 if someone is subscribed and data need to be exported, use atomic to access

	collector *collector
}

// NewView creates a new view with the given name and description.
// View names need to be unique globally in the entire system.
//
// Data collection will only filter measurements recorded by the given keys.
// Collected data will be processed by the given aggregation algorithm for
// the given time window.
//
// Views need to be registered via RegisterView, or subscribed to, or need to be force
// collected to retrieve collection data. Once the view is no longer required,
// view can be unregistered.
func NewView(name, description string, keys []tag.Key, measure Measure, agg Aggregation, window Window) *View {
	return &View{
		name:        name,
		description: description,
		tagKeys:     keys,
		m:           measure,
		start:       time.Time{},
		collector:   &collector{make(map[string]aggregator), agg, window},
	}
}

// Name returns the name of the view.
func (v *View) Name() string {
	return v.name
}

// Description returns the name of the view.
func (v *View) Description() string {
	return v.description
}

func (v *View) startForcedCollection() {
	atomic.StoreUint32(&v.forced, 1)
}

func (v *View) stopForcedCollection() {
	atomic.StoreUint32(&v.forced, 0)
}

func (v *View) subscribe() {
	atomic.StoreUint32(&v.subscribed, 1)
}

func (v *View) unsubscribe() {
	atomic.StoreUint32(&v.subscribed, 0)
}

// isCollecting returns true if the view is exporting data
// by subscription or enabled for force collection.
func (v *View) isCollecting() bool {
	return atomic.LoadUint32(&v.subscribed) == 1 || atomic.LoadUint32(&v.forced) == 1
}

// isSubscribed returns true if the view is exporting
// data by subscription.
func (v *View) isSubscribed() bool {
	return atomic.LoadUint32(&v.subscribed) == 1
}

func (v *View) clearRows() {
	v.collector.clearRows()
}

// Window returns the timing window being used to collect
// metrics from this view.
func (v *View) Window() Window {
	return v.collector.w
}

// Aggregation returns the data aggregation method used to aggregate
// the measurements collected by this view.
func (v *View) Aggregation() Aggregation {
	return v.collector.a
}

// Measure returns the measure the view is collecting measurements for.
func (v *View) Measure() Measure {
	return v.m
}

func (v *View) collectedRows(now time.Time) []*Row {
	return v.collector.collectedRows(v.tagKeys, now)
}

func (v *View) addSample(m *tag.Map, val interface{}, now time.Time) {
	if !v.isCollecting() {
		return
	}
	sig := string(tag.EncodeOrderedTags(m, v.tagKeys))
	v.collector.addSample(sig, val, now)
}

// A ViewData is a set of rows about usage of the single measure associated
// with the given view during a particular window. Each row is specific to a
// unique set of tags.
type ViewData struct {
	View       *View
	Start, End time.Time
	Rows       []*Row
}

// Row is the collected value for a specific set of key value pairs a.k.a tags.
type Row struct {
	Tags             []tag.Tag
	AggregationValue AggregationValue
}

func (r *Row) String() string {
	var buffer bytes.Buffer
	buffer.WriteString("{ ")
	buffer.WriteString("{ ")
	for _, t := range r.Tags {
		buffer.WriteString(fmt.Sprintf("{%v %v}", t.Key.Name(), t.Key.ValueToString(t.Value)))
	}
	buffer.WriteString(" }")
	buffer.WriteString(fmt.Sprintf("%v", r.AggregationValue))
	buffer.WriteString(" }")
	return buffer.String()
}

// Equal returns true if both Rows are equal. Tags are expected to be ordered
// by the key name. Even both rows have the same tags but the tags appear in
// different orders it will return false.
func (r *Row) Equal(other *Row) bool {
	if r == other {
		return true
	}

	return reflect.DeepEqual(r.Tags, other.Tags) && r.AggregationValue.equal(other.AggregationValue)
}

// ContainsRow returns true if rows contain r.
func ContainsRow(rows []*Row, r *Row) bool {
	for _, x := range rows {
		if r.Equal(x) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package block

import (
	"bytes"
	"regexp"
)

// TODO(akavel): add tests for blocks

type Line []byte

func (line Line) isBlank() bool {
	return len(bytes.Trim(line, " \t")) == 0
}
func (line Line) hasNonSpaceInPrefix(n int) bool {
	for i := 0; i < n && i < len(line); i++ {
		if line[i] != ' ' {
			return true
		}
	}
	return false
}
func (line Line) hasFourSpacePrefix() bool {
	return bytes.HasPrefix(line, []byte("    "))
}

type Detector interface {
	// Detect checks if the provided start line and optionally second line
	// signify start of the particular block kind.  If unsuccessful, 0 and
	// 0 should be returned.  If successful, at least one of the returned
	// numbers should be positive.  Consume is number of lines that sure
	// belong to the block, and won't be needed in any subsequent calls to
	// Continue.  Pause is number of lines that may be still needed in
	// subsequent calls to Continue, and/or aren't yet fully confirmed to
	// belong to the block.
	//
	// Note: it is not allowed for Detect to report 0 lines to consume and
	// then for Continue to reject all the paused lines.
	//
	// Note: second==nil means end of file/stream
	Detect(start, second Line) (consume, pause int)
	// Continue checks if the specified paused lines and next line may
	// belong to the block, as reported started by Detect.  If any of the
	// lines is detected to be of a next block, Continue should report:
	// consume <= len(paused), and nothing to pause.  Otherwise, Continue
	// must report: consume+pause == len(paused)+1.
	//
	// Number of paused lines passed to Continue will always be equal to
	// value reported from previous Detect or Continue.
	//
	// Note: next==nil means end of file/stream; however, Continue will
	// never be called with next==nil if previous call to
	// Detect/Continue didn't report any lines to pause.
	Continue(paused []Line, next Line) (consume, pause int)
}

var (
	reUnorderedList  = regexp.MustCompile(`^( *[\*\-\+] +)[^ ]`)
	reOrderedList    = regexp.MustCompile(`^( *([0-9]+)\. +)[^ ]`)
	reHorizontalRule = regexp.MustCompile(`^ *((\* *\* *\* *[\* ]*)|(\- *\- *\- *[\- ]*)|(_ *_ *_ *[_ ]*))$`)
)

type NeverContinue struct{}

func (NeverContinue) Continue([]Line, Line) (consume, pause int) { return 0, 0 }

// static assertion of Detector interface implementation by the listed types
var _ []Detector = []Detector{
	&Null{},
	&ReferenceResolution{},
	&SetextHeader{},
	&Code{},
	&AtxHeader{},
	&Quote{},
	&HorizontalRule{},
	&UnorderedList{},
	&OrderedList{},
	&Paragraph{},
}

type Null struct{ NeverContinue }

func (b *Null) Detect(start, second Line) (consume, pause int) {
	if start.isBlank() {
		return 1, 0
	}
	return 0, 0
}

type ReferenceResolution struct {
	NeverContinue
	unprocessedReferenceID        []byte
	refValueSequence              []byte
	unprocessedUrl                []byte
	refDefinitionTrailingSequence []byte
}

func (b *ReferenceResolution) Detect(start, second Line) (consume, pause int) {
	if start.hasFourSpacePrefix() {
		return 0, 0
	}
	// TODO(akavel): move the regexp out of function, for speed (or cache it?)
	re := regexp.MustCompile(`^ *\[(([^\\\[\]\!]|\\.|\![^\[])*((\!\[([^\\\[\]]|\\.)*\](\[([^\\\[\]]|\\.)*\])?)?([^\\\[\]]|\\.)*)*)\] *:(.*)$`)
	m := re.FindSubmatch(start)
	if len(m) == 0 {
		return 0, 0
	}
	b.unprocessedReferenceID = m[1]
	b.refValueSequence = m[9] // TODO(akavel): verify if right one
	re = regexp.MustCompile(`^ *([^ \<\>]+|\<[^\<\>]*\>)( .*)?$`)
	m = re.FindSubmatch(b.refValueSequence)
	if len(m) == 0 {
		return 0, 0
	}
	b.unprocessedUrl = m[1]
	b.refDefinitionTrailingSequence = m[2]

	// Detected ok. Now check if 1 or 2 lines.
	re = regexp.MustCompile(`^ +("(([^"\\]|\\.)*)"|'(([^'\\]|\\.)*)'|\(([^\\\(\)]|\\.)*\)) *$`)
	if bytes.IndexAny(b.refDefinitionTrailingSequence, " ") == -1 &&
		second != nil &&
		re.Match(second) {
		return 2, 0
	} else {
		return 1, 0
	}
}

type SetextHeader struct{ NeverContinue }

func (b *SetextHeader) Detect(start, second Line) (consume, pause int) {
	if second == nil {
		return 0, 0
	}
	re := regexp.MustCompile(`^(-+|=+) *$`)
	if re.Match(second) {
		return 2, 0
	}
	return 0, 0
}

type Code struct{}

func (b *Code) Detect(start, second Line) (consume, pause int) {
	if start.hasFourSpacePrefix() {
		return 1, 0
	}
	return 0, 0
}
func (b *Code) Continue(paused []Line, next Line) (consume, pause int) {
	// FIXME(akavel): handle next==nil !!!
	if next == nil {
		return 0, 0
		// note: len(paused)==1 if prev was blank, so we can ditch it anyway
	}
	// TODO(akavel): verify it's coded ok, it was converted from a different approach
	switch {
	// previous was blank, next is not tab-indented. Reject both.
	case len(paused) == 1 && !next.hasFourSpacePrefix():
		return 0, 0
	case next.isBlank():
		return len(paused), 1 // note: only case where we pause a line
	case next.hasFourSpacePrefix():
		return len(paused) + 1, 0
	// next not blank & not indented. End the block.
	default:
		return len(paused), 0
	}
}

type AtxHeader struct{ NeverContinue }

func (b *AtxHeader) Detect(start, second Line) (consume, pause int) {
	if bytes.HasPrefix(start, []byte("#")) {
		return 1, 0
	}
	return 0, 0
}

type Quote struct{}

func (b *Quote) Detect(start, second Line) (consume, pause int) {
	ltrim := bytes.TrimLeft(start, " ")
	if len(ltrim) > 0 && ltrim[0] == '>' {
		return 0, 1
	}
	return 0, 0
}
func (b *Quote) Continue(paused []Line, next Line) (consume, pause int) {
	// TODO(akavel): verify it's coded ok, it was converted from a different approach
	if next == nil {
		return len(paused), 0
	}
	if paused[0].isBlank() {
		if next.isBlank() ||
			next.hasFourSpacePrefix() ||
			bytes.TrimLeft(next, " ")[0] != '>' {
			return len(paused), 0
		}
	} else if !next.hasFourSpacePrefix() &&
		reHorizontalRule.Match(next) {
		return len(paused), 0
	}
	return len(paused), 1
}

type HorizontalRule struct{ NeverContinue }

func (b *HorizontalRule) Detect(start, second Line) (consume, pause int) {
	if reHorizontalRule.Match(start) {
		return 1, 0
	}
	return 0, 0
}

type UnorderedList struct {
	Starter []byte
}

func (b *UnorderedList) Detect(start, second Line) (consume, pause int) {
	m := reUnorderedList.FindSubmatch(start)
	if m == nil {
		return 0, 0
	}
	b.Starter = m[1]
	return 0, 1
}
func (b *UnorderedList) Continue(paused []Line, next Line) (consume, pause int) {
	if next == nil {
		return len(paused), 0
	}

	if paused[0].isBlank() {
		if next.isBlank() {
			return len(paused), 0
		}
		if !bytes.HasPrefix(next, b.Starter) &&
			// FIXME(akavel): spec refers to runes ("characters"), not bytes; fix this everywhere
			next.hasNonSpaceInPrefix(len(b.Starter)) {
			return len(paused), 0
		}
	} else {
		if !bytes.HasPrefix(next, b.Starter) &&
			next.hasNonSpaceInPrefix(len(b.Starter)) &&
			!next.hasFourSpacePrefix() &&
			(reUnorderedList.Match(next) ||
				reOrderedList.Match(next) ||
				reHorizontalRule.Match(next)) {
			return len(paused), 0
		}
	}
	return len(paused), 1
}

type OrderedList struct {
	Starter []byte
}

func (b *OrderedList) Detect(start, second Line) (consume, pause int) {
	m := reOrderedList.FindSubmatch(start)
	if m == nil {
		return 0, 0
	}
	b.Starter = m[1]
	return 0, 1
}
func (b *OrderedList) Continue(paused []Line, next Line) (consume, pause int) {
	if next == nil {
		return len(paused), 0
	}

	if paused[0].isBlank() {
		if next.isBlank() {
			return len(paused), 0
		}
		if !reOrderedList.Match(next) &&
			next.hasNonSpaceInPrefix(len(b.Starter)) {
			return len(paused), 0
		}
	} else {
		if !reOrderedList.Match(next) &&
			next.hasNonSpaceInPrefix(len(b.Starter)) &&
			!next.hasFourSpacePrefix() &&
			(reUnorderedList.Match(next) ||
				reHorizontalRule.Match(next)) {
			return len(paused), 0
		}
	}
	return len(paused), 1
}

type Paragraph struct {
	// NOTE: below fields must be set appropriately when creating a Paragraph
	InQuote bool
	InList  bool
}

func (b *Paragraph) Detect(start, second Line) (consume, pause int) {
	return 0, 1
}
func (b *Paragraph) Continue(paused []Line, next Line) (consume, pause int) {
	if next == nil {
		return len(paused), 0
	}
	// TODO(akavel): support HTML parser & related interactions [#paragraph-line-sequence]
	if paused[0].isBlank() {
		return len(paused), 0
	}
	if !next.hasFourSpacePrefix() {
		if reHorizontalRule.Match(next) ||
			(b.InQuote && bytes.HasPrefix(bytes.TrimLeft(next, " "), []byte(">"))) ||
			(b.InList && reOrderedList.Match(next)) ||
			(b.InList && reUnorderedList.Match(next)) {
			return len(paused), 0
		}
	}
	return len(paused), 1
}

/*
NOTES:

AtxHeader
 -> text-span-sequence

SetextHeader
 -> text-span-sequence

Quote
 -> process lines (strip certain prefix bytes)
  -> detect[defaults..., Paragraph{InQuote=1}]

UnorderedList
 -> detect[UnorderedItem]
  -> process lines (strip certain prefix bytes)
   -> detect[defaults..., Paragraph{InList=1}]

OrderedList
 -> detect[OrderedItem]
  -> process lines (strip certain prefix bytes)
   -> detect[defaults..., Paragraph{InList=1}]

Paragraph
 -> join & trim
  -> process as text-span-sequence

Block{
	block.Detector
	NLines int
	Nested []Block
}

// This function is guaranteed to be run after the line has been reported by
// Detect or Continue as consumed, and after all preceding lines were
// PostProcessed, and before any subsequent lines were PostProcessed.
PostProcess(Line)
*/
<commit_msg>name DefaultDetectors, and some need not be ptrs<commit_after>package block

import (
	"bytes"
	"regexp"
)

// TODO(akavel): add tests for blocks

type Line []byte

func (line Line) isBlank() bool {
	return len(bytes.Trim(line, " \t")) == 0
}
func (line Line) hasNonSpaceInPrefix(n int) bool {
	for i := 0; i < n && i < len(line); i++ {
		if line[i] != ' ' {
			return true
		}
	}
	return false
}
func (line Line) hasFourSpacePrefix() bool {
	return bytes.HasPrefix(line, []byte("    "))
}

type Detector interface {
	// Detect checks if the provided start line and optionally second line
	// signify start of the particular block kind.  If unsuccessful, 0 and
	// 0 should be returned.  If successful, at least one of the returned
	// numbers should be positive.  Consume is number of lines that sure
	// belong to the block, and won't be needed in any subsequent calls to
	// Continue.  Pause is number of lines that may be still needed in
	// subsequent calls to Continue, and/or aren't yet fully confirmed to
	// belong to the block.
	//
	// Note: it is not allowed for Detect to report 0 lines to consume and
	// then for Continue to reject all the paused lines.
	//
	// Note: second==nil means end of file/stream
	Detect(start, second Line) (consume, pause int)
	// Continue checks if the specified paused lines and next line may
	// belong to the block, as reported started by Detect.  If any of the
	// lines is detected to be of a next block, Continue should report:
	// consume <= len(paused), and nothing to pause.  Otherwise, Continue
	// must report: consume+pause == len(paused)+1.
	//
	// Number of paused lines passed to Continue will always be equal to
	// value reported from previous Detect or Continue.
	//
	// Note: next==nil means end of file/stream; however, Continue will
	// never be called with next==nil if previous call to
	// Detect/Continue didn't report any lines to pause.
	Continue(paused []Line, next Line) (consume, pause int)
}

var (
	reUnorderedList  = regexp.MustCompile(`^( *[\*\-\+] +)[^ ]`)
	reOrderedList    = regexp.MustCompile(`^( *([0-9]+)\. +)[^ ]`)
	reHorizontalRule = regexp.MustCompile(`^ *((\* *\* *\* *[\* ]*)|(\- *\- *\- *[\- ]*)|(_ *_ *_ *[_ ]*))$`)
)

type NeverContinue struct{}

func (NeverContinue) Continue([]Line, Line) (consume, pause int) { return 0, 0 }

// DefaultDetectors contains the list of default detectors in order in which
// they should be normally applied.
var DefaultDetectors []Detector = []Detector{
	Null{},
	&ReferenceResolution{},
	SetextHeader{},
	Code{},
	AtxHeader{},
	Quote{},
	HorizontalRule{},
	&UnorderedList{},
	&OrderedList{},
	Paragraph{},
}

type Null struct{ NeverContinue }

func (Null) Detect(start, second Line) (consume, pause int) {
	if start.isBlank() {
		return 1, 0
	}
	return 0, 0
}

type ReferenceResolution struct {
	NeverContinue
	unprocessedReferenceID        []byte
	refValueSequence              []byte
	unprocessedUrl                []byte
	refDefinitionTrailingSequence []byte
}

func (b *ReferenceResolution) Detect(start, second Line) (consume, pause int) {
	if start.hasFourSpacePrefix() {
		return 0, 0
	}
	// TODO(akavel): move the regexp out of function, for speed (or cache it?)
	re := regexp.MustCompile(`^ *\[(([^\\\[\]\!]|\\.|\![^\[])*((\!\[([^\\\[\]]|\\.)*\](\[([^\\\[\]]|\\.)*\])?)?([^\\\[\]]|\\.)*)*)\] *:(.*)$`)
	m := re.FindSubmatch(start)
	if len(m) == 0 {
		return 0, 0
	}
	b.unprocessedReferenceID = m[1]
	b.refValueSequence = m[9] // TODO(akavel): verify if right one
	re = regexp.MustCompile(`^ *([^ \<\>]+|\<[^\<\>]*\>)( .*)?$`)
	m = re.FindSubmatch(b.refValueSequence)
	if len(m) == 0 {
		return 0, 0
	}
	b.unprocessedUrl = m[1]
	b.refDefinitionTrailingSequence = m[2]

	// Detected ok. Now check if 1 or 2 lines.
	re = regexp.MustCompile(`^ +("(([^"\\]|\\.)*)"|'(([^'\\]|\\.)*)'|\(([^\\\(\)]|\\.)*\)) *$`)
	if bytes.IndexAny(b.refDefinitionTrailingSequence, " ") == -1 &&
		second != nil &&
		re.Match(second) {
		return 2, 0
	} else {
		return 1, 0
	}
}

type SetextHeader struct{ NeverContinue }

func (SetextHeader) Detect(start, second Line) (consume, pause int) {
	if second == nil {
		return 0, 0
	}
	re := regexp.MustCompile(`^(-+|=+) *$`)
	if re.Match(second) {
		return 2, 0
	}
	return 0, 0
}

type Code struct{}

func (Code) Detect(start, second Line) (consume, pause int) {
	if start.hasFourSpacePrefix() {
		return 1, 0
	}
	return 0, 0
}
func (Code) Continue(paused []Line, next Line) (consume, pause int) {
	// FIXME(akavel): handle next==nil !!!
	if next == nil {
		return 0, 0
		// note: len(paused)==1 if prev was blank, so we can ditch it anyway
	}
	// TODO(akavel): verify it's coded ok, it was converted from a different approach
	switch {
	// previous was blank, next is not tab-indented. Reject both.
	case len(paused) == 1 && !next.hasFourSpacePrefix():
		return 0, 0
	case next.isBlank():
		return len(paused), 1 // note: only case where we pause a line
	case next.hasFourSpacePrefix():
		return len(paused) + 1, 0
	// next not blank & not indented. End the block.
	default:
		return len(paused), 0
	}
}

type AtxHeader struct{ NeverContinue }

func (AtxHeader) Detect(start, second Line) (consume, pause int) {
	if bytes.HasPrefix(start, []byte("#")) {
		return 1, 0
	}
	return 0, 0
}

type Quote struct{}

func (Quote) Detect(start, second Line) (consume, pause int) {
	ltrim := bytes.TrimLeft(start, " ")
	if len(ltrim) > 0 && ltrim[0] == '>' {
		return 0, 1
	}
	return 0, 0
}
func (Quote) Continue(paused []Line, next Line) (consume, pause int) {
	// TODO(akavel): verify it's coded ok, it was converted from a different approach
	if next == nil {
		return len(paused), 0
	}
	if paused[0].isBlank() {
		if next.isBlank() ||
			next.hasFourSpacePrefix() ||
			bytes.TrimLeft(next, " ")[0] != '>' {
			return len(paused), 0
		}
	} else if !next.hasFourSpacePrefix() &&
		reHorizontalRule.Match(next) {
		return len(paused), 0
	}
	return len(paused), 1
}

type HorizontalRule struct{ NeverContinue }

func (HorizontalRule) Detect(start, second Line) (consume, pause int) {
	if reHorizontalRule.Match(start) {
		return 1, 0
	}
	return 0, 0
}

type UnorderedList struct {
	Starter []byte
}

func (b *UnorderedList) Detect(start, second Line) (consume, pause int) {
	m := reUnorderedList.FindSubmatch(start)
	if m == nil {
		return 0, 0
	}
	b.Starter = m[1]
	return 0, 1
}
func (b *UnorderedList) Continue(paused []Line, next Line) (consume, pause int) {
	if next == nil {
		return len(paused), 0
	}

	if paused[0].isBlank() {
		if next.isBlank() {
			return len(paused), 0
		}
		if !bytes.HasPrefix(next, b.Starter) &&
			// FIXME(akavel): spec refers to runes ("characters"), not bytes; fix this everywhere
			next.hasNonSpaceInPrefix(len(b.Starter)) {
			return len(paused), 0
		}
	} else {
		if !bytes.HasPrefix(next, b.Starter) &&
			next.hasNonSpaceInPrefix(len(b.Starter)) &&
			!next.hasFourSpacePrefix() &&
			(reUnorderedList.Match(next) ||
				reOrderedList.Match(next) ||
				reHorizontalRule.Match(next)) {
			return len(paused), 0
		}
	}
	return len(paused), 1
}

type OrderedList struct {
	Starter []byte
}

func (b *OrderedList) Detect(start, second Line) (consume, pause int) {
	m := reOrderedList.FindSubmatch(start)
	if m == nil {
		return 0, 0
	}
	b.Starter = m[1]
	return 0, 1
}
func (b *OrderedList) Continue(paused []Line, next Line) (consume, pause int) {
	if next == nil {
		return len(paused), 0
	}

	if paused[0].isBlank() {
		if next.isBlank() {
			return len(paused), 0
		}
		if !reOrderedList.Match(next) &&
			next.hasNonSpaceInPrefix(len(b.Starter)) {
			return len(paused), 0
		}
	} else {
		if !reOrderedList.Match(next) &&
			next.hasNonSpaceInPrefix(len(b.Starter)) &&
			!next.hasFourSpacePrefix() &&
			(reUnorderedList.Match(next) ||
				reHorizontalRule.Match(next)) {
			return len(paused), 0
		}
	}
	return len(paused), 1
}

type Paragraph struct {
	// NOTE: below fields must be set appropriately when creating a Paragraph
	InQuote bool
	InList  bool
}

func (Paragraph) Detect(start, second Line) (consume, pause int) {
	return 0, 1
}
func (b Paragraph) Continue(paused []Line, next Line) (consume, pause int) {
	if next == nil {
		return len(paused), 0
	}
	// TODO(akavel): support HTML parser & related interactions [#paragraph-line-sequence]
	if paused[0].isBlank() {
		return len(paused), 0
	}
	if !next.hasFourSpacePrefix() {
		if reHorizontalRule.Match(next) ||
			(b.InQuote && bytes.HasPrefix(bytes.TrimLeft(next, " "), []byte(">"))) ||
			(b.InList && reOrderedList.Match(next)) ||
			(b.InList && reUnorderedList.Match(next)) {
			return len(paused), 0
		}
	}
	return len(paused), 1
}

/*
NOTES:

AtxHeader
 -> text-span-sequence

SetextHeader
 -> text-span-sequence

Quote
 -> process lines (strip certain prefix bytes)
  -> detect[defaults..., Paragraph{InQuote=1}]

UnorderedList
 -> detect[UnorderedItem]
  -> process lines (strip certain prefix bytes)
   -> detect[defaults..., Paragraph{InList=1}]

OrderedList
 -> detect[OrderedItem]
  -> process lines (strip certain prefix bytes)
   -> detect[defaults..., Paragraph{InList=1}]

Paragraph
 -> join & trim
  -> process as text-span-sequence

Block{
	block.Detector
	NLines int
	Nested []Block
}

// This function is guaranteed to be run after the line has been reported by
// Detect or Continue as consumed, and after all preceding lines were
// PostProcessed, and before any subsequent lines were PostProcessed.
PostProcess(Line)
*/
<|endoftext|>


<commit_before>package acmewrapper

import (
	"crypto"
	"crypto/tls"
	"os"
	"sync"

	"github.com/xenolf/lego/acme"
)

// AcmeWrapper is the main object which controls tls certificates and their renewals
type AcmeWrapper struct {
	sync.Mutex
	Config Config

	certmutex   sync.RWMutex // certmutex is used to make sure that replacing certificates doesn't asplode
	configmutex sync.Mutex   // configmutex ensures that settings for the ACME stuff don't happen in parallel

	// Our user's private key & registration. Both are needed in order to be able
	// to renew/generate new certs.
	privatekey   crypto.PrivateKey
	registration *acme.RegistrationResource

	// A map of custom certificates associated with special SNIs. The SNI request
	// passes through here
	certs map[string]*tls.Certificate

	// The current TLS cert used for SSL requests when the SNI doesn't match the map
	cert *tls.Certificate

	// The ACME client
	client *acme.Client
}

// GetEmail returns the user email (if any)
// NOTE: NOT threadsafe
func (w *AcmeWrapper) GetEmail() string {
	return w.Config.Email
}

// GetRegistration returns the registration currently being used
// NOTE: NOT threadsafe
func (w *AcmeWrapper) GetRegistration() *acme.RegistrationResource {
	return w.registration
}

// GetPrivateKey returns the private key for the given user.
// NOTE: NOT threadsafe
func (w *AcmeWrapper) GetPrivateKey() crypto.PrivateKey {
	return w.privatekey
}

// GetCertificate returns the current TLS certificate
func (w *AcmeWrapper) GetCertificate() *tls.Certificate {
	w.certmutex.RLock()
	w.certmutex.RUnlock()
	return w.cert
}

// AddSNI adds a domain name and certificate pair to the AcmeWrapper.
// Whenever a request is for the passed domain, its associated certifcate is returned.
func (w *AcmeWrapper) AddSNI(domain string, cert *tls.Certificate) {
	w.certmutex.Lock()
	defer w.certmutex.Unlock()

	w.certs[domain] = cert
}

// RemSNI removes a domain name and certificate pair from the AcmeWrapper. It is assumed that
// they were added using AddSNI.
func (w *AcmeWrapper) RemSNI(domain string) {
	w.certmutex.Lock()
	defer w.certmutex.Unlock()

	delete(w.certs, domain)
}

// TLSConfigGetCertificate is the main function used in the ACME wrapper. This is set in tls.Config to
// the GetCertificate property. Note that Certificates must be empty for it to be called
// correctly, so unless you know what you're doing, just use AcmeWrapper.Get()
func (w *AcmeWrapper) TLSConfigGetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	w.certmutex.RLock()
	defer w.certmutex.RUnlock()

	// If the SNI is in the certs map, return that cert
	if _, ok := w.certs[clientHello.ServerName]; ok {
		return w.certs[clientHello.ServerName], nil
	}

	// Otherwise, return the default cert
	return w.cert, nil
}

// TLSConfig returns a TLS configuration that will automatically work with the golang ssl listener.
// This sets it up so that the server automatically uses a working cert, and updates the cert when
// necessary.
func (w *AcmeWrapper) TLSConfig() *tls.Config {
	return &tls.Config{
		GetCertificate: w.TLSConfigGetCertificate,
	}
}

// AcmeDisabled allows to enable/disable acme-based certificate. Note that it is assumed that
// this function is only called during server runtime (ie, your server is already listening).
// its main purpose is to enable live reload of acme configuration. Do NOT set AcmeDisabled
// in AcmeWrapper.Config, since it will panic.
func (w *AcmeWrapper) AcmeDisabled(set bool) error {
	w.Lock()
	w.Config.AcmeDisabled = set
	w.Unlock()
	if !set && w.client == nil {
		return w.initACME(true)
	}
	return nil
}

// SetNewCert loads a new TLS key/cert from the given files. Running it with the same
// filenames as existing cert will reload them
func (w *AcmeWrapper) SetNewCert(certfile, keyfile string) error {
	cert, err := tls.LoadX509KeyPair(certfile, keyfile)
	if err != nil {
		return err
	}
	w.certmutex.Lock()
	w.cert = &cert
	w.certmutex.Unlock()

	return nil
}

// New generates an AcmeWrapper given a configuration
func New(c Config) (*AcmeWrapper, error) {
	var err error
	// First, set up the default values for any settings that require
	// values
	if c.Server == "" {
		c.Server = DefaultServer
	}
	if c.PrivateKeyType == "" {
		c.PrivateKeyType = DefaultKeyType
	}
	if c.RenewTime == 0 {
		c.RenewTime = DefaultRenewTime
	}
	if c.RetryDelay == 0 {
		c.RetryDelay = DefaultRetryDelay
	}
	if c.Address == "" {
		c.Address = DefaultAddress
	}

	// Now set up the actual wrapper

	var w AcmeWrapper
	w.Config = c
	w.certs = make(map[string]*tls.Certificate)

	// Now load up the key and cert files for TLS if they are set
	if c.TLSKeyFile != "" || c.TLSCertFile != "" {
		err = w.SetNewCert(c.TLSCertFile, c.TLSKeyFile)
		if err != nil {
			if !os.IsNotExist(err) || c.AcmeDisabled {
				// The TLS key and cert file are only
				// allowed to not be there if ACME will generate them
				// TODO: We don't check here if both are missing vs 1 missing
				return nil, err
			}

		}
	}

	// If acme is enabled, initialize it!
	if !c.AcmeDisabled {
		// Initialize the ACME user
		// initUser succeeding initializes:
		//	- w.privatekey
		//	- w.registration
		//	- w.client
		if err = w.initACME(false); err != nil {
			return nil, err
		}
	}

	// Finally, start the background routine
	go backgroundExpirationChecker(&w)

	return &w, nil
}
<commit_msg>Fixed default wait value...<commit_after>package acmewrapper

import (
	"crypto"
	"crypto/tls"
	"os"
	"sync"

	"github.com/xenolf/lego/acme"
)

// AcmeWrapper is the main object which controls tls certificates and their renewals
type AcmeWrapper struct {
	sync.Mutex
	Config Config

	certmutex   sync.RWMutex // certmutex is used to make sure that replacing certificates doesn't asplode
	configmutex sync.Mutex   // configmutex ensures that settings for the ACME stuff don't happen in parallel

	// Our user's private key & registration. Both are needed in order to be able
	// to renew/generate new certs.
	privatekey   crypto.PrivateKey
	registration *acme.RegistrationResource

	// A map of custom certificates associated with special SNIs. The SNI request
	// passes through here
	certs map[string]*tls.Certificate

	// The current TLS cert used for SSL requests when the SNI doesn't match the map
	cert *tls.Certificate

	// The ACME client
	client *acme.Client
}

// GetEmail returns the user email (if any)
// NOTE: NOT threadsafe
func (w *AcmeWrapper) GetEmail() string {
	return w.Config.Email
}

// GetRegistration returns the registration currently being used
// NOTE: NOT threadsafe
func (w *AcmeWrapper) GetRegistration() *acme.RegistrationResource {
	return w.registration
}

// GetPrivateKey returns the private key for the given user.
// NOTE: NOT threadsafe
func (w *AcmeWrapper) GetPrivateKey() crypto.PrivateKey {
	return w.privatekey
}

// GetCertificate returns the current TLS certificate
func (w *AcmeWrapper) GetCertificate() *tls.Certificate {
	w.certmutex.RLock()
	w.certmutex.RUnlock()
	return w.cert
}

// AddSNI adds a domain name and certificate pair to the AcmeWrapper.
// Whenever a request is for the passed domain, its associated certifcate is returned.
func (w *AcmeWrapper) AddSNI(domain string, cert *tls.Certificate) {
	w.certmutex.Lock()
	defer w.certmutex.Unlock()

	w.certs[domain] = cert
}

// RemSNI removes a domain name and certificate pair from the AcmeWrapper. It is assumed that
// they were added using AddSNI.
func (w *AcmeWrapper) RemSNI(domain string) {
	w.certmutex.Lock()
	defer w.certmutex.Unlock()

	delete(w.certs, domain)
}

// TLSConfigGetCertificate is the main function used in the ACME wrapper. This is set in tls.Config to
// the GetCertificate property. Note that Certificates must be empty for it to be called
// correctly, so unless you know what you're doing, just use AcmeWrapper.Get()
func (w *AcmeWrapper) TLSConfigGetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	w.certmutex.RLock()
	defer w.certmutex.RUnlock()

	// If the SNI is in the certs map, return that cert
	if _, ok := w.certs[clientHello.ServerName]; ok {
		return w.certs[clientHello.ServerName], nil
	}

	// Otherwise, return the default cert
	return w.cert, nil
}

// TLSConfig returns a TLS configuration that will automatically work with the golang ssl listener.
// This sets it up so that the server automatically uses a working cert, and updates the cert when
// necessary.
func (w *AcmeWrapper) TLSConfig() *tls.Config {
	return &tls.Config{
		GetCertificate: w.TLSConfigGetCertificate,
	}
}

// AcmeDisabled allows to enable/disable acme-based certificate. Note that it is assumed that
// this function is only called during server runtime (ie, your server is already listening).
// its main purpose is to enable live reload of acme configuration. Do NOT set AcmeDisabled
// in AcmeWrapper.Config, since it will panic.
func (w *AcmeWrapper) AcmeDisabled(set bool) error {
	w.Lock()
	w.Config.AcmeDisabled = set
	w.Unlock()
	if !set && w.client == nil {
		return w.initACME(true)
	}
	return nil
}

// SetNewCert loads a new TLS key/cert from the given files. Running it with the same
// filenames as existing cert will reload them
func (w *AcmeWrapper) SetNewCert(certfile, keyfile string) error {
	cert, err := tls.LoadX509KeyPair(certfile, keyfile)
	if err != nil {
		return err
	}
	w.certmutex.Lock()
	w.cert = &cert
	w.certmutex.Unlock()

	return nil
}

// New generates an AcmeWrapper given a configuration
func New(c Config) (*AcmeWrapper, error) {
	var err error
	// First, set up the default values for any settings that require
	// values
	if c.Server == "" {
		c.Server = DefaultServer
	}
	if c.PrivateKeyType == "" {
		c.PrivateKeyType = DefaultKeyType
	}
	if c.RenewTime == 0 {
		c.RenewTime = DefaultRenewTime
	}
	if c.RetryDelay == 0 {
		c.RetryDelay = DefaultRetryDelay
	}
	if c.RenewCheck == 0 {
		c.RenewCheck = DefaultRenewCheck
	}
	if c.Address == "" {
		c.Address = DefaultAddress
	}

	// Now set up the actual wrapper

	var w AcmeWrapper
	w.Config = c
	w.certs = make(map[string]*tls.Certificate)

	// Now load up the key and cert files for TLS if they are set
	if c.TLSKeyFile != "" || c.TLSCertFile != "" {
		err = w.SetNewCert(c.TLSCertFile, c.TLSKeyFile)
		if err != nil {
			if !os.IsNotExist(err) || c.AcmeDisabled {
				// The TLS key and cert file are only
				// allowed to not be there if ACME will generate them
				// TODO: We don't check here if both are missing vs 1 missing
				return nil, err
			}

		}
	}

	// If acme is enabled, initialize it!
	if !c.AcmeDisabled {
		// Initialize the ACME user
		// initUser succeeding initializes:
		//	- w.privatekey
		//	- w.registration
		//	- w.client
		if err = w.initACME(false); err != nil {
			return nil, err
		}
	}

	// Finally, start the background routine
	go backgroundExpirationChecker(&w)

	return &w, nil
}
<|endoftext|>


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

package version

import (
	"fmt"

	inaming "v.io/x/ref/profiles/internal/naming"

	"v.io/v23/naming"
	"v.io/v23/rpc/version"
	"v.io/v23/verror"
)

// Range represents a range of RPC versions.
type Range struct {
	Min, Max version.RPCVersion
}

var (
	// SupportedRange represents the range of protocol verions supported by this
	// implementation.
	// Max should be incremented whenever we make a protocol
	// change that's not both forward and backward compatible.
	// Min should be incremented whenever we want to remove
	// support for old protocol versions.
	SupportedRange = &Range{Min: version.RPCVersion5, Max: version.RPCVersion7}

	// Export the methods on supportedRange.
	Endpoint           = SupportedRange.Endpoint
	ProxiedEndpoint    = SupportedRange.ProxiedEndpoint
	CommonVersion      = SupportedRange.CommonVersion
	CheckCompatibility = SupportedRange.CheckCompatibility
)

const pkgPath = "v.io/x/ref/profiles/internal/rpc/version"

func reg(id, msg string) verror.IDAction {
	return verror.Register(verror.ID(pkgPath+id), verror.NoRetry, msg)
}

var (
	// These errors are intended to be used as arguments to higher
	// level errors and hence {1}{2} is omitted from their format
	// strings to avoid repeating these n-times in the final error
	// message visible to the user.
	ErrNoCompatibleVersion         = reg(".errNoCompatibleVersionErr", "No compatible RPC version available{:3} not in range {4}..{5}")
	ErrUnknownVersion              = reg(".errUnknownVersionErr", "There was not enough information to determine a version")
	errInternalTypeConversionError = reg(".errInternalTypeConversionError", "failed to convert {3} to v.io/ref/profiles/internal/naming.Endpoint {3}")
)

// IsVersionError returns true if err is a versioning related error.
func IsVersionError(err error) bool {
	id := verror.ErrorID(err)
	return id == ErrNoCompatibleVersion.ID || id == ErrUnknownVersion.ID
}

// Endpoint returns an endpoint with the Min/MaxRPCVersion properly filled in
// to match this implementations supported protocol versions.
func (r *Range) Endpoint(protocol, address string, rid naming.RoutingID) *inaming.Endpoint {
	return &inaming.Endpoint{
		Protocol:      protocol,
		Address:       address,
		RID:           rid,
		MinRPCVersion: r.Min,
		MaxRPCVersion: r.Max,
	}
}

// intersectRanges finds the intersection between ranges
// supported by two endpoints.  We make an assumption here that if one
// of the endpoints has an UnknownVersion we assume it has the same
// extent as the other endpoint. If both endpoints have Unknown for a
// version number, an error is produced.
// For example:
//   a == (2, 4) and b == (Unknown, Unknown), intersect(a,b) == (2, 4)
//   a == (2, Unknown) and b == (3, 4), intersect(a,b) == (3, 4)
func intersectRanges(amin, amax, bmin, bmax version.RPCVersion) (min, max version.RPCVersion, err error) {
	u := version.UnknownRPCVersion

	min = amin
	if min == u || (bmin != u && bmin > min) {
		min = bmin
	}
	max = amax
	if max == u || (bmax != u && bmax < max) {
		max = bmax
	}

	if min == u || max == u {
		err = verror.New(ErrUnknownVersion, nil)
	} else if min > max {
		err = verror.New(ErrNoCompatibleVersion, nil, u, min, max)
	}
	return
}

func intersectEndpoints(a, b *inaming.Endpoint) (min, max version.RPCVersion, err error) {
	return intersectRanges(a.MinRPCVersion, a.MaxRPCVersion, b.MinRPCVersion, b.MaxRPCVersion)
}

func (r1 *Range) Intersect(r2 *Range) (*Range, error) {
	min, max, err := intersectRanges(r1.Min, r1.Max, r2.Min, r2.Max)
	if err != nil {
		return nil, err
	}
	r := &Range{Min: min, Max: max}
	return r, nil
}

// ProxiedEndpoint returns an endpoint with the Min/MaxRPCVersion properly filled in
// to match the intersection of capabilities of this process and the proxy.
func (r *Range) ProxiedEndpoint(rid naming.RoutingID, proxy naming.Endpoint) (*inaming.Endpoint, error) {
	proxyEP, ok := proxy.(*inaming.Endpoint)
	if !ok {
		return nil, verror.New(errInternalTypeConversionError, nil, fmt.Sprintf("%T", proxy))
	}

	ep := &inaming.Endpoint{
		Protocol:      proxyEP.Protocol,
		Address:       proxyEP.Address,
		RID:           rid,
		MinRPCVersion: r.Min,
		MaxRPCVersion: r.Max,
	}

	// This is the endpoint we are going to advertise.  It should only claim to support versions in
	// the intersection of those we support and those the proxy supports.
	var err error
	ep.MinRPCVersion, ep.MaxRPCVersion, err = intersectEndpoints(ep, proxyEP)
	if err != nil {
		return nil, fmt.Errorf("attempting to register with incompatible proxy: %s", proxy)
	}
	return ep, nil
}

// CommonVersion determines which version of the RPC protocol should be used
// between two endpoints.  Returns an error if the resulting version is incompatible
// with this RPC implementation.
func (r *Range) CommonVersion(a, b naming.Endpoint) (version.RPCVersion, error) {
	aEP, ok := a.(*inaming.Endpoint)
	if !ok {
		return 0, verror.New(errInternalTypeConversionError, nil, fmt.Sprintf("%T", a))
	}
	bEP, ok := b.(*inaming.Endpoint)
	if !ok {
		return 0, verror.New(errInternalTypeConversionError, nil, fmt.Sprintf("%T", b))
	}

	_, max, err := intersectEndpoints(aEP, bEP)
	if err != nil {
		return 0, err
	}

	// We want to use the maximum common version of the protocol.  We just
	// need to make sure that it is supported by this RPC implementation.
	if max < r.Min || max > r.Max {
		return version.UnknownRPCVersion, verror.New(ErrNoCompatibleVersion, nil, max, r.Min, r.Max)
	}
	return max, nil
}

// CheckCompatibility returns an error if the given endpoint is incompatible
// with this RPC implementation.  It returns nil otherwise.
func (r *Range) CheckCompatibility(remote naming.Endpoint) error {
	remoteEP, ok := remote.(*inaming.Endpoint)
	if !ok {
		return verror.New(errInternalTypeConversionError, nil, fmt.Sprintf("%T", remote))
	}

	_, _, err := intersectRanges(r.Min, r.Max,
		remoteEP.MinRPCVersion, remoteEP.MaxRPCVersion)

	return err
}
<commit_msg>rpc/version: update the supported version number to 8<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package version

import (
	"fmt"

	inaming "v.io/x/ref/profiles/internal/naming"

	"v.io/v23/naming"
	"v.io/v23/rpc/version"
	"v.io/v23/verror"
)

// Range represents a range of RPC versions.
type Range struct {
	Min, Max version.RPCVersion
}

var (
	// SupportedRange represents the range of protocol verions supported by this
	// implementation.
	// Max should be incremented whenever we make a protocol
	// change that's not both forward and backward compatible.
	// Min should be incremented whenever we want to remove
	// support for old protocol versions.
	SupportedRange = &Range{Min: version.RPCVersion5, Max: version.RPCVersion8}

	// Export the methods on supportedRange.
	Endpoint           = SupportedRange.Endpoint
	ProxiedEndpoint    = SupportedRange.ProxiedEndpoint
	CommonVersion      = SupportedRange.CommonVersion
	CheckCompatibility = SupportedRange.CheckCompatibility
)

const pkgPath = "v.io/x/ref/profiles/internal/rpc/version"

func reg(id, msg string) verror.IDAction {
	return verror.Register(verror.ID(pkgPath+id), verror.NoRetry, msg)
}

var (
	// These errors are intended to be used as arguments to higher
	// level errors and hence {1}{2} is omitted from their format
	// strings to avoid repeating these n-times in the final error
	// message visible to the user.
	ErrNoCompatibleVersion         = reg(".errNoCompatibleVersionErr", "No compatible RPC version available{:3} not in range {4}..{5}")
	ErrUnknownVersion              = reg(".errUnknownVersionErr", "There was not enough information to determine a version")
	errInternalTypeConversionError = reg(".errInternalTypeConversionError", "failed to convert {3} to v.io/ref/profiles/internal/naming.Endpoint {3}")
)

// IsVersionError returns true if err is a versioning related error.
func IsVersionError(err error) bool {
	id := verror.ErrorID(err)
	return id == ErrNoCompatibleVersion.ID || id == ErrUnknownVersion.ID
}

// Endpoint returns an endpoint with the Min/MaxRPCVersion properly filled in
// to match this implementations supported protocol versions.
func (r *Range) Endpoint(protocol, address string, rid naming.RoutingID) *inaming.Endpoint {
	return &inaming.Endpoint{
		Protocol:      protocol,
		Address:       address,
		RID:           rid,
		MinRPCVersion: r.Min,
		MaxRPCVersion: r.Max,
	}
}

// intersectRanges finds the intersection between ranges
// supported by two endpoints.  We make an assumption here that if one
// of the endpoints has an UnknownVersion we assume it has the same
// extent as the other endpoint. If both endpoints have Unknown for a
// version number, an error is produced.
// For example:
//   a == (2, 4) and b == (Unknown, Unknown), intersect(a,b) == (2, 4)
//   a == (2, Unknown) and b == (3, 4), intersect(a,b) == (3, 4)
func intersectRanges(amin, amax, bmin, bmax version.RPCVersion) (min, max version.RPCVersion, err error) {
	u := version.UnknownRPCVersion

	min = amin
	if min == u || (bmin != u && bmin > min) {
		min = bmin
	}
	max = amax
	if max == u || (bmax != u && bmax < max) {
		max = bmax
	}

	if min == u || max == u {
		err = verror.New(ErrUnknownVersion, nil)
	} else if min > max {
		err = verror.New(ErrNoCompatibleVersion, nil, u, min, max)
	}
	return
}

func intersectEndpoints(a, b *inaming.Endpoint) (min, max version.RPCVersion, err error) {
	return intersectRanges(a.MinRPCVersion, a.MaxRPCVersion, b.MinRPCVersion, b.MaxRPCVersion)
}

func (r1 *Range) Intersect(r2 *Range) (*Range, error) {
	min, max, err := intersectRanges(r1.Min, r1.Max, r2.Min, r2.Max)
	if err != nil {
		return nil, err
	}
	r := &Range{Min: min, Max: max}
	return r, nil
}

// ProxiedEndpoint returns an endpoint with the Min/MaxRPCVersion properly filled in
// to match the intersection of capabilities of this process and the proxy.
func (r *Range) ProxiedEndpoint(rid naming.RoutingID, proxy naming.Endpoint) (*inaming.Endpoint, error) {
	proxyEP, ok := proxy.(*inaming.Endpoint)
	if !ok {
		return nil, verror.New(errInternalTypeConversionError, nil, fmt.Sprintf("%T", proxy))
	}

	ep := &inaming.Endpoint{
		Protocol:      proxyEP.Protocol,
		Address:       proxyEP.Address,
		RID:           rid,
		MinRPCVersion: r.Min,
		MaxRPCVersion: r.Max,
	}

	// This is the endpoint we are going to advertise.  It should only claim to support versions in
	// the intersection of those we support and those the proxy supports.
	var err error
	ep.MinRPCVersion, ep.MaxRPCVersion, err = intersectEndpoints(ep, proxyEP)
	if err != nil {
		return nil, fmt.Errorf("attempting to register with incompatible proxy: %s", proxy)
	}
	return ep, nil
}

// CommonVersion determines which version of the RPC protocol should be used
// between two endpoints.  Returns an error if the resulting version is incompatible
// with this RPC implementation.
func (r *Range) CommonVersion(a, b naming.Endpoint) (version.RPCVersion, error) {
	aEP, ok := a.(*inaming.Endpoint)
	if !ok {
		return 0, verror.New(errInternalTypeConversionError, nil, fmt.Sprintf("%T", a))
	}
	bEP, ok := b.(*inaming.Endpoint)
	if !ok {
		return 0, verror.New(errInternalTypeConversionError, nil, fmt.Sprintf("%T", b))
	}

	_, max, err := intersectEndpoints(aEP, bEP)
	if err != nil {
		return 0, err
	}

	// We want to use the maximum common version of the protocol.  We just
	// need to make sure that it is supported by this RPC implementation.
	if max < r.Min || max > r.Max {
		return version.UnknownRPCVersion, verror.New(ErrNoCompatibleVersion, nil, max, r.Min, r.Max)
	}
	return max, nil
}

// CheckCompatibility returns an error if the given endpoint is incompatible
// with this RPC implementation.  It returns nil otherwise.
func (r *Range) CheckCompatibility(remote naming.Endpoint) error {
	remoteEP, ok := remote.(*inaming.Endpoint)
	if !ok {
		return verror.New(errInternalTypeConversionError, nil, fmt.Sprintf("%T", remote))
	}

	_, _, err := intersectRanges(r.Min, r.Max,
		remoteEP.MinRPCVersion, remoteEP.MaxRPCVersion)

	return err
}
<|endoftext|>


<commit_before>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// A helper for testing commands
// Author: bratseth

package cmd

import (
    "bytes"
    "github.com/vespa-engine/vespa/utils"
    "github.com/stretchr/testify/assert"
	"io/ioutil"
    "net/http"
    "testing"
    "time"
)

func executeCommand(t *testing.T, client *mockHttpClient, args []string, moreArgs []string) (standardout string) {
    utils.ActiveHttpClient = client

    // Reset - persistent flags in Cobra persists over tests
	rootCmd.SetArgs([]string{"status", "-t", ""})
	rootCmd.Execute()

	b := bytes.NewBufferString("")
    utils.Out = b
	rootCmd.SetArgs(concat(args, moreArgs))
	rootCmd.Execute()
	out, err := ioutil.ReadAll(b)
	assert.Empty(t, err, "No error")
	return string(out)
}

type mockHttpClient struct {
    // The HTTP status code that will be returned from the next invocation. Default: 200
    nextStatus int

    // The response body code that will be returned from the next invocation. Default: ""
    nextBody string

    // A recording of the last HTTP request made through this
    lastRequest *http.Request
}

func (c *mockHttpClient) Do(request *http.Request, timeout time.Duration) (response *http.Response, error error) {
    if c.nextStatus == 0 {
        c.nextStatus = 200
    }
    c.lastRequest = request
    return &http.Response{
        StatusCode: c.nextStatus,
        Body:       ioutil.NopCloser(bytes.NewBufferString(c.nextBody)),
        Header:     make(http.Header),
    },
    nil
}

func concat(array1 []string, array2 []string) []string {
    result := make([]string, len(array1) + len(array2))
    for i := 0; i < len(array1); i++ {
        result[i] = array1[i]
    }
    for i := 0; i < len(array2); i++ {
        result[i + len(array1)] = array2[i]
    }
    return result
}

<commit_msg>Simplify<commit_after>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// A helper for testing commands
// Author: bratseth

package cmd

import (
    "bytes"
    "github.com/vespa-engine/vespa/utils"
    "github.com/stretchr/testify/assert"
	"io/ioutil"
    "net/http"
    "testing"
    "time"
)

func executeCommand(t *testing.T, client *mockHttpClient, args []string, moreArgs []string) (standardout string) {
    utils.ActiveHttpClient = client

    // Reset - persistent flags in Cobra persists over tests
	rootCmd.SetArgs([]string{"status", "-t", ""})
	rootCmd.Execute()

	b := bytes.NewBufferString("")
    utils.Out = b
	rootCmd.SetArgs(append(args, moreArgs...))
	rootCmd.Execute()
	out, err := ioutil.ReadAll(b)
	assert.Empty(t, err, "No error")
	return string(out)
}

type mockHttpClient struct {
    // The HTTP status code that will be returned from the next invocation. Default: 200
    nextStatus int

    // The response body code that will be returned from the next invocation. Default: ""
    nextBody string

    // A recording of the last HTTP request made through this
    lastRequest *http.Request
}

func (c *mockHttpClient) Do(request *http.Request, timeout time.Duration) (response *http.Response, error error) {
    if c.nextStatus == 0 {
        c.nextStatus = 200
    }
    c.lastRequest = request
    return &http.Response{
        StatusCode: c.nextStatus,
        Body:       ioutil.NopCloser(bytes.NewBufferString(c.nextBody)),
        Header:     make(http.Header),
    },
    nil
}
<|endoftext|>


<commit_before>package blockdiag

import (
	"sort"
	"strings"
	"testing"
)

func TestShouldParser(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
		nodes       []string
		edges       []string
		attributes  map[string]string
	}{
		{
			"Empty diagram",
			`
blockdiag {}
`,
			[]string{},
			[]string{},
			map[string]string{},
		},
		{
			"Single Node",
			`
blockdiag {
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
		{
			// TODO Add test case for node chain without tailing ;
			"Node chain",
			`
blockdiag {
	A -> B;
}
`,
			[]string{"A", "B"},
			[]string{"A|B"},
			map[string]string{},
		},
		{
			"Multiple chains, using same nodes",
			`
blockdiag {
	A -> B -> C;
	A -> D;
}
`,
			[]string{"A", "B", "C", "D"},
			[]string{"A|B", "A|D", "B|C"},
			map[string]string{},
		},
		{
			"Self reference",
			`
blockdiag {
	A -> A;
}
`,
			[]string{"A"},
			[]string{"A|A"},
			map[string]string{},
		},
		{
			"Comment",
			`
# Comment
blockdiag # Comment
{
# Comment
	A; # Comment
# Comment
} # Comment
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
		{
			"Multi Char Node Names",
			`
blockdiag
{
	MultiCharNodeName1;
}
`,
			[]string{"MultiCharNodeName1"},
			[]string{},
			map[string]string{},
		},
		{
			"Digramm Attributes",
			`
blockdiag
{
	node_width = 128;
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{
				"node_width": "128",
			},
		},
		{
			"Digramm type 'diagram'",
			`
diagram
{
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
	} {
		got, err := ParseReader("shouldparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("%s: parse error: %s with input %s", test.description, err, test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("%s: assertion error: %s should parse to diag", test.description, test.input)
		}
		if gotDiag.NodesString() != strings.Join(test.nodes, ", ") {
			t.Fatalf("%s: nodes error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.nodes, ", "), gotDiag.NodesString())
		}
		if gotDiag.EdgesString() != strings.Join(test.edges, ", ") {
			t.Fatalf("%s edges error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.edges, ", "), gotDiag.EdgesString())
		}

		var attributes []string
		for key, value := range test.attributes {
			attributes = append(attributes, key+"="+value)
		}
		sort.Strings(attributes)
		if gotDiag.AttributesString() != strings.Join(attributes, "\n") {
			t.Fatalf("%s attributes error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(attributes, "\n"), gotDiag.AttributesString())
		}
	}
}

func TestShouldNotParse(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
	}{
		{
			"No block",
			`
blockdiag
`,
		},
	} {
		_, err := ParseReader("shouldnotparse.diag", strings.NewReader(test.input))
		if err == nil {
			t.Fatalf("%s: should not parse, but didn't give an error with input %s", test.description, test.input)
		}
	}
}

func TestCircular(t *testing.T) {
	for _, test := range []struct {
		input    string
		circular bool
	}{
		{
			`
blockdiag{
	A;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> B -> C;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> A;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> B -> C -> A;
}
`,
			true,
		},
	} {
		got, err := ParseReader("shouldnotparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		if gotDiag.FindCircular() != test.circular {
			t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		}
	}
}

func TestGetStartNodes(t *testing.T) {
	for _, test := range []struct {
		input      string
		startNodes []string
	}{
		{
			`
blockdiag{
	A -> B -> C;
}
`,
			[]string{"A"},
		},
		{
			`
blockdiag {
	A -> B -> C;
	D;
	E -> F;
}
`,
			[]string{"A", "D", "E"},
		},
		{
			`
blockdiag {
	D;
	E -> F;
	A -> B -> C;
}
`,
			[]string{"A", "D", "E"},
		},
	} {
		got, err := ParseReader("placeingrid.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		// if gotDiag.PlaceInGrid() != test.circular {
		// 	t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		// }
		startNodes := gotDiag.getStartNodes()
		if len(startNodes) != len(test.startNodes) {
			t.Fatalf("Start Nodes count wrong, expected: %s, got: %s", strings.Join(test.startNodes, ", "), startNodes)
		}
		sort.Strings(test.startNodes)
		for i, n := range startNodes {
			if n.Name != test.startNodes[i] {
				t.Fatalf("Start Nodes do not match, expected: %s, got: %s", strings.Join(test.startNodes, ", "), startNodes)
			}
		}
	}
}

func TestPlaceInGrid(t *testing.T) {
	for _, test := range []struct {
		input  string
		output string
	}{
		{
			`
blockdiag{
	A -> B -> C;
}
`, `[A] [B] [C]
`,
		},
		{
			`
blockdiag{
	A -> B -> C;
	B -> D;
	A -> E -> C;
}
`, `[A] [B] [C]
        [D]
    [E]
`,
		},
		{
			`
blockdiag{
	A -> B -> C -> B; # Circular with proper Start-Node
}
`, `[A] [B] [C]
`,
		},
		{
			`
blockdiag{
	A -> B -> C -> A; # Circular without Start-Node
}
`, `[A] [B] [C]
`,
		},
		{
			`
blockdiag{
	A; B; C; D; E; F; G; H; I; J; K; # 11 Rows
}
`, `[A]
[B]
[C]
[D]
[E]
[F]
[G]
[H]
[I]
[J]
[K]
`,
		}, {
			`
blockdiag{
	A -> B -> C -> D -> E -> F -> G -> H -> I -> J -> K; # 11 Cols
}
`, `[A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K]
`,
		}, {
			`
blockdiag {
	A -> B -> D;
	A -> C -> D;
}
`, `[A] [B] [D]
    [C]
`,
		}, {
			`
blockdiag {
	A -> B;
	A -> C -> B;
}
`, `[A]     [B]
    [C]
`,
		}, {
			`
blockdiag {
	A -> B;
	A -> C -> D -> B;
}
`, `[A]         [B]
    [C] [D]
`,
		},
	} {
		got, err := ParseReader("placeingrid.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		gotDiag.PlaceInGrid()
		if gotDiag.GridString() != test.output {
			t.Fatalf("expected: \n%s, got: \n%s", strings.Replace(test.output, " ", "\u00B7", -1), strings.Replace(gotDiag.GridString(), " ", "\u00B7", -1))
		}
	}
}

func TestDiagString(t *testing.T) {
	for _, test := range []struct {
		input  string
		output string
	}{
		{
			`
blockdiag{
	# One node, no connections
	A;
}
`, `
[A]
`,
		},
		{
			`
blockdiag{
	# Two nodes, no connections
	A;
	B;
}
`, `
[A]

[B]
`,
		},
		{
			`
blockdiag{
	# Two connected nodes
	A -> B;
}
`, `
[A]───>[B]
`,
		},
		{
			`
blockdiag{
	# Two seperate streams
	A -> B;
	C -> D;
}
`, `
[A]───>[B]

[C]───>[D]
`,
		},
		{
			`
blockdiag{
	# From one node to two nodes
	A -> B;
	A -> C;
}
`, `
[A]─┬─>[B]
    │
    └─>[C]
`,
		},
		{
			`
blockdiag{
	# From one node to three nodes
	A -> B;
	A -> C;
	A -> D;
}
`, `
[A]─┬─>[B]
    │
    ├─>[C]
    │
    └─>[D]
`,
		},
		{
			`
blockdiag{
	# Branch and merge
	A -> B -> D;
	A -> C -> D;
}
`, `
[A]─┬─>[B]─┬─>[D]
    │      │
    └─>[C]─┘
`,
		},
		{
			`
blockdiag{
	# Branch and merge two cols
	A -> B -> C -> E;
	A -> D -> E;
}
`, `
[A]─┬─>[B]───>[C]─┬─>[E]
    │             │
    └─>[D]────────┘
`,
		},
		{
			`
blockdiag {
	# Branch and merge two cols (Variant 2)
	A -> B;
	A -> C -> B;
}
`, `
[A]─┬──────┬─>[B]
    │      │
    └─>[C]─┘
`,
		},
		{
			`
blockdiag {
	# Branch and merge three cols (Variant 2)
	A -> B;
	A -> C -> D -> B;
}
`, `
[A]─┬─────────────┬─>[B]
    │             │
    └─>[C]───>[D]─┘
`,
		},
		{
			`
blockdiag {
	# Branch and merge two cols with alternative way
	A -> B -> C -> D;
	A -> E -> D;
	E -> F;
}
`, `
[A]─┬─>[B]───>[C]─┬─>[D]
    │      ┌──────┘
    └─>[E]─┴─>[F]
`,
		},
		{
			`
blockdiag {
	# Branch and merge two rows with two alternative ways
	A -> B -> C -> D;
	A -> E -> D;
	E -> F;
	E -> G;
}
`, `
[A]─┬─>[B]───>[C]─┬─>[D]
    │      ┌──────┘
    └─>[E]─┼─>[F]
           │
           └─>[G]
`,
		},
		{
			`
blockdiag{
	# Branch and merge over two rows
	A -> B -> E;
	A -> C;
	A -> D -> E;
}
`, `
[A]─┬─>[B]─┬─>[E]
    │      │
    ├─>[C] │
    │      │
    └─>[D]─┘
`,
		},
	} {
		got, err := ParseReader("diagstring.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		gotDiag.PlaceInGrid()
		if gotDiag.String() != test.output {
			t.Fatalf("for: \n%s\nexpected: \n%s\ngot: \n%s", test.input, strings.Replace(test.output, " ", "\u00B7", -1), strings.Replace(gotDiag.String(), " ", "\u00B7", -1))
		}
	}
}
<commit_msg>Cleanup tests<commit_after>package blockdiag

import (
	"sort"
	"strings"
	"testing"
)

func TestShouldParse(t *testing.T) {
	for _, test := range []struct {
		input      string
		nodes      []string
		edges      []string
		attributes map[string]string
	}{
		{
			`
# Empty diagram
blockdiag {}
`,
			[]string{},
			[]string{},
			map[string]string{},
		},
		{
			`
# Single Node
blockdiag {
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
		{
			// TODO Add test case for node chain without tailing ;
			`
# Node chain
blockdiag {
	A -> B;
}
`,
			[]string{"A", "B"},
			[]string{"A|B"},
			map[string]string{},
		},
		{
			`
# Multiple chains, using same nodes
blockdiag {
	A -> B -> C;
	A -> D;
}
`,
			[]string{"A", "B", "C", "D"},
			[]string{"A|B", "A|D", "B|C"},
			map[string]string{},
		},
		{
			`
# Self reference
blockdiag {
	A -> A;
}
`,
			[]string{"A"},
			[]string{"A|A"},
			map[string]string{},
		},
		{
			`
# Comment
blockdiag # Comment
{
# Comment
	A; # Comment
# Comment
} # Comment
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
		{
			`
# Multi Char Node Names
blockdiag
{
	MultiCharNodeName1;
}
`,
			[]string{"MultiCharNodeName1"},
			[]string{},
			map[string]string{},
		},
		{
			`
# Diagram Attributes
blockdiag
{
	node_width = 128;
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{
				"node_width": "128",
			},
		},
		{
			`
# Digram type 'diagram'
diagram
{
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
	} {
		got, err := ParseReader("shouldparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should parse, but did give an error: %s with input %s", err, test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		if gotDiag.NodesString() != strings.Join(test.nodes, ", ") {
			t.Fatalf("nodes error: %s, expected '%s', got: '%s'", test.input, strings.Join(test.nodes, ", "), gotDiag.NodesString())
		}
		if gotDiag.EdgesString() != strings.Join(test.edges, ", ") {
			t.Fatalf("edges error: %s, expected '%s', got: '%s'", test.input, strings.Join(test.edges, ", "), gotDiag.EdgesString())
		}

		var attributes []string
		for key, value := range test.attributes {
			attributes = append(attributes, key+"="+value)
		}
		sort.Strings(attributes)
		if gotDiag.AttributesString() != strings.Join(attributes, "\n") {
			t.Fatalf("attributes error: %s, expected '%s', got: '%s'", test.input, strings.Join(attributes, "\n"), gotDiag.AttributesString())
		}
	}
}

func TestShouldNotParse(t *testing.T) {
	for _, test := range []struct {
		input string
	}{
		{
			`
# No block
blockdiag
`,
		},
	} {
		_, err := ParseReader("shouldnotparse.diag", strings.NewReader(test.input))
		if err == nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
	}
}

func TestCircular(t *testing.T) {
	for _, test := range []struct {
		input    string
		circular bool
	}{
		{
			`
# Single node, not circular
blockdiag{
	A;
}
`,
			false,
		},
		{
			`
# Three steps straight, not circular
blockdiag{
	A -> B -> C;
}
`,
			false,
		},
		{
			`
# Self reference, not circular
blockdiag{
	A -> A;
}
`,
			false,
		},
		{
			`
# Three nodes, circular
blockdiag{
	A -> B -> C -> A;
}
`,
			true,
		},
	} {
		got, err := ParseReader("circular.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should parse, but did give an error: %s with input %s", err, test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		if gotDiag.FindCircular() != test.circular {
			t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		}
	}
}

func TestGetStartNodes(t *testing.T) {
	for _, test := range []struct {
		input      string
		startNodes []string
	}{
		{
			`
# Three nodes straight
blockdiag{
	A -> B -> C;
}
`,
			[]string{"A"},
		},
		{
			`
# Multiple disjunct process lines
blockdiag {
	A -> B -> C;
	D;
	E -> F;
}
`,
			[]string{"A", "D", "E"},
		},
		{
			`
# Multiple disjunct process lines 2
blockdiag {
	D;
	E -> F;
	A -> B -> C;
}
`,
			[]string{"A", "D", "E"},
		},
	} {
		got, err := ParseReader("placeingrid.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should parse, but did give an error: %s with input %s", err, test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		startNodes := gotDiag.getStartNodes()
		if len(startNodes) != len(test.startNodes) {
			t.Fatalf("Start Nodes count wrong, expected: %s, got: %s", strings.Join(test.startNodes, ", "), startNodes)
		}
		sort.Strings(test.startNodes)
		for i, n := range startNodes {
			if n.Name != test.startNodes[i] {
				t.Fatalf("Start Nodes do not match, expected: %s, got: %s", strings.Join(test.startNodes, ", "), startNodes)
			}
		}
	}
}

func TestPlaceInGrid(t *testing.T) {
	for _, test := range []struct {
		input  string
		output string
	}{
		{
			`
blockdiag{
	A -> B -> C;
}
`, `[A] [B] [C]
`,
		},
		{
			`
blockdiag{
	A -> B -> C;
	B -> D;
	A -> E -> C;
}
`, `[A] [B] [C]
        [D]
    [E]
`,
		},
		{
			`
blockdiag{
	A -> B -> C -> B; # Circular with proper Start-Node
}
`, `[A] [B] [C]
`,
		},
		{
			`
blockdiag{
	A -> B -> C -> A; # Circular without Start-Node
}
`, `[A] [B] [C]
`,
		},
		{
			`
blockdiag{
	A; B; C; D; E; F; G; H; I; J; K; # 11 Rows
}
`, `[A]
[B]
[C]
[D]
[E]
[F]
[G]
[H]
[I]
[J]
[K]
`,
		}, {
			`
blockdiag{
	A -> B -> C -> D -> E -> F -> G -> H -> I -> J -> K; # 11 Cols
}
`, `[A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K]
`,
		}, {
			`
blockdiag {
	A -> B -> D;
	A -> C -> D;
}
`, `[A] [B] [D]
    [C]
`,
		}, {
			`
blockdiag {
	A -> B;
	A -> C -> B;
}
`, `[A]     [B]
    [C]
`,
		}, {
			`
blockdiag {
	A -> B;
	A -> C -> D -> B;
}
`, `[A]         [B]
    [C] [D]
`,
		},
	} {
		got, err := ParseReader("placeingrid.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should parse, but did give an error: %s with input %s", err, test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		gotDiag.PlaceInGrid()
		if gotDiag.GridString() != test.output {
			t.Fatalf("expected: \n%s, got: \n%s", strings.Replace(test.output, " ", "\u00B7", -1), strings.Replace(gotDiag.GridString(), " ", "\u00B7", -1))
		}
	}
}

func TestDiagString(t *testing.T) {
	for _, test := range []struct {
		input  string
		output string
	}{
		{
			`
blockdiag{
	# One node, no connections
	A;
}
`, `
[A]
`,
		},
		{
			`
blockdiag{
	# Two nodes, no connections
	A;
	B;
}
`, `
[A]

[B]
`,
		},
		{
			`
blockdiag{
	# Two connected nodes
	A -> B;
}
`, `
[A]───>[B]
`,
		},
		{
			`
blockdiag{
	# Two seperate streams
	A -> B;
	C -> D;
}
`, `
[A]───>[B]

[C]───>[D]
`,
		},
		{
			`
blockdiag{
	# From one node to two nodes
	A -> B;
	A -> C;
}
`, `
[A]─┬─>[B]
    │
    └─>[C]
`,
		},
		{
			`
blockdiag{
	# From one node to three nodes
	A -> B;
	A -> C;
	A -> D;
}
`, `
[A]─┬─>[B]
    │
    ├─>[C]
    │
    └─>[D]
`,
		},
		{
			`
blockdiag{
	# Branch and merge
	A -> B -> D;
	A -> C -> D;
}
`, `
[A]─┬─>[B]─┬─>[D]
    │      │
    └─>[C]─┘
`,
		},
		{
			`
blockdiag{
	# Branch and merge two cols
	A -> B -> C -> E;
	A -> D -> E;
}
`, `
[A]─┬─>[B]───>[C]─┬─>[E]
    │             │
    └─>[D]────────┘
`,
		},
		{
			`
blockdiag {
	# Branch and merge two cols (Variant 2)
	A -> B;
	A -> C -> B;
}
`, `
[A]─┬──────┬─>[B]
    │      │
    └─>[C]─┘
`,
		},
		{
			`
blockdiag {
	# Branch and merge three cols (Variant 2)
	A -> B;
	A -> C -> D -> B;
}
`, `
[A]─┬─────────────┬─>[B]
    │             │
    └─>[C]───>[D]─┘
`,
		},
		{
			`
blockdiag {
	# Branch and merge two cols with alternative way
	A -> B -> C -> D;
	A -> E -> D;
	E -> F;
}
`, `
[A]─┬─>[B]───>[C]─┬─>[D]
    │      ┌──────┘
    └─>[E]─┴─>[F]
`,
		},
		{
			`
blockdiag {
	# Branch and merge two rows with two alternative ways
	A -> B -> C -> D;
	A -> E -> D;
	E -> F;
	E -> G;
}
`, `
[A]─┬─>[B]───>[C]─┬─>[D]
    │      ┌──────┘
    └─>[E]─┼─>[F]
           │
           └─>[G]
`,
		},
		{
			`
blockdiag{
	# Branch and merge over two rows
	A -> B -> E;
	A -> C;
	A -> D -> E;
}
`, `
[A]─┬─>[B]─┬─>[E]
    │      │
    ├─>[C] │
    │      │
    └─>[D]─┘
`,
		},
	} {
		got, err := ParseReader("diagstring.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should parse, but did give an error: %s with input %s", err, test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		gotDiag.PlaceInGrid()
		if gotDiag.String() != test.output {
			t.Fatalf("for: \n%s\nexpected: \n%s\ngot: \n%s", test.input, strings.Replace(test.output, " ", "\u00B7", -1), strings.Replace(gotDiag.String(), " ", "\u00B7", -1))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"crypto/rsa"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	yaml "gopkg.in/yaml.v2"

	"golang.org/x/crypto/ssh"

	"github.com/cloudfoundry/bosh-bootloader/application"
	"github.com/cloudfoundry/bosh-bootloader/aws"
	"github.com/cloudfoundry/bosh-bootloader/aws/clientmanager"
	"github.com/cloudfoundry/bosh-bootloader/aws/cloudformation"
	"github.com/cloudfoundry/bosh-bootloader/aws/cloudformation/templates"
	"github.com/cloudfoundry/bosh-bootloader/aws/ec2"
	"github.com/cloudfoundry/bosh-bootloader/aws/iam"
	"github.com/cloudfoundry/bosh-bootloader/bosh"
	"github.com/cloudfoundry/bosh-bootloader/cloudconfig"
	"github.com/cloudfoundry/bosh-bootloader/commands"
	"github.com/cloudfoundry/bosh-bootloader/gcp"
	"github.com/cloudfoundry/bosh-bootloader/helpers"
	"github.com/cloudfoundry/bosh-bootloader/storage"
	"github.com/cloudfoundry/bosh-bootloader/terraform"

	awscloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/aws"
	gcpcloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/gcp"
	gcpterraform "github.com/cloudfoundry/bosh-bootloader/terraform/gcp"
)

var (
	Version     string
	gcpBasePath string
)

func main() {
	// Command Set
	commandSet := application.CommandSet{
		commands.HelpCommand:               nil,
		commands.VersionCommand:            nil,
		commands.UpCommand:                 nil,
		commands.DestroyCommand:            nil,
		commands.DirectorAddressCommand:    nil,
		commands.DirectorUsernameCommand:   nil,
		commands.DirectorPasswordCommand:   nil,
		commands.DirectorCACertCommand:     nil,
		commands.SSHKeyCommand:             nil,
		commands.CreateLBsCommand:          nil,
		commands.UpdateLBsCommand:          nil,
		commands.DeleteLBsCommand:          nil,
		commands.LBsCommand:                nil,
		commands.EnvIDCommand:              nil,
		commands.PrintEnvCommand:           nil,
		commands.CloudConfigCommand:        nil,
		commands.BOSHDeploymentVarsCommand: nil,
	}

	// Utilities
	uuidGenerator := helpers.NewUUIDGenerator(rand.Reader)
	stringGenerator := helpers.NewStringGenerator(rand.Reader)
	envIDGenerator := helpers.NewEnvIDGenerator(rand.Reader)
	logger := application.NewLogger(os.Stdout)
	stderrLogger := application.NewLogger(os.Stderr)

	// Usage Command
	usage := commands.NewUsage(os.Stdout)
	storage.GetStateLogger = stderrLogger

	commandLineParser := application.NewCommandLineParser(usage.Print, commandSet)
	configurationParser := application.NewConfigurationParser(commandLineParser)
	configuration, err := configurationParser.Parse(os.Args[1:])
	if err != nil {
		fail(err)
	}

	stateStore := storage.NewStore(configuration.Global.StateDir)
	stateValidator := application.NewStateValidator(configuration.Global.StateDir)

	// Amazon
	awsConfiguration := aws.Config{
		AccessKeyID:      configuration.State.AWS.AccessKeyID,
		SecretAccessKey:  configuration.State.AWS.SecretAccessKey,
		Region:           configuration.State.AWS.Region,
		EndpointOverride: configuration.Global.EndpointOverride,
	}

	clientProvider := &clientmanager.ClientProvider{EndpointOverride: configuration.Global.EndpointOverride}
	clientProvider.SetConfig(awsConfiguration)

	credentialValidator := application.NewCredentialValidator(configuration)
	vpcStatusChecker := ec2.NewVPCStatusChecker(clientProvider)
	awsKeyPairCreator := ec2.NewKeyPairCreator(clientProvider)
	awsKeyPairDeleter := ec2.NewKeyPairDeleter(clientProvider, logger)
	keyPairChecker := ec2.NewKeyPairChecker(clientProvider)
	keyPairManager := ec2.NewKeyPairManager(awsKeyPairCreator, keyPairChecker, logger)
	keyPairSynchronizer := ec2.NewKeyPairSynchronizer(keyPairManager)
	availabilityZoneRetriever := ec2.NewAvailabilityZoneRetriever(clientProvider)
	templateBuilder := templates.NewTemplateBuilder(logger)
	stackManager := cloudformation.NewStackManager(clientProvider, logger)
	infrastructureManager := cloudformation.NewInfrastructureManager(templateBuilder, stackManager)
	certificateUploader := iam.NewCertificateUploader(clientProvider)
	certificateDescriber := iam.NewCertificateDescriber(clientProvider)
	certificateDeleter := iam.NewCertificateDeleter(clientProvider)
	certificateManager := iam.NewCertificateManager(certificateUploader, certificateDescriber, certificateDeleter)
	certificateValidator := iam.NewCertificateValidator()

	// GCP
	gcpClientProvider := gcp.NewClientProvider(gcpBasePath)
	gcpClientProvider.SetConfig(configuration.State.GCP.ServiceAccountKey, configuration.State.GCP.ProjectID, configuration.State.GCP.Zone)

	gcpKeyPairUpdater := gcp.NewKeyPairUpdater(rand.Reader, rsa.GenerateKey, ssh.NewPublicKey, gcpClientProvider, logger)
	gcpKeyPairDeleter := gcp.NewKeyPairDeleter(gcpClientProvider, logger)
	gcpNetworkInstancesChecker := gcp.NewNetworkInstancesChecker(gcpClientProvider)
	zones := gcp.NewZones()

	// EnvID
	envIDManager := helpers.NewEnvIDManager(envIDGenerator, gcpClientProvider, infrastructureManager)

	// Terraform
	gcpTemplateGenerator := gcpterraform.NewTemplateGenerator(zones)
	terraformCmd := terraform.NewCmd(os.Stderr)
	terraformExecutor := terraform.NewExecutor(terraformCmd, configuration.Global.Debug)
	terraformManager := terraform.NewManager(terraformExecutor, gcpTemplateGenerator, logger)

	// BOSH
	boshCommand := bosh.NewCmd(os.Stderr)
	boshExecutor := bosh.NewExecutor(boshCommand, ioutil.TempDir, ioutil.ReadFile, yaml.Unmarshal, json.Unmarshal,
		json.Marshal, ioutil.WriteFile)
	boshManager := bosh.NewManager(boshExecutor, terraformManager, stackManager, logger)
	boshClientProvider := bosh.NewClientProvider()

	// Cloud Config
	awsOpsGenerator := awscloudconfig.NewOpsGenerator(availabilityZoneRetriever, infrastructureManager)
	gcpOpsGenerator := gcpcloudconfig.NewOpsGenerator(terraformManager, zones)
	cloudConfigOpsGenerator := cloudconfig.NewOpsGenerator(awsOpsGenerator, gcpOpsGenerator)
	cloudConfigManager := cloudconfig.NewManager(logger, boshCommand, cloudConfigOpsGenerator, boshClientProvider)

	// Subcommands
	awsUp := commands.NewAWSUp(
		credentialValidator, infrastructureManager, keyPairSynchronizer, boshManager,
		availabilityZoneRetriever, certificateDescriber,
		cloudConfigManager, stateStore, clientProvider, envIDManager)

	awsCreateLBs := commands.NewAWSCreateLBs(
		logger, credentialValidator, certificateManager, infrastructureManager,
		availabilityZoneRetriever, boshClientProvider, cloudConfigManager, certificateValidator,
		uuidGenerator, stateStore,
	)

	gcpCreateLBs := commands.NewGCPCreateLBs(terraformManager, boshClientProvider, cloudConfigManager, stateStore, logger)

	awsUpdateLBs := commands.NewAWSUpdateLBs(credentialValidator, certificateManager, availabilityZoneRetriever, infrastructureManager,
		boshClientProvider, logger, uuidGenerator, stateStore)

	gcpUpdateLBs := commands.NewGCPUpdateLBs(gcpCreateLBs)

	awsDeleteLBs := commands.NewAWSDeleteLBs(
		credentialValidator, availabilityZoneRetriever, certificateManager,
		infrastructureManager, logger, cloudConfigManager, boshClientProvider, stateStore,
	)
	gcpDeleteLBs := commands.NewGCPDeleteLBs(stateStore, terraformManager, cloudConfigManager)

	gcpUp := commands.NewGCPUp(commands.NewGCPUpArgs{
		StateStore:         stateStore,
		KeyPairUpdater:     gcpKeyPairUpdater,
		GCPProvider:        gcpClientProvider,
		TerraformManager:   terraformManager,
		BoshManager:        boshManager,
		Logger:             logger,
		EnvIDManager:       envIDManager,
		CloudConfigManager: cloudConfigManager,
	})

	envGetter := commands.NewEnvGetter()

	// Commands
	commandSet[commands.HelpCommand] = commands.NewUsage(os.Stdout)
	commandSet[commands.VersionCommand] = commands.NewVersion(Version, os.Stdout)

	commandSet[commands.UpCommand] = commands.NewUp(awsUp, gcpUp, envGetter, boshManager)

	commandSet[commands.DestroyCommand] = commands.NewDestroy(
		credentialValidator, logger, os.Stdin, boshManager, vpcStatusChecker, stackManager,
		stringGenerator, infrastructureManager, awsKeyPairDeleter, gcpKeyPairDeleter, certificateDeleter,
		stateStore, stateValidator, terraformManager, gcpNetworkInstancesChecker,
	)

	commandSet[commands.CreateLBsCommand] = commands.NewCreateLBs(awsCreateLBs, gcpCreateLBs, stateValidator, boshManager)
	commandSet[commands.UpdateLBsCommand] = commands.NewUpdateLBs(awsUpdateLBs, gcpUpdateLBs, certificateValidator, stateValidator, logger, boshManager)
	commandSet[commands.DeleteLBsCommand] = commands.NewDeleteLBs(gcpDeleteLBs, awsDeleteLBs, logger, stateValidator, boshManager)
	commandSet[commands.LBsCommand] = commands.NewLBs(credentialValidator, stateValidator, infrastructureManager, terraformManager, os.Stdout)
	commandSet[commands.DirectorAddressCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorAddressPropertyName)
	commandSet[commands.DirectorUsernameCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorUsernamePropertyName)
	commandSet[commands.DirectorPasswordCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorPasswordPropertyName)
	commandSet[commands.DirectorCACertCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorCACertPropertyName)
	commandSet[commands.SSHKeyCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.SSHKeyPropertyName)
	commandSet[commands.EnvIDCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.EnvIDPropertyName)
	commandSet[commands.PrintEnvCommand] = commands.NewPrintEnv(logger, stateValidator, terraformManager, infrastructureManager)
	commandSet[commands.CloudConfigCommand] = commands.NewCloudConfig(logger, stateValidator, cloudConfigManager)

	commandSet[commands.BOSHDeploymentVarsCommand] = commands.NewBOSHDeploymentVars(logger, boshManager)

	app := application.New(commandSet, configuration, stateStore, usage)

	err = app.Run()
	if err != nil {
		fail(err)
	}
}

func fail(err error) {
	fmt.Fprintf(os.Stderr, "\n\n%s\n", err)
	os.Exit(1)
}
<commit_msg>extract getConfiguration function in main.go<commit_after>package main

import (
	"crypto/rand"
	"crypto/rsa"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	yaml "gopkg.in/yaml.v2"

	"golang.org/x/crypto/ssh"

	"github.com/cloudfoundry/bosh-bootloader/application"
	"github.com/cloudfoundry/bosh-bootloader/aws"
	"github.com/cloudfoundry/bosh-bootloader/aws/clientmanager"
	"github.com/cloudfoundry/bosh-bootloader/aws/cloudformation"
	"github.com/cloudfoundry/bosh-bootloader/aws/cloudformation/templates"
	"github.com/cloudfoundry/bosh-bootloader/aws/ec2"
	"github.com/cloudfoundry/bosh-bootloader/aws/iam"
	"github.com/cloudfoundry/bosh-bootloader/bosh"
	"github.com/cloudfoundry/bosh-bootloader/cloudconfig"
	"github.com/cloudfoundry/bosh-bootloader/commands"
	"github.com/cloudfoundry/bosh-bootloader/gcp"
	"github.com/cloudfoundry/bosh-bootloader/helpers"
	"github.com/cloudfoundry/bosh-bootloader/storage"
	"github.com/cloudfoundry/bosh-bootloader/terraform"

	awscloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/aws"
	gcpcloudconfig "github.com/cloudfoundry/bosh-bootloader/cloudconfig/gcp"
	gcpterraform "github.com/cloudfoundry/bosh-bootloader/terraform/gcp"
)

var (
	Version     string
	gcpBasePath string
)

func main() {
	// Command Set
	commandSet := application.CommandSet{
		commands.HelpCommand:               nil,
		commands.VersionCommand:            nil,
		commands.UpCommand:                 nil,
		commands.DestroyCommand:            nil,
		commands.DirectorAddressCommand:    nil,
		commands.DirectorUsernameCommand:   nil,
		commands.DirectorPasswordCommand:   nil,
		commands.DirectorCACertCommand:     nil,
		commands.SSHKeyCommand:             nil,
		commands.CreateLBsCommand:          nil,
		commands.UpdateLBsCommand:          nil,
		commands.DeleteLBsCommand:          nil,
		commands.LBsCommand:                nil,
		commands.EnvIDCommand:              nil,
		commands.PrintEnvCommand:           nil,
		commands.CloudConfigCommand:        nil,
		commands.BOSHDeploymentVarsCommand: nil,
	}

	// Utilities
	uuidGenerator := helpers.NewUUIDGenerator(rand.Reader)
	stringGenerator := helpers.NewStringGenerator(rand.Reader)
	envIDGenerator := helpers.NewEnvIDGenerator(rand.Reader)
	logger := application.NewLogger(os.Stdout)
	stderrLogger := application.NewLogger(os.Stderr)

	// Usage Command
	usage := commands.NewUsage(os.Stdout)

	configuration := getConfiguration(usage.Print, commandSet)

	storage.GetStateLogger = stderrLogger

	stateStore := storage.NewStore(configuration.Global.StateDir)
	stateValidator := application.NewStateValidator(configuration.Global.StateDir)

	// Amazon
	awsConfiguration := aws.Config{
		AccessKeyID:      configuration.State.AWS.AccessKeyID,
		SecretAccessKey:  configuration.State.AWS.SecretAccessKey,
		Region:           configuration.State.AWS.Region,
		EndpointOverride: configuration.Global.EndpointOverride,
	}

	clientProvider := &clientmanager.ClientProvider{EndpointOverride: configuration.Global.EndpointOverride}
	clientProvider.SetConfig(awsConfiguration)

	credentialValidator := application.NewCredentialValidator(configuration)
	vpcStatusChecker := ec2.NewVPCStatusChecker(clientProvider)
	awsKeyPairCreator := ec2.NewKeyPairCreator(clientProvider)
	awsKeyPairDeleter := ec2.NewKeyPairDeleter(clientProvider, logger)
	keyPairChecker := ec2.NewKeyPairChecker(clientProvider)
	keyPairManager := ec2.NewKeyPairManager(awsKeyPairCreator, keyPairChecker, logger)
	keyPairSynchronizer := ec2.NewKeyPairSynchronizer(keyPairManager)
	availabilityZoneRetriever := ec2.NewAvailabilityZoneRetriever(clientProvider)
	templateBuilder := templates.NewTemplateBuilder(logger)
	stackManager := cloudformation.NewStackManager(clientProvider, logger)
	infrastructureManager := cloudformation.NewInfrastructureManager(templateBuilder, stackManager)
	certificateUploader := iam.NewCertificateUploader(clientProvider)
	certificateDescriber := iam.NewCertificateDescriber(clientProvider)
	certificateDeleter := iam.NewCertificateDeleter(clientProvider)
	certificateManager := iam.NewCertificateManager(certificateUploader, certificateDescriber, certificateDeleter)
	certificateValidator := iam.NewCertificateValidator()

	// GCP
	gcpClientProvider := gcp.NewClientProvider(gcpBasePath)
	gcpClientProvider.SetConfig(configuration.State.GCP.ServiceAccountKey, configuration.State.GCP.ProjectID, configuration.State.GCP.Zone)
	gcpKeyPairUpdater := gcp.NewKeyPairUpdater(rand.Reader, rsa.GenerateKey, ssh.NewPublicKey, gcpClientProvider, logger)
	gcpKeyPairDeleter := gcp.NewKeyPairDeleter(gcpClientProvider, logger)
	gcpNetworkInstancesChecker := gcp.NewNetworkInstancesChecker(gcpClientProvider)
	zones := gcp.NewZones()

	// EnvID
	envIDManager := helpers.NewEnvIDManager(envIDGenerator, gcpClientProvider, infrastructureManager)

	// Terraform
	gcpTemplateGenerator := gcpterraform.NewTemplateGenerator(zones)
	terraformCmd := terraform.NewCmd(os.Stderr)
	terraformExecutor := terraform.NewExecutor(terraformCmd, configuration.Global.Debug)
	terraformManager := terraform.NewManager(terraformExecutor, gcpTemplateGenerator, logger)

	// BOSH
	boshCommand := bosh.NewCmd(os.Stderr)
	boshExecutor := bosh.NewExecutor(boshCommand, ioutil.TempDir, ioutil.ReadFile, yaml.Unmarshal, json.Unmarshal,
		json.Marshal, ioutil.WriteFile)
	boshManager := bosh.NewManager(boshExecutor, terraformManager, stackManager, logger)
	boshClientProvider := bosh.NewClientProvider()

	// Cloud Config
	awsOpsGenerator := awscloudconfig.NewOpsGenerator(availabilityZoneRetriever, infrastructureManager)
	gcpOpsGenerator := gcpcloudconfig.NewOpsGenerator(terraformManager, zones)
	cloudConfigOpsGenerator := cloudconfig.NewOpsGenerator(awsOpsGenerator, gcpOpsGenerator)
	cloudConfigManager := cloudconfig.NewManager(logger, boshCommand, cloudConfigOpsGenerator, boshClientProvider)

	// Subcommands
	awsUp := commands.NewAWSUp(
		credentialValidator, infrastructureManager, keyPairSynchronizer, boshManager,
		availabilityZoneRetriever, certificateDescriber,
		cloudConfigManager, stateStore, clientProvider, envIDManager)

	awsCreateLBs := commands.NewAWSCreateLBs(
		logger, credentialValidator, certificateManager, infrastructureManager,
		availabilityZoneRetriever, boshClientProvider, cloudConfigManager, certificateValidator,
		uuidGenerator, stateStore,
	)

	awsUpdateLBs := commands.NewAWSUpdateLBs(credentialValidator, certificateManager, availabilityZoneRetriever, infrastructureManager,
		boshClientProvider, logger, uuidGenerator, stateStore)

	awsDeleteLBs := commands.NewAWSDeleteLBs(
		credentialValidator, availabilityZoneRetriever, certificateManager,
		infrastructureManager, logger, cloudConfigManager, boshClientProvider, stateStore,
	)
	gcpDeleteLBs := commands.NewGCPDeleteLBs(stateStore, terraformManager, cloudConfigManager)

	gcpUp := commands.NewGCPUp(commands.NewGCPUpArgs{
		StateStore:         stateStore,
		KeyPairUpdater:     gcpKeyPairUpdater,
		GCPProvider:        gcpClientProvider,
		TerraformManager:   terraformManager,
		BoshManager:        boshManager,
		Logger:             logger,
		EnvIDManager:       envIDManager,
		CloudConfigManager: cloudConfigManager,
	})

	gcpCreateLBs := commands.NewGCPCreateLBs(terraformManager, boshClientProvider, cloudConfigManager, stateStore, logger)

	gcpUpdateLBs := commands.NewGCPUpdateLBs(gcpCreateLBs)

	envGetter := commands.NewEnvGetter()

	// Commands
	commandSet[commands.HelpCommand] = commands.NewUsage(os.Stdout)
	commandSet[commands.VersionCommand] = commands.NewVersion(Version, os.Stdout)
	commandSet[commands.UpCommand] = commands.NewUp(awsUp, gcpUp, envGetter, boshManager)
	commandSet[commands.DestroyCommand] = commands.NewDestroy(
		credentialValidator, logger, os.Stdin, boshManager, vpcStatusChecker, stackManager,
		stringGenerator, infrastructureManager, awsKeyPairDeleter, gcpKeyPairDeleter, certificateDeleter,
		stateStore, stateValidator, terraformManager, gcpNetworkInstancesChecker,
	)
	commandSet[commands.CreateLBsCommand] = commands.NewCreateLBs(awsCreateLBs, gcpCreateLBs, stateValidator, boshManager)
	commandSet[commands.UpdateLBsCommand] = commands.NewUpdateLBs(awsUpdateLBs, gcpUpdateLBs, certificateValidator, stateValidator, logger, boshManager)
	commandSet[commands.DeleteLBsCommand] = commands.NewDeleteLBs(gcpDeleteLBs, awsDeleteLBs, logger, stateValidator, boshManager)
	commandSet[commands.LBsCommand] = commands.NewLBs(credentialValidator, stateValidator, infrastructureManager, terraformManager, os.Stdout)
	commandSet[commands.DirectorAddressCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorAddressPropertyName)
	commandSet[commands.DirectorUsernameCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorUsernamePropertyName)
	commandSet[commands.DirectorPasswordCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorPasswordPropertyName)
	commandSet[commands.DirectorCACertCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.DirectorCACertPropertyName)
	commandSet[commands.SSHKeyCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.SSHKeyPropertyName)
	commandSet[commands.EnvIDCommand] = commands.NewStateQuery(logger, stateValidator, terraformManager, infrastructureManager, commands.EnvIDPropertyName)
	commandSet[commands.PrintEnvCommand] = commands.NewPrintEnv(logger, stateValidator, terraformManager, infrastructureManager)
	commandSet[commands.CloudConfigCommand] = commands.NewCloudConfig(logger, stateValidator, cloudConfigManager)
	commandSet[commands.BOSHDeploymentVarsCommand] = commands.NewBOSHDeploymentVars(logger, boshManager)

	app := application.New(commandSet, configuration, stateStore, usage)

	err := app.Run()
	if err != nil {
		fail(err)
	}
}

func fail(err error) {
	fmt.Fprintf(os.Stderr, "\n\n%s\n", err)
	os.Exit(1)
}

func getConfiguration(printUsage func(), commandSet application.CommandSet) application.Configuration {
	commandLineParser := application.NewCommandLineParser(printUsage, commandSet)
	configurationParser := application.NewConfigurationParser(commandLineParser)
	configuration, err := configurationParser.Parse(os.Args[1:])
	if err != nil {
		fail(err)
	}

	return configuration
}
<|endoftext|>