<commit_before>package nsq

import (
	"log"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

// ProducerConfig carries the different variables to tune a newly started
// producer.
type ProducerConfig struct {
	Address         string
	Topic           string
	MaxConcurrency  int
	DialTimeout     time.Duration
	ReadTimeout     time.Duration
	WriteTimeout    time.Duration
	MaxRetryTimeout time.Duration
	MinRetryTimeout time.Duration
}

// Producer provide an abstraction around using direct connections to nsqd
// nodes to send messages.
type Producer struct {
	// Communication channels of the producer.
	reqs chan ProducerRequest
	done chan struct{}
	once sync.Once
	join sync.WaitGroup
	ok   uint32

	// Immutable state of the producer.
	address         string
	topic           string
	dialTimeout     time.Duration
	readTimeout     time.Duration
	writeTimeout    time.Duration
	maxRetryTimeout time.Duration
	minRetryTimeout time.Duration
}

// ProducerRequest are used to represent operations that are submitted to
// producers.
type ProducerRequest struct {
	Topic    string
	Message  []byte
	Response chan<- error
	Deadline time.Time
}

// StartProducer starts and returns a new producer p, configured with the
// variables from the config parameter, or returning an non-nil error if
// some of the configuration variables were invalid.
func StartProducer(config ProducerConfig) (p *Producer, err error) {
	if len(config.Address) == 0 {
		config.Address = "localhost:4151"
	}

	if config.MaxConcurrency == 0 {
		config.MaxConcurrency = DefaultMaxConcurrency
	}

	if config.DialTimeout == 0 {
		config.DialTimeout = DefaultDialTimeout
	}

	if config.ReadTimeout == 0 {
		config.ReadTimeout = DefaultReadTimeout
	}

	if config.WriteTimeout == 0 {
		config.WriteTimeout = DefaultWriteTimeout
	}

	if config.MaxRetryTimeout == 0 {
		config.MaxRetryTimeout = DefaultMaxRetryTimeout
	}

	if config.MinRetryTimeout == 0 {
		config.MinRetryTimeout = DefaultMinRetryTimeout
	}

	p = &Producer{
		reqs:            make(chan ProducerRequest, config.MaxConcurrency),
		done:            make(chan struct{}),
		address:         config.Address,
		topic:           config.Topic,
		dialTimeout:     config.DialTimeout,
		readTimeout:     config.ReadTimeout,
		writeTimeout:    config.WriteTimeout,
		maxRetryTimeout: config.MaxRetryTimeout,
		minRetryTimeout: config.MinRetryTimeout,
	}
	p.join.Add(config.MaxConcurrency)

	for i := 0; i != config.MaxConcurrency; i++ {
		go p.run()
	}

	return
}

// Stop gracefully shutsdown the producer, cancelling all inflight requests and
// waiting for all backend connections to be closed.
//
// It is safe to call the method multiple times and from multiple goroutines,
// they will all block until the producer has been completely shutdown.
func (p *Producer) Stop() {
	p.once.Do(p.stop)
	err := errors.New("publishing to a producer that was already stopped")

	for req := range p.reqs {
		req.complete(err)
	}

	p.join.Wait()
}

// Publish sends a message using the producer p, returning an error if it was
// already closed or if an error occurred while publishing the message.
//
// Note that no retry is done internally, the producer will fail after the
// first unsuccessful attempt to publish the message. It is the responsibility
// of the caller to retry if necessary.
func (p *Producer) Publish(message []byte) (err error) {
	return p.PublishTo(p.topic, message)
}

// PublishTo sends a message to a specific topic using the producer p, returning
// an error if it was already closed or if an error occurred while publishing the
// message.
//
// Note that no retry is done internally, the producer will fail after the
// first unsuccessful attempt to publish the message. It is the responsibility
// of the caller to retry if necessary.
func (p *Producer) PublishTo(topic string, message []byte) (err error) {
	defer func() {
		if recover() != nil {
			err = errors.New("publishing to a producer that was already stopped")
		}
	}()

	if len(topic) == 0 {
		return errors.New("no topic set for publishing message")
	}

	response := make(chan error, 1)
	deadline := time.Now().Add(p.dialTimeout + p.readTimeout + p.writeTimeout)

	// Attempts to queue the request so one of the active connections can pick
	// it up.
	p.reqs <- ProducerRequest{
		Topic:    topic,
		Message:  message,
		Response: response,
		Deadline: deadline,
	}

	// This will always trigger, either if the connection was lost or if a
	// response was successfully sent.
	err = <-response
	return
}

// Requests returns a write-only channel that can be used to submit requests to p.
//
// This method is useful when the publish operation needs to be associated with
// other operations on channels in a select statement for example, or to publish
// in a non-blocking fashion.
func (p *Producer) Requests() chan<- ProducerRequest {
	return p.reqs
}

// Connected returns true if the producer has successfully established a
// connection to nsqd, false otherwise.
func (p *Producer) Connected() bool {
	return atomic.LoadUint32(&p.ok) != 0
}

func (p *Producer) stop() {
	close(p.done)
	close(p.reqs)
}

func (p *Producer) run() {
	var conn *Conn
	var reqChan <-chan ProducerRequest
	var resChan chan Frame
	var pending []ProducerRequest
	var retry time.Duration

	shutdown := func(err error) {
		atomic.StoreUint32(&p.ok, 0)

		if conn != nil {
			close(resChan)
			conn.Close()
			conn = nil
			reqChan = nil
			resChan = nil
			pending = completeAllProducerRequests(pending, err)
		}
	}

	connect := func() (err error) {
		if conn, err = DialTimeout(p.address, p.dialTimeout); err != nil {
			log.Printf("failed to connect to %s, retrying after %s: %s", p.address, retry, err)
			retry = p.sleep(retry)
			return
		}

		retry = 0
		reqChan = p.reqs
		resChan = make(chan Frame, 16)
		go p.flush(conn, resChan)

		atomic.StoreUint32(&p.ok, 1)
		return
	}

	defer p.join.Done()
	defer shutdown(nil)

	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	connect()

	for {
		select {
		case <-p.done:
			return

		case now := <-ticker.C:
			if conn == nil {
				connect()
			}

			if producerRequestsTimedOut(pending, now) {
				shutdown(errors.New("timeout"))
				continue
			}

		case req, ok := <-reqChan:
			if !ok {
				return
			}

			if err := p.publish(conn, req.Topic, req.Message); err != nil {
				req.complete(err)
				shutdown(err)
				continue
			}

			pending = append(pending, req)

		case frame := <-resChan:
			switch f := frame.(type) {
			case Response:
				switch f {
				case OK:
					pending = completeNextProducerRequest(pending, nil)

				case Heartbeat:
					if err := p.ping(conn); err != nil {
						shutdown(err)
						continue
					}

				case CloseWait:
					return

				default:
					shutdown(errors.Errorf("closing connection after receiving an unexpected response from %s: %s", conn.RemoteAddr(), f))
					continue
				}

			case Error:
				shutdown(errors.Errorf("closing connection after receiving an error from %s: %s", conn.RemoteAddr(), f))
				continue

			case Message:
				shutdown(errors.Errorf("closing connection after receiving an unexpected message from %s: %s", conn.RemoteAddr(), f.FrameType()))
				continue

			default:
				shutdown(errors.Errorf("closing connection after receiving an unsupported frame from %s: %s", conn.RemoteAddr(), f.FrameType()))
				continue
			}
		}
	}
}

func (p *Producer) flush(conn *Conn, resChan chan<- Frame) {
	defer func() { recover() }() // may happen if the resChan is closed
	defer conn.Close()

	for {
		frame, err := conn.ReadFrame()

		if err != nil {
			resChan <- Error(err.Error())
			return
		}

		resChan <- frame
	}
}

func (p *Producer) write(conn *Conn, cmd Command) (err error) {
	if err = conn.SetDeadline(time.Now().Add(p.writeTimeout)); err == nil {
		err = conn.WriteCommand(cmd)
	}
	return
}

func (p *Producer) publish(conn *Conn, topic string, message []byte) error {
	return p.write(conn, Pub{
		Topic:   topic,
		Message: message,
	})
}

func (p *Producer) ping(conn *Conn) error {
	return p.write(conn, Nop{})
}

func (p *Producer) sleep(d time.Duration) time.Duration {
	if d < p.minRetryTimeout {
		d = p.minRetryTimeout
	}

	t := time.NewTimer(d)
	defer t.Stop()

	select {
	case <-t.C:
	case <-p.done:
	}

	if d *= 2; d > p.maxRetryTimeout {
		d = p.maxRetryTimeout
	}

	return d
}

func (r ProducerRequest) complete(err error) {
	if r.Response != nil {
		r.Response <- err
	}
}

func completeNextProducerRequest(reqs []ProducerRequest, err error) []ProducerRequest {
	reqs[0].complete(err)
	return reqs[1:]
}

func completeAllProducerRequests(reqs []ProducerRequest, err error) []ProducerRequest {
	for _, req := range reqs {
		req.complete(err)
	}
	return nil
}

func producerRequestsTimedOut(reqs []ProducerRequest, now time.Time) bool {
	return len(reqs) != 0 && now.After(reqs[0].Deadline)
}
<commit_msg>simplify logic, retry to connect every second, always connect<commit_after>package nsq

import (
	"log"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

// ProducerConfig carries the different variables to tune a newly started
// producer.
type ProducerConfig struct {
	Address        string
	Topic          string
	MaxConcurrency int
	DialTimeout    time.Duration
	ReadTimeout    time.Duration
	WriteTimeout   time.Duration
}

// Producer provide an abstraction around using direct connections to nsqd
// nodes to send messages.
type Producer struct {
	// Communication channels of the producer.
	reqs chan ProducerRequest
	done chan struct{}
	once sync.Once
	join sync.WaitGroup
	ok   uint32

	// Immutable state of the producer.
	address      string
	topic        string
	dialTimeout  time.Duration
	readTimeout  time.Duration
	writeTimeout time.Duration
}

// ProducerRequest are used to represent operations that are submitted to
// producers.
type ProducerRequest struct {
	Topic    string
	Message  []byte
	Response chan<- error
	Deadline time.Time
}

// StartProducer starts and returns a new producer p, configured with the
// variables from the config parameter, or returning an non-nil error if
// some of the configuration variables were invalid.
func StartProducer(config ProducerConfig) (p *Producer, err error) {
	if len(config.Address) == 0 {
		config.Address = "localhost:4151"
	}

	if config.MaxConcurrency == 0 {
		config.MaxConcurrency = DefaultMaxConcurrency
	}

	if config.DialTimeout == 0 {
		config.DialTimeout = DefaultDialTimeout
	}

	if config.ReadTimeout == 0 {
		config.ReadTimeout = DefaultReadTimeout
	}

	if config.WriteTimeout == 0 {
		config.WriteTimeout = DefaultWriteTimeout
	}

	p = &Producer{
		reqs:         make(chan ProducerRequest, config.MaxConcurrency),
		done:         make(chan struct{}),
		address:      config.Address,
		topic:        config.Topic,
		dialTimeout:  config.DialTimeout,
		readTimeout:  config.ReadTimeout,
		writeTimeout: config.WriteTimeout,
	}
	p.join.Add(config.MaxConcurrency)

	for i := 0; i != config.MaxConcurrency; i++ {
		go p.run()
	}

	return
}

// Stop gracefully shutsdown the producer, cancelling all inflight requests and
// waiting for all backend connections to be closed.
//
// It is safe to call the method multiple times and from multiple goroutines,
// they will all block until the producer has been completely shutdown.
func (p *Producer) Stop() {
	p.once.Do(p.stop)
	err := errors.New("publishing to a producer that was already stopped")

	for req := range p.reqs {
		req.complete(err)
	}

	p.join.Wait()
}

// Publish sends a message using the producer p, returning an error if it was
// already closed or if an error occurred while publishing the message.
//
// Note that no retry is done internally, the producer will fail after the
// first unsuccessful attempt to publish the message. It is the responsibility
// of the caller to retry if necessary.
func (p *Producer) Publish(message []byte) (err error) {
	return p.PublishTo(p.topic, message)
}

// PublishTo sends a message to a specific topic using the producer p, returning
// an error if it was already closed or if an error occurred while publishing the
// message.
//
// Note that no retry is done internally, the producer will fail after the
// first unsuccessful attempt to publish the message. It is the responsibility
// of the caller to retry if necessary.
func (p *Producer) PublishTo(topic string, message []byte) (err error) {
	defer func() {
		if recover() != nil {
			err = errors.New("publishing to a producer that was already stopped")
		}
	}()

	if len(topic) == 0 {
		return errors.New("no topic set for publishing message")
	}

	response := make(chan error, 1)
	deadline := time.Now().Add(p.dialTimeout + p.readTimeout + p.writeTimeout)

	// Attempts to queue the request so one of the active connections can pick
	// it up.
	p.reqs <- ProducerRequest{
		Topic:    topic,
		Message:  message,
		Response: response,
		Deadline: deadline,
	}

	// This will always trigger, either if the connection was lost or if a
	// response was successfully sent.
	err = <-response
	return
}

// Requests returns a write-only channel that can be used to submit requests to p.
//
// This method is useful when the publish operation needs to be associated with
// other operations on channels in a select statement for example, or to publish
// in a non-blocking fashion.
func (p *Producer) Requests() chan<- ProducerRequest {
	return p.reqs
}

// Connected returns true if the producer has successfully established a
// connection to nsqd, false otherwise.
func (p *Producer) Connected() bool {
	return atomic.LoadUint32(&p.ok) != 0
}

func (p *Producer) stop() {
	close(p.done)
	close(p.reqs)
}

func (p *Producer) run() {
	var conn *Conn
	var reqChan <-chan ProducerRequest
	var resChan chan Frame
	var pending []ProducerRequest

	shutdown := func(err error) {
		atomic.StoreUint32(&p.ok, 0)

		if conn != nil {
			close(resChan)
			conn.Close()
			conn = nil
			reqChan = nil
			resChan = nil
			pending = completeAllProducerRequests(pending, err)
		}
	}

	connect := func() (err error) {
		if conn, err = DialTimeout(p.address, p.dialTimeout); err != nil {
			log.Printf("failed to connect to %s: %s", p.address, err)
			return
		}

		reqChan = p.reqs
		resChan = make(chan Frame, 16)
		go p.flush(conn, resChan)

		atomic.StoreUint32(&p.ok, 1)
		return
	}

	defer p.join.Done()
	defer shutdown(nil)

	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	connect()

	for {
		select {
		case <-p.done:
			return

		case now := <-ticker.C:
			if conn == nil {
				connect()
			}

			if producerRequestsTimedOut(pending, now) {
				shutdown(errors.New("timeout"))
				continue
			}

		case req, ok := <-reqChan:
			if !ok {
				return
			}

			if err := p.publish(conn, req.Topic, req.Message); err != nil {
				req.complete(err)
				shutdown(err)
				continue
			}

			pending = append(pending, req)

		case frame := <-resChan:
			switch f := frame.(type) {
			case Response:
				switch f {
				case OK:
					pending = completeNextProducerRequest(pending, nil)

				case Heartbeat:
					if err := p.ping(conn); err != nil {
						shutdown(err)
						continue
					}

				case CloseWait:
					return

				default:
					shutdown(errors.Errorf("closing connection after receiving an unexpected response from %s: %s", conn.RemoteAddr(), f))
					continue
				}

			case Error:
				shutdown(errors.Errorf("closing connection after receiving an error from %s: %s", conn.RemoteAddr(), f))
				continue

			case Message:
				shutdown(errors.Errorf("closing connection after receiving an unexpected message from %s: %s", conn.RemoteAddr(), f.FrameType()))
				continue

			default:
				shutdown(errors.Errorf("closing connection after receiving an unsupported frame from %s: %s", conn.RemoteAddr(), f.FrameType()))
				continue
			}
		}
	}
}

func (p *Producer) flush(conn *Conn, resChan chan<- Frame) {
	defer func() { recover() }() // may happen if the resChan is closed
	defer conn.Close()

	for {
		frame, err := conn.ReadFrame()

		if err != nil {
			resChan <- Error(err.Error())
			return
		}

		resChan <- frame
	}
}

func (p *Producer) write(conn *Conn, cmd Command) (err error) {
	if err = conn.SetDeadline(time.Now().Add(p.writeTimeout)); err == nil {
		err = conn.WriteCommand(cmd)
	}
	return
}

func (p *Producer) publish(conn *Conn, topic string, message []byte) error {
	return p.write(conn, Pub{
		Topic:   topic,
		Message: message,
	})
}

func (p *Producer) ping(conn *Conn) error {
	return p.write(conn, Nop{})
}

func (r ProducerRequest) complete(err error) {
	if r.Response != nil {
		r.Response <- err
	}
}

func completeNextProducerRequest(reqs []ProducerRequest, err error) []ProducerRequest {
	reqs[0].complete(err)
	return reqs[1:]
}

func completeAllProducerRequests(reqs []ProducerRequest, err error) []ProducerRequest {
	for _, req := range reqs {
		req.complete(err)
	}
	return nil
}

func producerRequestsTimedOut(reqs []ProducerRequest, now time.Time) bool {
	return len(reqs) != 0 && now.After(reqs[0].Deadline)
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/compute/v1"
	"google.golang.org/api/googleapi"
)

func resourceComputeNetwork() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeNetworkCreate,
		Read:   resourceComputeNetworkRead,
		Delete: resourceComputeNetworkDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"ipv4_range": &schema.Schema{
				Type:       schema.TypeString,
				Optional:   true,
				ForceNew:   true,
				Deprecated: "Please use google_compute_subnetwork resources instead.",
			},

			"gateway_ipv4": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"auto_create_subnetworks": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
				Default:  false, // TODO: ideally should be true to match Google's default behaviour, but this causes backward
				// compatibility issue with existing terraform configs
			},

			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"self_link": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceComputeNetworkCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	//
	// Possible modes:
	// - 1 Legacy mode - Create a network in the legacy mode. ipv4_range is set. auto_create_subnetworks must be false
	//     and not sent in request
	// - 2 Distributed Mode - Create a new generation network that supports subnetworks:
	//   - 2.a - Auto subnet mode - auto_create_subnetworks = true, Google will generate 1 subnetwork per region
	//   - 2.b - Custom subnet mode - auto_create_subnetworks = false & ipv4_range not set,
	//
	ipv4range := d.Get("ipv4_range").(string)
	autoCreateSubnetworks := d.Get("auto_create_subnetworks").(bool)

	if ipv4range != "" && autoCreateSubnetworks {
		return fmt.Errorf("Error: cannot define ipv4_range with auto_create_subnetworks = true.")
	}

	// Build the network parameter
	network := &compute.Network{
		Name: d.Get("name").(string),
		AutoCreateSubnetworks: autoCreateSubnetworks,
		Description:           d.Get("description").(string),
	}

	if v, ok := d.GetOk("ipv4_range"); ok {
		log.Printf("[DEBUG] Setting IPv4Range (%#v) for legacy network mode", v.(string))
		network.IPv4Range = v.(string)
	} else {
		// custom subnet mode, so make sure AutoCreateSubnetworks field is included in request otherwise
		// google will create a network in legacy mode.
		network.ForceSendFields = []string{"AutoCreateSubnetworks"}
	}

	log.Printf("[DEBUG] Network insert request: %#v", network)
	op, err := config.clientCompute.Networks.Insert(
		config.Project, network).Do()
	if err != nil {
		return fmt.Errorf("Error creating network: %s", err)
	}

	// It probably maybe worked, so store the ID now
	d.SetId(network.Name)

	err = computeOperationWaitGlobal(config, op, "Creating Network")
	if err != nil {
		return err
	}

	return resourceComputeNetworkRead(d, meta)
}

func resourceComputeNetworkRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	network, err := config.clientCompute.Networks.Get(
		config.Project, d.Id()).Do()
	if err != nil {
		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
			log.Printf("[WARN] Removing Network %q because it's gone", d.Get("name").(string))
			// The resource doesn't exist anymore
			d.SetId("")

			return nil
		}

		return fmt.Errorf("Error reading network: %s", err)
	}

	d.Set("gateway_ipv4", network.GatewayIPv4)
	d.Set("self_link", network.SelfLink)

	return nil
}

func resourceComputeNetworkDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	// Delete the network
	op, err := config.clientCompute.Networks.Delete(
		config.Project, d.Id()).Do()
	if err != nil {
		return fmt.Errorf("Error deleting network: %s", err)
	}

	err = computeOperationWaitGlobal(config, op, "Deleting Network")
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}
<commit_msg>provider/google: Clarify comment about defaults<commit_after>package google

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/compute/v1"
	"google.golang.org/api/googleapi"
)

func resourceComputeNetwork() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeNetworkCreate,
		Read:   resourceComputeNetworkRead,
		Delete: resourceComputeNetworkDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"ipv4_range": &schema.Schema{
				Type:       schema.TypeString,
				Optional:   true,
				ForceNew:   true,
				Deprecated: "Please use google_compute_subnetwork resources instead.",
			},

			"gateway_ipv4": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"auto_create_subnetworks": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
				/* Ideally this would default to true as per the API, but that would cause
				   existing Terraform configs which have not been updated to report this as
				   a change. Perhaps we can bump this for a minor release bump rather than
				   a point release. */
				Default: false,
			},

			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"self_link": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceComputeNetworkCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	//
	// Possible modes:
	// - 1 Legacy mode - Create a network in the legacy mode. ipv4_range is set. auto_create_subnetworks must be false
	//     and not sent in request
	// - 2 Distributed Mode - Create a new generation network that supports subnetworks:
	//   - 2.a - Auto subnet mode - auto_create_subnetworks = true, Google will generate 1 subnetwork per region
	//   - 2.b - Custom subnet mode - auto_create_subnetworks = false & ipv4_range not set,
	//
	ipv4range := d.Get("ipv4_range").(string)
	autoCreateSubnetworks := d.Get("auto_create_subnetworks").(bool)

	if ipv4range != "" && autoCreateSubnetworks {
		return fmt.Errorf("Error: cannot define ipv4_range with auto_create_subnetworks = true.")
	}

	// Build the network parameter
	network := &compute.Network{
		Name: d.Get("name").(string),
		AutoCreateSubnetworks: autoCreateSubnetworks,
		Description:           d.Get("description").(string),
	}

	if v, ok := d.GetOk("ipv4_range"); ok {
		log.Printf("[DEBUG] Setting IPv4Range (%#v) for legacy network mode", v.(string))
		network.IPv4Range = v.(string)
	} else {
		// custom subnet mode, so make sure AutoCreateSubnetworks field is included in request otherwise
		// google will create a network in legacy mode.
		network.ForceSendFields = []string{"AutoCreateSubnetworks"}
	}

	log.Printf("[DEBUG] Network insert request: %#v", network)
	op, err := config.clientCompute.Networks.Insert(
		config.Project, network).Do()
	if err != nil {
		return fmt.Errorf("Error creating network: %s", err)
	}

	// It probably maybe worked, so store the ID now
	d.SetId(network.Name)

	err = computeOperationWaitGlobal(config, op, "Creating Network")
	if err != nil {
		return err
	}

	return resourceComputeNetworkRead(d, meta)
}

func resourceComputeNetworkRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	network, err := config.clientCompute.Networks.Get(
		config.Project, d.Id()).Do()
	if err != nil {
		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
			log.Printf("[WARN] Removing Network %q because it's gone", d.Get("name").(string))
			// The resource doesn't exist anymore
			d.SetId("")

			return nil
		}

		return fmt.Errorf("Error reading network: %s", err)
	}

	d.Set("gateway_ipv4", network.GatewayIPv4)
	d.Set("self_link", network.SelfLink)

	return nil
}

func resourceComputeNetworkDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	// Delete the network
	op, err := config.clientCompute.Networks.Delete(
		config.Project, d.Id()).Do()
	if err != nil {
		return fmt.Errorf("Error deleting network: %s", err)
	}

	err = computeOperationWaitGlobal(config, op, "Deleting Network")
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}
<|endoftext|>


<commit_before>package gosql

import (
	"fmt"
	. "github.com/suboat/go-sql-kit"
	. "github.com/suboat/go-sql-kit/sql"
)

type SQLXQuery struct {
	SQLQuery
	index  int
	values []interface{}
}

func NewSQLXQuery() *SQLXQuery {
	s := &SQLXQuery{SQLQuery: *NewSQLQuery(), index: 1, values: nil}
	s.SetValueFormat(s.ValueString)
	return s
}

func (s *SQLXQuery) String(alias ...string) string {
	s.index = 1
	s.values = nil
	return s.SQLQuery.String(alias...)
}

func (s *SQLXQuery) ValueString(v *QueryValue) string {
	opera := ""
	switch v.Key {
	case QueryKeyEq:
		opera = "="
	case QueryKeyNe:
		opera = "<>"
	case QueryKeyLt:
		opera = "<"
	case QueryKeyLte:
		opera = "<="
	case QueryKeyGt:
		opera = ">"
	case QueryKeyGte:
		opera = ">="
	case QueryKeyLike:
		return fmt.Sprintf("%v LIKE '%%%v%%'", v.Field, v.Value)
	default:
		return ""
	}
	defer func() {
		s.index++
		s.values = append(s.values, v.Value)
	}()
	return fmt.Sprintf("%v%v$%v", v.Field, opera, s.index)
}

