<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package tlf

import (
	"errors"
	"reflect"
	"sort"

	"github.com/keybase/client/go/protocol/keybase1"
)

// Handle uniquely identified top-level folders by readers and writers.
//
// NOTE: if you change this type, ensure you update the `NumField` check in
// `MakeHandle`, and that the new fields are correctly checked for equality in
// `DeepEqual`.
// TODO: Have separate types for writers vs. readers.
type Handle struct {
	Writers           []keybase1.UserOrTeamID    `codec:"w,omitempty"`
	Readers           []keybase1.UserOrTeamID    `codec:"r,omitempty"`
	UnresolvedWriters []keybase1.SocialAssertion `codec:"uw,omitempty"`
	UnresolvedReaders []keybase1.SocialAssertion `codec:"ur,omitempty"`
	ConflictInfo      *HandleExtension           `codec:"ci,omitempty"`
	FinalizedInfo     *HandleExtension           `codec:"fi,omitempty"`

	// caching field to track whether we've sorted the slices.
	sorted bool `codec:"-"`
}

// errNoWriters is the error returned by MakeHandle if it is
// passed an empty list of writers.
var errNoWriters = errors.New("Cannot make TLF handle with no writers; need rekey?")

// errInvalidWriter is the error returned by MakeHandle if it
// is passed an invalid writer.
var errInvalidWriter = errors.New("Cannot make TLF handle with invalid writer")

// errInvalidReader is the error returned by MakeHandle if it
// is passed an invalid reader.
var errInvalidReader = errors.New("Cannot make TLF handle with invalid reader")

// UIDList can be used to lexicographically sort UserOrTeamIDs.
type UIDList []keybase1.UserOrTeamID

func (u UIDList) Len() int {
	return len(u)
}

func (u UIDList) Less(i, j int) bool {
	return u[i].Less(u[j])
}

func (u UIDList) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// SocialAssertionList can be used to lexicographically sort SocialAssertions.
type SocialAssertionList []keybase1.SocialAssertion

func (u SocialAssertionList) Len() int {
	return len(u)
}

func (u SocialAssertionList) Less(i, j int) bool {
	si := u[i].String()
	sj := u[j].String()
	return si < sj
}

func (u SocialAssertionList) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// MakeHandle creates a Handle from the given list of
// readers and writers. If the given reader list contains just
// keybase1.PUBLIC_UID, then the returned handle will be for a public
// folder. Otherwise, it will be private. PUBLIC_UID shouldn't be in
// any list in any other case.
func MakeHandle(
	writers, readers []keybase1.UserOrTeamID,
	unresolvedWriters, unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension) (Handle, error) {
	if reflect.ValueOf(Handle{}).NumField() != 7 {
		panic(errors.New(
			"Unexpected number of fields in Handle; please update the check " +
				"above and ensure that Handle.DeepEqual() accounts for the " +
				"new field"))
	}
	if len(writers) == 0 {
		return Handle{}, errNoWriters
	}

	if writers[0].IsTeamOrSubteam() {
		// Right now we only support single-team private TLFs.
		if len(writers) > 1 || len(unresolvedWriters) != 0 {
			return Handle{}, errInvalidWriter
		} else if len(readers) != 0 || len(unresolvedReaders) != 0 {
			return Handle{}, errInvalidReader
		}
	}

	for i, w := range writers {
		if w == keybase1.PUBLIC_UID {
			return Handle{}, errInvalidWriter
		} else if i > 0 && w.IsTeamOrSubteam() {
			return Handle{}, errInvalidWriter
		}
	}

	// If we have more than one reader, none of them should be the
	// public UID.  And no readers should be a team.
	checkPublic := (len(readers) + len(unresolvedReaders)) > 1
	for _, r := range readers {
		if checkPublic && r == keybase1.PUBLIC_UID {
			return Handle{}, errInvalidReader
		} else if r.IsTeamOrSubteam() {
			return Handle{}, errInvalidReader
		}
	}

	// TODO: Check for overlap between readers and writers, and
	// for duplicates.

	writersCopy := make([]keybase1.UserOrTeamID, len(writers))
	copy(writersCopy, writers)
	sort.Sort(UIDList(writersCopy))

	var readersCopy []keybase1.UserOrTeamID
	if len(readers) > 0 {
		readersCopy = make([]keybase1.UserOrTeamID, len(readers))
		copy(readersCopy, readers)
		sort.Sort(UIDList(readersCopy))
	}

	var unresolvedWritersCopy []keybase1.SocialAssertion
	if len(unresolvedWriters) > 0 {
		unresolvedWritersCopy = make([]keybase1.SocialAssertion, len(unresolvedWriters))
		copy(unresolvedWritersCopy, unresolvedWriters)
		sort.Sort(SocialAssertionList(unresolvedWritersCopy))
	}

	var unresolvedReadersCopy []keybase1.SocialAssertion
	if len(unresolvedReaders) > 0 {
		unresolvedReadersCopy = make([]keybase1.SocialAssertion, len(unresolvedReaders))
		copy(unresolvedReadersCopy, unresolvedReaders)
		sort.Sort(SocialAssertionList(unresolvedReadersCopy))
	}

	conflictInfo, finalizedInfo := HandleExtensionList(extensions).Splat()

	return Handle{
		Writers:           writersCopy,
		Readers:           readersCopy,
		UnresolvedWriters: unresolvedWritersCopy,
		UnresolvedReaders: unresolvedReadersCopy,
		ConflictInfo:      conflictInfo,
		FinalizedInfo:     finalizedInfo,
	}, nil
}

// IsBackedByTeam returns true if h represents a TLF backed by a team. It could
// be either a SingleTeam TLF or a private/public TLF backed by an implicit
// team.
func (h Handle) IsBackedByTeam() bool {
	if len(h.Writers) != 1 ||
		len(h.Readers) != 0 ||
		len(h.UnresolvedReaders) != 0 ||
		len(h.UnresolvedWriters) != 0 {
		return false
	}
	return h.Writers[0].IsTeamOrSubteam()
}

// Type returns the type of TLF this Handle represents.
func (h Handle) Type() Type {
	if len(h.Readers) == 1 &&
		h.Readers[0].Equal(keybase1.PublicUID.AsUserOrTeam()) {
		return Public
	} else if len(h.Writers) == 1 && h.Writers[0].IsTeamOrSubteam() {
		return SingleTeam
	}
	return Private
}

// TypeForKeying returns the keying type for the handle h.
func (h Handle) TypeForKeying() KeyingType {
	if h.IsBackedByTeam() {
		return TeamKeying
	}
	return h.Type().ToKeyingType()
}

func (h Handle) findUserInList(user keybase1.UserOrTeamID,
	users []keybase1.UserOrTeamID) bool {
	for _, u := range users {
		if u == user {
			return true
		}
	}
	return false
}

// IsWriter returns whether or not the given user is a writer for the
// top-level folder represented by this Handle.
func (h Handle) IsWriter(user keybase1.UserOrTeamID) bool {
	if h.TypeForKeying() == TeamKeying {
		panic("Can't call Handle.IsWriter() for a single team TLF")
	}
	return h.findUserInList(user, h.Writers)
}

// IsReader returns whether or not the given user is a reader for the
// top-level folder represented by this Handle.
func (h Handle) IsReader(user keybase1.UserOrTeamID) bool {
	if h.TypeForKeying() == TeamKeying {
		panic("Can't call Handle.IsReader() for a single team TLF")
	}
	return h.TypeForKeying() == PublicKeying ||
		h.findUserInList(user, h.Readers) ||
		h.IsWriter(user)
}

// ResolvedUsers returns the concatenation of h.Writers and h.Readers,
// except if the handle is public, the returned list won't contain
// PUBLIC_UID.
func (h Handle) ResolvedUsers() []keybase1.UserOrTeamID {
	var resolvedUsers []keybase1.UserOrTeamID
	resolvedUsers = append(resolvedUsers, h.Writers...)
	if h.TypeForKeying() == PrivateKeying {
		resolvedUsers = append(resolvedUsers, h.Readers...)
	}
	return resolvedUsers
}

// HasUnresolvedUsers returns true if this handle has any unresolved
// writers or readers.
func (h Handle) HasUnresolvedUsers() bool {
	return len(h.UnresolvedWriters) > 0 || len(h.UnresolvedReaders) > 0
}

// UnresolvedUsers returns the concatenation of h.UnresolvedWriters
// and h.UnresolvedReaders.
func (h Handle) UnresolvedUsers() []keybase1.SocialAssertion {
	var unresolvedUsers []keybase1.SocialAssertion
	unresolvedUsers = append(unresolvedUsers, h.UnresolvedWriters...)
	unresolvedUsers = append(unresolvedUsers, h.UnresolvedReaders...)
	return unresolvedUsers
}

func uidSliceToSet(s []keybase1.UserOrTeamID) map[keybase1.UserOrTeamID]bool {
	m := make(map[keybase1.UserOrTeamID]bool, len(s))
	for _, u := range s {
		m[u] = true
	}
	return m
}

func assertionSliceToSet(s []keybase1.SocialAssertion) map[keybase1.SocialAssertion]bool {
	m := make(map[keybase1.SocialAssertion]bool, len(s))
	for _, u := range s {
		m[u] = true
	}
	return m
}

func resolveAssertions(
	assertions map[keybase1.SocialAssertion]keybase1.UID,
	unresolved []keybase1.SocialAssertion, resolved []keybase1.UserOrTeamID) (
	map[keybase1.UserOrTeamID]bool, []keybase1.SocialAssertion) {
	resolvedMap := uidSliceToSet(resolved)
	unresolvedMap := assertionSliceToSet(unresolved)
	for a, u := range assertions {
		if unresolvedMap[a] {
			resolvedMap[u.AsUserOrTeam()] = true
			delete(unresolvedMap, a)
		}
	}
	return resolvedMap, assertionSetToSlice(unresolvedMap)
}

func uidSetToSlice(m map[keybase1.UserOrTeamID]bool) (
	s []keybase1.UserOrTeamID) {
	for u := range m {
		s = append(s, u)
	}
	return s
}

func assertionSetToSlice(m map[keybase1.SocialAssertion]bool) (s []keybase1.SocialAssertion) {
	for u := range m {
		s = append(s, u)
	}
	return s
}

// ResolveAssertions creates a new Handle given an existing one with
// while resolving the passed assertions.
func (h Handle) ResolveAssertions(
	assertions map[keybase1.SocialAssertion]keybase1.UID) Handle {
	if len(assertions) == 0 || (len(h.UnresolvedWriters) == 0 && len(h.UnresolvedReaders) == 0) || h.IsFinal() {
		return h
	}
	var resolvedWriters, resolvedReaders map[keybase1.UserOrTeamID]bool
	resolvedWriters, h.UnresolvedWriters = resolveAssertions(assertions, h.UnresolvedWriters, h.Writers)
	resolvedReaders, h.UnresolvedReaders = resolveAssertions(assertions, h.UnresolvedReaders, h.Readers)
	h.Writers = uidSetToSlice(resolvedWriters)
	for _, u := range h.Writers {
		delete(resolvedReaders, u)
	}
	h.Readers = uidSetToSlice(resolvedReaders)
	sort.Sort(UIDList(h.Writers))
	sort.Sort(UIDList(h.Readers))
	sort.Sort(SocialAssertionList(h.UnresolvedWriters))
	sort.Sort(SocialAssertionList(h.UnresolvedReaders))
	return h
}

// Extensions returns a list of extensions for the given handle.
func (h Handle) Extensions() (extensions []HandleExtension) {
	if h.ConflictInfo != nil {
		extensions = append(extensions, *h.ConflictInfo)
	}
	if h.FinalizedInfo != nil {
		extensions = append(extensions, *h.FinalizedInfo)
	}
	return extensions
}

// IsFinal returns true if the handle has been finalized.
func (h Handle) IsFinal() bool {
	return h.FinalizedInfo != nil
}

// IsConflict returns true if the handle is a conflict handle.
func (h Handle) IsConflict() bool {
	return h.ConflictInfo != nil
}

// DeepEqual returns true if the handle is equal to another handle.
func (h Handle) DeepEqual(other Handle) bool {
	if len(h.Writers) != len(other.Writers) {
		return false
	}
	if len(h.UnresolvedWriters) != len(other.UnresolvedWriters) {
		return false
	}
	if len(h.Readers) != len(other.Readers) {
		return false
	}
	if len(h.UnresolvedReaders) != len(other.UnresolvedReaders) {
		return false
	}

	if !h.sorted {
		sort.Sort(UIDList(h.Writers))
		sort.Sort(UIDList(h.Readers))
		sort.Sort(SocialAssertionList(h.UnresolvedWriters))
		sort.Sort(SocialAssertionList(h.UnresolvedReaders))
		h.sorted = true
	}
	if !other.sorted {
		sort.Sort(UIDList(other.Writers))
		sort.Sort(UIDList(other.Readers))
		sort.Sort(SocialAssertionList(other.UnresolvedWriters))
		sort.Sort(SocialAssertionList(other.UnresolvedReaders))
		other.sorted = true
	}

	for i, v := range h.Writers {
		if other.Writers[i] != v {
			return false
		}
	}
	for i, v := range h.UnresolvedWriters {
		if other.UnresolvedWriters[i] != v {
			return false
		}
	}
	for i, v := range h.Readers {
		if other.Readers[i] != v {
			return false
		}
	}
	for i, v := range h.UnresolvedReaders {
		if other.UnresolvedReaders[i] != v {
			return false
		}
	}
	if h.IsConflict() != other.IsConflict() {
		return false
	}
	if h.IsFinal() != other.IsFinal() {
		return false
	}
	if h.ConflictInfo != nil &&
		h.ConflictInfo.String() != other.ConflictInfo.String() {
		return false
	}
	if h.FinalizedInfo != nil &&
		h.FinalizedInfo.String() != other.FinalizedInfo.String() {
		return false
	}

	return true
}
<commit_msg>tlf.handle: Cache sorted value properly, and use a pointer receiver for DeepEqual.<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package tlf

