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

package state

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"
)

// Space represents the state of a space.
// A space is a security subdivision of a network. In practice, a space
// is a collection of related subnets that have no firewalls between
// each other, and that have the same ingress and egress policies.
type Space struct {
	st  *State
	doc spaceDoc
}

type spaceDoc struct {
	DocID    string `bson:"_id"`
	EnvUUID  string `bson:"env-uuid"`
	Life     Life   `bson:"life"`
	Name     string `bson:"name"`
	IsPublic bool   `bson:"is-public"`
}

// Life returns whether the space is Alive, Dying or Dead.
func (s *Space) Life() Life {
	return s.doc.Life
}

// ID returns the unique id for the space, for other entities to reference it
func (s *Space) ID() string {
	return s.doc.DocID
}

// String implements fmt.Stringer.
func (s *Space) String() string {
	return s.doc.Name
}

// Name returns the name of the Space.
func (s *Space) Name() string {
	return s.doc.Name
}

// Subnets returns all the subnets associated with the Space.
func (s *Space) Subnets() (results []*Subnet, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot fetch subnets")
	name := s.Name()

	subnetsCollection, closer := s.st.getCollection(subnetsC)
	defer closer()

	var doc subnetDoc
	iter := subnetsCollection.Find(bson.D{{"space-name", name}}).Iter()
	defer iter.Close()
	for iter.Next(&doc) {
		subnet := &Subnet{s.st, doc}
		results = append(results, subnet)
	}
	if err := iter.Err(); err != nil {
		return nil, err
	}
	return results, nil
}

// AddSpace creates and returns a new space.
func (st *State) AddSpace(name string, subnets []string, isPublic bool) (newSpace *Space, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot add space %q", name)
	if !names.IsValidSpace(name) {
		return nil, errors.NewNotValid(nil, "invalid space name")
	}

	spaceID := st.docID(name)
	spaceDoc := spaceDoc{
		DocID:    spaceID,
		EnvUUID:  st.EnvironUUID(),
		Life:     Alive,
		Name:     name,
		IsPublic: isPublic,
	}
	newSpace = &Space{doc: spaceDoc, st: st}

	ops := []txn.Op{{
		C:      spacesC,
		Id:     spaceID,
		Assert: txn.DocMissing,
		Insert: spaceDoc,
	}}

	for _, subnetId := range subnets {
		ops = append(ops, txn.Op{
			C:      subnetsC,
			Id:     st.docID(subnetId),
			Assert: txn.DocExists,
			Update: bson.D{{"$set", bson.D{{"space-name", name}}}},
		})
	}

	if err = st.runTransaction(ops); err == txn.ErrAborted {
		if _, err = st.Space(name); err == nil {
			return nil, errors.AlreadyExistsf("space %q", name)
		}
		for _, subnetId := range subnets {
			_, err := st.Subnet(subnetId)
			if errors.IsNotFound(err) {
				return nil, err
			}
		}
	}
	return newSpace, errors.Trace(err)
}

// Space returns a space from state that matches the provided name. An error
// is returned if the space doesn't exist or if there was a problem accessing
// its information.
func (st *State) Space(name string) (*Space, error) {
	spaces, closer := st.getCollection(spacesC)
	defer closer()

	var doc spaceDoc
	err := spaces.FindId(name).One(&doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("space %q", name)
	}
	if err != nil {
		return nil, errors.Annotatef(err, "cannot get space %q", name)
	}
	return &Space{st, doc}, nil
}

// AllSpaces returns all spaces from state.
func (st *State) AllSpaces() ([]*Space, error) {
	spacesCollection, closer := st.getCollection(spacesC)
	defer closer()

	docs := []spaceDoc{}
	var spaces []*Space
	err := spacesCollection.Find(nil).All(&docs)
	if err != nil {
		return nil, errors.Annotatef(err, "cannot get all spaces")
	}
	for _, doc := range docs {
		spaces = append(spaces, &Space{st: st, doc: doc})
	}
	return spaces, nil
}

// EnsureDead sets the Life of the space to Dead, if it's Alive. It
// does nothing otherwise.
func (s *Space) EnsureDead() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set space %q to dead", s)

	if s.doc.Life == Dead {
		return nil
	}

	ops := []txn.Op{{
		C:      spacesC,
		Id:     s.doc.DocID,
		Update: bson.D{{"$set", bson.D{{"life", Dead}}}},
		Assert: isAliveDoc,
	}}
	if err = s.st.runTransaction(ops); err != nil {
		// Ignore ErrAborted if it happens, otherwise return err.
		return onAbort(err, nil)
	}
	s.doc.Life = Dead
	return nil
}

// Remove removes a dead space. If the space is not dead it returns an error.
func (s *Space) Remove() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot remove space %q", s)

	if s.doc.Life != Dead {
		return errors.New("space is not dead")
	}

	ops := []txn.Op{{
		C:      spacesC,
		Id:     s.doc.DocID,
		Remove: true,
		Assert: notDeadDoc,
	}}

	err = s.st.runTransaction(ops)
	if err == mgo.ErrNotFound {
		return nil
	}
	return err
}

// Refresh: refreshes the contents of the Space from the underlying
// state. It returns an error that satisfies errors.IsNotFound if the Space has
// been removed.
func (s *Space) Refresh() error {
	spaces, closer := s.st.getCollection(spacesC)
	defer closer()

	err := spaces.FindId(s.doc.DocID).One(&s.doc)
	if err == mgo.ErrNotFound {
		return errors.NotFoundf("space %q", s)
	}
	if err != nil {
		return errors.Errorf("cannot refresh space %q: %v", s, err)
	}
	return nil
}
<commit_msg>Tweaks from review<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"
)

// Space represents the state of a space.
// A space is a security subdivision of a network. In practice, a space
// is a collection of related subnets that have no firewalls between
// each other, and that have the same ingress and egress policies.
type Space struct {
	st  *State
	doc spaceDoc
}

type spaceDoc struct {
	DocID    string `bson:"_id"`
	EnvUUID  string `bson:"env-uuid"`
	Life     Life   `bson:"life"`
	Name     string `bson:"name"`
	IsPublic bool   `bson:"is-public"`
}

// Life returns whether the space is Alive, Dying or Dead.
func (s *Space) Life() Life {
	return s.doc.Life
}

// ID returns the unique id for the space, for other entities to reference it
func (s *Space) ID() string {
	return s.doc.DocID
}

// String implements fmt.Stringer.
func (s *Space) String() string {
	return s.doc.Name
}

// Name returns the name of the Space.
func (s *Space) Name() string {
	return s.doc.Name
}

// Subnets returns all the subnets associated with the Space.
func (s *Space) Subnets() (results []*Subnet, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot fetch subnets")
	name := s.Name()

	subnetsCollection, closer := s.st.getCollection(subnetsC)
	defer closer()

	var doc subnetDoc
	iter := subnetsCollection.Find(bson.D{{"space-name", name}}).Iter()
	defer iter.Close()
	for iter.Next(&doc) {
		subnet := &Subnet{s.st, doc}
		results = append(results, subnet)
	}
	if err := iter.Err(); err != nil {
		return nil, err
	}
	return results, nil
}

// AddSpace creates and returns a new space.
func (st *State) AddSpace(name string, subnets []string, isPublic bool) (newSpace *Space, err error) {
	defer errors.DeferredAnnotatef(&err, "cannot add space %q", name)
	if !names.IsValidSpace(name) {
		return nil, errors.NewNotValid(nil, "invalid space name")
	}

	spaceID := st.docID(name)
	spaceDoc := spaceDoc{
		DocID:    spaceID,
		EnvUUID:  st.EnvironUUID(),
		Life:     Alive,
		Name:     name,
		IsPublic: isPublic,
	}
	newSpace = &Space{doc: spaceDoc, st: st}

	ops := []txn.Op{{
		C:      spacesC,
		Id:     spaceID,
		Assert: txn.DocMissing,
		Insert: spaceDoc,
	}}

	for _, subnetId := range subnets {
		// TODO:(mfoord) once we have refcounting for subnets we should
		// also assert that the refcount is zero as moving the space of a
		// subnet in use is not permitted.
		ops = append(ops, txn.Op{
			C:      subnetsC,
			Id:     st.docID(subnetId),
			Assert: txn.DocExists,
			Update: bson.D{{"$set", bson.D{{"space-name", name}}}},
		})
	}

	if err = st.runTransaction(ops); err == txn.ErrAborted {
		if _, err = st.Space(name); err == nil {
			return nil, errors.AlreadyExistsf("space %q", name)
		}
		for _, subnetId := range subnets {
			_, err := st.Subnet(subnetId)
			if errors.IsNotFound(err) {
				return nil, err
			}
		}
	}
	return newSpace, errors.Trace(err)
}

// Space returns a space from state that matches the provided name. An error
// is returned if the space doesn't exist or if there was a problem accessing
// its information.
func (st *State) Space(name string) (*Space, error) {
	spaces, closer := st.getCollection(spacesC)
	defer closer()

	var doc spaceDoc
	err := spaces.FindId(name).One(&doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("space %q", name)
	}
	if err != nil {
		return nil, errors.Annotatef(err, "cannot get space %q", name)
	}
	return &Space{st, doc}, nil
}

// AllSpaces returns all spaces for the environment.
func (st *State) AllSpaces() ([]*Space, error) {
	spacesCollection, closer := st.getCollection(spacesC)
	defer closer()

	docs := []spaceDoc{}
	err := spacesCollection.Find(nil).All(&docs)
	if err != nil {
		return nil, errors.Annotatef(err, "cannot get all spaces")
	}
	spaces := make([]*Space, len(docs))
	for i, doc := range docs {
		spaces[i] = &Space{st: st, doc: doc}
	}
	return spaces, nil
}

// EnsureDead sets the Life of the space to Dead, if it's Alive. It
// does nothing otherwise.
func (s *Space) EnsureDead() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot set space %q to dead", s)

	if s.doc.Life == Dead {
		return nil
	}

	ops := []txn.Op{{
		C:      spacesC,
		Id:     s.doc.DocID,
		Update: bson.D{{"$set", bson.D{{"life", Dead}}}},
		Assert: isAliveDoc,
	}}
	if err = s.st.runTransaction(ops); err != nil {
		// Ignore ErrAborted if it happens, otherwise return err.
		return onAbort(err, nil)
	}
	s.doc.Life = Dead
	return nil
}

// Remove removes a dead space. If the space is not dead it returns an error.
func (s *Space) Remove() (err error) {
	defer errors.DeferredAnnotatef(&err, "cannot remove space %q", s)

	if s.doc.Life != Dead {
		return errors.New("space is not dead")
	}

	ops := []txn.Op{{
		C:      spacesC,
		Id:     s.doc.DocID,
		Remove: true,
		Assert: notDeadDoc,
	}}

	err = s.st.runTransaction(ops)
	if err == mgo.ErrNotFound {
		return nil
	}
	return err
}

// Refresh: refreshes the contents of the Space from the underlying
// state. It returns an error that satisfies errors.IsNotFound if the Space has
// been removed.
func (s *Space) Refresh() error {
	spaces, closer := s.st.getCollection(spacesC)
	defer closer()

	err := spaces.FindId(s.doc.DocID).One(&s.doc)
	if err == mgo.ErrNotFound {
		return errors.NotFoundf("space %q", s)
	}
	if err != nil {
		return errors.Errorf("cannot refresh space %q: %v", s, err)
	}
	return nil
}
<|endoftext|>


<commit_before>package extensions

import (
	"encoding/json"
	"github.com/pawelszydlo/papa-bot"
	"github.com/pawelszydlo/papa-bot/events"
	"github.com/pawelszydlo/papa-bot/utils"
	"strings"
	"text/template"
	"time"
)

