<commit_before>package dropbox

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSharing_CreateSharedLink(t *testing.T) {
	c := client()
	out, err := c.Sharing.CreateSharedLink(&CreateSharedLinkInput{
		Path:     "/hello.txt"
	})

	assert.NoError(t, err, "error sharing file")
	assert.Equal(t, "/hello.txt", out.Path)
}

func TestSharing_ListSharedFolder(t *testing.T) {
	c := client()
	out, err := c.Sharing.ListSharedFolders(&ListSharedFolderInput{
		Limit: 1,
	})

	assert.NoError(t, err, "listing shared folders")
	assert.NotEmpty(t, out.Entries, "output should be non-empty")

	for out.Cursor != "" {
		out, err = c.Sharing.ListSharedFoldersContinue(&ListSharedFolderContinueInput{
			Cursor: out.Cursor,
		})

		assert.NoError(t, err, "listing shared folders")
		assert.NotEmpty(t, out.Entries, "output should be non-empty")
	}
}
<commit_msg>One more time<commit_after>package dropbox

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSharing_CreateSharedLink(t *testing.T) {
	c := client()
	out, err := c.Sharing.CreateSharedLink(&CreateSharedLinkInput{
		Path: "/hello.txt",
	})

	assert.NoError(t, err, "error sharing file")
	assert.Equal(t, "/hello.txt", out.Path)
}

func TestSharing_ListSharedFolder(t *testing.T) {
	c := client()
	out, err := c.Sharing.ListSharedFolders(&ListSharedFolderInput{
		Limit: 1,
	})

	assert.NoError(t, err, "listing shared folders")
	assert.NotEmpty(t, out.Entries, "output should be non-empty")

	for out.Cursor != "" {
		out, err = c.Sharing.ListSharedFoldersContinue(&ListSharedFolderContinueInput{
			Cursor: out.Cursor,
		})

		assert.NoError(t, err, "listing shared folders")
		assert.NotEmpty(t, out.Entries, "output should be non-empty")
	}
}
<|endoftext|>


<commit_before>package rabbus

import (
	"sync"
	"time"

	"github.com/rafaeljesus/retry-go"
	"github.com/sony/gobreaker"
	"github.com/streadway/amqp"
)

const (
	// Transient means higher throughput but messages will not be restored on broker restart.
	Transient uint8 = 1
	// Persistent messages will be restored to durable queues and lost on non-durable queues during server restart.
	Persistent uint8 = 2
	// ContentTypeJSON define json content type
	ContentTypeJSON = "application/json"
	// ContentTypePlain define plain text content type
	ContentTypePlain = "plain/text"

	contentEncoding = "UTF-8"
)

// Rabbus exposes a interface for emitting and listening for messages.
type Rabbus interface {
	// EmitAsync emits a message to RabbitMQ, but does not wait for the response from broker.
	EmitAsync() chan<- Message
	// EmitErr returns an error if encoding payload fails, or if after circuit breaker is open or retries attempts exceed.
	EmitErr() <-chan error
	// EmitOk returns true when the message was sent.
	EmitOk() <-chan struct{}
	// Listen to a message from RabbitMQ, returns
	// an error if exchange, queue name and function handler not passed or if an error occurred while creating
	// amqp consumer.
	Listen(ListenConfig) (chan ConsumerMessage, error)
	// Close attempt to close channel and connection.
	Close() error
}

// Config carries the variables to tune a newly started rabbus.
type Config struct {
	// Dsn is the amqp url address.
	Dsn string
	// Durable indicates of the queue will survive broker restarts. Default to true.
	Durable bool
	// PassiveExchange forces passive connection with all exchanges using
	// amqp's ExchangeDeclarePassive instead the default ExchangeDeclare
	PassiveExchange bool
	// Qos controls how many messages or how many bytes will be consumed before receiving delivery acks
	Qos
	// Retry settings for the in memory retry mechanism
	Retry
	// Breaker circuit breaker configuration
	Breaker
}

// Retry config for the in memory retry mechanism
type Retry struct {
	// Attempts is the max number of retries on broker outages.
	Attempts int
	// Sleep is the sleep time of the retry mechanism.
	Sleep time.Duration

	reconnectSleep time.Duration
}

// Breaker carries the configuration for circuit breaker
type Breaker struct {
	// Interval is the cyclic period of the closed state for CircuitBreaker to clear the internal counts,
	// If Interval is 0, CircuitBreaker doesn't clear the internal counts during the closed state.
	Interval time.Duration
	// Timeout is the period of the open state, after which the state of CircuitBreaker becomes half-open.
	// If Timeout is 0, the timeout value of CircuitBreaker is set to 60 seconds.
	Timeout time.Duration
	// Threshold when a threshold of failures has been reached, future calls to the broker will not run.
	// During this state, the circuit breaker will periodically allow the calls to run and, if it is successful,
	// will start running the function again. Default value is 5.
	Threshold uint32
	// OnStateChange is called whenever the state of CircuitBreaker changes.
	OnStateChange func(name, from, to string)
}

// Qos controls how many messages or how many bytes the server will try to keep on the network for consumers before receiving delivery acks.
type Qos struct {
	PrefetchCount int
	PrefetchSize  int
	Global        bool
}

// Message carries fields for sending messages.
type Message struct {
	// Exchange the exchange name.
	Exchange string
	// Kind the exchange type.
	Kind string
	// Key the routing key name.
	Key string
	// Payload the message payload.
	Payload []byte
	// DeliveryMode indicates if the is Persistent or Transient.
	DeliveryMode uint8
	// ContentType the message content-type.
	ContentType string
	// Headers the message application headers
	Headers map[string]interface{}
}

// ListenConfig carries fields for listening messages.
type ListenConfig struct {
	// Exchange the exchange name.
	Exchange string
	// Kind the exchange type.
	Kind string
	// Key the routing key name.
	Key string
	// PassiveExchange determines a passive exchange connection it uses
	// amqp's ExchangeDeclarePassive instead the default ExchangeDeclare
	PassiveExchange bool
	// Queue the queue name
	Queue string
}

func (lc ListenConfig) validate() error {
	if lc.Exchange == "" {
		return ErrMissingExchange
	}

	if lc.Kind == "" {
		return ErrMissingKind
	}

	if lc.Queue == "" {
		return ErrMissingQueue
	}

	return nil
}

// Delivery wraps amqp.Delivery struct
type Delivery struct {
	amqp.Delivery
}

// RabbusInterpreter interpret (implement) Rabbus interface definition
type RabbusInterpreter struct {
	mu         sync.RWMutex
	conn       *amqp.Connection
	ch         *amqp.Channel
	breaker    *gobreaker.CircuitBreaker
	emit       chan Message
	emitErr    chan error
	emitOk     chan struct{}
	config     Config
	exDeclared map[string]struct{}
}

// NewRabbus returns a new RabbusInterpreter configured with the
// variables from the config parameter, or returning an non-nil err
// if an error occurred while creating connection and channel.
func NewRabbus(c Config) (*RabbusInterpreter, error) {
	conn, ch, err := createConnAndChan(c.Dsn)
	if err != nil {
		return nil, err
	}

	if err := ch.Qos(c.Qos.PrefetchCount, c.Qos.PrefetchSize, c.Qos.Global); err != nil {
		return nil, err
	}

	if c.Threshold == 0 {
		c.Threshold = 5
	}

	if c.Retry.Sleep == 0 {
		c.Retry.reconnectSleep = time.Second * 10
	}

	ri := newRabbusInterpreter(conn, ch, c)
	go ri.register()

	return ri, nil
}

// EmitAsync emits a message to RabbitMQ, but does not wait for the response from broker.
func (ri *RabbusInterpreter) EmitAsync() chan<- Message {
	return ri.emit
}

// EmitErr returns an error if encoding payload fails, or if after circuit breaker is open or retries attempts exceed.
func (ri *RabbusInterpreter) EmitErr() <-chan error {
	return ri.emitErr
}

// EmitOk returns true when the message was sent.
func (ri *RabbusInterpreter) EmitOk() <-chan struct{} {
	return ri.emitOk
}

// Listen to a message from RabbitMQ, returns
// an error if exchange, queue name and function handler not passed or if an error occurred while creating
// amqp consumer.
func (ri *RabbusInterpreter) Listen(c ListenConfig) (chan ConsumerMessage, error) {
	if err := c.validate(); err != nil {
		return nil, err
	}

	msgs, err := ri.createConsumer(c)
	if err != nil {
		return nil, err
	}

	messages := make(chan ConsumerMessage, 256)
	go ri.wrapMessage(c, msgs, messages)
	go ri.observeClose(c, messages)

	return messages, nil
}

// Close attempt to close channel and connection.
func (ri *RabbusInterpreter) Close() error {
	close(ri.emit)
	close(ri.emitOk)
	close(ri.emitErr)

	return ri.closeConnAndChan()
}

func (ri *RabbusInterpreter) register() {
	for m := range ri.emit {
		ri.produce(m)
	}
}

func (ri *RabbusInterpreter) produce(m Message) {
	if _, ok := ri.exDeclared[m.Exchange]; !ok {
		if err := ri.declareExchange(m.Exchange, m.Kind); err != nil {
			ri.emitErr <- err
			return
		}
		ri.exDeclared[m.Exchange] = struct{}{}
	}

	if m.ContentType == "" {
		m.ContentType = ContentTypeJSON
	}

	if m.DeliveryMode == 0 {
		m.DeliveryMode = Persistent
	}

	opts := amqp.Publishing{
		Headers:         amqp.Table(m.Headers),
		ContentType:     m.ContentType,
		ContentEncoding: contentEncoding,
		DeliveryMode:    m.DeliveryMode,
		Timestamp:       time.Now(),
		Body:            m.Payload,
	}

	if _, err := ri.breaker.Execute(func() (interface{}, error) {
		return nil, retry.Do(func() error {
			return ri.ch.Publish(m.Exchange, m.Key, false, false, opts)
		}, ri.config.Retry.Attempts, ri.config.Retry.Sleep)
	}); err != nil {
		ri.emitErr <- err
		return
	}

	ri.emitOk <- struct{}{}
}