import (
	"errors"
	"reflect"
	"sort"

	"github.com/keybase/client/go/protocol/keybase1"
)

// Handle uniquely identified top-level folders by readers and writers.
//
// NOTE: if you change this type, ensure you update the `NumField` check in
// `init`, and that the new fields are correctly checked for equality in
// `Handle.DeepEqual()`.
// TODO: Have separate types for writers vs. readers.
type Handle struct {
	Writers           []keybase1.UserOrTeamID    `codec:"w,omitempty"`
	Readers           []keybase1.UserOrTeamID    `codec:"r,omitempty"`
	UnresolvedWriters []keybase1.SocialAssertion `codec:"uw,omitempty"`
	UnresolvedReaders []keybase1.SocialAssertion `codec:"ur,omitempty"`
	ConflictInfo      *HandleExtension           `codec:"ci,omitempty"`
	FinalizedInfo     *HandleExtension           `codec:"fi,omitempty"`

	// caching field to track whether we've sorted the slices.
	sorted bool `codec:"-"`
}

// init verifies that we haven't changed the number of fields, so that if we
// do, the engineer can take a good, long look at what they've done.
func init() {
	if reflect.ValueOf(Handle{}).NumField() != 7 {
		panic(errors.New(
			"Unexpected number of fields in Handle; please update the check " +
				"above and ensure that Handle.DeepEqual() accounts for the " +
				"new field"))
	}
}

// errNoWriters is the error returned by MakeHandle if it is
// passed an empty list of writers.
var errNoWriters = errors.New("Cannot make TLF handle with no writers; need rekey?")

// errInvalidWriter is the error returned by MakeHandle if it
// is passed an invalid writer.
var errInvalidWriter = errors.New("Cannot make TLF handle with invalid writer")

// errInvalidReader is the error returned by MakeHandle if it
// is passed an invalid reader.
var errInvalidReader = errors.New("Cannot make TLF handle with invalid reader")

// UIDList can be used to lexicographically sort UserOrTeamIDs.
type UIDList []keybase1.UserOrTeamID

func (u UIDList) Len() int {
	return len(u)
}

func (u UIDList) Less(i, j int) bool {
	return u[i].Less(u[j])
}

func (u UIDList) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// SocialAssertionList can be used to lexicographically sort SocialAssertions.
type SocialAssertionList []keybase1.SocialAssertion

func (u SocialAssertionList) Len() int {
	return len(u)
}

func (u SocialAssertionList) Less(i, j int) bool {
	si := u[i].String()
	sj := u[j].String()
	return si < sj
}

func (u SocialAssertionList) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// MakeHandle creates a Handle from the given list of
// readers and writers. If the given reader list contains just
// keybase1.PUBLIC_UID, then the returned handle will be for a public
// folder. Otherwise, it will be private. PUBLIC_UID shouldn't be in
// any list in any other case.
func MakeHandle(
	writers, readers []keybase1.UserOrTeamID,
	unresolvedWriters, unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension) (Handle, error) {
	if len(writers) == 0 {
		return Handle{}, errNoWriters
	}

	if writers[0].IsTeamOrSubteam() {
		// Right now we only support single-team private TLFs.
		if len(writers) > 1 || len(unresolvedWriters) != 0 {
			return Handle{}, errInvalidWriter
		} else if len(readers) != 0 || len(unresolvedReaders) != 0 {
			return Handle{}, errInvalidReader
		}
	}

	for i, w := range writers {
		if w == keybase1.PUBLIC_UID {
			return Handle{}, errInvalidWriter
		} else if i > 0 && w.IsTeamOrSubteam() {
			return Handle{}, errInvalidWriter
		}
	}

	// If we have more than one reader, none of them should be the
	// public UID.  And no readers should be a team.
	checkPublic := (len(readers) + len(unresolvedReaders)) > 1
	for _, r := range readers {
		if checkPublic && r == keybase1.PUBLIC_UID {
			return Handle{}, errInvalidReader
		} else if r.IsTeamOrSubteam() {
			return Handle{}, errInvalidReader
		}
	}

	// TODO: Check for overlap between readers and writers, and
	// for duplicates.

	writersCopy := make([]keybase1.UserOrTeamID, len(writers))
	copy(writersCopy, writers)
	sort.Sort(UIDList(writersCopy))

	var readersCopy []keybase1.UserOrTeamID
	if len(readers) > 0 {
		readersCopy = make([]keybase1.UserOrTeamID, len(readers))
		copy(readersCopy, readers)
		sort.Sort(UIDList(readersCopy))
	}

	var unresolvedWritersCopy []keybase1.SocialAssertion
	if len(unresolvedWriters) > 0 {
		unresolvedWritersCopy = make([]keybase1.SocialAssertion, len(unresolvedWriters))
		copy(unresolvedWritersCopy, unresolvedWriters)
		sort.Sort(SocialAssertionList(unresolvedWritersCopy))
	}

	var unresolvedReadersCopy []keybase1.SocialAssertion
	if len(unresolvedReaders) > 0 {
		unresolvedReadersCopy = make([]keybase1.SocialAssertion, len(unresolvedReaders))
		copy(unresolvedReadersCopy, unresolvedReaders)
		sort.Sort(SocialAssertionList(unresolvedReadersCopy))
	}

	conflictInfo, finalizedInfo := HandleExtensionList(extensions).Splat()

	return Handle{
		Writers:           writersCopy,
		Readers:           readersCopy,
		UnresolvedWriters: unresolvedWritersCopy,
		UnresolvedReaders: unresolvedReadersCopy,
		ConflictInfo:      conflictInfo,
		FinalizedInfo:     finalizedInfo,
		sorted:            true,
	}, nil
}

// IsBackedByTeam returns true if h represents a TLF backed by a team. It could
// be either a SingleTeam TLF or a private/public TLF backed by an implicit
// team.
func (h Handle) IsBackedByTeam() bool {
	if len(h.Writers) != 1 ||
		len(h.Readers) != 0 ||
		len(h.UnresolvedReaders) != 0 ||
		len(h.UnresolvedWriters) != 0 {
		return false
	}
	return h.Writers[0].IsTeamOrSubteam()
}

// Type returns the type of TLF this Handle represents.
func (h Handle) Type() Type {
	if len(h.Readers) == 1 &&
		h.Readers[0].Equal(keybase1.PublicUID.AsUserOrTeam()) {
		return Public
	} else if len(h.Writers) == 1 && h.Writers[0].IsTeamOrSubteam() {
		return SingleTeam
	}
	return Private
}

// TypeForKeying returns the keying type for the handle h.
func (h Handle) TypeForKeying() KeyingType {
	if h.IsBackedByTeam() {
		return TeamKeying
	}
	return h.Type().ToKeyingType()
}

func (h Handle) findUserInList(user keybase1.UserOrTeamID,
	users []keybase1.UserOrTeamID) bool {
	for _, u := range users {
		if u == user {
			return true
		}
	}
	return false
}

// IsWriter returns whether or not the given user is a writer for the
// top-level folder represented by this Handle.
func (h Handle) IsWriter(user keybase1.UserOrTeamID) bool {
	if h.TypeForKeying() == TeamKeying {
		panic("Can't call Handle.IsWriter() for a single team TLF")
	}
	return h.findUserInList(user, h.Writers)
}

// IsReader returns whether or not the given user is a reader for the
// top-level folder represented by this Handle.
func (h Handle) IsReader(user keybase1.UserOrTeamID) bool {
	if h.TypeForKeying() == TeamKeying {
		panic("Can't call Handle.IsReader() for a single team TLF")
	}
	return h.TypeForKeying() == PublicKeying ||
		h.findUserInList(user, h.Readers) ||
		h.IsWriter(user)
}

// ResolvedUsers returns the concatenation of h.Writers and h.Readers,
// except if the handle is public, the returned list won't contain
// PUBLIC_UID.
func (h Handle) ResolvedUsers() []keybase1.UserOrTeamID {
	var resolvedUsers []keybase1.UserOrTeamID
	resolvedUsers = append(resolvedUsers, h.Writers...)
	if h.TypeForKeying() == PrivateKeying {
		resolvedUsers = append(resolvedUsers, h.Readers...)
	}
	return resolvedUsers
}

// HasUnresolvedUsers returns true if this handle has any unresolved
// writers or readers.
func (h Handle) HasUnresolvedUsers() bool {
	return len(h.UnresolvedWriters) > 0 || len(h.UnresolvedReaders) > 0
}

// UnresolvedUsers returns the concatenation of h.UnresolvedWriters
// and h.UnresolvedReaders.
func (h Handle) UnresolvedUsers() []keybase1.SocialAssertion {
	var unresolvedUsers []keybase1.SocialAssertion
	unresolvedUsers = append(unresolvedUsers, h.UnresolvedWriters...)
	unresolvedUsers = append(unresolvedUsers, h.UnresolvedReaders...)
	return unresolvedUsers
}

func uidSliceToSet(s []keybase1.UserOrTeamID) map[keybase1.UserOrTeamID]bool {
	m := make(map[keybase1.UserOrTeamID]bool, len(s))
	for _, u := range s {
		m[u] = true
	}
	return m
}

func assertionSliceToSet(s []keybase1.SocialAssertion) map[keybase1.SocialAssertion]bool {
	m := make(map[keybase1.SocialAssertion]bool, len(s))
	for _, u := range s {
		m[u] = true
	}
	return m
}

func resolveAssertions(
	assertions map[keybase1.SocialAssertion]keybase1.UID,
	unresolved []keybase1.SocialAssertion, resolved []keybase1.UserOrTeamID) (
	map[keybase1.UserOrTeamID]bool, []keybase1.SocialAssertion) {
	resolvedMap := uidSliceToSet(resolved)
	unresolvedMap := assertionSliceToSet(unresolved)
	for a, u := range assertions {
		if unresolvedMap[a] {
			resolvedMap[u.AsUserOrTeam()] = true
			delete(unresolvedMap, a)
		}
	}
	return resolvedMap, assertionSetToSlice(unresolvedMap)
}

func uidSetToSlice(m map[keybase1.UserOrTeamID]bool) (
	s []keybase1.UserOrTeamID) {
	for u := range m {
		s = append(s, u)
	}
	return s
}

func assertionSetToSlice(m map[keybase1.SocialAssertion]bool) (s []keybase1.SocialAssertion) {
	for u := range m {
		s = append(s, u)
	}
	return s
}

// ResolveAssertions creates a new Handle given an existing one with
// while resolving the passed assertions.
func (h Handle) ResolveAssertions(
	assertions map[keybase1.SocialAssertion]keybase1.UID) Handle {
	if len(assertions) == 0 || (len(h.UnresolvedWriters) == 0 && len(h.UnresolvedReaders) == 0) || h.IsFinal() {
		return h
	}
	var resolvedWriters, resolvedReaders map[keybase1.UserOrTeamID]bool
	resolvedWriters, h.UnresolvedWriters = resolveAssertions(assertions, h.UnresolvedWriters, h.Writers)
	resolvedReaders, h.UnresolvedReaders = resolveAssertions(assertions, h.UnresolvedReaders, h.Readers)
	h.Writers = uidSetToSlice(resolvedWriters)
	for _, u := range h.Writers {
		delete(resolvedReaders, u)
	}
	h.Readers = uidSetToSlice(resolvedReaders)
	sort.Sort(UIDList(h.Writers))
	sort.Sort(UIDList(h.Readers))
	sort.Sort(SocialAssertionList(h.UnresolvedWriters))
	sort.Sort(SocialAssertionList(h.UnresolvedReaders))
	h.sorted = true
	return h
}

// Extensions returns a list of extensions for the given handle.
func (h Handle) Extensions() (extensions []HandleExtension) {
	if h.ConflictInfo != nil {
		extensions = append(extensions, *h.ConflictInfo)
	}
	if h.FinalizedInfo != nil {
		extensions = append(extensions, *h.FinalizedInfo)
	}
	return extensions
}

// IsFinal returns true if the handle has been finalized.
func (h Handle) IsFinal() bool {
	return h.FinalizedInfo != nil
}

// IsConflict returns true if the handle is a conflict handle.
func (h Handle) IsConflict() bool {
	return h.ConflictInfo != nil
}