func (s *SQLXQuery) GetValues() []interface{} {
	return s.values
}

func (s *SQLXQuery) JSONtoSQLString(str string, alias ...string) (string, []interface{}, error) {
	if err := s.ParseJSONString(str); err != nil {
		return "", nil, err
	}
	return s.String(alias...), s.GetValues(), nil
}

func (s *SQLXQuery) SQLString(m map[string]interface{}, alias ...string) (string, []interface{}, error) {
	if err := s.Parse(m); err != nil {
		return "", nil, err
	}
	return s.String(alias...), s.GetValues(), nil
}
<commit_msg>feat: sqlx support between<commit_after>package gosql

import (
	"fmt"
	. "github.com/suboat/go-sql-kit"
	. "github.com/suboat/go-sql-kit/sql"
)

type SQLXQuery struct {
	SQLQuery
	index  int
	values []interface{}
}

func NewSQLXQuery() *SQLXQuery {
	s := &SQLXQuery{SQLQuery: *NewSQLQuery(), index: 1, values: nil}
	s.SetValueFormat(s.ValueString)
	return s
}

func (s *SQLXQuery) String(alias ...string) string {
	s.index = 1
	s.values = nil
	return s.SQLQuery.String(alias...)
}

func (s *SQLXQuery) ValueString(v *QueryValue) string {
	opera := ""
	switch v.Key {
	case QueryKeyEq:
		opera = "="
	case QueryKeyNe:
		opera = "<>"
	case QueryKeyLt:
		opera = "<"
	case QueryKeyLte:
		opera = "<="
	case QueryKeyGt:
		opera = ">"
	case QueryKeyGte:
		opera = ">="
	case QueryKeyLike:
		return fmt.Sprintf("%v LIKE '%%%v%%'", v.Field, v.Value)
	case QueryKeyBetween, QueryKeyNotBetween:
		if vs, ok := v.Value.([]interface{}); ok {
			if vs == nil || len(vs) < 2 {
				return ""
			}
			defer func() {
				s.index++
				s.values = append(s.values, vs[0])
				s.index++
				s.values = append(s.values, vs[1])
			}()
			if v.Key == QueryKeyBetween {
				return fmt.Sprintf("%v BETWEEN $%v AND $%v", v.Field, s.index, s.index+1)
			} else {
				return fmt.Sprintf("%v NOT BETWEEN $%v AND $%v", v.Field, s.index, s.index+1)
			}
		}
	default:
		return ""
	}
	defer func() {
		s.index++
		s.values = append(s.values, v.Value)
	}()
	return fmt.Sprintf("%v%v$%v", v.Field, opera, s.index)
}

func (s *SQLXQuery) GetValues() []interface{} {
	return s.values
}

func (s *SQLXQuery) JSONtoSQLString(str string, alias ...string) (string, []interface{}, error) {
	if err := s.ParseJSONString(str); err != nil {
		return "", nil, err
	}
	return s.String(alias...), s.GetValues(), nil
}

func (s *SQLXQuery) SQLString(m map[string]interface{}, alias ...string) (string, []interface{}, error) {
	if err := s.Parse(m); err != nil {
		return "", nil, err
	}
	return s.String(alias...), s.GetValues(), nil
}
<|endoftext|>


<commit_before>// $G $D/$F.go && $L $F.$A && ./$A.out

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func Alloc(i int) int {
	switch i {
	default:
		return 5;
	case 1:
		return 1;
	case 10:
		return 10;
	}
}

func main() {
	s := Alloc(7);
}

/*
bug028.go:7: unreachable statements in a switch
*/
<commit_msg>test wasn't being run. it works fine.<commit_after>// $G $D/$F.go && $L $F.$A && ./$A.out

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func Alloc(i int) int {
	switch i {
	default:
		return 5;
	case 1:
		return 1;
	case 10:
		return 10;
	}
}

func main() {
	s := Alloc(7);
	if s != 5 { panic "bad" }
}

/*
bug028.go:7: unreachable statements in a switch
*/
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

const x x = 2 // ERROR "loop"

/*
bug081.go:3: first constant must evaluate an expression
Bus error
*/
<commit_msg>test: match gccgo error messages for bug081.go.<commit_after>// errchk $G $D/$F.go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

const x x = 2 // ERROR "loop|type"

/*
bug081.go:3: first constant must evaluate an expression
Bus error
*/
<|endoftext|>


<commit_before>package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	_ "github.com/mattn/go-sqlite3"
	_ "golang.org/x/crypto/bcrypt"
)

func main() {
	db := NewDB(":memory:")
	service := &Service{db: db}

	service.Serve()
}

type DB struct {
	conn *sql.DB
}

type UserModel struct {
	Id    int
	Name  string
	Email string

	Password string

	CreatedAt time.Time
	UpdatedAt time.Time
}

// todo
type RecipeModel struct{}

type RecipeTagModel struct {
	Id       int
	RecipeId int
	Tag      string
}

func NewDB(uri string) *DB {
	conn, err := sql.Open("sqlite3", uri)
	if err != nil {
		panic(err)
	}

	db := DB{conn: conn}
	if err := db.initializeSchema(); err != nil {
		panic(err)
	}

	return &db
}

const (
	baseSchema = `
CREATE TABLE IF NOT EXISTS users(
  id INTEGER PRIMARY KEY autoincrement,

  name  TEXT,
  email TEXT,

  password TEXT,

  created_at DATETIME DEFAULT current_timestamp,
  updated_at DATETIME
);

CREATE TABLE IF NOT EXISTS recipes(
  id INTEGER PRIMARY KEY autoincrement,

  title       TEXT,
  description TEXT,
  category    TEXT,

  instructions TEXT,
  ingredients  TEXT,
  rating       INT,

  created_at DATETIME DEFAULT current_timestamp,
  updated_at DATETIME
);

CREATE TABLE IF NOT EXISTS recipe_tags(
  id INTEGER PRIMARY KEY autoincrement,

  recipe_id INTEGER,
  tag TEXT,

  FOREIGN KEY (recipe_id) REFERENCES recipes(id)
);

CREATE UNIQUE INDEX IF NOT EXISTS unique_tags_by_recipe_id
  ON recipe_tags(tag, recipe_id);
`
)

func (db DB) initializeSchema() error {
	log.Printf("[db] applying base schema")
	_, err := db.conn.Exec(baseSchema)
	return err
}

func (db DB) UserById(id int) (*UserModel, error) {
	// TODO
	return nil, nil
}

type Service struct {
	db           *DB
	cookieSecret []byte
}

const (
	authCookieName = "u"
)

func (s *Service) signCookie(value string) string {
	mac := hmac.New(sha256.New, s.cookieSecret)
	mac.Write([]byte(value))

	return string(mac.Sum(nil))
}

// returns (user_id, verified?)
func (s *Service) verifyCookie(c *http.Cookie) (int, bool) {
	// Check for expired cookies first
	if time.Now().After(c.Expires) {
		return 0, false
	}

	// Next try to verify
	values := strings.SplitN(c.Value, ".", 2)
	if len(values) != 2 {
		return 0, false
	}

	given := []byte(values[0])
	expected := []byte(s.signCookie(values[1]))

	if hmac.Equal(expected, given) {
		uid, _ := strconv.Atoi(values[1])
		return uid, true
	}

	return 0, false
}

func (s *Service) loggedInUser(req *http.Request) *UserModel {
	if cookie, err := req.Cookie(authCookieName); err != nil {
		return nil
	} else if uid, ok := s.verifyCookie(cookie); !ok {
		return nil
	} else if user, err := s.db.UserById(uid); err != nil {
		log.Printf("user lookup failed: %+v\n", err)
		return nil
	} else {
		return user
	}
}

type MiniMux struct {
	*http.ServeMux
}

func (m *MiniMux) HandleScoped(path string, h http.HandlerFunc) {
	m.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
		// Implementation stolen from `http.StripPrefix` (which takes a
		// http.Handler instead)
		p := strings.TrimPrefix(req.URL.Path, path)
		r2 := new(http.Request)
		*r2 = *req
		r2.URL = new(url.URL)
		*r2.URL = *req.URL
		r2.URL.Path = p
	})
}

func (s *Service) Serve() {
	mux := MiniMux{http.NewServeMux()}

	mux.HandleScoped("/user", s.handleUserResource)
	mux.HandleScoped("/session/", s.handleSessionResource)
	mux.HandleScoped("/recipe/", s.handleRecipeResource)
	mux.HandleScoped("/tag/", s.handleTagResource)

	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		if req.URL.Path != "/" {
			http.NotFound(w, req)
		} else {
			s.handleListing(w, req)
		}
	})

	log.Printf("[service] starting http server")
	http.ListenAndServe(":8080", mux)
}

func sendStatus(w http.ResponseWriter, statusCode int) {
	w.WriteHeader(statusCode)
	w.Write([]byte(http.StatusText(statusCode)))
}

// POST /user
// PUT  /user
func (s *Service) handleUserResource(w http.ResponseWriter, req *http.Request) {
	email := req.FormValue("email")
	password := req.FormValue("password")

	switch req.Method {
	case http.MethodPost:
		fmt.Printf("create user email=%s password=%s\n", email, password)

	case http.MethodPut:
		user := s.loggedInUser(req)
		if user == nil {
			sendStatus(w, http.StatusUnauthorized)
			return
		}

		fmt.Printf("update user (%+v) email=%s password=%s\n",
			user, email, password)

	default:
		http.NotFound(w, req)
	}
}

// GET  /session/login
// POST /session/login
// GET  /session/logout
func (s *Service) handleSessionResource(w http.ResponseWriter, req *http.Request) {
	type createLoginBody struct {
		Email    string
		Password string
	}

	path := req.URL.Path

	switch {
	case req.Method == http.MethodGet && path == "login":
		// todo

	case req.Method == http.MethodPost && path == "login":
		// todo

		http.Redirect(w, req, "/", http.StatusSeeOther)

	case req.Method == http.MethodGet && path == "logout":
		http.SetCookie(w, &http.Cookie{
			Name:     authCookieName,
			Value:    "",
			Path:     "/",
			Expires:  time.Unix(0, 0),
			HttpOnly: true,
		})

		http.Redirect(w, req, "/", http.StatusSeeOther)

	default:
		http.NotFound(w, req)
	}

}

// GET    /recipe
// POST   /recipe
// GET    /recipe/:id
// PUT    /recipe/:id
// DELETE /recipe/:id
func (s *Service) handleRecipeResource(w http.ResponseWriter, req *http.Request) {
	type createRecipeBody struct{}
	type updateRecipeBody struct{}
}

// GET /tag
// GET /tag/:id
func (s *Service) handleTagResource(w http.ResponseWriter, req *http.Request) {

}

// GET /
func (s *Service) handleListing(w http.ResponseWriter, req *http.Request) {

}
<commit_msg>Simple login sketch<commit_after>package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	_ "github.com/mattn/go-sqlite3"
	"golang.org/x/crypto/bcrypt"
)

func main() {
	db := NewDB("/tmp/recipebin.db")
	service := &Service{db: db}

	service.Serve()
}

type DB struct {
	conn *sql.DB
}

type UserModel struct {
	Id    int
	Email string

	Password string

	CreatedAt time.Time
	UpdatedAt sql.NullInt64
}

// todo
type RecipeModel struct{}

type RecipeTagModel struct {
	Id       int
	RecipeId int
	Tag      string
}

func NewDB(uri string) *DB {
	conn, err := sql.Open("sqlite3", uri)
	if err != nil {
		panic(err)
	}

	db := DB{conn: conn}
	if err := db.initializeSchema(); err != nil {
		panic(err)
	}

	return &db
}

const (
	baseSchema = `
CREATE TABLE IF NOT EXISTS users(
  id INTEGER PRIMARY KEY autoincrement,

  email TEXT UNIQUE NOT NULL,

  password TEXT UNIQUE NOT NULL,

  created_at DATETIME DEFAULT current_timestamp,
  updated_at DATETIME
);

CREATE TABLE IF NOT EXISTS recipes(
  id INTEGER PRIMARY KEY autoincrement,

  title       TEXT,
  description TEXT,
  category    TEXT,

  instructions TEXT,
  ingredients  TEXT,
  rating       INT,

  created_at DATETIME DEFAULT current_timestamp,
  updated_at DATETIME
);

CREATE TABLE IF NOT EXISTS recipe_tags(
  id INTEGER PRIMARY KEY autoincrement,

  recipe_id INTEGER,
  tag TEXT,

  FOREIGN KEY (recipe_id) REFERENCES recipes(id)
);

CREATE UNIQUE INDEX IF NOT EXISTS unique_user_emails
  ON users(lower(email));

CREATE UNIQUE INDEX IF NOT EXISTS unique_tags_by_recipe_id
  ON recipe_tags(tag, recipe_id);
`
)