func (ri *RabbusInterpreter) createConsumer(c ListenConfig) (<-chan amqp.Delivery, error) {
	if err := ri.declareExchange(c.Exchange, c.Kind); err != nil {
		return nil, err
	}

	ri.exDeclared[c.Exchange] = struct{}{}

	q, err := ri.ch.QueueDeclare(c.Queue, ri.config.Durable, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	if err := ri.ch.QueueBind(q.Name, c.Key, c.Exchange, false, nil); err != nil {
		return nil, err
	}

	return ri.ch.Consume(q.Name, "", false, false, false, false, nil)
}

func (ri *RabbusInterpreter) wrapMessage(c ListenConfig, sourceChan <-chan amqp.Delivery, targetChan chan ConsumerMessage) {
	for {
		m, ok := <-sourceChan
		if ok {
			targetChan <- newConsumerMessage(m)
		}
	}
}

func (ri *RabbusInterpreter) observeClose(c ListenConfig, messages chan ConsumerMessage) {
	if err := <-ri.conn.NotifyClose(make(chan *amqp.Error)); err != nil {
		for {
			time.Sleep(ri.config.Retry.reconnectSleep)

			conn, ch, err := createConnAndChan(ri.config.Dsn)
			if err != nil {
				// if it fails to create amqp conn or channel it should try later
				continue
			}

			ri.mu.Lock()
			ri.conn = conn
			ri.ch = ch
			ri.mu.Unlock()

			msgs, err := ri.createConsumer(c)
			if err != nil {
				// if it fails to re-create topologies it should close the conn and channel previously created
				ri.closeConnAndChan()
				// and try again later
				continue
			}

			go ri.wrapMessage(c, msgs, messages)
			go ri.observeClose(c, messages)

			// at this point we successfully restablished the connection
			break
		}
	}
}

func (ri *RabbusInterpreter) declareExchange(ex, kind string) error {
	if ri.config.PassiveExchange {
		return ri.ch.ExchangeDeclarePassive(ex, kind, ri.config.Durable, false, false, false, nil)
	}

	return ri.ch.ExchangeDeclare(ex, kind, ri.config.Durable, false, false, false, nil)
}

func (ri *RabbusInterpreter) closeConnAndChan() (err error) {
	if err = ri.ch.Close(); err != nil {
		return
	}

	if ri.conn != nil {
		err = ri.conn.Close()
	}

	return
}

func createConnAndChan(dsn string) (*amqp.Connection, *amqp.Channel, error) {
	conn, err := amqp.Dial(dsn)
	if err != nil {
		return nil, nil, err
	}

	ch, err := conn.Channel()
	if err != nil {
		return nil, nil, err
	}

	return conn, ch, nil
}

func newRabbusInterpreter(conn *amqp.Connection, ch *amqp.Channel, c Config) *RabbusInterpreter {
	return &RabbusInterpreter{
		conn:       conn,
		ch:         ch,
		config:     c,
		breaker:    gobreaker.NewCircuitBreaker(newBreakerSettings(c.Breaker)),
		emit:       make(chan Message),
		emitErr:    make(chan error),
		emitOk:     make(chan struct{}),
		exDeclared: make(map[string]struct{}),
	}
}

func newBreakerSettings(c Breaker) gobreaker.Settings {
	return gobreaker.Settings{
		Name:     "rabbus-circuit-breaker",
		Interval: c.Interval,
		Timeout:  c.Timeout,
		ReadyToTrip: func(counts gobreaker.Counts) bool {
			return counts.ConsecutiveFailures > c.Threshold
		},
		OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
			c.OnStateChange(name, from.String(), to.String())
		},
	}
}
<commit_msg>Fix producer not resilient<commit_after>package rabbus

import (
	"sync"
	"time"

	"github.com/rafaeljesus/retry-go"
	"github.com/sony/gobreaker"
	"github.com/streadway/amqp"
)

const (
	// Transient means higher throughput but messages will not be restored on broker restart.
	Transient uint8 = 1
	// Persistent messages will be restored to durable queues and lost on non-durable queues during server restart.
	Persistent uint8 = 2
	// ContentTypeJSON define json content type
	ContentTypeJSON = "application/json"
	// ContentTypePlain define plain text content type
	ContentTypePlain = "plain/text"

	contentEncoding = "UTF-8"
)

// Rabbus exposes a interface for emitting and listening for messages.
type Rabbus interface {
	// EmitAsync emits a message to RabbitMQ, but does not wait for the response from broker.
	EmitAsync() chan<- Message
	// EmitErr returns an error if encoding payload fails, or if after circuit breaker is open or retries attempts exceed.
	EmitErr() <-chan error
	// EmitOk returns true when the message was sent.
	EmitOk() <-chan struct{}
	// Listen to a message from RabbitMQ, returns
	// an error if exchange, queue name and function handler not passed or if an error occurred while creating
	// amqp consumer.
	Listen(ListenConfig) (chan ConsumerMessage, error)
	// Close attempt to close channel and connection.
	Close() error
}

// Config carries the variables to tune a newly started rabbus.
type Config struct {
	// Dsn is the amqp url address.
	Dsn string
	// Durable indicates of the queue will survive broker restarts. Default to true.
	Durable bool
	// PassiveExchange forces passive connection with all exchanges using
	// amqp's ExchangeDeclarePassive instead the default ExchangeDeclare
	PassiveExchange bool
	// Qos controls how many messages or how many bytes will be consumed before receiving delivery acks
	Qos
	// Retry settings for the in memory retry mechanism
	Retry
	// Breaker circuit breaker configuration
	Breaker
}

// Retry config for the in memory retry mechanism
type Retry struct {
	// Attempts is the max number of retries on broker outages.
	Attempts int
	// Sleep is the sleep time of the retry mechanism.
	Sleep time.Duration

	reconnectSleep time.Duration
}

// Breaker carries the configuration for circuit breaker
type Breaker struct {
	// Interval is the cyclic period of the closed state for CircuitBreaker to clear the internal counts,
	// If Interval is 0, CircuitBreaker doesn't clear the internal counts during the closed state.
	Interval time.Duration
	// Timeout is the period of the open state, after which the state of CircuitBreaker becomes half-open.
	// If Timeout is 0, the timeout value of CircuitBreaker is set to 60 seconds.
	Timeout time.Duration
	// Threshold when a threshold of failures has been reached, future calls to the broker will not run.
	// During this state, the circuit breaker will periodically allow the calls to run and, if it is successful,
	// will start running the function again. Default value is 5.
	Threshold uint32
	// OnStateChange is called whenever the state of CircuitBreaker changes.
	OnStateChange func(name, from, to string)
}

// Qos controls how many messages or how many bytes the server will try to keep on the network for consumers before receiving delivery acks.
type Qos struct {
	PrefetchCount int
	PrefetchSize  int
	Global        bool
}

// Message carries fields for sending messages.
type Message struct {
	// Exchange the exchange name.
	Exchange string
	// Kind the exchange type.
	Kind string
	// Key the routing key name.
	Key string
	// Payload the message payload.
	Payload []byte
	// DeliveryMode indicates if the is Persistent or Transient.
	DeliveryMode uint8
	// ContentType the message content-type.
	ContentType string
	// Headers the message application headers
	Headers map[string]interface{}
}

// ListenConfig carries fields for listening messages.
type ListenConfig struct {
	// Exchange the exchange name.
	Exchange string
	// Kind the exchange type.
	Kind string
	// Key the routing key name.
	Key string
	// PassiveExchange determines a passive exchange connection it uses
	// amqp's ExchangeDeclarePassive instead the default ExchangeDeclare
	PassiveExchange bool
	// Queue the queue name
	Queue string
}

func (lc ListenConfig) validate() error {
	if lc.Exchange == "" {
		return ErrMissingExchange
	}

	if lc.Kind == "" {
		return ErrMissingKind
	}

	if lc.Queue == "" {
		return ErrMissingQueue
	}

	return nil
}

// Delivery wraps amqp.Delivery struct
type Delivery struct {
	amqp.Delivery
}

// RabbusInterpreter interpret (implement) Rabbus interface definition
type RabbusInterpreter struct {
	mu         sync.RWMutex
	conn       *amqp.Connection
	ch         *amqp.Channel
	breaker    *gobreaker.CircuitBreaker
	emit       chan Message
	emitErr    chan error
	emitOk     chan struct{}
	reconn     chan struct{}
	config     Config
	exDeclared map[string]struct{}
}

// NewRabbus returns a new RabbusInterpreter configured with the
// variables from the config parameter, or returning an non-nil err
// if an error occurred while creating connection and channel.
func NewRabbus(c Config) (*RabbusInterpreter, error) {
	conn, ch, err := createConnAndChan(c.Dsn)
	if err != nil {
		return nil, err
	}

	if err := ch.Qos(c.Qos.PrefetchCount, c.Qos.PrefetchSize, c.Qos.Global); err != nil {
		return nil, err
	}

	if c.Threshold == 0 {
		c.Threshold = 5
	}

	if c.Retry.Sleep == 0 {
		c.Retry.reconnectSleep = time.Second * 10
	}

	ri := newRabbusInterpreter(conn, ch, c)
	go ri.register()
	go ri.listenClose()

	return ri, nil
}

// EmitAsync emits a message to RabbitMQ, but does not wait for the response from broker.
func (ri *RabbusInterpreter) EmitAsync() chan<- Message {
	return ri.emit
}

// EmitErr returns an error if encoding payload fails, or if after circuit breaker is open or retries attempts exceed.
func (ri *RabbusInterpreter) EmitErr() <-chan error {
	return ri.emitErr
}

// EmitOk returns true when the message was sent.
func (ri *RabbusInterpreter) EmitOk() <-chan struct{} {
	return ri.emitOk
}

// Listen to a message from RabbitMQ, returns
// an error if exchange, queue name and function handler not passed or if an error occurred while creating
// amqp consumer.
func (ri *RabbusInterpreter) Listen(c ListenConfig) (chan ConsumerMessage, error) {
	if err := c.validate(); err != nil {
		return nil, err
	}

	msgs, err := ri.createConsumer(c)
	if err != nil {
		return nil, err
	}

	messages := make(chan ConsumerMessage, 256)
	go ri.wrapMessage(c, msgs, messages)
	go ri.listenReconn(c, messages)

	return messages, nil
}

// Close attempt to close channel and connection.
func (ri *RabbusInterpreter) Close() error {
	close(ri.emit)
	close(ri.emitOk)
	close(ri.emitErr)
	close(ri.reconn)

	return ri.closeConnAndChan()
}

func (ri *RabbusInterpreter) register() {
	for m := range ri.emit {
		ri.produce(m)
	}
}

func (ri *RabbusInterpreter) produce(m Message) {
	if _, ok := ri.exDeclared[m.Exchange]; !ok {
		if err := ri.declareExchange(m.Exchange, m.Kind); err != nil {
			ri.emitErr <- err
			return
		}
		ri.exDeclared[m.Exchange] = struct{}{}
	}

	if m.ContentType == "" {
		m.ContentType = ContentTypeJSON
	}

	if m.DeliveryMode == 0 {
		m.DeliveryMode = Persistent
	}

	opts := amqp.Publishing{
		Headers:         amqp.Table(m.Headers),
		ContentType:     m.ContentType,
		ContentEncoding: contentEncoding,
		DeliveryMode:    m.DeliveryMode,
		Timestamp:       time.Now(),
		Body:            m.Payload,
	}

	if _, err := ri.breaker.Execute(func() (interface{}, error) {
		return nil, retry.Do(func() error {
			return ri.ch.Publish(m.Exchange, m.Key, false, false, opts)
		}, ri.config.Retry.Attempts, ri.config.Retry.Sleep)
	}); err != nil {
		ri.emitErr <- err
		return
	}

	ri.emitOk <- struct{}{}
}