// DeepEqual returns true if the handle is equal to another handle.
// This can mutate the Handle in that it might sort its Writers,
// Readers, UnresolvedWriters, and UnresolvedReaders.
func (h *Handle) DeepEqual(other Handle) bool {
	if len(h.Writers) != len(other.Writers) {
		return false
	}
	if len(h.UnresolvedWriters) != len(other.UnresolvedWriters) {
		return false
	}
	if len(h.Readers) != len(other.Readers) {
		return false
	}
	if len(h.UnresolvedReaders) != len(other.UnresolvedReaders) {
		return false
	}

	if !h.sorted {
		sort.Sort(UIDList(h.Writers))
		sort.Sort(UIDList(h.Readers))
		sort.Sort(SocialAssertionList(h.UnresolvedWriters))
		sort.Sort(SocialAssertionList(h.UnresolvedReaders))
		h.sorted = true
	}
	if !other.sorted {
		sort.Sort(UIDList(other.Writers))
		sort.Sort(UIDList(other.Readers))
		sort.Sort(SocialAssertionList(other.UnresolvedWriters))
		sort.Sort(SocialAssertionList(other.UnresolvedReaders))
	}

	for i, v := range h.Writers {
		if other.Writers[i] != v {
			return false
		}
	}
	for i, v := range h.UnresolvedWriters {
		if other.UnresolvedWriters[i] != v {
			return false
		}
	}
	for i, v := range h.Readers {
		if other.Readers[i] != v {
			return false
		}
	}
	for i, v := range h.UnresolvedReaders {
		if other.UnresolvedReaders[i] != v {
			return false
		}
	}
	if h.IsConflict() != other.IsConflict() {
		return false
	}
	if h.IsFinal() != other.IsFinal() {
		return false
	}
	if h.ConflictInfo != nil &&
		h.ConflictInfo.String() != other.ConflictInfo.String() {
		return false
	}
	if h.FinalizedInfo != nil &&
		h.FinalizedInfo.String() != other.FinalizedInfo.String() {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package showbudget

import (
	"fmt"
	"io"
	"sort"

	"github.com/gosuri/uitable"
	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/gnuflag"
	"github.com/juju/juju/api/modelmanager"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/loggo"
	"gopkg.in/juju/names.v2"
	"gopkg.in/macaroon-bakery.v1/httpbakery"

	api "github.com/juju/romulus/api/budget"
	wireformat "github.com/juju/romulus/wireformat/budget"
)

var logger = loggo.GetLogger("romulus.cmd.showbudget")

// NewShowBudgetCommand returns a new command that is used
// to show details of the specified wireformat.
func NewShowBudgetCommand() modelcmd.CommandBase {
	return &showBudgetCommand{}
}

type showBudgetCommand struct {
	modelcmd.ModelCommandBase

	out    cmd.Output
	budget string
}

const showBudgetDoc = `
Display budget usage information.

Examples:
    juju show-budget personal
`

// Info implements cmd.Command.Info.
func (c *showBudgetCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "show-budget",
		Args:    "<budget>",
		Purpose: "Show details about a budget.",
		Doc:     showBudgetDoc,
	}
}

// Init implements cmd.Command.Init.
func (c *showBudgetCommand) Init(args []string) error {
	if len(args) < 1 {
		return errors.New("missing arguments")
	}
	c.budget, args = args[0], args[1:]

	return cmd.CheckEmpty(args)
}

// SetFlags implements cmd.Command.SetFlags.
func (c *showBudgetCommand) SetFlags(f *gnuflag.FlagSet) {
	c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{
		"tabular": formatTabular,
		"json":    cmd.FormatJson,
	})
}

func (c *showBudgetCommand) Run(ctx *cmd.Context) error {
	client, err := c.BakeryClient()
	if err != nil {
		return errors.Annotate(err, "failed to create an http client")
	}
	api, err := newBudgetAPIClient(client)
	if err != nil {
		return errors.Annotate(err, "failed to create an api client")
	}
	budget, err := api.GetBudget(c.budget)
	if err != nil {
		return errors.Annotate(err, "failed to retrieve the budget")
	}
	c.resolveModelNames(budget)
	err = c.out.Write(ctx, budget)
	return errors.Trace(err)
}

// resolveModelNames is a best-effort method to resolve model names - if we
// encounter any error, we do not issue an error.
func (c *showBudgetCommand) resolveModelNames(budget *wireformat.BudgetWithAllocations) {
	models := make([]names.ModelTag, len(budget.Allocations))
	for i, allocation := range budget.Allocations {
		models[i] = names.NewModelTag(allocation.Model)
	}
	client, err := newAPIClient(c)
	if err != nil {
		logger.Errorf("failed to open the API client: %v", err)
		return
	}
	modelInfoSlice, err := client.ModelInfo(models)
	if err != nil {
		logger.Errorf("failed to retrieve model info: %v", err)
		return
	}
	for j, info := range modelInfoSlice {
		if info.Error != nil {
			logger.Errorf("failed to get model info for model %q: %v", models[j], info.Error)
			continue
		}
		for i, allocation := range budget.Allocations {
			if info.Result.UUID == allocation.Model {
				budget.Allocations[i].Model = info.Result.Name
			}
		}
	}
}

// formatTabular returns a tabular view of available budgets.
func formatTabular(writer io.Writer, value interface{}) error {
	b, ok := value.(*wireformat.BudgetWithAllocations)
	if !ok {
		return errors.Errorf("expected value of type %T, got %T", b, value)
	}

	table := uitable.New()
	table.MaxColWidth = 50
	table.Wrap = true
	for _, col := range []int{2, 3, 5} {
		table.RightAlign(col)
	}

	table.AddRow("MODEL", "SERVICES", "SPENT", "ALLOCATED", "BY", "USAGE")
	for _, allocation := range b.Allocations {
		firstLine := true
		// We'll sort the service names to avoid nondeterministic
		// command output.
		services := make([]string, 0, len(allocation.Services))
		for serviceName, _ := range allocation.Services {
			services = append(services, serviceName)
		}
		sort.Strings(services)
		for _, serviceName := range services {
			service, _ := allocation.Services[serviceName]
			if firstLine {
				table.AddRow(allocation.Model, serviceName, service.Consumed, allocation.Limit, allocation.Owner, allocation.Usage)
				firstLine = false
				continue
			}
			table.AddRow("", serviceName, service.Consumed, "", "")
		}

	}
	table.AddRow("", "", "", "", "")
	table.AddRow("TOTAL", "", b.Total.Consumed, b.Total.Allocated, "", b.Total.Usage)
	table.AddRow("BUDGET", "", "", b.Limit, "")
	table.AddRow("UNALLOCATED", "", "", b.Total.Unallocated, "")
	fmt.Fprint(writer, table)
	return nil
}

var newAPIClient = newAPIClientImpl

func newAPIClientImpl(c *showBudgetCommand) (APIClient, error) {
	root, err := c.NewAPIRoot()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return modelmanager.NewClient(root), nil
}

type APIClient interface {
	ModelInfo(tags []names.ModelTag) ([]params.ModelInfoResult, error)
}

var newBudgetAPIClient = newBudgetAPIClientImpl

func newBudgetAPIClientImpl(c *httpbakery.Client) (budgetAPIClient, error) {
	client := api.NewClient(c)
	return client, nil
}

type budgetAPIClient interface {
	GetBudget(string) (*wireformat.BudgetWithAllocations, error)
}
<commit_msg>Fixes LP:#1614316. Use controller API to look up model info.<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package showbudget

import (
	"fmt"
	"io"
	"sort"

	"github.com/gosuri/uitable"
	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/gnuflag"
	"github.com/juju/juju/api/modelmanager"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/loggo"
	"gopkg.in/juju/names.v2"
	"gopkg.in/macaroon-bakery.v1/httpbakery"

	api "github.com/juju/romulus/api/budget"
	wireformat "github.com/juju/romulus/wireformat/budget"
)

var logger = loggo.GetLogger("romulus.cmd.showbudget")

// NewShowBudgetCommand returns a new command that is used
// to show details of the specified wireformat.
func NewShowBudgetCommand() modelcmd.CommandBase {
	return &showBudgetCommand{}
}

type showBudgetCommand struct {
	modelcmd.ModelCommandBase

	out    cmd.Output
	budget string
}

const showBudgetDoc = `
Display budget usage information.

Examples:
    juju show-budget personal
`

// Info implements cmd.Command.Info.
func (c *showBudgetCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "show-budget",
		Args:    "<budget>",
		Purpose: "Show details about a budget.",
		Doc:     showBudgetDoc,
	}
}

// Init implements cmd.Command.Init.
func (c *showBudgetCommand) Init(args []string) error {
	if len(args) < 1 {
		return errors.New("missing arguments")
	}
	c.budget, args = args[0], args[1:]

	return cmd.CheckEmpty(args)
}

// SetFlags implements cmd.Command.SetFlags.
func (c *showBudgetCommand) SetFlags(f *gnuflag.FlagSet) {
	c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{
		"tabular": formatTabular,
		"json":    cmd.FormatJson,
	})
}

func (c *showBudgetCommand) Run(ctx *cmd.Context) error {
	client, err := c.BakeryClient()
	if err != nil {
		return errors.Annotate(err, "failed to create an http client")
	}
	api, err := newBudgetAPIClient(client)
	if err != nil {
		return errors.Annotate(err, "failed to create an api client")
	}
	budget, err := api.GetBudget(c.budget)
	if err != nil {
		return errors.Annotate(err, "failed to retrieve the budget")
	}
	c.resolveModelNames(budget)
	err = c.out.Write(ctx, budget)
	return errors.Trace(err)
}

// resolveModelNames is a best-effort method to resolve model names - if we
// encounter any error, we do not issue an error.
func (c *showBudgetCommand) resolveModelNames(budget *wireformat.BudgetWithAllocations) {
	models := make([]names.ModelTag, len(budget.Allocations))
	for i, allocation := range budget.Allocations {
		models[i] = names.NewModelTag(allocation.Model)
	}
	client, err := newAPIClient(c)
	if err != nil {
		logger.Errorf("failed to open the API client: %v", err)
		return
	}
	modelInfoSlice, err := client.ModelInfo(models)
	if err != nil {
		logger.Errorf("failed to retrieve model info: %v", err)
		return
	}
	for j, info := range modelInfoSlice {
		if info.Error != nil {
			logger.Errorf("failed to get model info for model %q: %v", models[j], info.Error)
			continue
		}
		for i, allocation := range budget.Allocations {
			if info.Result.UUID == allocation.Model {
				budget.Allocations[i].Model = info.Result.Name
			}
		}
	}
}

// formatTabular returns a tabular view of available budgets.
func formatTabular(writer io.Writer, value interface{}) error {
	b, ok := value.(*wireformat.BudgetWithAllocations)
	if !ok {
		return errors.Errorf("expected value of type %T, got %T", b, value)
	}

	table := uitable.New()
	table.MaxColWidth = 50
	table.Wrap = true
	for _, col := range []int{2, 3, 5} {
		table.RightAlign(col)
	}

	table.AddRow("MODEL", "SERVICES", "SPENT", "ALLOCATED", "BY", "USAGE")
	for _, allocation := range b.Allocations {
		firstLine := true
		// We'll sort the service names to avoid nondeterministic
		// command output.
		services := make([]string, 0, len(allocation.Services))
		for serviceName, _ := range allocation.Services {
			services = append(services, serviceName)
		}
		sort.Strings(services)
		for _, serviceName := range services {
			service, _ := allocation.Services[serviceName]
			if firstLine {
				table.AddRow(allocation.Model, serviceName, service.Consumed, allocation.Limit, allocation.Owner, allocation.Usage)
				firstLine = false
				continue
			}
			table.AddRow("", serviceName, service.Consumed, "", "")
		}

	}
	table.AddRow("", "", "", "", "")
	table.AddRow("TOTAL", "", b.Total.Consumed, b.Total.Allocated, "", b.Total.Usage)
	table.AddRow("BUDGET", "", "", b.Limit, "")
	table.AddRow("UNALLOCATED", "", "", b.Total.Unallocated, "")
	fmt.Fprint(writer, table)
	return nil
}

var newAPIClient = newAPIClientImpl

func newAPIClientImpl(c *showBudgetCommand) (APIClient, error) {
	root, err := c.NewControllerAPIRoot()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return modelmanager.NewClient(root), nil
}

type APIClient interface {
	ModelInfo(tags []names.ModelTag) ([]params.ModelInfoResult, error)
}

var newBudgetAPIClient = newBudgetAPIClientImpl

func newBudgetAPIClientImpl(c *httpbakery.Client) (budgetAPIClient, error) {
	client := api.NewClient(c)
	return client, nil
}

