<commit_before>// Copyright © 2016 Zenly <hello@zen.ly>.
//
// 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 protein

import (
	"context"
	"reflect"
	"unsafe"

	"github.com/gogo/protobuf/proto"
	proto_vanilla "github.com/golang/protobuf/proto"
	"github.com/znly/protein/failure"
	"github.com/znly/protein/protoscan"

	"github.com/pkg/errors"
)

// -----------------------------------------------------------------------------

// TODO(cmc): document all of this

type TranscoderGetter func(ctx context.Context, uid string) ([]byte, error)
type TranscoderSetter func(ctx context.Context, payload []byte) error
type TranscoderSerializer func(ps *ProtobufSchema) ([]byte, error)
type TranscoderDeserializer func(payload []byte, ps *ProtobufSchema) error

// -----------------------------------------------------------------------------

// TODO(cmc): document all of this

type TranscoderOpt func(trc *Transcoder) *Transcoder

var (
	TranscoderOptGetter = func(getter TranscoderGetter) TranscoderOpt {
		return func(trc *Transcoder) *Transcoder {
			trc.getter = getter
			return trc
		}
	}
	TranscoderOptSetter = func(setter TranscoderSetter) TranscoderOpt {
		return func(trc *Transcoder) *Transcoder {
			trc.setter = setter
			return trc
		}
	}
	TranscoderOptSerializer = func(serializer TranscoderSerializer) TranscoderOpt {
		return func(trc *Transcoder) *Transcoder {
			trc.serializer = serializer
			return trc
		}
	}
	TranscoderOptDeserializer = func(deserializer TranscoderDeserializer) TranscoderOpt {
		return func(trc *Transcoder) *Transcoder {
			trc.deserializer = deserializer
			return trc
		}
	}
)

// -----------------------------------------------------------------------------

// TODO(cmc)
//
// Transcoder implements a Wirer that integrates with a Bank in order to augment
// the protobuf payloads that it encodes with additional versioning metadata.
//
// These metadata are then used by the internal deserializer of the versioned Wirer
// to determinate how to decode an incoming payload on the wire.
type Transcoder struct {
	sm *SchemaMap

	getter       TranscoderGetter
	setter       TranscoderSetter
	serializer   TranscoderSerializer
	deserializer TranscoderDeserializer

	typeCache map[string]reflect.Type
}

// TODO(cmc)
func NewTranscoder(ctx context.Context,
	hasher protoscan.Hasher, hashPrefix string, opts ...TranscoderOpt,
) (*Transcoder, error) {
	t := &Transcoder{}

	sm, err := ScanSchemas(hasher, hashPrefix)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	t.sm = sm
	t.typeCache = map[string]reflect.Type{}

	opts = append([]TranscoderOpt{
		/* default getter: always returns ErrSchemaNotFound */
		TranscoderOptGetter(func(
			ctx context.Context, schemaUID string,
		) ([]byte, error) {
			/* err not found */
			return nil, errors.Wrapf(failure.ErrSchemaNotFound,
				"`%s`: no schema with this UID", schemaUID,
			)
		}),
		/* default setter: no-op */
		TranscoderOptSetter(func(context.Context, []byte) error { return nil }),
		/* default serializer: wraps the ProtobufSchema within a ProtobufPayload */
		TranscoderOptSerializer(func(ps *ProtobufSchema) ([]byte, error) {
			return t.Encode(ps)
		}),
		/* default deserializer: unwraps a ProtobufPayload */
		TranscoderOptDeserializer(func(payload []byte, ps *ProtobufSchema) error {
			return t.DecodeAs(payload, ps)
		}),
	}, opts...)
	for _, opt := range opts {
		t = opt(t)
	}

	if err := sm.ForEach(func(ps *ProtobufSchema) error {
		select {
		case <-ctx.Done():
			return errors.WithStack(ctx.Err())
		default:
		}
		b, err := t.serializer(ps)
		if err != nil {
			return errors.WithStack(err)
		}
		return t.setter(ctx, b)
	}); err != nil {
		return nil, errors.WithStack(err)
	}

	return t, nil
}

// -----------------------------------------------------------------------------

// TODO(cmc)
//
// get retrieves the ProtobufSchema associated with the specified identifier,
// plus all of its direct & indirect dependencies flattened in a map.
//
// The retrieval process is done in two steps:
// - First, the root schema, as identified by `uid`, is fetched from the local
//   local cache; if it cannot be found in there, it'll be retrieved from
//   the backing TuyauDB store.
//   If it cannot be found in the TuyauDB store, then a "schema not found"
//   error is returned.
// - Second, the same process is applied for every direct & indirect dependency
//   of the root schema.
//   The only difference is that all the dependencies missing from the local
//   cache will be bulk-fetched from the TuyauDB store to avoid unnecessary
//   round-trips.
//   A "schemas not found" error is returned if one or more dependencies couldn't
//   be found.
func (t *Transcoder) get(
	ctx context.Context, schemaUID string,
) (map[string]*ProtobufSchema, error) {
	schemas := map[string]*ProtobufSchema{}

	// get root schema
	if ps := t.sm.GetByUID(schemaUID); ps != nil { // try the local cache first..
		schemas[schemaUID] = ps
	} else { // ..then fallback on user-defined getter
		b, err := t.getter(ctx, schemaUID)
		if err != nil {
			return nil, errors.Wrapf(err, "`%s`: schema not found", schemaUID)
		}
		var ps ProtobufSchema
		if err := t.deserializer(b, &ps); err != nil {
			return nil, errors.WithStack(err)
		}
		t.sm.Add(map[string]*ProtobufSchema{schemaUID: &ps}) // upsert local-cache
	}

	// get dependencies
	deps := schemas[schemaUID].GetDeps()

	// try the local cache first..
	psNotFound := make(map[string]struct{}, len(deps))
	for depUID := range deps {
		if ps := t.sm.GetByUID(depUID); ps != nil {
			schemas[depUID] = ps
		} else {
			psNotFound[depUID] = struct{}{}
		}
	}
	if len(psNotFound) <= 0 { // found everything needed in local cache!
		return schemas, nil
	}

	// ..then fallback on user-defined getter
	var b []byte
	var err error
	for depUID := range psNotFound {
		b, err = t.getter(ctx, depUID)
		if err != nil {
			return nil, errors.Wrapf(err, "`%s`: schema not found", depUID)
		}
		var ps ProtobufSchema
		if err := t.deserializer(b, &ps); err != nil {
			return nil, errors.WithStack(err)
		}
		delete(psNotFound, depUID)                           // it's been found!
		t.sm.Add(map[string]*ProtobufSchema{schemaUID: &ps}) // upsert local-cache
	}
	if len(psNotFound) > 0 {
		err := errors.Errorf("one or more dependencies couldn't be found")
		for depUID := range psNotFound {
			err = errors.Wrapf(err, "`%s`: dependency not found", depUID)
		}
		return nil, err
	}

	return schemas, nil
}

// -----------------------------------------------------------------------------

// TODO(cmc)
//
// Encode marshals the given protobuf message then wraps it up into a
// ProtobufPayload object that adds additional versioning metadata.
//
// Encode uses the message's fully-qualified name to reverse-lookup its UID.
// Note that a single FQ-name might point to multiple UIDs if multiple versions
// of the associated message are currently availaible in the bank.
// When this happens, the first UID from the returned list will be used (and
// since this list is randomly-ordered, effectively a random UID will be used).
// ---> this won't do remote calls
func (t *Transcoder) Encode(msg proto.Message, fqName ...string) ([]byte, error) {
	// marshal the actual protobuf message
	payload, err := proto.Marshal(msg)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	// TODO(cmc): explain this mess
	var fqn string
	if len(fqName) > 0 {
		fqn = fqName[0]
	} else if fqn = proto_vanilla.MessageName(msg); len(fqn) > 0 {
	} else if fqn = proto.MessageName(msg); len(fqn) > 0 {
	} else {
		return nil, errors.Errorf("cannot encode, unknown protobuf schema")
	}
	fqn = "." + fqn

	// find the first UID associated with the fully-qualified name of `msg`
	ps := t.sm.GetByFQName(fqn)
	if ps == nil {
		return nil, errors.Errorf("`%s`: FQ-name not found", fqn)
	}
	// wrap the marshaled payload within a ProtobufPayload message
	pp := &ProtobufPayload{
		UID:     ps.UID,
		Payload: payload,
	}
	// marshal the ProtobufPayload
	return proto.Marshal(pp)
}

// -----------------------------------------------------------------------------

// We need to access protobuf's internal decoding machinery: the `go:linkname`
// directive instructs the compiler to declare a local symbol as an alias
// for an external one, even if it's private.
// This allows us to bind to the private `unmarshalType` method of the
// `proto.Buffer` class, which does the actual work of computing the
// necessary struct tags for a given protobuf field.
//
// `unmarshalType` is actually a method of the `proto.Buffer` class, hence the
// `b` given as first parameter will be used as "this".
//
// Due to the way Go mangles symbol names when using vendoring, the go:linkname
// clause is automatically generated via linkname-gen[1].
// [1] https://github.com/znly/linkname-gen.
//
//go:generate linkname-gen -symbol "github.com/gogo/protobuf/proto.(*Buffer).unmarshalType" -def "func unmarshalType(*proto.Buffer, reflect.Type, *proto.StructProperties, bool, unsafe.Pointer) error"

// Decode decodes the `payload` into a dynamically-defined structure type.
func (t *Transcoder) Decode(payload []byte) (reflect.Value, error) {
	var pp ProtobufPayload
	if err := proto.Unmarshal(payload, &pp); err != nil {
		return reflect.ValueOf(nil), errors.WithStack(err)
	}

	// fetch structure-type from cache, or create it if it doesn't exist
	var structType reflect.Type
	var ok bool
	uid := pp.GetUID()
	if structType, ok = t.typeCache[uid]; !ok {
		st, err := CreateStructType(uid, t.sm)
		if err != nil {
			return reflect.ValueOf(nil), errors.WithStack(err)
		}
		if st.Kind() != reflect.Struct {
			return reflect.ValueOf(nil), errors.Errorf(
				"`%s`: not a struct type", structType,
			)
		}
		structType = st
		t.typeCache[uid] = st // upsert type-cache
	}

	// allocate a new structure using the given type definition, the
	// returned `reflect.Value`'s underlying type is a pointer to struct
	obj := reflect.New(structType)

	b := proto.NewBuffer(pp.GetPayload())
	unmarshalType(b,
		// the structure definition, computed at runtime
		structType,
		// the protobuf properties of the struct, computed via its struct tags
		proto.GetProperties(structType),
		// is_group, deprecated
		false,
		// the address we want to deserialize to
		unsafe.Pointer(obj.Elem().Addr().Pointer()),
	)

	return obj, nil
}

// TODO(cmc): doc & test
func (t *Transcoder) DecodeAs(payload []byte, dst proto.Message) error {
	var ps ProtobufPayload
	if err := proto.Unmarshal(payload, &ps); err != nil {
		return errors.WithStack(err)
	}
	return proto.Unmarshal(ps.Payload, dst)
}
<commit_msg>transcoder > made struct-type cache thread-safe<commit_after>// Copyright © 2016 Zenly <hello@zen.ly>.
//
// 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 protein