func (db DB) initializeSchema() error {
	log.Printf("[db] applying base schema")
	_, err := db.conn.Exec(baseSchema)
	return err
}

func (db DB) UserById(id int) (*UserModel, error) {
	var user UserModel

	err := db.conn.QueryRow(`
SELECT id
     , email
     , password
     , created_at
     , updated_at
FROM users
WHERE id = ?`, id).Scan(
		&user.Id,
		&user.Email,
		&user.Password,
		&user.CreatedAt,
		&user.UpdatedAt)

	return &user, err
}

func (db DB) UserByEmailAndPassword(email, password string) (*UserModel, error) {
	var user UserModel

	err := db.conn.QueryRow(`
SELECT id
     , email
     , password
     , created_at
     , updated_at
FROM users
WHERE lower(email) = lower(?)`, email).Scan(
		&user.Id,
		&user.Email,
		&user.Password,
		&user.CreatedAt,
		&user.UpdatedAt)

	if err != nil {
		return nil, err
	}

	err = bcrypt.CompareHashAndPassword(
		[]byte(user.Password),
		[]byte(password))
	return &user, err
}

func (db DB) NewUser(email, password string) error {
	sql := `
INSERT INTO users (email, password)
VALUES (?, ?)
`

	if hash, err := HashPassword(password); err != nil {
		return err
	} else {
		_, err = db.conn.Exec(sql, email, hash)
		return err
	}
}

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	return string(bytes), err
}

type Service struct {
	db           *DB
	cookieSecret []byte
}

const (
	authCookieName = "u"
)

func (s *Service) signCookie(value string) string {
	mac := hmac.New(sha256.New, s.cookieSecret)
	mac.Write([]byte(value))

	return string(mac.Sum(nil))
}

// returns (user_id, verified?)
func (s *Service) verifyCookie(c *http.Cookie) (int, bool) {
	// Check for expired cookies first
	if time.Now().After(c.Expires) {
		return 0, false
	}

	// Next try to verify
	values := strings.SplitN(c.Value, ".", 2)
	if len(values) != 2 {
		return 0, false
	}

	given := []byte(values[0])
	expected := []byte(s.signCookie(values[1]))

	if hmac.Equal(expected, given) {
		uid, _ := strconv.Atoi(values[1])
		return uid, true
	}

	return 0, false
}

func (s *Service) setLoginUser(w http.ResponseWriter, u *UserModel) {
	signed := s.signCookie(strconv.Itoa(u.Id))
	value := fmt.Sprintf("%d.%s", u.Id, signed)

	http.SetCookie(w, &http.Cookie{
		Name:  authCookieName,
		Value: value,
	})
}

func (s *Service) loggedInUser(req *http.Request) *UserModel {
	if cookie, err := req.Cookie(authCookieName); err != nil {
		return nil
	} else if uid, ok := s.verifyCookie(cookie); !ok {
		return nil
	} else if user, err := s.db.UserById(uid); err != nil {
		log.Printf("user lookup failed: %+v\n", err)
		return nil
	} else {
		return user
	}
}

type MiniMux struct {
	*http.ServeMux
}

func (m *MiniMux) HandleScoped(path string, h http.HandlerFunc) {
	m.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
		// Implementation stolen from `http.StripPrefix` (which takes a
		// http.Handler instead)
		p := strings.TrimPrefix(req.URL.Path, path)
		r2 := new(http.Request)
		*r2 = *req
		r2.URL = new(url.URL)
		*r2.URL = *req.URL
		r2.URL.Path = p

		// Proceed with our modified request
		h(w, r2)
	})
}

func (s *Service) Serve() {
	mux := MiniMux{http.NewServeMux()}

	// TODO: hook up request logging

	mux.HandleScoped("/user", s.handleUserResource)
	mux.HandleScoped("/session/", s.handleSessionResource)
	mux.HandleScoped("/recipe", s.handleRecipeResource)
	mux.HandleScoped("/tag/", s.handleTagResource)

	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Println("in the base case")
		if req.URL.Path != "/" {
			http.NotFound(w, req)
		} else {
			s.handleListing(w, req)
		}
	})

	log.Printf("[service] starting http server")
	http.ListenAndServe(":8080", mux)
}

func sendStatus(w http.ResponseWriter, statusCode int) {
	w.WriteHeader(statusCode)
	w.Write([]byte(http.StatusText(statusCode)))
}

// POST /user
// PUT  /user
func (s *Service) handleUserResource(w http.ResponseWriter, req *http.Request) {
	email := req.FormValue("email")
	password := req.FormValue("password")

	switch req.Method {
	case http.MethodPost:
		fmt.Printf("create user email=%s password=%s\n", email, password)
		if err := s.db.NewUser(email, password); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		http.Redirect(w, req, "/", http.StatusSeeOther)

	case http.MethodPut:
		user := s.loggedInUser(req)
		if user == nil {
			sendStatus(w, http.StatusUnauthorized)
			return
		}

		fmt.Printf("update user (%+v) email=%s password=%s\n",
			user, email, password)

	default:
		http.NotFound(w, req)
	}
}

// POST /session/login
// GET  /session/logout
func (s *Service) handleSessionResource(w http.ResponseWriter, req *http.Request) {
	path := req.URL.Path
	fmt.Printf("session: %s\n", path)

	switch {
	case req.Method == http.MethodPost && path == "login":
		email := req.FormValue("email")
		password := req.FormValue("password")

		user, err := s.db.UserByEmailAndPassword(email, password)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		s.setLoginUser(w, user)
		http.Redirect(w, req, "/", http.StatusSeeOther)

	case req.Method == http.MethodGet && path == "logout":
		http.SetCookie(w, &http.Cookie{
			Name:     authCookieName,
			Value:    "",
			Path:     "/",
			Expires:  time.Unix(0, 0),
			HttpOnly: true,
		})

		http.Redirect(w, req, "/", http.StatusSeeOther)

	default:
		http.NotFound(w, req)
	}

}

// GET    /recipe
// POST   /recipe
// GET    /recipe/:id
// PUT    /recipe/:id
// DELETE /recipe/:id
func (s *Service) handleRecipeResource(w http.ResponseWriter, req *http.Request) {
	type createRecipeBody struct{}
	type updateRecipeBody struct{}
}

// GET /tag
// GET /tag/:id
func (s *Service) handleTagResource(w http.ResponseWriter, req *http.Request) {

}

// GET /
func (s *Service) handleListing(w http.ResponseWriter, req *http.Request) {

}
<|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 main

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

	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"istio.io/istio/pkg/collateral"
	"istio.io/istio/pkg/log"
	"istio.io/istio/pkg/probe"
	"istio.io/istio/pkg/version"
	"istio.io/istio/security/pkg/cmd"
	"istio.io/istio/security/pkg/pki/ca"
	"istio.io/istio/security/pkg/pki/ca/controller"
	"istio.io/istio/security/pkg/registry"
	"istio.io/istio/security/pkg/registry/kube"
	"istio.io/istio/security/pkg/server/grpc"
)

const (
	defaultCACertTTL = 365 * 24 * time.Hour

	defaultWorkloadCertTTL = time.Hour

	maxWorkloadCertTTL = 7 * 24 * time.Hour

	// The default issuer organization for self-signed CA certificate.
	selfSignedCAOrgDefault = "k8s.cluster.local"

	// The key for the environment variable that specifies the namespace.
	namespaceKey = "NAMESPACE"
)

type cliOptions struct {
	certChainFile   string
	signingCertFile string
	signingKeyFile  string
	rootCertFile    string

	namespace string

	istioCaStorageNamespace string

	kubeConfigFile string

	selfSignedCA    bool
	selfSignedCAOrg string

	caCertTTL          time.Duration
	workloadCertTTL    time.Duration
	maxWorkloadCertTTL time.Duration

	grpcHostname string
	grpcPort     int

	loggingOptions *log.Options

	// The path to the file which indicates the liveness of the server by its existence.
	// This will be used for k8s liveness probe. If empty, it does nothing.
	LivenessProbeOptions *probe.Options
}

var (
	opts = cliOptions{
		loggingOptions:       log.NewOptions(),
		LivenessProbeOptions: &probe.Options{},
	}

	rootCmd = &cobra.Command{
		Use:   "istio_ca",
		Short: "Istio Certificate Authority (CA)",
		Run: func(cmd *cobra.Command, args []string) {
			runCA()
		},
	}
)

func fatalf(template string, args ...interface{}) {
	if len(args) > 0 {
		log.Errorf(template, args)
	} else {
		log.Errorf(template)
	}
	os.Exit(-1)
}

func init() {
	flags := rootCmd.Flags()

	flags.StringVar(&opts.certChainFile, "cert-chain", "", "Speicifies path to the certificate chain file")
	flags.StringVar(&opts.signingCertFile, "signing-cert", "", "Specifies path to the CA signing certificate file")
	flags.StringVar(&opts.signingKeyFile, "signing-key", "", "Specifies path to the CA signing key file")
	flags.StringVar(&opts.rootCertFile, "root-cert", "", "Specifies path to the root certificate file")

	flags.StringVar(&opts.namespace, "namespace", "",
		"Select a namespace for the CA to listen to. If unspecified, Istio CA tries to use the ${"+namespaceKey+"} "+
			"environment variable. If neither is set, Istio CA listens to all namespaces.")
	flags.StringVar(&opts.istioCaStorageNamespace, "istio-ca-storage-namespace", "istio-system", "Namespace where "+
		"the Istio CA pods is running. Will not be used if explicit file or other storage mechanism is specified.")

	flags.StringVar(&opts.kubeConfigFile, "kube-config", "",
		"Specifies path to kubeconfig file. This must be specified when not running inside a Kubernetes pod.")

	flags.BoolVar(&opts.selfSignedCA, "self-signed-ca", false,
		"Indicates whether to use auto-generated self-signed CA certificate. "+
			"When set to true, the '--signing-cert' and '--signing-key' options are ignored.")
	flags.StringVar(&opts.selfSignedCAOrg, "self-signed-ca-org", "k8s.cluster.local",
		fmt.Sprintf("The issuer organization used in self-signed CA certificate (default to %s)",
			selfSignedCAOrgDefault))

	flags.DurationVar(&opts.caCertTTL, "ca-cert-ttl", defaultCACertTTL,
		"The TTL of self-signed CA root certificate")
	flags.DurationVar(&opts.workloadCertTTL, "workload-cert-ttl", defaultWorkloadCertTTL, "The TTL of issued workload certificates")
	flags.DurationVar(&opts.maxWorkloadCertTTL, "max-workload-cert-ttl", maxWorkloadCertTTL, "The max TTL of issued workload certificates")

	flags.StringVar(&opts.grpcHostname, "grpc-hostname", "localhost", "Specifies the hostname for GRPC server.")
	flags.IntVar(&opts.grpcPort, "grpc-port", 0, "Specifies the port number for GRPC server. "+
		"If unspecified, Istio CA will not server GRPC request.")

	flags.StringVar(&opts.LivenessProbeOptions.Path, "livenessProbePath", "",
		"Path to the file for the liveness probe.")
	flags.DurationVar(&opts.LivenessProbeOptions.UpdateInterval, "livenessProbeInterval", 0,
		"Interval of updating file for the liveness probe.")

	rootCmd.AddCommand(version.CobraCommand())

	rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{
		Title:   "Istio CA",
		Section: "istio_ca CLI",
		Manual:  "Istio CA",
	}))

	opts.loggingOptions.AttachCobraFlags(rootCmd)
	cmd.InitializeFlags(rootCmd)
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		log.Errora(err)
		os.Exit(-1)
	}
}

func runCA() {
	if err := log.Configure(opts.loggingOptions); err != nil {
		fatalf("Failed to configure logging (%v)", err)
	}

	if value, exists := os.LookupEnv(namespaceKey); exists {
		// When -namespace is not set, try to read the namespace from environment variable.
		if opts.namespace == "" {
			opts.namespace = value
		}
		// Use environment variable for istioCaStorageNamespace if it exists
		opts.istioCaStorageNamespace = value
	}

	verifyCommandLineOptions()

	cs := createClientset()
	ca := createCA(cs.CoreV1())
	// For workloads in K8s, we apply the configured workload cert TTL.
	sc := controller.NewSecretController(ca, opts.workloadCertTTL, cs.CoreV1(), opts.namespace)

	stopCh := make(chan struct{})
	sc.Run(stopCh)

	if opts.grpcPort > 0 {
		// start registry if gRPC server is to be started
		reg := registry.GetIdentityRegistry()
		ch := make(chan struct{})

		// monitor service objects with "alpha.istio.io/kubernetes-serviceaccounts" annotation
		serviceController := kube.NewServiceController(cs.CoreV1(), opts.namespace, reg)
		serviceController.Run(ch)

		// monitor service account objects for istio mesh expansion
		serviceAccountController := kube.NewServiceAccountController(cs.CoreV1(), opts.namespace, reg)
		serviceAccountController.Run(ch)

		// The CA API uses cert with the max workload cert TTL.
		grpcServer := grpc.New(ca, opts.maxWorkloadCertTTL, opts.grpcHostname, opts.grpcPort)
		if err := grpcServer.Run(); err != nil {
			// stop the registry-related controllers
			ch <- struct{}{}

			log.Warnf("Failed to start GRPC server with error: %v", err)
		}
	}

	log.Info("Istio CA has started")
	select {} // wait forever
}