func (ri *RabbusInterpreter) createConsumer(c ListenConfig) (<-chan amqp.Delivery, error) {
	if err := ri.declareExchange(c.Exchange, c.Kind); err != nil {
		return nil, err
	}

	ri.exDeclared[c.Exchange] = struct{}{}

	q, err := ri.ch.QueueDeclare(c.Queue, ri.config.Durable, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	if err := ri.ch.QueueBind(q.Name, c.Key, c.Exchange, false, nil); err != nil {
		return nil, err
	}

	return ri.ch.Consume(q.Name, "", false, false, false, false, nil)
}

func (ri *RabbusInterpreter) wrapMessage(c ListenConfig, sourceChan <-chan amqp.Delivery, targetChan chan ConsumerMessage) {
	for {
		m, ok := <-sourceChan
		if ok {
			targetChan <- newConsumerMessage(m)
		}
	}
}

func (ri *RabbusInterpreter) listenClose() {
	if err := <-ri.conn.NotifyClose(make(chan *amqp.Error)); err != nil {
		for {
			time.Sleep(ri.config.Retry.reconnectSleep)

			conn, ch, err := createConnAndChan(ri.config.Dsn)
			if err != nil {
				// try again later
				continue
			}

			ri.mu.Lock()
			ri.conn = conn
			ri.ch = ch
			ri.mu.Unlock()

			go ri.listenClose()

			ri.reconn <- struct{}{}

			break
		}
	}
}

func (ri *RabbusInterpreter) listenReconn(c ListenConfig, messages chan ConsumerMessage) {
	for {
		_, ok := <-ri.reconn
		if ok {
			msgs, err := ri.createConsumer(c)
			if err != nil {
				ri.closeConnAndChan()
				continue
			}

			go ri.wrapMessage(c, msgs, messages)
			go ri.listenReconn(c, messages)
			break
		}
	}
}

func (ri *RabbusInterpreter) declareExchange(ex, kind string) error {
	if ri.config.PassiveExchange {
		return ri.ch.ExchangeDeclarePassive(ex, kind, ri.config.Durable, false, false, false, nil)
	}

	return ri.ch.ExchangeDeclare(ex, kind, ri.config.Durable, false, false, false, nil)
}

func (ri *RabbusInterpreter) closeConnAndChan() (err error) {
	if err = ri.ch.Close(); err != nil {
		return
	}

	if ri.conn != nil {
		err = ri.conn.Close()
	}

	return
}

func createConnAndChan(dsn string) (*amqp.Connection, *amqp.Channel, error) {
	conn, err := amqp.Dial(dsn)
	if err != nil {
		return nil, nil, err
	}

	ch, err := conn.Channel()
	if err != nil {
		return nil, nil, err
	}

	return conn, ch, nil
}

func newRabbusInterpreter(conn *amqp.Connection, ch *amqp.Channel, c Config) *RabbusInterpreter {
	return &RabbusInterpreter{
		conn:       conn,
		ch:         ch,
		config:     c,
		breaker:    gobreaker.NewCircuitBreaker(newBreakerSettings(c.Breaker)),
		emit:       make(chan Message),
		emitErr:    make(chan error),
		emitOk:     make(chan struct{}),
		reconn:     make(chan struct{}),
		exDeclared: make(map[string]struct{}),
	}
}

func newBreakerSettings(c Breaker) gobreaker.Settings {
	return gobreaker.Settings{
		Name:     "rabbus-circuit-breaker",
		Interval: c.Interval,
		Timeout:  c.Timeout,
		ReadyToTrip: func(counts gobreaker.Counts) bool {
			return counts.ConsecutiveFailures > c.Threshold
		},
		OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
			c.OnStateChange(name, from.String(), to.String())
		},
	}
}
<|endoftext|>


<commit_before>package plugin

import (
	"context"
	"errors"

	"google.golang.org/grpc"

	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/plugin/pb"
)

func newGRPCStorageClient(conn *grpc.ClientConn) *GRPCStorageClient {
	return &GRPCStorageClient{
		client: pb.NewStorageClient(conn),
	}
}

// GRPCStorageClient is an implementation of logical.Storage that communicates
// over RPC.
type GRPCStorageClient struct {
	client pb.StorageClient
}

func (s *GRPCStorageClient) List(ctx context.Context, prefix string) ([]string, error) {
	reply, err := s.client.List(ctx, &pb.StorageListArgs{
		Prefix: prefix,
	}, largeMsgGRPCCallOpts...)
	if err != nil {
		return reply.Keys, err
	}
	if reply.Err != "" {
		return reply.Keys, errors.New(reply.Err)
	}
	return reply.Keys, nil
}

func (s *GRPCStorageClient) Get(ctx context.Context, key string) (*logical.StorageEntry, error) {
	reply, err := s.client.Get(ctx, &pb.StorageGetArgs{
		Key: key,
	}, largeMsgGRPCCallOpts...)
	if err != nil {
		return nil, err
	}
	if reply.Err != "" {
		return nil, errors.New(reply.Err)
	}
	return pb.ProtoStorageEntryToLogicalStorageEntry(reply.Entry), nil
}

func (s *GRPCStorageClient) Put(ctx context.Context, entry *logical.StorageEntry) error {
	reply, err := s.client.Put(ctx, &pb.StoragePutArgs{
		Entry: pb.LogicalStorageEntryToProtoStorageEntry(entry),
	}, largeMsgGRPCCallOpts...)
	if err != nil {
		return err
	}
	if reply.Err != "" {
		return errors.New(reply.Err)
	}
	return nil
}

func (s *GRPCStorageClient) Delete(ctx context.Context, key string) error {
	reply, err := s.client.Delete(ctx, &pb.StorageDeleteArgs{
		Key: key,
	})
	if err != nil {
		return err
	}
	if reply.Err != "" {
		return errors.New(reply.Err)
	}
	return nil
}

// StorageServer is a net/rpc compatible structure for serving
type GRPCStorageServer struct {
	impl logical.Storage
}

func (s *GRPCStorageServer) List(ctx context.Context, args *pb.StorageListArgs) (*pb.StorageListReply, error) {
	keys, err := s.impl.List(ctx, args.Prefix)
	return &pb.StorageListReply{
		Keys: keys,
		Err:  pb.ErrToString(err),
	}, nil
}

func (s *GRPCStorageServer) Get(ctx context.Context, args *pb.StorageGetArgs) (*pb.StorageGetReply, error) {
	storageEntry, err := s.impl.Get(ctx, args.Key)
	return &pb.StorageGetReply{
		Entry: pb.LogicalStorageEntryToProtoStorageEntry(storageEntry),
		Err:   pb.ErrToString(err),
	}, nil
}

func (s *GRPCStorageServer) Put(ctx context.Context, args *pb.StoragePutArgs) (*pb.StoragePutReply, error) {
	err := s.impl.Put(ctx, pb.ProtoStorageEntryToLogicalStorageEntry(args.Entry))
	return &pb.StoragePutReply{
		Err: pb.ErrToString(err),
	}, nil
}