type budgetAPIClient interface {
	GetBudget(string) (*wireformat.BudgetWithAllocations, error)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package phases

import (
	"fmt"

	"github.com/pkg/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/klog"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
	cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
	kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
	patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode"
	"k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
	"k8s.io/kubernetes/pkg/util/normalizer"
)

var (
	uploadKubeadmConfigLongDesc = fmt.Sprintf(normalizer.LongDesc(`
		Uploads the kubeadm ClusterConfiguration to a ConfigMap called %s in the %s namespace.
		This enables correct configuration of system components and a seamless user experience when upgrading.

		Alternatively, you can use kubeadm config.
		`), kubeadmconstants.KubeadmConfigConfigMap, metav1.NamespaceSystem)

	uploadKubeadmConfigExample = normalizer.Examples(`
		# uploads the configuration of your cluster
		kubeadm init phase upload-config --config=myConfig.yaml
		`)

	uploadKubeletConfigLongDesc = normalizer.LongDesc(`
		Uploads kubelet configuration extracted from the kubeadm InitConfiguration object to a ConfigMap
		of the form kubelet-config-1.X in the cluster, where X is the minor version of the current (API Server) Kubernetes version.
		`)

	uploadKubeletConfigExample = normalizer.Examples(`
		# Uploads the kubelet configuration from the kubeadm Config file to a ConfigMap in the cluster.
		kubeadm init phase upload-config kubelet --config kubeadm.yaml
		`)
)

type uploadConfigData interface {
	Cfg() *kubeadmapi.InitConfiguration
	Client() (clientset.Interface, error)
}

// NewUploadConfigPhase returns the phase to uploadConfig
func NewUploadConfigPhase() workflow.Phase {
	return workflow.Phase{
		Name:    "upload-config",
		Aliases: []string{"uploadconfig"},
		Short:   "Uploads the kubeadm and kubelet configuration to a ConfigMap",
		Long:    cmdutil.MacroCommandLongDescription,
		Phases: []workflow.Phase{
			{
				Name:           "all",
				Short:          "Uploads all configuration to a config map",
				RunAllSiblings: true,
			},
			{
				Name:         "kubeadm",
				Short:        "Uploads the kubeadm ClusterConfiguration to a ConfigMap",
				Long:         uploadKubeadmConfigLongDesc,
				Example:      uploadKubeadmConfigExample,
				Run:          runUploadKubeadmConfig,
				InheritFlags: getUploadConfigPhaseFlags(),
			},
			{
				Name:         "kubelet",
				Short:        "Uploads the kubelet component config to a ConfigMap",
				Long:         uploadKubeletConfigLongDesc,
				Example:      uploadKubeletConfigExample,
				Run:          runUploadKubeletConfig,
				InheritFlags: getUploadConfigPhaseFlags(),
			},
		},
	}
}

func getUploadConfigPhaseFlags() []string {
	return []string{
		options.CfgPath,
		options.KubeconfigPath,
	}
}

// runUploadKubeadmConfig uploads the kubeadm configuration to a ConfigMap
func runUploadKubeadmConfig(c workflow.RunData) error {
	cfg, client, err := getUploadConfigData(c)
	if err != nil {
		return err
	}

	klog.V(1).Infof("[upload-config] Uploading the kubeadm ClusterConfiguration to a ConfigMap")
	if err := uploadconfig.UploadConfiguration(cfg, client); err != nil {
		return errors.Wrap(err, "error uploading the kubeadm ClusterConfiguration")
	}
	return nil
}

// runUploadKubeletConfig uploads the kubelet configuration to a ConfigMap
func runUploadKubeletConfig(c workflow.RunData) error {
	cfg, client, err := getUploadConfigData(c)
	if err != nil {
		return err
	}

	klog.V(1).Infof("[upload-config] Uploading the kubelet component config to a ConfigMap")
	if err = kubeletphase.CreateConfigMap(cfg, client); err != nil {
		return errors.Wrap(err, "error creating kubelet configuration ConfigMap")
	}

	klog.V(1).Infof("[upload-config] Preserving the CRISocket information for the control-plane node")
	if err := patchnodephase.AnnotateCRISocket(client, cfg.NodeRegistration.Name, cfg.NodeRegistration.CRISocket); err != nil {
		return errors.Wrap(err, "Error writing Crisocket information for the control-plane node")
	}
	return nil
}

func getUploadConfigData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.Interface, error) {
	data, ok := c.(uploadConfigData)
	if !ok {
		return nil, nil, errors.New("upload-config phase invoked with an invalid data struct")
	}
	cfg := data.Cfg()
	client, err := data.Client()
	if err != nil {
		return nil, nil, err
	}
	return cfg, client, err
}
<commit_msg>Add flags to kubeadm init phase uploadconfig all<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package phases

import (
	"fmt"

	"github.com/pkg/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/klog"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
	cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
	kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
	kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
	patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode"
	"k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
	"k8s.io/kubernetes/pkg/util/normalizer"
)

var (
	uploadKubeadmConfigLongDesc = fmt.Sprintf(normalizer.LongDesc(`
		Uploads the kubeadm ClusterConfiguration to a ConfigMap called %s in the %s namespace.
		This enables correct configuration of system components and a seamless user experience when upgrading.

		Alternatively, you can use kubeadm config.
		`), kubeadmconstants.KubeadmConfigConfigMap, metav1.NamespaceSystem)

	uploadKubeadmConfigExample = normalizer.Examples(`
		# uploads the configuration of your cluster
		kubeadm init phase upload-config --config=myConfig.yaml
		`)

	uploadKubeletConfigLongDesc = normalizer.LongDesc(`
		Uploads kubelet configuration extracted from the kubeadm InitConfiguration object to a ConfigMap
		of the form kubelet-config-1.X in the cluster, where X is the minor version of the current (API Server) Kubernetes version.
		`)

	uploadKubeletConfigExample = normalizer.Examples(`
		# Uploads the kubelet configuration from the kubeadm Config file to a ConfigMap in the cluster.
		kubeadm init phase upload-config kubelet --config kubeadm.yaml
		`)
)

type uploadConfigData interface {
	Cfg() *kubeadmapi.InitConfiguration
	Client() (clientset.Interface, error)
}

// NewUploadConfigPhase returns the phase to uploadConfig
func NewUploadConfigPhase() workflow.Phase {
	return workflow.Phase{
		Name:    "upload-config",
		Aliases: []string{"uploadconfig"},
		Short:   "Uploads the kubeadm and kubelet configuration to a ConfigMap",
		Long:    cmdutil.MacroCommandLongDescription,
		Phases: []workflow.Phase{
			{
				Name:           "all",
				Short:          "Uploads all configuration to a config map",
				RunAllSiblings: true,
				InheritFlags:   getUploadConfigPhaseFlags(),
			},
			{
				Name:         "kubeadm",
				Short:        "Uploads the kubeadm ClusterConfiguration to a ConfigMap",
				Long:         uploadKubeadmConfigLongDesc,
				Example:      uploadKubeadmConfigExample,
				Run:          runUploadKubeadmConfig,
				InheritFlags: getUploadConfigPhaseFlags(),
			},
			{
				Name:         "kubelet",
				Short:        "Uploads the kubelet component config to a ConfigMap",
				Long:         uploadKubeletConfigLongDesc,
				Example:      uploadKubeletConfigExample,
				Run:          runUploadKubeletConfig,
				InheritFlags: getUploadConfigPhaseFlags(),
			},
		},
	}
}

func getUploadConfigPhaseFlags() []string {
	return []string{
		options.CfgPath,
		options.KubeconfigPath,
	}
}

// runUploadKubeadmConfig uploads the kubeadm configuration to a ConfigMap
func runUploadKubeadmConfig(c workflow.RunData) error {
	cfg, client, err := getUploadConfigData(c)
	if err != nil {
		return err
	}

	klog.V(1).Infof("[upload-config] Uploading the kubeadm ClusterConfiguration to a ConfigMap")
	if err := uploadconfig.UploadConfiguration(cfg, client); err != nil {
		return errors.Wrap(err, "error uploading the kubeadm ClusterConfiguration")
	}
	return nil
}

// runUploadKubeletConfig uploads the kubelet configuration to a ConfigMap
func runUploadKubeletConfig(c workflow.RunData) error {
	cfg, client, err := getUploadConfigData(c)
	if err != nil {
		return err
	}

	klog.V(1).Infof("[upload-config] Uploading the kubelet component config to a ConfigMap")
	if err = kubeletphase.CreateConfigMap(cfg, client); err != nil {
		return errors.Wrap(err, "error creating kubelet configuration ConfigMap")
	}

	klog.V(1).Infof("[upload-config] Preserving the CRISocket information for the control-plane node")
	if err := patchnodephase.AnnotateCRISocket(client, cfg.NodeRegistration.Name, cfg.NodeRegistration.CRISocket); err != nil {
		return errors.Wrap(err, "Error writing Crisocket information for the control-plane node")
	}
	return nil
}

func getUploadConfigData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.Interface, error) {
	data, ok := c.(uploadConfigData)
	if !ok {
		return nil, nil, errors.New("upload-config phase invoked with an invalid data struct")
	}
	cfg := data.Cfg()
	client, err := data.Client()
	if err != nil {
		return nil, nil, err
	}
	return cfg, client, err
}
<|endoftext|>


<commit_before>package collectors

import (
	"fmt"
	"regexp"
	"strings"

	"bosun.org/_third_party/github.com/coreos/go-systemd/dbus"
	"bosun.org/cmd/scollector/conf"
	"bosun.org/metadata"
	"bosun.org/opentsdb"
	"bosun.org/util"
)

type systemdServiceConfig struct {
	regex *regexp.Regexp
	watch bool
}

var systemdServices []*systemdServiceConfig

func init() {
	registerInit(func(c *conf.Conf) {
		for _, s := range c.SystemdService {
			AddSystemdServiceConfig(s)
		}
		collectors = append(collectors, &IntervalCollector{
			F: func() (opentsdb.MultiDataPoint, error) {
				return c_systemd()
			},
			name: "c_systemd",
		})
	})
}

func AddSystemdServiceConfig(params conf.ServiceParams) error {
	if params.Name == "" {
		return fmt.Errorf("empty service Name")
	}
	reg, err := regexp.Compile(params.Name)
	if err != nil {
		return err
	}
	serviceConfig := systemdServiceConfig{regex: reg, watch: params.WatchProc}
	systemdServices = append(systemdServices, &serviceConfig)
	return nil
}

// c_systemd() iterates through all units provided by systemd's dbus info.
// If the unit is a service we care about, it sends service metrics.
// Also sends process data if WatchProc was set to true in the [[SystemdService]] config.
func c_systemd() (opentsdb.MultiDataPoint, error) {
	conn, err := dbus.New()
	if err != nil {
		return nil, err
	}

	units, err := conn.ListUnits()
	if err != nil {
		return nil, err
	}

	var md opentsdb.MultiDataPoint
	for _, unit := range units {
		if strings.HasSuffix(unit.Name, ".service") {
			shortName := strings.TrimSuffix(unit.Name, ".service")
			for _, config := range systemdServices {
				if config.regex.MatchString(shortName) {
					if config.watch {
						err = watchSystemdServiceProc(&md, conn, unit)
						if err != nil {
							return nil, err
						}
					}
					if _, ok := activeState[unit.ActiveState]; !ok {
						return nil, fmt.Errorf("error: unit %s has unknown ActiveState: %s\n", unit.Name, unit.ActiveState)
					}
					systemdTags := opentsdb.TagSet{"name": unit.Name}
					osTags := opentsdb.TagSet{"name": shortName}
					Add(&md, "linux.systemd.unit.activestate", activeState[unit.ActiveState], systemdTags, metadata.Gauge, metadata.StatusCode, descActiveState)
					Add(&md, osServiceRunning, util.Btoi(unit.ActiveState == "active"), osTags, metadata.Gauge, metadata.Bool, osServiceRunningDesc)
				}
			}
		}
	}

	return md, err
}

// watchSystemdService() attempts to determine the main PID of a service and
// builds a WatchedProc{} which is then sent to linuxProcMonitor()
func watchSystemdServiceProc(md *opentsdb.MultiDataPoint, conn *dbus.Conn, unit dbus.UnitStatus) error {
	// ExecMainPID can be non-running. MainPID is the pid of the running service.
	mainPIDProp, err := conn.GetUnitTypeProperty(unit.Name, "Service", "MainPID")
	if err != nil {
		return err
	}

	mainPID := mainPIDProp.Value.Value().(uint32)
	// MainPID is 0 if there is no running service.
	if mainPID == 0 {
		return nil
	}

	cmdline, err := getLinuxCmdline(fmt.Sprint(mainPID))
	if err != nil {
		return err
	}
	if cmdline == nil {
		return nil
	}

	wp := WatchedProc{
		Command:   cmdline[0],
		Name:      strings.TrimSuffix(unit.Name, ".service"),
		Processes: make(map[string]int),
		ArgMatch:  regexp.MustCompile(""),
		idPool:    new(idPool)}

	// Since we only have one PID per service (at the moment), this is always set to 1
	wp.Processes[fmt.Sprint(mainPID)] = wp.get()

	if e := linuxProcMonitor(&wp, md); e != nil {
		return e
	}

	return err
}

var activeState = map[string]int{
	"active":       0,
	"reloading":    1,
	"inactive":     2,
	"failed":       3,
	"activating":   4,
	"deactivating": 5,
}

const (
	descActiveState = "0: active, 1: reloading, 2: inactive, 3: failed, 4: activating, 5: deactivating"
)
<commit_msg>collectors/systemd: Close dbus connection properly.<commit_after>package collectors