func createClientset() *kubernetes.Clientset {
	c := generateConfig()
	cs, err := kubernetes.NewForConfig(c)
	if err != nil {
		fatalf("Failed to create a clientset (error: %s)", err)
	}
	return cs
}

func createCA(core corev1.SecretsGetter) ca.CertificateAuthority {
	var caOpts *ca.IstioCAOptions
	var err error

	if opts.selfSignedCA {
		log.Info("Use self-signed certificate as the CA certificate")

		// TODO(wattli): Refactor this and combine it with NewIstioCA().
		caOpts, err = ca.NewSelfSignedIstioCAOptions(opts.caCertTTL, opts.workloadCertTTL,
			opts.maxWorkloadCertTTL, opts.selfSignedCAOrg, opts.istioCaStorageNamespace, core)
		if err != nil {
			fatalf("Failed to create a self-signed Istio CA (error: %v)", err)
		}
	} else {
		var certChainBytes []byte
		if opts.certChainFile != "" {
			certChainBytes = readFile(opts.certChainFile)
		}
		caOpts = &ca.IstioCAOptions{
			CertChainBytes:   certChainBytes,
			CertTTL:          opts.workloadCertTTL,
			MaxCertTTL:       opts.maxWorkloadCertTTL,
			SigningCertBytes: readFile(opts.signingCertFile),
			SigningKeyBytes:  readFile(opts.signingKeyFile),
			RootCertBytes:    readFile(opts.rootCertFile),
		}
	}

	caOpts.LivenessProbeOptions = opts.LivenessProbeOptions

	istioCA, err := ca.NewIstioCA(caOpts)
	if err != nil {
		log.Errorf("Failed to create an Istio CA (error: %v)", err)
	}
	return istioCA
}

func generateConfig() *rest.Config {
	if opts.kubeConfigFile != "" {
		c, err := clientcmd.BuildConfigFromFlags("", opts.kubeConfigFile)
		if err != nil {
			fatalf("Failed to create a config object from file %s, (error %v)", opts.kubeConfigFile, err)
		}
		return c
	}

	// When `kubeConfigFile` is unspecified, use the in-cluster configuration.
	c, err := rest.InClusterConfig()
	if err != nil {
		fatalf("Failed to create a in-cluster config (error: %s)", err)
	}
	return c
}

func readFile(filename string) []byte {
	bs, err := ioutil.ReadFile(filename)
	if err != nil {
		fatalf("Failed to read file %s (error: %v)", filename, err)
	}
	return bs
}

func verifyCommandLineOptions() {
	if opts.selfSignedCA {
		return
	}

	if opts.signingCertFile == "" {
		fatalf(
			"No signing cert has been specified. Either specify a cert file via '-signing-cert' option " +
				"or use '-self-signed-ca'")
	}

	if opts.signingKeyFile == "" {
		fatalf(
			"No signing key has been specified. Either specify a key file via '-signing-key' option " +
				"or use '-self-signed-ca'")
	}

	if opts.rootCertFile == "" {
		fatalf(
			"No root cert has been specified. Either specify a root cert file via '-root-cert' option " +
				"or use '-self-signed-ca'")
	}
}
<commit_msg>Rename the maxWorkloadCertTTL to avoid confusion (#3230)<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 main

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

	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"istio.io/istio/pkg/collateral"
	"istio.io/istio/pkg/log"
	"istio.io/istio/pkg/probe"
	"istio.io/istio/pkg/version"
	"istio.io/istio/security/pkg/cmd"
	"istio.io/istio/security/pkg/pki/ca"
	"istio.io/istio/security/pkg/pki/ca/controller"
	"istio.io/istio/security/pkg/registry"
	"istio.io/istio/security/pkg/registry/kube"
	"istio.io/istio/security/pkg/server/grpc"
)

const (
	defaultCACertTTL = 365 * 24 * time.Hour

	defaultMaxWorkloadCertTTL = 7 * 24 * time.Hour

	defaultWorkloadCertTTL = time.Hour

	// The default issuer organization for self-signed CA certificate.
	selfSignedCAOrgDefault = "k8s.cluster.local"

	// The key for the environment variable that specifies the namespace.
	namespaceKey = "NAMESPACE"
)

type cliOptions struct {
	certChainFile   string
	signingCertFile string
	signingKeyFile  string
	rootCertFile    string

	namespace string

	istioCaStorageNamespace string

	kubeConfigFile string

	selfSignedCA    bool
	selfSignedCAOrg string

	caCertTTL          time.Duration
	workloadCertTTL    time.Duration
	maxWorkloadCertTTL time.Duration

	grpcHostname string
	grpcPort     int

	loggingOptions *log.Options

	// The path to the file which indicates the liveness of the server by its existence.
	// This will be used for k8s liveness probe. If empty, it does nothing.
	LivenessProbeOptions *probe.Options
}

var (
	opts = cliOptions{
		loggingOptions:       log.NewOptions(),
		LivenessProbeOptions: &probe.Options{},
	}

	rootCmd = &cobra.Command{
		Use:   "istio_ca",
		Short: "Istio Certificate Authority (CA)",
		Run: func(cmd *cobra.Command, args []string) {
			runCA()
		},
	}
)

func fatalf(template string, args ...interface{}) {
	if len(args) > 0 {
		log.Errorf(template, args)
	} else {
		log.Errorf(template)
	}
	os.Exit(-1)
}

func init() {
	flags := rootCmd.Flags()

	flags.StringVar(&opts.certChainFile, "cert-chain", "", "Speicifies path to the certificate chain file")
	flags.StringVar(&opts.signingCertFile, "signing-cert", "", "Specifies path to the CA signing certificate file")
	flags.StringVar(&opts.signingKeyFile, "signing-key", "", "Specifies path to the CA signing key file")
	flags.StringVar(&opts.rootCertFile, "root-cert", "", "Specifies path to the root certificate file")

	flags.StringVar(&opts.namespace, "namespace", "",
		"Select a namespace for the CA to listen to. If unspecified, Istio CA tries to use the ${"+namespaceKey+"} "+
			"environment variable. If neither is set, Istio CA listens to all namespaces.")
	flags.StringVar(&opts.istioCaStorageNamespace, "istio-ca-storage-namespace", "istio-system", "Namespace where "+
		"the Istio CA pods is running. Will not be used if explicit file or other storage mechanism is specified.")

	flags.StringVar(&opts.kubeConfigFile, "kube-config", "",
		"Specifies path to kubeconfig file. This must be specified when not running inside a Kubernetes pod.")

	flags.BoolVar(&opts.selfSignedCA, "self-signed-ca", false,
		"Indicates whether to use auto-generated self-signed CA certificate. "+
			"When set to true, the '--signing-cert' and '--signing-key' options are ignored.")
	flags.StringVar(&opts.selfSignedCAOrg, "self-signed-ca-org", "k8s.cluster.local",
		fmt.Sprintf("The issuer organization used in self-signed CA certificate (default to %s)",
			selfSignedCAOrgDefault))

	flags.DurationVar(&opts.caCertTTL, "ca-cert-ttl", defaultCACertTTL,
		"The TTL of self-signed CA root certificate")
	flags.DurationVar(&opts.workloadCertTTL, "workload-cert-ttl", defaultWorkloadCertTTL, "The TTL of issued workload certificates")
	flags.DurationVar(&opts.maxWorkloadCertTTL, "max-workload-cert-ttl", defaultMaxWorkloadCertTTL, "The max TTL of issued workload certificates")

	flags.StringVar(&opts.grpcHostname, "grpc-hostname", "localhost", "Specifies the hostname for GRPC server.")
	flags.IntVar(&opts.grpcPort, "grpc-port", 0, "Specifies the port number for GRPC server. "+
		"If unspecified, Istio CA will not server GRPC request.")

	flags.StringVar(&opts.LivenessProbeOptions.Path, "livenessProbePath", "",
		"Path to the file for the liveness probe.")
	flags.DurationVar(&opts.LivenessProbeOptions.UpdateInterval, "livenessProbeInterval", 0,
		"Interval of updating file for the liveness probe.")

	rootCmd.AddCommand(version.CobraCommand())

	rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{
		Title:   "Istio CA",
		Section: "istio_ca CLI",
		Manual:  "Istio CA",
	}))

	opts.loggingOptions.AttachCobraFlags(rootCmd)
	cmd.InitializeFlags(rootCmd)
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		log.Errora(err)
		os.Exit(-1)
	}
}

func runCA() {
	if err := log.Configure(opts.loggingOptions); err != nil {
		fatalf("Failed to configure logging (%v)", err)
	}

	if value, exists := os.LookupEnv(namespaceKey); exists {
		// When -namespace is not set, try to read the namespace from environment variable.
		if opts.namespace == "" {
			opts.namespace = value
		}
		// Use environment variable for istioCaStorageNamespace if it exists
		opts.istioCaStorageNamespace = value
	}

	verifyCommandLineOptions()

	cs := createClientset()
	ca := createCA(cs.CoreV1())
	// For workloads in K8s, we apply the configured workload cert TTL.
	sc := controller.NewSecretController(ca, opts.workloadCertTTL, cs.CoreV1(), opts.namespace)

	stopCh := make(chan struct{})
	sc.Run(stopCh)

	if opts.grpcPort > 0 {
		// start registry if gRPC server is to be started
		reg := registry.GetIdentityRegistry()
		ch := make(chan struct{})

		// monitor service objects with "alpha.istio.io/kubernetes-serviceaccounts" annotation
		serviceController := kube.NewServiceController(cs.CoreV1(), opts.namespace, reg)
		serviceController.Run(ch)

		// monitor service account objects for istio mesh expansion
		serviceAccountController := kube.NewServiceAccountController(cs.CoreV1(), opts.namespace, reg)
		serviceAccountController.Run(ch)

		// The CA API uses cert with the max workload cert TTL.
		grpcServer := grpc.New(ca, opts.maxWorkloadCertTTL, opts.grpcHostname, opts.grpcPort)
		if err := grpcServer.Run(); err != nil {
			// stop the registry-related controllers
			ch <- struct{}{}

			log.Warnf("Failed to start GRPC server with error: %v", err)
		}
	}

	log.Info("Istio CA has started")
	select {} // wait forever
}

func createClientset() *kubernetes.Clientset {
	c := generateConfig()
	cs, err := kubernetes.NewForConfig(c)
	if err != nil {
		fatalf("Failed to create a clientset (error: %s)", err)
	}
	return cs
}

func createCA(core corev1.SecretsGetter) ca.CertificateAuthority {
	var caOpts *ca.IstioCAOptions
	var err error

	if opts.selfSignedCA {
		log.Info("Use self-signed certificate as the CA certificate")

		// TODO(wattli): Refactor this and combine it with NewIstioCA().
		caOpts, err = ca.NewSelfSignedIstioCAOptions(opts.caCertTTL, opts.workloadCertTTL,
			opts.maxWorkloadCertTTL, opts.selfSignedCAOrg, opts.istioCaStorageNamespace, core)
		if err != nil {
			fatalf("Failed to create a self-signed Istio CA (error: %v)", err)
		}
	} else {
		var certChainBytes []byte
		if opts.certChainFile != "" {
			certChainBytes = readFile(opts.certChainFile)
		}
		caOpts = &ca.IstioCAOptions{
			CertChainBytes:   certChainBytes,
			CertTTL:          opts.workloadCertTTL,
			MaxCertTTL:       opts.maxWorkloadCertTTL,
			SigningCertBytes: readFile(opts.signingCertFile),
			SigningKeyBytes:  readFile(opts.signingKeyFile),
			RootCertBytes:    readFile(opts.rootCertFile),
		}
	}

	caOpts.LivenessProbeOptions = opts.LivenessProbeOptions

	istioCA, err := ca.NewIstioCA(caOpts)
	if err != nil {
		log.Errorf("Failed to create an Istio CA (error: %v)", err)
	}
	return istioCA
}