import (
	"context"
	"reflect"
	"sync"
	"unsafe"

	"github.com/gogo/protobuf/proto"
	proto_vanilla "github.com/golang/protobuf/proto"
	"github.com/znly/protein/failure"
	"github.com/znly/protein/protoscan"

	"github.com/pkg/errors"
)

// -----------------------------------------------------------------------------

// TODO(cmc): document all of this

type TranscoderGetter func(ctx context.Context, uid string) ([]byte, error)
type TranscoderSetter func(ctx context.Context, payload []byte) error
type TranscoderSerializer func(ps *ProtobufSchema) ([]byte, error)
type TranscoderDeserializer func(payload []byte, ps *ProtobufSchema) error

// -----------------------------------------------------------------------------

// TODO(cmc): document all of this

type TranscoderOpt func(trc *Transcoder) *Transcoder

var (
	TranscoderOptGetter = func(getter TranscoderGetter) TranscoderOpt {
		return func(trc *Transcoder) *Transcoder {
			trc.getter = getter
			return trc
		}
	}
	TranscoderOptSetter = func(setter TranscoderSetter) TranscoderOpt {
		return func(trc *Transcoder) *Transcoder {
			trc.setter = setter
			return trc
		}
	}
	TranscoderOptSerializer = func(serializer TranscoderSerializer) TranscoderOpt {
		return func(trc *Transcoder) *Transcoder {
			trc.serializer = serializer
			return trc
		}
	}
	TranscoderOptDeserializer = func(deserializer TranscoderDeserializer) TranscoderOpt {
		return func(trc *Transcoder) *Transcoder {
			trc.deserializer = deserializer
			return trc
		}
	}
)

// -----------------------------------------------------------------------------

// TODO(cmc)
//
// Transcoder implements a Wirer that integrates with a Bank in order to augment
// the protobuf payloads that it encodes with additional versioning metadata.
//
// These metadata are then used by the internal deserializer of the versioned Wirer
// to determinate how to decode an incoming payload on the wire.
type Transcoder struct {
	sm *SchemaMap

	getter       TranscoderGetter
	setter       TranscoderSetter
	serializer   TranscoderSerializer
	deserializer TranscoderDeserializer

	typeCacheLock *sync.RWMutex
	typeCache     map[string]reflect.Type
}

// TODO(cmc)
func NewTranscoder(ctx context.Context,
	hasher protoscan.Hasher, hashPrefix string, opts ...TranscoderOpt,
) (*Transcoder, error) {
	t := &Transcoder{}

	sm, err := ScanSchemas(hasher, hashPrefix)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	t.sm = sm
	t.typeCacheLock = &sync.RWMutex{}
	t.typeCache = map[string]reflect.Type{}

	opts = append([]TranscoderOpt{
		/* default getter: always returns ErrSchemaNotFound */
		TranscoderOptGetter(func(
			ctx context.Context, schemaUID string,
		) ([]byte, error) {
			/* err not found */
			return nil, errors.Wrapf(failure.ErrSchemaNotFound,
				"`%s`: no schema with this UID", schemaUID,
			)
		}),
		/* default setter: no-op */
		TranscoderOptSetter(func(context.Context, []byte) error { return nil }),
		/* default serializer: wraps the ProtobufSchema within a ProtobufPayload */
		TranscoderOptSerializer(func(ps *ProtobufSchema) ([]byte, error) {
			return t.Encode(ps)
		}),
		/* default deserializer: unwraps a ProtobufPayload */
		TranscoderOptDeserializer(func(payload []byte, ps *ProtobufSchema) error {
			return t.DecodeAs(payload, ps)
		}),
	}, opts...)
	for _, opt := range opts {
		t = opt(t)
	}

	if err := sm.ForEach(func(ps *ProtobufSchema) error {
		select {
		case <-ctx.Done():
			return errors.WithStack(ctx.Err())
		default:
		}
		b, err := t.serializer(ps)
		if err != nil {
			return errors.WithStack(err)
		}
		return t.setter(ctx, b)
	}); err != nil {
		return nil, errors.WithStack(err)
	}

	return t, nil
}

// -----------------------------------------------------------------------------

// TODO(cmc)
//
// get retrieves the ProtobufSchema associated with the specified identifier,
// plus all of its direct & indirect dependencies flattened in a map.
//
// The retrieval process is done in two steps:
// - First, the root schema, as identified by `uid`, is fetched from the local
//   local cache; if it cannot be found in there, it'll be retrieved from
//   the backing TuyauDB store.
//   If it cannot be found in the TuyauDB store, then a "schema not found"
//   error is returned.
// - Second, the same process is applied for every direct & indirect dependency
//   of the root schema.
//   The only difference is that all the dependencies missing from the local
//   cache will be bulk-fetched from the TuyauDB store to avoid unnecessary
//   round-trips.
//   A "schemas not found" error is returned if one or more dependencies couldn't
//   be found.
func (t *Transcoder) get(
	ctx context.Context, schemaUID string,
) (map[string]*ProtobufSchema, error) {
	schemas := map[string]*ProtobufSchema{}

	// get root schema
	if ps := t.sm.GetByUID(schemaUID); ps != nil { // try the local cache first..
		schemas[schemaUID] = ps
	} else { // ..then fallback on user-defined getter
		b, err := t.getter(ctx, schemaUID)
		if err != nil {
			return nil, errors.Wrapf(err, "`%s`: schema not found", schemaUID)
		}
		var ps ProtobufSchema
		if err := t.deserializer(b, &ps); err != nil {
			return nil, errors.WithStack(err)
		}
		t.sm.Add(map[string]*ProtobufSchema{schemaUID: &ps}) // upsert local-cache
	}

	// get dependencies
	deps := schemas[schemaUID].GetDeps()

	// try the local cache first..
	psNotFound := make(map[string]struct{}, len(deps))
	for depUID := range deps {
		if ps := t.sm.GetByUID(depUID); ps != nil {
			schemas[depUID] = ps
		} else {
			psNotFound[depUID] = struct{}{}
		}
	}
	if len(psNotFound) <= 0 { // found everything needed in local cache!
		return schemas, nil
	}

	// ..then fallback on user-defined getter
	var b []byte
	var err error
	for depUID := range psNotFound {
		b, err = t.getter(ctx, depUID)
		if err != nil {
			return nil, errors.Wrapf(err, "`%s`: schema not found", depUID)
		}
		var ps ProtobufSchema
		if err := t.deserializer(b, &ps); err != nil {
			return nil, errors.WithStack(err)
		}
		delete(psNotFound, depUID)                           // it's been found!
		t.sm.Add(map[string]*ProtobufSchema{schemaUID: &ps}) // upsert local-cache
	}
	if len(psNotFound) > 0 {
		err := errors.Errorf("one or more dependencies couldn't be found")
		for depUID := range psNotFound {
			err = errors.Wrapf(err, "`%s`: dependency not found", depUID)
		}
		return nil, err
	}

	return schemas, nil
}

// -----------------------------------------------------------------------------

// TODO(cmc)
//
// Encode marshals the given protobuf message then wraps it up into a
// ProtobufPayload object that adds additional versioning metadata.
//
// Encode uses the message's fully-qualified name to reverse-lookup its UID.
// Note that a single FQ-name might point to multiple UIDs if multiple versions
// of the associated message are currently availaible in the bank.
// When this happens, the first UID from the returned list will be used (and
// since this list is randomly-ordered, effectively a random UID will be used).
// ---> this won't do remote calls
func (t *Transcoder) Encode(msg proto.Message, fqName ...string) ([]byte, error) {
	// marshal the actual protobuf message
	payload, err := proto.Marshal(msg)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	// TODO(cmc): explain this mess
	var fqn string
	if len(fqName) > 0 {
		fqn = fqName[0]
	} else if fqn = proto_vanilla.MessageName(msg); len(fqn) > 0 {
	} else if fqn = proto.MessageName(msg); len(fqn) > 0 {
	} else {
		return nil, errors.Errorf("cannot encode, unknown protobuf schema")
	}
	fqn = "." + fqn

	// find the first UID associated with the fully-qualified name of `msg`
	ps := t.sm.GetByFQName(fqn)
	if ps == nil {
		return nil, errors.Errorf("`%s`: FQ-name not found", fqn)
	}
	// wrap the marshaled payload within a ProtobufPayload message
	pp := &ProtobufPayload{
		UID:     ps.UID,
		Payload: payload,
	}
	// marshal the ProtobufPayload
	return proto.Marshal(pp)
}

// -----------------------------------------------------------------------------

// We need to access protobuf's internal decoding machinery: the `go:linkname`
// directive instructs the compiler to declare a local symbol as an alias
// for an external one, even if it's private.
// This allows us to bind to the private `unmarshalType` method of the
// `proto.Buffer` class, which does the actual work of computing the
// necessary struct tags for a given protobuf field.
//
// `unmarshalType` is actually a method of the `proto.Buffer` class, hence the
// `b` given as first parameter will be used as "this".
//
// Due to the way Go mangles symbol names when using vendoring, the go:linkname
// clause is automatically generated via linkname-gen[1].
// [1] https://github.com/znly/linkname-gen.
//
//go:generate linkname-gen -symbol "github.com/gogo/protobuf/proto.(*Buffer).unmarshalType" -def "func unmarshalType(*proto.Buffer, reflect.Type, *proto.StructProperties, bool, unsafe.Pointer) error"

// Decode decodes the `payload` into a dynamically-defined structure type.
func (t *Transcoder) Decode(payload []byte) (reflect.Value, error) {
	var pp ProtobufPayload
	if err := proto.Unmarshal(payload, &pp); err != nil {
		return reflect.ValueOf(nil), errors.WithStack(err)
	}

	// fetch structure-type from cache, or create it if it doesn't exist
	var structType reflect.Type
	var ok bool
	uid := pp.GetUID()
	t.typeCacheLock.RLock()
	structType, ok = t.typeCache[uid]
	t.typeCacheLock.RUnlock()
	if !ok {
		st, err := CreateStructType(uid, t.sm)
		if err != nil {
			return reflect.ValueOf(nil), errors.WithStack(err)
		}
		if st.Kind() != reflect.Struct {
			return reflect.ValueOf(nil), errors.Errorf(
				"`%s`: not a struct type", structType,
			)
		}
		structType = st
		t.typeCacheLock.Lock()
		t.typeCache[uid] = st // upsert type-cache
		t.typeCacheLock.Unlock()
	}

	// allocate a new structure using the given type definition, the
	// returned `reflect.Value`'s underlying type is a pointer to struct
	obj := reflect.New(structType)

	b := proto.NewBuffer(pp.GetPayload())
	unmarshalType(b,
		// the structure definition, computed at runtime
		structType,
		// the protobuf properties of the struct, computed via its struct tags
		proto.GetProperties(structType),
		// is_group, deprecated
		false,
		// the address we want to deserialize to
		unsafe.Pointer(obj.Elem().Addr().Pointer()),
	)

	return obj, nil
}

// TODO(cmc): doc & test
func (t *Transcoder) DecodeAs(payload []byte, dst proto.Message) error {
	var ps ProtobufPayload
	if err := proto.Unmarshal(payload, &ps); err != nil {
		return errors.WithStack(err)
	}
	return proto.Unmarshal(ps.Payload, dst)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"strings"

	"github.com/google/go-github/github"

	"github.com/wsxiaoys/terminal"
	"github.com/wsxiaoys/terminal/color"
)

func NewPullRequestor(config *Config, client *github.Client) *PullRequestor {
	return &PullRequestor{config, client}
}