// ExtensionLastSpoken tracks when a person has last spoken.
type ExtensionLastSpoken struct {
	LastAsk    map[string]map[string]time.Time
	LastSpoken map[string]map[string]time.Time

	Texts *ExtensionLastSpokenTexts
	bot   *papaBot.Bot
}

type ExtensionLastSpokenTexts struct {
	NeverSpoken    string
	TempLastSpoken *template.Template
}

// Init inits the extension.
func (ext *ExtensionLastSpoken) Init(bot *papaBot.Bot) error {
	// Register new command.
	bot.RegisterCommand(&papaBot.BotCommand{
		[]string{"ls", "lastspoken"},
		false, false, false,
		"<nick>", "Show when the person was last seen speaking.",
		ext.commandLastSpoken})
	// Load texts.
	texts := new(ExtensionLastSpokenTexts) // Can't load directly because of reflection issues.
	if err := bot.LoadTexts("last_spoken", texts); err != nil {
		return err
	}
	ext.Texts = texts
	ext.bot = bot
	// Init first level maps.
	ext.LastAsk = map[string]map[string]time.Time{}
	ext.LastSpoken = map[string]map[string]time.Time{}
	// Attach event handler.
	bot.EventDispatcher.RegisterListener(events.EventChatMessage, ext.ChatListener)
	// Load previous data.
	if err := json.Unmarshal([]byte(bot.GetVar("LastSpoken")), &ext.LastSpoken); err != nil {
		ext.bot.Log.Warningf("Error parsing JSON from LastSpoken: %s", err)
	} else {
		ext.bot.Log.Infof("Successfully loaded previous LastSpoken data for %d channels.", len(ext.LastSpoken))
	}
	return nil
}

func (ext *ExtensionLastSpoken) saveLastSpoken() {
	jsonString, err := json.Marshal(ext.LastSpoken)
	if err != nil {
		ext.bot.Log.Errorf("Can't save LastSpoken data: %s", err)
	} else {
		ext.bot.Log.Infof("Saving last spoken data: %s", string(jsonString))
		ext.bot.SetVar("LastSpoken", string(jsonString))
	}
}

// ChatListener listens to chat messages and records who has spoken.
func (ext *ExtensionLastSpoken) ChatListener(message events.EventMessage) {
	// Make sure the channel map is initialized.
	if ext.LastSpoken[message.ChannelId()] == nil {
		ext.LastSpoken[message.ChannelId()] = map[string]time.Time{}
	}
	ext.LastSpoken[message.ChannelId()][message.Nick] = time.Now()
	ext.saveLastSpoken()
}

func (ext *ExtensionLastSpoken) commandLastSpoken(bot *papaBot.Bot, sourceEvent *events.EventMessage, params []string) {
	if len(params) < 1 {
		return
	}
	nick := strings.Join(params, " ")
	// Make sure the channel maps are initialized.
	if ext.LastAsk[sourceEvent.ChannelId()] == nil {
		ext.LastAsk[sourceEvent.ChannelId()] = map[string]time.Time{}
	}
	if ext.LastSpoken[sourceEvent.ChannelId()] == nil {
		ext.LastSpoken[sourceEvent.ChannelId()] = map[string]time.Time{}
	}
	// Answer only once per 5 minutes per channel.
	if time.Since(ext.LastAsk[sourceEvent.ChannelId()][nick]) > 5*time.Minute {
		message := ext.Texts.NeverSpoken
		ext.LastAsk[sourceEvent.ChannelId()][nick] = time.Now()
		if !ext.LastSpoken[sourceEvent.ChannelId()][nick].IsZero() {
			message = utils.Format(ext.Texts.TempLastSpoken, map[string]string{
				"heard": ext.bot.Humanizer.TimeDiffNow(
					utils.MustForceLocalTimezone(ext.LastSpoken[sourceEvent.ChannelId()][nick]), false),
				"nick": nick,
			})
		}
		bot.SendMessage(sourceEvent, message)
	}
}
<commit_msg>Change "last spoken" extension to display exact time.<commit_after>package extensions

import (
	"encoding/json"
	"github.com/pawelszydlo/papa-bot"
	"github.com/pawelszydlo/papa-bot/events"
	"github.com/pawelszydlo/papa-bot/utils"
	"strings"
	"text/template"
	"time"
)

// ExtensionLastSpoken tracks when a person has last spoken.
type ExtensionLastSpoken struct {
	LastAsk    map[string]map[string]time.Time
	LastSpoken map[string]map[string]time.Time

	Texts *ExtensionLastSpokenTexts
	bot   *papaBot.Bot
}

type ExtensionLastSpokenTexts struct {
	NeverSpoken    string
	TempLastSpoken *template.Template
}

// Init inits the extension.
func (ext *ExtensionLastSpoken) Init(bot *papaBot.Bot) error {
	// Register new command.
	bot.RegisterCommand(&papaBot.BotCommand{
		[]string{"ls", "lastspoken"},
		false, false, false,
		"<nick>", "Show when the person was last seen speaking.",
		ext.commandLastSpoken})
	// Load texts.
	texts := new(ExtensionLastSpokenTexts) // Can't load directly because of reflection issues.
	if err := bot.LoadTexts("last_spoken", texts); err != nil {
		return err
	}
	ext.Texts = texts
	ext.bot = bot
	// Init first level maps.
	ext.LastAsk = map[string]map[string]time.Time{}
	ext.LastSpoken = map[string]map[string]time.Time{}
	// Attach event handler.
	bot.EventDispatcher.RegisterListener(events.EventChatMessage, ext.ChatListener)
	// Load previous data.
	if err := json.Unmarshal([]byte(bot.GetVar("LastSpoken")), &ext.LastSpoken); err != nil {
		ext.bot.Log.Warningf("Error parsing JSON from LastSpoken: %s", err)
	} else {
		ext.bot.Log.Infof("Successfully loaded previous LastSpoken data for %d channels.", len(ext.LastSpoken))
	}
	return nil
}

func (ext *ExtensionLastSpoken) saveLastSpoken() {
	jsonString, err := json.Marshal(ext.LastSpoken)
	if err != nil {
		ext.bot.Log.Errorf("Can't save LastSpoken data: %s", err)
	} else {
		ext.bot.Log.Infof("Saving last spoken data: %s", string(jsonString))
		ext.bot.SetVar("LastSpoken", string(jsonString))
	}
}

// ChatListener listens to chat messages and records who has spoken.
func (ext *ExtensionLastSpoken) ChatListener(message events.EventMessage) {
	// Make sure the channel map is initialized.
	if ext.LastSpoken[message.ChannelId()] == nil {
		ext.LastSpoken[message.ChannelId()] = map[string]time.Time{}
	}
	ext.LastSpoken[message.ChannelId()][message.Nick] = time.Now()
	ext.saveLastSpoken()
}

func (ext *ExtensionLastSpoken) commandLastSpoken(bot *papaBot.Bot, sourceEvent *events.EventMessage, params []string) {
	if len(params) < 1 {
		return
	}
	nick := strings.Join(params, " ")
	// Make sure the channel maps are initialized.
	if ext.LastAsk[sourceEvent.ChannelId()] == nil {
		ext.LastAsk[sourceEvent.ChannelId()] = map[string]time.Time{}
	}
	if ext.LastSpoken[sourceEvent.ChannelId()] == nil {
		ext.LastSpoken[sourceEvent.ChannelId()] = map[string]time.Time{}
	}
	// Answer only once per 5 minutes per channel.
	if time.Since(ext.LastAsk[sourceEvent.ChannelId()][nick]) > 5*time.Minute {
		message := ext.Texts.NeverSpoken
		ext.LastAsk[sourceEvent.ChannelId()][nick] = time.Now()
		if !ext.LastSpoken[sourceEvent.ChannelId()][nick].IsZero() {
			message = utils.Format(ext.Texts.TempLastSpoken, map[string]string{
				"heard": ext.bot.Humanizer.TimeDiffNow(
					utils.MustForceLocalTimezone(ext.LastSpoken[sourceEvent.ChannelId()][nick]), true),
				"nick": nick,
			})
		}
		bot.SendMessage(sourceEvent, message)
	}
}
<|endoftext|>


<commit_before>package objconv

import (
	"bytes"
	"fmt"
	"io"
	"reflect"
	"strings"
	"time"
)

// Decode decodes the content from the reader into the value.
//
// The format must be a string describing the content type of the data
// (like json, resp, ...).
func Decode(in io.Reader, format string, value interface{}) (err error) {
	defer func() { err = convertPanicToError(recover()) }()
	return NewDecoder(DecoderConfig{
		Input:  in,
		Parser: NewParser(format),
	}).Decode(value)
}

// DecodeBytes decodes the content from the byte slice into the value.
//
// The format must be a string describing the content type of the data
// (like json, resp, ...).
func DecodeBytes(in []byte, format string, value interface{}) (err error) {
	return Decode(bytes.NewReader(in), format, value)
}

// DecodeString decodes the content from the string into the value.
//
// The format must be a string describing the content type of the data
// (like json, resp, ...).
func DecodeString(in string, format string, value interface{}) (err error) {
	return Decode(strings.NewReader(in), format, value)
}

// A Decoder reads and decodes values from an input stream.
type Decoder interface {
	// Decode reads the next value from the its input and stores it in the value
	// pointed to by v.
	Decode(v interface{}) error
}

// DecoderFunc is an adapter to allow use of ordinary functions as decoders.
type DecoderFunc func(interface{}) error

// Decode calls f(v).
func (f DecoderFunc) Decode(v interface{}) error { return f(v) }

// DecoderConfig carries the configuration for creating an encoder.
type DecoderConfig struct {
	// Input is the data stream that the decoder reads from.
	Input io.Reader

	// Parser defines the format used by the decoder.
	Parser Parser

	// Tag sets the name of the tag used when decoding struct fields.
	Tag string
}

// A StreamDecoder reads and decodes a stream of values from an input stream.
type StreamDecoder interface {
	Decoder

	// Len returns the expected number of elements returned from the stream.
	//
	// Depending on the actual format that the stream is decoding this value
	// may or may not be accurate, some formats may also return a negative
	// value to indicate that the number of elements is unknown.
	Len() int

	// Error returns the last error encountered by the decoder.
	Error() error
}

// NewDecoder returns a new decoder configured with config.
func NewDecoder(config DecoderConfig) Decoder {
	config = setDecoderConfigDefaults(config)
	return &decoder{
		r: config.Input,
		p: config.Parser,
		t: config.Tag,
	}
}

// NewStreamDecoder returns a new stream decoder configured with config.
func NewStreamDecoder(config DecoderConfig) StreamDecoder {
	config = setDecoderConfigDefaults(config)
	return &streamDecoder{
		decoder: decoder{
			r: config.Input,
			p: config.Parser,
			t: config.Tag,
		},
	}
}

func setDecoderConfigDefaults(config DecoderConfig) DecoderConfig {
	if config.Input == nil {
		panic("objconv.NewDecoder: config.Input is nil")
	}

	if config.Parser == nil {
		panic("objconv.NewDecoder: config.Parser is nil")
	}

	if len(config.Tag) == 0 {
		config.Tag = "objconv"
	}

	return config
}

type decoder struct {
	r io.Reader
	p Parser
	t string
}

func (d *decoder) Decode(v interface{}) (err error) {
	defer func() { err = convertPanicToError(recover()) }()
	d.decode(NewReader(d.r), v)
	return
}

func (d *decoder) parse(r *Reader, v interface{}) (interface{}, reflect.Value) {
	to := reflect.ValueOf(v).Elem()
	return d.p.Parse(r, to.Interface()), to
}

func (d *decoder) decode(r *Reader, v interface{}) {
	from, to := d.parse(r, v)
	d.decodeValue(r, from, to)
}