func (s *GRPCStorageServer) Delete(ctx context.Context, args *pb.StorageDeleteArgs) (*pb.StorageDeleteReply, error) {
	err := s.impl.Delete(ctx, args.Key)
	return &pb.StorageDeleteReply{
		Err: pb.ErrToString(err),
	}, nil
}
<commit_msg>plugin/grpc: Fix a panic when a transport error occurs during a list (#4244)<commit_after>package plugin

import (
	"context"
	"errors"

	"google.golang.org/grpc"

	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/plugin/pb"
)

func newGRPCStorageClient(conn *grpc.ClientConn) *GRPCStorageClient {
	return &GRPCStorageClient{
		client: pb.NewStorageClient(conn),
	}
}

// GRPCStorageClient is an implementation of logical.Storage that communicates
// over RPC.
type GRPCStorageClient struct {
	client pb.StorageClient
}

func (s *GRPCStorageClient) List(ctx context.Context, prefix string) ([]string, error) {
	reply, err := s.client.List(ctx, &pb.StorageListArgs{
		Prefix: prefix,
	}, largeMsgGRPCCallOpts...)
	if err != nil {
		return []string{}, err
	}
	if reply.Err != "" {
		return reply.Keys, errors.New(reply.Err)
	}
	return reply.Keys, nil
}

func (s *GRPCStorageClient) Get(ctx context.Context, key string) (*logical.StorageEntry, error) {
	reply, err := s.client.Get(ctx, &pb.StorageGetArgs{
		Key: key,
	}, largeMsgGRPCCallOpts...)
	if err != nil {
		return nil, err
	}
	if reply.Err != "" {
		return nil, errors.New(reply.Err)
	}
	return pb.ProtoStorageEntryToLogicalStorageEntry(reply.Entry), nil
}

func (s *GRPCStorageClient) Put(ctx context.Context, entry *logical.StorageEntry) error {
	reply, err := s.client.Put(ctx, &pb.StoragePutArgs{
		Entry: pb.LogicalStorageEntryToProtoStorageEntry(entry),
	}, largeMsgGRPCCallOpts...)
	if err != nil {
		return err
	}
	if reply.Err != "" {
		return errors.New(reply.Err)
	}
	return nil
}

func (s *GRPCStorageClient) Delete(ctx context.Context, key string) error {
	reply, err := s.client.Delete(ctx, &pb.StorageDeleteArgs{
		Key: key,
	})
	if err != nil {
		return err
	}
	if reply.Err != "" {
		return errors.New(reply.Err)
	}
	return nil
}

// StorageServer is a net/rpc compatible structure for serving
type GRPCStorageServer struct {
	impl logical.Storage
}

func (s *GRPCStorageServer) List(ctx context.Context, args *pb.StorageListArgs) (*pb.StorageListReply, error) {
	keys, err := s.impl.List(ctx, args.Prefix)
	return &pb.StorageListReply{
		Keys: keys,
		Err:  pb.ErrToString(err),
	}, nil
}

func (s *GRPCStorageServer) Get(ctx context.Context, args *pb.StorageGetArgs) (*pb.StorageGetReply, error) {
	storageEntry, err := s.impl.Get(ctx, args.Key)
	return &pb.StorageGetReply{
		Entry: pb.LogicalStorageEntryToProtoStorageEntry(storageEntry),
		Err:   pb.ErrToString(err),
	}, nil
}

func (s *GRPCStorageServer) Put(ctx context.Context, args *pb.StoragePutArgs) (*pb.StoragePutReply, error) {
	err := s.impl.Put(ctx, pb.ProtoStorageEntryToLogicalStorageEntry(args.Entry))
	return &pb.StoragePutReply{
		Err: pb.ErrToString(err),
	}, nil
}

func (s *GRPCStorageServer) Delete(ctx context.Context, args *pb.StorageDeleteArgs) (*pb.StorageDeleteReply, error) {
	err := s.impl.Delete(ctx, args.Key)
	return &pb.StorageDeleteReply{
		Err: pb.ErrToString(err),
	}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/consensus"
)

var (
	updateCmd = &cobra.Command{
		Use:   "update",
		Short: "Update Sia",
		Long:  "Check for (and/or download) available updates for Sia.",
		Run:   wrap(updatecmd),
	}

	updateCheckCmd = &cobra.Command{
		Use:   "check",
		Short: "Check for available updates",
		Long:  "Check for available updates.",
		Run:   wrap(updatecheckcmd),
	}

	updateApplyCmd = &cobra.Command{
		Use:   "apply [version]",
		Short: "Apply an update",
		Long:  "Update Sia to 'version'. To use the latest version, run 'update apply current', or simply 'update'.",
		Run:   wrap(updateapplycmd),
	}

	statusCmd = &cobra.Command{
		Use:   "status",
		Short: "Print the current state of the daemon",
		Long:  "Query the daemon for values such as the current difficulty, target, height, peers, transactions, etc.",
		Run:   wrap(statuscmd),
	}

	stopCmd = &cobra.Command{
		Use:   "stop",
		Short: "Stop the Sia daemon",
		Long:  "Stop the Sia daemon.",
		Run:   wrap(stopcmd),
	}

	syncCmd = &cobra.Command{
		Use:   "sync",
		Short: "Synchronize with the network",
		Long:  "Attempt to synchronize with a randomly selected peer.",
		Run:   wrap(synccmd),
	}
)

// TODO: this should be defined outside of siac
type updateResp struct {
	Available bool
	Version   string
}

func updatecmd() {
	update := new(updateResp)
	err := getAPI("/update/check", update)
	if err != nil {
		fmt.Println("Could not check for update:", err)
		return
	}
	if !update.Available {
		fmt.Println("Already up to date.")
		return
	}
	err = callAPI("/update/apply?version=" + update.Version)
	if err != nil {
		fmt.Println("Could not apply update:", err)
		return
	}
	fmt.Printf("Updated to version %s! Restart siad now.\n", update.Version)
}

func updatecheckcmd() {
	update := new(updateResp)
	err := getAPI("/update/check", update)
	if err != nil {
		fmt.Println("Could not check for update:", err)
		return
	}
	if !update.Available {
		fmt.Println("Up to date!")
		return
	}
	fmt.Printf("Update %s is available! Run 'siac update apply %s' to install it.\n", update.Version, update.Version)
}

func updateapplycmd(version string) {
	err := callAPI("/update/apply?version=" + version)
	if err != nil {
		fmt.Println("Could not apply update:", err)
		return
	}
	fmt.Printf("Updated to version %s! Restart siad now.\n", version)
}

func statuscmd() {
	status := new(consensus.StateInfo)
	err := getAPI("/status", status)
	if err != nil {
		fmt.Println("Could not get daemon status:", err)
		return
	}
	fmt.Printf(`Block:  %v
Height: %v
Target: %v
`, status.CurrentBlock, status.Height, status.Target)
}

func stopcmd() {
	err := callAPI("/stop")
	if err != nil {
		fmt.Println("Could not stop daemon:", err)
		return
	}
	fmt.Println("Sia daemon stopped.")
}

func synccmd() {
	err := callAPI("/sync")
	if err != nil {
		fmt.Println("Could not sync:", err)
	}
	fmt.Println("Sync initiated.")
}
<commit_msg>fix some siac API calls<commit_after>package main

import (
	"fmt"

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/consensus"
)

var (
	updateCmd = &cobra.Command{
		Use:   "update",
		Short: "Update Sia",
		Long:  "Check for (and/or download) available updates for Sia.",
		Run:   wrap(updatecmd),
	}

	updateCheckCmd = &cobra.Command{
		Use:   "check",
		Short: "Check for available updates",
		Long:  "Check for available updates.",
		Run:   wrap(updatecheckcmd),
	}

	updateApplyCmd = &cobra.Command{
		Use:   "apply [version]",
		Short: "Apply an update",
		Long:  "Update Sia to 'version'. To use the latest version, run 'update apply current', or simply 'update'.",
		Run:   wrap(updateapplycmd),
	}

	statusCmd = &cobra.Command{
		Use:   "status",
		Short: "Print the current state of the daemon",
		Long:  "Query the daemon for values such as the current difficulty, target, height, peers, transactions, etc.",
		Run:   wrap(statuscmd),
	}

	stopCmd = &cobra.Command{
		Use:   "stop",
		Short: "Stop the Sia daemon",
		Long:  "Stop the Sia daemon.",
		Run:   wrap(stopcmd),
	}

	syncCmd = &cobra.Command{
		Use:   "sync",
		Short: "Synchronize with the network",
		Long:  "Attempt to synchronize with a randomly selected peer.",
		Run:   wrap(synccmd),
	}
)

// TODO: this should be defined outside of siac
type updateResp struct {
	Available bool
	Version   string
}

func updatecmd() {
	update := new(updateResp)
	err := getAPI("/daemon/update/check", update)
	if err != nil {
		fmt.Println("Could not check for update:", err)
		return
	}
	if !update.Available {
		fmt.Println("Already up to date.")
		return
	}
	err = callAPI("/daemon/update/apply?version=" + update.Version)
	if err != nil {
		fmt.Println("Could not apply update:", err)
		return
	}
	fmt.Printf("Updated to version %s! Restart siad now.\n", update.Version)
}

func updatecheckcmd() {
	update := new(updateResp)
	err := getAPI("/daemon/update/check", update)
	if err != nil {
		fmt.Println("Could not check for update:", err)
		return
	}
	if !update.Available {
		fmt.Println("Up to date!")
		return
	}
	fmt.Printf("Update %s is available! Run 'siac update apply %s' to install it.\n", update.Version, update.Version)
}

func updateapplycmd(version string) {
	err := callAPI("/daemon/update/apply?version=" + version)
	if err != nil {
		fmt.Println("Could not apply update:", err)
		return
	}
	fmt.Printf("Updated to version %s! Restart siad now.\n", version)
}

func statuscmd() {
	status := new(consensus.StateInfo)
	err := getAPI("/consensus/status", status)
	if err != nil {
		fmt.Println("Could not get daemon status:", err)
		return
	}
	fmt.Printf(`Block:  %v
Height: %v
Target: %v
`, status.CurrentBlock, status.Height, status.Target)
}

func stopcmd() {
	err := callAPI("/stop/stop")
	if err != nil {
		fmt.Println("Could not stop daemon:", err)
		return
	}
	fmt.Println("Sia daemon stopped.")
}

func synccmd() {
	err := callAPI("/sync")
	if err != nil {
		fmt.Println("Could not sync:", err)
	}
	fmt.Println("Sync initiated.")
}
<|endoftext|>


<commit_before>// Copyright 2017 Istio 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 crd

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

	"github.com/prometheus/client_golang/prometheus"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/cache"

	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/serviceregistry/kube"
	"istio.io/istio/pkg/log"
)

// controller is a collection of synchronized resource watchers.
// Caches are thread-safe
type controller struct {
	client *Client
	queue  kube.Queue
	kinds  map[string]cacheHandler
}

type cacheHandler struct {
	informer cache.SharedIndexInformer
	handler  *kube.ChainHandler
}

var (
	// experiment on getting some monitoring on config errors.
	k8sEvents = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "pilot_k8s_cfg_events",
		Help: "Events from k8s config.",
	}, []string{"type", "event"})

	k8sErrors = prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Name: "pilot_k8s_object_errors",
		Help: "Errors converting k8s CRDs",
	}, []string{"name"})

	// InvalidCRDs contains a sync.Map keyed by the namespace/name of the entry, and has the error as value.
	// It can be used by tools like ctrlz to display the errors.
	InvalidCRDs atomic.Value
)

func init() {
	prometheus.MustRegister(k8sEvents)
}

// NewController creates a new Kubernetes controller for CRDs
// Use "" for namespace to listen for all namespace changes
func NewController(client *Client, options kube.ControllerOptions) model.ConfigStoreCache {
	log.Infof("CRD controller watching namespaces %q", options.WatchedNamespace)

	// Queue requires a time duration for a retry delay after a handler error
	out := &controller{
		client: client,
		queue:  kube.NewQueue(1 * time.Second),
		kinds:  make(map[string]cacheHandler),
	}

	// add stores for CRD kinds
	for _, schema := range client.ConfigDescriptor() {
		out.addInformer(schema, options.WatchedNamespace, options.ResyncPeriod)
	}

	return out
}

func (c *controller) addInformer(schema model.ProtoSchema, namespace string, resyncPeriod time.Duration) {
	c.kinds[schema.Type] = c.createInformer(knownTypes[schema.Type].object.DeepCopyObject(), schema.Type, resyncPeriod,
		func(opts meta_v1.ListOptions) (result runtime.Object, err error) {
			result = knownTypes[schema.Type].collection.DeepCopyObject()
			rc, ok := c.client.clientset[apiVersion(&schema)]
			if !ok {
				return nil, fmt.Errorf("client not initialized %s", schema.Type)
			}
			req := rc.dynamic.Get().
				Resource(ResourceName(schema.Plural)).
				VersionedParams(&opts, meta_v1.ParameterCodec)

			if !schema.ClusterScoped {
				req = req.Namespace(namespace)
			}
			err = req.Do().Into(result)
			return
		},
		func(opts meta_v1.ListOptions) (watch.Interface, error) {
			rc, ok := c.client.clientset[apiVersion(&schema)]
			if !ok {
				return nil, fmt.Errorf("client not initialized %s", schema.Type)
			}
			opts.Watch = true
			req := rc.dynamic.Get().
				Resource(ResourceName(schema.Plural)).
				VersionedParams(&opts, meta_v1.ParameterCodec)
			if !schema.ClusterScoped {
				req = req.Namespace(namespace)
			}
			return req.Watch()
		})
}

// notify is the first handler in the handler chain.
// Returning an error causes repeated execution of the entire chain.
func (c *controller) notify(obj interface{}, event model.Event) error {
	if !c.HasSynced() {
		return errors.New("waiting till full synchronization")
	}
	_, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
	if err != nil {
		log.Infof("Error retrieving key: %v", err)
	}
	return nil
}

func (c *controller) createInformer(
	o runtime.Object,
	otype string,
	resyncPeriod time.Duration,
	lf cache.ListFunc,
	wf cache.WatchFunc) cacheHandler {
	handler := &kube.ChainHandler{}
	handler.Append(c.notify)

	// TODO: finer-grained index (perf)
	informer := cache.NewSharedIndexInformer(
		&cache.ListWatch{ListFunc: lf, WatchFunc: wf}, o,
		resyncPeriod, cache.Indexers{})

	informer.AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			// TODO: filtering functions to skip over un-referenced resources (perf)
			AddFunc: func(obj interface{}) {
				k8sEvents.With(prometheus.Labels{"type": otype, "event": "add"}).Add(1)
				c.queue.Push(kube.NewTask(handler.Apply, obj, model.EventAdd))
			},
			UpdateFunc: func(old, cur interface{}) {
				if !reflect.DeepEqual(old, cur) {
					k8sEvents.With(prometheus.Labels{"type": otype, "event": "update"}).Add(1)
					c.queue.Push(kube.NewTask(handler.Apply, cur, model.EventUpdate))
				} else {
					k8sEvents.With(prometheus.Labels{"type": otype, "event": "updateSame"}).Add(1)
				}
			},
			DeleteFunc: func(obj interface{}) {
				k8sEvents.With(prometheus.Labels{"type": otype, "event": "add"}).Add(1)
				c.queue.Push(kube.NewTask(handler.Apply, obj, model.EventDelete))
			},
		})

	return cacheHandler{informer: informer, handler: handler}
}

func (c *controller) RegisterEventHandler(typ string, f func(model.Config, model.Event)) {
	schema, exists := c.ConfigDescriptor().GetByType(typ)
	if !exists {
		return
	}
	c.kinds[typ].handler.Append(func(object interface{}, ev model.Event) error {
		item, ok := object.(IstioObject)
		if ok {
			config, err := ConvertObject(schema, item, c.client.domainSuffix)
			if err != nil {
				log.Warnf("error translating object for schema %#v : %v\n Object:\n%#v", schema, err, object)
			} else {
				f(*config, ev)
			}
		}
		return nil
	})
}