type PullRequestor struct {
	config *Config
	client *github.Client
}

func (self *PullRequestor) writePullRequest(organization string, project string, pr github.PullRequest, out chan<- string) {
	if self.config.Debug {
		log.Println("Listing comments")
	}
	comments, _, err := self.client.PullRequests.ListComments(organization, project, *pr.Number, nil)

	if err != nil {
		log.Fatal(err)
	}

	var buffer bytes.Buffer

	buffer.WriteString(color.Sprintf("@w[%d] %s\n", *pr.Number, *pr.Title))

	buffer.WriteString(color.Sprintf("@{/}(Created on %s :: %d comments)\n", *pr.CreatedAt, len(comments)))

	if pr.Body == nil || len(*pr.Body) == 0 {
		buffer.WriteString(color.Sprint("@r<no body>\n"))
	} else {
		bodyLen := len(*pr.Body)
		if bodyLen > 120 {
			bodyLen = 120
		}

		buffer.WriteString(color.Sprintf("@b%s\n", (*pr.Body)[:bodyLen]))
	}

	buffer.WriteString(color.Sprintf("@g%s\n\n", (*pr.HTMLURL)))

	if self.config.Debug {
		log.Println("Done printing pull requests")
	}

	out <- buffer.String()
}

func (self *PullRequestor) PrintPullRequests(repo Repository, isDone chan<- bool) {
	//done := make(chan bool)

	for _, project := range repo.Projects {
		//		go func() {
		var buffer bytes.Buffer
		if self.config.Debug {
			log.Println("Getting pull requests for", project)
		}
		prs, _, err := self.client.PullRequests.List(repo.Organization, project, nil)

		if err != nil {
			log.Fatal(err)
		}

		if len(prs) == 0 {
			continue
			//done <- false
			//return
		}

		// use a channel to collect all of the writes to a buffer then write it all at once
		printer := make(chan string)
		defer close(printer)
		for _, pr := range prs {
			go self.writePullRequest(repo.Organization, project, pr, printer)
		}

		// write out the printer to the terminal after everything has been collected
		terminal.Stdout.Color("y")

		// Get color codes here: https://github.com/wsxiaoys/terminal/blob/master/color/color.go
		title := fmt.Sprintf(" [ %s ] ", strings.ToUpper(project))
		paddingWidth := (80 - len(title)) / 2

		buffer.WriteString(color.Sprintf("%s\n", strings.Repeat("=", 80)))
		buffer.WriteString(color.Sprintf("@{!m}%s%s%s\n", strings.Repeat("-", paddingWidth), title, strings.Repeat("-", paddingWidth)))

		for i := 0; i < len(prs); i++ {
			buffer.WriteString(<-printer)
		}
		color.Print(buffer.String())

		//done <- true
		//		}()
	}

	//for i := 0; i < len(repo.Projects); i++ {
	//	<-done
	//}
	isDone <- true
}

func (self *PullRequestor) ListRepos() {
	// list all repositories for the authenticated user
	done := make(chan bool)
	defer close(done)
	for _, repo := range self.config.Repositories {
		go self.PrintPullRequests(repo, done)
	}

	for i := 0; i < len(self.config.Repositories); i++ {
		<-done
	}
}
<commit_msg>Add some logging and comment out another attempt at more go func<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"strings"

	"github.com/google/go-github/github"

	"github.com/wsxiaoys/terminal"
	"github.com/wsxiaoys/terminal/color"
)

func NewPullRequestor(config *Config, client *github.Client) *PullRequestor {
	return &PullRequestor{config, client}
}

type PullRequestor struct {
	config *Config
	client *github.Client
}

func (self *PullRequestor) writePullRequest(organization string, project string, pr github.PullRequest, out chan<- string) {
	if self.config.Debug {
		log.Println("Listing comments for", project)
	}
	comments, _, err := self.client.PullRequests.ListComments(organization, project, *pr.Number, nil)

	if err != nil {
		log.Fatal(err)
	}

	var buffer bytes.Buffer

	buffer.WriteString(color.Sprintf("@w[%d] %s\n", *pr.Number, *pr.Title))

	buffer.WriteString(color.Sprintf("@{/}(Created on %s :: %d comments)\n", *pr.CreatedAt, len(comments)))

	if pr.Body == nil || len(*pr.Body) == 0 {
		buffer.WriteString(color.Sprint("@r<no body>\n"))
	} else {
		bodyLen := len(*pr.Body)
		if bodyLen > 120 {
			bodyLen = 120
		}

		buffer.WriteString(color.Sprintf("@b%s\n", (*pr.Body)[:bodyLen]))
	}

	buffer.WriteString(color.Sprintf("@g%s\n\n", (*pr.HTMLURL)))

	if self.config.Debug {
		log.Println("Done printing pull requests for", project)
	}

	out <- buffer.String()
}

func (self *PullRequestor) PrintPullRequests(repo Repository, done chan<- struct{}) {
	//out := make(chan string)
	//defer close(out)

	for _, project := range repo.Projects {
		//go func() {
		var buffer bytes.Buffer
		if self.config.Debug {
			log.Println("Getting pull requests for", project)
		}
		prs, _, err := self.client.PullRequests.List(repo.Organization, project, nil)

		if err != nil {
			log.Fatal(err)
		}

		if len(prs) == 0 {
			//out <- ""
			//return
			continue
		}

		// use a channel to collect all of the writes to a buffer then write it all at once
		printer := make(chan string)
		defer close(printer)

		for _, pr := range prs {
			go self.writePullRequest(repo.Organization, project, pr, printer)
		}

		// write out the printer to the terminal after everything has been collected
		terminal.Stdout.Color("y")

		// Get color codes here: https://github.com/wsxiaoys/terminal/blob/master/color/color.go
		title := fmt.Sprintf(" [ %s (%d) ] ", strings.ToUpper(project), len(prs))
		paddingWidth := (80 - len(title)) / 2

		buffer.WriteString(color.Sprintf("%s\n", strings.Repeat("=", 80)))
		buffer.WriteString(color.Sprintf("@{!m}%s%s%s\n", strings.Repeat("-", paddingWidth), title, strings.Repeat("-", paddingWidth)))

		for i := 0; i < len(prs); i++ {
			buffer.WriteString(<-printer)
		}

		color.Print(buffer.String())
		//out <- buffer.String()
		//}()
	}

	//for i := 0; i < len(repo.Projects); i++ {
	//	fmt.Print(<-out)
	//}
	done <- struct{}{}
}

func (self *PullRequestor) ListRepos() {
	// list all repositories for the authenticated user
	done := make(chan struct{})
	defer close(done)

	for _, repo := range self.config.Repositories {
		go self.PrintPullRequests(repo, done)
	}

	for i := 0; i < len(self.config.Repositories); i++ {
		<-done
	}
}
<|endoftext|>


<commit_before><commit_msg>Changed log on bot login<commit_after><|endoftext|>


<commit_before>package operators

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	"github.com/stretchr/objx"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/dynamic"
	coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	configv1 "github.com/openshift/api/config/v1"
	configclient "github.com/openshift/client-go/config/clientset/versioned"
)

const (
	operatorWait = 1 * time.Minute
	cvoWait      = 5 * time.Minute
)

var _ = g.Describe("[Feature:Platform][Smoke] Managed cluster should", func() {
	defer g.GinkgoRecover()

	g.It("start all core operators", func() {
		cfg, err := e2e.LoadConfig()
		o.Expect(err).NotTo(o.HaveOccurred())
		c, err := e2e.LoadClientset()
		o.Expect(err).NotTo(o.HaveOccurred())
		dc, err := dynamic.NewForConfig(cfg)
		o.Expect(err).NotTo(o.HaveOccurred())

		// presence of the CVO namespace gates this test
		g.By("checking for the cluster version operator")
		skipUnlessCVO(c.CoreV1().Namespaces())

		g.By("waiting for the cluster version to be applied")
		cvc := dc.Resource(schema.GroupVersionResource{Group: "config.openshift.io", Resource: "clusterversions", Version: "v1"})
		var lastErr error
		var lastCV objx.Map
		if err := wait.PollImmediate(3*time.Second, cvoWait, func() (bool, error) {
			obj, err := cvc.Get("version", metav1.GetOptions{})
			if err != nil {
				lastErr = err
				e2e.Logf("Unable to check for cluster version: %v", err)
				return false, nil
			}
			cv := objx.Map(obj.UnstructuredContent())
			lastErr = nil
			lastCV = cv
			if cond := condition(cv, "Progressing"); cond.Get("status").String() != "False" {
				e2e.Logf("ClusterVersion is still progressing: %s", cond.Get("message").String())
				return false, nil
			}
			if cond := condition(cv, "Available"); cond.Get("status").String() != "True" {
				e2e.Logf("ClusterVersion is not available: %s", cond.Get("message").String())
				return false, nil
			}
			e2e.Logf("ClusterVersion available: %s", condition(cv, "Progressing").Get("message").String())
			return true, nil
		}); err != nil {
			o.Expect(lastErr).NotTo(o.HaveOccurred())
			e2e.Logf("Last cluster version seen: %s", lastCV)
			if msg := condition(lastCV, "Failing").Get("message").String(); len(msg) > 0 {
				e2e.Logf("ClusterVersion is reporting a failure: %s", msg)
			}
			e2e.Failf("ClusterVersion never became available: %s", condition(lastCV, "Progressing").Get("message").String())
		}

		// gate on all clusteroperators being ready
		available := make(map[string]struct{})
		g.By(fmt.Sprintf("waiting for all cluster operators to be stable at the same time"))
		coc := dc.Resource(schema.GroupVersionResource{Group: "config.openshift.io", Resource: "clusteroperators", Version: "v1"})
		lastErr = nil
		var lastCOs []objx.Map
		wait.PollImmediate(time.Second, operatorWait, func() (bool, error) {
			obj, err := coc.List(metav1.ListOptions{})
			if err != nil {
				lastErr = err
				e2e.Logf("Unable to check for cluster operators: %v", err)
				return false, nil
			}
			cv := objx.Map(obj.UnstructuredContent())
			lastErr = nil
			items := objects(cv.Get("items"))
			lastCOs = items

			if len(items) == 0 {
				return false, nil
			}

			var unavailable []objx.Map
			var unavailableNames []string
			for _, co := range items {
				if condition(co, "Available").Get("status").String() != "True" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
				if condition(co, "Progressing").Get("status").String() != "False" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
				if condition(co, "Failing").Get("status").String() != "False" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
			}
			if len(unavailable) > 0 {
				e2e.Logf("Operators still doing work: %s", strings.Join(unavailableNames, ", "))
				return false, nil
			}
			return true, nil
		})

		o.Expect(lastErr).NotTo(o.HaveOccurred())
		var unavailable []string
		buf := &bytes.Buffer{}
		w := tabwriter.NewWriter(buf, 0, 4, 1, ' ', 0)
		fmt.Fprintf(w, "NAMESPACE\tNAME\tPROGRESSING\tAVAILABLE\tVERSION\tMESSAGE\n")
		for _, co := range lastCOs {
			ns := co.Get("metadata.namespace").String()
			name := co.Get("metadata.name").String()
			if condition(co, "Available").Get("status").String() != "True" {
				unavailable = append(unavailable, fmt.Sprintf("%s/%s", ns, name))
			} else {
				available[fmt.Sprintf("%s/%s", ns, name)] = struct{}{}
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
				ns,
				name,
				condition(co, "Progressing").Get("status").String(),
				condition(co, "Available").Get("status").String(),
				co.Get("status.version").String(),
				condition(co, "Failing").Get("message").String(),
			)
		}
		w.Flush()
		e2e.Logf("ClusterOperators:\n%s", buf.String())

		if len(unavailable) > 0 {
			e2e.Failf("Some cluster operators never became available %s", strings.Join(unavailable, ", "))
		}
		// Check at least one core operator is available
		if len(available) == 0 {
			e2e.Failf("There must be at least one cluster operator")
		}
	})
})