func generateConfig() *rest.Config {
	if opts.kubeConfigFile != "" {
		c, err := clientcmd.BuildConfigFromFlags("", opts.kubeConfigFile)
		if err != nil {
			fatalf("Failed to create a config object from file %s, (error %v)", opts.kubeConfigFile, err)
		}
		return c
	}

	// When `kubeConfigFile` is unspecified, use the in-cluster configuration.
	c, err := rest.InClusterConfig()
	if err != nil {
		fatalf("Failed to create a in-cluster config (error: %s)", err)
	}
	return c
}

func readFile(filename string) []byte {
	bs, err := ioutil.ReadFile(filename)
	if err != nil {
		fatalf("Failed to read file %s (error: %v)", filename, err)
	}
	return bs
}

func verifyCommandLineOptions() {
	if opts.selfSignedCA {
		return
	}

	if opts.signingCertFile == "" {
		fatalf(
			"No signing cert has been specified. Either specify a cert file via '-signing-cert' option " +
				"or use '-self-signed-ca'")
	}

	if opts.signingKeyFile == "" {
		fatalf(
			"No signing key has been specified. Either specify a key file via '-signing-key' option " +
				"or use '-self-signed-ca'")
	}

	if opts.rootCertFile == "" {
		fatalf(
			"No root cert has been specified. Either specify a root cert file via '-root-cert' option " +
				"or use '-self-signed-ca'")
	}
}
<|endoftext|>


<commit_before>package push

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/tomologic/wrench/config"
	"github.com/tomologic/wrench/utils"

	"github.com/spf13/cobra"
)

func AddToWrench(rootCmd *cobra.Command) {
	var flag_additional_tags string

	var cmdBump = &cobra.Command{
		Use:   "push [--additional-tags]",
		Short: "Push project release image to docker registry",
		Long:  `will push release image for current version to specified registry`,
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) == 1 {
				if err := push(args[0], flag_additional_tags); err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
			} else {
				cmd.Usage()
				os.Exit(1)
			}
		},
	}

	cmdBump.Flags().StringVar(&flag_additional_tags, "additional-tags", "", "Comma separated list of additional tags to push 'latest,prod'")

	rootCmd.AddCommand(cmdBump)
}

func remove_empty_strings(s []string) []string {
	var r []string
	for _, str := range s {
		if str != "" {
			r = append(r, str)
		}
	}
	return r
}

func push(registry string, additional_tags string) error {
	tags := strings.Split(additional_tags, ",")
	tags = append(tags, config.GetProjectVersion())
	tags = remove_empty_strings(tags)

	for _, tag := range tags {
		image_name := fmt.Sprintf("%s/%s:%s",
			config.GetProjectOrganization(),
			config.GetProjectName(),
			config.GetProjectVersion())

		registry = strings.Trim(registry, "/")

		tmp_image_name := fmt.Sprintf("%s/%s/%s:%s",
			registry,
			config.GetProjectOrganization(),
			config.GetProjectName(),
			tag)

		// prefix image name with registry
		if err := tag_image(image_name, tmp_image_name); err != nil {
			return err
		}

		// push prefixed image
		push_err := push_image(tmp_image_name)

		// cleanup prefixed images
		cleanup_err := remove_image(tmp_image_name)

		if push_err != nil {
			return push_err
		}

		if cleanup_err != nil {
			return cleanup_err
		}
	}

	return nil
}

func tag_image(image_name string, new_image_name string) error {
	command := fmt.Sprintf(
		"docker tag %s %s",
		image_name,
		new_image_name)

	exitcode, out := utils.RunCmd(command)
	if exitcode != 0 {
		fmt.Fprintln(os.Stderr, out)

		return errors.New(fmt.Sprintf(
			"Could not retag %s to %s",
			image_name,
			new_image_name))
	}

	return nil
}

func push_image(image string) error {
	command := fmt.Sprintf(
		"docker push %s", image)

	exitcode, out := utils.RunCmd(command)
	if exitcode != 0 {
		fmt.Fprintln(os.Stderr, out)

		return errors.New(fmt.Sprintf("Could not push %s", image))
	}

	return nil
}

func remove_image(image string) error {
	command := fmt.Sprintf(
		"docker rmi %s", image)

	exitcode, out := utils.RunCmd(command)
	if exitcode != 0 {
		fmt.Fprintln(os.Stderr, out)

		return errors.New(fmt.Sprintf(
			"Could not remove %s", image))
	}

	return nil
}
<commit_msg>Docker requires force when reusing tags<commit_after>package push

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/tomologic/wrench/config"
	"github.com/tomologic/wrench/utils"

	"github.com/spf13/cobra"
)

func AddToWrench(rootCmd *cobra.Command) {
	var flag_additional_tags string

	var cmdBump = &cobra.Command{
		Use:   "push [--additional-tags]",
		Short: "Push project release image to docker registry",
		Long:  `will push release image for current version to specified registry`,
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) == 1 {
				if err := push(args[0], flag_additional_tags); err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
			} else {
				cmd.Usage()
				os.Exit(1)
			}
		},
	}

	cmdBump.Flags().StringVar(&flag_additional_tags, "additional-tags", "", "Comma separated list of additional tags to push 'latest,prod'")

	rootCmd.AddCommand(cmdBump)
}

func remove_empty_strings(s []string) []string {
	var r []string
	for _, str := range s {
		if str != "" {
			r = append(r, str)
		}
	}
	return r
}

func push(registry string, additional_tags string) error {
	tags := strings.Split(additional_tags, ",")
	tags = append(tags, config.GetProjectVersion())
	tags = remove_empty_strings(tags)

	for _, tag := range tags {
		image_name := fmt.Sprintf("%s/%s:%s",
			config.GetProjectOrganization(),
			config.GetProjectName(),
			config.GetProjectVersion())

		registry = strings.Trim(registry, "/")

		tmp_image_name := fmt.Sprintf("%s/%s/%s:%s",
			registry,
			config.GetProjectOrganization(),
			config.GetProjectName(),
			tag)

		// prefix image name with registry
		if err := tag_image(image_name, tmp_image_name); err != nil {
			return err
		}

		// push prefixed image
		push_err := push_image(tmp_image_name)

		// cleanup prefixed images
		cleanup_err := remove_image(tmp_image_name)

		if push_err != nil {
			return push_err
		}

		if cleanup_err != nil {
			return cleanup_err
		}
	}

	return nil
}

func tag_image(image_name string, new_image_name string) error {
	command := fmt.Sprintf(
		"docker tag -f %s %s",
		image_name,
		new_image_name)

	exitcode, out := utils.RunCmd(command)
	if exitcode != 0 {
		fmt.Fprintln(os.Stderr, out)

		return errors.New(fmt.Sprintf(
			"Could not retag %s to %s",
			image_name,
			new_image_name))
	}

	return nil
}

func push_image(image string) error {
	command := fmt.Sprintf(
		"docker push %s", image)

	exitcode, out := utils.RunCmd(command)
	if exitcode != 0 {
		fmt.Fprintln(os.Stderr, out)

		return errors.New(fmt.Sprintf("Could not push %s", image))
	}

	return nil
}

func remove_image(image string) error {
	command := fmt.Sprintf(
		"docker rmi %s", image)

	exitcode, out := utils.RunCmd(command)
	if exitcode != 0 {
		fmt.Fprintln(os.Stderr, out)

		return errors.New(fmt.Sprintf(
			"Could not remove %s", image))
	}

	return nil
}
<|endoftext|>


<commit_before>package etcdclient

import (
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/client"
)

// EtcdClient interface lets your Get/Set from Etcd
type EtcdClient interface {
	Del(key string) error
	Get(key string) (string, error)
	Set(key, value string) error
	Ls(directory string) ([]string, error)
	LsRecursive(directory string) ([]string, error)
}

// SimpleEtcdClient implements EtcdClient
type SimpleEtcdClient struct {
	etcd client.Client
}

// Dial constructs a new EtcdClient
func Dial(etcdURI string) (EtcdClient, error) {
	etcd, err := client.New(client.Config{
		Endpoints: []string{etcdURI},
	})
	if err != nil {
		return nil, err
	}
	return &SimpleEtcdClient{etcd}, nil
}

// Del deletes a key from Etcd
func (etcdClient *SimpleEtcdClient) Del(key string) error {
	api := client.NewKeysAPI(etcdClient.etcd)
	_, err := api.Delete(context.Background(), key, nil)
	if err != nil {
		if client.IsKeyNotFound(err) {
			return nil
		}
	}
	return err
}

// Get gets a value in Etcd
func (etcdClient *SimpleEtcdClient) Get(key string) (string, error) {
	api := client.NewKeysAPI(etcdClient.etcd)
	response, err := api.Get(context.Background(), key, nil)
	if err != nil {
		if client.IsKeyNotFound(err) {
			return "", nil
		}
		return "", err
	}
	return response.Node.Value, nil
}

// Set sets a value in Etcd
func (etcdClient *SimpleEtcdClient) Set(key, value string) error {
	api := client.NewKeysAPI(etcdClient.etcd)
	_, err := api.Set(context.Background(), key, value, nil)
	return err
}

// Ls returns all the keys available in the directory
func (etcdClient *SimpleEtcdClient) Ls(directory string) ([]string, error) {
	api := client.NewKeysAPI(etcdClient.etcd)
	options := &client.GetOptions{Sort: true, Recursive: false}
	response, err := api.Get(context.Background(), directory, options)

	if err != nil {
		if client.IsKeyNotFound(err) {
			return make([]string, 0), nil
		}
		return make([]string, 0), err
	}

	return nodesToStringSlice(response.Node.Nodes), nil
}

// LsRecursive returns all the keys available in the directory, recursively
func (etcdClient *SimpleEtcdClient) LsRecursive(directory string) ([]string, error) {
	api := client.NewKeysAPI(etcdClient.etcd)
	options := &client.GetOptions{Sort: true, Recursive: true}
	response, err := api.Get(context.Background(), directory, options)

	if err != nil {
		if client.IsKeyNotFound(err) {
			return make([]string, 0), nil
		}
		return make([]string, 0), err
	}

	return nodesToStringSlice(response.Node.Nodes), nil
}

func nodesToStringSlice(nodes client.Nodes) []string {
	var keys []string

	for _, node := range nodes {
		keys = append(keys, node.Key)

		for _, key := range nodesToStringSlice(node.Nodes) {
			keys = append(keys, key)
		}
	}

	return keys
}
<commit_msg>Add DelDir, v2.4.0<commit_after>package etcdclient

import (
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/client"
)

// EtcdClient interface lets your Get/Set from Etcd
type EtcdClient interface {
	Del(key string) error
	DelDir(key string) error
	Get(key string) (string, error)
	Set(key, value string) error
	Ls(directory string) ([]string, error)
	LsRecursive(directory string) ([]string, error)
}

// SimpleEtcdClient implements EtcdClient
type SimpleEtcdClient struct {
	etcd client.Client
}

// Dial constructs a new EtcdClient
func Dial(etcdURI string) (EtcdClient, error) {
	etcd, err := client.New(client.Config{
		Endpoints: []string{etcdURI},
	})
	if err != nil {
		return nil, err
	}
	return &SimpleEtcdClient{etcd}, nil
}

// Del deletes a key from Etcd
func (etcdClient *SimpleEtcdClient) Del(key string) error {
	api := client.NewKeysAPI(etcdClient.etcd)
	_, err := api.Delete(context.Background(), key, nil)
	if err != nil {
		if client.IsKeyNotFound(err) {
			return nil
		}
	}
	return err
}

// DelDir deletes a dir from Etcd
func (etcdClient *SimpleEtcdClient) DelDir(key string) error {
	api := client.NewKeysAPI(etcdClient.etcd)
	_, err := api.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
	if err != nil {
		if client.IsKeyNotFound(err) {
			return nil
		}
	}
	return err
}

// Get gets a value in Etcd
func (etcdClient *SimpleEtcdClient) Get(key string) (string, error) {
	api := client.NewKeysAPI(etcdClient.etcd)
	response, err := api.Get(context.Background(), key, nil)
	if err != nil {
		if client.IsKeyNotFound(err) {
			return "", nil
		}
		return "", err
	}
	return response.Node.Value, nil
}

// Set sets a value in Etcd
func (etcdClient *SimpleEtcdClient) Set(key, value string) error {
	api := client.NewKeysAPI(etcdClient.etcd)
	_, err := api.Set(context.Background(), key, value, nil)
	return err
}

// Ls returns all the keys available in the directory
func (etcdClient *SimpleEtcdClient) Ls(directory string) ([]string, error) {
	api := client.NewKeysAPI(etcdClient.etcd)
	options := &client.GetOptions{Sort: true, Recursive: false}
	response, err := api.Get(context.Background(), directory, options)

	if err != nil {
		if client.IsKeyNotFound(err) {
			return make([]string, 0), nil
		}
		return make([]string, 0), err
	}

	return nodesToStringSlice(response.Node.Nodes), nil
}