import (
	"fmt"
	"regexp"
	"strings"

	"bosun.org/_third_party/github.com/coreos/go-systemd/dbus"
	"bosun.org/cmd/scollector/conf"
	"bosun.org/metadata"
	"bosun.org/opentsdb"
	"bosun.org/util"
)

type systemdServiceConfig struct {
	regex *regexp.Regexp
	watch bool
}

var systemdServices []*systemdServiceConfig

func init() {
	registerInit(func(c *conf.Conf) {
		for _, s := range c.SystemdService {
			AddSystemdServiceConfig(s)
		}
		collectors = append(collectors, &IntervalCollector{
			F: func() (opentsdb.MultiDataPoint, error) {
				return c_systemd()
			},
			name: "c_systemd",
		})
	})
}

func AddSystemdServiceConfig(params conf.ServiceParams) error {
	if params.Name == "" {
		return fmt.Errorf("empty service Name")
	}
	reg, err := regexp.Compile(params.Name)
	if err != nil {
		return err
	}
	serviceConfig := systemdServiceConfig{regex: reg, watch: params.WatchProc}
	systemdServices = append(systemdServices, &serviceConfig)
	return nil
}

// c_systemd() iterates through all units provided by systemd's dbus info.
// If the unit is a service we care about, it sends service metrics.
// Also sends process data if WatchProc was set to true in the [[SystemdService]] config.
func c_systemd() (opentsdb.MultiDataPoint, error) {
	conn, err := dbus.New()
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	units, err := conn.ListUnits()
	if err != nil {
		return nil, err
	}

	var md opentsdb.MultiDataPoint
	for _, unit := range units {
		if strings.HasSuffix(unit.Name, ".service") {
			shortName := strings.TrimSuffix(unit.Name, ".service")
			for _, config := range systemdServices {
				if config.regex.MatchString(shortName) {
					if config.watch {
						err = watchSystemdServiceProc(&md, conn, unit)
						if err != nil {
							return nil, err
						}
					}
					if _, ok := activeState[unit.ActiveState]; !ok {
						return nil, fmt.Errorf("error: unit %s has unknown ActiveState: %s\n", unit.Name, unit.ActiveState)
					}
					systemdTags := opentsdb.TagSet{"name": unit.Name}
					osTags := opentsdb.TagSet{"name": shortName}
					Add(&md, "linux.systemd.unit.activestate", activeState[unit.ActiveState], systemdTags, metadata.Gauge, metadata.StatusCode, descActiveState)
					Add(&md, osServiceRunning, util.Btoi(unit.ActiveState == "active"), osTags, metadata.Gauge, metadata.Bool, osServiceRunningDesc)
				}
			}
		}
	}

	return md, err
}

// watchSystemdService() attempts to determine the main PID of a service and
// builds a WatchedProc{} which is then sent to linuxProcMonitor()
func watchSystemdServiceProc(md *opentsdb.MultiDataPoint, conn *dbus.Conn, unit dbus.UnitStatus) error {
	// ExecMainPID can be non-running. MainPID is the pid of the running service.
	mainPIDProp, err := conn.GetUnitTypeProperty(unit.Name, "Service", "MainPID")
	if err != nil {
		return err
	}

	mainPID := mainPIDProp.Value.Value().(uint32)
	// MainPID is 0 if there is no running service.
	if mainPID == 0 {
		return nil
	}

	cmdline, err := getLinuxCmdline(fmt.Sprint(mainPID))
	if err != nil {
		return err
	}
	if cmdline == nil {
		return nil
	}

	wp := WatchedProc{
		Command:   cmdline[0],
		Name:      strings.TrimSuffix(unit.Name, ".service"),
		Processes: make(map[string]int),
		ArgMatch:  regexp.MustCompile(""),
		idPool:    new(idPool)}

	// Since we only have one PID per service (at the moment), this is always set to 1
	wp.Processes[fmt.Sprint(mainPID)] = wp.get()

	if e := linuxProcMonitor(&wp, md); e != nil {
		return e
	}

	return err
}

var activeState = map[string]int{
	"active":       0,
	"reloading":    1,
	"inactive":     2,
	"failed":       3,
	"activating":   4,
	"deactivating": 5,
}

const (
	descActiveState = "0: active, 1: reloading, 2: inactive, 3: failed, 4: activating, 5: deactivating"
)
<|endoftext|>


<commit_before>package registry

type tagsResponse struct {
	Tags []string `json:"tags"`
}

func (registry *Registry) Tags(repository string) (tags []string, err error) {
	url := registry.url("/v2/%s/tags/list", repository)

	var response tagsResponse
	for {
		registry.Logf("registry.tags url=%s repository=%s", url, repository)
		url, err = registry.getPaginatedJson(url, &response)
		switch err {
		case ErrNoMorePages:
			tags = append(tags, response.Tags...)
			return tags, nil
		case nil:
			tags = append(tags, response.Tags...)
			continue
		default:
			return nil, err
		}
	}
}
<commit_msg>add get tag layer size<commit_after>package registry

type tagsResponse struct {
	Tags []string `json:"tags"`
}

func (registry *Registry) Tags(repository string) (tags []string, err error) {
	url := registry.url("/v2/%s/tags/list", repository)

	var response tagsResponse
	for {
		registry.Logf("registry.tags url=%s repository=%s", url, repository)
		url, err = registry.getPaginatedJson(url, &response)
		switch err {
		case ErrNoMorePages:
			tags = append(tags, response.Tags...)
			return tags, nil
		case nil:
			tags = append(tags, response.Tags...)
			continue
		default:
			return nil, err
		}
	}
}

func (registry *Registry) TagSize(repository, reference string) (size int64, err error) {
	deserialized, err := registry.Manifest(repository, reference)
	if err != nil {
		return -1, err
	}
	size = int64(0)
	for _, layer := range deserialized.Layers {
		size += layer.Size
	}
	return size, nil
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/pressly/imgry/imagick"

	"github.com/goware/urlx"
	"github.com/pressly/imgry"
	"github.com/zenazn/goji/web"
)

var (
	MimeTypes = map[string]string{
		"png":  "image/png",
		"jpeg": "image/jpeg",
		"jpg":  "image/jpeg",
		"bmp":  "image/bmp",
		"bm":   "image/bmp",
		"gif":  "image/gif",
		"ico":  "image/x-icon",
	}

	ErrInvalidURL = errors.New("invalid url")
)

func BucketGetIndex(c web.C, w http.ResponseWriter, r *http.Request) {
	if r.URL.Query().Get("url") == "" {
		respond.Data(w, 200, []byte{})
		return
	}
	BucketFetchItem(c, w, r)
}

func BucketGetItem(c web.C, w http.ResponseWriter, r *http.Request) {
	// TODO: bucket binding should happen in a middleware... refactor all handlers
	// that use it..
	bucket, err := NewBucket(c.URLParams["bucket"])
	if err != nil {
		lg.Error("Failed to create bucket for %s cause: %s", r.URL, err)
		respond.ImageError(w, 422, err)
		return
	}

	sizing, err := imgry.NewSizingFromQuery(r.URL.RawQuery)
	if err != nil {
		lg.Error("Failed to create sizing for %s cause: %s", r.URL, err)
		respond.ImageError(w, 422, err)
		return
	}

	im, err := bucket.GetImageSize(c.URLParams["key"], sizing)
	if err != nil {
		lg.Error("Failed to get image for %s cause: %s", r.URL, err)
		respond.ImageError(w, 422, err)
		return
	}

	w.Header().Set("Content-Type", im.MimeType())
	w.Header().Set("X-Meta-Width", fmt.Sprintf("%d", im.Width))
	w.Header().Set("X-Meta-Height", fmt.Sprintf("%d", im.Height))
	w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", app.Config.Server.CacheMaxAge))

	// If requested, only return the image details instead of the data
	if r.URL.Query().Get("info") != "" {
		// TODO: eventually, once the ruby stack is updated, we should
		// return an ImageInfo packet here instead..
		respond.JSON(w, http.StatusOK, im)
		return
	}

	respond.Data(w, 200, im.Data)
}

func BucketFetchItem(c web.C, w http.ResponseWriter, r *http.Request) {
	bucket, err := NewBucket(c.URLParams["bucket"])
	if err != nil {
		respond.ImageError(w, 422, err)
		return
	}

	fetchUrl := r.URL.Query().Get("url")
	if fetchUrl == "" {
		respond.ImageError(w, 422, ErrInvalidURL)
		return
	}

	u, err := urlx.Parse(fetchUrl)
	if err != nil {
		respond.ImageError(w, 422, ErrInvalidURL)
		return
	}
	fetchUrl = u.String()

	imKey := sha1Hash(fetchUrl) // transform to what is expected..
	c.URLParams["key"] = imKey

	// First check if we have the original.. a bit of extra overhead, but its okay
	_, err = bucket.DbFindImage(imKey, nil)
	if err != nil && err != ErrImageNotFound {
		respond.ImageError(w, 422, err)
		return
	}

	// lg.Info("BucketFetchItem, after DbFindImage... %v", err)

	// Fetch the image on-demand and add to bucket if we dont have it
	if err == ErrImageNotFound {
		_, err := bucket.AddImagesFromUrls([]string{fetchUrl})
		if err != nil {
			lg.Error("Fetching failed for %s because %s", fetchUrl, err)
			respond.ImageError(w, 422, err)
			return
		}
	}

	BucketGetItem(c, w, r)
}

// TODO: this can be optimized significantly..........
// Ping / DecodeConfig ... do we have to use image magick.......?
func GetImageInfo(c web.C, w http.ResponseWriter, r *http.Request) {
	url := r.URL.Query().Get("url")
	if url == "" {
		respond.ApiError(w, 422, errors.New("no image url"))
		return
	}

	response, err := app.HttpFetcher.Get(url)
	if err != nil {
		respond.ApiError(w, 422, err)
		return
	}
	data := response.Data

	ng := imagick.Engine{}
	imfo, err := ng.GetImageInfo(data)
	if err != nil {
		respond.ApiError(w, 422, err)
		return
	}
	imfo.URL = response.URL.String()
	imfo.Mimetype = MimeTypes[imfo.Format]

	w.Header().Set("X-Meta-Width", fmt.Sprintf("%d", imfo.Width))
	w.Header().Set("X-Meta-Height", fmt.Sprintf("%d", imfo.Height))
	w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", app.Config.Server.CacheMaxAge))
	respond.JSON(w, 200, imfo)
}

// Image upload to an s3 bucket, respond with a direct url to the uploaded
// image. Avoid using respond.ApiError() here to prevent any of the responses
// from being cached.
func BucketImageUpload(c web.C, w http.ResponseWriter, r *http.Request) {
	var url string
	var err error

	file, header, err := r.FormFile("file")
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}
	defer file.Close()

	data, err := ioutil.ReadAll(file)
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	im := NewImageFromSrcUrl(header.Filename)
	defer im.Release()

	im.Data = data
	if err = im.LoadImage(); err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	s3Bucket := getS3Bucket(app.Config.Chainstore.S3AccessKey,
		app.Config.Chainstore.S3SecretKey,
		app.Config.Chainstore.S3Bucket)

	path := s3Path(c.URLParams["bucket"], im.Data, im.Format)

	url, err = s3Upload(s3Bucket, path, im)
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	imfo := imgry.ImageInfo{
		URL:           url,
		Format:        im.Format,
		Mimetype:      im.MimeType(),
		Width:         im.Width,
		Height:        im.Height,
		AspectRatio:   float64(im.Width) / float64(im.Height),
		ContentLength: len(im.Data),
	}

	respond.JSON(w, 200, imfo)
}

func BucketAddItems(c web.C, w http.ResponseWriter, r *http.Request) {
	bucket, err := NewBucket(c.URLParams["bucket"])
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	urls := r.URL.Query()["url[]"]
	urls = append(urls, r.URL.Query()["url"]...)

	if len(urls) == 0 {
		respond.JSON(w, 422, map[string]interface{}{"error": "Url or urls parameter required"})
		return
	}

	images, err := bucket.AddImagesFromUrls(urls)
	if err != nil {
		// TODO: refactor.. ApiError will cache invalid image errors,
		// but for an array of urls, we shouldn't cache the entire response
		if len(urls) == 1 {
			respond.ApiError(w, 422, err)
		} else {
			respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		}
		return
	}

	respond.JSON(w, 200, images)
}

func BucketDeleteItem(c web.C, w http.ResponseWriter, r *http.Request) {
	bucket, err := NewBucket(c.URLParams["bucket"])
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	pUrl := r.URL.Query().Get("url")
	if pUrl != "" {
		pKey := sha1Hash(pUrl) // transform to what is expected..
		c.URLParams["key"] = pKey
	}
	imageKey := c.URLParams["key"]
	if imageKey == "" {
		respond.JSON(w, 422, map[string]interface{}{
			"error": "Unable to determine the key for the delete operation",
		})
		return
	}

	err = bucket.DbDelImage(imageKey)
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	respond.JSON(w, 200, []byte{})
}