var _ = g.Describe("[Feature:Platform] Managed cluster should", func() {
	defer g.GinkgoRecover()

	g.It("have operators on the cluster version", func() {
		if len(os.Getenv("TEST_UNSUPPORTED_ALLOW_VERSION_SKEW")) > 0 {
			e2e.Skipf("Test is disabled to allow cluster components to have different versions")
		}
		cfg, err := e2e.LoadConfig()
		o.Expect(err).NotTo(o.HaveOccurred())
		c := configclient.NewForConfigOrDie(cfg)
		coreclient, err := e2e.LoadClientset()
		o.Expect(err).NotTo(o.HaveOccurred())

		// presence of the CVO namespace gates this test
		g.By("checking for the cluster version operator")
		skipUnlessCVO(coreclient.CoreV1().Namespaces())

		// we need to get the list of versions
		cv, err := c.ConfigV1().ClusterVersions().Get("version", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		coList, err := c.ConfigV1().ClusterOperators().List(metav1.ListOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(coList.Items).NotTo(o.BeEmpty())

		g.By("all cluster operators report an operator version in the first position equal to the cluster version")
		for _, co := range coList.Items {
			msg := fmt.Sprintf("unexpected operator status versions %s:\n%#v", co.Name, co.Status.Versions)
			o.Expect(co.Status.Versions).NotTo(o.BeEmpty(), msg)
			operator := findOperatorVersion(co.Status.Versions, "operator")
			o.Expect(operator).NotTo(o.BeNil(), msg)
			o.Expect(operator.Name).To(o.Equal("operator"), msg)
			o.Expect(operator.Version).To(o.Equal(cv.Status.Desired.Version), msg)
		}
	})
})

func skipUnlessCVO(c coreclient.NamespaceInterface) {
	err := wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
		_, err := c.Get("openshift-cluster-version", metav1.GetOptions{})
		if err == nil {
			return true, nil
		}
		if errors.IsNotFound(err) {
			e2e.Skipf("The cluster is not managed by a cluster-version operator")
		}
		e2e.Logf("Unable to check for cluster version operator: %v", err)
		return false, nil
	})
	o.Expect(err).NotTo(o.HaveOccurred())
}

func findOperatorVersion(versions []configv1.OperandVersion, name string) *configv1.OperandVersion {
	for i := range versions {
		if versions[i].Name == name {
			return &versions[i]
		}
	}
	return nil
}

func contains(names []string, name string) bool {
	for _, s := range names {
		if s == name {
			return true
		}
	}
	return false
}

func jsonString(from objx.Map) string {
	s, _ := from.JSON()
	return s
}

func objects(from *objx.Value) []objx.Map {
	var values []objx.Map
	switch {
	case from.IsObjxMapSlice():
		return from.ObjxMapSlice()
	case from.IsInterSlice():
		for _, i := range from.InterSlice() {
			if msi, ok := i.(map[string]interface{}); ok {
				values = append(values, objx.Map(msi))
			}
		}
	}
	return values
}

func condition(cv objx.Map, condition string) objx.Map {
	for _, obj := range objects(cv.Get("status.conditions")) {
		if obj.Get("type").String() == condition {
			return obj
		}
	}
	return objx.Map(nil)
}
<commit_msg>test/extended/operators: ensure clusterversion current version is set<commit_after>package operators

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	"github.com/stretchr/objx"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/dynamic"
	coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	configv1 "github.com/openshift/api/config/v1"
	configclient "github.com/openshift/client-go/config/clientset/versioned"
)

const (
	operatorWait = 1 * time.Minute
	cvoWait      = 5 * time.Minute
)

var _ = g.Describe("[Feature:Platform][Smoke] Managed cluster should", func() {
	defer g.GinkgoRecover()

	g.It("start all core operators", func() {
		cfg, err := e2e.LoadConfig()
		o.Expect(err).NotTo(o.HaveOccurred())
		c, err := e2e.LoadClientset()
		o.Expect(err).NotTo(o.HaveOccurred())
		dc, err := dynamic.NewForConfig(cfg)
		o.Expect(err).NotTo(o.HaveOccurred())

		// presence of the CVO namespace gates this test
		g.By("checking for the cluster version operator")
		skipUnlessCVO(c.CoreV1().Namespaces())

		g.By("waiting for the cluster version to be applied")
		cvc := dc.Resource(schema.GroupVersionResource{Group: "config.openshift.io", Resource: "clusterversions", Version: "v1"})
		var lastErr error
		var lastCV objx.Map
		if err := wait.PollImmediate(3*time.Second, cvoWait, func() (bool, error) {
			obj, err := cvc.Get("version", metav1.GetOptions{})
			if err != nil {
				lastErr = err
				e2e.Logf("Unable to check for cluster version: %v", err)
				return false, nil
			}
			cv := objx.Map(obj.UnstructuredContent())
			lastErr = nil
			lastCV = cv
			payload := cv.Get("status.desired.payload").String()
			if len(payload) == 0 {
				e2e.Logf("ClusterVersion has no current payload version")
				return false, nil
			}
			if cond := condition(cv, "Progressing"); cond.Get("status").String() != "False" {
				e2e.Logf("ClusterVersion is still progressing: %s", cond.Get("message").String())
				return false, nil
			}
			if cond := condition(cv, "Available"); cond.Get("status").String() != "True" {
				e2e.Logf("ClusterVersion is not available: %s", cond.Get("message").String())
				return false, nil
			}
			e2e.Logf("ClusterVersion available: %s", condition(cv, "Progressing").Get("message").String())
			return true, nil
		}); err != nil {
			o.Expect(lastErr).NotTo(o.HaveOccurred())
			e2e.Logf("Last cluster version seen: %s", lastCV)
			if msg := condition(lastCV, "Failing").Get("message").String(); len(msg) > 0 {
				e2e.Logf("ClusterVersion is reporting a failure: %s", msg)
			}
			e2e.Failf("ClusterVersion never became available: %s", condition(lastCV, "Progressing").Get("message").String())
		}

		// gate on all clusteroperators being ready
		available := make(map[string]struct{})
		g.By(fmt.Sprintf("waiting for all cluster operators to be stable at the same time"))
		coc := dc.Resource(schema.GroupVersionResource{Group: "config.openshift.io", Resource: "clusteroperators", Version: "v1"})
		lastErr = nil
		var lastCOs []objx.Map
		wait.PollImmediate(time.Second, operatorWait, func() (bool, error) {
			obj, err := coc.List(metav1.ListOptions{})
			if err != nil {
				lastErr = err
				e2e.Logf("Unable to check for cluster operators: %v", err)
				return false, nil
			}
			cv := objx.Map(obj.UnstructuredContent())
			lastErr = nil
			items := objects(cv.Get("items"))
			lastCOs = items

			if len(items) == 0 {
				return false, nil
			}

			var unavailable []objx.Map
			var unavailableNames []string
			for _, co := range items {
				if condition(co, "Available").Get("status").String() != "True" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
				if condition(co, "Progressing").Get("status").String() != "False" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
				if condition(co, "Failing").Get("status").String() != "False" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
			}
			if len(unavailable) > 0 {
				e2e.Logf("Operators still doing work: %s", strings.Join(unavailableNames, ", "))
				return false, nil
			}
			return true, nil
		})

		o.Expect(lastErr).NotTo(o.HaveOccurred())
		var unavailable []string
		buf := &bytes.Buffer{}
		w := tabwriter.NewWriter(buf, 0, 4, 1, ' ', 0)
		fmt.Fprintf(w, "NAMESPACE\tNAME\tPROGRESSING\tAVAILABLE\tVERSION\tMESSAGE\n")
		for _, co := range lastCOs {
			ns := co.Get("metadata.namespace").String()
			name := co.Get("metadata.name").String()
			if condition(co, "Available").Get("status").String() != "True" {
				unavailable = append(unavailable, fmt.Sprintf("%s/%s", ns, name))
			} else {
				available[fmt.Sprintf("%s/%s", ns, name)] = struct{}{}
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
				ns,
				name,
				condition(co, "Progressing").Get("status").String(),
				condition(co, "Available").Get("status").String(),
				co.Get("status.version").String(),
				condition(co, "Failing").Get("message").String(),
			)
		}
		w.Flush()
		e2e.Logf("ClusterOperators:\n%s", buf.String())

		if len(unavailable) > 0 {
			e2e.Failf("Some cluster operators never became available %s", strings.Join(unavailable, ", "))
		}
		// Check at least one core operator is available
		if len(available) == 0 {
			e2e.Failf("There must be at least one cluster operator")
		}
	})
})

var _ = g.Describe("[Feature:Platform] Managed cluster should", func() {
	defer g.GinkgoRecover()

	g.It("have operators on the cluster version", func() {
		if len(os.Getenv("TEST_UNSUPPORTED_ALLOW_VERSION_SKEW")) > 0 {
			e2e.Skipf("Test is disabled to allow cluster components to have different versions")
		}
		cfg, err := e2e.LoadConfig()
		o.Expect(err).NotTo(o.HaveOccurred())
		c := configclient.NewForConfigOrDie(cfg)
		coreclient, err := e2e.LoadClientset()
		o.Expect(err).NotTo(o.HaveOccurred())

		// presence of the CVO namespace gates this test
		g.By("checking for the cluster version operator")
		skipUnlessCVO(coreclient.CoreV1().Namespaces())

		// we need to get the list of versions
		cv, err := c.ConfigV1().ClusterVersions().Get("version", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		coList, err := c.ConfigV1().ClusterOperators().List(metav1.ListOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(coList.Items).NotTo(o.BeEmpty())

		g.By("all cluster operators report an operator version in the first position equal to the cluster version")
		for _, co := range coList.Items {
			msg := fmt.Sprintf("unexpected operator status versions %s:\n%#v", co.Name, co.Status.Versions)
			o.Expect(co.Status.Versions).NotTo(o.BeEmpty(), msg)
			operator := findOperatorVersion(co.Status.Versions, "operator")
			o.Expect(operator).NotTo(o.BeNil(), msg)
			o.Expect(operator.Name).To(o.Equal("operator"), msg)
			o.Expect(operator.Version).To(o.Equal(cv.Status.Desired.Version), msg)
		}
	})
})

func skipUnlessCVO(c coreclient.NamespaceInterface) {
	err := wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
		_, err := c.Get("openshift-cluster-version", metav1.GetOptions{})
		if err == nil {
			return true, nil
		}
		if errors.IsNotFound(err) {
			e2e.Skipf("The cluster is not managed by a cluster-version operator")
		}
		e2e.Logf("Unable to check for cluster version operator: %v", err)
		return false, nil
	})
	o.Expect(err).NotTo(o.HaveOccurred())
}

func findOperatorVersion(versions []configv1.OperandVersion, name string) *configv1.OperandVersion {
	for i := range versions {
		if versions[i].Name == name {
			return &versions[i]
		}
	}
	return nil
}