// LsRecursive returns all the keys available in the directory, recursively
func (etcdClient *SimpleEtcdClient) LsRecursive(directory string) ([]string, error) {
	api := client.NewKeysAPI(etcdClient.etcd)
	options := &client.GetOptions{Sort: true, Recursive: true}
	response, err := api.Get(context.Background(), directory, options)

	if err != nil {
		if client.IsKeyNotFound(err) {
			return make([]string, 0), nil
		}
		return make([]string, 0), err
	}

	return nodesToStringSlice(response.Node.Nodes), nil
}

func nodesToStringSlice(nodes client.Nodes) []string {
	var keys []string

	for _, node := range nodes {
		keys = append(keys, node.Key)

		for _, key := range nodesToStringSlice(node.Nodes) {
			keys = append(keys, key)
		}
	}

	return keys
}
<|endoftext|>


<commit_before>/*
 * yubo@yubo.org
 * 2015-05-13
 */
package rrddb

/*
#include <stdlib.h>
#include "cgo_rrddb.h"
#cgo CFLAGS: -std=c99 -D_XOPEN_SOURCE=600 -D__DBINTERFACE_PRIVATE -DRRD_LITE -D_BSD_SOURCE -DHAVE_CONFIG_H -D_POSIX_SOURCE -DNUMVERS=1.4009
#cgo LDFLAGS: -lm
*/
import "C"

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"
)

const (
	maxUint         = ^uint(0)
	maxInt          = int(maxUint >> 1)
	minInt          = -maxInt - 1
	RET_ERROR       = -1 /* Return values. */
	RET_SUCCESS     = 0
	RET_SPECIAL     = 1
	MAX_PAGE_NUMBER = 0xffffffff /* >= # of pages in a file */
	MAX_PAGE_OFFSET = 65535      /* >= # of bytes in a page */
	MAX_REC_NUMBER  = 0xffffffff /* >= # of records in a tree */
	R_CURSOR        = 1          /* del, put, seq */
	__R_UNUSED      = 2          /* UNUSED */
	R_FIRST         = 3          /* seq */
	R_IAFTER        = 4          /* put (RECNO) */
	R_IBEFORE       = 5          /* put (RECNO) */
	R_LAST          = 6          /* seq (BTREE, RECNO) */
	R_NEXT          = 7          /* seq */
	R_NOOVERWRITE   = 8          /* put */
	R_PREV          = 9          /* seq (BTREE, RECNO) */
	R_SETCURSOR     = 10         /* put (RECNO) */
	R_RECNOSYNC     = 11         /* sync (RECNO) */
	DB_BTREE        = 0
	DB_HASH         = 1
	DB_RECNO        = 2
	DB_LOCK         = 0x20000000 /* Do locking. */
	DB_SHMEM        = 0x40000000 /* Use shared memory. */
	DB_TXN          = 0x80000000 /* Do transactions. */
	BTREEMAGIC      = 0x053162
	BTREEVERSION    = 3
	HASHMAGIC       = 0x061561
	HASHVERSION     = 2
	R_FIXEDLEN      = 0x01 /* fixed-length records */
	R_NOKEY         = 0x02 /* key not required */
	R_SNAPSHOT      = 0x04 /* snapshot the input */
)

var mutex sync.Mutex
var (
	oStart           = C.CString("-s")
	oEnd             = C.CString("-e")
	oTitle           = C.CString("-t")
	oVlabel          = C.CString("-v")
	oWidth           = C.CString("-w")
	oHeight          = C.CString("-h")
	oUpperLimit      = C.CString("-u")
	oLowerLimit      = C.CString("-l")
	oRigid           = C.CString("-r")
	oAltAutoscale    = C.CString("-A")
	oAltAutoscaleMin = C.CString("-J")
	oAltAutoscaleMax = C.CString("-M")
	oNoGridFit       = C.CString("-N")

	oLogarithmic   = C.CString("-o")
	oUnitsExponent = C.CString("-X")
	oUnitsLength   = C.CString("-L")

	oRightAxis      = C.CString("--right-axis")
	oRightAxisLabel = C.CString("--right-axis-label")

	oDaemon = C.CString("--daemon")

	oNoLegend = C.CString("-g")

	oLazy = C.CString("-z")

	oColor = C.CString("-c")

	oSlopeMode   = C.CString("-E")
	oImageFormat = C.CString("-a")
	oInterlaced  = C.CString("-i")

	oBase      = C.CString("-b")
	oWatermark = C.CString("-W")

	oStep    = C.CString("--step")
	oMaxRows = C.CString("-m")
)

type Error string

type Rrddb struct {
	arname string
	dbname string
	dbtype string
	dbinfo string
	dblock int
	p      unsafe.Pointer
}

type Creator struct {
	filename string
	start    time.Time
	step     uint
	args     []string
	rd       *Rrddb
}
type Updater struct {
	filename cstring
	template cstring
	offset   int64
	size     int64
	args     []unsafe.Pointer
	rd       *Rrddb
}
type FetchResult struct {
	Filename string
	Cf       string
	Start    time.Time
	End      time.Time
	Step     time.Duration
	DsNames  []string
	RowCnt   int
	values   []float64
}

/*
type Archive struct {
	Filename string
	ar       unsafe.Pointer
}
*/
type cstring []byte

/* function */
func (e Error) Error() string {
	return string(e)
}

func newCstring(s string) cstring {
	cs := make(cstring, len(s)+1)
	copy(cs, s)
	return cs
}

func (cs cstring) p() unsafe.Pointer {
	if len(cs) == 0 {
		return nil
	}
	return unsafe.Pointer(&cs[0])
}

func (cs cstring) String() string {
	return string(cs[:len(cs)-1])
}

func (d *Rrddb) String() string {
	return fmt.Sprintf("db[0x%08x] arname[%s] dbname[%s] type[%s] info[%s] lock[%d]",
		d.p, d.arname, d.dbname, d.dbtype, d.dbinfo, d.dblock)
}

func join(args []interface{}) string {
	sa := make([]string, len(args))
	for i, a := range args {
		var s string
		switch v := a.(type) {
		case time.Time:
			s = i64toa(v.Unix())
		default:
			s = fmt.Sprint(v)
		}
		sa[i] = s
	}
	return strings.Join(sa, ":")
}

func makeArgs(args []string) []*C.char {
	ret := make([]*C.char, len(args))
	for i, s := range args {
		ret[i] = C.CString(s)
	}
	return ret
}

func freeCString(s *C.char) {
	C.free(unsafe.Pointer(s))
}

func freeArgs(cArgs []*C.char) {
	for _, s := range cArgs {
		freeCString(s)
	}
}

func makeError(e *C.char) error {
	var null *C.char
	if e == null {
		return nil
	}
	return Error(C.GoString(e))
}

func ftoa(f float64) string {
	return strconv.FormatFloat(f, 'e', 10, 64)
}

func ftoc(f float64) *C.char {
	return C.CString(ftoa(f))
}

func i64toa(i int64) string {
	return strconv.FormatInt(i, 10)
}

func i64toc(i int64) *C.char {
	return C.CString(i64toa(i))
}

func u64toa(u uint64) string {
	return strconv.FormatUint(u, 10)
}

func u64toc(u uint64) *C.char {
	return C.CString(u64toa(u))
}
func itoa(i int) string {
	return i64toa(int64(i))
}

func itoc(i int) *C.char {
	return i64toc(int64(i))
}

func utoa(u uint) string {
	return u64toa(uint64(u))
}

func utoc(u uint) *C.char {
	return u64toc(uint64(u))
}

func parseInfoKey(ik string) (kname, kkey string, kid int) {
	kid = -1
	o := strings.IndexRune(ik, '[')
	if o == -1 {
		kname = ik
		return
	}
	c := strings.IndexRune(ik[o+1:], ']')
	if c == -1 {
		kname = ik
		return
	}
	c += o + 1
	kname = ik[:o] + ik[c+1:]
	kkey = ik[o+1 : c]
	if strings.HasPrefix(kname, "ds.") {
		return
	} else if id, err := strconv.Atoi(kkey); err == nil && id >= 0 {
		kid = id
	}
	return
}

func updateInfoValue(i *C.struct_rrd_info_t, v interface{}) interface{} {
	switch i._type {
	case C.RD_I_VAL:
		return float64(*(*C.rrd_value_t)(unsafe.Pointer(&i.value[0])))
	case C.RD_I_CNT:
		return uint(*(*C.ulong)(unsafe.Pointer(&i.value[0])))
	case C.RD_I_STR:
		return C.GoString(*(**C.char)(unsafe.Pointer(&i.value[0])))
	case C.RD_I_INT:
		return int(*(*C.int)(unsafe.Pointer(&i.value[0])))
	case C.RD_I_BLO:
		blob := *(*C.rrd_blob_t)(unsafe.Pointer(&i.value[0]))
		b := C.GoBytes(unsafe.Pointer(blob.ptr), C.int(blob.size))
		if v == nil {
			return b
		}
		return append(v.([]byte), b...)
	}

	return nil
}

func parseRRDInfo(i *C.rrd_info_t) map[string]interface{} {
	defer C.rrd_info_free(i)

	r := make(map[string]interface{})
	for w := (*C.struct_rrd_info_t)(i); w != nil; w = w.next {
		kname, kkey, kid := parseInfoKey(C.GoString(w.key))
		v, ok := r[kname]
		switch {
		case kid != -1:
			var a []interface{}
			if ok {
				a = v.([]interface{})
			}
			if len(a) < kid+1 {
				oldA := a
				a = make([]interface{}, kid+1)
				copy(a, oldA)
			}
			a[kid] = updateInfoValue(w, a[kid])
			v = a
		case kkey != "":
			var m map[string]interface{}
			if ok {
				m = v.(map[string]interface{})
			} else {
				m = make(map[string]interface{})
			}
			old, _ := m[kkey]
			m[kkey] = updateInfoValue(w, old)
			v = m
		default:
			v = updateInfoValue(w, v)
		}
		r[kname] = v
	}
	return r
}
<commit_msg>fix build error<commit_after>/*
 * yubo@yubo.org
 * 2015-05-13
 */
package rrddb

/*
#include <stdlib.h>
#include "cgo_rrddb.h"
#cgo CFLAGS: -std=c99 -D__DBINTERFACE_PRIVATE -DRRD_LITE -D_BSD_SOURCE -DHAVE_CONFIG_H -D_POSIX_SOURCE -DNUMVERS=1.4009
#cgo LDFLAGS: -lm
*/
import "C"

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"
)

const (
	maxUint         = ^uint(0)
	maxInt          = int(maxUint >> 1)
	minInt          = -maxInt - 1
	RET_ERROR       = -1 /* Return values. */
	RET_SUCCESS     = 0
	RET_SPECIAL     = 1
	MAX_PAGE_NUMBER = 0xffffffff /* >= # of pages in a file */
	MAX_PAGE_OFFSET = 65535      /* >= # of bytes in a page */
	MAX_REC_NUMBER  = 0xffffffff /* >= # of records in a tree */
	R_CURSOR        = 1          /* del, put, seq */
	__R_UNUSED      = 2          /* UNUSED */
	R_FIRST         = 3          /* seq */
	R_IAFTER        = 4          /* put (RECNO) */
	R_IBEFORE       = 5          /* put (RECNO) */
	R_LAST          = 6          /* seq (BTREE, RECNO) */
	R_NEXT          = 7          /* seq */
	R_NOOVERWRITE   = 8          /* put */
	R_PREV          = 9          /* seq (BTREE, RECNO) */
	R_SETCURSOR     = 10         /* put (RECNO) */
	R_RECNOSYNC     = 11         /* sync (RECNO) */
	DB_BTREE        = 0
	DB_HASH         = 1
	DB_RECNO        = 2
	DB_LOCK         = 0x20000000 /* Do locking. */
	DB_SHMEM        = 0x40000000 /* Use shared memory. */
	DB_TXN          = 0x80000000 /* Do transactions. */
	BTREEMAGIC      = 0x053162
	BTREEVERSION    = 3
	HASHMAGIC       = 0x061561
	HASHVERSION     = 2
	R_FIXEDLEN      = 0x01 /* fixed-length records */
	R_NOKEY         = 0x02 /* key not required */
	R_SNAPSHOT      = 0x04 /* snapshot the input */
)