func (c *controller) HasSynced() bool {
	for kind, ctl := range c.kinds {
		if !ctl.informer.HasSynced() {
			log.Infof("controller %q is syncing...", kind)
			return false
		}
	}
	return true
}

func (c *controller) Run(stop <-chan struct{}) {
	go c.queue.Run(stop)

	for _, ctl := range c.kinds {
		go ctl.informer.Run(stop)
	}

	<-stop
	log.Info("controller terminated")
}

func (c *controller) ConfigDescriptor() model.ConfigDescriptor {
	return c.client.ConfigDescriptor()
}

func (c *controller) Get(typ, name, namespace string) (*model.Config, bool) {
	schema, exists := c.client.ConfigDescriptor().GetByType(typ)
	if !exists {
		return nil, false
	}

	store := c.kinds[typ].informer.GetStore()
	data, exists, err := store.GetByKey(kube.KeyFunc(name, namespace))
	if !exists {
		return nil, false
	}
	if err != nil {
		log.Warna(err)
		return nil, false
	}

	obj, ok := data.(IstioObject)
	if !ok {
		log.Warn("Cannot convert to config from store")
		return nil, false
	}

	config, err := ConvertObject(schema, obj, c.client.domainSuffix)
	if err != nil {
		return nil, false
	}

	return config, true
}

func (c *controller) Create(config model.Config) (string, error) {
	return c.client.Create(config)
}

func (c *controller) Update(config model.Config) (string, error) {
	return c.client.Update(config)
}

func (c *controller) Delete(typ, name, namespace string) error {
	return c.client.Delete(typ, name, namespace)
}

func (c *controller) List(typ, namespace string) ([]model.Config, error) {
	schema, ok := c.client.ConfigDescriptor().GetByType(typ)
	if !ok {
		return nil, fmt.Errorf("missing type %q", typ)
	}

	var newErrors sync.Map
	var errs error
	out := make([]model.Config, 0)
	oldMap := InvalidCRDs.Load()
	if oldMap != nil {
		oldMap.(*sync.Map).Range(func(key, value interface{}) bool {
			k8sErrors.With(prometheus.Labels{"name": key.(string)}).Set(1)
			return true
		})
	}
	for _, data := range c.kinds[typ].informer.GetStore().List() {
		item, ok := data.(IstioObject)
		if !ok {
			continue
		}

		if namespace != "" && namespace != item.GetObjectMeta().Namespace {
			continue
		}

		config, err := ConvertObject(schema, item, c.client.domainSuffix)
		if err != nil {
			key := item.GetObjectMeta().Namespace + "/" + item.GetObjectMeta().Name
			log.Errorf("Failed to convert %s object, ignoring: %s %v %v", typ, key, err, item.GetSpec())
			// DO NOT RETURN ERROR: if a single object is bad, it'll be ignored (with a log message), but
			// the rest should still be processed.
			// TODO: find a way to reset and represent the error !!
			newErrors.Store(key, err)
			k8sErrors.With(prometheus.Labels{"name": key}).Set(1)
		} else {
			out = append(out, *config)
		}
	}
	InvalidCRDs.Store(&newErrors)
	return out, errs
}
<commit_msg>fix bug: must register prometheus collector (#8524)<commit_after>// Copyright 2017 Istio 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 crd

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

	"github.com/prometheus/client_golang/prometheus"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/cache"

	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/serviceregistry/kube"
	"istio.io/istio/pkg/log"
)

// controller is a collection of synchronized resource watchers.
// Caches are thread-safe
type controller struct {
	client *Client
	queue  kube.Queue
	kinds  map[string]cacheHandler
}

type cacheHandler struct {
	informer cache.SharedIndexInformer
	handler  *kube.ChainHandler
}

var (
	// experiment on getting some monitoring on config errors.
	k8sEvents = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "pilot_k8s_cfg_events",
		Help: "Events from k8s config.",
	}, []string{"type", "event"})

	k8sErrors = prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Name: "pilot_k8s_object_errors",
		Help: "Errors converting k8s CRDs",
	}, []string{"name"})

	// InvalidCRDs contains a sync.Map keyed by the namespace/name of the entry, and has the error as value.
	// It can be used by tools like ctrlz to display the errors.
	InvalidCRDs atomic.Value
)

func init() {
	prometheus.MustRegister(k8sEvents)
	prometheus.MustRegister(k8sErrors)
}

// NewController creates a new Kubernetes controller for CRDs
// Use "" for namespace to listen for all namespace changes
func NewController(client *Client, options kube.ControllerOptions) model.ConfigStoreCache {
	log.Infof("CRD controller watching namespaces %q", options.WatchedNamespace)

	// Queue requires a time duration for a retry delay after a handler error
	out := &controller{
		client: client,
		queue:  kube.NewQueue(1 * time.Second),
		kinds:  make(map[string]cacheHandler),
	}

	// add stores for CRD kinds
	for _, schema := range client.ConfigDescriptor() {
		out.addInformer(schema, options.WatchedNamespace, options.ResyncPeriod)
	}

	return out
}

func (c *controller) addInformer(schema model.ProtoSchema, namespace string, resyncPeriod time.Duration) {
	c.kinds[schema.Type] = c.createInformer(knownTypes[schema.Type].object.DeepCopyObject(), schema.Type, resyncPeriod,
		func(opts meta_v1.ListOptions) (result runtime.Object, err error) {
			result = knownTypes[schema.Type].collection.DeepCopyObject()
			rc, ok := c.client.clientset[apiVersion(&schema)]
			if !ok {
				return nil, fmt.Errorf("client not initialized %s", schema.Type)
			}
			req := rc.dynamic.Get().
				Resource(ResourceName(schema.Plural)).
				VersionedParams(&opts, meta_v1.ParameterCodec)

			if !schema.ClusterScoped {
				req = req.Namespace(namespace)
			}
			err = req.Do().Into(result)
			return
		},
		func(opts meta_v1.ListOptions) (watch.Interface, error) {
			rc, ok := c.client.clientset[apiVersion(&schema)]
			if !ok {
				return nil, fmt.Errorf("client not initialized %s", schema.Type)
			}
			opts.Watch = true
			req := rc.dynamic.Get().
				Resource(ResourceName(schema.Plural)).
				VersionedParams(&opts, meta_v1.ParameterCodec)
			if !schema.ClusterScoped {
				req = req.Namespace(namespace)
			}
			return req.Watch()
		})
}

// notify is the first handler in the handler chain.
// Returning an error causes repeated execution of the entire chain.
func (c *controller) notify(obj interface{}, event model.Event) error {
	if !c.HasSynced() {
		return errors.New("waiting till full synchronization")
	}
	_, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
	if err != nil {
		log.Infof("Error retrieving key: %v", err)
	}
	return nil
}

func (c *controller) createInformer(
	o runtime.Object,
	otype string,
	resyncPeriod time.Duration,
	lf cache.ListFunc,
	wf cache.WatchFunc) cacheHandler {
	handler := &kube.ChainHandler{}
	handler.Append(c.notify)

	// TODO: finer-grained index (perf)
	informer := cache.NewSharedIndexInformer(
		&cache.ListWatch{ListFunc: lf, WatchFunc: wf}, o,
		resyncPeriod, cache.Indexers{})

	informer.AddEventHandler(
		cache.ResourceEventHandlerFuncs{
			// TODO: filtering functions to skip over un-referenced resources (perf)
			AddFunc: func(obj interface{}) {
				k8sEvents.With(prometheus.Labels{"type": otype, "event": "add"}).Add(1)
				c.queue.Push(kube.NewTask(handler.Apply, obj, model.EventAdd))
			},
			UpdateFunc: func(old, cur interface{}) {
				if !reflect.DeepEqual(old, cur) {
					k8sEvents.With(prometheus.Labels{"type": otype, "event": "update"}).Add(1)
					c.queue.Push(kube.NewTask(handler.Apply, cur, model.EventUpdate))
				} else {
					k8sEvents.With(prometheus.Labels{"type": otype, "event": "updateSame"}).Add(1)
				}
			},
			DeleteFunc: func(obj interface{}) {
				k8sEvents.With(prometheus.Labels{"type": otype, "event": "add"}).Add(1)
				c.queue.Push(kube.NewTask(handler.Apply, obj, model.EventDelete))
			},
		})

	return cacheHandler{informer: informer, handler: handler}
}

func (c *controller) RegisterEventHandler(typ string, f func(model.Config, model.Event)) {
	schema, exists := c.ConfigDescriptor().GetByType(typ)
	if !exists {
		return
	}
	c.kinds[typ].handler.Append(func(object interface{}, ev model.Event) error {
		item, ok := object.(IstioObject)
		if ok {
			config, err := ConvertObject(schema, item, c.client.domainSuffix)
			if err != nil {
				log.Warnf("error translating object for schema %#v : %v\n Object:\n%#v", schema, err, object)
			} else {
				f(*config, ev)
			}
		}
		return nil
	})
}

func (c *controller) HasSynced() bool {
	for kind, ctl := range c.kinds {
		if !ctl.informer.HasSynced() {
			log.Infof("controller %q is syncing...", kind)
			return false
		}
	}
	return true
}

func (c *controller) Run(stop <-chan struct{}) {
	go c.queue.Run(stop)

	for _, ctl := range c.kinds {
		go ctl.informer.Run(stop)
	}

	<-stop
	log.Info("controller terminated")
}

func (c *controller) ConfigDescriptor() model.ConfigDescriptor {
	return c.client.ConfigDescriptor()
}

func (c *controller) Get(typ, name, namespace string) (*model.Config, bool) {
	schema, exists := c.client.ConfigDescriptor().GetByType(typ)
	if !exists {
		return nil, false
	}

	store := c.kinds[typ].informer.GetStore()
	data, exists, err := store.GetByKey(kube.KeyFunc(name, namespace))
	if !exists {
		return nil, false
	}
	if err != nil {
		log.Warna(err)
		return nil, false
	}

	obj, ok := data.(IstioObject)
	if !ok {
		log.Warn("Cannot convert to config from store")
		return nil, false
	}

	config, err := ConvertObject(schema, obj, c.client.domainSuffix)
	if err != nil {
		return nil, false
	}

	return config, true
}

func (c *controller) Create(config model.Config) (string, error) {
	return c.client.Create(config)
}

func (c *controller) Update(config model.Config) (string, error) {
	return c.client.Update(config)
}

func (c *controller) Delete(typ, name, namespace string) error {
	return c.client.Delete(typ, name, namespace)
}