func (d *decoder) decodeValue(r *Reader, v interface{}, to reflect.Value) {
	switch x := v.(type) {
	case bool:
		d.decodeBool(x, to)

	case int64:
		d.decodeInt(x, to)

	case uint64:
		d.decodeUint(x, to)

	case float64:
		d.decodeFloat(x, to)

	case string:
		d.decodeString(x, to)

	case []byte:
		d.decodeBytes(x, to)

	case time.Time:
		d.decodeTime(x, to)

	case time.Duration:
		d.decodeDuration(x, to)

	case error:
		d.decodeError(x, to)

	case ArrayParser:
		d.decodeArray(r, x, to)

	case MapParser:
		d.decodeMap(r, x, to)

	default:
		if x == nil {
			d.decodeNil(to)
		} else {
			panic(fmt.Sprintf("the parser produced an unsupported value of type %T, this is a bug", x))
		}
	}
}

func (d *decoder) decodeNil(to reflect.Value) {
	to.Set(reflect.Zero(to.Type()))
}

func (d *decoder) decodeBool(v bool, to reflect.Value) {
	to.SetBool(v)
}

func (d *decoder) decodeInt(v int64, to reflect.Value) {
	switch to.Kind() {
	case reflect.Int:
		to.SetInt(int64(convertInt64ToInt(v)))

	case reflect.Int8:
		to.SetInt(int64(convertInt64ToInt8(v)))

	case reflect.Int16:
		to.SetInt(int64(convertInt64ToInt16(v)))

	case reflect.Int32:
		to.SetInt(int64(convertInt64ToInt32(v)))

	case reflect.Int64:
		to.SetInt(v)

	case reflect.Uint:
		to.SetUint(uint64(convertInt64ToUint(v)))

	case reflect.Uint8:
		to.SetUint(uint64(convertInt64ToUint8(v)))

	case reflect.Uint16:
		to.SetUint(uint64(convertInt64ToUint16(v)))

	case reflect.Uint32:
		to.SetUint(uint64(convertInt64ToUint32(v)))

	case reflect.Uint64:
		to.SetUint(convertInt64ToUint64(v))

	case reflect.Uintptr:
		to.SetUint(uint64(convertInt64ToUintptr(v)))

	case reflect.Float32:
		to.SetFloat(float64(convertInt64ToFloat32(v)))

	case reflect.Float64:
		to.SetFloat(convertInt64ToFloat64(v))

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeUint(v uint64, to reflect.Value) {
	switch to.Kind() {
	case reflect.Int:
		to.SetInt(int64(convertUint64ToInt(v)))

	case reflect.Int8:
		to.SetInt(int64(convertUint64ToInt8(v)))

	case reflect.Int16:
		to.SetInt(int64(convertUint64ToInt16(v)))

	case reflect.Int32:
		to.SetInt(int64(convertUint64ToInt32(v)))

	case reflect.Int64:
		to.SetInt(convertUint64ToInt64(v))

	case reflect.Uint:
		to.SetUint(uint64(convertUint64ToUint(v)))

	case reflect.Uint8:
		to.SetUint(uint64(convertUint64ToUint8(v)))

	case reflect.Uint16:
		to.SetUint(uint64(convertUint64ToUint16(v)))

	case reflect.Uint32:
		to.SetUint(uint64(convertUint64ToUint32(v)))

	case reflect.Uint64:
		to.SetUint(v)

	case reflect.Uintptr:
		to.SetUint(uint64(convertUint64ToUintptr(v)))

	case reflect.Float32:
		to.SetFloat(float64(convertUint64ToFloat32(v)))

	case reflect.Float64:
		to.SetFloat(convertUint64ToFloat64(v))

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeFloat(v float64, to reflect.Value) {
	switch to.Kind() {
	case reflect.Float32, reflect.Float64:
		to.SetFloat(v)

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeString(v string, to reflect.Value) {
	switch to.Kind() {
	case reflect.Slice:
		d.decodeStringToSlice(v, to)

	case reflect.String:
		to.SetString(v)

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeStringToSlice(v string, to reflect.Value) {
	switch to.Type().Elem().Kind() {
	case reflect.Uint8: // []byte
		to.SetBytes([]byte(v))

	case reflect.Int32: // []rune
		to.Set(reflect.ValueOf([]rune(string(v))))

	default:
		to.SetString(v)
	}
}

func (d *decoder) decodeBytes(v []byte, to reflect.Value) {
	switch to.Kind() {
	case reflect.Slice:
		d.decodeBytesToSlice(v, to)

	case reflect.String:
		to.SetString(string(v))

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeBytesToSlice(v []byte, to reflect.Value) {
	switch to.Type().Elem().Kind() {
	case reflect.Int32: // []rune
		to.Set(reflect.ValueOf([]rune(string(v))))

	default:
		to.SetBytes(v)
	}
}

func (d *decoder) decodeDuration(v time.Duration, to reflect.Value) { to.Set(reflect.ValueOf(v)) }

func (d *decoder) decodeTime(v time.Time, to reflect.Value) { to.Set(reflect.ValueOf(v)) }

func (d *decoder) decodeError(v error, to reflect.Value) { to.Set(reflect.ValueOf(v)) }

func (d *decoder) decodeArray(r *Reader, v ArrayParser, to reflect.Value) {
	t := to.Type()

	switch t.Kind() {
	case reflect.Slice:
	default:
		t = reflect.TypeOf(([]interface{})(nil))
	}

	s := reflect.MakeSlice(t, 0, v.Len())
	z := reflect.Zero(t.Elem())
	h := z.Interface()

	for i := 0; true; i++ {
		if x, ok := v.Parse(r, h); !ok {
			break
		} else {
			s = reflect.Append(s, z)
			d.decodeValue(r, x, s.Index(i))
		}
	}

	to.Set(s)
}

func (d *decoder) decodeMap(r *Reader, v MapParser, to reflect.Value) {
	if t := to.Type(); t.Kind() == reflect.Struct {
		d.decodeMapToStruct(r, v, to, t)
	} else {
		d.decodeMapToMap(r, v, to, t)
	}
}

func (d *decoder) decodeMapToMap(r *Reader, v MapParser, to reflect.Value, t reflect.Type) {
	m := reflect.MakeMap(t)
	kt := t.Key()
	vt := t.Elem()

	for {
		key := reflect.New(kt).Elem()
		if x, ok := v.ParseKey(r, key.Interface()); !ok {
			break
		} else {
			d.decodeValue(r, x, key)
		}

		val := reflect.New(vt).Elem()
		d.decodeValue(r, v.ParseValue(r, val.Interface()), val)
		m.SetMapIndex(key, val)
	}

	to.Set(m)
}

func (d *decoder) decodeMapToStruct(r *Reader, v MapParser, to reflect.Value, t reflect.Type) {
	s := LookupStruct(t).SetterValue(d.t, to)

	for {
		var f string

		if x, ok := v.ParseKey(r, f); !ok {
			break
		} else {
			d.decodeValue(r, x, reflect.ValueOf(&f).Elem())
		}

		if fv, ok := s[f]; ok {
			d.decodeValue(r, v.ParseValue(r, fv.Interface()), fv)
		}
	}
}

type streamDecoder struct {
	decoder
	reader *Reader
	array  ArrayParser
	err    error
}

func (d *streamDecoder) Decode(v interface{}) (err error) {
	if err = d.err; err != nil {
		return
	}

	if d.reader == nil {
		d.reader = NewReader(d.r)
	}

	defer func() { err = d.convertPanicToError(recover()) }()
	from, to := d.parse(d.reader, v)

	if d.array == nil {
		switch x := from.(type) {
		case ArrayParser:
			d.array = x
		default:
			d.array = ArrayParserLen(1, ArrayParserFunc(func(r *Reader, hint interface{}) (interface{}, bool) {
				return x, true
			}))
		}
	}

	if x, ok := d.array.Parse(d.reader, to.Interface()); !ok {
		panic(io.EOF)
	} else {
		d.decodeValue(d.reader, x, to)
	}

	return
}

func (d *streamDecoder) Len() int {
	if d.array == nil {
		return -1
	}
	return d.array.Len()
}

func (d *streamDecoder) Error() error {
	return d.err
}

func (d *streamDecoder) convertPanicToError(v interface{}) (err error) {
	if err = convertPanicToError(v); err != nil {
		d.err = err
	}
	return
}
<commit_msg>fix: make the stream decoder return the remaining number of arguments<commit_after>package objconv

import (
	"bytes"
	"fmt"
	"io"
	"reflect"
	"strings"
	"time"
)

// Decode decodes the content from the reader into the value.
//
// The format must be a string describing the content type of the data
// (like json, resp, ...).
func Decode(in io.Reader, format string, value interface{}) (err error) {
	defer func() { err = convertPanicToError(recover()) }()
	return NewDecoder(DecoderConfig{
		Input:  in,
		Parser: NewParser(format),
	}).Decode(value)
}

// DecodeBytes decodes the content from the byte slice into the value.
//
// The format must be a string describing the content type of the data
// (like json, resp, ...).
func DecodeBytes(in []byte, format string, value interface{}) (err error) {
	return Decode(bytes.NewReader(in), format, value)
}

// DecodeString decodes the content from the string into the value.
//
// The format must be a string describing the content type of the data
// (like json, resp, ...).
func DecodeString(in string, format string, value interface{}) (err error) {
	return Decode(strings.NewReader(in), format, value)
}

// A Decoder reads and decodes values from an input stream.
type Decoder interface {
	// Decode reads the next value from the its input and stores it in the value
	// pointed to by v.
	Decode(v interface{}) error
}

// DecoderFunc is an adapter to allow use of ordinary functions as decoders.
type DecoderFunc func(interface{}) error

// Decode calls f(v).
func (f DecoderFunc) Decode(v interface{}) error { return f(v) }

// DecoderConfig carries the configuration for creating an encoder.
type DecoderConfig struct {
	// Input is the data stream that the decoder reads from.
	Input io.Reader

	// Parser defines the format used by the decoder.
	Parser Parser

	// Tag sets the name of the tag used when decoding struct fields.
	Tag string
}

// A StreamDecoder reads and decodes a stream of values from an input stream.
type StreamDecoder interface {
	Decoder

	// Len returns the expected number of elements returned from the stream.
	//
	// Depending on the actual format that the stream is decoding this value
	// may or may not be accurate, some formats may also return a negative
	// value to indicate that the number of elements is unknown.
	Len() int

	// Error returns the last error encountered by the decoder.
	Error() error
}

// NewDecoder returns a new decoder configured with config.
func NewDecoder(config DecoderConfig) Decoder {
	config = setDecoderConfigDefaults(config)
	return &decoder{
		r: config.Input,
		p: config.Parser,
		t: config.Tag,
	}
}

// NewStreamDecoder returns a new stream decoder configured with config.
func NewStreamDecoder(config DecoderConfig) StreamDecoder {
	config = setDecoderConfigDefaults(config)
	return &streamDecoder{
		decoder: decoder{
			r: config.Input,
			p: config.Parser,
			t: config.Tag,
		},
	}
}

func setDecoderConfigDefaults(config DecoderConfig) DecoderConfig {
	if config.Input == nil {
		panic("objconv.NewDecoder: config.Input is nil")
	}

	if config.Parser == nil {
		panic("objconv.NewDecoder: config.Parser is nil")
	}

	if len(config.Tag) == 0 {
		config.Tag = "objconv"
	}

	return config
}

type decoder struct {
	r io.Reader
	p Parser
	t string
}

func (d *decoder) Decode(v interface{}) (err error) {
	defer func() { err = convertPanicToError(recover()) }()
	d.decode(NewReader(d.r), v)
	return
}

func (d *decoder) parse(r *Reader, v interface{}) (interface{}, reflect.Value) {
	to := reflect.ValueOf(v).Elem()
	return d.p.Parse(r, to.Interface()), to
}

func (d *decoder) decode(r *Reader, v interface{}) {
	from, to := d.parse(r, v)
	d.decodeValue(r, from, to)
}

func (d *decoder) decodeValue(r *Reader, v interface{}, to reflect.Value) {
	switch x := v.(type) {
	case bool:
		d.decodeBool(x, to)

	case int64:
		d.decodeInt(x, to)

	case uint64:
		d.decodeUint(x, to)

	case float64:
		d.decodeFloat(x, to)

	case string:
		d.decodeString(x, to)

	case []byte:
		d.decodeBytes(x, to)

	case time.Time:
		d.decodeTime(x, to)

	case time.Duration:
		d.decodeDuration(x, to)

	case error:
		d.decodeError(x, to)

	case ArrayParser:
		d.decodeArray(r, x, to)

	case MapParser:
		d.decodeMap(r, x, to)

	default:
		if x == nil {
			d.decodeNil(to)
		} else {
			panic(fmt.Sprintf("the parser produced an unsupported value of type %T, this is a bug", x))
		}
	}
}

func (d *decoder) decodeNil(to reflect.Value) {
	to.Set(reflect.Zero(to.Type()))
}

func (d *decoder) decodeBool(v bool, to reflect.Value) {
	to.SetBool(v)
}

func (d *decoder) decodeInt(v int64, to reflect.Value) {
	switch to.Kind() {
	case reflect.Int:
		to.SetInt(int64(convertInt64ToInt(v)))

	case reflect.Int8:
		to.SetInt(int64(convertInt64ToInt8(v)))

	case reflect.Int16:
		to.SetInt(int64(convertInt64ToInt16(v)))

	case reflect.Int32:
		to.SetInt(int64(convertInt64ToInt32(v)))

	case reflect.Int64:
		to.SetInt(v)

	case reflect.Uint:
		to.SetUint(uint64(convertInt64ToUint(v)))

	case reflect.Uint8:
		to.SetUint(uint64(convertInt64ToUint8(v)))

	case reflect.Uint16:
		to.SetUint(uint64(convertInt64ToUint16(v)))

	case reflect.Uint32:
		to.SetUint(uint64(convertInt64ToUint32(v)))

	case reflect.Uint64:
		to.SetUint(convertInt64ToUint64(v))

	case reflect.Uintptr:
		to.SetUint(uint64(convertInt64ToUintptr(v)))

	case reflect.Float32:
		to.SetFloat(float64(convertInt64ToFloat32(v)))

	case reflect.Float64:
		to.SetFloat(convertInt64ToFloat64(v))

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeUint(v uint64, to reflect.Value) {
	switch to.Kind() {
	case reflect.Int:
		to.SetInt(int64(convertUint64ToInt(v)))

	case reflect.Int8:
		to.SetInt(int64(convertUint64ToInt8(v)))

	case reflect.Int16:
		to.SetInt(int64(convertUint64ToInt16(v)))

	case reflect.Int32:
		to.SetInt(int64(convertUint64ToInt32(v)))

	case reflect.Int64:
		to.SetInt(convertUint64ToInt64(v))

	case reflect.Uint:
		to.SetUint(uint64(convertUint64ToUint(v)))

	case reflect.Uint8:
		to.SetUint(uint64(convertUint64ToUint8(v)))

	case reflect.Uint16:
		to.SetUint(uint64(convertUint64ToUint16(v)))

	case reflect.Uint32:
		to.SetUint(uint64(convertUint64ToUint32(v)))

	case reflect.Uint64:
		to.SetUint(v)

	case reflect.Uintptr:
		to.SetUint(uint64(convertUint64ToUintptr(v)))

	case reflect.Float32:
		to.SetFloat(float64(convertUint64ToFloat32(v)))

	case reflect.Float64:
		to.SetFloat(convertUint64ToFloat64(v))

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeFloat(v float64, to reflect.Value) {
	switch to.Kind() {
	case reflect.Float32, reflect.Float64:
		to.SetFloat(v)

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeString(v string, to reflect.Value) {
	switch to.Kind() {
	case reflect.Slice:
		d.decodeStringToSlice(v, to)

	case reflect.String:
		to.SetString(v)

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeStringToSlice(v string, to reflect.Value) {
	switch to.Type().Elem().Kind() {
	case reflect.Uint8: // []byte
		to.SetBytes([]byte(v))

	case reflect.Int32: // []rune
		to.Set(reflect.ValueOf([]rune(string(v))))

	default:
		to.SetString(v)
	}
}

func (d *decoder) decodeBytes(v []byte, to reflect.Value) {
	switch to.Kind() {
	case reflect.Slice:
		d.decodeBytesToSlice(v, to)

	case reflect.String:
		to.SetString(string(v))

	default:
		to.Set(reflect.ValueOf(v))
	}
}

func (d *decoder) decodeBytesToSlice(v []byte, to reflect.Value) {
	switch to.Type().Elem().Kind() {
	case reflect.Int32: // []rune
		to.Set(reflect.ValueOf([]rune(string(v))))

	default:
		to.SetBytes(v)
	}
}

func (d *decoder) decodeDuration(v time.Duration, to reflect.Value) { to.Set(reflect.ValueOf(v)) }

func (d *decoder) decodeTime(v time.Time, to reflect.Value) { to.Set(reflect.ValueOf(v)) }

func (d *decoder) decodeError(v error, to reflect.Value) { to.Set(reflect.ValueOf(v)) }

func (d *decoder) decodeArray(r *Reader, v ArrayParser, to reflect.Value) {
	t := to.Type()

	switch t.Kind() {
	case reflect.Slice:
	default:
		t = reflect.TypeOf(([]interface{})(nil))
	}

	s := reflect.MakeSlice(t, 0, v.Len())
	z := reflect.Zero(t.Elem())
	h := z.Interface()

	for i := 0; true; i++ {
		if x, ok := v.Parse(r, h); !ok {
			break
		} else {
			s = reflect.Append(s, z)
			d.decodeValue(r, x, s.Index(i))
		}
	}

	to.Set(s)
}

func (d *decoder) decodeMap(r *Reader, v MapParser, to reflect.Value) {
	if t := to.Type(); t.Kind() == reflect.Struct {
		d.decodeMapToStruct(r, v, to, t)
	} else {
		d.decodeMapToMap(r, v, to, t)
	}
}

func (d *decoder) decodeMapToMap(r *Reader, v MapParser, to reflect.Value, t reflect.Type) {
	m := reflect.MakeMap(t)
	kt := t.Key()
	vt := t.Elem()

	for {
		key := reflect.New(kt).Elem()
		if x, ok := v.ParseKey(r, key.Interface()); !ok {
			break
		} else {
			d.decodeValue(r, x, key)
		}

		val := reflect.New(vt).Elem()
		d.decodeValue(r, v.ParseValue(r, val.Interface()), val)
		m.SetMapIndex(key, val)
	}

	to.Set(m)
}

func (d *decoder) decodeMapToStruct(r *Reader, v MapParser, to reflect.Value, t reflect.Type) {
	s := LookupStruct(t).SetterValue(d.t, to)

	for {
		var f string

		if x, ok := v.ParseKey(r, f); !ok {
			break
		} else {
			d.decodeValue(r, x, reflect.ValueOf(&f).Elem())
		}

		if fv, ok := s[f]; ok {
			d.decodeValue(r, v.ParseValue(r, fv.Interface()), fv)
		}
	}
}

type streamDecoder struct {
	decoder
	reader *Reader
	array  ArrayParser
	err    error
	count  int
}

func (d *streamDecoder) Decode(v interface{}) (err error) {
	if err = d.err; err != nil {
		return
	}

	if d.reader == nil {
		d.reader = NewReader(d.r)
	}

	defer func() { err = d.convertPanicToError(recover()) }()
	from, to := d.parse(d.reader, v)

	if d.array == nil {
		switch x := from.(type) {
		case ArrayParser:
			d.array = x
		default:
			d.array = ArrayParserLen(1, ArrayParserFunc(func(r *Reader, hint interface{}) (interface{}, bool) {
				return x, true
			}))
		}
	}

	if x, ok := d.array.Parse(d.reader, to.Interface()); !ok {
		panic(io.EOF)
	} else {
		d.decodeValue(d.reader, x, to)
	}

	d.count++
	return
}

func (d *streamDecoder) Len() int {
	if d.array == nil {
		return -1
	}
	n := d.array.Len()
	if n >= 0 {
		n -= d.count
	}
	return n
}

func (d *streamDecoder) Error() error {
	return d.err
}

func (d *streamDecoder) convertPanicToError(v interface{}) (err error) {
	if err = convertPanicToError(v); err != nil {
		d.err = err
	}
	return
}
<|endoftext|>


<commit_before>package sortedmap

import "sort"

func (sm *SortedMap) delete(key interface{}) bool {
	if val, ok := sm.idx[key]; ok {

		smLen := len(sm.sorted)
		i := sort.Search(smLen, func(i int) bool {
			return sm.lessFn(val, sm.idx[sm.sorted[i]])
		})

		if i == smLen {
			i--
		} else if i < smLen - 1 {
			i++
		}
		for i > 0 && sm.sorted[i] != key {
			i--
		}

		delete(sm.idx, key)
		sm.sorted = deleteInterface(sm.sorted, i)

		return true
	}
	return false
}

func (sm *SortedMap) deleteBetween(lowerBound, upperBound interface{}) bool {
	bounds := sm.between(lowerBound, upperBound)
	if len(bounds) < 2 {
		return false
	}
	for i := bounds[0]; i < bounds[1] - 1; i++ {
		delete(sm.idx, sm.sorted[i])
		sm.sorted = deleteInterface(sm.sorted, i)
	}
	return true
}

// Delete removes a value from the collection, using the given key.
// Because the index position of each sorted key changes on each insert and a simpler structure was ideal, deletes can have a worse-case complexity of O(n), meaning the goroutine must loop through the sorted slice to find and delete the given key.
func (sm *SortedMap) Delete(key interface{}) bool {
	return sm.delete(key)
}

// BatchDelete removes values from the collection, using the given keys, returning a slice of the results.
func (sm *SortedMap) BatchDelete(keys []interface{}) []bool {
	results := make([]bool, len(keys))
	for i, key := range keys {
		results[i] = sm.delete(key)
	}
	return results
}

// DeleteBetween removes values that are between the given values from the collection.
// DeleteBetween returns true if the operation was successful, or false otherwise.
func (sm *SortedMap) DeleteBetween(lowerBound, upperBound interface{}) bool {
	return sm.deleteBetween(lowerBound, upperBound)
}<commit_msg>renamed variable for clarity<commit_after>package sortedmap

import "sort"

func (sm *SortedMap) delete(key interface{}) bool {
	if val, ok := sm.idx[key]; ok {

		smLen := len(sm.sorted)
		i := sort.Search(smLen, func(i int) bool {
			return sm.lessFn(val, sm.idx[sm.sorted[i]])
		})

		if i == smLen {
			i--
		} else if i < smLen - 1 {
			i++
		}
		for i > 0 && sm.sorted[i] != key {
			i--
		}

		delete(sm.idx, key)
		sm.sorted = deleteInterface(sm.sorted, i)

		return true
	}
	return false
}

func (sm *SortedMap) deleteBetween(lowerBound, upperBound interface{}) bool {
	iterBounds := sm.between(lowerBound, upperBound)
	if len(iterBounds) < 2 {
		return false
	}
	for i := iterBounds[0]; i < iterBounds[1] - 1; i++ {
		delete(sm.idx, sm.sorted[i])
		sm.sorted = deleteInterface(sm.sorted, i)
	}
	return true
}

// Delete removes a value from the collection, using the given key.
// Because the index position of each sorted key changes on each insert and a simpler structure was ideal, deletes can have a worse-case complexity of O(n), meaning the goroutine must loop through the sorted slice to find and delete the given key.
func (sm *SortedMap) Delete(key interface{}) bool {
	return sm.delete(key)
}

// BatchDelete removes values from the collection, using the given keys, returning a slice of the results.
func (sm *SortedMap) BatchDelete(keys []interface{}) []bool {
	results := make([]bool, len(keys))
	for i, key := range keys {
		results[i] = sm.delete(key)
	}
	return results
}

// DeleteBetween removes values that are between the given values from the collection.
// DeleteBetween returns true if the operation was successful, or false otherwise.
func (sm *SortedMap) DeleteBetween(lowerBound, upperBound interface{}) bool {
	return sm.deleteBetween(lowerBound, upperBound)
}<|endoftext|>


<commit_before>package ds

import (
	"context"

	"cloud.google.com/go/datastore"
)

// DeleteByID deletes data from datastore by IDKey
func (client *Client) DeleteByID(ctx context.Context, kind string, id int64) error {
	if id == 0 {
		return datastore.ErrInvalidKey
	}
	return client.Delete(ctx, datastore.IDKey(kind, id, nil))
}

// DeleteByStringID deletes data from datastore by IDKey
func (client *Client) DeleteByStringID(ctx context.Context, kind string, id string) error {
	tid := parseID(id)
	if tid == 0 {
		return datastore.ErrInvalidKey
	}
	return client.Delete(ctx, datastore.IDKey(kind, tid, nil))
}

// DeleteByIDs deletes data from datastore by IDKeys
func (client *Client) DeleteByIDs(ctx context.Context, kind string, ids []int64) error {
	keys := make([]*datastore.Key, len(ids))
	for i, id := range ids {
		if id == 0 {
			return datastore.ErrInvalidKey
		}
		keys[i] = datastore.IDKey(kind, id, nil)
	}
	return client.DeleteMulti(ctx, keys)
}

// DeleteByStringIDs deletes data from datastore by IDKeys
func (client *Client) DeleteByStringIDs(ctx context.Context, kind string, ids []string) error {
	k := kind
	keys := make([]*datastore.Key, len(ids))
	for i, id := range ids {
		tid := parseID(id)
		if tid == 0 {
			return datastore.ErrInvalidKey
		}
		keys[i] = datastore.IDKey(k, tid, nil)
	}
	return client.DeleteMulti(ctx, keys)
}

// DeleteByName deletes data from datastore by NameKey
func (client *Client) DeleteByName(ctx context.Context, kind string, name string) error {
	if len(name) == 0 {
		return datastore.ErrInvalidKey
	}
	return client.Delete(ctx, datastore.NameKey(kind, name, nil))
}

// DeleteByNames deletes data from datastore by NameKeys
func (client *Client) DeleteByNames(ctx context.Context, kind string, names []string) error {
	keys := make([]*datastore.Key, len(names))
	for i, name := range names {
		if len(name) == 0 {
			return datastore.ErrInvalidKey
		}
		keys[i] = datastore.NameKey(kind, name, nil)
	}
	return client.DeleteMulti(ctx, keys)
}

// DeleteModel deletes data by get key from model
func (client *Client) DeleteModel(ctx context.Context, src interface{}) error {
	key := ExtractKey(src)
	if key == nil {
		return datastore.ErrInvalidKey
	}
	return client.Delete(ctx, key)
}

// DeleteModels deletes data by get keys from models
func (client *Client) DeleteModels(ctx context.Context, src interface{}) error {
	keys := ExtractKeys(src)
	if len(keys) == 0 {
		return datastore.ErrInvalidKey
	}
	return client.DeleteMulti(ctx, keys)
}
<commit_msg>add delete by key<commit_after>package ds

import (
	"context"

	"cloud.google.com/go/datastore"
)

// DeleteByKey deletes data from datastore by key
func (client *Client) DeleteByKey(ctx context.Context, key *datastore.Key) error {
	err := client.Delete(ctx, key)
	if client.Cache != nil {
		client.Cache.Del(key)
	}
	return err
}

// DeleteByKeys deletes data from datastore by keys
func (client *Client) DeleteByKeys(ctx context.Context, keys []*datastore.Key) error {
	err := client.DeleteMulti(ctx, keys)
	if client.Cache != nil {
		client.Cache.DelMulti(keys)
	}
	return err
}

// DeleteByID deletes data from datastore by IDKey
func (client *Client) DeleteByID(ctx context.Context, kind string, id int64) error {
	if id == 0 {
		return datastore.ErrInvalidKey
	}
	return client.Delete(ctx, datastore.IDKey(kind, id, nil))
}

// DeleteByStringID deletes data from datastore by IDKey
func (client *Client) DeleteByStringID(ctx context.Context, kind string, id string) error {
	tid := parseID(id)
	if tid == 0 {
		return datastore.ErrInvalidKey
	}
	return client.Delete(ctx, datastore.IDKey(kind, tid, nil))
}

// DeleteByIDs deletes data from datastore by IDKeys
func (client *Client) DeleteByIDs(ctx context.Context, kind string, ids []int64) error {
	keys := make([]*datastore.Key, len(ids))
	for i, id := range ids {
		if id == 0 {
			return datastore.ErrInvalidKey
		}
		keys[i] = datastore.IDKey(kind, id, nil)
	}
	return client.DeleteMulti(ctx, keys)
}

// DeleteByStringIDs deletes data from datastore by IDKeys
func (client *Client) DeleteByStringIDs(ctx context.Context, kind string, ids []string) error {
	k := kind
	keys := make([]*datastore.Key, len(ids))
	for i, id := range ids {
		tid := parseID(id)
		if tid == 0 {
			return datastore.ErrInvalidKey
		}
		keys[i] = datastore.IDKey(k, tid, nil)
	}
	return client.DeleteMulti(ctx, keys)
}

// DeleteByName deletes data from datastore by NameKey
func (client *Client) DeleteByName(ctx context.Context, kind string, name string) error {
	if len(name) == 0 {
		return datastore.ErrInvalidKey
	}
	return client.Delete(ctx, datastore.NameKey(kind, name, nil))
}

// DeleteByNames deletes data from datastore by NameKeys
func (client *Client) DeleteByNames(ctx context.Context, kind string, names []string) error {
	keys := make([]*datastore.Key, len(names))
	for i, name := range names {
		if len(name) == 0 {
			return datastore.ErrInvalidKey
		}
		keys[i] = datastore.NameKey(kind, name, nil)
	}
	return client.DeleteMulti(ctx, keys)
}

// DeleteModel deletes data by get key from model
func (client *Client) DeleteModel(ctx context.Context, src interface{}) error {
	key := ExtractKey(src)
	if key == nil {
		return datastore.ErrInvalidKey
	}
	return client.Delete(ctx, key)
}

// DeleteModels deletes data by get keys from models
func (client *Client) DeleteModels(ctx context.Context, src interface{}) error {
	keys := ExtractKeys(src)
	if len(keys) == 0 {
		return datastore.ErrInvalidKey
	}
	return client.DeleteMulti(ctx, keys)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 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 cacher

import (
	"k8s.io/component-base/metrics"
	"k8s.io/component-base/metrics/legacyregistry"
)

const (
	namespace = "apiserver"
	subsystem = "watch_cache"
)

/*
 * By default, all the following metrics are defined as falling under
 * ALPHA stability level https://github.com/kubernetes/enhancements/blob/master/keps/sig-instrumentation/1209-metrics-stability/kubernetes-control-plane-metrics-stability.md#stability-classes)
 *
 * Promoting the stability level of the metric is a responsibility of the component owner, since it
 * involves explicitly acknowledging support for the metric across multiple releases, in accordance with
 * the metric stability policy.
 */
var (
	initCounter = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Name:           "apiserver_init_events_total",
			Help:           "Counter of init events processed in watch cache broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	eventsCounter = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Namespace:      namespace,
			Subsystem:      subsystem,
			Name:           "events_dispatched_total",
			Help:           "Counter of events dispatched in watch cache broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	terminatedWatchersCounter = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Name:           "apiserver_terminated_watchers_total",
			Help:           "Counter of watchers closed due to unresponsiveness broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	watchCacheCapacityIncreaseTotal = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Name:           "watch_cache_capacity_increase_total",
			Help:           "Total number of watch cache capacity increase events broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	watchCacheCapacityDecreaseTotal = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Name:           "watch_cache_capacity_decrease_total",
			Help:           "Total number of watch cache capacity decrease events broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	watchCacheCapacity = metrics.NewGaugeVec(
		&metrics.GaugeOpts{
			Name:           "watch_cache_capacity",
			Help:           "Total capacity of watch cache broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	watchCacheInitializations = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Namespace:      namespace,
			Subsystem:      subsystem,
			Name:           "watch_cache_initializations_total",
			Help:           "Counter of watch cache initializations broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)
)

func init() {
	legacyregistry.MustRegister(initCounter)
	legacyregistry.MustRegister(eventsCounter)
	legacyregistry.MustRegister(terminatedWatchersCounter)
	legacyregistry.MustRegister(watchCacheCapacityIncreaseTotal)
	legacyregistry.MustRegister(watchCacheCapacityDecreaseTotal)
	legacyregistry.MustRegister(watchCacheCapacity)
	legacyregistry.MustRegister(watchCacheInitializations)
}

// recordsWatchCacheCapacityChange record watchCache capacity resize(increase or decrease) operations.
func recordsWatchCacheCapacityChange(objType string, old, new int) {
	watchCacheCapacity.WithLabelValues(objType).Set(float64(new))
	if old < new {
		watchCacheCapacityIncreaseTotal.WithLabelValues(objType).Inc()
		return
	}
	watchCacheCapacityDecreaseTotal.WithLabelValues(objType).Inc()
}
<commit_msg>remove stutter from alpha metric<commit_after>/*
Copyright 2020 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 cacher

import (
	"k8s.io/component-base/metrics"
	"k8s.io/component-base/metrics/legacyregistry"
)

const (
	namespace = "apiserver"
	subsystem = "watch_cache"
)

/*
 * By default, all the following metrics are defined as falling under
 * ALPHA stability level https://github.com/kubernetes/enhancements/blob/master/keps/sig-instrumentation/1209-metrics-stability/kubernetes-control-plane-metrics-stability.md#stability-classes)
 *
 * Promoting the stability level of the metric is a responsibility of the component owner, since it
 * involves explicitly acknowledging support for the metric across multiple releases, in accordance with
 * the metric stability policy.
 */
var (
	initCounter = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Name:           "apiserver_init_events_total",
			Help:           "Counter of init events processed in watch cache broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	eventsCounter = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Namespace:      namespace,
			Subsystem:      subsystem,
			Name:           "events_dispatched_total",
			Help:           "Counter of events dispatched in watch cache broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	terminatedWatchersCounter = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Name:           "apiserver_terminated_watchers_total",
			Help:           "Counter of watchers closed due to unresponsiveness broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	watchCacheCapacityIncreaseTotal = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Name:           "watch_cache_capacity_increase_total",
			Help:           "Total number of watch cache capacity increase events broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	watchCacheCapacityDecreaseTotal = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Name:           "watch_cache_capacity_decrease_total",
			Help:           "Total number of watch cache capacity decrease events broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	watchCacheCapacity = metrics.NewGaugeVec(
		&metrics.GaugeOpts{
			Name:           "watch_cache_capacity",
			Help:           "Total capacity of watch cache broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)

	watchCacheInitializations = metrics.NewCounterVec(
		&metrics.CounterOpts{
			Namespace:      namespace,
			Subsystem:      subsystem,
			Name:           "initializations_total",
			Help:           "Counter of watch cache initializations broken by resource type.",
			StabilityLevel: metrics.ALPHA,
		},
		[]string{"resource"},
	)
)

func init() {
	legacyregistry.MustRegister(initCounter)
	legacyregistry.MustRegister(eventsCounter)
	legacyregistry.MustRegister(terminatedWatchersCounter)
	legacyregistry.MustRegister(watchCacheCapacityIncreaseTotal)
	legacyregistry.MustRegister(watchCacheCapacityDecreaseTotal)
	legacyregistry.MustRegister(watchCacheCapacity)
	legacyregistry.MustRegister(watchCacheInitializations)
}

// recordsWatchCacheCapacityChange record watchCache capacity resize(increase or decrease) operations.
func recordsWatchCacheCapacityChange(objType string, old, new int) {
	watchCacheCapacity.WithLabelValues(objType).Set(float64(new))
	if old < new {
		watchCacheCapacityIncreaseTotal.WithLabelValues(objType).Inc()
		return
	}
	watchCacheCapacityDecreaseTotal.WithLabelValues(objType).Inc()
}
<|endoftext|>


<commit_before>package models

import "labix.org/v2/mgo/bson"

type Account struct {
	ObjectId bson.ObjectId `bson:"_id" json:"-"`
	Profile  struct {
		Nickname  string `bson:"nickname"`
		FirstName string `bson:"firstName"`
		LastName  string `bson:"lastName"`
		Hash      string `bson:"hash"`
	} `bson:"profile"`
}
<commit_msg>Go Model: change object id to id<commit_after>package models

import "labix.org/v2/mgo/bson"

type Account struct {
	Id      bson.ObjectId `bson:"_id" json:"-"`
	Profile struct {
		Nickname  string `bson:"nickname"`
		FirstName string `bson:"firstName"`
		LastName  string `bson:"lastName"`
		Hash      string `bson:"hash"`
	} `bson:"profile"`
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"labix.org/v2/mgo/bson"
)

type MachineGroup struct {
	Id bson.ObjectId `bson:"id" json:"id"`
}

type MachineStatus struct {
	State      string    `bson:"state" json:"state"`
	ModifiedAt time.Time `bson:"modifiedAt" json:"modifiedAt"`
}

type MachineUser struct {
	Id    bson.ObjectId `bson:"id" json:"id"`
	Sudo  bool          `bson:"sudo" json:"sudo"`
	Owner bool          `bson:"owner" json:"owner"`
}

type MachineAssignee struct {
	AssignedAt time.Time `bson:"assignedAt" json:"assignedAt"`
	InProgress bool      `bson:"inProgress" json:"inProgress"`
}

type Machine struct {
	ObjectId    bson.ObjectId   `bson:"_id" json:"_id"`
	Uid         string          `bson:"uid" json:"uid"`
	QueryString string          `bson:"queryString" json:"queryString"`
	IpAddress   string          `bson:"ipAddress" json:"ipAddress"`
	Domain      string          `bson:"domain" json:"domain"`
	Provider    string          `bson:"provider" json:"provider"`
	Label       string          `bson:"label" json:"label"`
	Slug        string          `bson:"slug" json:"slug"`
	Provisoners []bson.ObjectId `bson:"provisoners" json:"provisoners"`
	Credential  string          `bson:"credential" json:"credential"`
	Users       []MachineUser   `bson:"users" json:"users"`
	Groups      []MachineGroup  `bson:"groups" json:"groups"`
	CreatedAt   time.Time       `bson:"createdAt" json:"createdAt" `
	Status      MachineStatus   `bson:"status" json:"status"`
	Meta        interface{}     `bson:"meta" json:"meta"`
	Assignee    MachineAssignee `bson:"assignee" json:"assignee"`
	UserDeleted bool            `bson:"userDeleted" json:"userDeleted"`
}
<commit_msg>go: added a helper function for getting the owner of the machien<commit_after>package models

import (
	"time"

	"labix.org/v2/mgo/bson"
)

type MachineGroup struct {
	Id bson.ObjectId `bson:"id" json:"id"`
}

type MachineStatus struct {
	State      string    `bson:"state" json:"state"`
	ModifiedAt time.Time `bson:"modifiedAt" json:"modifiedAt"`
}

type MachineUser struct {
	Id    bson.ObjectId `bson:"id" json:"id"`
	Sudo  bool          `bson:"sudo" json:"sudo"`
	Owner bool          `bson:"owner" json:"owner"`
}

type MachineAssignee struct {
	AssignedAt time.Time `bson:"assignedAt" json:"assignedAt"`
	InProgress bool      `bson:"inProgress" json:"inProgress"`
}

type Machine struct {
	ObjectId    bson.ObjectId   `bson:"_id" json:"_id"`
	Uid         string          `bson:"uid" json:"uid"`
	QueryString string          `bson:"queryString" json:"queryString"`
	IpAddress   string          `bson:"ipAddress" json:"ipAddress"`
	Domain      string          `bson:"domain" json:"domain"`
	Provider    string          `bson:"provider" json:"provider"`
	Label       string          `bson:"label" json:"label"`
	Slug        string          `bson:"slug" json:"slug"`
	Provisoners []bson.ObjectId `bson:"provisoners" json:"provisoners"`
	Credential  string          `bson:"credential" json:"credential"`
	Users       []MachineUser   `bson:"users" json:"users"`
	Groups      []MachineGroup  `bson:"groups" json:"groups"`
	CreatedAt   time.Time       `bson:"createdAt" json:"createdAt" `
	Status      MachineStatus   `bson:"status" json:"status"`
	Meta        interface{}     `bson:"meta" json:"meta"`
	Assignee    MachineAssignee `bson:"assignee" json:"assignee"`
	UserDeleted bool            `bson:"userDeleted" json:"userDeleted"`
}

// Owner returns the owner of a machine
func (m *Machine) Owner() *MachineUser {
	for _, user := range m.Users {
		// this is the correct way to remove all users but the owner from a
		// machine
		if user.Sudo && user.Owner {
			return &user
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package utils

import (
	"flag"
	"fmt"
	"koding/config"
	"koding/tools/log"
	"math/rand"
	"os"
	"runtime"
	"time"
)

var version string
var numClients int = 0
var ChangeNumClients chan int = make(chan int)
var ShuttingDown bool = false

func Startup(serviceName string, needRoot bool) {
	if needRoot && os.Getuid() != 0 {
		fmt.Println("Must be run as root.")
		os.Exit(1)
	}

	if serviceName != "broker" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	rand.Seed(time.Now().UnixNano())

	var profile string
	flag.StringVar(&profile, "c", "", "Configuration profile")
	flag.BoolVar(&log.LogDebug, "d", false, "Log debug messages")

	flag.Parse()
	if flag.NArg() != 0 {
		flag.PrintDefaults()
		os.Exit(1)
	}
	if profile == "" {
		fmt.Println("Please specify a configuration profile (-c).")
		flag.PrintDefaults()
		os.Exit(1)
	}

	config.LoadConfig(profile)

	log.Service = serviceName
	log.Profile = profile
	log.LogToLoggr = config.Current.LogToLoggr
	log.Info(fmt.Sprintf("Process '%v' started (version '%v').", serviceName, version))

	go func() {
		for {
			numClients += <-ChangeNumClients
			if ShuttingDown && numClients == 0 {
				log.Info("Shutdown complete. Terminating.")
				os.Exit(0)
			}
		}
	}()
}

func BeginShutdown() {
	ShuttingDown = true
	ChangeNumClients <- 0
}

func RunStatusLogger() {
	go func() {
		for {
			message := "Status: Serving %d clients."
			if ShuttingDown {
				message = "Status: Shutting down, still %d clients."
			}
			log.Info(fmt.Sprintf(message, numClients), fmt.Sprintf("Number of goroutines: %d", runtime.NumGoroutine()))
			time.Sleep(10 * time.Minute)
		}
	}()
}
<commit_msg>broker: Removed single system thread workaround because amqp library got fixed.<commit_after>package utils

import (
	"flag"
	"fmt"
	"koding/config"
	"koding/tools/log"
	"math/rand"
	"os"
	"runtime"
	"time"
)

var version string
var numClients int = 0
var ChangeNumClients chan int = make(chan int)
var ShuttingDown bool = false

func Startup(serviceName string, needRoot bool) {
	if needRoot && os.Getuid() != 0 {
		fmt.Println("Must be run as root.")
		os.Exit(1)
	}

	runtime.GOMAXPROCS(runtime.NumCPU())
	rand.Seed(time.Now().UnixNano())

	var profile string
	flag.StringVar(&profile, "c", "", "Configuration profile")
	flag.BoolVar(&log.LogDebug, "d", false, "Log debug messages")

	flag.Parse()
	if flag.NArg() != 0 {
		flag.PrintDefaults()
		os.Exit(1)
	}
	if profile == "" {
		fmt.Println("Please specify a configuration profile (-c).")
		flag.PrintDefaults()
		os.Exit(1)
	}

	config.LoadConfig(profile)

	log.Service = serviceName
	log.Profile = profile
	log.LogToLoggr = config.Current.LogToLoggr
	log.Info(fmt.Sprintf("Process '%v' started (version '%v').", serviceName, version))

	go func() {
		for {
			numClients += <-ChangeNumClients
			if ShuttingDown && numClients == 0 {
				log.Info("Shutdown complete. Terminating.")
				os.Exit(0)
			}
		}
	}()
}

func BeginShutdown() {
	ShuttingDown = true
	ChangeNumClients <- 0
}

func RunStatusLogger() {
	go func() {
		for {
			message := "Status: Serving %d clients."
			if ShuttingDown {
				message = "Status: Shutting down, still %d clients."
			}
			log.Info(fmt.Sprintf(message, numClients), fmt.Sprintf("Number of goroutines: %d", runtime.NumGoroutine()))
			time.Sleep(10 * time.Minute)
		}
	}()
}
<|endoftext|>


<commit_before>package tabletmanager

import (
	"flag"
	"fmt"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/context"

	mproto "github.com/youtube/vitess/go/mysql/proto"
	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/binlog/binlogplayer"
	"github.com/youtube/vitess/go/vt/binlog/proto"
	"github.com/youtube/vitess/go/vt/key"
	"github.com/youtube/vitess/go/vt/mysqlctl"
	"github.com/youtube/vitess/go/vt/topotools"
	"github.com/youtube/vitess/go/vt/zktopo"

	pb "github.com/youtube/vitess/go/vt/proto/topodata"
)

// fakeBinlogClient implements binlogplayer.Client
type fakeBinlogClient struct {
	t               *testing.T
	expectedDialUID uint32

	keyRangeChannel chan *proto.BinlogTransaction
}

func newFakeBinlogClient(t *testing.T, expectedDialUID uint32) *fakeBinlogClient {
	return &fakeBinlogClient{
		t:               t,
		expectedDialUID: expectedDialUID,

		keyRangeChannel: make(chan *proto.BinlogTransaction),
	}
}

// Dial is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) Dial(endPoint *pb.EndPoint, connTimeout time.Duration) error {
	if fbc.expectedDialUID != endPoint.Uid {
		fbc.t.Errorf("fakeBinlogClient.Dial expected uid %v got %v", fbc.expectedDialUID, endPoint.Uid)
	}
	return nil
}

// Close is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) Close() {
}

// Dial is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) ServeUpdateStream(ctx context.Context, position string) (chan *proto.StreamEvent, binlogplayer.ErrFunc, error) {
	return nil, nil, fmt.Errorf("NYI")
}

// Dial is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) StreamTables(ctx context.Context, position string, tables []string, charset *mproto.Charset) (chan *proto.BinlogTransaction, binlogplayer.ErrFunc, error) {
	return nil, nil, fmt.Errorf("NYI")
}

// Dial is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) StreamKeyRange(ctx context.Context, position string, keyspaceIDType key.KeyspaceIdType, keyRange *pb.KeyRange, charset *mproto.Charset) (chan *proto.BinlogTransaction, binlogplayer.ErrFunc, error) {
	c := make(chan *proto.BinlogTransaction)
	var finalErr error
	go func() {
		for {
			select {
			case bt := <-fbc.keyRangeChannel:
				c <- bt
			case <-ctx.Done():
				finalErr = ctx.Err()
				close(c)
				return
			}
		}
	}()
	return c, func() error {
		return finalErr
	}, nil
}

func TestBinlogPlayerMapHorizontalSplit(t *testing.T) {
	ts := zktopo.NewTestServer(t, []string{"cell1"})
	ctx := context.Background()

	// create the keyspace, a full set of covering shards,
	// and a new split destination shard.
	keyspace := "ks"
	if err := ts.CreateKeyspace(ctx, keyspace, &pb.Keyspace{
		ShardingColumnType: pb.KeyspaceIdType_UINT64,
		ShardingColumnName: "sharding_key",
	}); err != nil {
		t.Fatalf("CreateKeyspace failed: %v", err)
	}
	for _, shard := range []string{"-80", "80-", "40-60"} {
		if err := ts.CreateShard(ctx, keyspace, shard); err != nil {
			t.Fatalf("CreateShard failed: %v", err)
		}
	}

	// create one replica remote tablet in source shard
	tablet := &pb.Tablet{
		Alias: &pb.TabletAlias{
			Cell: "cell1",
			Uid:  100,
		},
		Type: pb.TabletType_REPLICA,
		KeyRange: &pb.KeyRange{
			End: []byte{0x80},
		},
		Keyspace: keyspace,
		Shard:    "-80",
		PortMap: map[string]int32{
			"vt": 80,
		},
	}
	if err := ts.CreateTablet(ctx, tablet); err != nil {
		t.Fatalf("CreateTablet failed: %v", err)
	}
	if err := topotools.UpdateTabletEndpoints(ctx, ts, tablet); err != nil {
		t.Fatalf("topotools.UpdateTabletEndpoints failed: %v", err)
	}

	// register a binlog player factory that will return the instances
	// we want
	clientSyncChannel := make(chan *fakeBinlogClient)
	binlogplayer.RegisterClientFactory("test", func() binlogplayer.Client {
		return <-clientSyncChannel
	})
	flag.Lookup("binlog_player_protocol").Value.Set("test")

	// create the map on the local tablet
	mysqlDaemon := &mysqlctl.FakeMysqlDaemon{MysqlPort: 3306}
	vtClientSyncChannel := make(chan *binlogplayer.VtClientMock)
	bpm := NewBinlogPlayerMap(ts, mysqlDaemon, func() binlogplayer.VtClient {
		return <-vtClientSyncChannel
	})

	tablet = &pb.Tablet{
		Alias: &pb.TabletAlias{
			Cell: "cell1",
			Uid:  1,
		},
		KeyRange: &pb.KeyRange{
			Start: []byte{0x40},
			End:   []byte{0x60},
		},
		Keyspace: keyspace,
		Shard:    "40-60",
	}

	ki, err := ts.GetKeyspace(ctx, keyspace)
	if err != nil {
		t.Fatalf("GetKeyspace failed: %v", err)
	}
	si, err := ts.GetShard(ctx, keyspace, "40-60")
	if err != nil {
		t.Fatalf("GetShard failed: %v", err)
	}

	// no source shard for the shard, not adding players
	bpm.RefreshMap(ctx, tablet, ki, si)
	if bpm.isRunningFilteredReplication() {
		t.Errorf("isRunningFilteredReplication should be false")
	}
	if mysqlDaemon.BinlogPlayerEnabled {
		t.Errorf("mysqlDaemon.BinlogPlayerEnabled should be false")
	}

	// now add the source in shard
	si.SourceShards = []*pb.Shard_SourceShard{
		&pb.Shard_SourceShard{
			Uid:      1,
			Keyspace: keyspace,
			Shard:    "-80",
			KeyRange: &pb.KeyRange{
				End: []byte{0x80},
			},
		},
	}
	if err := ts.UpdateShard(ctx, si); err != nil {
		t.Fatalf("UpdateShard failed: %v", err)
	}

	// now we have a source, adding players
	bpm.RefreshMap(ctx, tablet, ki, si)
	if !bpm.isRunningFilteredReplication() {
		t.Errorf("isRunningFilteredReplication should be true")
	}

	// write a mocked vtClientMock that will be used to read the
	// start position at first. Note this also synchronizes the player,
	// so we can then check mysqlDaemon.BinlogPlayerEnabled.
	vtClientMock := binlogplayer.NewVtClientMock()
	vtClientMock.CommitChannel = make(chan []string)
	vtClientMock.Result = &mproto.QueryResult{
		Fields:       nil,
		RowsAffected: 1,
		InsertId:     0,
		Rows: [][]sqltypes.Value{
			[]sqltypes.Value{
				sqltypes.MakeString([]byte("MariaDB/0-1-1234")),
				sqltypes.MakeString([]byte("")),
			},
		},
	}
	vtClientSyncChannel <- vtClientMock
	if !mysqlDaemon.BinlogPlayerEnabled {
		t.Errorf("mysqlDaemon.BinlogPlayerEnabled should be true")
	}

	// the client will then try to connect to the remote tablet.
	// give it what it needs.
	fbc := newFakeBinlogClient(t, 100)
	clientSyncChannel <- fbc

	// now we can feed an event through the fake connection
	fbc.keyRangeChannel <- &proto.BinlogTransaction{
		Statements: []proto.Statement{
			proto.Statement{
				Category: proto.BL_DML,
				Sql:      "INSERT INTO tablet VALUES(1)",
			},
		},
		Timestamp:     72,
		TransactionID: "MariaDB/0-1-1235",
	}

	// and make sure it results in a committed statement
	sql := <-vtClientMock.CommitChannel
	if len(sql) != 5 ||
		sql[0] != "SELECT pos, flags FROM _vt.blp_checkpoint WHERE source_shard_uid=1" ||
		sql[1] != "BEGIN" ||
		!strings.HasPrefix(sql[2], "UPDATE _vt.blp_checkpoint SET pos='MariaDB/0-1-1235', time_updated=") ||
		!strings.HasSuffix(sql[2], ", transaction_timestamp=72 WHERE source_shard_uid=1") ||
		sql[3] != "INSERT INTO tablet VALUES(1)" ||
		sql[4] != "COMMIT" {
		t.Errorf("Got wrong SQL: %#v", sql)
	}

	// now stop the binlog player map, by removing the source shard.
	// this will stop the player, which will cancel its context,
	// and exit the fake streaming connection.
	si.SourceShards = nil
	bpm.RefreshMap(ctx, tablet, ki, si)
	if bpm.isRunningFilteredReplication() {
		t.Errorf("isRunningFilteredReplication should be false")
	}
}
<commit_msg>Also testing status and blp position list.<commit_after>package tabletmanager

import (
	"flag"
	"fmt"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/context"

	mproto "github.com/youtube/vitess/go/mysql/proto"
	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/binlog/binlogplayer"
	"github.com/youtube/vitess/go/vt/binlog/proto"
	"github.com/youtube/vitess/go/vt/key"
	"github.com/youtube/vitess/go/vt/mysqlctl"
	myproto "github.com/youtube/vitess/go/vt/mysqlctl/proto"
	"github.com/youtube/vitess/go/vt/topotools"
	"github.com/youtube/vitess/go/vt/zktopo"

	pb "github.com/youtube/vitess/go/vt/proto/topodata"
)

// fakeBinlogClient implements binlogplayer.Client
type fakeBinlogClient struct {
	t               *testing.T
	expectedDialUID uint32

	keyRangeChannel chan *proto.BinlogTransaction
}

func newFakeBinlogClient(t *testing.T, expectedDialUID uint32) *fakeBinlogClient {
	return &fakeBinlogClient{
		t:               t,
		expectedDialUID: expectedDialUID,

		keyRangeChannel: make(chan *proto.BinlogTransaction),
	}
}

// Dial is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) Dial(endPoint *pb.EndPoint, connTimeout time.Duration) error {
	if fbc.expectedDialUID != endPoint.Uid {
		fbc.t.Errorf("fakeBinlogClient.Dial expected uid %v got %v", fbc.expectedDialUID, endPoint.Uid)
	}
	return nil
}

// Close is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) Close() {
}

// Dial is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) ServeUpdateStream(ctx context.Context, position string) (chan *proto.StreamEvent, binlogplayer.ErrFunc, error) {
	return nil, nil, fmt.Errorf("NYI")
}

// Dial is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) StreamTables(ctx context.Context, position string, tables []string, charset *mproto.Charset) (chan *proto.BinlogTransaction, binlogplayer.ErrFunc, error) {
	return nil, nil, fmt.Errorf("NYI")
}

// Dial is part of the binlogplayer.Client interface
func (fbc *fakeBinlogClient) StreamKeyRange(ctx context.Context, position string, keyspaceIDType key.KeyspaceIdType, keyRange *pb.KeyRange, charset *mproto.Charset) (chan *proto.BinlogTransaction, binlogplayer.ErrFunc, error) {
	c := make(chan *proto.BinlogTransaction)
	var finalErr error
	go func() {
		for {
			select {
			case bt := <-fbc.keyRangeChannel:
				c <- bt
			case <-ctx.Done():
				finalErr = ctx.Err()
				close(c)
				return
			}
		}
	}()
	return c, func() error {
		return finalErr
	}, nil
}

func TestBinlogPlayerMapHorizontalSplit(t *testing.T) {
	ts := zktopo.NewTestServer(t, []string{"cell1"})
	ctx := context.Background()

	// create the keyspace, a full set of covering shards,
	// and a new split destination shard.
	keyspace := "ks"
	if err := ts.CreateKeyspace(ctx, keyspace, &pb.Keyspace{
		ShardingColumnType: pb.KeyspaceIdType_UINT64,
		ShardingColumnName: "sharding_key",
	}); err != nil {
		t.Fatalf("CreateKeyspace failed: %v", err)
	}
	for _, shard := range []string{"-80", "80-", "40-60"} {
		if err := ts.CreateShard(ctx, keyspace, shard); err != nil {
			t.Fatalf("CreateShard failed: %v", err)
		}
	}

	// create one replica remote tablet in source shard
	tablet := &pb.Tablet{
		Alias: &pb.TabletAlias{
			Cell: "cell1",
			Uid:  100,
		},
		Type: pb.TabletType_REPLICA,
		KeyRange: &pb.KeyRange{
			End: []byte{0x80},
		},
		Keyspace: keyspace,
		Shard:    "-80",
		PortMap: map[string]int32{
			"vt": 80,
		},
	}
	if err := ts.CreateTablet(ctx, tablet); err != nil {
		t.Fatalf("CreateTablet failed: %v", err)
	}
	if err := topotools.UpdateTabletEndpoints(ctx, ts, tablet); err != nil {
		t.Fatalf("topotools.UpdateTabletEndpoints failed: %v", err)
	}

	// register a binlog player factory that will return the instances
	// we want
	clientSyncChannel := make(chan *fakeBinlogClient)
	binlogplayer.RegisterClientFactory("test", func() binlogplayer.Client {
		return <-clientSyncChannel
	})
	flag.Lookup("binlog_player_protocol").Value.Set("test")

	// create the map on the local tablet
	mysqlDaemon := &mysqlctl.FakeMysqlDaemon{MysqlPort: 3306}
	vtClientSyncChannel := make(chan *binlogplayer.VtClientMock)
	bpm := NewBinlogPlayerMap(ts, mysqlDaemon, func() binlogplayer.VtClient {
		return <-vtClientSyncChannel
	})

	tablet = &pb.Tablet{
		Alias: &pb.TabletAlias{
			Cell: "cell1",
			Uid:  1,
		},
		KeyRange: &pb.KeyRange{
			Start: []byte{0x40},
			End:   []byte{0x60},
		},
		Keyspace: keyspace,
		Shard:    "40-60",
	}

	ki, err := ts.GetKeyspace(ctx, keyspace)
	if err != nil {
		t.Fatalf("GetKeyspace failed: %v", err)
	}
	si, err := ts.GetShard(ctx, keyspace, "40-60")
	if err != nil {
		t.Fatalf("GetShard failed: %v", err)
	}

	// no source shard for the shard, not adding players
	bpm.RefreshMap(ctx, tablet, ki, si)
	if bpm.isRunningFilteredReplication() {
		t.Errorf("isRunningFilteredReplication should be false")
	}
	if mysqlDaemon.BinlogPlayerEnabled {
		t.Errorf("mysqlDaemon.BinlogPlayerEnabled should be false")
	}

	// now add the source in shard
	si.SourceShards = []*pb.Shard_SourceShard{
		&pb.Shard_SourceShard{
			Uid:      1,
			Keyspace: keyspace,
			Shard:    "-80",
			KeyRange: &pb.KeyRange{
				End: []byte{0x80},
			},
		},
	}
	if err := ts.UpdateShard(ctx, si); err != nil {
		t.Fatalf("UpdateShard failed: %v", err)
	}

	// now we have a source, adding players
	bpm.RefreshMap(ctx, tablet, ki, si)
	if !bpm.isRunningFilteredReplication() {
		t.Errorf("isRunningFilteredReplication should be true")
	}

	// write a mocked vtClientMock that will be used to read the
	// start position at first. Note this also synchronizes the player,
	// so we can then check mysqlDaemon.BinlogPlayerEnabled.
	vtClientMock := binlogplayer.NewVtClientMock()
	vtClientMock.CommitChannel = make(chan []string)
	vtClientMock.Result = &mproto.QueryResult{
		Fields:       nil,
		RowsAffected: 1,
		InsertId:     0,
		Rows: [][]sqltypes.Value{
			[]sqltypes.Value{
				sqltypes.MakeString([]byte("MariaDB/0-1-1234")),
				sqltypes.MakeString([]byte("")),
			},
		},
	}
	vtClientSyncChannel <- vtClientMock
	if !mysqlDaemon.BinlogPlayerEnabled {
		t.Errorf("mysqlDaemon.BinlogPlayerEnabled should be true")
	}

	// the client will then try to connect to the remote tablet.
	// give it what it needs.
	fbc := newFakeBinlogClient(t, 100)
	clientSyncChannel <- fbc

	// now we can feed an event through the fake connection
	fbc.keyRangeChannel <- &proto.BinlogTransaction{
		Statements: []proto.Statement{
			proto.Statement{
				Category: proto.BL_DML,
				Sql:      "INSERT INTO tablet VALUES(1)",
			},
		},
		Timestamp:     72,
		TransactionID: "MariaDB/0-1-1235",
	}

	// and make sure it results in a committed statement
	sql := <-vtClientMock.CommitChannel
	if len(sql) != 5 ||
		sql[0] != "SELECT pos, flags FROM _vt.blp_checkpoint WHERE source_shard_uid=1" ||
		sql[1] != "BEGIN" ||
		!strings.HasPrefix(sql[2], "UPDATE _vt.blp_checkpoint SET pos='MariaDB/0-1-1235', time_updated=") ||
		!strings.HasSuffix(sql[2], ", transaction_timestamp=72 WHERE source_shard_uid=1") ||
		sql[3] != "INSERT INTO tablet VALUES(1)" ||
		sql[4] != "COMMIT" {
		t.Errorf("Got wrong SQL: %#v", sql)
	}

	// ask for status, make sure we got what we expect
	s := bpm.Status()
	if s.State != "Running" ||
		len(s.Controllers) != 1 ||
		s.Controllers[0].Index != 1 ||
		s.Controllers[0].State != "Running" ||
		s.Controllers[0].SourceShard.Keyspace != keyspace ||
		s.Controllers[0].SourceShard.Shard != "-80" ||
		s.Controllers[0].LastError != "" {
		t.Errorf("unexpected state: %v", s)
	}

	// ask for BlpPositionList, make sure we got what we expect
	go func() {
		vtcm := binlogplayer.NewVtClientMock()
		vtcm.Result = &mproto.QueryResult{
			Fields:       nil,
			RowsAffected: 1,
			InsertId:     0,
			Rows: [][]sqltypes.Value{
				[]sqltypes.Value{
					sqltypes.MakeString([]byte("MariaDB/0-1-1235")),
					sqltypes.MakeString([]byte("")),
				},
			},
		}
		vtClientSyncChannel <- vtcm
	}()
	bpl, err := bpm.BlpPositionList()
	if len(bpl.Entries) != 1 ||
		bpl.Entries[0].Uid != 1 ||
		bpl.Entries[0].Position.GTIDSet.(myproto.MariadbGTID).Domain != 0 ||
		bpl.Entries[0].Position.GTIDSet.(myproto.MariadbGTID).Server != 1 ||
		bpl.Entries[0].Position.GTIDSet.(myproto.MariadbGTID).Sequence != 1235 {
		t.Errorf("unexpected BlpPositionList: %v", bpl)
	}

	// now stop the binlog player map, by removing the source shard.
	// this will stop the player, which will cancel its context,
	// and exit the fake streaming connection.
	si.SourceShards = nil
	bpm.RefreshMap(ctx, tablet, ki, si)
	if bpm.isRunningFilteredReplication() {
		t.Errorf("isRunningFilteredReplication should be false")
	}
	s = bpm.Status()
	if s.State != "Running" ||
		len(s.Controllers) != 0 {
		t.Errorf("unexpected state: %v", s)
	}

	// now just stop the map
	bpm.Stop()
	s = bpm.Status()
	if s.State != "Stopped" ||
		len(s.Controllers) != 0 {
		t.Errorf("unexpected state: %v", s)
	}
}
<|endoftext|>


<commit_before>// TODO: using gopkg.in/yaml.v2

package main

import "gopkg.in/yaml.v2"

func main() {
}
<commit_msg>Create hello-yaml.go<commit_after>// TODO: using gopkg.in/yaml.v2

package main

import "gopkg.in/yaml.v2"

func main() {
}<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2019 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package executor

import (
	"context"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gopkg.in/guregu/null.v3"

	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/lib/types"
)

func getTestExternallyControlledConfig() ExternallyControlledConfig {
	return ExternallyControlledConfig{
		ExternallyControlledConfigParams: ExternallyControlledConfigParams{
			VUs:      null.IntFrom(2),
			MaxVUs:   null.IntFrom(10),
			Duration: types.NullDurationFrom(2 * time.Second),
		},
	}
}

func TestExternallyControlledRun(t *testing.T) {
	t.Parallel()

	et, err := lib.NewExecutionTuple(nil, nil)
	require.NoError(t, err)
	es := lib.NewExecutionState(lib.Options{}, et, 10, 50)

	doneIters := new(uint64)
	var ctx, cancel, executor, _ = setupExecutor(
		t, getTestExternallyControlledConfig(), es,
		simpleRunner(func(ctx context.Context) error {
			time.Sleep(200 * time.Millisecond)
			atomic.AddUint64(doneIters, 1)
			return nil
		}),
	)
	defer cancel()

	var (
		wg     sync.WaitGroup
		errCh  = make(chan error, 1)
		doneCh = make(chan struct{})
	)
	wg.Add(1)
	go func() {
		defer wg.Done()
		es.MarkStarted()
		errCh <- executor.Run(ctx, nil)
		es.MarkEnded()
		close(doneCh)
	}()

	updateConfig := func(vus, maxVUs int64, errMsg string) {
		newConfig := ExternallyControlledConfigParams{
			VUs:      null.IntFrom(vus),
			MaxVUs:   null.IntFrom(maxVUs),
			Duration: types.NullDurationFrom(2 * time.Second),
		}
		err := executor.(*ExternallyControlled).UpdateConfig(ctx, newConfig)
		if errMsg != "" {
			assert.EqualError(t, err, errMsg)
		} else {
			assert.NoError(t, err)
		}
	}

	var resultVUCount [][]int64
	snapshot := func() {
		resultVUCount = append(resultVUCount,
			[]int64{es.GetCurrentlyActiveVUsCount(), es.GetInitializedVUsCount()})
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		snapshotTicker := time.NewTicker(500 * time.Millisecond)
		ticks := 0
		for {
			select {
			case <-snapshotTicker.C:
				snapshot()
				switch ticks {
				case 0, 2:
					updateConfig(4, 10, "")
				case 1:
					updateConfig(8, 20, "")
				case 3:
					updateConfig(15, 10,
						"invalid configuration supplied: the number of active VUs (15)"+
							" must be less than or equal to the number of maxVUs (10)")
					updateConfig(-1, 10,
						"invalid configuration supplied: the number of VUs shouldn't be negative")
				}
				ticks++
			case <-doneCh:
				snapshotTicker.Stop()
				snapshot()
				return
			}
		}
	}()

	wg.Wait()
	require.NoError(t, <-errCh)
	assert.Equal(t, uint64(48), atomic.LoadUint64(doneIters))
	assert.Equal(t, [][]int64{{2, 10}, {4, 10}, {8, 20}, {4, 10}, {0, 10}}, resultVUCount)
}
<commit_msg>Use InDelta in TestExternallyControlledRun<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2019 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package executor

import (
	"context"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gopkg.in/guregu/null.v3"

	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/lib/types"
)

func getTestExternallyControlledConfig() ExternallyControlledConfig {
	return ExternallyControlledConfig{
		ExternallyControlledConfigParams: ExternallyControlledConfigParams{
			VUs:      null.IntFrom(2),
			MaxVUs:   null.IntFrom(10),
			Duration: types.NullDurationFrom(2 * time.Second),
		},
	}
}

func TestExternallyControlledRun(t *testing.T) {
	t.Parallel()

	et, err := lib.NewExecutionTuple(nil, nil)
	require.NoError(t, err)
	es := lib.NewExecutionState(lib.Options{}, et, 10, 50)

	doneIters := new(uint64)
	ctx, cancel, executor, _ := setupExecutor(
		t, getTestExternallyControlledConfig(), es,
		simpleRunner(func(ctx context.Context) error {
			time.Sleep(200 * time.Millisecond)
			atomic.AddUint64(doneIters, 1)
			return nil
		}),
	)
	defer cancel()

	var (
		wg     sync.WaitGroup
		errCh  = make(chan error, 1)
		doneCh = make(chan struct{})
	)
	wg.Add(1)
	go func() {
		defer wg.Done()
		es.MarkStarted()
		errCh <- executor.Run(ctx, nil)
		es.MarkEnded()
		close(doneCh)
	}()

	updateConfig := func(vus, maxVUs int64, errMsg string) {
		newConfig := ExternallyControlledConfigParams{
			VUs:      null.IntFrom(vus),
			MaxVUs:   null.IntFrom(maxVUs),
			Duration: types.NullDurationFrom(2 * time.Second),
		}
		err := executor.(*ExternallyControlled).UpdateConfig(ctx, newConfig)
		if errMsg != "" {
			assert.EqualError(t, err, errMsg)
		} else {
			assert.NoError(t, err)
		}
	}

	var resultVUCount [][]int64
	snapshot := func() {
		resultVUCount = append(resultVUCount,
			[]int64{es.GetCurrentlyActiveVUsCount(), es.GetInitializedVUsCount()})
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		snapshotTicker := time.NewTicker(500 * time.Millisecond)
		ticks := 0
		for {
			select {
			case <-snapshotTicker.C:
				snapshot()
				switch ticks {
				case 0, 2:
					updateConfig(4, 10, "")
				case 1:
					updateConfig(8, 20, "")
				case 3:
					updateConfig(15, 10,
						"invalid configuration supplied: the number of active VUs (15)"+
							" must be less than or equal to the number of maxVUs (10)")
					updateConfig(-1, 10,
						"invalid configuration supplied: the number of VUs shouldn't be negative")
				}
				ticks++
			case <-doneCh:
				snapshotTicker.Stop()
				snapshot()
				return
			}
		}
	}()

	wg.Wait()
	require.NoError(t, <-errCh)
	assert.InDelta(t, 48, int(atomic.LoadUint64(doneIters)), 2)
	assert.Equal(t, [][]int64{{2, 10}, {4, 10}, {8, 20}, {4, 10}, {0, 10}}, resultVUCount)
}
<|endoftext|>