func contains(names []string, name string) bool {
	for _, s := range names {
		if s == name {
			return true
		}
	}
	return false
}

func jsonString(from objx.Map) string {
	s, _ := from.JSON()
	return s
}

func objects(from *objx.Value) []objx.Map {
	var values []objx.Map
	switch {
	case from.IsObjxMapSlice():
		return from.ObjxMapSlice()
	case from.IsInterSlice():
		for _, i := range from.InterSlice() {
			if msi, ok := i.(map[string]interface{}); ok {
				values = append(values, objx.Map(msi))
			}
		}
	}
	return values
}

func condition(cv objx.Map, condition string) objx.Map {
	for _, obj := range objects(cv.Get("status.conditions")) {
		if obj.Get("type").String() == condition {
			return obj
		}
	}
	return objx.Map(nil)
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru-client 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 main

import (
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/cmd/tsuru-base"
	"os"
)

const (
	version = "0.10.1"
	header  = "Supported-Tsuru"
)

func buildManager(name string) *cmd.Manager {
	lookup := func(context *cmd.Context) error {
		command := plugin{}
		return command.Run(context, nil)
	}
	m := cmd.BuildBaseManager(name, version, header, lookup)
	m.Register(&tsuru.AppRun{})
	m.Register(&tsuru.AppInfo{})
	m.Register(&AppCreate{})
	m.Register(&AppRemove{})
	m.Register(&UnitAdd{})
	m.Register(&UnitRemove{})
	m.Register(tsuru.AppList{})
	m.Register(&tsuru.AppLog{})
	m.Register(&tsuru.AppGrant{})
	m.Register(&tsuru.AppRevoke{})
	m.Register(&tsuru.AppRestart{})
	m.Register(&tsuru.AppStart{})
	m.Register(&tsuru.AppStop{})
	m.Register(&tsuru.SetCName{})
	m.Register(&tsuru.UnsetCName{})
	m.Register(&tsuru.EnvGet{})
	m.Register(&tsuru.EnvSet{})
	m.Register(&tsuru.EnvUnset{})
	m.Register(&KeyAdd{})
	m.Register(&KeyRemove{})
	m.Register(tsuru.ServiceList{})
	m.Register(tsuru.ServiceAdd{})
	m.Register(tsuru.ServiceRemove{})
	m.Register(tsuru.ServiceDoc{})
	m.Register(tsuru.ServiceInfo{})
	m.Register(tsuru.ServiceInstanceStatus{})
	m.Register(&tsuru.ServiceBind{})
	m.Register(&tsuru.ServiceUnbind{})
	m.Register(platformList{})
	m.Register(&pluginInstall{})
	m.Register(&pluginRemove{})
	m.Register(&pluginList{})
	m.Register(swap{})
	return m
}

func main() {
	name := cmd.ExtractProgramName(os.Args[0])
	manager := buildManager(name)
	manager.Run(os.Args[1:])
}
<commit_msg>tsuru: bump to 0.10.2<commit_after>// Copyright 2014 tsuru-client 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 main

import (
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/cmd/tsuru-base"
	"os"
)

const (
	version = "0.10.2"
	header  = "Supported-Tsuru"
)

func buildManager(name string) *cmd.Manager {
	lookup := func(context *cmd.Context) error {
		command := plugin{}
		return command.Run(context, nil)
	}
	m := cmd.BuildBaseManager(name, version, header, lookup)
	m.Register(&tsuru.AppRun{})
	m.Register(&tsuru.AppInfo{})
	m.Register(&AppCreate{})
	m.Register(&AppRemove{})
	m.Register(&UnitAdd{})
	m.Register(&UnitRemove{})
	m.Register(tsuru.AppList{})
	m.Register(&tsuru.AppLog{})
	m.Register(&tsuru.AppGrant{})
	m.Register(&tsuru.AppRevoke{})
	m.Register(&tsuru.AppRestart{})
	m.Register(&tsuru.AppStart{})
	m.Register(&tsuru.AppStop{})
	m.Register(&tsuru.SetCName{})
	m.Register(&tsuru.UnsetCName{})
	m.Register(&tsuru.EnvGet{})
	m.Register(&tsuru.EnvSet{})
	m.Register(&tsuru.EnvUnset{})
	m.Register(&KeyAdd{})
	m.Register(&KeyRemove{})
	m.Register(tsuru.ServiceList{})
	m.Register(tsuru.ServiceAdd{})
	m.Register(tsuru.ServiceRemove{})
	m.Register(tsuru.ServiceDoc{})
	m.Register(tsuru.ServiceInfo{})
	m.Register(tsuru.ServiceInstanceStatus{})
	m.Register(&tsuru.ServiceBind{})
	m.Register(&tsuru.ServiceUnbind{})
	m.Register(platformList{})
	m.Register(&pluginInstall{})
	m.Register(&pluginRemove{})
	m.Register(&pluginList{})
	m.Register(swap{})
	return m
}

func main() {
	name := cmd.ExtractProgramName(os.Args[0])
	manager := buildManager(name)
	manager.Run(os.Args[1:])
}
<|endoftext|>


<commit_before>package types

import (
	"fmt"
	"reflect"
)

// ToString transforms the given value into
// a string. If the value is a nil pointer
// or a nil interface, it returns the empty
// string.
func ToString(val interface{}) string {
	if val == nil {
		return ""
	}
	v := reflect.ValueOf(val)
	if k := v.Type().Kind(); k == reflect.Ptr || k == reflect.Interface {
		if v.IsNil() {
			return ""
		}
	}
	return fmt.Sprintf("%v", val)
}

// ToInt tries to convert its argument to an integer. It will convert
// bool, uint and its varities, floats and even strings if it can parse
// them.
func ToInt(val interface{}) (int, error) {
	iv := reflect.ValueOf(val)
	if !iv.IsValid() {
		return 0, fmt.Errorf("invalid value")
	}
	v := reflect.Indirect(iv)
	switch v.Kind() {
	case reflect.String:
	case reflect.Bool:
		if v.Bool() {
			return 1, nil
		}
		return 0, nil
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return int(v.Int()), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return int(v.Uint()), nil
	case reflect.Float32, reflect.Float64:
		return int(v.Float()), nil
	}
	return 0, fmt.Errorf("can't convert %v to int", v.Type())
}

// Equal is a shortcut for reflect.DeepEqual.
func Equal(obj1, obj2 interface{}) bool {
	return reflect.DeepEqual(obj1, obj2)
}

// IsTrue returns whether the value is 'true', in the sense of not the zero of its type,
// and whether the value has a meaningful truth value. This function is a copy of the
// one found in text/template
func IsTrue(value interface{}) (truth, ok bool) {
	val := reflect.ValueOf(value)
	if !val.IsValid() {
		// Something like var x interface{}, never set. It's a form of nil.
		return false, true
	}
	switch val.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		truth = val.Len() > 0
	case reflect.Bool:
		truth = val.Bool()
	case reflect.Complex64, reflect.Complex128:
		truth = val.Complex() != 0
	case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
		truth = !val.IsNil()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		truth = val.Int() != 0
	case reflect.Float32, reflect.Float64:
		truth = val.Float() != 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		truth = val.Uint() != 0
	case reflect.Struct:
		truth = true // Struct values are always true.
	default:
		return
	}
	return truth, true
}
<commit_msg>Add some utility functions for testing types<commit_after>package types

import (
	"fmt"
	"reflect"
)

// ToString transforms the given value into
// a string. If the value is a nil pointer
// or a nil interface, it returns the empty
// string.
func ToString(val interface{}) string {
	if val == nil {
		return ""
	}
	v := reflect.ValueOf(val)
	if k := v.Type().Kind(); k == reflect.Ptr || k == reflect.Interface {
		if v.IsNil() {
			return ""
		}
	}
	return fmt.Sprintf("%v", val)
}

// ToInt tries to convert its argument to an integer. It will convert
// bool, uint and its varities, floats and even strings if it can parse
// them.
func ToInt(val interface{}) (int, error) {
	iv := reflect.ValueOf(val)
	if !iv.IsValid() {
		return 0, fmt.Errorf("invalid value")
	}
	v := reflect.Indirect(iv)
	switch v.Kind() {
	case reflect.String:
	case reflect.Bool:
		if v.Bool() {
			return 1, nil
		}
		return 0, nil
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return int(v.Int()), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return int(v.Uint()), nil
	case reflect.Float32, reflect.Float64:
		return int(v.Float()), nil
	}
	return 0, fmt.Errorf("can't convert %v to int", v.Type())
}

// Equal is a shortcut for reflect.DeepEqual.
func Equal(obj1, obj2 interface{}) bool {
	return reflect.DeepEqual(obj1, obj2)
}

// IsTrue returns whether the value is 'true', in the sense of not the zero of its type,
// and whether the value has a meaningful truth value. This function is a copy of the
// one found in text/template
func IsTrue(value interface{}) (truth, ok bool) {
	val := reflect.ValueOf(value)
	if !val.IsValid() {
		// Something like var x interface{}, never set. It's a form of nil.
		return false, true
	}
	switch val.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		truth = val.Len() > 0
	case reflect.Bool:
		truth = val.Bool()
	case reflect.Complex64, reflect.Complex128:
		truth = val.Complex() != 0
	case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
		truth = !val.IsNil()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		truth = val.Int() != 0
	case reflect.Float32, reflect.Float64:
		truth = val.Float() != 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		truth = val.Uint() != 0
	case reflect.Struct:
		truth = true // Struct values are always true.
	default:
		return
	}
	return truth, true
}

// IsInt returns true iff typ is any of the int types.
func IsInt(typ reflect.Type) bool {
	k := typ.Kind()
	return k == reflect.Int || k == reflect.Int8 || k == reflect.Int16 || k == reflect.Int32 || k == reflect.Int64
}

// IsUint returns true iff typ is any of the uint types.
func IsUint(typ reflect.Type) bool {
	k := typ.Kind()
	return k == reflect.Uint || k == reflect.Uint8 || k == reflect.Uint16 || k == reflect.Uint32 || k == reflect.Uint64
}

// IsFloat returns true iff typ is any of the float types.
func IsFloat(typ reflect.Type) bool {
	k := typ.Kind()
	return k == reflect.Float32 || k == reflect.Float64
}
<|endoftext|>