func (c *controller) List(typ, namespace string) ([]model.Config, error) {
	schema, ok := c.client.ConfigDescriptor().GetByType(typ)
	if !ok {
		return nil, fmt.Errorf("missing type %q", typ)
	}

	var newErrors sync.Map
	var errs error
	out := make([]model.Config, 0)
	oldMap := InvalidCRDs.Load()
	if oldMap != nil {
		oldMap.(*sync.Map).Range(func(key, value interface{}) bool {
			k8sErrors.With(prometheus.Labels{"name": key.(string)}).Set(1)
			return true
		})
	}
	for _, data := range c.kinds[typ].informer.GetStore().List() {
		item, ok := data.(IstioObject)
		if !ok {
			continue
		}

		if namespace != "" && namespace != item.GetObjectMeta().Namespace {
			continue
		}

		config, err := ConvertObject(schema, item, c.client.domainSuffix)
		if err != nil {
			key := item.GetObjectMeta().Namespace + "/" + item.GetObjectMeta().Name
			log.Errorf("Failed to convert %s object, ignoring: %s %v %v", typ, key, err, item.GetSpec())
			// DO NOT RETURN ERROR: if a single object is bad, it'll be ignored (with a log message), but
			// the rest should still be processed.
			// TODO: find a way to reset and represent the error !!
			newErrors.Store(key, err)
			k8sErrors.With(prometheus.Labels{"name": key}).Set(1)
		} else {
			out = append(out, *config)
		}
	}
	InvalidCRDs.Store(&newErrors)
	return out, errs
}
<|endoftext|>


<commit_before>package helm

import (
	"context"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/rancher/norman/types/slice"
	"github.com/rancher/rancher/pkg/auth/providers/local"
	hutils "github.com/rancher/rancher/pkg/controllers/user/helm/utils"
	"github.com/rancher/rancher/pkg/randomtoken"
	mgmtv3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/apis/project.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	"gopkg.in/yaml.v2"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
)

const (
	helmTokenPrefix = "helm-token-"
)

func Register(user *config.UserContext) {
	appClient := user.Management.Project.Apps("")
	stackLifecycle := &Lifecycle{
		TokenClient:           user.Management.Management.Tokens(""),
		UserClient:            user.Management.Management.Users(""),
		K8sClient:             user.K8sClient,
		TemplateVersionClient: user.Management.Management.TemplateVersions(""),
		ListenConfigClient:    user.Management.Management.ListenConfigs(""),
		ClusterName:           user.ClusterName,
	}
	appClient.AddClusterScopedLifecycle("helm-controller", user.ClusterName, stackLifecycle)
}

type Lifecycle struct {
	TokenClient           mgmtv3.TokenInterface
	UserClient            mgmtv3.UserInterface
	TemplateVersionClient mgmtv3.TemplateVersionInterface
	K8sClient             kubernetes.Interface
	ListenConfigClient    mgmtv3.ListenConfigInterface
	ClusterName           string
}

func (l *Lifecycle) Create(obj *v3.App) (*v3.App, error) {
	if obj.Spec.ExternalID == "" {
		return obj, nil
	}
	newObj, err := v3.AppConditionInstalled.DoUntilTrue(obj, func() (runtime.Object, error) {
		templateVersionID, err := hutils.ParseExternalID(obj.Spec.ExternalID)
		if err != nil {
			return obj, err
		}
		if err := l.Run(obj, "install", templateVersionID); err != nil {
			return obj, err
		}
		configMaps, err := l.K8sClient.CoreV1().ConfigMaps(obj.Spec.InstallNamespace).List(metav1.ListOptions{
			LabelSelector: fmt.Sprintf("%s=%s", "NAME", obj.Name),
		})
		if err != nil {
			return obj, err
		}
		releases := []v3.ReleaseInfo{}
		for _, cm := range configMaps.Items {
			releaseInfo := v3.ReleaseInfo{}
			releaseInfo.Name = cm.Name
			releaseInfo.Version = cm.Labels["VERSION"]
			releaseInfo.CreateTimestamp = cm.CreationTimestamp.Format(time.RFC3339)
			releaseInfo.ModifiedAt = cm.Labels["MODIFIED_AT"]
			releaseInfo.TemplateVersionID = templateVersionID
			releases = append(releases, releaseInfo)
		}
		obj.Status.Releases = releases
		return obj, nil
	})
	return newObj.(*v3.App), err
}

func (l *Lifecycle) Updated(obj *v3.App) (*v3.App, error) {
	if obj.Spec.ExternalID == "" {
		return obj, nil
	}
	templateVersionID, err := hutils.ParseExternalID(obj.Spec.ExternalID)
	if err != nil {
		return obj, err
	}
	templateVersion, err := l.TemplateVersionClient.Get(templateVersionID, metav1.GetOptions{})
	if err != nil {
		return obj, err
	}
	if err := l.saveTemplates(obj, templateVersion); err != nil {
		return obj, err
	}
	configMaps, err := l.K8sClient.CoreV1().ConfigMaps(obj.Spec.InstallNamespace).List(metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", "NAME", obj.Name),
	})
	if err != nil {
		return obj, err
	}
	releases := obj.Status.Releases
	alreadyExistedReleaseNames := []string{}
	for _, k := range releases {
		alreadyExistedReleaseNames = append(alreadyExistedReleaseNames, k.Name)
	}
	for _, cm := range configMaps.Items {
		if !slice.ContainsString(alreadyExistedReleaseNames, cm.Name) {
			logrus.Infof("uploading release %s into namespace %s", cm.Name, obj.Name)
			releaseInfo := v3.ReleaseInfo{}
			releaseInfo.Name = cm.Name
			releaseInfo.Version = cm.Labels["VERSION"]
			releaseInfo.CreateTimestamp = cm.CreationTimestamp.Format(time.RFC3339)
			releaseInfo.ModifiedAt = cm.Labels["MODIFIED_AT"]
			releaseInfo.TemplateVersionID = templateVersionID
			releases = append(releases, releaseInfo)
		}
	}
	obj.Status.Releases = releases
	return obj, nil
}

func (l *Lifecycle) Remove(obj *v3.App) (*v3.App, error) {
	if obj.Spec.ExternalID == "" {
		return obj, nil
	}
	templateVersionID, err := hutils.ParseExternalID(obj.Spec.ExternalID)
	if err != nil {
		return obj, err
	}
	if err := l.Run(obj, "delete", templateVersionID); err != nil {
		return obj, err
	}
	return obj, nil
}

func (l *Lifecycle) Run(obj *v3.App, action, templateVersionID string) error {
	templateVersion, err := l.TemplateVersionClient.Get(templateVersionID, metav1.GetOptions{})
	if err != nil {
		return err
	}
	if err := l.saveTemplates(obj, templateVersion); err != nil {
		return err
	}
	files := map[string]string{}
	for _, file := range templateVersion.Spec.Files {
		content, err := base64.StdEncoding.DecodeString(file.Contents)
		if err != nil {
			return err
		}
		files[file.Name] = string(content)
	}
	tempDir, err := ioutil.TempDir("", "helm-")
	if err != nil {
		return err
	}
	dir, err := hutils.WriteTempDir(tempDir, files)
	defer os.RemoveAll(dir)
	if err != nil {
		return err
	}

	cont, cancel := context.WithCancel(context.Background())
	defer cancel()
	addr := hutils.GenerateRandomPort()
	probeAddr := hutils.GenerateRandomPort()
	userID := obj.Annotations["field.cattle.io/creatorId"]
	user, err := l.UserClient.Get(userID, metav1.GetOptions{})
	if err != nil {
		return err
	}
	token := ""
	if t, err := l.TokenClient.Get(helmTokenPrefix+user.Name, metav1.GetOptions{}); err != nil && !errors.IsNotFound(err) {
		return err
	} else if errors.IsNotFound(err) {
		token, err = l.generateToken(user)
		if err != nil {
			return err
		}
	} else {
		token = t.Name + ":" + t.Token
	}

	data, err := yaml.Marshal(hutils.RestToRaw(token, l.ClusterName))
	if err != nil {
		return err
	}
	if err := os.MkdirAll(filepath.Join(tempDir, obj.Namespace), 0755); err != nil {
		return err
	}
	kubeConfigPath := filepath.Join(tempDir, obj.Namespace, ".kubeconfig")
	if err := ioutil.WriteFile(kubeConfigPath, data, 0755); err != nil {
		return err
	}
	defer os.RemoveAll(kubeConfigPath)
	go hutils.StartTiller(cont, addr, probeAddr, obj.Spec.InstallNamespace, kubeConfigPath)
	switch action {
	case "install":
		if err := hutils.InstallCharts(dir, addr, obj); err != nil {
			return err
		}
	case "delete":
		if err := hutils.DeleteCharts(addr, obj); err != nil {
			return err
		}
	}
	return nil
}

const userIDLabel = "authn.management.cattle.io/token-userId"

// this might be pretty hacky, need to revisit
func (l *Lifecycle) generateToken(user *mgmtv3.User) (string, error) {
	token := mgmtv3.Token{
		TTLMillis:    0,
		Description:  "token for helm chart deployment",
		UserID:       user.Name,
		AuthProvider: local.Name,
		IsDerived:    false,
	}
	key, err := randomtoken.Generate()
	if err != nil {
		return "", fmt.Errorf("failed to generate token key")
	}

	labels := make(map[string]string)
	labels[userIDLabel] = token.UserID

	token.APIVersion = "management.cattle.io/v3"
	token.Kind = "Token"
	token.Token = key
	token.ObjectMeta = metav1.ObjectMeta{
		Name:   helmTokenPrefix + user.Name,
		Labels: labels,
	}
	createdToken, err := l.TokenClient.Create(&token)

	if err != nil {
		return "", err
	}
	return createdToken.Name + ":" + createdToken.Token, nil
}

func (l *Lifecycle) saveTemplates(obj *v3.App, templateVersion *mgmtv3.TemplateVersion) error {
	templates := map[string]string{}
	for _, file := range templateVersion.Spec.Files {
		templates[file.Name] = file.Contents
	}
	obj.Spec.Templates = templates
	return nil
}
<commit_msg>fix helm token<commit_after>package helm

import (
	"context"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/rancher/norman/types/slice"
	"github.com/rancher/rancher/pkg/auth/providers/local"
	hutils "github.com/rancher/rancher/pkg/controllers/user/helm/utils"
	"github.com/rancher/rancher/pkg/randomtoken"
	mgmtv3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/apis/project.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	"gopkg.in/yaml.v2"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
)

const (
	helmTokenPrefix = "helm-token-"
)

func Register(user *config.UserContext) {
	appClient := user.Management.Project.Apps("")
	stackLifecycle := &Lifecycle{
		TokenClient:           user.Management.Management.Tokens(""),
		UserClient:            user.Management.Management.Users(""),
		K8sClient:             user.K8sClient,
		TemplateVersionClient: user.Management.Management.TemplateVersions(""),
		ListenConfigClient:    user.Management.Management.ListenConfigs(""),
		ClusterName:           user.ClusterName,
	}
	appClient.AddClusterScopedLifecycle("helm-controller", user.ClusterName, stackLifecycle)
}

type Lifecycle struct {
	TokenClient           mgmtv3.TokenInterface
	UserClient            mgmtv3.UserInterface
	TemplateVersionClient mgmtv3.TemplateVersionInterface
	K8sClient             kubernetes.Interface
	ListenConfigClient    mgmtv3.ListenConfigInterface
	ClusterName           string
}