// DEPRECATED
func BucketV0FetchItem(c web.C, w http.ResponseWriter, r *http.Request) {
	if c.URLParams == nil {
		c.URLParams = make(map[string]string)
	}
	c.URLParams["bucket"] = "tmp" // we imply the bucket name..
	BucketFetchItem(c, w, r)
}
<commit_msg>add base64 file upload support<commit_after>package server

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"

	"encoding/base64"

	"github.com/pressly/imgry/imagick"
	"github.com/pressly/omnibed/lib/lg"

	"github.com/goware/urlx"
	"github.com/pressly/imgry"
	"github.com/zenazn/goji/web"
)

var (
	MimeTypes = map[string]string{
		"png":  "image/png",
		"jpeg": "image/jpeg",
		"jpg":  "image/jpeg",
		"bmp":  "image/bmp",
		"bm":   "image/bmp",
		"gif":  "image/gif",
		"ico":  "image/x-icon",
	}

	ErrInvalidURL = errors.New("invalid url")
)

func BucketGetIndex(c web.C, w http.ResponseWriter, r *http.Request) {
	if r.URL.Query().Get("url") == "" {
		respond.Data(w, 200, []byte{})
		return
	}
	BucketFetchItem(c, w, r)
}

func BucketGetItem(c web.C, w http.ResponseWriter, r *http.Request) {
	// TODO: bucket binding should happen in a middleware... refactor all handlers
	// that use it..
	bucket, err := NewBucket(c.URLParams["bucket"])
	if err != nil {
		lg.Error("Failed to create bucket for %s cause: %s", r.URL, err)
		respond.ImageError(w, 422, err)
		return
	}

	sizing, err := imgry.NewSizingFromQuery(r.URL.RawQuery)
	if err != nil {
		lg.Error("Failed to create sizing for %s cause: %s", r.URL, err)
		respond.ImageError(w, 422, err)
		return
	}

	im, err := bucket.GetImageSize(c.URLParams["key"], sizing)
	if err != nil {
		lg.Error("Failed to get image for %s cause: %s", r.URL, err)
		respond.ImageError(w, 422, err)
		return
	}

	w.Header().Set("Content-Type", im.MimeType())
	w.Header().Set("X-Meta-Width", fmt.Sprintf("%d", im.Width))
	w.Header().Set("X-Meta-Height", fmt.Sprintf("%d", im.Height))
	w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", app.Config.Server.CacheMaxAge))

	// If requested, only return the image details instead of the data
	if r.URL.Query().Get("info") != "" {
		// TODO: eventually, once the ruby stack is updated, we should
		// return an ImageInfo packet here instead..
		respond.JSON(w, http.StatusOK, im)
		return
	}

	respond.Data(w, 200, im.Data)
}

func BucketFetchItem(c web.C, w http.ResponseWriter, r *http.Request) {
	bucket, err := NewBucket(c.URLParams["bucket"])
	if err != nil {
		respond.ImageError(w, 422, err)
		return
	}

	fetchUrl := r.URL.Query().Get("url")
	if fetchUrl == "" {
		respond.ImageError(w, 422, ErrInvalidURL)
		return
	}

	u, err := urlx.Parse(fetchUrl)
	if err != nil {
		respond.ImageError(w, 422, ErrInvalidURL)
		return
	}
	fetchUrl = u.String()

	imKey := sha1Hash(fetchUrl) // transform to what is expected..
	c.URLParams["key"] = imKey

	// First check if we have the original.. a bit of extra overhead, but its okay
	_, err = bucket.DbFindImage(imKey, nil)
	if err != nil && err != ErrImageNotFound {
		respond.ImageError(w, 422, err)
		return
	}

	// lg.Info("BucketFetchItem, after DbFindImage... %v", err)

	// Fetch the image on-demand and add to bucket if we dont have it
	if err == ErrImageNotFound {
		_, err := bucket.AddImagesFromUrls([]string{fetchUrl})
		if err != nil {
			lg.Error("Fetching failed for %s because %s", fetchUrl, err)
			respond.ImageError(w, 422, err)
			return
		}
	}

	BucketGetItem(c, w, r)
}

// TODO: this can be optimized significantly..........
// Ping / DecodeConfig ... do we have to use image magick.......?
func GetImageInfo(c web.C, w http.ResponseWriter, r *http.Request) {
	url := r.URL.Query().Get("url")
	if url == "" {
		respond.ApiError(w, 422, errors.New("no image url"))
		return
	}

	response, err := app.HttpFetcher.Get(url)
	if err != nil {
		respond.ApiError(w, 422, err)
		return
	}
	data := response.Data

	ng := imagick.Engine{}
	imfo, err := ng.GetImageInfo(data)
	if err != nil {
		respond.ApiError(w, 422, err)
		return
	}
	imfo.URL = response.URL.String()
	imfo.Mimetype = MimeTypes[imfo.Format]

	w.Header().Set("X-Meta-Width", fmt.Sprintf("%d", imfo.Width))
	w.Header().Set("X-Meta-Height", fmt.Sprintf("%d", imfo.Height))
	w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", app.Config.Server.CacheMaxAge))
	respond.JSON(w, 200, imfo)
}

// Image upload to an s3 bucket, respond with a direct url to the uploaded
// image. Avoid using respond.ApiError() here to prevent any of the responses
// from being cached.
func BucketImageUpload(c web.C, w http.ResponseWriter, r *http.Request) {
	var url string
	var err error
	var data []byte
	var im *Image

	file, header, err := r.FormFile("file")
	switch err {
	case nil:
		defer file.Close()

		data, err = ioutil.ReadAll(file)
		if err != nil {
			respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
			return
		}
		im = NewImageFromSrcUrl(header.Filename)

	case http.ErrMissingFile:
		base64file := r.FormValue("base64file")
		fileLen := len(base64file)
		if fileLen < 100 {
			respond.JSON(w, 422, map[string]interface{}{"error": "invalid file upload"})
			return
		}
		data, err = base64.StdEncoding.DecodeString(base64file)
		if err != nil {
			respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
			return
		}

		// balance collision chance vs hash time
		if fileLen > 10000 {
			fileLen = 10000
		}
		im = NewImageFromSrcUrl(string(base64file[0:fileLen]))

	default:
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	defer im.Release()

	im.Data = data
	if err = im.LoadImage(); err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	s3Bucket := getS3Bucket(app.Config.Chainstore.S3AccessKey,
		app.Config.Chainstore.S3SecretKey,
		app.Config.Chainstore.S3Bucket)

	path := s3Path(c.URLParams["bucket"], im.Data, im.Format)

	url, err = s3Upload(s3Bucket, path, im)
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	imfo := imgry.ImageInfo{
		URL:           url,
		Format:        im.Format,
		Mimetype:      im.MimeType(),
		Width:         im.Width,
		Height:        im.Height,
		AspectRatio:   float64(im.Width) / float64(im.Height),
		ContentLength: len(im.Data),
	}

	respond.JSON(w, 200, imfo)
}

func BucketAddItems(c web.C, w http.ResponseWriter, r *http.Request) {
	bucket, err := NewBucket(c.URLParams["bucket"])
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	urls := r.URL.Query()["url[]"]
	urls = append(urls, r.URL.Query()["url"]...)

	if len(urls) == 0 {
		respond.JSON(w, 422, map[string]interface{}{"error": "Url or urls parameter required"})
		return
	}

	images, err := bucket.AddImagesFromUrls(urls)
	if err != nil {
		// TODO: refactor.. ApiError will cache invalid image errors,
		// but for an array of urls, we shouldn't cache the entire response
		if len(urls) == 1 {
			respond.ApiError(w, 422, err)
		} else {
			respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		}
		return
	}

	respond.JSON(w, 200, images)
}

func BucketDeleteItem(c web.C, w http.ResponseWriter, r *http.Request) {
	bucket, err := NewBucket(c.URLParams["bucket"])
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	pUrl := r.URL.Query().Get("url")
	if pUrl != "" {
		pKey := sha1Hash(pUrl) // transform to what is expected..
		c.URLParams["key"] = pKey
	}
	imageKey := c.URLParams["key"]
	if imageKey == "" {
		respond.JSON(w, 422, map[string]interface{}{
			"error": "Unable to determine the key for the delete operation",
		})
		return
	}

	err = bucket.DbDelImage(imageKey)
	if err != nil {
		respond.JSON(w, 422, map[string]interface{}{"error": err.Error()})
		return
	}

	respond.JSON(w, 200, []byte{})
}

// DEPRECATED
func BucketV0FetchItem(c web.C, w http.ResponseWriter, r *http.Request) {
	if c.URLParams == nil {
		c.URLParams = make(map[string]string)
	}
	c.URLParams["bucket"] = "tmp" // we imply the bucket name..
	BucketFetchItem(c, w, r)
}
<|endoftext|>


<commit_before>// Copyright 2015 Claudemiro Alves Feitosa Neto. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gochip8

import (
	"testing"

	"math/rand"

	"github.com/bmizerany/assert"
)

// dumbSound - Only for testing purpuses
type dumbSound struct {
	BeepCalled bool
}

func (s *dumbSound) Beep() {
	(*s).BeepCalled = true
}

// newCpuAt creates a new cpu and write the ins to the pc memory location
// Only for testing purpuse
func newCpuAt(ins uint16) cpu {
	s := &dumbSound{}
	c := newCpu(s)
	c.memory.WriteWord(uint16(c.pc), ins)

	return c
}

func TestTimers(t *testing.T) {
	s := &dumbSound{}
	c := newCpu(s)

	// With Beep
	c.memory.WriteWord(uint16(c.pc), 0x0E0)

	c.dt = 1
	c.st = 1

	assert.Equal(t, s.BeepCalled, false)

	c.Step()

	assert.Equal(t, c.dt, byte(0))
	assert.Equal(t, c.st, byte(0))
	assert.Equal(t, s.BeepCalled, true)

	// No Beep
	c.memory.WriteWord(uint16(c.pc), 0x0E0)
	s.BeepCalled = false

	c.st = 2
	c.Step()

	assert.Equal(t, s.BeepCalled, false)
	assert.Equal(t, c.st, byte(1))
}

// 00E0 - CLS
func Test00E0(t *testing.T) {

	c := newCpuAt(0x00E0)
	c.gfx.SetPixel(0, 0)
	c.Step()

	for i := 0; i < 64; i++ {
		for j := 0; j < 32; j++ {
			assert.Equal(t, byte(COLOR_BLACK), byte(c.gfx.GetPixel(i, j)))
		}
	}
}

// 00EE - RET
func Test00EE(t *testing.T) {
	c := newCpuAt(0x00EE)

	c.stack.Push(pc(1))
	c.Step()

	assert.Equal(t, pc(1), c.pc)
}

// 1nnn - JP addr
func Test1000(t *testing.T) {
	c := newCpuAt(0x1FFF)
	c.Step()

	assert.Equal(t, pc(0x0FFF), c.pc)
}

// 2nnn - CALL addr
func Test2000(t *testing.T) {
	c := newCpuAt(0x2FFF)
	oldPC := c.pc + 2 // The increment happen before the Step

	c.Step()

	assert.Equal(t, oldPC, c.stack.Pop())
	assert.Equal(t, pc(0x0FFF), c.pc)
}

// 3xkk - SE Vx, byte
func Test3000Equal(t *testing.T) {
	c := newCpuAt(0x31FF)
	c.regs[1] = 0xFF

	oldPC := c.pc + 4
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 3xkk - SE Vx, byte
func Test3000NotEqual(t *testing.T) {
	c := newCpuAt(0x31FF)
	c.regs[1] = 0xAA

	oldPC := c.pc + 2
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 4xkk - SNE Vx, byte
func Test4000Equal(t *testing.T) {
	c := newCpuAt(0x41FF)
	c.regs[1] = 0xFF

	oldPC := c.pc + 2
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 4xkk - SNE Vx, byte
func Test4000NotEqual(t *testing.T) {
	c := newCpuAt(0x41FF)
	c.regs[1] = 0xAA

	oldPC := c.pc + 4
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 5xy0 - SE Vx, Vy
func Test5000Equal(t *testing.T) {
	c := newCpuAt(0x512F)
	c.regs[1] = 0xFF
	c.regs[2] = 0xFF

	oldPC := c.pc + 4
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 5xy0 - SE Vx, Vy
func Test5000NotEqual(t *testing.T) {
	c := newCpuAt(0x512F)
	c.regs[1] = 0xFF
	c.regs[2] = 0xAA

	oldPC := c.pc + 2
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 6xkk - LD Vx, byte
func Test6000(t *testing.T) {
	c := newCpuAt(0x61FF)
	c.Step()
	assert.Equal(t, c.regs[1], byte(0xFF))
}

// 7xkk - ADD Vx, byte
func Test7000(t *testing.T) {
	c := newCpuAt(0x7101)
	c.regs[1] = 1
	c.Step()
	assert.Equal(t, c.regs[1], byte(2))
}

// 8xy0 - LD Vx, Vy
func Test8xy0(t *testing.T) {
	c := newCpuAt(0x8120)
	c.regs[1] = 1
	c.regs[2] = 0
	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[2], byte(0))
	c.Step()
	assert.Equal(t, c.regs[1], byte(0))
	assert.Equal(t, c.regs[2], byte(0))
}

// 8xy1 - OR Vx, Vy
func Test8xy1(t *testing.T) {
	c := newCpuAt(0x8121)
	c.regs[1] = 1
	c.regs[2] = 2
	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[2], byte(2))
	c.Step()
	assert.Equal(t, c.regs[1], byte(1|2))
	assert.Equal(t, c.regs[2], byte(2))
}

// 8xy2 - OR Vx, Vy
func Test8xy2(t *testing.T) {
	c := newCpuAt(0x8122)
	c.regs[1] = 1
	c.regs[2] = 2
	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[2], byte(2))
	c.Step()
	assert.Equal(t, c.regs[1], byte(1&2))
	assert.Equal(t, c.regs[2], byte(2))
}

// 8xy3 - OR Vx, Vy
func Test8xy3(t *testing.T) {
	c := newCpuAt(0x8123)
	c.regs[1] = 1
	c.regs[2] = 2
	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[2], byte(2))
	c.Step()
	assert.Equal(t, c.regs[1], byte(1^2))
	assert.Equal(t, c.regs[2], byte(2))
}

// 8xy4 - ADD Vx, Vy
func Test8xy4Overflow(t *testing.T) {
	c := newCpuAt(0x8124)
	c.regs[1] = 0xFF
	c.regs[2] = 1

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(0))
	assert.Equal(t, c.regs[0xF], byte(1))
}

// 8xy4 - ADD Vx, Vy
func Test8xy4ONotverflow(t *testing.T) {
	c := newCpuAt(0x8124)
	c.regs[1] = 0xF0
	c.regs[2] = 1

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(0xF0+1))
	assert.Equal(t, c.regs[0xF], byte(0))
}

// 8xy5 - SUB Vx, Vy
func Test8xy5Greater(t *testing.T) {
	c := newCpuAt(0x8125)
	c.regs[1] = 2
	c.regs[2] = 1

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[0xF], byte(1))
}

