<commit_before>/*
Copyright 2015 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 cache

import (
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/clock"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
)

// Config contains all the settings for a Controller.
type Config struct {
	// The queue for your objects; either a FIFO or
	// a DeltaFIFO. Your Process() function should accept
	// the output of this Queue's Pop() method.
	Queue

	// Something that can list and watch your objects.
	ListerWatcher

	// Something that can process your objects.
	Process ProcessFunc

	// The type of your objects.
	ObjectType runtime.Object

	// Reprocess everything at least this often.
	// Note that if it takes longer for you to clear the queue than this
	// period, you will end up processing items in the order determined
	// by FIFO.Replace(). Currently, this is random. If this is a
	// problem, we can change that replacement policy to append new
	// things to the end of the queue instead of replacing the entire
	// queue.
	FullResyncPeriod time.Duration

	// ShouldResync, if specified, is invoked when the controller's reflector determines the next
	// periodic sync should occur. If this returns true, it means the reflector should proceed with
	// the resync.
	ShouldResync ShouldResyncFunc

	// If true, when Process() returns an error, re-enqueue the object.
	// TODO: add interface to let you inject a delay/backoff or drop
	//       the object completely if desired. Pass the object in
	//       question to this interface as a parameter.
	RetryOnError bool
}

// ShouldResyncFunc is a type of function that indicates if a reflector should perform a
// resync or not. It can be used by a shared informer to support multiple event handlers with custom
// resync periods.
type ShouldResyncFunc func() bool

// ProcessFunc processes a single object.
type ProcessFunc func(obj interface{}) error

// Controller is a generic controller framework.
type controller struct {
	config         Config
	reflector      *Reflector
	reflectorMutex sync.RWMutex
	clock          clock.Clock
}

type Controller interface {
	Run(stopCh <-chan struct{})
	HasSynced() bool
	LastSyncResourceVersion() string
}

// New makes a new Controller from the given Config.
func New(c *Config) Controller {
	ctlr := &controller{
		config: *c,
		clock:  &clock.RealClock{},
	}
	return ctlr
}

// Run begins processing items, and will continue until a value is sent down stopCh.
// It's an error to call Run more than once.
// Run blocks; call via go.
func (c *controller) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	go func() {
		<-stopCh
		c.config.Queue.Close()
	}()
	r := NewReflector(
		c.config.ListerWatcher,
		c.config.ObjectType,
		c.config.Queue,
		c.config.FullResyncPeriod,
	)
	r.ShouldResync = c.config.ShouldResync
	r.clock = c.clock

	c.reflectorMutex.Lock()
	c.reflector = r
	c.reflectorMutex.Unlock()

	var wg wait.Group
	defer wg.Wait()

	wg.StartWithChannel(stopCh, r.Run)

	wait.Until(c.processLoop, time.Second, stopCh)
}

// Returns true once this controller has completed an initial resource listing
func (c *controller) HasSynced() bool {
	return c.config.Queue.HasSynced()
}

func (c *controller) LastSyncResourceVersion() string {
	if c.reflector == nil {
		return ""
	}
	return c.reflector.LastSyncResourceVersion()
}

// processLoop drains the work queue.
// TODO: Consider doing the processing in parallel. This will require a little thought
// to make sure that we don't end up processing the same object multiple times
// concurrently.
//
// TODO: Plumb through the stopCh here (and down to the queue) so that this can
// actually exit when the controller is stopped. Or just give up on this stuff
// ever being stoppable. Converting this whole package to use Context would
// also be helpful.
func (c *controller) processLoop() {
	for {
		obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process))
		if err != nil {
			if err == FIFOClosedError {
				return
			}
			if c.config.RetryOnError {
				// This is the safe way to re-enqueue.
				c.config.Queue.AddIfNotPresent(obj)
			}
		}
	}
}

// ResourceEventHandler can handle notifications for events that happen to a
// resource. The events are informational only, so you can't return an
// error.
//  * OnAdd is called when an object is added.
//  * OnUpdate is called when an object is modified. Note that oldObj is the
//      last known state of the object-- it is possible that several changes
//      were combined together, so you can't use this to see every single
//      change. OnUpdate is also called when a re-list happens, and it will
//      get called even if nothing changed. This is useful for periodically
//      evaluating or syncing something.
//  * OnDelete will get the final state of the item if it is known, otherwise
//      it will get an object of type DeletedFinalStateUnknown. This can
//      happen if the watch is closed and misses the delete event and we don't
//      notice the deletion until the subsequent re-list.
type ResourceEventHandler interface {
	OnAdd(obj interface{})
	OnUpdate(oldObj, newObj interface{})
	OnDelete(obj interface{})
}

// ResourceEventHandlerFuncs is an adaptor to let you easily specify as many or
// as few of the notification functions as you want while still implementing
// ResourceEventHandler.
type ResourceEventHandlerFuncs struct {
	AddFunc    func(obj interface{})
	UpdateFunc func(oldObj, newObj interface{})
	DeleteFunc func(obj interface{})
}

// OnAdd calls AddFunc if it's not nil.
func (r ResourceEventHandlerFuncs) OnAdd(obj interface{}) {
	if r.AddFunc != nil {
		r.AddFunc(obj)
	}
}

// OnUpdate calls UpdateFunc if it's not nil.
func (r ResourceEventHandlerFuncs) OnUpdate(oldObj, newObj interface{}) {
	if r.UpdateFunc != nil {
		r.UpdateFunc(oldObj, newObj)
	}
}

// OnDelete calls DeleteFunc if it's not nil.
func (r ResourceEventHandlerFuncs) OnDelete(obj interface{}) {
	if r.DeleteFunc != nil {
		r.DeleteFunc(obj)
	}
}

// FilteringResourceEventHandler applies the provided filter to all events coming
// in, ensuring the appropriate nested handler method is invoked. An object
// that starts passing the filter after an update is considered an add, and an
// object that stops passing the filter after an update is considered a delete.
type FilteringResourceEventHandler struct {
	FilterFunc func(obj interface{}) bool
	Handler    ResourceEventHandler
}

// OnAdd calls the nested handler only if the filter succeeds
func (r FilteringResourceEventHandler) OnAdd(obj interface{}) {
	if !r.FilterFunc(obj) {
		return
	}
	r.Handler.OnAdd(obj)
}

// OnUpdate ensures the proper handler is called depending on whether the filter matches
func (r FilteringResourceEventHandler) OnUpdate(oldObj, newObj interface{}) {
	newer := r.FilterFunc(newObj)
	older := r.FilterFunc(oldObj)
	switch {
	case newer && older:
		r.Handler.OnUpdate(oldObj, newObj)
	case newer && !older:
		r.Handler.OnAdd(newObj)
	case !newer && older:
		r.Handler.OnDelete(oldObj)
	default:
		// do nothing
	}
}

// OnDelete calls the nested handler only if the filter succeeds
func (r FilteringResourceEventHandler) OnDelete(obj interface{}) {
	if !r.FilterFunc(obj) {
		return
	}
	r.Handler.OnDelete(obj)
}

// DeletionHandlingMetaNamespaceKeyFunc checks for
// DeletedFinalStateUnknown objects before calling
// MetaNamespaceKeyFunc.
func DeletionHandlingMetaNamespaceKeyFunc(obj interface{}) (string, error) {
	if d, ok := obj.(DeletedFinalStateUnknown); ok {
		return d.Key, nil
	}
	return MetaNamespaceKeyFunc(obj)
}

// NewInformer returns a Store and a controller for populating the store
// while also providing event notifications. You should only used the returned
// Store for Get/List operations; Add/Modify/Deletes will cause the event
// notifications to be faulty.
//
// Parameters:
//  * lw is list and watch functions for the source of the resource you want to
//    be informed of.
//  * objType is an object of the type that you expect to receive.
//  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
//    calls, even if nothing changed). Otherwise, re-list will be delayed as
//    long as possible (until the upstream source closes the watch or times out,
//    or you stop the controller).
//  * h is the object you want notifications sent to.
//
func NewInformer(
	lw ListerWatcher,
	objType runtime.Object,
	resyncPeriod time.Duration,
	h ResourceEventHandler,
) (Store, Controller) {
	// This will hold the client state, as we know it.
	clientState := NewStore(DeletionHandlingMetaNamespaceKeyFunc)

	// This will hold incoming changes. Note how we pass clientState in as a
	// KeyLister, that way resync operations will result in the correct set
	// of update/delete deltas.
	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, clientState)

	cfg := &Config{
		Queue:            fifo,
		ListerWatcher:    lw,
		ObjectType:       objType,
		FullResyncPeriod: resyncPeriod,
		RetryOnError:     false,

		Process: func(obj interface{}) error {
			// from oldest to newest
			for _, d := range obj.(Deltas) {
				switch d.Type {
				case Sync, Added, Updated:
					if old, exists, err := clientState.Get(d.Object); err == nil && exists {
						if err := clientState.Update(d.Object); err != nil {
							return err
						}
						h.OnUpdate(old, d.Object)
					} else {
						if err := clientState.Add(d.Object); err != nil {
							return err
						}
						h.OnAdd(d.Object)
					}
				case Deleted:
					if err := clientState.Delete(d.Object); err != nil {
						return err
					}
					h.OnDelete(d.Object)
				}
			}
			return nil
		},
	}
	return clientState, New(cfg)
}

// NewIndexerInformer returns a Indexer and a controller for populating the index
// while also providing event notifications. You should only used the returned
// Index for Get/List operations; Add/Modify/Deletes will cause the event
// notifications to be faulty.
//
// Parameters:
//  * lw is list and watch functions for the source of the resource you want to
//    be informed of.
//  * objType is an object of the type that you expect to receive.
//  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
//    calls, even if nothing changed). Otherwise, re-list will be delayed as
//    long as possible (until the upstream source closes the watch or times out,
//    or you stop the controller).
//  * h is the object you want notifications sent to.
//  * indexers is the indexer for the received object type.
//
func NewIndexerInformer(
	lw ListerWatcher,
	objType runtime.Object,
	resyncPeriod time.Duration,
	h ResourceEventHandler,
	indexers Indexers,
) (Indexer, Controller) {
	// This will hold the client state, as we know it.
	clientState := NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers)

	// This will hold incoming changes. Note how we pass clientState in as a
	// KeyLister, that way resync operations will result in the correct set
	// of update/delete deltas.
	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, clientState)

	cfg := &Config{
		Queue:            fifo,
		ListerWatcher:    lw,
		ObjectType:       objType,
		FullResyncPeriod: resyncPeriod,
		RetryOnError:     false,

		Process: func(obj interface{}) error {
			// from oldest to newest
			for _, d := range obj.(Deltas) {
				switch d.Type {
				case Sync, Added, Updated:
					if old, exists, err := clientState.Get(d.Object); err == nil && exists {
						if err := clientState.Update(d.Object); err != nil {
							return err
						}
						h.OnUpdate(old, d.Object)
					} else {
						if err := clientState.Add(d.Object); err != nil {
							return err
						}
						h.OnAdd(d.Object)
					}
				case Deleted:
					if err := clientState.Delete(d.Object); err != nil {
						return err
					}
					h.OnDelete(d.Object)
				}
			}
			return nil
		},
	}
	return clientState, New(cfg)
}
<commit_msg>Remove duplicate code<commit_after>/*
Copyright 2015 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 cache

import (
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/clock"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
)

// Config contains all the settings for a Controller.
type Config struct {
	// The queue for your objects; either a FIFO or
	// a DeltaFIFO. Your Process() function should accept
	// the output of this Queue's Pop() method.
	Queue

	// Something that can list and watch your objects.
	ListerWatcher

	// Something that can process your objects.
	Process ProcessFunc

	// The type of your objects.
	ObjectType runtime.Object

	// Reprocess everything at least this often.
	// Note that if it takes longer for you to clear the queue than this
	// period, you will end up processing items in the order determined
	// by FIFO.Replace(). Currently, this is random. If this is a
	// problem, we can change that replacement policy to append new
	// things to the end of the queue instead of replacing the entire
	// queue.
	FullResyncPeriod time.Duration

	// ShouldResync, if specified, is invoked when the controller's reflector determines the next
	// periodic sync should occur. If this returns true, it means the reflector should proceed with
	// the resync.
	ShouldResync ShouldResyncFunc

	// If true, when Process() returns an error, re-enqueue the object.
	// TODO: add interface to let you inject a delay/backoff or drop
	//       the object completely if desired. Pass the object in
	//       question to this interface as a parameter.
	RetryOnError bool
}

// ShouldResyncFunc is a type of function that indicates if a reflector should perform a
// resync or not. It can be used by a shared informer to support multiple event handlers with custom
// resync periods.
type ShouldResyncFunc func() bool

// ProcessFunc processes a single object.
type ProcessFunc func(obj interface{}) error

// Controller is a generic controller framework.
type controller struct {
	config         Config
	reflector      *Reflector
	reflectorMutex sync.RWMutex
	clock          clock.Clock
}

type Controller interface {
	Run(stopCh <-chan struct{})
	HasSynced() bool
	LastSyncResourceVersion() string
}

// New makes a new Controller from the given Config.
func New(c *Config) Controller {
	ctlr := &controller{
		config: *c,
		clock:  &clock.RealClock{},
	}
	return ctlr
}

// Run begins processing items, and will continue until a value is sent down stopCh.
// It's an error to call Run more than once.
// Run blocks; call via go.
func (c *controller) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	go func() {
		<-stopCh
		c.config.Queue.Close()
	}()
	r := NewReflector(
		c.config.ListerWatcher,
		c.config.ObjectType,
		c.config.Queue,
		c.config.FullResyncPeriod,
	)
	r.ShouldResync = c.config.ShouldResync
	r.clock = c.clock

	c.reflectorMutex.Lock()
	c.reflector = r
	c.reflectorMutex.Unlock()

	var wg wait.Group
	defer wg.Wait()

	wg.StartWithChannel(stopCh, r.Run)

	wait.Until(c.processLoop, time.Second, stopCh)
}

// Returns true once this controller has completed an initial resource listing
func (c *controller) HasSynced() bool {
	return c.config.Queue.HasSynced()
}

func (c *controller) LastSyncResourceVersion() string {
	if c.reflector == nil {
		return ""
	}
	return c.reflector.LastSyncResourceVersion()
}

// processLoop drains the work queue.
// TODO: Consider doing the processing in parallel. This will require a little thought
// to make sure that we don't end up processing the same object multiple times
// concurrently.
//
// TODO: Plumb through the stopCh here (and down to the queue) so that this can
// actually exit when the controller is stopped. Or just give up on this stuff
// ever being stoppable. Converting this whole package to use Context would
// also be helpful.
func (c *controller) processLoop() {
	for {
		obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process))
		if err != nil {
			if err == FIFOClosedError {
				return
			}
			if c.config.RetryOnError {
				// This is the safe way to re-enqueue.
				c.config.Queue.AddIfNotPresent(obj)
			}
		}
	}
}

// ResourceEventHandler can handle notifications for events that happen to a
// resource. The events are informational only, so you can't return an
// error.
//  * OnAdd is called when an object is added.
//  * OnUpdate is called when an object is modified. Note that oldObj is the
//      last known state of the object-- it is possible that several changes
//      were combined together, so you can't use this to see every single
//      change. OnUpdate is also called when a re-list happens, and it will
//      get called even if nothing changed. This is useful for periodically
//      evaluating or syncing something.
//  * OnDelete will get the final state of the item if it is known, otherwise
//      it will get an object of type DeletedFinalStateUnknown. This can
//      happen if the watch is closed and misses the delete event and we don't
//      notice the deletion until the subsequent re-list.
type ResourceEventHandler interface {
	OnAdd(obj interface{})
	OnUpdate(oldObj, newObj interface{})
	OnDelete(obj interface{})
}

// ResourceEventHandlerFuncs is an adaptor to let you easily specify as many or
// as few of the notification functions as you want while still implementing
// ResourceEventHandler.
type ResourceEventHandlerFuncs struct {
	AddFunc    func(obj interface{})
	UpdateFunc func(oldObj, newObj interface{})
	DeleteFunc func(obj interface{})
}

// OnAdd calls AddFunc if it's not nil.
func (r ResourceEventHandlerFuncs) OnAdd(obj interface{}) {
	if r.AddFunc != nil {
		r.AddFunc(obj)
	}
}

// OnUpdate calls UpdateFunc if it's not nil.
func (r ResourceEventHandlerFuncs) OnUpdate(oldObj, newObj interface{}) {
	if r.UpdateFunc != nil {
		r.UpdateFunc(oldObj, newObj)
	}
}

// OnDelete calls DeleteFunc if it's not nil.
func (r ResourceEventHandlerFuncs) OnDelete(obj interface{}) {
	if r.DeleteFunc != nil {
		r.DeleteFunc(obj)
	}
}

// FilteringResourceEventHandler applies the provided filter to all events coming
// in, ensuring the appropriate nested handler method is invoked. An object
// that starts passing the filter after an update is considered an add, and an
// object that stops passing the filter after an update is considered a delete.
type FilteringResourceEventHandler struct {
	FilterFunc func(obj interface{}) bool
	Handler    ResourceEventHandler
}

// OnAdd calls the nested handler only if the filter succeeds
func (r FilteringResourceEventHandler) OnAdd(obj interface{}) {
	if !r.FilterFunc(obj) {
		return
	}
	r.Handler.OnAdd(obj)
}

// OnUpdate ensures the proper handler is called depending on whether the filter matches
func (r FilteringResourceEventHandler) OnUpdate(oldObj, newObj interface{}) {
	newer := r.FilterFunc(newObj)
	older := r.FilterFunc(oldObj)
	switch {
	case newer && older:
		r.Handler.OnUpdate(oldObj, newObj)
	case newer && !older:
		r.Handler.OnAdd(newObj)
	case !newer && older:
		r.Handler.OnDelete(oldObj)
	default:
		// do nothing
	}
}

// OnDelete calls the nested handler only if the filter succeeds
func (r FilteringResourceEventHandler) OnDelete(obj interface{}) {
	if !r.FilterFunc(obj) {
		return
	}
	r.Handler.OnDelete(obj)
}

// DeletionHandlingMetaNamespaceKeyFunc checks for
// DeletedFinalStateUnknown objects before calling
// MetaNamespaceKeyFunc.
func DeletionHandlingMetaNamespaceKeyFunc(obj interface{}) (string, error) {
	if d, ok := obj.(DeletedFinalStateUnknown); ok {
		return d.Key, nil
	}
	return MetaNamespaceKeyFunc(obj)
}

// NewInformer returns a Store and a controller for populating the store
// while also providing event notifications. You should only used the returned
// Store for Get/List operations; Add/Modify/Deletes will cause the event
// notifications to be faulty.
//
// Parameters:
//  * lw is list and watch functions for the source of the resource you want to
//    be informed of.
//  * objType is an object of the type that you expect to receive.
//  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
//    calls, even if nothing changed). Otherwise, re-list will be delayed as
//    long as possible (until the upstream source closes the watch or times out,
//    or you stop the controller).
//  * h is the object you want notifications sent to.
//
func NewInformer(
	lw ListerWatcher,
	objType runtime.Object,
	resyncPeriod time.Duration,
	h ResourceEventHandler,
) (Store, Controller) {
	// This will hold the client state, as we know it.
	clientState := NewStore(DeletionHandlingMetaNamespaceKeyFunc)

	return clientState, newInformer(lw, objType, resyncPeriod, h, clientState)
}

// NewIndexerInformer returns a Indexer and a controller for populating the index
// while also providing event notifications. You should only used the returned
// Index for Get/List operations; Add/Modify/Deletes will cause the event
// notifications to be faulty.
//
// Parameters:
//  * lw is list and watch functions for the source of the resource you want to
//    be informed of.
//  * objType is an object of the type that you expect to receive.
//  * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
//    calls, even if nothing changed). Otherwise, re-list will be delayed as
//    long as possible (until the upstream source closes the watch or times out,
//    or you stop the controller).
//  * h is the object you want notifications sent to.
//  * indexers is the indexer for the received object type.
//
func NewIndexerInformer(
	lw ListerWatcher,
	objType runtime.Object,
	resyncPeriod time.Duration,
	h ResourceEventHandler,
	indexers Indexers,
) (Indexer, Controller) {
	// This will hold the client state, as we know it.
	clientState := NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers)

	return clientState, newInformer(lw, objType, resyncPeriod, h, clientState)
}

func newInformer(
	lw ListerWatcher,
	objType runtime.Object,
	resyncPeriod time.Duration,
	h ResourceEventHandler,
	clientState Store,
) Controller {
	// This will hold incoming changes. Note how we pass clientState in as a
	// KeyLister, that way resync operations will result in the correct set
	// of update/delete deltas.
	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, clientState)

	cfg := &Config{
		Queue:            fifo,
		ListerWatcher:    lw,
		ObjectType:       objType,
		FullResyncPeriod: resyncPeriod,
		RetryOnError:     false,

		Process: func(obj interface{}) error {
			// from oldest to newest
			for _, d := range obj.(Deltas) {
				switch d.Type {
				case Sync, Added, Updated:
					if old, exists, err := clientState.Get(d.Object); err == nil && exists {
						if err := clientState.Update(d.Object); err != nil {
							return err
						}
						h.OnUpdate(old, d.Object)
					} else {
						if err := clientState.Add(d.Object); err != nil {
							return err
						}
						h.OnAdd(d.Object)
					}
				case Deleted:
					if err := clientState.Delete(d.Object); err != nil {
						return err
					}
					h.OnDelete(d.Object)
				}
			}
			return nil
		},
	}
	return New(cfg)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"os"

	log "github.com/sirupsen/logrus"
	"github.com/wzhliang/xing"
	"github.com/wzhliang/xing/examples/hello"
)

func _assert(err error) {
	if err != nil {
		log.Errorf("Client: %v", err)
	}
}

func main() {
	url := "amqp://guest:guest@localhost:5672/"
	producer, err := xing.NewClient("orchestration.controller", url,
		xing.SetIdentifier(&xing.NoneIdentifier{}),
		xing.SetSerializer(&xing.JSONSerializer{}),
	)
	name := fmt.Sprintf("host.agent.%s", os.Args[1])
	cli := hello.NewGreeterClient(name, producer)
	ret, err := cli.Hello(context.Background(), &hello.HelloRequest{
		Name: "鸠摩智",
	})
	_assert(err)
	if err != nil {
		fmt.Printf("returned: %v\n", ret)
	}
	_, err = cli.Nihao(context.Background(), &hello.HelloRequest{
		Name: "王语嫣",
	})
	_assert(err)
	producer.Close()
}
<commit_msg>better example naming<commit_after>package main

import (
	"context"
	"fmt"
	"os"

	log "github.com/sirupsen/logrus"
	"github.com/wzhliang/xing"
	"github.com/wzhliang/xing/examples/hello"
)

func _assert(err error) {
	if err != nil {
		log.Errorf("Client: %v", err)
	}
}

func main() {
	url := "amqp://guest:guest@localhost:5672/"
	producer, err := xing.NewClient("orchestration.controller", url,
		xing.SetIdentifier(&xing.NoneIdentifier{}),
		xing.SetSerializer(&xing.JSONSerializer{}),
	)
	target := fmt.Sprintf("host.agent.%s", os.Args[1])
	cli := hello.NewGreeterClient(target, producer)
	ret, err := cli.Hello(context.Background(), &hello.HelloRequest{
		Name: "鸠摩智",
	})
	_assert(err)
	if err != nil {
		fmt.Printf("returned: %v\n", ret)
	}
	_, err = cli.Nihao(context.Background(), &hello.HelloRequest{
		Name: "王语嫣",
	})
	_assert(err)
	producer.Close()
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"runtime"
)

const BINARY_VERSION = "0.3.1"

func Version(app string) string {
	return fmt.Sprintf("%s v%s (built w/%s)", app, BINARY_VERSION, runtime.Version())
}
<commit_msg>bump v0.3.2-alpha<commit_after>package util

import (
	"fmt"
	"runtime"
)

const BINARY_VERSION = "0.3.2-alpha"

func Version(app string) string {
	return fmt.Sprintf("%s v%s (built w/%s)", app, BINARY_VERSION, runtime.Version())
}
<|endoftext|>


<commit_before>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package main

import (
	"fmt"

	"github.com/go-vgo/robotgo"
	// "go-vgo/robotgo"
)

func addEvent() {
	ok := robotgo.AddEvents("q", "ctrl", "shift")
	if ok {
		fmt.Println("add events...")
	}

	ok = robotgo.AddEvents("w")
	if ok {
		fmt.Println("add events")
	}

	s := robotgo.Start()
	defer robotgo.End()

	for ev := range s {
		fmt.Println(ev)
	}
}

func add() {
	fmt.Println("--- Please press v---")
	eve := robotgo.AddEvent("v")

	if eve {
		fmt.Println("--- You press v---", "v")
	}

	fmt.Println("--- Please press k---")
	keve := robotgo.AddEvent("k")
	if keve {
		fmt.Println("--- You press k---", "k")
	}

	fmt.Println("--- Please press f1---")
	feve := robotgo.AddEvent("f1")
	if feve {
		fmt.Println("You press...", "f1")
	}
}

func event() {
	////////////////////////////////////////////////////////////////////////////////
	// Global event listener
	////////////////////////////////////////////////////////////////////////////////

	add()

	fmt.Println("--- Please press left mouse button---")
	mleft := robotgo.AddEvent("mleft")
	if mleft {
		fmt.Println("--- You press left mouse button---", "mleft")
	}

	mright := robotgo.AddEvent("mright")
	if mright {
		fmt.Println("--- You press right mouse button---", "mright")
	}

	// stop AddEvent
	// robotgo.StopEvent()
}

func main() {
	addEvent()

	event()
}
<commit_msg>update event example code add print hint<commit_after>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package main

import (
	"fmt"

	"github.com/go-vgo/robotgo"
	// "go-vgo/robotgo"
)

func addEvent() {
	fmt.Println("--- Please press ctrl + shift + q ---")
	ok := robotgo.AddEvents("q", "ctrl", "shift")
	if ok {
		fmt.Println("add events...")
	}

	fmt.Println("--- Please press w---")
	ok = robotgo.AddEvents("w")
	if ok {
		fmt.Println("add events")
	}

	s := robotgo.Start()
	defer robotgo.End()

	for ev := range s {
		fmt.Println(ev)
	}
}

func add() {
	fmt.Println("--- Please press v---")
	eve := robotgo.AddEvent("v")

	if eve {
		fmt.Println("--- You press v---", "v")
	}

	fmt.Println("--- Please press k---")
	keve := robotgo.AddEvent("k")
	if keve {
		fmt.Println("--- You press k---", "k")
	}

	fmt.Println("--- Please press f1---")
	feve := robotgo.AddEvent("f1")
	if feve {
		fmt.Println("You press...", "f1")
	}
}

func event() {
	////////////////////////////////////////////////////////////////////////////////
	// Global event listener
	////////////////////////////////////////////////////////////////////////////////

	add()

	fmt.Println("--- Please press left mouse button---")
	mleft := robotgo.AddEvent("mleft")
	if mleft {
		fmt.Println("--- You press left mouse button---", "mleft")
	}

	mright := robotgo.AddEvent("mright")
	if mright {
		fmt.Println("--- You press right mouse button---", "mright")
	}

	// stop AddEvent
	// robotgo.StopEvent()
}

func main() {
	fmt.Println("test begin...")

	addEvent()

	event()
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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 pubsub

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"time"

	"cloud.google.com/go/iam"
	"github.com/golang/protobuf/proto"
	gax "github.com/googleapis/gax-go"
	"golang.org/x/net/context"
	"google.golang.org/api/support/bundler"
	pb "google.golang.org/genproto/googleapis/pubsub/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
)

const (
	// The maximum number of messages that can be in a single publish request, as
	// determined by the PubSub service.
	MaxPublishRequestCount = 1000

	// The maximum size of a single publish request in bytes, as determined by the PubSub service.
	MaxPublishRequestBytes = 1e7

	maxInt = int(^uint(0) >> 1)
)

// ErrOversizedMessage indicates that a message's size exceeds MaxPublishRequestBytes.
var ErrOversizedMessage = bundler.ErrOversizedItem

// Topic is a reference to a PubSub topic.
//
// The methods of Topic are safe for use by multiple goroutines.
type Topic struct {
	c *Client
	// The fully qualified identifier for the topic, in the format "projects/<projid>/topics/<name>"
	name string

	// Settings for publishing messages. All changes must be made before the
	// first call to Publish. The default is DefaultPublishSettings.
	PublishSettings PublishSettings

	mu      sync.RWMutex
	stopped bool
	bundler *bundler.Bundler

	wg sync.WaitGroup

	// Channel for message bundles to be published. Close to indicate that Stop was called.
	bundlec chan []*bundledMessage
}

// PublishSettings control the bundling of published messages.
type PublishSettings struct {

	// Publish a non-empty batch after this delay has passed.
	DelayThreshold time.Duration

	// Publish a batch when it has this many messages. The maximum is
	// MaxPublishRequestCount.
	CountThreshold int

	// Publish a batch when its size in bytes reaches this value.
	ByteThreshold int

	// The number of goroutines that invoke the Publish RPC concurrently.
	// Defaults to a multiple of GOMAXPROCS.
	NumGoroutines int

	// The maximum time that the client will attempt to publish a bundle of messages.
	Timeout time.Duration
}

// DefaultPublishSettings holds the default values for topics' PublishSettings.
var DefaultPublishSettings = PublishSettings{
	DelayThreshold: 1 * time.Millisecond,
	CountThreshold: 100,
	ByteThreshold:  1e6,
	Timeout:        60 * time.Second,
}

// CreateTopic creates a new topic.
// The specified topic ID must start with a letter, and contain only letters
// ([A-Za-z]), numbers ([0-9]), dashes (-), underscores (_), periods (.),
// tildes (~), plus (+) or percent signs (%). It must be between 3 and 255
// characters in length, and must not start with "goog".
// If the topic already exists an error will be returned.
func (c *Client) CreateTopic(ctx context.Context, id string) (*Topic, error) {
	t := c.Topic(id)
	_, err := c.pubc.CreateTopic(ctx, &pb.Topic{Name: t.name})
	if err != nil {
		return nil, err
	}
	return t, nil
}

// Topic creates a reference to a topic in the client's project.
//
// If a Topic's Publish method is called, it has background goroutines
// associated with it. Clean them up by calling Topic.Stop.
//
// Avoid creating many Topic instances if you use them to publish.
func (c *Client) Topic(id string) *Topic {
	return c.TopicInProject(id, c.projectID)
}

// TopicInProject creates a reference to a topic in the given project.
//
// If a Topic's Publish method is called, it has background goroutines
// associated with it. Clean them up by calling Topic.Stop.
//
// Avoid creating many Topic instances if you use them to publish.
func (c *Client) TopicInProject(id, projectID string) *Topic {
	return newTopic(c, fmt.Sprintf("projects/%s/topics/%s", projectID, id))
}

func newTopic(c *Client, name string) *Topic {
	// bundlec is unbuffered. A buffer would occupy memory not
	// accounted for by the bundler, so BufferedByteLimit would be a lie:
	// the actual memory consumed would be higher.
	return &Topic{
		c:               c,
		name:            name,
		PublishSettings: DefaultPublishSettings,
		bundlec:         make(chan []*bundledMessage),
	}
}

// Topics returns an iterator which returns all of the topics for the client's project.
func (c *Client) Topics(ctx context.Context) *TopicIterator {
	it := c.pubc.ListTopics(ctx, &pb.ListTopicsRequest{Project: c.fullyQualifiedProjectName()})
	return &TopicIterator{
		c: c,
		next: func() (string, error) {
			topic, err := it.Next()
			if err != nil {
				return "", err
			}
			return topic.Name, nil
		},
	}
}

// TopicIterator is an iterator that returns a series of topics.
type TopicIterator struct {
	c    *Client
	next func() (string, error)
}

// Next returns the next topic. If there are no more topics, iterator.Done will be returned.
func (tps *TopicIterator) Next() (*Topic, error) {
	topicName, err := tps.next()
	if err != nil {
		return nil, err
	}
	return newTopic(tps.c, topicName), nil
}

// ID returns the unique idenfier of the topic within its project.
func (t *Topic) ID() string {
	slash := strings.LastIndex(t.name, "/")
	if slash == -1 {
		// name is not a fully-qualified name.
		panic("bad topic name")
	}
	return t.name[slash+1:]
}

// String returns the printable globally unique name for the topic.
func (t *Topic) String() string {
	return t.name
}

// Delete deletes the topic.
func (t *Topic) Delete(ctx context.Context) error {
	return t.c.pubc.DeleteTopic(ctx, &pb.DeleteTopicRequest{Topic: t.name})
}

// Exists reports whether the topic exists on the server.
func (t *Topic) Exists(ctx context.Context) (bool, error) {
	if t.name == "_deleted-topic_" {
		return false, nil
	}
	_, err := t.c.pubc.GetTopic(ctx, &pb.GetTopicRequest{Topic: t.name})
	if err == nil {
		return true, nil
	}
	if grpc.Code(err) == codes.NotFound {
		return false, nil
	}
	return false, err
}

func (t *Topic) IAM() *iam.Handle {
	return iam.InternalNewHandle(t.c.pubc.Connection(), t.name)
}

// Subscriptions returns an iterator which returns the subscriptions for this topic.
//
// Some of the returned subscriptions may belong to a project other than t.
func (t *Topic) Subscriptions(ctx context.Context) *SubscriptionIterator {
	it := t.c.pubc.ListTopicSubscriptions(ctx, &pb.ListTopicSubscriptionsRequest{
		Topic: t.name,
	})
	return &SubscriptionIterator{
		c:    t.c,
		next: it.Next,
	}
}

var errTopicStopped = errors.New("pubsub: Stop has been called for this topic")

// Publish publishes msg to the topic asynchronously. Messages are batched and
// sent according to the topic's PublishSettings. Publish never blocks.
//
// Publish returns a non-nil PublishResult which will be ready when the
// message has been sent (or has failed to be sent) to the server.
//
// Publish creates goroutines for batching and sending messages. These goroutines
// need to be stopped by calling t.Stop(). Once stopped, future calls to Publish
// will immediately return a PublishResult with an error.
func (t *Topic) Publish(ctx context.Context, msg *Message) *PublishResult {
	// TODO(jba): if this turns out to take significant time, try to approximate it.
	// Or, convert the messages to protos in Publish, instead of in the service.
	msg.size = proto.Size(&pb.PubsubMessage{
		Data:       msg.Data,
		Attributes: msg.Attributes,
	})
	r := &PublishResult{ready: make(chan struct{})}
	t.initBundler()
	t.mu.RLock()
	defer t.mu.RUnlock()
	// TODO(aboulhosn) [from bcmills] consider changing the semantics of bundler to perform this logic so we don't have to do it here
	if t.stopped {
		r.set("", errTopicStopped)
		return r
	}

	// TODO(jba) [from bcmills] consider using a shared channel per bundle
	// (requires Bundler API changes; would reduce allocations)
	// The call to Add should never return an error because the bundler's
	// BufferedByteLimit is set to maxInt; we do not perform any flow
	// control in the client.
	err := t.bundler.Add(&bundledMessage{msg, r}, msg.size)
	if err != nil {
		r.set("", err)
	}
	return r
}

// Send all remaining published messages and stop goroutines created for handling
// publishing. Returns once all outstanding messages have been sent or have
// failed to be sent.
func (t *Topic) Stop() {
	t.mu.Lock()
	noop := t.stopped || t.bundler == nil
	t.stopped = true
	t.mu.Unlock()
	if noop {
		return
	}
	t.bundler.Flush()
	// At this point, all pending bundles have been published and the bundler's
	// goroutines have exited, so it is OK for this goroutine to close bundlec.
	close(t.bundlec)
	t.wg.Wait()
}

// A PublishResult holds the result from a call to Publish.
type PublishResult struct {
	ready    chan struct{}
	serverID string
	err      error
}

// Ready returns a channel that is closed when the result is ready.
// When the Ready channel is closed, Get is guaranteed not to block.
func (r *PublishResult) Ready() <-chan struct{} { return r.ready }

// Get returns the server-generated message ID and/or error result of a Publish call.
// Get blocks until the Publish call completes or the context is done.
func (r *PublishResult) Get(ctx context.Context) (serverID string, err error) {
	// If the result is already ready, return it even if the context is done.
	select {
	case <-r.Ready():
		return r.serverID, r.err
	default:
	}
	select {
	case <-ctx.Done():
		return "", ctx.Err()
	case <-r.Ready():
		return r.serverID, r.err
	}
}

func (r *PublishResult) set(sid string, err error) {
	r.serverID = sid
	r.err = err
	close(r.ready)
}

type bundledMessage struct {
	msg *Message
	res *PublishResult
}

func (t *Topic) initBundler() {
	t.mu.RLock()
	noop := t.stopped || t.bundler != nil
	t.mu.RUnlock()
	if noop {
		return
	}
	t.mu.Lock()
	defer t.mu.Unlock()
	// Must re-check, since we released the lock.
	if t.stopped || t.bundler != nil {
		return
	}

	// TODO(jba): use a context detached from the one passed to NewClient.
	ctx := context.TODO()
	// Unless overridden, run several goroutines per CPU to call the Publish RPC.
	n := t.PublishSettings.NumGoroutines
	if n <= 0 {
		n = 25 * runtime.GOMAXPROCS(0)
	}
	timeout := t.PublishSettings.Timeout
	t.wg.Add(n)
	for i := 0; i < n; i++ {
		go func() {
			defer t.wg.Done()
			for b := range t.bundlec {
				bctx := ctx
				cancel := func() {}
				if timeout != 0 {
					bctx, cancel = context.WithTimeout(ctx, timeout)
				}
				t.publishMessageBundle(bctx, b)
				cancel()
			}
		}()
	}
	t.bundler = bundler.NewBundler(&bundledMessage{}, func(items interface{}) {
		t.bundlec <- items.([]*bundledMessage)

	})
	t.bundler.DelayThreshold = t.PublishSettings.DelayThreshold
	t.bundler.BundleCountThreshold = t.PublishSettings.CountThreshold
	if t.bundler.BundleCountThreshold > MaxPublishRequestCount {
		t.bundler.BundleCountThreshold = MaxPublishRequestCount
	}
	t.bundler.BundleByteThreshold = t.PublishSettings.ByteThreshold
	t.bundler.BufferedByteLimit = maxInt
	t.bundler.BundleByteLimit = MaxPublishRequestBytes
}

func (t *Topic) publishMessageBundle(ctx context.Context, bms []*bundledMessage) {
	pbMsgs := make([]*pb.PubsubMessage, len(bms))
	for i, bm := range bms {
		pbMsgs[i] = &pb.PubsubMessage{
			Data:       bm.msg.Data,
			Attributes: bm.msg.Attributes,
		}
		bm.msg = nil // release bm.msg for GC
	}
	res, err := t.c.pubc.Publish(ctx, &pb.PublishRequest{
		Topic:    t.name,
		Messages: pbMsgs,
	}, gax.WithGRPCOptions(grpc.MaxCallSendMsgSize(maxSendRecvBytes)))
	for i, bm := range bms {
		if err != nil {
			bm.res.set("", err)
		} else {
			bm.res.set(res.MessageIds[i], nil)
		}
	}
}
<commit_msg>pubsub: use bundler's HandleLimit to cap publish goroutines<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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 pubsub

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"time"

	"cloud.google.com/go/iam"
	"github.com/golang/protobuf/proto"
	gax "github.com/googleapis/gax-go"
	"golang.org/x/net/context"
	"google.golang.org/api/support/bundler"
	pb "google.golang.org/genproto/googleapis/pubsub/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
)

const (
	// The maximum number of messages that can be in a single publish request, as
	// determined by the PubSub service.
	MaxPublishRequestCount = 1000

	// The maximum size of a single publish request in bytes, as determined by the PubSub service.
	MaxPublishRequestBytes = 1e7

	maxInt = int(^uint(0) >> 1)
)

// ErrOversizedMessage indicates that a message's size exceeds MaxPublishRequestBytes.
var ErrOversizedMessage = bundler.ErrOversizedItem

// Topic is a reference to a PubSub topic.
//
// The methods of Topic are safe for use by multiple goroutines.
type Topic struct {
	c *Client
	// The fully qualified identifier for the topic, in the format "projects/<projid>/topics/<name>"
	name string

	// Settings for publishing messages. All changes must be made before the
	// first call to Publish. The default is DefaultPublishSettings.
	PublishSettings PublishSettings

	mu      sync.RWMutex
	stopped bool
	bundler *bundler.Bundler

	wg sync.WaitGroup
}

// PublishSettings control the bundling of published messages.
type PublishSettings struct {

	// Publish a non-empty batch after this delay has passed.
	DelayThreshold time.Duration

	// Publish a batch when it has this many messages. The maximum is
	// MaxPublishRequestCount.
	CountThreshold int

	// Publish a batch when its size in bytes reaches this value.
	ByteThreshold int

	// The number of goroutines that invoke the Publish RPC concurrently.
	// Defaults to a multiple of GOMAXPROCS.
	NumGoroutines int

	// The maximum time that the client will attempt to publish a bundle of messages.
	Timeout time.Duration
}

// DefaultPublishSettings holds the default values for topics' PublishSettings.
var DefaultPublishSettings = PublishSettings{
	DelayThreshold: 1 * time.Millisecond,
	CountThreshold: 100,
	ByteThreshold:  1e6,
	Timeout:        60 * time.Second,
}

// CreateTopic creates a new topic.
// The specified topic ID must start with a letter, and contain only letters
// ([A-Za-z]), numbers ([0-9]), dashes (-), underscores (_), periods (.),
// tildes (~), plus (+) or percent signs (%). It must be between 3 and 255
// characters in length, and must not start with "goog".
// If the topic already exists an error will be returned.
func (c *Client) CreateTopic(ctx context.Context, id string) (*Topic, error) {
	t := c.Topic(id)
	_, err := c.pubc.CreateTopic(ctx, &pb.Topic{Name: t.name})
	if err != nil {
		return nil, err
	}
	return t, nil
}

// Topic creates a reference to a topic in the client's project.
//
// If a Topic's Publish method is called, it has background goroutines
// associated with it. Clean them up by calling Topic.Stop.
//
// Avoid creating many Topic instances if you use them to publish.
func (c *Client) Topic(id string) *Topic {
	return c.TopicInProject(id, c.projectID)
}

// TopicInProject creates a reference to a topic in the given project.
//
// If a Topic's Publish method is called, it has background goroutines
// associated with it. Clean them up by calling Topic.Stop.
//
// Avoid creating many Topic instances if you use them to publish.
func (c *Client) TopicInProject(id, projectID string) *Topic {
	return newTopic(c, fmt.Sprintf("projects/%s/topics/%s", projectID, id))
}

func newTopic(c *Client, name string) *Topic {
	return &Topic{
		c:               c,
		name:            name,
		PublishSettings: DefaultPublishSettings,
	}
}

// Topics returns an iterator which returns all of the topics for the client's project.
func (c *Client) Topics(ctx context.Context) *TopicIterator {
	it := c.pubc.ListTopics(ctx, &pb.ListTopicsRequest{Project: c.fullyQualifiedProjectName()})
	return &TopicIterator{
		c: c,
		next: func() (string, error) {
			topic, err := it.Next()
			if err != nil {
				return "", err
			}
			return topic.Name, nil
		},
	}
}

// TopicIterator is an iterator that returns a series of topics.
type TopicIterator struct {
	c    *Client
	next func() (string, error)
}

// Next returns the next topic. If there are no more topics, iterator.Done will be returned.
func (tps *TopicIterator) Next() (*Topic, error) {
	topicName, err := tps.next()
	if err != nil {
		return nil, err
	}
	return newTopic(tps.c, topicName), nil
}

// ID returns the unique idenfier of the topic within its project.
func (t *Topic) ID() string {
	slash := strings.LastIndex(t.name, "/")
	if slash == -1 {
		// name is not a fully-qualified name.
		panic("bad topic name")
	}
	return t.name[slash+1:]
}

// String returns the printable globally unique name for the topic.
func (t *Topic) String() string {
	return t.name
}

// Delete deletes the topic.
func (t *Topic) Delete(ctx context.Context) error {
	return t.c.pubc.DeleteTopic(ctx, &pb.DeleteTopicRequest{Topic: t.name})
}

// Exists reports whether the topic exists on the server.
func (t *Topic) Exists(ctx context.Context) (bool, error) {
	if t.name == "_deleted-topic_" {
		return false, nil
	}
	_, err := t.c.pubc.GetTopic(ctx, &pb.GetTopicRequest{Topic: t.name})
	if err == nil {
		return true, nil
	}
	if grpc.Code(err) == codes.NotFound {
		return false, nil
	}
	return false, err
}

func (t *Topic) IAM() *iam.Handle {
	return iam.InternalNewHandle(t.c.pubc.Connection(), t.name)
}

// Subscriptions returns an iterator which returns the subscriptions for this topic.
//
// Some of the returned subscriptions may belong to a project other than t.
func (t *Topic) Subscriptions(ctx context.Context) *SubscriptionIterator {
	it := t.c.pubc.ListTopicSubscriptions(ctx, &pb.ListTopicSubscriptionsRequest{
		Topic: t.name,
	})
	return &SubscriptionIterator{
		c:    t.c,
		next: it.Next,
	}
}

var errTopicStopped = errors.New("pubsub: Stop has been called for this topic")

// Publish publishes msg to the topic asynchronously. Messages are batched and
// sent according to the topic's PublishSettings. Publish never blocks.
//
// Publish returns a non-nil PublishResult which will be ready when the
// message has been sent (or has failed to be sent) to the server.
//
// Publish creates goroutines for batching and sending messages. These goroutines
// need to be stopped by calling t.Stop(). Once stopped, future calls to Publish
// will immediately return a PublishResult with an error.
func (t *Topic) Publish(ctx context.Context, msg *Message) *PublishResult {
	// TODO(jba): if this turns out to take significant time, try to approximate it.
	// Or, convert the messages to protos in Publish, instead of in the service.
	msg.size = proto.Size(&pb.PubsubMessage{
		Data:       msg.Data,
		Attributes: msg.Attributes,
	})
	r := &PublishResult{ready: make(chan struct{})}
	t.initBundler()
	t.mu.RLock()
	defer t.mu.RUnlock()
	// TODO(aboulhosn) [from bcmills] consider changing the semantics of bundler to perform this logic so we don't have to do it here
	if t.stopped {
		r.set("", errTopicStopped)
		return r
	}

	// TODO(jba) [from bcmills] consider using a shared channel per bundle
	// (requires Bundler API changes; would reduce allocations)
	// The call to Add should never return an error because the bundler's
	// BufferedByteLimit is set to maxInt; we do not perform any flow
	// control in the client.
	err := t.bundler.Add(&bundledMessage{msg, r}, msg.size)
	if err != nil {
		r.set("", err)
	}
	return r
}

// Send all remaining published messages and stop goroutines created for handling
// publishing. Returns once all outstanding messages have been sent or have
// failed to be sent.
func (t *Topic) Stop() {
	t.mu.Lock()
	noop := t.stopped || t.bundler == nil
	t.stopped = true
	t.mu.Unlock()
	if noop {
		return
	}
	t.bundler.Flush()
}

// A PublishResult holds the result from a call to Publish.
type PublishResult struct {
	ready    chan struct{}
	serverID string
	err      error
}

// Ready returns a channel that is closed when the result is ready.
// When the Ready channel is closed, Get is guaranteed not to block.
func (r *PublishResult) Ready() <-chan struct{} { return r.ready }

// Get returns the server-generated message ID and/or error result of a Publish call.
// Get blocks until the Publish call completes or the context is done.
func (r *PublishResult) Get(ctx context.Context) (serverID string, err error) {
	// If the result is already ready, return it even if the context is done.
	select {
	case <-r.Ready():
		return r.serverID, r.err
	default:
	}
	select {
	case <-ctx.Done():
		return "", ctx.Err()
	case <-r.Ready():
		return r.serverID, r.err
	}
}

func (r *PublishResult) set(sid string, err error) {
	r.serverID = sid
	r.err = err
	close(r.ready)
}

type bundledMessage struct {
	msg *Message
	res *PublishResult
}

func (t *Topic) initBundler() {
	t.mu.RLock()
	noop := t.stopped || t.bundler != nil
	t.mu.RUnlock()
	if noop {
		return
	}
	t.mu.Lock()
	defer t.mu.Unlock()
	// Must re-check, since we released the lock.
	if t.stopped || t.bundler != nil {
		return
	}

	timeout := t.PublishSettings.Timeout
	t.bundler = bundler.NewBundler(&bundledMessage{}, func(items interface{}) {
		// TODO(jba): use a context detached from the one passed to NewClient.
		ctx := context.TODO()
		if timeout != 0 {
			var cancel func()
			ctx, cancel = context.WithTimeout(ctx, timeout)
			defer cancel()
		}
		t.publishMessageBundle(ctx, items.([]*bundledMessage))
	})
	t.bundler.DelayThreshold = t.PublishSettings.DelayThreshold
	t.bundler.BundleCountThreshold = t.PublishSettings.CountThreshold
	if t.bundler.BundleCountThreshold > MaxPublishRequestCount {
		t.bundler.BundleCountThreshold = MaxPublishRequestCount
	}
	t.bundler.BundleByteThreshold = t.PublishSettings.ByteThreshold
	t.bundler.BufferedByteLimit = maxInt
	t.bundler.BundleByteLimit = MaxPublishRequestBytes
	// Unless overridden, allow many goroutines per CPU to call the Publish RPC concurrently.
	// The default value was determined via extensive load testing (see the loadtest subdirectory).
	if t.PublishSettings.NumGoroutines > 0 {
		t.bundler.HandlerLimit = t.PublishSettings.NumGoroutines
	} else {
		t.bundler.HandlerLimit = 25 * runtime.GOMAXPROCS(0)
	}
}

func (t *Topic) publishMessageBundle(ctx context.Context, bms []*bundledMessage) {
	pbMsgs := make([]*pb.PubsubMessage, len(bms))
	for i, bm := range bms {
		pbMsgs[i] = &pb.PubsubMessage{
			Data:       bm.msg.Data,
			Attributes: bm.msg.Attributes,
		}
		bm.msg = nil // release bm.msg for GC
	}
	res, err := t.c.pubc.Publish(ctx, &pb.PublishRequest{
		Topic:    t.name,
		Messages: pbMsgs,
	}, gax.WithGRPCOptions(grpc.MaxCallSendMsgSize(maxSendRecvBytes)))
	for i, bm := range bms {
		if err != nil {
			bm.res.set("", err)
		} else {
			bm.res.set(res.MessageIds[i], nil)
		}
	}
}
<|endoftext|>


<commit_before>package nakadi

import (
	"reflect"
	"time"
)

// publishAPI defines interface that is used for publishing. Used because of unit tests
type publishAPI interface {
	Publish(events interface{}) error
}

// BatchPublishAPI allows publishing of events in a batched manner. The batcher collects single events into batches,
// respecting batch collection timeout and max batch size. Instead of creating many separate requests to nakadi it will
// aggregate single evewnts and publish them in batches.
type BatchPublishAPI struct {
	publishAPI             publishAPI
	batchCollectionTimeout time.Duration
	maxBatchSize           int
	eventsChannel          chan *eventToPublish
	dispatchFinished       chan int
}

// BatchOptions specifies parameters that should be used to collect events to batches
type BatchOptions struct {
	// Maximum amount of time that event will spend in intermediate queue before being published.
	BatchCollectionTimeout time.Duration
	// Maximum batch size - it is guaranteed that not more than MaxBatchSize events will be sent within one batch
	MaxBatchSize int
	// Size of the intermediate queue in which events are stored before being published.
	// If the queue is full, publishing call will be blocked, waiting for batch to be assembled
	BatchQueueSize int
}

func (o *BatchOptions) withDefaults() *BatchOptions {
	var copyOptions BatchOptions
	if o != nil {
		copyOptions = *o
	}
	if copyOptions.BatchCollectionTimeout == 0 {
		copyOptions.BatchCollectionTimeout = time.Second
	}
	if copyOptions.MaxBatchSize == 0 {
		copyOptions.MaxBatchSize = 10
	}
	if copyOptions.BatchQueueSize == 0 {
		copyOptions.BatchQueueSize = 1000
	}
	return &copyOptions
}

// NewBatchPublishAPI creates a proxy for batching from a client, publishOptions and batchOptions.
func NewBatchPublishAPI(
	client *Client,
	eventType string,
	publishOptions *PublishOptions,
	batchOptions *BatchOptions,
) *BatchPublishAPI {
	publishOptions = publishOptions.withDefaults()
	api := NewPublishAPI(client, eventType, publishOptions)

	batchOptions = batchOptions.withDefaults()
	result := BatchPublishAPI{
		publishAPI:             api,
		batchCollectionTimeout: batchOptions.BatchCollectionTimeout,
		maxBatchSize:           batchOptions.MaxBatchSize,
		eventsChannel:          make(chan *eventToPublish, batchOptions.BatchQueueSize),
		dispatchFinished:       make(chan int),
	}
	go result.dispatchThread()
	return &result
}

// Publish will publish requested data through PublishApi. In case if it is a single event (not a slice), it will be
// added to a batch and published as a part of a batch.
func (p *BatchPublishAPI) Publish(event interface{}) error {
	if reflect.TypeOf(event).Kind() == reflect.Slice {
		return p.publishAPI.Publish(event)
	}
	eventProxy := eventToPublish{
		requestedAt:   time.Now(),
		event:         event,
		publishResult: make(chan error, 1),
	}
	defer close(eventProxy.publishResult)

	p.eventsChannel <- &eventProxy
	return <-eventProxy.publishResult
}

type eventToPublish struct {
	requestedAt   time.Time
	event         interface{}
	publishResult chan error
}

// Close stops batching goroutine and waits for it to confirm stop process
func (p *BatchPublishAPI) Close() {
	close(p.eventsChannel)
	<-p.dispatchFinished
	close(p.dispatchFinished)
}

func (p *BatchPublishAPI) publishBatchToNakadi(events []*eventToPublish) {
	itemsToPublish := make([]interface{}, len(events))
	for idx, evt := range events {
		itemsToPublish[idx] = evt.event
	}
	err := p.publishAPI.Publish(itemsToPublish)
	for _, evt := range events {
		evt.publishResult <- err
	}
}

func (p *BatchPublishAPI) dispatchThread() {
	defer func() { p.dispatchFinished <- 1 }()
	batch := make([]*eventToPublish, 0, 1)
	var finishBatchCollectionAt *time.Time
	flush := func() {
		if len(batch) > 0 {
			go p.publishBatchToNakadi(batch)
			batch = make([]*eventToPublish, 0, 1)
		}
		finishBatchCollectionAt = nil
	}
	for {
		if finishBatchCollectionAt == nil {
			event, ok := <-p.eventsChannel
			if !ok {
				break
			}
			batch = append(batch, event)
			finishAt := event.requestedAt.Add(p.batchCollectionTimeout)
			finishBatchCollectionAt = &finishAt
		} else {
			if len(batch) >= p.maxBatchSize || time.Now().After(*finishBatchCollectionAt) {
				flush()
			} else {
				select {
				case <-time.After(time.Until(*finishBatchCollectionAt)):
					flush()
				case evt, ok := <-p.eventsChannel:
					if ok {
						batch = append(batch, evt)
						break
					}
					flush()
				}
			}
		}
	}
}
<commit_msg>Fix racy code in batch publisher<commit_after>package nakadi

import (
	"reflect"
	"time"
)

// publishAPI defines interface that is used for publishing. Used because of unit tests
type publishAPI interface {
	Publish(events interface{}) error
}

// BatchPublishAPI allows publishing of events in a batched manner. The batcher collects single events into batches,
// respecting batch collection timeout and max batch size. Instead of creating many separate requests to nakadi it will
// aggregate single evewnts and publish them in batches.
type BatchPublishAPI struct {
	publishAPI             publishAPI
	batchCollectionTimeout time.Duration
	maxBatchSize           int
	eventsChannel          chan *eventToPublish
	dispatchFinished       chan int
}

// BatchOptions specifies parameters that should be used to collect events to batches
type BatchOptions struct {
	// Maximum amount of time that event will spend in intermediate queue before being published.
	BatchCollectionTimeout time.Duration
	// Maximum batch size - it is guaranteed that not more than MaxBatchSize events will be sent within one batch
	MaxBatchSize int
	// Size of the intermediate queue in which events are stored before being published.
	// If the queue is full, publishing call will be blocked, waiting for batch to be assembled
	BatchQueueSize int
}

func (o *BatchOptions) withDefaults() *BatchOptions {
	var copyOptions BatchOptions
	if o != nil {
		copyOptions = *o
	}
	if copyOptions.BatchCollectionTimeout == 0 {
		copyOptions.BatchCollectionTimeout = time.Second
	}
	if copyOptions.MaxBatchSize == 0 {
		copyOptions.MaxBatchSize = 10
	}
	if copyOptions.BatchQueueSize == 0 {
		copyOptions.BatchQueueSize = 1000
	}
	return &copyOptions
}

// NewBatchPublishAPI creates a proxy for batching from a client, publishOptions and batchOptions.
func NewBatchPublishAPI(
	client *Client,
	eventType string,
	publishOptions *PublishOptions,
	batchOptions *BatchOptions,
) *BatchPublishAPI {
	publishOptions = publishOptions.withDefaults()
	api := NewPublishAPI(client, eventType, publishOptions)

	batchOptions = batchOptions.withDefaults()
	result := BatchPublishAPI{
		publishAPI:             api,
		batchCollectionTimeout: batchOptions.BatchCollectionTimeout,
		maxBatchSize:           batchOptions.MaxBatchSize,
		eventsChannel:          make(chan *eventToPublish, batchOptions.BatchQueueSize),
		dispatchFinished:       make(chan int),
	}
	go result.dispatchThread()
	return &result
}

// Publish will publish requested data through PublishApi. In case if it is a single event (not a slice), it will be
// added to a batch and published as a part of a batch.
func (p *BatchPublishAPI) Publish(event interface{}) error {
	if reflect.TypeOf(event).Kind() == reflect.Slice {
		return p.publishAPI.Publish(event)
	}
	eventProxy := eventToPublish{
		requestedAt:   time.Now(),
		event:         event,
		publishResult: make(chan error, 1),
	}
	defer close(eventProxy.publishResult)

	p.eventsChannel <- &eventProxy
	return <-eventProxy.publishResult
}

type eventToPublish struct {
	requestedAt   time.Time
	event         interface{}
	publishResult chan error
}

// Close stops batching goroutine and waits for it to confirm stop process
func (p *BatchPublishAPI) Close() {
	close(p.eventsChannel)
	<-p.dispatchFinished
	close(p.dispatchFinished)
}

func (p *BatchPublishAPI) publishBatchToNakadi(events []*eventToPublish) {
	itemsToPublish := make([]interface{}, len(events))
	for idx, evt := range events {
		itemsToPublish[idx] = evt.event
	}
	err := p.publishAPI.Publish(itemsToPublish)
	for _, evt := range events {
		evt.publishResult <- err
	}
}

func (p *BatchPublishAPI) dispatchThread() {
	defer func() { p.dispatchFinished <- 1 }()
	batch := make([]*eventToPublish, 0, 1)
	var finishBatchCollectionAt *time.Time
	flush := func() {
		if len(batch) > 0 {
			p.publishBatchToNakadi(batch)
			batch = make([]*eventToPublish, 0, 1)
		}
		finishBatchCollectionAt = nil
	}
	for {
		if finishBatchCollectionAt == nil {
			event, ok := <-p.eventsChannel
			if !ok {
				break
			}
			batch = append(batch, event)
			finishAt := event.requestedAt.Add(p.batchCollectionTimeout)
			finishBatchCollectionAt = &finishAt
		} else {
			if len(batch) >= p.maxBatchSize || time.Now().After(*finishBatchCollectionAt) {
				flush()
			} else {
				select {
				case <-time.After(time.Until(*finishBatchCollectionAt)):
					flush()
				case evt, ok := <-p.eventsChannel:
					if ok {
						batch = append(batch, evt)
						break
					}
					flush()
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package opc

// Fader effect
//   Listen to a midi knob and fade the entire pattern to black.
//   Fade the even pixels to black first, then the odd pixels.

import (
	"github.com/longears/pixelslinger/colorutils"
	"github.com/longears/pixelslinger/midi"
	//"time"
)

func MakeEffectFader(locations []float64) ByteThread {
	return func(bytesIn chan []byte, bytesOut chan []byte, midiState *midi.MidiState) {
		for bytes := range bytesIn {
			n_pixels := len(bytes) / 3
			//t := float64(time.Now().UnixNano())/1.0e9 - 9.4e8

			// fill in bytes array
			var gain float64
			for ii := 0; ii < n_pixels; ii++ {
				//--------------------------------------------------------------------------------

				//pct := float64(ii) / float64(n_pixels)
				//gain := colorutils.Cos(pct, t, 0.2, 0, 1)

				knob1 := float64(midiState.ControllerValues[1]) / 127.0

				if ii%2 == 0 {
					gain = colorutils.Clamp(colorutils.Remap(knob1, 0, 0.5, 0, 1), 0, 1)
				} else {
					gain = colorutils.Clamp(colorutils.Remap(knob1, 0.5, 1, 0, 1), 0, 1)
				}

				bytes[ii*3+0] = byte(float64(bytes[ii*3+0]) * gain)
				bytes[ii*3+1] = byte(float64(bytes[ii*3+1]) * gain)
				bytes[ii*3+2] = byte(float64(bytes[ii*3+2]) * gain)

				//--------------------------------------------------------------------------------
			}
			bytesOut <- bytes
		}
	}
}
<commit_msg>Fader effect: add white flash; improve gain knob<commit_after>package opc

// Fader effect
//   Listen to a midi knob and fade the entire pattern to black.
//   Fade the even pixels to black first, then the odd pixels.

import (
	"github.com/longears/pixelslinger/colorutils"
	"github.com/longears/pixelslinger/midi"
	//"time"
)

func MakeEffectFader(locations []float64) ByteThread {
	return func(bytesIn chan []byte, bytesOut chan []byte, midiState *midi.MidiState) {
		var r, g, b, gain, flash float64
		for bytes := range bytesIn {
			n_pixels := len(bytes) / 3
			//t := float64(time.Now().UnixNano())/1.0e9 - 9.4e8

			// flash white when pad is down
			pad1 := midiState.KeyVolumes[36]
			flash *= 0.6
			if pad1 > 0 {
				flash = 1
			}

			// knob fade to black
			knob1 := float64(midiState.ControllerValues[1]) / 127.0

			// fill in bytes array
			for ii := 0; ii < n_pixels; ii++ {
				//--------------------------------------------------------------------------------
				//pct := float64(ii) / float64(n_pixels)

				r = float64(bytes[ii*3+0]) / 255
				g = float64(bytes[ii*3+1]) / 255
				b = float64(bytes[ii*3+2]) / 255

				r += flash
				g += flash * 1.1
				b += flash * 1.2

				// knob fade to black
				if ii%2 == 0 {
					gain = colorutils.Clamp(colorutils.Remap(knob1, 0, 0.4, 0, 1), 0, 1)
				} else {
					gain = colorutils.Clamp(colorutils.Remap(knob1, 0.6, 1, 0, 1), 0, 1)
				}
				r *= gain
				g *= gain
				b *= gain

				bytes[ii*3+0] = colorutils.FloatToByte(r)
				bytes[ii*3+1] = colorutils.FloatToByte(g)
				bytes[ii*3+2] = colorutils.FloatToByte(b)

				//--------------------------------------------------------------------------------
			}
			bytesOut <- bytes
		}
	}
}
<|endoftext|>


<commit_before>package fakestoreadapter

import (
	"regexp"
	"strings"
	"sync"

	"github.com/cloudfoundry/storeadapter"
)

type containerNode struct {
	dir   bool
	nodes map[string]*containerNode

	storeNode storeadapter.StoreNode
}

type FakeStoreAdapterErrorInjector struct {
	KeyRegexp *regexp.Regexp
	Error     error
}

func NewFakeStoreAdapterErrorInjector(keyRegexp string, err error) *FakeStoreAdapterErrorInjector {
	return &FakeStoreAdapterErrorInjector{
		KeyRegexp: regexp.MustCompile(keyRegexp),
		Error:     err,
	}
}

type FakeStoreAdapter struct {
	DidConnect    bool
	DidDisconnect bool

	ConnectErr        error
	DisconnectErr     error
	SetErrInjector    *FakeStoreAdapterErrorInjector
	GetErrInjector    *FakeStoreAdapterErrorInjector
	ListErrInjector   *FakeStoreAdapterErrorInjector
	DeleteErrInjector *FakeStoreAdapterErrorInjector
	CreateErrInjector *FakeStoreAdapterErrorInjector

	WatchErrChannel chan error

	rootNode *containerNode

	MaintainedNodeName string
	MaintainNodeError  error
	ReleaseNodeChannel chan chan bool

	createLock *sync.Mutex

	eventChannel chan storeadapter.WatchEvent
	sendEvents   bool
}

func New() *FakeStoreAdapter {
	adapter := &FakeStoreAdapter{}
	adapter.Reset()
	return adapter
}

func (adapter *FakeStoreAdapter) Reset() {
	adapter.DidConnect = false
	adapter.DidDisconnect = false

	adapter.ConnectErr = nil
	adapter.DisconnectErr = nil
	adapter.SetErrInjector = nil
	adapter.GetErrInjector = nil
	adapter.ListErrInjector = nil
	adapter.DeleteErrInjector = nil
	adapter.CreateErrInjector = nil

	adapter.rootNode = &containerNode{
		dir:   true,
		nodes: make(map[string]*containerNode),
	}

	adapter.createLock = new(sync.Mutex)
	adapter.sendEvents = false
	adapter.eventChannel = make(chan storeadapter.WatchEvent)
}

func (adapter *FakeStoreAdapter) Connect() error {
	adapter.DidConnect = true
	return adapter.ConnectErr
}

func (adapter *FakeStoreAdapter) Disconnect() error {
	adapter.DidDisconnect = true
	return adapter.DisconnectErr
}

func (adapter *FakeStoreAdapter) sendEvent(prevNode *storeadapter.StoreNode, node *storeadapter.StoreNode, eventType storeadapter.EventType) {
	if adapter.sendEvents {
		go func() {
			adapter.eventChannel <- storeadapter.WatchEvent{
				Type:     eventType,
				Node:     node,
				PrevNode: prevNode,
			}
		}()
	}
}

func (adapter *FakeStoreAdapter) SetMulti(nodes []storeadapter.StoreNode) error {
	var eventType storeadapter.EventType

	for _, node := range nodes {
		prevNode, err := adapter.Get(node.Key)
		if err == nil {
			eventType = storeadapter.UpdateEvent
		}

		if adapter.SetErrInjector != nil && adapter.SetErrInjector.KeyRegexp.MatchString(node.Key) {
			return adapter.SetErrInjector.Error
		}
		components := adapter.keyComponents(node.Key)

		container := adapter.rootNode
		for i, component := range components {
			if i == len(components)-1 {
				existingNode, exists := container.nodes[component]
				if exists && existingNode.dir {
					return storeadapter.ErrorNodeIsDirectory
				}
				container.nodes[component] = &containerNode{storeNode: node}
			} else {
				existingNode, exists := container.nodes[component]
				if exists {
					if !existingNode.dir {
						return storeadapter.ErrorNodeIsNotDirectory
					}
					container = existingNode
				} else {
					newContainer := &containerNode{dir: true, nodes: make(map[string]*containerNode)}
					container.nodes[component] = newContainer
					container = newContainer
				}
			}
		}

		adapter.sendEvent(&prevNode, &node, eventType)
	}

	return nil
}

func (adapter *FakeStoreAdapter) Create(node storeadapter.StoreNode) error {
	adapter.createLock.Lock()
	defer adapter.createLock.Unlock()

	if adapter.CreateErrInjector != nil && adapter.CreateErrInjector.KeyRegexp.MatchString(node.Key) {
		return adapter.CreateErrInjector.Error
	}

	_, err := adapter.Get(node.Key)
	if err == nil {
		return storeadapter.ErrorKeyExists
	}

	return adapter.SetMulti([]storeadapter.StoreNode{node})
}

func (adapter *FakeStoreAdapter) Get(key string) (storeadapter.StoreNode, error) {
	if adapter.GetErrInjector != nil && adapter.GetErrInjector.KeyRegexp.MatchString(key) {
		return storeadapter.StoreNode{}, adapter.GetErrInjector.Error
	}

	components := adapter.keyComponents(key)
	container := adapter.rootNode
	for _, component := range components {
		var exists bool
		container, exists = container.nodes[component]
		if !exists {
			return storeadapter.StoreNode{}, storeadapter.ErrorKeyNotFound
		}
	}

	if container.dir {
		return storeadapter.StoreNode{}, storeadapter.ErrorNodeIsDirectory
	} else {
		return container.storeNode, nil
	}
}

func (adapter *FakeStoreAdapter) ListRecursively(key string) (storeadapter.StoreNode, error) {
	if adapter.ListErrInjector != nil && adapter.ListErrInjector.KeyRegexp.MatchString(key) {
		return storeadapter.StoreNode{}, adapter.ListErrInjector.Error
	}

	container := adapter.rootNode

	components := adapter.keyComponents(key)
	for _, component := range components {
		var exists bool
		container, exists = container.nodes[component]
		if !exists {
			return storeadapter.StoreNode{}, storeadapter.ErrorKeyNotFound
		}
	}

	if !container.dir {
		return storeadapter.StoreNode{}, storeadapter.ErrorNodeIsNotDirectory
	}

	return adapter.listContainerNode(key, container), nil
}

func (adapter *FakeStoreAdapter) listContainerNode(key string, container *containerNode) storeadapter.StoreNode {
	childNodes := []storeadapter.StoreNode{}

	for nodeKey, node := range container.nodes {
		if node.dir {
			if key == "/" {
				nodeKey = "/" + nodeKey
			} else {
				nodeKey = key + "/" + nodeKey
			}
			childNodes = append(childNodes, adapter.listContainerNode(nodeKey, node))
		} else {
			childNodes = append(childNodes, node.storeNode)
		}
	}

	return storeadapter.StoreNode{
		Key:        key,
		Dir:        true,
		ChildNodes: childNodes,
	}
}

func (adapter *FakeStoreAdapter) Delete(keys ...string) error {
	for _, key := range keys {
		node, _ := adapter.Get(key)

		if adapter.DeleteErrInjector != nil && adapter.DeleteErrInjector.KeyRegexp.MatchString(key) {
			return adapter.DeleteErrInjector.Error
		}

		components := adapter.keyComponents(key)
		container := adapter.rootNode
		parentNode := adapter.rootNode
		for _, component := range components {
			var exists bool
			parentNode = container
			container, exists = container.nodes[component]
			if !exists {
				return storeadapter.ErrorKeyNotFound
			}
		}

		delete(parentNode.nodes, components[len(components)-1])
		adapter.sendEvent(&node, nil, storeadapter.DeleteEvent)
	}

	return nil
}

func (adapter *FakeStoreAdapter) CompareAndDelete(node storeadapter.StoreNode) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) UpdateDirTTL(key string, ttl uint64) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) Update(node storeadapter.StoreNode) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) CompareAndSwap(oldNode storeadapter.StoreNode, newNode storeadapter.StoreNode) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) CompareAndSwapByIndex(oldNodeIndex uint64, newNode storeadapter.StoreNode) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) Watch(key string) (events <-chan storeadapter.WatchEvent, stop chan<- bool, errors <-chan error) {
	adapter.sendEvents = true
	adapter.WatchErrChannel = make(chan error, 1)

	// We haven't implemented stop yet

	return adapter.eventChannel, nil, adapter.WatchErrChannel
}

func (adapter *FakeStoreAdapter) keyComponents(key string) (components []string) {
	for _, s := range strings.Split(key, "/") {
		if s != "" {
			components = append(components, s)
		}
	}

	return components
}

func (adapter *FakeStoreAdapter) MaintainNode(storeNode storeadapter.StoreNode) (lostNodeChannel <-chan bool, releaseNode chan chan bool, err error) {
	adapter.MaintainedNodeName = storeNode.Key
	adapter.ReleaseNodeChannel = make(chan chan bool, 1)

	return nil, adapter.ReleaseNodeChannel, adapter.MaintainNodeError
}
<commit_msg>fakestoreadapter has maintainnodestatus channel<commit_after>package fakestoreadapter

import (
	"regexp"
	"strings"
	"sync"

	"github.com/cloudfoundry/storeadapter"
)

type containerNode struct {
	dir   bool
	nodes map[string]*containerNode

	storeNode storeadapter.StoreNode
}

type FakeStoreAdapterErrorInjector struct {
	KeyRegexp *regexp.Regexp
	Error     error
}

func NewFakeStoreAdapterErrorInjector(keyRegexp string, err error) *FakeStoreAdapterErrorInjector {
	return &FakeStoreAdapterErrorInjector{
		KeyRegexp: regexp.MustCompile(keyRegexp),
		Error:     err,
	}
}

type FakeStoreAdapter struct {
	DidConnect    bool
	DidDisconnect bool

	ConnectErr        error
	DisconnectErr     error
	SetErrInjector    *FakeStoreAdapterErrorInjector
	GetErrInjector    *FakeStoreAdapterErrorInjector
	ListErrInjector   *FakeStoreAdapterErrorInjector
	DeleteErrInjector *FakeStoreAdapterErrorInjector
	CreateErrInjector *FakeStoreAdapterErrorInjector

	WatchErrChannel chan error

	rootNode *containerNode

	MaintainedNodeName string
	MaintainNodeError  error
	MaintainNodeStatus chan bool
	ReleaseNodeChannel chan chan bool

	createLock *sync.Mutex

	eventChannel chan storeadapter.WatchEvent
	sendEvents   bool
}

func New() *FakeStoreAdapter {
	adapter := &FakeStoreAdapter{}
	adapter.Reset()
	return adapter
}

func (adapter *FakeStoreAdapter) Reset() {
	adapter.DidConnect = false
	adapter.DidDisconnect = false

	adapter.ConnectErr = nil
	adapter.DisconnectErr = nil
	adapter.SetErrInjector = nil
	adapter.GetErrInjector = nil
	adapter.ListErrInjector = nil
	adapter.DeleteErrInjector = nil
	adapter.CreateErrInjector = nil
	adapter.MaintainNodeStatus = make(chan bool, 1)

	adapter.rootNode = &containerNode{
		dir:   true,
		nodes: make(map[string]*containerNode),
	}

	adapter.createLock = new(sync.Mutex)
	adapter.sendEvents = false
	adapter.eventChannel = make(chan storeadapter.WatchEvent)
}

func (adapter *FakeStoreAdapter) Connect() error {
	adapter.DidConnect = true
	return adapter.ConnectErr
}

func (adapter *FakeStoreAdapter) Disconnect() error {
	adapter.DidDisconnect = true
	return adapter.DisconnectErr
}

func (adapter *FakeStoreAdapter) sendEvent(prevNode *storeadapter.StoreNode, node *storeadapter.StoreNode, eventType storeadapter.EventType) {
	if adapter.sendEvents {
		go func() {
			adapter.eventChannel <- storeadapter.WatchEvent{
				Type:     eventType,
				Node:     node,
				PrevNode: prevNode,
			}
		}()
	}
}

func (adapter *FakeStoreAdapter) SetMulti(nodes []storeadapter.StoreNode) error {
	var eventType storeadapter.EventType

	for _, node := range nodes {
		prevNode, err := adapter.Get(node.Key)
		if err == nil {
			eventType = storeadapter.UpdateEvent
		}

		if adapter.SetErrInjector != nil && adapter.SetErrInjector.KeyRegexp.MatchString(node.Key) {
			return adapter.SetErrInjector.Error
		}
		components := adapter.keyComponents(node.Key)

		container := adapter.rootNode
		for i, component := range components {
			if i == len(components)-1 {
				existingNode, exists := container.nodes[component]
				if exists && existingNode.dir {
					return storeadapter.ErrorNodeIsDirectory
				}
				container.nodes[component] = &containerNode{storeNode: node}
			} else {
				existingNode, exists := container.nodes[component]
				if exists {
					if !existingNode.dir {
						return storeadapter.ErrorNodeIsNotDirectory
					}
					container = existingNode
				} else {
					newContainer := &containerNode{dir: true, nodes: make(map[string]*containerNode)}
					container.nodes[component] = newContainer
					container = newContainer
				}
			}
		}

		adapter.sendEvent(&prevNode, &node, eventType)
	}

	return nil
}

func (adapter *FakeStoreAdapter) Create(node storeadapter.StoreNode) error {
	adapter.createLock.Lock()
	defer adapter.createLock.Unlock()

	if adapter.CreateErrInjector != nil && adapter.CreateErrInjector.KeyRegexp.MatchString(node.Key) {
		return adapter.CreateErrInjector.Error
	}

	_, err := adapter.Get(node.Key)
	if err == nil {
		return storeadapter.ErrorKeyExists
	}

	return adapter.SetMulti([]storeadapter.StoreNode{node})
}

func (adapter *FakeStoreAdapter) Get(key string) (storeadapter.StoreNode, error) {
	if adapter.GetErrInjector != nil && adapter.GetErrInjector.KeyRegexp.MatchString(key) {
		return storeadapter.StoreNode{}, adapter.GetErrInjector.Error
	}

	components := adapter.keyComponents(key)
	container := adapter.rootNode
	for _, component := range components {
		var exists bool
		container, exists = container.nodes[component]
		if !exists {
			return storeadapter.StoreNode{}, storeadapter.ErrorKeyNotFound
		}
	}

	if container.dir {
		return storeadapter.StoreNode{}, storeadapter.ErrorNodeIsDirectory
	} else {
		return container.storeNode, nil
	}
}

func (adapter *FakeStoreAdapter) ListRecursively(key string) (storeadapter.StoreNode, error) {
	if adapter.ListErrInjector != nil && adapter.ListErrInjector.KeyRegexp.MatchString(key) {
		return storeadapter.StoreNode{}, adapter.ListErrInjector.Error
	}

	container := adapter.rootNode

	components := adapter.keyComponents(key)
	for _, component := range components {
		var exists bool
		container, exists = container.nodes[component]
		if !exists {
			return storeadapter.StoreNode{}, storeadapter.ErrorKeyNotFound
		}
	}

	if !container.dir {
		return storeadapter.StoreNode{}, storeadapter.ErrorNodeIsNotDirectory
	}

	return adapter.listContainerNode(key, container), nil
}

func (adapter *FakeStoreAdapter) listContainerNode(key string, container *containerNode) storeadapter.StoreNode {
	childNodes := []storeadapter.StoreNode{}

	for nodeKey, node := range container.nodes {
		if node.dir {
			if key == "/" {
				nodeKey = "/" + nodeKey
			} else {
				nodeKey = key + "/" + nodeKey
			}
			childNodes = append(childNodes, adapter.listContainerNode(nodeKey, node))
		} else {
			childNodes = append(childNodes, node.storeNode)
		}
	}

	return storeadapter.StoreNode{
		Key:        key,
		Dir:        true,
		ChildNodes: childNodes,
	}
}

func (adapter *FakeStoreAdapter) Delete(keys ...string) error {
	for _, key := range keys {
		node, _ := adapter.Get(key)

		if adapter.DeleteErrInjector != nil && adapter.DeleteErrInjector.KeyRegexp.MatchString(key) {
			return adapter.DeleteErrInjector.Error
		}

		components := adapter.keyComponents(key)
		container := adapter.rootNode
		parentNode := adapter.rootNode
		for _, component := range components {
			var exists bool
			parentNode = container
			container, exists = container.nodes[component]
			if !exists {
				return storeadapter.ErrorKeyNotFound
			}
		}

		delete(parentNode.nodes, components[len(components)-1])
		adapter.sendEvent(&node, nil, storeadapter.DeleteEvent)
	}

	return nil
}

func (adapter *FakeStoreAdapter) CompareAndDelete(node storeadapter.StoreNode) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) UpdateDirTTL(key string, ttl uint64) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) Update(node storeadapter.StoreNode) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) CompareAndSwap(oldNode storeadapter.StoreNode, newNode storeadapter.StoreNode) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) CompareAndSwapByIndex(oldNodeIndex uint64, newNode storeadapter.StoreNode) error {
	panic("not implemented")
}

func (adapter *FakeStoreAdapter) Watch(key string) (events <-chan storeadapter.WatchEvent, stop chan<- bool, errors <-chan error) {
	adapter.sendEvents = true
	adapter.WatchErrChannel = make(chan error, 1)

	// We haven't implemented stop yet

	return adapter.eventChannel, nil, adapter.WatchErrChannel
}

func (adapter *FakeStoreAdapter) keyComponents(key string) (components []string) {
	for _, s := range strings.Split(key, "/") {
		if s != "" {
			components = append(components, s)
		}
	}

	return components
}

func (adapter *FakeStoreAdapter) MaintainNode(storeNode storeadapter.StoreNode) (status <-chan bool, releaseNode chan chan bool, err error) {
	adapter.MaintainedNodeName = storeNode.Key
	adapter.ReleaseNodeChannel = make(chan chan bool, 1)

	return adapter.MaintainNodeStatus, adapter.ReleaseNodeChannel, adapter.MaintainNodeError
}
<|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 framework

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/test/e2e/framework"
)

// CreateNSIfNotExists creates a namespace if it is new, otherwise it will return the existing namespace pointer
func CreateNSIfNotExists(f *framework.Framework, namespaceName string) (*v1.Namespace, error) {
	var ns *v1.Namespace
	var err error
	fullNamespace := getNamespace(f, namespaceName)
	if fullNamespace == "" {
		ns, err = f.CreateNamespace(namespaceName, nil)
		if err != nil {
			return nil, err
		}
		framework.Logf("Created new namespace: %s", namespaceName)
	} else {
		ns, err = f.ClientSet.CoreV1().Namespaces().Get(fullNamespace, metav1.GetOptions{})
		if err != nil {
			return nil, err
		}
		framework.Logf("Namespace exists %s ", namespaceName)
	}
	return ns, err
}

// getNamespace takes the basename from the config and returns the full generated namespace name
func getNamespace(f *framework.Framework, baseName string) string {
	existingNamespaces, _ := f.ClientSet.Core().Namespaces().List(metav1.ListOptions{})
	for _, value := range existingNamespaces.Items {
		if value.GenerateName == fmt.Sprintf("e2e-tests-%v-", baseName) {
			return value.Name
		}
	}
	return ""
}

// ParseConfig unmarshalls the json file defined in the CL config into a struct
func (cl *ClusterLoaderObject) ParseConfig() (*v1.Pod, error) {
	pod := &v1.Pod{}
	// If the file is defined used that as the config
	if cl.File != "" {
		configFile, err := ioutil.ReadFile(MakePath(cl.File))
		if err != nil {
			return pod, err
		}

		if err = json.Unmarshal(configFile, &pod); err != nil {
			return pod, err
		}
	} else if cl.Image != "" && cl.Basename != "" {
		// Otherwise if we have the image name use that instead
		zero := int64(0)
		pod.Spec = v1.PodSpec{TerminationGracePeriodSeconds: &zero, Containers: []v1.Container{
			{
				Name:  cl.Basename,
				Image: cl.Image,
			},
		},
		}
	} else {
		return pod, errors.New("Missing both config file and imagename")
	}

	return pod, nil
}

// MakePath returns fully qualfied file location as a string
func MakePath(file string) string {
	// Handle an empty filename.
	if file == "" {
		framework.Failf("No template file defined!")
	}
	return filepath.Join("content/", file)
}

// ConvertToLabelSet will convert the string label to a set, while also setting a default value
func (cl *ClusterLoaderObject) ConvertToLabelSet() (labels.Set, error) {
	if cl.Label == "" {
		cl.Label = "purpose=test"
	}
	label, err := labels.ConvertSelectorToLabelsMap(cl.Label)
	return label, err
}
<commit_msg>Fix perf-tests<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 framework

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/test/e2e/framework"
)

// CreateNSIfNotExists creates a namespace if it is new, otherwise it will return the existing namespace pointer
func CreateNSIfNotExists(f *framework.Framework, namespaceName string) (*v1.Namespace, error) {
	var ns *v1.Namespace
	var err error
	fullNamespace := getNamespace(f, namespaceName)
	if fullNamespace == "" {
		ns, err = f.CreateNamespace(namespaceName, nil)
		if err != nil {
			return nil, err
		}
		framework.Logf("Created new namespace: %s", namespaceName)
	} else {
		ns, err = f.ClientSet.CoreV1().Namespaces().Get(fullNamespace, metav1.GetOptions{})
		if err != nil {
			return nil, err
		}
		framework.Logf("Namespace exists %s ", namespaceName)
	}
	return ns, err
}

// getNamespace takes the basename from the config and returns the full generated namespace name
func getNamespace(f *framework.Framework, baseName string) string {
	existingNamespaces, _ := f.ClientSet.Core().Namespaces().List(metav1.ListOptions{})
	for _, value := range existingNamespaces.Items {
		if value.GenerateName == fmt.Sprintf("e2e-tests-%v-", baseName) {
			return value.Name
		}
	}
	return ""
}

// ParseConfig unmarshalls the json file defined in the CL config into a struct
func (cl *ClusterLoaderObject) ParseConfig() (*v1.Pod, error) {
	pod := &v1.Pod{}
	// If the file is defined used that as the config
	if cl.File != "" {
		configFile, err := ioutil.ReadFile(MakePath(cl.File))
		if err != nil {
			return pod, err
		}

		if err = json.Unmarshal(configFile, &pod); err != nil {
			return pod, err
		}
	} else if cl.Image != "" && cl.Basename != "" {
		// Otherwise if we have the image name use that instead
		zero := int64(0)
		pod.Spec = v1.PodSpec{TerminationGracePeriodSeconds: &zero, Containers: []v1.Container{
			{
				Name:  cl.Basename,
				Image: cl.Image,
			},
		},
		}
	} else {
		return pod, errors.New("Missing both config file and imagename")
	}

	return pod, nil
}

// MakePath returns fully qualfied file location as a string
func MakePath(file string) string {
	// Handle an empty filename.
	if file == "" {
		framework.Failf("No template file defined!")
	}
	// TODO: We should enable passing this as a flag instead of hardcoding.
	return filepath.Join(os.Getenv("GOPATH"), "src/k8s.io/perf-tests/clusterloader/content/", file)
}

// ConvertToLabelSet will convert the string label to a set, while also setting a default value
func (cl *ClusterLoaderObject) ConvertToLabelSet() (labels.Set, error) {
	if cl.Label == "" {
		cl.Label = "purpose=test"
	}
	label, err := labels.ConvertSelectorToLabelsMap(cl.Label)
	return label, err
}
<|endoftext|>


<commit_before>package parse

import "testing"

// func TestModule(t *testing.T) {
// 	newState("").module()()
// }

func TestAtom(t *testing.T) {
	s := newState("ident")
	result, err := s.atom()()

	if !s.Exhausted() {
		t.Error("Source is not exhausted.")
	}

	if err != nil {
		t.Error(err.Error())
	}

	t.Logf("%#v", toString(result))
}

func TestStrip(t *testing.T) {
	s := newState("  ident  ")
	result, err := s.strip(s.atom())()

	if !s.Exhausted() {
		t.Error("Source is not exhausted.")
	}

	if err != nil {
		t.Error(err.Error())
	}

	t.Logf("%#v", toString(result))
}

// func TestList(t *testing.T) {
// 	s := newState("()")
// 	result, err := s.list()()

// 	if !s.Exhausted() {
// 		t.Error("Source is not exhausted.")
// 	}

// 	if err != nil {
// 		t.Error(err.Error())
// 	}

// 	t.Logf("%#v", toString(result))
// }

// func TestElem(t *testing.T) {
// 	for _, str := range []string{"ident", "  ident  "} {
// 		result, err := newState(str).atom()()

// 		if err != nil {
// 			t.Error(err.Error())
// 		}

// 		t.Logf("%#v", toString(result))
// 	}
// }

func TestBlank(t *testing.T) {
	for _, str := range []string{"", "   ", "\t", "\n\n", " ; laskdjf \n \t "} {
		s := newState(str)
		result, err := s.blank()()

		if !s.Exhausted() {
			t.Error("Source is not exhausted.")
		}

		if result != nil {
			t.Errorf("`result` should be null. (%#v)", result)
		}

		if err != nil {
			t.Errorf("`err` should be null. (%#v)", result)
		}
	}
}

func toString(any interface{}) string {
	xs := any.([]interface{})
	rs := make([]rune, len(xs))

	for i, x := range xs {
		rs[i] = x.(rune)
	}

	return string(rs)
}
<commit_msg>Test wrapChars<commit_after>package parse

import "testing"

// func TestModule(t *testing.T) {
// 	newState("").module()()
// }

func TestAtom(t *testing.T) {
	s := newState("ident")
	result, err := s.atom()()

	if !s.Exhausted() {
		t.Error("Source is not exhausted.")
	}

	if err != nil {
		t.Error(err.Error())
	} else {
		t.Logf("%#v", toString(result))
	}
}

func TestStrip(t *testing.T) {
	s := newState("  ident  ")
	result, err := s.strip(s.atom())()

	if !s.Exhausted() {
		t.Error("Source is not exhausted.")
	}

	if err != nil {
		t.Error(err.Error())
	} else {
		t.Logf("%#v", toString(result))
	}
}

func TestWrapChars(t *testing.T) {
	s := newState(" ; laskdfjsl \t  dkjf\n ( \tident \n)  ")
	result, err := s.wrapChars('(', s.atom(), ')')()

	if !s.Exhausted() {
		t.Error("Source is not exhausted.")
	}

	if err != nil {
		t.Error(err.Error())
	} else {
		t.Logf("%#v", toString(result))
	}
}

// func TestList(t *testing.T) {
// 	s := newState("()")
// 	result, err := s.list()()

// 	if !s.Exhausted() {
// 		t.Error("Source is not exhausted.")
// 	}

// 	if err != nil {
// 		t.Error(err.Error())
// 	}

// 	t.Logf("%#v", toString(result))
// }

// func TestElem(t *testing.T) {
// 	for _, str := range []string{"ident", "  ident  "} {
// 		t.Logf("source: %#v", str)

// 		s := newState(str)
// 		result, err := s.elem()()

// 		if err != nil {
// 			t.Error(err.Error())
// 		} else {
// 			t.Logf("%#v", toString(result))
// 		}
// 	}
// }

func TestBlank(t *testing.T) {
	for _, str := range []string{"", "   ", "\t", "\n\n", " ; laskdjf \n \t "} {
		s := newState(str)
		result, err := s.blank()()

		if !s.Exhausted() {
			t.Error("Source is not exhausted.")
		}

		if result != nil {
			t.Errorf("`result` should be null. (%#v)", result)
		}

		if err != nil {
			t.Errorf("`err` should be null. (%#v)", result)
		}
	}
}

func toString(any interface{}) string {
	xs := any.([]interface{})
	rs := make([]rune, len(xs))

	for i, x := range xs {
		rs[i] = x.(rune)
	}

	return string(rs)
}
<|endoftext|>


<commit_before>package openshift

import (
	"context"
	"strings"

	configv1 "github.com/openshift/api/config/v1"

	mf "github.com/jcrossley3/manifestival"
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
)

var log = logf.Log.WithName("openshift")

// Configure OpenShift if we're soaking in it
func Configure(c client.Client, scheme *runtime.Scheme) (result []mf.Transformer) {
	if t := ingress(c); t != nil {
		result = append(result, t)
	}
	if t := egress(c); t != nil {
		result = append(result, t)
	}
	if len(result) > 0 {
		// We must be on OpenShift!
		result = append(result, rbac(scheme))
	}
	return result
}

// TODO: These are addressed in master and shouldn't be required for 0.6.0
func rbac(scheme *runtime.Scheme) mf.Transformer {
	return func(u *unstructured.Unstructured) *unstructured.Unstructured {
		if u.GetKind() == "ClusterRole" && u.GetName() == "knative-serving-core" {
			role := &rbacv1.ClusterRole{}
			scheme.Convert(u, role, nil) // check for err?
		OUT:
			for i, rule := range role.Rules {
				for _, group := range rule.APIGroups {
					if group == "apps" {
						resource := "deployments/finalizers"
						log.Info("Adding RBAC", "group", group, "resource", resource)
						role.Rules[i].Resources = append(rule.Resources, resource)
						break OUT
					}
				}
			}
			// Required to open privileged ports in OpenShift
			rule := rbacv1.PolicyRule{
				Verbs:         []string{"use"},
				APIGroups:     []string{"security.openshift.io"},
				Resources:     []string{"securitycontextconstraints"},
				ResourceNames: []string{"privileged", "anyuid"},
			}
			log.Info("Adding RBAC", "rule", rule)
			role.Rules = append(role.Rules, rule)
			scheme.Convert(role, u, nil)
		}
		return u
	}
}

func ingress(c client.Client) mf.Transformer {
	ingressConfig := &configv1.Ingress{}
	if err := c.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, ingressConfig); err != nil {
		if !meta.IsNoMatchError(err) {
			log.Error(err, "Unexpected error during detection")
		}
		return nil
	}
	domain := ingressConfig.Spec.Domain
	if len(domain) == 0 {
		return nil
	}
	return func(u *unstructured.Unstructured) *unstructured.Unstructured {
		if u.GetKind() == "ConfigMap" && u.GetName() == "config-network" {
			k, v := domain, ""
			log.Info("Setting ingress", k, v)
			unstructured.SetNestedField(u.Object, v, "data", k)
		}
		return u
	}
}

func egress(c client.Client) mf.Transformer {
	networkConfig := &configv1.Network{}
	if err := c.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, networkConfig); err != nil {
		if !meta.IsNoMatchError(err) {
			log.Error(err, "Unexpected error during detection")
		}
		return nil
	}
	network := strings.Join(networkConfig.Spec.ServiceNetwork, ",")
	if len(network) == 0 {
		return nil
	}
	return func(u *unstructured.Unstructured) *unstructured.Unstructured {
		if u.GetKind() == "ConfigMap" && u.GetName() == "config-network" {
			k, v := "istio.sidecar.includeOutboundIPRanges", network
			log.Info("Setting egress", k, v)
			unstructured.SetNestedField(u.Object, v, "data", k)
		}
		return u
	}
}
<commit_msg>OpenShift ingress config goes in config-domain<commit_after>package openshift

import (
	"context"
	"strings"

	configv1 "github.com/openshift/api/config/v1"

	mf "github.com/jcrossley3/manifestival"
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
)

var log = logf.Log.WithName("openshift")

// Configure OpenShift if we're soaking in it
func Configure(c client.Client, scheme *runtime.Scheme) (result []mf.Transformer) {
	if t := ingress(c); t != nil {
		result = append(result, t)
	}
	if t := egress(c); t != nil {
		result = append(result, t)
	}
	if len(result) > 0 {
		// We must be on OpenShift!
		result = append(result, rbac(scheme))
	}
	return result
}

// TODO: These are addressed in master and shouldn't be required for 0.6.0
func rbac(scheme *runtime.Scheme) mf.Transformer {
	return func(u *unstructured.Unstructured) *unstructured.Unstructured {
		if u.GetKind() == "ClusterRole" && u.GetName() == "knative-serving-core" {
			role := &rbacv1.ClusterRole{}
			scheme.Convert(u, role, nil) // check for err?
		OUT:
			for i, rule := range role.Rules {
				for _, group := range rule.APIGroups {
					if group == "apps" {
						resource := "deployments/finalizers"
						log.Info("Adding RBAC", "group", group, "resource", resource)
						role.Rules[i].Resources = append(rule.Resources, resource)
						break OUT
					}
				}
			}
			// Required to open privileged ports in OpenShift
			rule := rbacv1.PolicyRule{
				Verbs:         []string{"use"},
				APIGroups:     []string{"security.openshift.io"},
				Resources:     []string{"securitycontextconstraints"},
				ResourceNames: []string{"privileged", "anyuid"},
			}
			log.Info("Adding RBAC", "rule", rule)
			role.Rules = append(role.Rules, rule)
			scheme.Convert(role, u, nil)
		}
		return u
	}
}

func ingress(c client.Client) mf.Transformer {
	ingressConfig := &configv1.Ingress{}
	if err := c.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, ingressConfig); err != nil {
		if !meta.IsNoMatchError(err) {
			log.Error(err, "Unexpected error during detection")
		}
		return nil
	}
	domain := ingressConfig.Spec.Domain
	if len(domain) == 0 {
		return nil
	}
	return func(u *unstructured.Unstructured) *unstructured.Unstructured {
		if u.GetKind() == "ConfigMap" && u.GetName() == "config-domain" {
			k, v := domain, ""
			log.Info("Setting ingress", k, v)
			unstructured.SetNestedField(u.Object, v, "data", k)
		}
		return u
	}
}

func egress(c client.Client) mf.Transformer {
	networkConfig := &configv1.Network{}
	if err := c.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, networkConfig); err != nil {
		if !meta.IsNoMatchError(err) {
			log.Error(err, "Unexpected error during detection")
		}
		return nil
	}
	network := strings.Join(networkConfig.Spec.ServiceNetwork, ",")
	if len(network) == 0 {
		return nil
	}
	return func(u *unstructured.Unstructured) *unstructured.Unstructured {
		if u.GetKind() == "ConfigMap" && u.GetName() == "config-network" {
			k, v := "istio.sidecar.includeOutboundIPRanges", network
			log.Info("Setting egress", k, v)
			unstructured.SetNestedField(u.Object, v, "data", k)
		}
		return u
	}
}
<|endoftext|>