func (l *Lifecycle) Create(obj *v3.App) (*v3.App, error) {
	if obj.Spec.ExternalID == "" {
		return obj, nil
	}
	newObj, err := v3.AppConditionInstalled.DoUntilTrue(obj, func() (runtime.Object, error) {
		templateVersionID, err := hutils.ParseExternalID(obj.Spec.ExternalID)
		if err != nil {
			return obj, err
		}
		if err := l.Run(obj, "install", templateVersionID); err != nil {
			return obj, err
		}
		configMaps, err := l.K8sClient.CoreV1().ConfigMaps(obj.Spec.InstallNamespace).List(metav1.ListOptions{
			LabelSelector: fmt.Sprintf("%s=%s", "NAME", obj.Name),
		})
		if err != nil {
			return obj, err
		}
		releases := []v3.ReleaseInfo{}
		for _, cm := range configMaps.Items {
			releaseInfo := v3.ReleaseInfo{}
			releaseInfo.Name = cm.Name
			releaseInfo.Version = cm.Labels["VERSION"]
			releaseInfo.CreateTimestamp = cm.CreationTimestamp.Format(time.RFC3339)
			releaseInfo.ModifiedAt = cm.Labels["MODIFIED_AT"]
			releaseInfo.TemplateVersionID = templateVersionID
			releases = append(releases, releaseInfo)
		}
		obj.Status.Releases = releases
		return obj, nil
	})
	return newObj.(*v3.App), err
}

func (l *Lifecycle) Updated(obj *v3.App) (*v3.App, error) {
	if obj.Spec.ExternalID == "" {
		return obj, nil
	}
	templateVersionID, err := hutils.ParseExternalID(obj.Spec.ExternalID)
	if err != nil {
		return obj, err
	}
	templateVersion, err := l.TemplateVersionClient.Get(templateVersionID, metav1.GetOptions{})
	if err != nil {
		return obj, err
	}
	if err := l.saveTemplates(obj, templateVersion); err != nil {
		return obj, err
	}
	configMaps, err := l.K8sClient.CoreV1().ConfigMaps(obj.Spec.InstallNamespace).List(metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", "NAME", obj.Name),
	})
	if err != nil {
		return obj, err
	}
	releases := obj.Status.Releases
	alreadyExistedReleaseNames := []string{}
	for _, k := range releases {
		alreadyExistedReleaseNames = append(alreadyExistedReleaseNames, k.Name)
	}
	for _, cm := range configMaps.Items {
		if !slice.ContainsString(alreadyExistedReleaseNames, cm.Name) {
			logrus.Infof("uploading release %s into namespace %s", cm.Name, obj.Name)
			releaseInfo := v3.ReleaseInfo{}
			releaseInfo.Name = cm.Name
			releaseInfo.Version = cm.Labels["VERSION"]
			releaseInfo.CreateTimestamp = cm.CreationTimestamp.Format(time.RFC3339)
			releaseInfo.ModifiedAt = cm.Labels["MODIFIED_AT"]
			releaseInfo.TemplateVersionID = templateVersionID
			releases = append(releases, releaseInfo)
		}
	}
	obj.Status.Releases = releases
	return obj, nil
}

func (l *Lifecycle) Remove(obj *v3.App) (*v3.App, error) {
	if obj.Spec.ExternalID == "" {
		return obj, nil
	}
	templateVersionID, err := hutils.ParseExternalID(obj.Spec.ExternalID)
	if err != nil {
		return obj, err
	}
	if err := l.Run(obj, "delete", templateVersionID); err != nil {
		return obj, err
	}
	return obj, nil
}

func (l *Lifecycle) Run(obj *v3.App, action, templateVersionID string) error {
	templateVersion, err := l.TemplateVersionClient.Get(templateVersionID, metav1.GetOptions{})
	if err != nil {
		return err
	}
	if err := l.saveTemplates(obj, templateVersion); err != nil {
		return err
	}
	files := map[string]string{}
	for _, file := range templateVersion.Spec.Files {
		content, err := base64.StdEncoding.DecodeString(file.Contents)
		if err != nil {
			return err
		}
		files[file.Name] = string(content)
	}
	tempDir, err := ioutil.TempDir("", "helm-")
	if err != nil {
		return err
	}
	dir, err := hutils.WriteTempDir(tempDir, files)
	defer os.RemoveAll(dir)
	if err != nil {
		return err
	}

	cont, cancel := context.WithCancel(context.Background())
	defer cancel()
	addr := hutils.GenerateRandomPort()
	probeAddr := hutils.GenerateRandomPort()
	userID := obj.Annotations["field.cattle.io/creatorId"]
	user, err := l.UserClient.Get(userID, metav1.GetOptions{})
	if err != nil {
		return err
	}
	token := ""
	if t, err := l.TokenClient.Get(helmTokenPrefix+user.Name, metav1.GetOptions{}); err != nil && !errors.IsNotFound(err) {
		return err
	} else if errors.IsNotFound(err) {
		token, err = l.generateToken(user)
		if err != nil {
			return err
		}
	} else {
		token = t.Name + ":" + t.Token
	}

	data, err := yaml.Marshal(hutils.RestToRaw(token, l.ClusterName))
	if err != nil {
		return err
	}
	if err := os.MkdirAll(filepath.Join(tempDir, obj.Namespace), 0755); err != nil {
		return err
	}
	kubeConfigPath := filepath.Join(tempDir, obj.Namespace, ".kubeconfig")
	if err := ioutil.WriteFile(kubeConfigPath, data, 0755); err != nil {
		return err
	}
	defer os.RemoveAll(kubeConfigPath)
	go hutils.StartTiller(cont, addr, probeAddr, obj.Spec.InstallNamespace, kubeConfigPath)
	switch action {
	case "install":
		if err := hutils.InstallCharts(dir, addr, obj); err != nil {
			return err
		}
	case "delete":
		if err := hutils.DeleteCharts(addr, obj); err != nil {
			return err
		}
	}
	return nil
}

const userIDLabel = "authn.management.cattle.io/token-userId"

// this might be pretty hacky, need to revisit
func (l *Lifecycle) generateToken(user *mgmtv3.User) (string, error) {
	token := mgmtv3.Token{
		TTLMillis:    0,
		Description:  "token for helm chart deployment",
		UserID:       user.Name,
		AuthProvider: local.Name,
		IsDerived:    true,
	}
	key, err := randomtoken.Generate()
	if err != nil {
		return "", fmt.Errorf("failed to generate token key")
	}

	labels := make(map[string]string)
	labels[userIDLabel] = token.UserID

	token.Token = key
	token.ObjectMeta = metav1.ObjectMeta{
		Name:   helmTokenPrefix + user.Name,
		Labels: labels,
	}
	createdToken, err := l.TokenClient.Create(&token)

	if err != nil {
		return "", err
	}
	return createdToken.Name + ":" + createdToken.Token, nil
}