<commit_before>/*
 * Copyright 2020 The Knative 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 prober

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"path"
	"runtime"
	"text/template"
	"time"

	"github.com/kelseyhightower/envconfig"
	"github.com/wavesoftware/go-ensure"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1"
	testlib "knative.dev/eventing/test/lib"
	"knative.dev/eventing/test/lib/duck"
	"knative.dev/eventing/test/lib/resources"
	"knative.dev/pkg/apis"
)

const (
	defaultConfigName          = "wathola-config"
	defaultConfigHomedirPath   = ".config/wathola"
	defaultHomedir             = "/home/nonroot"
	defaultConfigFilename      = "config.toml"
	defaultWatholaEventsPrefix = "com.github.cardil.wathola"
	defaultBrokerName          = "default"
	defaultHealthEndpoint      = "/healthz"
	defaultFinishedSleep       = 5 * time.Second

	Silence DuplicateAction = "silence"
	Warn    DuplicateAction = "warn"
	Error   DuplicateAction = "error"
)

// DuplicateAction is the action to take in case of duplicated events
type DuplicateAction string

var eventTypes = []string{"step", "finished"}

// Config represents a configuration for prober.
type Config struct {
	Wathola
	Namespace     string
	Interval      time.Duration
	FinishedSleep time.Duration
	Serving       ServingConfig
	FailOnErrors  bool
	OnDuplicate   DuplicateAction
}

// Wathola represents options related strictly to wathola testing tool.
type Wathola struct {
	ConfigMap
	EventsTypePrefix string
	HealthEndpoint   string
	BrokerName       string
}

// ConfigMap represents options of wathola config toml file.
type ConfigMap struct {
	ConfigMapName    string
	ConfigMountPoint string
	ConfigFilename   string
}

// ServingConfig represents a options for serving test component (wathola-forwarder).
type ServingConfig struct {
	Use         bool
	ScaleToZero bool
}

// NewConfig creates a new configuration object with default values filled in.
// Values can be influenced by kelseyhightower/envconfig with
// `e2e_upgrade_tests` prefix.
func NewConfig(namespace string) *Config {
	config := &Config{
		Namespace:     "",
		Interval:      Interval,
		FinishedSleep: defaultFinishedSleep,
		FailOnErrors:  true,
		OnDuplicate:   Warn,
		Serving: ServingConfig{
			Use:         false,
			ScaleToZero: true,
		},
		Wathola: Wathola{
			ConfigMap: ConfigMap{
				ConfigMapName:    defaultConfigName,
				ConfigMountPoint: fmt.Sprintf("%s/%s", defaultHomedir, defaultConfigHomedirPath),
				ConfigFilename:   defaultConfigFilename,
			},
			EventsTypePrefix: defaultWatholaEventsPrefix,
			HealthEndpoint:   defaultHealthEndpoint,
			BrokerName:       defaultBrokerName,
		},
	}

	// FIXME: remove while fixing https://github.com/knative/eventing/issues/2665
	config.FailOnErrors = false

	err := envconfig.Process("e2e_upgrade_tests", config)
	ensure.NoError(err)
	config.Namespace = namespace
	return config
}

func (p *prober) deployConfiguration() {
	p.deployBroker()
	p.deployConfigMap()
	p.deployTriggers()
}

func (p *prober) deployBroker() {
	p.client.CreateBrokerV1Beta1OrFail(p.config.BrokerName)
}

func (p *prober) fetchBrokerURL() (*apis.URL, error) {
	namespace := p.config.Namespace
	p.log.Debugf("Fetching %s broker URL for ns %s",
		p.config.BrokerName, namespace)
	meta := resources.NewMetaResource(
		p.config.BrokerName, p.config.Namespace, testlib.BrokerTypeMeta,
	)
	err := duck.WaitForResourceReady(p.client.Dynamic, meta)
	if err != nil {
		return nil, err
	}
	broker, err := p.client.Eventing.EventingV1beta1().Brokers(namespace).Get(
		context.Background(), p.config.BrokerName, metav1.GetOptions{},
	)
	if err != nil {
		return nil, err
	}
	url := broker.Status.Address.URL
	p.log.Debugf("%s broker URL for ns %s is %v",
		p.config.BrokerName, namespace, url)
	return url, nil
}

func (p *prober) deployConfigMap() {
	name := p.config.ConfigMapName
	p.log.Infof("Deploying config map: \"%s/%s\"", p.config.Namespace, name)
	brokerURL, err := p.fetchBrokerURL()
	ensure.NoError(err)
	configData := p.compileTemplate(p.config.ConfigFilename, brokerURL)
	p.client.CreateConfigMapOrFail(name, p.config.Namespace, map[string]string{
		p.config.ConfigFilename: configData,
	})
}

func (p *prober) deployTriggers() {
	for _, eventType := range eventTypes {
		name := fmt.Sprintf("wathola-trigger-%v", eventType)
		fullType := fmt.Sprintf("%v.%v", p.config.EventsTypePrefix, eventType)
		subscriberOption := resources.WithSubscriberServiceRefForTriggerV1Beta1(receiverName)
		if p.config.Serving.Use {
			subscriberOption = resources.WithSubscriberKServiceRefForTrigger(forwarderName)
		}
		p.client.CreateTriggerOrFailV1Beta1(name,
			resources.WithBrokerV1Beta1(p.config.BrokerName),
			resources.WithAttributesTriggerFilterV1Beta1(
				eventingv1beta1.TriggerAnyFilter,
				fullType,
				map[string]interface{}{},
			),
			subscriberOption,
		)
	}
}

func (p *prober) compileTemplate(templateName string, brokerURL fmt.Stringer) string {
	_, filename, _, _ := runtime.Caller(0)
	templateFilepath := path.Join(path.Dir(filename), templateName)
	templateBytes, err := ioutil.ReadFile(templateFilepath)
	ensure.NoError(err)
	tmpl, err := template.New(templateName).Parse(string(templateBytes))
	ensure.NoError(err)
	var buff bytes.Buffer
	data := struct {
		*Config
		BrokerURL string
	}{
		p.config,
		brokerURL.String(),
	}
	ensure.NoError(tmpl.Execute(&buff, data))
	return buff.String()
}
<commit_msg>feat/upgrade-test: Add configurable broker deployment options (#4616)<commit_after>/*
 * Copyright 2020 The Knative 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 prober

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"path"
	"runtime"
	"text/template"
	"time"

	"github.com/kelseyhightower/envconfig"
	"github.com/wavesoftware/go-ensure"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1"
	testlib "knative.dev/eventing/test/lib"
	"knative.dev/eventing/test/lib/duck"
	"knative.dev/eventing/test/lib/resources"
	"knative.dev/pkg/apis"
)

const (
	defaultConfigName          = "wathola-config"
	defaultConfigHomedirPath   = ".config/wathola"
	defaultHomedir             = "/home/nonroot"
	defaultConfigFilename      = "config.toml"
	defaultWatholaEventsPrefix = "com.github.cardil.wathola"
	defaultBrokerName          = "default"
	defaultHealthEndpoint      = "/healthz"
	defaultFinishedSleep       = 5 * time.Second

	Silence DuplicateAction = "silence"
	Warn    DuplicateAction = "warn"
	Error   DuplicateAction = "error"
)

// DuplicateAction is the action to take in case of duplicated events
type DuplicateAction string

var eventTypes = []string{"step", "finished"}

// Config represents a configuration for prober.
type Config struct {
	Wathola
	Namespace     string
	Interval      time.Duration
	FinishedSleep time.Duration
	Serving       ServingConfig
	FailOnErrors  bool
	OnDuplicate   DuplicateAction
	BrokerOpts    []resources.BrokerV1Beta1Option
}

// Wathola represents options related strictly to wathola testing tool.
type Wathola struct {
	ConfigMap
	EventsTypePrefix string
	HealthEndpoint   string
	BrokerName       string
}

// ConfigMap represents options of wathola config toml file.
type ConfigMap struct {
	ConfigMapName    string
	ConfigMountPoint string
	ConfigFilename   string
}

// ServingConfig represents a options for serving test component (wathola-forwarder).
type ServingConfig struct {
	Use         bool
	ScaleToZero bool
}

// NewConfig creates a new configuration object with default values filled in.
// Values can be influenced by kelseyhightower/envconfig with
// `e2e_upgrade_tests` prefix.
func NewConfig(namespace string) *Config {
	config := &Config{
		Namespace:     "",
		Interval:      Interval,
		FinishedSleep: defaultFinishedSleep,
		FailOnErrors:  true,
		OnDuplicate:   Warn,
		BrokerOpts:    make([]resources.BrokerV1Beta1Option, 0),
		Serving: ServingConfig{
			Use:         false,
			ScaleToZero: true,
		},
		Wathola: Wathola{
			ConfigMap: ConfigMap{
				ConfigMapName:    defaultConfigName,
				ConfigMountPoint: fmt.Sprintf("%s/%s", defaultHomedir, defaultConfigHomedirPath),
				ConfigFilename:   defaultConfigFilename,
			},
			EventsTypePrefix: defaultWatholaEventsPrefix,
			HealthEndpoint:   defaultHealthEndpoint,
			BrokerName:       defaultBrokerName,
		},
	}

	// FIXME: remove while fixing https://github.com/knative/eventing/issues/2665
	config.FailOnErrors = false

	err := envconfig.Process("e2e_upgrade_tests", config)
	ensure.NoError(err)
	config.Namespace = namespace
	return config
}

func (p *prober) deployConfiguration() {
	p.deployBroker()
	p.deployConfigMap()
	p.deployTriggers()
}

func (p *prober) deployBroker() {
	p.client.CreateBrokerV1Beta1OrFail(p.config.BrokerName, p.config.BrokerOpts...)
}

func (p *prober) fetchBrokerURL() (*apis.URL, error) {
	namespace := p.config.Namespace
	p.log.Debugf("Fetching %s broker URL for ns %s",
		p.config.BrokerName, namespace)
	meta := resources.NewMetaResource(
		p.config.BrokerName, p.config.Namespace, testlib.BrokerTypeMeta,
	)
	err := duck.WaitForResourceReady(p.client.Dynamic, meta)
	if err != nil {
		return nil, err
	}
	broker, err := p.client.Eventing.EventingV1beta1().Brokers(namespace).Get(
		context.Background(), p.config.BrokerName, metav1.GetOptions{},
	)
	if err != nil {
		return nil, err
	}
	url := broker.Status.Address.URL
	p.log.Debugf("%s broker URL for ns %s is %v",
		p.config.BrokerName, namespace, url)
	return url, nil
}

func (p *prober) deployConfigMap() {
	name := p.config.ConfigMapName
	p.log.Infof("Deploying config map: \"%s/%s\"", p.config.Namespace, name)
	brokerURL, err := p.fetchBrokerURL()
	ensure.NoError(err)
	configData := p.compileTemplate(p.config.ConfigFilename, brokerURL)
	p.client.CreateConfigMapOrFail(name, p.config.Namespace, map[string]string{
		p.config.ConfigFilename: configData,
	})
}

func (p *prober) deployTriggers() {
	for _, eventType := range eventTypes {
		name := fmt.Sprintf("wathola-trigger-%v", eventType)
		fullType := fmt.Sprintf("%v.%v", p.config.EventsTypePrefix, eventType)
		subscriberOption := resources.WithSubscriberServiceRefForTriggerV1Beta1(receiverName)
		if p.config.Serving.Use {
			subscriberOption = resources.WithSubscriberKServiceRefForTrigger(forwarderName)
		}
		p.client.CreateTriggerOrFailV1Beta1(name,
			resources.WithBrokerV1Beta1(p.config.BrokerName),
			resources.WithAttributesTriggerFilterV1Beta1(
				eventingv1beta1.TriggerAnyFilter,
				fullType,
				map[string]interface{}{},
			),
			subscriberOption,
		)
	}
}

func (p *prober) compileTemplate(templateName string, brokerURL fmt.Stringer) string {
	_, filename, _, _ := runtime.Caller(0)
	templateFilepath := path.Join(path.Dir(filename), templateName)
	templateBytes, err := ioutil.ReadFile(templateFilepath)
	ensure.NoError(err)
	tmpl, err := template.New(templateName).Parse(string(templateBytes))
	ensure.NoError(err)
	var buff bytes.Buffer
	data := struct {
		*Config
		BrokerURL string
	}{
		p.config,
		brokerURL.String(),
	}
	ensure.NoError(tmpl.Execute(&buff, data))
	return buff.String()
}
<|endoftext|>


<commit_before>package vbox

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/chrootarchive"
	"github.com/docker/docker/pkg/ioutils"
	"github.com/golang/glog"
	"github.com/hyperhq/hyperd/utils"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
	"github.com/hyperhq/runv/lib/govbox"
)

func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) {
	// Mount the root filesystem so we can apply the diff/layer.
	layerFs, err := d.Get(id, "")
	if err != nil {
		return 0, err
	}
	defer d.Put(id)

	start := time.Now().UTC()
	glog.V(1).Info("Start untar layer")
	if size, err = chrootarchive.ApplyLayer(layerFs, diff); err != nil {
		return 0, err
	}
	glog.V(1).Infof("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
	root := path.Join(utils.HYPER_ROOT, "vbox")
	idDisk := fmt.Sprintf("%s/images/%s.vdi", root, id)
	if _, err = os.Stat(idDisk); err != nil {
		return 0, err
	}
	if err = d.VmMountLayer(id); err != nil {
		return 0, err
	}
	// XXX should remove the image/container's directory
	return size, err
}

func (d *Driver) Diff(id, parent string) (diff archive.Archive, err error) {
	if d.daemon == nil {
		if err := d.Setup(); err != nil {
			return nil, err
		}
	}
	var (
		podData string
		tgtDisk string = ""
		code    int
		cause   string
	)
	srcDisk := fmt.Sprintf("%s/images/%s.vdi", d.RootPath(), id)
	if parent != "" {
		tgtDisk = fmt.Sprintf("%s/images/%s.vdi", d.RootPath(), parent)
	}
	outDir := path.Join(utils.HYPER_ROOT, "tar")
	if err := os.MkdirAll(outDir, 0755); err != nil {
		return nil, err
	}
	uuid, err := virtualbox.GetMediumUUID(srcDisk)
	if err == nil {
		srcDisk = uuid
	}
	// create pod
	podId := "diff-" + id[:10]
	podData, err = MakeDiffPod(podId, "puller:latest", id, srcDisk, tgtDisk, outDir)
	if err != nil {
		return nil, err
	}

	// start or replace pod
	vm, ok := d.daemon.VmList[d.pullVm]
	if !ok {
		return nil, fmt.Errorf("can not find VM(%s)", d.pullVm)
	}
	if vm.Status == types.S_VM_IDLE {
		p, err := daemon.CreatePod(podId, podData)
		if err != nil {
			glog.Errorf("can not create pod %s", podData)
			return nil, err
		}
		code, cause, err = d.daemon.StartInternal(p, d.pullVm, nil, false, types.VM_KEEP_AFTER_SHUTDOWN, []*hypervisor.TtyIO{})
		if err != nil {
			glog.Errorf("Code is %d, Cause is %s, %s", code, cause, err.Error())
			d.daemon.KillVm(d.pullVm)
			return nil, err
		}
		vm := d.daemon.VmList[d.pullVm]
		// wait for cmd finish
		Status, err := vm.GetResponseChan()
		if err != nil {
			glog.Error(err.Error())
			return nil, err
		}
		defer vm.ReleaseResponseChan(Status)

		var vmResponse *types.VmResponse
		for {
			vmResponse = <-Status
			if vmResponse.VmId == d.pullVm {
				if vmResponse.Code == types.E_POD_FINISHED {
					glog.Infof("Got E_POD_FINISHED code response")
					break
				}
			}
		}

		p.SetVM(d.pullVm, vm)

		// release pod from VM
		code, cause, err = d.daemon.StopPod(podId, "no")
		if err != nil {
			glog.Errorf("Code is %d, Cause is %s, %s", code, cause, err.Error())
			d.daemon.KillVm(d.pullVm)
			return nil, err
		}
		d.daemon.CleanPod(podId)
	} else {
		glog.Errorf("pull vm should not be associated")
		return nil, fmt.Errorf("pull vm is not idle")
	}

	tarFile := outDir + "/" + id + ".tar"
	if _, err := os.Stat(tarFile); err != nil {
		// If the parent is nil, the first layer is also nil.
		// So we may not got tar file
		if parent == "" {
			layerFs := fmt.Sprintf("%s/diff/%s", d.RootPath(), id)
			archive, err := archive.Tar(layerFs, archive.Uncompressed)
			if err != nil {
				return nil, err
			}
			return ioutils.NewReadCloserWrapper(archive, func() error {
				err := archive.Close()
				return err
			}), nil
		} else {
			return nil, fmt.Errorf("the out tar file is not exist")
		}
	}
	f, err := os.Open(tarFile)
	if err != nil {
		return nil, err
	}
	var archive io.ReadCloser
	archive = ioutil.NopCloser(f)
	glog.Infof("Diff between %s and %s", id, parent)
	return ioutils.NewReadCloserWrapper(archive, func() error {
		err := archive.Close()
		return err
	}), nil
}

func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
	return nil, nil
}

func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
	return 0, nil
}
<commit_msg>vbox: use new vm to run diff pod<commit_after>package vbox

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/chrootarchive"
	"github.com/docker/docker/pkg/ioutils"
	"github.com/golang/glog"
	"github.com/hyperhq/hyperd/utils"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
	"github.com/hyperhq/runv/lib/govbox"
)

func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) {
	// Mount the root filesystem so we can apply the diff/layer.
	layerFs, err := d.Get(id, "")
	if err != nil {
		return 0, err
	}
	defer d.Put(id)

	start := time.Now().UTC()
	glog.V(1).Info("Start untar layer")
	if size, err = chrootarchive.ApplyLayer(layerFs, diff); err != nil {
		return 0, err
	}
	glog.V(1).Infof("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
	root := path.Join(utils.HYPER_ROOT, "vbox")
	idDisk := fmt.Sprintf("%s/images/%s.vdi", root, id)
	if _, err = os.Stat(idDisk); err != nil {
		return 0, err
	}
	if err = d.VmMountLayer(id); err != nil {
		return 0, err
	}
	// XXX should remove the image/container's directory
	return size, err
}

func (d *Driver) Diff(id, parent string) (diff archive.Archive, err error) {
	if d.daemon == nil {
		if err := d.Setup(); err != nil {
			return nil, err
		}
	}
	var (
		podData string
		tgtDisk string = ""
		code    int
		cause   string
	)
	srcDisk := fmt.Sprintf("%s/images/%s.vdi", d.RootPath(), id)
	if parent != "" {
		tgtDisk = fmt.Sprintf("%s/images/%s.vdi", d.RootPath(), parent)
	}
	outDir := path.Join(utils.HYPER_ROOT, "tar")
	if err := os.MkdirAll(outDir, 0755); err != nil {
		return nil, err
	}
	uuid, err := virtualbox.GetMediumUUID(srcDisk)
	if err == nil {
		srcDisk = uuid
	}
	// create pod
	podId := "diff-" + id[:10]
	podData, err = MakeDiffPod(podId, "puller:latest", id, srcDisk, tgtDisk, outDir)
	if err != nil {
		return nil, err
	}

	p, err := daemon.CreatePod(podId, podData)
	if err != nil {
		glog.Errorf("can not create pod %s", podData)
		return nil, err
	}
	defer d.daemon.CleanPod(podId)

	// start vm
	vmId := fmt.Sprintf("%s-%s", d.pullVm, utils.RandStr(10, "alpha"))
	vm, err := d.daemon.StartVm(vmId, 1, 64, false, types.VM_KEEP_NONE)
	if err != nil {
		glog.Errorf(err.Error())
		return nil, err
	}

	// wait for cmd finish
	Status, err := vm.GetResponseChan()
	if err != nil {
		glog.Error(err.Error())
		d.daemon.KillVm(vmId)
		return nil, err
	}
	defer vm.ReleaseResponseChan(Status)

	code, cause, err = d.daemon.StartInternal(p, vmId, nil, false, types.VM_KEEP_NONE, []*hypervisor.TtyIO{})
	if err != nil {
		glog.Errorf("Code is %d, Cause is %s, %s", code, cause, err.Error())
		d.daemon.KillVm(vmId)
		return nil, err
	}

	var vmResponse *types.VmResponse
	for {
		vmResponse = <-Status
		if vmResponse.VmId == vmId {
			if vmResponse.Code == types.E_VM_SHUTDOWN {
				glog.Infof("vm shutdown")
				break
			}
		}
	}

	tarFile := outDir + "/" + id + ".tar"
	if _, err := os.Stat(tarFile); err != nil {
		// If the parent is nil, the first layer is also nil.
		// So we may not got tar file
		if parent == "" {
			layerFs := fmt.Sprintf("%s/diff/%s", d.RootPath(), id)
			archive, err := archive.Tar(layerFs, archive.Uncompressed)
			if err != nil {
				return nil, err
			}
			return ioutils.NewReadCloserWrapper(archive, func() error {
				err := archive.Close()
				return err
			}), nil
		} else {
			return nil, fmt.Errorf("the out tar file is not exist")
		}
	}
	f, err := os.Open(tarFile)
	if err != nil {
		return nil, err
	}
	var archive io.ReadCloser
	archive = ioutil.NopCloser(f)
	glog.Infof("Diff between %s and %s", id, parent)
	return ioutils.NewReadCloserWrapper(archive, func() error {
		err := archive.Close()
		return err
	}), nil
}

func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
	return nil, nil
}

func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
	return 0, nil
}
<|endoftext|>


<commit_before>package codeutilsShared

// Structure of Project configuration
type ProjectConfig struct {
	Name         string
	ContentTypes []string
	Go           GoCompilerOptions         // Go key maps to GoCompilerOptions struct
	HTML         HTMLCompilerOptions       // HTML maps to HTMLCompilerOptions struct
	LESS         LESSCompilerOptions       // LESS key maps to LessCompilerOptions struct
	TypeScript   TypeScriptCompilerOptions // TypeScript maps to TypeScriptCompilerOptions struct
	UsesTests    string
}

// #region Go

// Options available for compiling the entire Go source tree
type GoCompilerOptions struct {
	CompileFromRoot bool                       // Defaults to true, compiles everything in root into binary
	Branches        map[string]GoBranchOptions // Maps each string to a GoBranchOptions struct
}

// Options available for compiling individual branches in the Go source tree
type GoBranchOptions struct {
	BinaryName       string   // Name of the binary to output (will default to project name)
	DoInstall        bool     // Set DoInstall as a bool (BinaryName will be ignored) Implies this branch is a go package.
	SourcesToInclude []string // Sources to include, you can be very selective here
}

// #endregion

// #region HTML

// Options available for "compile" HTML
type HTMLCompilerOptions struct {
	Compress bool // Defaults to true, despite bool defaults to false
	CopyAll  bool // Defaults to true, copies everything in src/html
}

// #endregion

// #region LESS

// Options available for compiling the entire LESS source tree
type LESSCompilerOptions struct {
	CompileFromRoot bool                         // Defaults to true, compiles everything in root into binary
	Branches        map[string]LESSBranchOptions // Maps each string to a LESSBranchOptions struct
}

// Options available for compiling individual branches in the LESS source tree
type LESSBranchOptions struct {
	AdditionalCompileOptions []string // Additional compile options to pass to lessc
	FileName                 string   // Name of the file of the outputted CSS
	UniqueHash               bool     // Define UniqueHash as a bool, determines whether we should append a fragment of the hash of the file to the file name
	UseGlob                  bool
}

// #endregion

// #region TypeScript

// Options available for compiling TypeScript
type TypeScriptCompilerOptions struct {
	LibreJSLicense    string // Define LibreJSLicense as the valid Spdx license that is also valid with LibreJS Gopher / LibreJS
	MinifyContent     bool   // Defaults to true, compiles using Google closure compiler
	OutputDeclaration bool   // Defaults to false, like a normal bool type
	RemoveComments    bool   // Defaults to true
	Target            string // Defaults to ES5 - ES3 and ES6 should be options via TS
	UniqueHash        bool   // Define UniqueHash as a bool, determines whether we should append a fragment of the hash of the file to the file name
	UseLibreJSHeader  bool   // Define LibreJSHeader, adds compliant headers to JS code
}

// #endregion
<commit_msg>Drop HTML compile options and RemoveComments option from TypeScriptCompilerOptions.<commit_after>package codeutilsShared

// Structure of Project configuration
type ProjectConfig struct {
	Name         string
	ContentTypes []string
	Go           GoCompilerOptions         // Go key maps to GoCompilerOptions struct
	LESS         LESSCompilerOptions       // LESS key maps to LessCompilerOptions struct
	TypeScript   TypeScriptCompilerOptions // TypeScript maps to TypeScriptCompilerOptions struct
	UsesTests    string
}

// #region Go

// Options available for compiling the entire Go source tree
type GoCompilerOptions struct {
	CompileFromRoot bool                       // Defaults to true, compiles everything in root into binary
	Branches        map[string]GoBranchOptions // Maps each string to a GoBranchOptions struct
}

// Options available for compiling individual branches in the Go source tree
type GoBranchOptions struct {
	BinaryName       string   // Name of the binary to output (will default to project name)
	DoInstall        bool     // Set DoInstall as a bool (BinaryName will be ignored) Implies this branch is a go package.
	SourcesToInclude []string // Sources to include, you can be very selective here
}

// #endregion

// #region LESS

// Options available for compiling the entire LESS source tree
type LESSCompilerOptions struct {
	CompileFromRoot bool                         // Defaults to true, compiles everything in root into binary
	Branches        map[string]LESSBranchOptions // Maps each string to a LESSBranchOptions struct
}

// Options available for compiling individual branches in the LESS source tree
type LESSBranchOptions struct {
	AdditionalCompileOptions []string // Additional compile options to pass to lessc
	FileName                 string   // Name of the file of the outputted CSS
	UniqueHash               bool     // Define UniqueHash as a bool, determines whether we should append a fragment of the hash of the file to the file name
	UseGlob                  bool
}

// #endregion

// #region TypeScript

// Options available for compiling TypeScript
type TypeScriptCompilerOptions struct {
	LibreJSLicense    string // Define LibreJSLicense as the valid Spdx license that is also valid with LibreJS Gopher / LibreJS
	MinifyContent     bool   // Defaults to true, compiles using Google closure compiler
	OutputDeclaration bool   // Defaults to false, like a normal bool type
	Target            string // Defaults to ES5 - ES3 and ES6 should be options via TS
	UniqueHash        bool   // Define UniqueHash as a bool, determines whether we should append a fragment of the hash of the file to the file name
	UseLibreJSHeader  bool   // Define LibreJSHeader, adds compliant headers to JS code
}

// #endregion
<|endoftext|>


<commit_before>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package stylish

import (
	"fmt"
	"strings"

	"github.com/mitchellh/go-wordwrap"
)

// ProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessStart "i am a process"
//
// Output:
// + I am a process ------------------------------------------------------------ >
func ProcessStart(msg string, v ...interface{}) string {

	maxLen := 80
	subLen := len(fmt.Sprintf("+ %s%s >", fmt.Sprintf(msg, v...)))

	process := fmt.Sprintf(msg, v...)

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("%s\n", fmt.Sprintf("+ %s %s >", process, strings.Repeat("-", (maxLen-subLen))))
}

// NestedProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
// with a nested prefix according to the level specified
func NestedProcessStart(msg string, level int) string {
	return fmt.Sprintf("%s%s", GenerateNestedPrefix(level), ProcessStart(msg))

	maxLen := 80 - level
	subLen := len(fmt.Sprintf("+ %s%s >", fmt.Sprintf(msg, v...)))

	process := fmt.Sprintf(msg, v...)

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("%s\n", fmt.Sprintf("%s+ %s %s >", GenerateNestedPrefix(level), process, strings.Repeat("-", (maxLen-subLen))))
}

// ProcessEnd styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessEnd
//
// Output:
// <new line>
func ProcessEnd() string {
	return fmt.Sprintf("\n")
}

// Marker is the root for Bullet/SubBullet; used alone, it allows for a custom
// mark to be specified
//
// Usage:
// Maker "*",  "i am a marker"
//
// Output:
// * i am a marker
func Marker(mark, msg string, v ...interface{}) string {
	return fmt.Sprintf("%s %s\n", mark, fmt.Sprintf(msg, v...))
}

// Bullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// Bullet "i am a bullet"
//
// Output:
// + i am a bullet
func Bullet(msg string, v ...interface{}) string {
	return Marker("+", fmt.Sprintf(msg, v...))
}

// NestedBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
// with a nested prefix according to the level specified
func NestedBullet(msg string, level int) string {
	return fmt.Sprintf("%s%s", GenerateNestedPrefix(level), Bullet(msg))
}

// SubBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// SubBullet "i am a sub bullet"
//
// Output:
//   i am a sub bullet
func SubBullet(msg string, v ...interface{}) string {
	return Marker(" ", fmt.Sprintf(msg, v...))
}

// Warning styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#warning
//
// Usage:
// Warning "You just bought Hot Pockets!"
//
// Output:
// -----------------------------  WARNING  -----------------------------
// You just bought Hot Pockets!
func Warning(body string, v ...interface{}) string {
	return fmt.Sprintf(`
----------------------------------  WARNING  ----------------------------------
%s
`, wordwrap.WrapString(fmt.Sprintf(body, v...), 70))
}

// ErrorHead styles and prints an error heading as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorHead "nuclear launch detected"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
func ErrorHead(heading string, v ...interface{}) string {
	return fmt.Sprintf("\n! %s !\n", strings.ToUpper(fmt.Sprintf(heading, v...)))
}

// ErrorBody styles and prints an error body as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorBody "All your base are belong to us"
//
// Output:
// All your base are belong to us
func ErrorBody(body string, v ...interface{}) string {
	return fmt.Sprintf("%s\n", wordwrap.WrapString(fmt.Sprintf(body, v...), 80))
}

// Error styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// Error "nuclear launch detected", "All your base are belong to us"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
//
// All your base are belong to us
func Error(heading, body string) string {
	return fmt.Sprintf("%s%s", ErrorHead(heading), ErrorBody(body))
}

// GenerateNestedPrefix will generate a prefix string of spaces to match the
// specified depth level
func GenerateNestedPrefix(level int) string {
	prefix := ""

	for i := 0; i < level; i++ {
		prefix += "  "
	}

	return prefix
}
<commit_msg>Add elipses for options arguments<commit_after>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package stylish

import (
	"fmt"
	"strings"

	"github.com/mitchellh/go-wordwrap"
)

// ProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessStart "i am a process"
//
// Output:
// + I am a process ------------------------------------------------------------ >
func ProcessStart(msg string, v ...interface{}) string {

	maxLen := 80
	subLen := len(fmt.Sprintf("+ %s%s >", fmt.Sprintf(msg, v...)))

	process := fmt.Sprintf(msg, v...)

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("%s\n", fmt.Sprintf("+ %s %s >", process, strings.Repeat("-", (maxLen-subLen))))
}

// NestedProcessStart styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
// with a nested prefix according to the level specified
func NestedProcessStart(msg string, level int, v ...interface{}) string {
	return fmt.Sprintf("%s%s", GenerateNestedPrefix(level), ProcessStart(msg))

	maxLen := 80 - level
	subLen := len(fmt.Sprintf("+ %s%s >", fmt.Sprintf(msg, v...)))

	process := fmt.Sprintf(msg, v...)

	// print process, inserting a '-' (colon) 'n' times, where 'n' is the number
	// remaining after subtracting subLen (number of 'reserved' characters) from
	// maxLen (maximum number of allowed characters)
	return fmt.Sprintf("%s\n", fmt.Sprintf("%s+ %s %s >", GenerateNestedPrefix(level), process, strings.Repeat("-", (maxLen-subLen))))
}

// ProcessEnd styles and prints a 'child process' as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#child-process
//
// Usage:
// ProcessEnd
//
// Output:
// <new line>
func ProcessEnd() string {
	return fmt.Sprintf("\n")
}

// Marker is the root for Bullet/SubBullet; used alone, it allows for a custom
// mark to be specified
//
// Usage:
// Maker "*",  "i am a marker"
//
// Output:
// * i am a marker
func Marker(mark, msg string, v ...interface{}) string {
	return fmt.Sprintf("%s %s\n", mark, fmt.Sprintf(msg, v...))
}

// Bullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// Bullet "i am a bullet"
//
// Output:
// + i am a bullet
func Bullet(msg string, v ...interface{}) string {
	return Marker("+", fmt.Sprintf(msg, v...))
}

// NestedBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
// with a nested prefix according to the level specified
func NestedBullet(msg string, level int) string {
	return fmt.Sprintf("%s%s", GenerateNestedPrefix(level), Bullet(msg))
}

// SubBullet styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#bullet-points
//
// Usage:
// SubBullet "i am a sub bullet"
//
// Output:
//   i am a sub bullet
func SubBullet(msg string, v ...interface{}) string {
	return Marker(" ", fmt.Sprintf(msg, v...))
}

// Warning styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#warning
//
// Usage:
// Warning "You just bought Hot Pockets!"
//
// Output:
// -----------------------------  WARNING  -----------------------------
// You just bought Hot Pockets!
func Warning(body string, v ...interface{}) string {
	return fmt.Sprintf(`
----------------------------------  WARNING  ----------------------------------
%s
`, wordwrap.WrapString(fmt.Sprintf(body, v...), 70))
}

// ErrorHead styles and prints an error heading as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorHead "nuclear launch detected"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
func ErrorHead(heading string, v ...interface{}) string {
	return fmt.Sprintf("\n! %s !\n", strings.ToUpper(fmt.Sprintf(heading, v...)))
}

// ErrorBody styles and prints an error body as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// ErrorBody "All your base are belong to us"
//
// Output:
// All your base are belong to us
func ErrorBody(body string, v ...interface{}) string {
	return fmt.Sprintf("%s\n", wordwrap.WrapString(fmt.Sprintf(body, v...), 80))
}

// Error styles and prints a message as outlined at:
// http://nanodocs.gopagoda.io/engines/style-guide#fatal_errors
//
// Usage:
// Error "nuclear launch detected", "All your base are belong to us"
//
// Output:
// ! NUCLEAR LAUNCH DETECTED !
//
// All your base are belong to us
func Error(heading, body string) string {
	return fmt.Sprintf("%s%s", ErrorHead(heading), ErrorBody(body))
}

// GenerateNestedPrefix will generate a prefix string of spaces to match the
// specified depth level
func GenerateNestedPrefix(level int) string {
	prefix := ""

	for i := 0; i < level; i++ {
		prefix += "  "
	}

	return prefix
}
<|endoftext|>