var mutex sync.Mutex
var (
	oStart           = C.CString("-s")
	oEnd             = C.CString("-e")
	oTitle           = C.CString("-t")
	oVlabel          = C.CString("-v")
	oWidth           = C.CString("-w")
	oHeight          = C.CString("-h")
	oUpperLimit      = C.CString("-u")
	oLowerLimit      = C.CString("-l")
	oRigid           = C.CString("-r")
	oAltAutoscale    = C.CString("-A")
	oAltAutoscaleMin = C.CString("-J")
	oAltAutoscaleMax = C.CString("-M")
	oNoGridFit       = C.CString("-N")

	oLogarithmic   = C.CString("-o")
	oUnitsExponent = C.CString("-X")
	oUnitsLength   = C.CString("-L")

	oRightAxis      = C.CString("--right-axis")
	oRightAxisLabel = C.CString("--right-axis-label")

	oDaemon = C.CString("--daemon")

	oNoLegend = C.CString("-g")

	oLazy = C.CString("-z")

	oColor = C.CString("-c")

	oSlopeMode   = C.CString("-E")
	oImageFormat = C.CString("-a")
	oInterlaced  = C.CString("-i")

	oBase      = C.CString("-b")
	oWatermark = C.CString("-W")

	oStep    = C.CString("--step")
	oMaxRows = C.CString("-m")
)

type Error string

type Rrddb struct {
	arname string
	dbname string
	dbtype string
	dbinfo string
	dblock int
	p      unsafe.Pointer
}

type Creator struct {
	filename string
	start    time.Time
	step     uint
	args     []string
	rd       *Rrddb
}
type Updater struct {
	filename cstring
	template cstring
	offset   int64
	size     int64
	args     []unsafe.Pointer
	rd       *Rrddb
}
type FetchResult struct {
	Filename string
	Cf       string
	Start    time.Time
	End      time.Time
	Step     time.Duration
	DsNames  []string
	RowCnt   int
	values   []float64
}

/*
type Archive struct {
	Filename string
	ar       unsafe.Pointer
}
*/
type cstring []byte

/* function */
func (e Error) Error() string {
	return string(e)
}

func newCstring(s string) cstring {
	cs := make(cstring, len(s)+1)
	copy(cs, s)
	return cs
}

func (cs cstring) p() unsafe.Pointer {
	if len(cs) == 0 {
		return nil
	}
	return unsafe.Pointer(&cs[0])
}

func (cs cstring) String() string {
	return string(cs[:len(cs)-1])
}

func (d *Rrddb) String() string {
	return fmt.Sprintf("db[0x%08x] arname[%s] dbname[%s] type[%s] info[%s] lock[%d]",
		d.p, d.arname, d.dbname, d.dbtype, d.dbinfo, d.dblock)
}

func join(args []interface{}) string {
	sa := make([]string, len(args))
	for i, a := range args {
		var s string
		switch v := a.(type) {
		case time.Time:
			s = i64toa(v.Unix())
		default:
			s = fmt.Sprint(v)
		}
		sa[i] = s
	}
	return strings.Join(sa, ":")
}

func makeArgs(args []string) []*C.char {
	ret := make([]*C.char, len(args))
	for i, s := range args {
		ret[i] = C.CString(s)
	}
	return ret
}

func freeCString(s *C.char) {
	C.free(unsafe.Pointer(s))
}

func freeArgs(cArgs []*C.char) {
	for _, s := range cArgs {
		freeCString(s)
	}
}

func makeError(e *C.char) error {
	var null *C.char
	if e == null {
		return nil
	}
	return Error(C.GoString(e))
}

func ftoa(f float64) string {
	return strconv.FormatFloat(f, 'e', 10, 64)
}

func ftoc(f float64) *C.char {
	return C.CString(ftoa(f))
}

func i64toa(i int64) string {
	return strconv.FormatInt(i, 10)
}

func i64toc(i int64) *C.char {
	return C.CString(i64toa(i))
}

func u64toa(u uint64) string {
	return strconv.FormatUint(u, 10)
}

func u64toc(u uint64) *C.char {
	return C.CString(u64toa(u))
}
func itoa(i int) string {
	return i64toa(int64(i))
}

func itoc(i int) *C.char {
	return i64toc(int64(i))
}

func utoa(u uint) string {
	return u64toa(uint64(u))
}

func utoc(u uint) *C.char {
	return u64toc(uint64(u))
}

func parseInfoKey(ik string) (kname, kkey string, kid int) {
	kid = -1
	o := strings.IndexRune(ik, '[')
	if o == -1 {
		kname = ik
		return
	}
	c := strings.IndexRune(ik[o+1:], ']')
	if c == -1 {
		kname = ik
		return
	}
	c += o + 1
	kname = ik[:o] + ik[c+1:]
	kkey = ik[o+1 : c]
	if strings.HasPrefix(kname, "ds.") {
		return
	} else if id, err := strconv.Atoi(kkey); err == nil && id >= 0 {
		kid = id
	}
	return
}

func updateInfoValue(i *C.struct_rrd_info_t, v interface{}) interface{} {
	switch i._type {
	case C.RD_I_VAL:
		return float64(*(*C.rrd_value_t)(unsafe.Pointer(&i.value[0])))
	case C.RD_I_CNT:
		return uint(*(*C.ulong)(unsafe.Pointer(&i.value[0])))
	case C.RD_I_STR:
		return C.GoString(*(**C.char)(unsafe.Pointer(&i.value[0])))
	case C.RD_I_INT:
		return int(*(*C.int)(unsafe.Pointer(&i.value[0])))
	case C.RD_I_BLO:
		blob := *(*C.rrd_blob_t)(unsafe.Pointer(&i.value[0]))
		b := C.GoBytes(unsafe.Pointer(blob.ptr), C.int(blob.size))
		if v == nil {
			return b
		}
		return append(v.([]byte), b...)
	}

	return nil
}

func parseRRDInfo(i *C.rrd_info_t) map[string]interface{} {
	defer C.rrd_info_free(i)

	r := make(map[string]interface{})
	for w := (*C.struct_rrd_info_t)(i); w != nil; w = w.next {
		kname, kkey, kid := parseInfoKey(C.GoString(w.key))
		v, ok := r[kname]
		switch {
		case kid != -1:
			var a []interface{}
			if ok {
				a = v.([]interface{})
			}
			if len(a) < kid+1 {
				oldA := a
				a = make([]interface{}, kid+1)
				copy(a, oldA)
			}
			a[kid] = updateInfoValue(w, a[kid])
			v = a
		case kkey != "":
			var m map[string]interface{}
			if ok {
				m = v.(map[string]interface{})
			} else {
				m = make(map[string]interface{})
			}
			old, _ := m[kkey]
			m[kkey] = updateInfoValue(w, old)
			v = m
		default:
			v = updateInfoValue(w, v)
		}
		r[kname] = v
	}
	return r
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/franela/goblin"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"net/http"
	"testing"
)

type ResponseBodyGenerator interface {
	Generate() string
}

type FakeResponseBodyGenerator struct {
	use string
}

func (instance *FakeResponseBodyGenerator) Generate() string {
	return instance.use
}

func (instance *FakeResponseBodyGenerator) Use(value string) {
	instance.use = value
}

func NewFakeResponseBodyGenerator() *FakeResponseBodyGenerator {
	return &FakeResponseBodyGenerator{""}
}

func Test_Enanos(t *testing.T) {
	g := goblin.Goblin(t)
	g.Describe("Enanos Server:", func() {

		var fakeResponseBodyGenerator *FakeResponseBodyGenerator

		url := func(path string) (fullPath string) {
			fullPath = "http://localhost:8000" + path
			return
		}

		g.BeforeEach(func() {
			fakeResponseBodyGenerator = NewFakeResponseBodyGenerator()

			go func() {
				defaultHappy := func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusOK)
				}
				defaultGrumpy := func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusInternalServerError)
				}
				defaultSneezy := func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusOK)
					w.Write([]byte(fakeResponseBodyGenerator.Generate()))
				}
				mux := http.NewServeMux()
				mux.HandleFunc("/default/happy", defaultHappy)
				mux.HandleFunc("/default/grumpy", defaultGrumpy)
				mux.HandleFunc("/default/sneezy", defaultSneezy)
				err := http.ListenAndServe(":8000", mux)
				if err != nil {
					fmt.Errorf("error encountered %v", err)
				}
			}()
		})

		g.Describe("Happy :", func() {
			g.It("GET returns 200", func() {
				resp, _ := http.Get(url("/default/happy"))
				assert.Equal(t, http.StatusOK, resp.StatusCode)
			})
		})

		g.Describe("Grumpy :", func() {
			g.It("GET returns 500", func() {
				resp, _ := http.Get(url("/default/grumpy"))
				assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
			})
		})

		g.Describe("Sneezy :", func() {
			g.It("GET returns 200", func() {
				resp, _ := http.Get(url("/default/sneezy"))
				assert.Equal(t, http.StatusOK, resp.StatusCode)
			})

			g.It("GET returns random response body", func() {
				sample := "foobar"
				fakeResponseBodyGenerator.Use(sample)
				resp, _ := http.Get(url("/default/sneezy"))
				defer resp.Body.Close()
				body, _ := ioutil.ReadAll(resp.Body)
				assert.Equal(t, sample, string(body))
			})
		})
	})
}
<commit_msg>Extracted the logic for the handlers into an interface and wrapped their calls inside the test setup<commit_after>package main

import (
	"fmt"
	"github.com/franela/goblin"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"net/http"
	"testing"
)

type ResponseBodyGenerator interface {
	Generate() string
}

type FakeResponseBodyGenerator struct {
	use string
	val int
}

func (instance *FakeResponseBodyGenerator) Generate() string {
	return instance.use
}

func (instance *FakeResponseBodyGenerator) UseString(value string) {
	instance.use = value
}

func NewFakeResponseBodyGenerator() *FakeResponseBodyGenerator {
	return &FakeResponseBodyGenerator{"", 1}
}

type DefaultEnanosHttpHandlerFactory struct {
	responseBodyGenerator ResponseBodyGenerator
}

func (instance *DefaultEnanosHttpHandlerFactory) Happy(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func (instance *DefaultEnanosHttpHandlerFactory) Grumpy(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusInternalServerError)
}

func (instance *DefaultEnanosHttpHandlerFactory) Sneezy(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	data := instance.responseBodyGenerator.Generate()
	w.Write([]byte(data))
}

func NewDefaultEnanosHttpHandlerFactory(responseBodyGenerator ResponseBodyGenerator) *DefaultEnanosHttpHandlerFactory {
	return &DefaultEnanosHttpHandlerFactory{responseBodyGenerator}
}

func Test_Enanos(t *testing.T) {
	g := goblin.Goblin(t)
	g.Describe("Enanos Server:", func() {

		var fakeResponseBodyGenerator *FakeResponseBodyGenerator
		var enanosHttpHandlerFactory *DefaultEnanosHttpHandlerFactory

		url := func(path string) (fullPath string) {
			fullPath = "http://localhost:8000" + path
			return
		}

		g.BeforeEach(func() {
			go func() {
				fakeResponseBodyGenerator = NewFakeResponseBodyGenerator()
				enanosHttpHandlerFactory = NewDefaultEnanosHttpHandlerFactory(fakeResponseBodyGenerator)
				mux := http.NewServeMux()
				mux.HandleFunc("/default/happy", func(writer http.ResponseWriter, request *http.Request) {
					enanosHttpHandlerFactory.Happy(writer, request)
				})
				mux.HandleFunc("/default/grumpy", func(writer http.ResponseWriter, request *http.Request) {
					enanosHttpHandlerFactory.Grumpy(writer, request)
				})
				mux.HandleFunc("/default/sneezy", func(writer http.ResponseWriter, request *http.Request) {
					enanosHttpHandlerFactory.Sneezy(writer, request)
				})
				err := http.ListenAndServe(":8000", mux)
				if err != nil {
					fmt.Errorf("error encountered %v", err)
				}
			}()
		})

		g.Describe("Happy :", func() {
			g.It("GET returns 200", func() {
				resp, _ := http.Get(url("/default/happy"))
				assert.Equal(t, http.StatusOK, resp.StatusCode)
			})
		})

		g.Describe("Grumpy :", func() {
			g.It("GET returns 500", func() {
				resp, _ := http.Get(url("/default/grumpy"))
				assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
			})
		})

		g.Describe("Sneezy :", func() {
			g.It("GET returns 200", func() {
				resp, _ := http.Get(url("/default/sneezy"))
				assert.Equal(t, http.StatusOK, resp.StatusCode)
			})

			g.It("GET returns random response body", func() {
				sample := "foobar"
				fakeResponseBodyGenerator.UseString(sample)
				resp, _ := http.Get(url("/default/sneezy"))
				defer resp.Body.Close()
				body, _ := ioutil.ReadAll(resp.Body)
				assert.Equal(t, sample, string(body))
			})
		})
	})
}
<|endoftext|>