func (l *Lifecycle) saveTemplates(obj *v3.App, templateVersion *mgmtv3.TemplateVersion) error {
	templates := map[string]string{}
	for _, file := range templateVersion.Spec.Files {
		templates[file.Name] = file.Contents
	}
	obj.Spec.Templates = templates
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package syncers

import (
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/ingress-gce/pkg/neg/types"
	"k8s.io/ingress-gce/pkg/utils"
	"k8s.io/klog"
)

// LocalL4ILBEndpointGetter implements the NetworkEndpointsCalculator interface.
// It exposes methods to calculate Network endpoints for VM_PRIMARY_IP NEGs when the service
// uses "ExternalTrafficPolicy: Local" mode.
// In this mode, the endpoints of the NEG are calculated by listing the nodes that host the service endpoints(pods)
// for the given service. These candidate nodes picked as is, if the count is less than the subset size limit(250).
// Otherwise, a subset of nodes is selected.
// In a cluster with nodes node1... node 50. If nodes node10 to node 45 run the pods for a given ILB service, all these
// nodes - node10, node 11 ... node45 will be part of the subset.
type LocalL4ILBEndpointsCalculator struct {
	nodeLister      listers.NodeLister
	zoneGetter      types.ZoneGetter
	subsetSizeLimit int
	svcId           string
}

func NewLocalL4ILBEndpointsCalculator(nodeLister listers.NodeLister, zoneGetter types.ZoneGetter, svcId string) *LocalL4ILBEndpointsCalculator {
	return &LocalL4ILBEndpointsCalculator{nodeLister: nodeLister, zoneGetter: zoneGetter, subsetSizeLimit: maxSubsetSizeLocal, svcId: svcId}
}

// Mode indicates the mode that the EndpointsCalculator is operating in.
func (l *LocalL4ILBEndpointsCalculator) Mode() types.EndpointsCalculatorMode {
	return types.L4LocalMode
}

// CalculateEndpoints determines the endpoints in the NEGs based on the current service endpoints and the current NEGs.
func (l *LocalL4ILBEndpointsCalculator) CalculateEndpoints(ep *v1.Endpoints, currentMap map[string]types.NetworkEndpointSet) (map[string]types.NetworkEndpointSet, types.EndpointPodMap, error) {
	// List all nodes where the service endpoints are running. Get a subset of the desired count.
	zoneNodeMap := make(map[string][]*v1.Node)
	nodeNames := sets.String{}
	numEndpoints := 0
	for _, curEp := range ep.Subsets {
		for _, addr := range curEp.Addresses {
			if addr.NodeName == nil {
				klog.V(2).Infof("Endpoint %q in Endpoints %s/%s does not have an associated node. Skipping", addr.IP, ep.Namespace, ep.Name)
				continue
			}
			if addr.TargetRef == nil {
				klog.V(2).Infof("Endpoint %q in Endpoints %s/%s does not have an associated pod. Skipping", addr.IP, ep.Namespace, ep.Name)
				continue
			}
			numEndpoints++
			if nodeNames.Has(*addr.NodeName) {
				continue
			}
			nodeNames.Insert(*addr.NodeName)
			node, err := l.nodeLister.Get(*addr.NodeName)
			if err != nil {
				klog.Errorf("failed to retrieve node object for %q: %v", *addr.NodeName, err)
				continue
			}
			zone, err := l.zoneGetter.GetZoneForNode(node.Name)
			if err != nil {
				klog.Errorf("Unable to find zone for node %s, err %v, skipping", node.Name, err)
				continue
			}
			zoneNodeMap[zone] = append(zoneNodeMap[zone], node)
		}
	}
	if numEndpoints == 0 {
		// TODO verify the behavior seen by a client when accessing an ILB whose NEGs have no endpoints.
		return nil, nil, nil
	}
	// Compute the networkEndpoints, with total endpoints count <= l.subsetSizeLimit
	subsetMap, err := getSubsetPerZone(zoneNodeMap, l.subsetSizeLimit, l.svcId, currentMap)
	return subsetMap, nil, err
}

// ClusterL4ILBEndpointGetter implements the NetworkEndpointsCalculator interface.
// It exposes methods to calculate Network endpoints for VM_PRIMARY_IP NEGs when the service
// uses "ExternalTrafficPolicy: Cluster" mode This is the default mode.
// In this mode, the endpoints of the NEG are calculated by selecting nodes at random. Upto 25(subset size limit in this
// mode) are selected.
type ClusterL4ILBEndpointsCalculator struct {
	// nodeLister is used for listing all the nodes in the cluster when calculating the subset.
	nodeLister listers.NodeLister
	// zoneGetter looks up the zone for a given node when calculating subsets.
	zoneGetter types.ZoneGetter
	// subsetSizeLimit is the max value of the subset size in this mode.
	subsetSizeLimit int
	// svcId is the unique identifier for the service, that is used as a salt when hashing nodenames.
	svcId string
}

func NewClusterL4ILBEndpointsCalculator(nodeLister listers.NodeLister, zoneGetter types.ZoneGetter, svcId string) *ClusterL4ILBEndpointsCalculator {
	return &ClusterL4ILBEndpointsCalculator{nodeLister: nodeLister, zoneGetter: zoneGetter,
		subsetSizeLimit: maxSubsetSizeDefault, svcId: svcId}
}

// Mode indicates the mode that the EndpointsCalculator is operating in.
func (l *ClusterL4ILBEndpointsCalculator) Mode() types.EndpointsCalculatorMode {
	return types.L4ClusterMode
}

// CalculateEndpoints determines the endpoints in the NEGs based on the current service endpoints and the current NEGs.
func (l *ClusterL4ILBEndpointsCalculator) CalculateEndpoints(ep *v1.Endpoints, currentMap map[string]types.NetworkEndpointSet) (map[string]types.NetworkEndpointSet, types.EndpointPodMap, error) {
	// In this mode, any of the cluster nodes can be part of the subset, whether or not a matching pod runs on it.
	nodes, _ := l.nodeLister.ListWithPredicate(utils.GetNodeConditionPredicate())

	nodeZoneMap := make(map[string][]*v1.Node)
	for _, node := range nodes {
		zone, err := l.zoneGetter.GetZoneForNode(node.Name)
		if err != nil {
			klog.Errorf("Unable to find zone for node %s, err %v, skipping", node.Name, err)
			continue
		}
		nodeZoneMap[zone] = append(nodeZoneMap[zone], node)
	}
	// Compute the networkEndpoints, with total endpoints <= l.subsetSizeLimit.
	subsetMap, err := getSubsetPerZone(nodeZoneMap, l.subsetSizeLimit, l.svcId, currentMap)
	return subsetMap, nil, err
}

// L7EndpointsCalculator implements methods to calculate Network endpoints for VM_IP_PORT NEGs
type L7EndpointsCalculator struct {
	zoneGetter          types.ZoneGetter
	servicePortName     string
	podLister           cache.Indexer
	subsetLabels        string
	networkEndpointType types.NetworkEndpointType
}

func NewL7EndpointsCalculator(zoneGetter types.ZoneGetter, podLister cache.Indexer, svcPortName, subsetLabels string, endpointType types.NetworkEndpointType) *L7EndpointsCalculator {
	return &L7EndpointsCalculator{
		zoneGetter:          zoneGetter,
		servicePortName:     svcPortName,
		podLister:           podLister,
		subsetLabels:        subsetLabels,
		networkEndpointType: endpointType,
	}
}

// Mode indicates the mode that the EndpointsCalculator is operating in.
func (l *L7EndpointsCalculator) Mode() types.EndpointsCalculatorMode {
	return types.L7Mode
}

// CalculateEndpoints determines the endpoints in the NEGs based on the current service endpoints and the current NEGs.
func (l *L7EndpointsCalculator) CalculateEndpoints(ep *v1.Endpoints, currentMap map[string]types.NetworkEndpointSet) (map[string]types.NetworkEndpointSet, types.EndpointPodMap, error) {
	return toZoneNetworkEndpointMap(ep, l.zoneGetter, l.servicePortName, l.podLister, l.subsetLabels, "")
}
<commit_msg>fix a bug where neg type is not passed<commit_after>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package syncers

import (
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/ingress-gce/pkg/neg/types"
	"k8s.io/ingress-gce/pkg/utils"
	"k8s.io/klog"
)

// LocalL4ILBEndpointGetter implements the NetworkEndpointsCalculator interface.
// It exposes methods to calculate Network endpoints for VM_PRIMARY_IP NEGs when the service
// uses "ExternalTrafficPolicy: Local" mode.
// In this mode, the endpoints of the NEG are calculated by listing the nodes that host the service endpoints(pods)
// for the given service. These candidate nodes picked as is, if the count is less than the subset size limit(250).
// Otherwise, a subset of nodes is selected.
// In a cluster with nodes node1... node 50. If nodes node10 to node 45 run the pods for a given ILB service, all these
// nodes - node10, node 11 ... node45 will be part of the subset.
type LocalL4ILBEndpointsCalculator struct {
	nodeLister      listers.NodeLister
	zoneGetter      types.ZoneGetter
	subsetSizeLimit int
	svcId           string
}

func NewLocalL4ILBEndpointsCalculator(nodeLister listers.NodeLister, zoneGetter types.ZoneGetter, svcId string) *LocalL4ILBEndpointsCalculator {
	return &LocalL4ILBEndpointsCalculator{nodeLister: nodeLister, zoneGetter: zoneGetter, subsetSizeLimit: maxSubsetSizeLocal, svcId: svcId}
}

// Mode indicates the mode that the EndpointsCalculator is operating in.
func (l *LocalL4ILBEndpointsCalculator) Mode() types.EndpointsCalculatorMode {
	return types.L4LocalMode
}

// CalculateEndpoints determines the endpoints in the NEGs based on the current service endpoints and the current NEGs.
func (l *LocalL4ILBEndpointsCalculator) CalculateEndpoints(ep *v1.Endpoints, currentMap map[string]types.NetworkEndpointSet) (map[string]types.NetworkEndpointSet, types.EndpointPodMap, error) {
	// List all nodes where the service endpoints are running. Get a subset of the desired count.
	zoneNodeMap := make(map[string][]*v1.Node)
	nodeNames := sets.String{}
	numEndpoints := 0
	for _, curEp := range ep.Subsets {
		for _, addr := range curEp.Addresses {
			if addr.NodeName == nil {
				klog.V(2).Infof("Endpoint %q in Endpoints %s/%s does not have an associated node. Skipping", addr.IP, ep.Namespace, ep.Name)
				continue
			}
			if addr.TargetRef == nil {
				klog.V(2).Infof("Endpoint %q in Endpoints %s/%s does not have an associated pod. Skipping", addr.IP, ep.Namespace, ep.Name)
				continue
			}
			numEndpoints++
			if nodeNames.Has(*addr.NodeName) {
				continue
			}
			nodeNames.Insert(*addr.NodeName)
			node, err := l.nodeLister.Get(*addr.NodeName)
			if err != nil {
				klog.Errorf("failed to retrieve node object for %q: %v", *addr.NodeName, err)
				continue
			}
			zone, err := l.zoneGetter.GetZoneForNode(node.Name)
			if err != nil {
				klog.Errorf("Unable to find zone for node %s, err %v, skipping", node.Name, err)
				continue
			}
			zoneNodeMap[zone] = append(zoneNodeMap[zone], node)
		}
	}
	if numEndpoints == 0 {
		// TODO verify the behavior seen by a client when accessing an ILB whose NEGs have no endpoints.
		return nil, nil, nil
	}
	// Compute the networkEndpoints, with total endpoints count <= l.subsetSizeLimit
	subsetMap, err := getSubsetPerZone(zoneNodeMap, l.subsetSizeLimit, l.svcId, currentMap)
	return subsetMap, nil, err
}

// ClusterL4ILBEndpointGetter implements the NetworkEndpointsCalculator interface.
// It exposes methods to calculate Network endpoints for VM_PRIMARY_IP NEGs when the service
// uses "ExternalTrafficPolicy: Cluster" mode This is the default mode.
// In this mode, the endpoints of the NEG are calculated by selecting nodes at random. Upto 25(subset size limit in this
// mode) are selected.
type ClusterL4ILBEndpointsCalculator struct {
	// nodeLister is used for listing all the nodes in the cluster when calculating the subset.
	nodeLister listers.NodeLister
	// zoneGetter looks up the zone for a given node when calculating subsets.
	zoneGetter types.ZoneGetter
	// subsetSizeLimit is the max value of the subset size in this mode.
	subsetSizeLimit int
	// svcId is the unique identifier for the service, that is used as a salt when hashing nodenames.
	svcId string
}

func NewClusterL4ILBEndpointsCalculator(nodeLister listers.NodeLister, zoneGetter types.ZoneGetter, svcId string) *ClusterL4ILBEndpointsCalculator {
	return &ClusterL4ILBEndpointsCalculator{nodeLister: nodeLister, zoneGetter: zoneGetter,
		subsetSizeLimit: maxSubsetSizeDefault, svcId: svcId}
}

// Mode indicates the mode that the EndpointsCalculator is operating in.
func (l *ClusterL4ILBEndpointsCalculator) Mode() types.EndpointsCalculatorMode {
	return types.L4ClusterMode
}

// CalculateEndpoints determines the endpoints in the NEGs based on the current service endpoints and the current NEGs.
func (l *ClusterL4ILBEndpointsCalculator) CalculateEndpoints(ep *v1.Endpoints, currentMap map[string]types.NetworkEndpointSet) (map[string]types.NetworkEndpointSet, types.EndpointPodMap, error) {
	// In this mode, any of the cluster nodes can be part of the subset, whether or not a matching pod runs on it.
	nodes, _ := l.nodeLister.ListWithPredicate(utils.GetNodeConditionPredicate())

	nodeZoneMap := make(map[string][]*v1.Node)
	for _, node := range nodes {
		zone, err := l.zoneGetter.GetZoneForNode(node.Name)
		if err != nil {
			klog.Errorf("Unable to find zone for node %s, err %v, skipping", node.Name, err)
			continue
		}
		nodeZoneMap[zone] = append(nodeZoneMap[zone], node)
	}
	// Compute the networkEndpoints, with total endpoints <= l.subsetSizeLimit.
	subsetMap, err := getSubsetPerZone(nodeZoneMap, l.subsetSizeLimit, l.svcId, currentMap)
	return subsetMap, nil, err
}

// L7EndpointsCalculator implements methods to calculate Network endpoints for VM_IP_PORT NEGs
type L7EndpointsCalculator struct {
	zoneGetter          types.ZoneGetter
	servicePortName     string
	podLister           cache.Indexer
	subsetLabels        string
	networkEndpointType types.NetworkEndpointType
}

func NewL7EndpointsCalculator(zoneGetter types.ZoneGetter, podLister cache.Indexer, svcPortName, subsetLabels string, endpointType types.NetworkEndpointType) *L7EndpointsCalculator {
	return &L7EndpointsCalculator{
		zoneGetter:          zoneGetter,
		servicePortName:     svcPortName,
		podLister:           podLister,
		subsetLabels:        subsetLabels,
		networkEndpointType: endpointType,
	}
}

// Mode indicates the mode that the EndpointsCalculator is operating in.
func (l *L7EndpointsCalculator) Mode() types.EndpointsCalculatorMode {
	return types.L7Mode
}

// CalculateEndpoints determines the endpoints in the NEGs based on the current service endpoints and the current NEGs.
func (l *L7EndpointsCalculator) CalculateEndpoints(ep *v1.Endpoints, currentMap map[string]types.NetworkEndpointSet) (map[string]types.NetworkEndpointSet, types.EndpointPodMap, error) {
	return toZoneNetworkEndpointMap(ep, l.zoneGetter, l.servicePortName, l.podLister, l.subsetLabels, l.networkEndpointType)
}
<|endoftext|>