// 8xy5 - SUB Vx, Vy
func Test8xy5NotGreater(t *testing.T) {
	c := newCpuAt(0x8125)
	c.regs[1] = 1
	c.regs[2] = 2

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(0xFF))
	assert.Equal(t, c.regs[0xF], byte(0))
}

// 8xy6 - SHR Vx {, Vy}
func Test8xy6(t *testing.T) {
	c := newCpuAt(0x8126)
	c.regs[1] = 1

	c.Step()

	assert.Equal(t, c.regs[0xF], byte(1&0x0001))
	assert.Equal(t, c.regs[1], byte(1/2))
}

// 8xy7 - SUBN Vx, Vy
func Test8xy7Greater(t *testing.T) {
	c := newCpuAt(0x8127)
	c.regs[1] = 1
	c.regs[2] = 2

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[0xF], byte(1))
}

// 8xy7 - SUBN Vx, Vy
func Test8xy7NotGreater(t *testing.T) {
	c := newCpuAt(0x8127)
	c.regs[1] = 2
	c.regs[2] = 1

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(0xFF))
	assert.Equal(t, c.regs[0xF], byte(0))
}

// 8xyE - SHL Vx {, Vy}
func Test8xyE(t *testing.T) {
	c := newCpuAt(0x812E)
	c.regs[1] = 1

	c.Step()

	assert.Equal(t, c.regs[0xF], byte(1&0x0001))
	assert.Equal(t, c.regs[1], byte(1*2))
}

// 9xy0 - SNE Vx, Vy
func Test9xy0NotEqual(t *testing.T) {
	c := newCpuAt(0x9120)
	c.regs[1] = 1
	c.regs[2] = 2

	oldPC := c.pc

	c.Step()

	assert.Equal(t, c.pc, oldPC+4)
}

// 9xy0 - SNE Vx, Vy
func Test9xy0Equal(t *testing.T) {
	c := newCpuAt(0x9120)
	c.regs[1] = 1
	c.regs[2] = 1

	oldPC := c.pc

	c.Step()

	assert.Equal(t, c.pc, oldPC+2)
}

// Annn - LD I, addr
func TestA000(t *testing.T) {
	c := newCpuAt(0xAFFF)

	c.Step()

	assert.Equal(t, c.i, uint16(0xFFF))
}

// Bnnn - JP V0, addr
func TestB000(t *testing.T) {
	c := newCpuAt(0xBFFF)
	c.regs[0] = 1

	c.Step()

	assert.Equal(t, c.pc, pc(0xFFF+1))
}

// Cxkk - RND Vx, byte
func TestC000(t *testing.T) {
	c := newCpuAt(0xC1FF)

	rand.Seed(1)         // Seed with a Known value
	expectedRandom := 33 // With the expected seed, 33 is the first random.

	c.Step()

	assert.Equal(t, c.regs[1], byte(expectedRandom&0xFF))
}

// TODO: Dxyn - DRW Vx, Vy, nibble

// Ex9E - SKP Vx
func TestEx9E_True(t *testing.T) {
	c := newCpuAt(0xE19E)
	c.keys[1] = true

	oldPC := c.pc + 4

	c.Step()

	assert.Equal(t, pc(oldPC), c.pc)
}

// Ex9E - SKP Vx
func TestEx9E_False(t *testing.T) {
	c := newCpuAt(0xE19E)
	c.keys[1] = false

	oldPC := c.pc + 2

	c.Step()

	assert.Equal(t, pc(oldPC), c.pc)
}

// ExA1 - SKNP Vx
func TestExA1_True(t *testing.T) {
	c := newCpuAt(0xE1A1)
	c.keys[1] = true

	oldPC := c.pc + 2

	c.Step()

	assert.Equal(t, pc(oldPC), c.pc)
}

// ExA1 - SKNP Vx
func TestExA1_False(t *testing.T) {
	c := newCpuAt(0xE1A1)
	c.keys[1] = false

	oldPC := c.pc + 4

	c.Step()

	assert.Equal(t, pc(oldPC), c.pc)
}
<commit_msg>Added test for 0xFx0A<commit_after>// Copyright 2015 Claudemiro Alves Feitosa Neto. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gochip8

import (
	"testing"

	"math/rand"

	"github.com/bmizerany/assert"
)

// dumbSound - Only for testing purpuses
type dumbSound struct {
	BeepCalled bool
}

func (s *dumbSound) Beep() {
	(*s).BeepCalled = true
}

// newCpuAt creates a new cpu and write the ins to the pc memory location
// Only for testing purpuse
func newCpuAt(ins uint16) cpu {
	s := &dumbSound{}
	c := newCpu(s)
	c.memory.WriteWord(uint16(c.pc), ins)

	return c
}

func TestTimers(t *testing.T) {
	s := &dumbSound{}
	c := newCpu(s)

	// With Beep
	c.memory.WriteWord(uint16(c.pc), 0x0E0)

	c.dt = 1
	c.st = 1

	assert.Equal(t, s.BeepCalled, false)

	c.Step()

	assert.Equal(t, c.dt, byte(0))
	assert.Equal(t, c.st, byte(0))
	assert.Equal(t, s.BeepCalled, true)

	// No Beep
	c.memory.WriteWord(uint16(c.pc), 0x0E0)
	s.BeepCalled = false

	c.st = 2
	c.Step()

	assert.Equal(t, s.BeepCalled, false)
	assert.Equal(t, c.st, byte(1))
}

// 00E0 - CLS
func Test00E0(t *testing.T) {

	c := newCpuAt(0x00E0)
	c.gfx.SetPixel(0, 0)
	c.Step()

	for i := 0; i < 64; i++ {
		for j := 0; j < 32; j++ {
			assert.Equal(t, byte(COLOR_BLACK), byte(c.gfx.GetPixel(i, j)))
		}
	}
}

// 00EE - RET
func Test00EE(t *testing.T) {
	c := newCpuAt(0x00EE)

	c.stack.Push(pc(1))
	c.Step()

	assert.Equal(t, pc(1), c.pc)
}

// 1nnn - JP addr
func Test1000(t *testing.T) {
	c := newCpuAt(0x1FFF)
	c.Step()

	assert.Equal(t, pc(0x0FFF), c.pc)
}

// 2nnn - CALL addr
func Test2000(t *testing.T) {
	c := newCpuAt(0x2FFF)
	oldPC := c.pc + 2 // The increment happen before the Step

	c.Step()

	assert.Equal(t, oldPC, c.stack.Pop())
	assert.Equal(t, pc(0x0FFF), c.pc)
}

// 3xkk - SE Vx, byte
func Test3000Equal(t *testing.T) {
	c := newCpuAt(0x31FF)
	c.regs[1] = 0xFF

	oldPC := c.pc + 4
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 3xkk - SE Vx, byte
func Test3000NotEqual(t *testing.T) {
	c := newCpuAt(0x31FF)
	c.regs[1] = 0xAA

	oldPC := c.pc + 2
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 4xkk - SNE Vx, byte
func Test4000Equal(t *testing.T) {
	c := newCpuAt(0x41FF)
	c.regs[1] = 0xFF

	oldPC := c.pc + 2
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 4xkk - SNE Vx, byte
func Test4000NotEqual(t *testing.T) {
	c := newCpuAt(0x41FF)
	c.regs[1] = 0xAA

	oldPC := c.pc + 4
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 5xy0 - SE Vx, Vy
func Test5000Equal(t *testing.T) {
	c := newCpuAt(0x512F)
	c.regs[1] = 0xFF
	c.regs[2] = 0xFF

	oldPC := c.pc + 4
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 5xy0 - SE Vx, Vy
func Test5000NotEqual(t *testing.T) {
	c := newCpuAt(0x512F)
	c.regs[1] = 0xFF
	c.regs[2] = 0xAA

	oldPC := c.pc + 2
	c.Step()

	assert.Equal(t, oldPC, c.pc)
}

// 6xkk - LD Vx, byte
func Test6000(t *testing.T) {
	c := newCpuAt(0x61FF)
	c.Step()
	assert.Equal(t, c.regs[1], byte(0xFF))
}

// 7xkk - ADD Vx, byte
func Test7000(t *testing.T) {
	c := newCpuAt(0x7101)
	c.regs[1] = 1
	c.Step()
	assert.Equal(t, c.regs[1], byte(2))
}

// 8xy0 - LD Vx, Vy
func Test8xy0(t *testing.T) {
	c := newCpuAt(0x8120)
	c.regs[1] = 1
	c.regs[2] = 0
	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[2], byte(0))
	c.Step()
	assert.Equal(t, c.regs[1], byte(0))
	assert.Equal(t, c.regs[2], byte(0))
}

// 8xy1 - OR Vx, Vy
func Test8xy1(t *testing.T) {
	c := newCpuAt(0x8121)
	c.regs[1] = 1
	c.regs[2] = 2
	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[2], byte(2))
	c.Step()
	assert.Equal(t, c.regs[1], byte(1|2))
	assert.Equal(t, c.regs[2], byte(2))
}

// 8xy2 - OR Vx, Vy
func Test8xy2(t *testing.T) {
	c := newCpuAt(0x8122)
	c.regs[1] = 1
	c.regs[2] = 2
	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[2], byte(2))
	c.Step()
	assert.Equal(t, c.regs[1], byte(1&2))
	assert.Equal(t, c.regs[2], byte(2))
}

// 8xy3 - OR Vx, Vy
func Test8xy3(t *testing.T) {
	c := newCpuAt(0x8123)
	c.regs[1] = 1
	c.regs[2] = 2
	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[2], byte(2))
	c.Step()
	assert.Equal(t, c.regs[1], byte(1^2))
	assert.Equal(t, c.regs[2], byte(2))
}

// 8xy4 - ADD Vx, Vy
func Test8xy4Overflow(t *testing.T) {
	c := newCpuAt(0x8124)
	c.regs[1] = 0xFF
	c.regs[2] = 1

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(0))
	assert.Equal(t, c.regs[0xF], byte(1))
}

// 8xy4 - ADD Vx, Vy
func Test8xy4ONotverflow(t *testing.T) {
	c := newCpuAt(0x8124)
	c.regs[1] = 0xF0
	c.regs[2] = 1

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(0xF0+1))
	assert.Equal(t, c.regs[0xF], byte(0))
}

// 8xy5 - SUB Vx, Vy
func Test8xy5Greater(t *testing.T) {
	c := newCpuAt(0x8125)
	c.regs[1] = 2
	c.regs[2] = 1

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[0xF], byte(1))
}

// 8xy5 - SUB Vx, Vy
func Test8xy5NotGreater(t *testing.T) {
	c := newCpuAt(0x8125)
	c.regs[1] = 1
	c.regs[2] = 2

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(0xFF))
	assert.Equal(t, c.regs[0xF], byte(0))
}

// 8xy6 - SHR Vx {, Vy}
func Test8xy6(t *testing.T) {
	c := newCpuAt(0x8126)
	c.regs[1] = 1

	c.Step()

	assert.Equal(t, c.regs[0xF], byte(1&0x0001))
	assert.Equal(t, c.regs[1], byte(1/2))
}

// 8xy7 - SUBN Vx, Vy
func Test8xy7Greater(t *testing.T) {
	c := newCpuAt(0x8127)
	c.regs[1] = 1
	c.regs[2] = 2

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(1))
	assert.Equal(t, c.regs[0xF], byte(1))
}

// 8xy7 - SUBN Vx, Vy
func Test8xy7NotGreater(t *testing.T) {
	c := newCpuAt(0x8127)
	c.regs[1] = 2
	c.regs[2] = 1

	assert.Equal(t, c.regs[0xF], byte(0))

	c.Step()

	assert.Equal(t, c.regs[1], byte(0xFF))
	assert.Equal(t, c.regs[0xF], byte(0))
}

// 8xyE - SHL Vx {, Vy}
func Test8xyE(t *testing.T) {
	c := newCpuAt(0x812E)
	c.regs[1] = 1

	c.Step()

	assert.Equal(t, c.regs[0xF], byte(1&0x0001))
	assert.Equal(t, c.regs[1], byte(1*2))
}

// 9xy0 - SNE Vx, Vy
func Test9xy0NotEqual(t *testing.T) {
	c := newCpuAt(0x9120)
	c.regs[1] = 1
	c.regs[2] = 2

	oldPC := c.pc

	c.Step()

	assert.Equal(t, c.pc, oldPC+4)
}

// 9xy0 - SNE Vx, Vy
func Test9xy0Equal(t *testing.T) {
	c := newCpuAt(0x9120)
	c.regs[1] = 1
	c.regs[2] = 1

	oldPC := c.pc

	c.Step()

	assert.Equal(t, c.pc, oldPC+2)
}

// Annn - LD I, addr
func TestA000(t *testing.T) {
	c := newCpuAt(0xAFFF)

	c.Step()

	assert.Equal(t, c.i, uint16(0xFFF))
}

// Bnnn - JP V0, addr
func TestB000(t *testing.T) {
	c := newCpuAt(0xBFFF)
	c.regs[0] = 1

	c.Step()

	assert.Equal(t, c.pc, pc(0xFFF+1))
}

// Cxkk - RND Vx, byte
func TestC000(t *testing.T) {
	c := newCpuAt(0xC1FF)

	rand.Seed(1)         // Seed with a Known value
	expectedRandom := 33 // With the expected seed, 33 is the first random.

	c.Step()

	assert.Equal(t, c.regs[1], byte(expectedRandom&0xFF))
}

// TODO: Dxyn - DRW Vx, Vy, nibble

// Ex9E - SKP Vx
func TestEx9E_True(t *testing.T) {
	c := newCpuAt(0xE19E)
	c.keys[1] = true

	oldPC := c.pc + 4

	c.Step()

	assert.Equal(t, pc(oldPC), c.pc)
}

// Ex9E - SKP Vx
func TestEx9E_False(t *testing.T) {
	c := newCpuAt(0xE19E)
	c.keys[1] = false

	oldPC := c.pc + 2

	c.Step()

	assert.Equal(t, pc(oldPC), c.pc)
}

// ExA1 - SKNP Vx
func TestExA1_True(t *testing.T) {
	c := newCpuAt(0xE1A1)
	c.keys[1] = true

	oldPC := c.pc + 2

	c.Step()

	assert.Equal(t, pc(oldPC), c.pc)
}

// ExA1 - SKNP Vx
func TestExA1_False(t *testing.T) {
	c := newCpuAt(0xE1A1)
	c.keys[1] = false

	oldPC := c.pc + 4

	c.Step()

	assert.Equal(t, pc(oldPC), c.pc)
}

// Fx07 - LD Vx, DT
func TestFx0A(t *testing.T) {
	c := newCpuAt(0xF107)
	c.regs[1] = 1
	c.dt = 2

	c.Step()

	assert.Equal(t, c.regs[1], byte(2))
}
<|endoftext|>


<commit_before>package bagman

import (
	"fmt"
	"io"
	"os"
	"strings"
	"crypto/md5"
//	"io/ioutil"
	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"
)

// Returns an S3 client for the specified region, using
// AWS credentials from the environment. Please keep your AWS
// keys out of the source code repos! Store them somewhere
// else and load them into environment variables AWS_ACCESS_KEY_ID
// and AWS_SECRET_ACCESS_KEY.
func GetClient(region aws.Region) (*s3.S3, error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		return nil, err
	}
	return s3.New(auth, region), nil
}

// Returns a list of keys in the specified bucket.
// If limit is zero, this will return all the keys in the bucket;
// otherwise, it will return only the number of keys specifed.
// Note that listing all keys may result in the underlying client
// issuing multiple requests.
func ListBucket(bucket *s3.Bucket, limit int) (keys []s3.Key, err error) {
	actualLimit := limit
	if limit == 0 {
		actualLimit = 1000
	}
	bucketList, err := bucket.List("", "/", "", actualLimit)
	if err != nil {
		return nil, err
	}
	contents := bucketList.Contents
	if len(contents) == 0 {
		return contents, nil
	}
	for limit == 0 {
		lastKey := contents[len(contents) - 1].Key
		bucketList, err := bucket.List("", "/", lastKey, actualLimit)
		if err != nil {
			return nil, err
		}
		contents = append(contents, bucketList.Contents ...)
		if !bucketList.IsTruncated {
			break
		}
	}
	return contents, nil
}

// Fetches key from bucket and saves it to path.
// This validates the md5 sum of the byte stream before
// saving to disk. If the md5 sum of the downloaded bytes
// does not match the md5 sum in the key, this will not
// save the file. It will just return an error.
func FetchToFile(bucket *s3.Bucket, key s3.Key, path string) (fetchResult *FetchResult) {
	result := new(FetchResult)
	result.BucketName = bucket.Name
	result.Key = key.Key
	result.LocalTarFile = path

	// In general, we want to retry if the fetch operation
	// fails. We will override this in certain cases below.
	result.Retry = true

	// S3 etag is md5 hex string enclosed in quotes,
	// unless file was a multipart upload. See below for that.
	result.RemoteMd5 = strings.Replace(key.ETag, "\"", "", -1)

	// Fetch the file into a reader instead of using the usual bucket.Get().
	// Files may be up to 250GB, so we want to process them as streams.
	// If we get an error here, it's typically a network error, and we
	// will want to retry later.
	readCloser, err := bucket.GetReader(key.Key)
	if err != nil {
		result.Error = err
		if strings.Contains(err.Error(), "key does not exist") {
			result.Retry = false
		}
		return result
	}
	defer readCloser.Close()

	// Write the contents of the stream into both our md5 hasher
	// and the file.
	md5Hash := md5.New()
	outputFile, err := os.Create(path)
	if err != nil {
		result.Error = err
		return result
	}
	defer outputFile.Close()

	multiWriter := io.MultiWriter(outputFile, md5Hash)
	bytesWritten, err := io.Copy(multiWriter, readCloser)
	if err != nil {
		result.Error = err
		return result
	}
	if bytesWritten != key.Size {
		result.Error = fmt.Errorf("Wrote only %d of %d bytes for %s", bytesWritten, key.Size, key.Key)
		return result
	}

	result.LocalMd5 = fmt.Sprintf("%x", md5Hash.Sum(nil))

	// ETag for S3 multi-part upload is not an accurate md5 sum.
	// If the ETag ends with a dash and some number, it's a
	// multi-part upload.
	if strings.Contains(result.RemoteMd5, "-") {
		result.Warning = fmt.Sprintf("Skipping md5 check on %s: this was a multi-part upload", key.Key)
		result.Md5Verified = false
		result.Md5Verifiable = false
	} else {
		result.Md5Verifiable = true
		result.Md5Verified = true
		if result.LocalMd5 != result.RemoteMd5 {
			os.Remove(path)
			result.Error = fmt.Errorf("Our md5 sum '%x' does not match the S3 md5 sum '%s'",
				result.LocalMd5, result.RemoteMd5)
			result.Md5Verified = false
			// Don't bother reprocessing this item.
			result.Retry = false
		}
	}
	return result
}
<commit_msg>Moved some bucket list functions from cli to s3.<commit_after>package bagman

import (
	"fmt"
	"io"
	"os"
	"strings"
	"crypto/md5"
	"errors"
	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"
)

// Returns an S3 client for the specified region, using
// AWS credentials from the environment. Please keep your AWS
// keys out of the source code repos! Store them somewhere
// else and load them into environment variables AWS_ACCESS_KEY_ID
// and AWS_SECRET_ACCESS_KEY.
func GetClient(region aws.Region) (*s3.S3, error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		return nil, err
	}
	return s3.New(auth, region), nil
}

// Returns a list of keys in the specified bucket.
// If limit is zero, this will return all the keys in the bucket;
// otherwise, it will return only the number of keys specifed.
// Note that listing all keys may result in the underlying client
// issuing multiple requests.
func ListBucket(bucket *s3.Bucket, limit int) (keys []s3.Key, err error) {
	actualLimit := limit
	if limit == 0 {
		actualLimit = 1000
	}
	bucketList, err := bucket.List("", "/", "", actualLimit)
	if err != nil {
		return nil, err
	}
	contents := bucketList.Contents
	if len(contents) == 0 {
		return contents, nil
	}
	for limit == 0 {
		lastKey := contents[len(contents) - 1].Key
		bucketList, err := bucket.List("", "/", lastKey, actualLimit)
		if err != nil {
			return nil, err
		}
		contents = append(contents, bucketList.Contents ...)
		if !bucketList.IsTruncated {
			break
		}
	}
	return contents, nil
}

// Fetches key from bucket and saves it to path.
// This validates the md5 sum of the byte stream before
// saving to disk. If the md5 sum of the downloaded bytes
// does not match the md5 sum in the key, this will not
// save the file. It will just return an error.
func FetchToFile(bucket *s3.Bucket, key s3.Key, path string) (fetchResult *FetchResult) {
	result := new(FetchResult)
	result.BucketName = bucket.Name
	result.Key = key.Key
	result.LocalTarFile = path

	// In general, we want to retry if the fetch operation
	// fails. We will override this in certain cases below.
	result.Retry = true

	// S3 etag is md5 hex string enclosed in quotes,
	// unless file was a multipart upload. See below for that.
	result.RemoteMd5 = strings.Replace(key.ETag, "\"", "", -1)

	// Fetch the file into a reader instead of using the usual bucket.Get().
	// Files may be up to 250GB, so we want to process them as streams.
	// If we get an error here, it's typically a network error, and we
	// will want to retry later.
	readCloser, err := bucket.GetReader(key.Key)
	if err != nil {
		result.Error = err
		if strings.Contains(err.Error(), "key does not exist") {
			result.Retry = false
		}
		return result
	}
	defer readCloser.Close()

	// Write the contents of the stream into both our md5 hasher
	// and the file.
	md5Hash := md5.New()
	outputFile, err := os.Create(path)
	if err != nil {
		result.Error = err
		return result
	}
	defer outputFile.Close()

	multiWriter := io.MultiWriter(outputFile, md5Hash)
	bytesWritten, err := io.Copy(multiWriter, readCloser)
	if err != nil {
		result.Error = err
		return result
	}
	if bytesWritten != key.Size {
		result.Error = fmt.Errorf("Wrote only %d of %d bytes for %s", bytesWritten, key.Size, key.Key)
		return result
	}

	result.LocalMd5 = fmt.Sprintf("%x", md5Hash.Sum(nil))

	// ETag for S3 multi-part upload is not an accurate md5 sum.
	// If the ETag ends with a dash and some number, it's a
	// multi-part upload.
	if strings.Contains(result.RemoteMd5, "-") {
		result.Warning = fmt.Sprintf("Skipping md5 check on %s: this was a multi-part upload", key.Key)
		result.Md5Verified = false
		result.Md5Verifiable = false
	} else {
		result.Md5Verifiable = true
		result.Md5Verified = true
		if result.LocalMd5 != result.RemoteMd5 {
			os.Remove(path)
			result.Error = fmt.Errorf("Our md5 sum '%x' does not match the S3 md5 sum '%s'",
				result.LocalMd5, result.RemoteMd5)
			result.Md5Verified = false
			// Don't bother reprocessing this item.
			result.Retry = false
		}
	}
	return result
}

// Collects info about all of the buckets listed in buckets.
// TODO: Write unit test
func CheckAllBuckets(buckets []string) (bucketSummaries []*BucketSummary, err error) {
	bucketSummaries = make([]*BucketSummary, 0)
	for _, bucketName := range(buckets) {
		bucketSummary, err := CheckBucket(bucketName)
		if err != nil {
			return bucketSummaries, err
		}
		bucketSummaries = append(bucketSummaries, bucketSummary)
	}
	return bucketSummaries, nil
}

// Returns info about the contents of the bucket named bucketName.
// BucketSummary contains the bucket name, a list of keys, and the
// size of the largest file in the bucket.
// TODO: Write unit test
func CheckBucket(bucketName string) (bucketSummary *BucketSummary, err error) {
	client, err := GetClient(aws.USEast)
	if err != nil {
		return nil, err
	}
	bucket := client.Bucket(bucketName)
	if bucket == nil {
		err = errors.New(fmt.Sprintf("Cannot retrieve bucket: %s", bucketName))
		return nil, err
	}
	bucketSummary = new(BucketSummary)
	bucketSummary.BucketName = bucketName
	bucketSummary.Keys, err = ListBucket(bucket, 0)
	if err != nil {
		return nil, err
	}
	return bucketSummary, nil
}
<|endoftext|>