<commit_before>package messaging

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"
)

// DefaultReconnectTimeout is the default time to wait between when a broker
// stream disconnects and another connection is retried.
const DefaultReconnectTimeout = 100 * time.Millisecond

// ClientConfig represents the Client configuration that must be persisted
// across restarts.
type ClientConfig struct {
	Brokers []*url.URL `json:"brokers"`
}

// NewClientConfig returns a new instance of ClientConfig.
func NewClientConfig(u []*url.URL) *ClientConfig {
	return &ClientConfig{
		Brokers: u,
	}
}

// Client represents a client for the broker's HTTP API.
// Once opened, the client will stream down all messages that
type Client struct {
	mu        sync.Mutex
	replicaID uint64       // the replica that the client is connecting as.
	config    ClientConfig // The Client state that must be persisted to disk.

	opened bool
	done   chan chan struct{} // disconnection notification

	// Channel streams messages from the broker.
	c chan *Message

	// The amount of time to wait before reconnecting to a broker stream.
	ReconnectTimeout time.Duration

	// The logging interface used by the client for out-of-band errors.
	Logger *log.Logger
}

// NewClient returns a new instance of Client.
func NewClient(replicaID uint64) *Client {
	return &Client{
		replicaID:        replicaID,
		ReconnectTimeout: DefaultReconnectTimeout,
		Logger:           log.New(os.Stderr, "[messaging] ", log.LstdFlags),
	}
}

// ReplicaID returns the replica id that the client was opened with.
func (c *Client) ReplicaID() uint64 { return c.replicaID }

// C returns streaming channel.
// Messages can be duplicated so it is important to check the index
// of the incoming message index to make sure it has not been processed.
func (c *Client) C() <-chan *Message { return c.c }

// URLs returns a list of broker URLs to connect to.
func (c *Client) URLs() []*url.URL {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.config.Brokers
}

// LeaderURL returns the URL of the broker leader.
func (c *Client) LeaderURL() *url.URL {
	c.mu.Lock()
	defer c.mu.Unlock()

	// TODO(benbjohnson): Actually keep track of the leader.
	// HACK(benbjohnson): For testing, just grab a url.
	return c.config.Brokers[0]
}

// SetLogOutput sets writer for all Client log output.
func (c *Client) SetLogOutput(w io.Writer) {
	c.Logger = log.New(w, "[messaging] ", log.LstdFlags)
}

// Open initializes and opens the connection to the cluster. The
// URLs used to contact the cluster are either those supplied to
// the function, or if none are supplied, are read from the file
// at "path". These URLs do need to be URLs of actual Brokers.
// Regardless of URL source, at least 1 URL must be available
// for the client to be successfully opened.
func (c *Client) Open(path string, urls []*url.URL) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Return error if the client is already open.
	if c.opened {
		return ErrClientOpen
	}

	// Read URLs from file if no URLs are provided.
	if len(urls) == 0 {
		// Read URLs from config file. There is no guarantee
		// that the Brokers URLs in the config file are still
		// the Brokers, so we're going to double-check.
		b, err := ioutil.ReadFile(path)
		if os.IsNotExist(err) {
			// nop
		} else if err != nil {
			return err
		} else {
			if err := json.Unmarshal(b, &c.config); err != nil {
				return err
			}
			urls = c.config.Brokers
		}
	}

	if len(urls) < 1 {
		return ErrBrokerURLRequired
	}

	// Now that we have the seed URLs, actually use these to
	// get the actual Broker URLs. Do that here.
	c.config.Brokers = urls // Let's pretend they are the same

	// Create a channel for streaming messages.
	c.c = make(chan *Message, 0)

	// Open the streamer if there's an ID set.
	if c.replicaID != 0 {
		c.done = make(chan chan struct{})
		go c.streamer(c.done)
	}

	// Set open flag.
	c.opened = true

	return nil
}

// Close disconnects the client from the broker cluster.
func (c *Client) Close() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Return error if the client is already closed.
	if !c.opened {
		return ErrClientClosed
	}

	// Shutdown streamer.
	if c.done != nil {
		ch := make(chan struct{})
		c.done <- ch
		<-ch
		c.done = nil
	}

	// Close message stream.
	close(c.c)
	c.c = nil

	// Unset open flag.
	c.opened = false

	return nil
}

// Publish sends a message to the broker and returns an index or error.
func (c *Client) Publish(m *Message) (uint64, error) {
	var resp *http.Response
	var err error

	u := *c.LeaderURL()
	for {
		// Send the message to the messages endpoint.
		u.Path = "/messaging/messages"
		u.RawQuery = url.Values{
			"type":    {strconv.FormatUint(uint64(m.Type), 10)},
			"topicID": {strconv.FormatUint(m.TopicID, 10)},
		}.Encode()
		resp, err = http.Post(u.String(), "application/octet-stream", bytes.NewReader(m.Data))
		if err != nil {
			return 0, err
		}
		defer resp.Body.Close()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-200 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return 0, fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			u = *redirectURL
			continue
		} else if resp.StatusCode != http.StatusOK {
			if errstr := resp.Header.Get("X-Broker-Error"); errstr != "" {
				return 0, errors.New(errstr)
			}
			return 0, fmt.Errorf("cannot publish(%d)", resp.StatusCode)
		} else {
			break
		}
	}

	// Parse broker index.
	index, err := strconv.ParseUint(resp.Header.Get("X-Broker-Index"), 10, 64)
	if err != nil {
		return 0, fmt.Errorf("invalid index: %s", err)
	}

	return index, nil
}

// CreateReplica creates a replica on the broker.
func (c *Client) CreateReplica(id uint64, u *url.URL) error {
	var resp *http.Response
	var err error

	leaderURL := *c.LeaderURL()
	for {
		leaderURL.Path = "/messaging/replicas"
		leaderURL.RawQuery = url.Values{
			"id":  {strconv.FormatUint(id, 10)},
			"url": {u.String()},
		}.Encode()
		resp, err = http.Post(leaderURL.String(), "application/octet-stream", nil)
		if err != nil {
			return err
		}
		defer func() { _ = resp.Body.Close() }()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-201 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			leaderURL = *redirectURL
			continue
		} else if resp.StatusCode != http.StatusCreated {
			return errors.New(resp.Header.Get("X-Broker-Error"))
		}
		break
	}

	return nil
}

// DeleteReplica removes a replica on the broker.
func (c *Client) DeleteReplica(id uint64) error {
	var resp *http.Response
	var err error

	u := *c.LeaderURL()
	for {
		u.Path = "/messaging/replicas"
		u.RawQuery = url.Values{"id": {strconv.FormatUint(id, 10)}}.Encode()
		req, _ := http.NewRequest("DELETE", u.String(), nil)
		resp, err = http.DefaultClient.Do(req)
		if err != nil {
			return err
		}
		defer func() { _ = resp.Body.Close() }()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-204 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			u = *redirectURL
			continue
		} else if resp.StatusCode != http.StatusNoContent {
			return errors.New(resp.Header.Get("X-Broker-Error"))
		}
		break
	}

	return nil
}

// Subscribe subscribes a replica to a topic on the broker.
func (c *Client) Subscribe(replicaID, topicID uint64) error {
	var resp *http.Response
	var err error

	u := *c.LeaderURL()
	for {
		u.Path = "/messaging/subscriptions"
		u.RawQuery = url.Values{
			"replicaID": {strconv.FormatUint(replicaID, 10)},
			"topicID":   {strconv.FormatUint(topicID, 10)},
		}.Encode()
		resp, err = http.Post(u.String(), "application/octet-stream", nil)
		if err != nil {
			return err
		}
		defer func() { _ = resp.Body.Close() }()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-201 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			u = *redirectURL
			continue
		} else if resp.StatusCode != http.StatusCreated {
			return errors.New(resp.Header.Get("X-Broker-Error"))
		}
		break
	}

	return nil
}

// Unsubscribe unsubscribes a replica from a topic on the broker.
func (c *Client) Unsubscribe(replicaID, topicID uint64) error {
	var resp *http.Response
	var err error

	u := *c.LeaderURL()
	for {
		u.Path = "/messaging/subscriptions"
		u.RawQuery = url.Values{
			"replicaID": {strconv.FormatUint(replicaID, 10)},
			"topicID":   {strconv.FormatUint(topicID, 10)},
		}.Encode()
		req, _ := http.NewRequest("DELETE", u.String(), nil)
		resp, err = http.DefaultClient.Do(req)
		if err != nil {
			return err
		}
		defer func() { _ = resp.Body.Close() }()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-204 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			u = *redirectURL
			continue
		} else if resp.StatusCode != http.StatusNoContent {
			return errors.New(resp.Header.Get("X-Broker-Error"))
		}
		break
	}

	return nil
}

// streamer connects to a broker server and streams the replica's messages.
func (c *Client) streamer(done chan chan struct{}) {
	for {
		// Check for the client disconnection.
		select {
		case ch := <-done:
			close(ch)
			return
		default:
		}

		// TODO: Validate that there is at least one broker URL.

		// Choose a random broker URL.
		urls := c.URLs()
		u := *urls[rand.Intn(len(urls))]

		// Connect to broker and stream.
		u.Path = "/messaging/messages"
		if err := c.streamFromURL(&u, done); err == errDone {
			return
		} else if err != nil {
			c.Logger.Print(err)
		}
	}
}

// streamFromURL connects to a broker server and streams the replica's messages.
func (c *Client) streamFromURL(u *url.URL, done chan chan struct{}) error {
	// Set the replica id on the URL and open the stream.
	u.RawQuery = url.Values{"replicaID": {strconv.FormatUint(c.replicaID, 10)}}.Encode()
	resp, err := http.Get(u.String())
	if err != nil {
		time.Sleep(c.ReconnectTimeout)
		return nil
	}
	defer func() { _ = resp.Body.Close() }()

	// Ensure that we received a 200 OK from the server before streaming.
	if resp.StatusCode != http.StatusOK {
		time.Sleep(c.ReconnectTimeout)
		c.Logger.Printf("reconnecting to broker: %s (status=%d)", u, resp.StatusCode)
		return nil
	}

	c.Logger.Printf("connected to broker: %s", u)

	// Continuously decode messages from request body in a separate goroutine.
	errNotify := make(chan error, 0)
	go func() {
		dec := NewMessageDecoder(resp.Body)
		for {
			// Decode message from the stream.
			m := &Message{}
			if err := dec.Decode(m); err != nil {
				errNotify <- err
				return
			}

			// TODO: Write broker set updates, do not passthrough to channel.

			// Write message to streaming channel.
			c.c <- m
		}
	}()

	// Check for the client disconnect or error from the stream.
	select {
	case ch := <-done:
		// Close body.
		_ = resp.Body.Close()

		// Clear message buffer.
		select {
		case <-c.c:
		default:
		}

		// Notify the close function and return marker error.
		close(ch)
		return errDone

	case err := <-errNotify:
		return err
	}
}

// marker error for the streamer.
var errDone = errors.New("done")
<commit_msg>Client should track current broker leader<commit_after>package messaging

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"
)

// DefaultReconnectTimeout is the default time to wait between when a broker
// stream disconnects and another connection is retried.
const DefaultReconnectTimeout = 100 * time.Millisecond

// ClientConfig represents the Client configuration that must be persisted
// across restarts.
type ClientConfig struct {
	Brokers []*url.URL `json:"brokers"`
	Leader  *url.URL   `json:"leader"`
}

// NewClientConfig returns a new instance of ClientConfig.
func NewClientConfig(u []*url.URL) *ClientConfig {
	return &ClientConfig{
		Brokers: u,
	}
}

// Client represents a client for the broker's HTTP API.
// Once opened, the client will stream down all messages that
type Client struct {
	mu        sync.Mutex
	replicaID uint64       // the replica that the client is connecting as.
	config    ClientConfig // The Client state that must be persisted to disk.

	opened bool
	done   chan chan struct{} // disconnection notification

	// Channel streams messages from the broker.
	c chan *Message

	// The amount of time to wait before reconnecting to a broker stream.
	ReconnectTimeout time.Duration

	// The logging interface used by the client for out-of-band errors.
	Logger *log.Logger
}

// NewClient returns a new instance of Client.
func NewClient(replicaID uint64) *Client {
	return &Client{
		replicaID:        replicaID,
		ReconnectTimeout: DefaultReconnectTimeout,
		Logger:           log.New(os.Stderr, "[messaging] ", log.LstdFlags),
	}
}

// ReplicaID returns the replica id that the client was opened with.
func (c *Client) ReplicaID() uint64 { return c.replicaID }

// C returns streaming channel.
// Messages can be duplicated so it is important to check the index
// of the incoming message index to make sure it has not been processed.
func (c *Client) C() <-chan *Message { return c.c }

// URLs returns a list of broker URLs to connect to.
func (c *Client) URLs() []*url.URL {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.config.Brokers
}

// LeaderURL returns the URL of the broker leader.
func (c *Client) LeaderURL() *url.URL {
	c.mu.Lock()
	defer c.mu.Unlock()

	// If an explicit leader has not been set, just pick the first of the brokers.
	if c.config.Leader == nil {
		return c.config.Brokers[0]
	} else {
		return c.config.Leader
	}
}

// SetLeaderURL sets the explicit broker leader.
func (c *Client) SetLeaderURL(u *url.URL) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if u == nil {
		return
	}
	c.Logger.Printf("setting broker leader to %s", u.String())
	c.config.Leader = u
}

// SetLogOutput sets writer for all Client log output.
func (c *Client) SetLogOutput(w io.Writer) {
	c.Logger = log.New(w, "[messaging] ", log.LstdFlags)
}

// Open initializes and opens the connection to the cluster. The
// URLs used to contact the cluster are either those supplied to
// the function, or if none are supplied, are read from the file
// at "path". These URLs do need to be URLs of actual Brokers.
// Regardless of URL source, at least 1 URL must be available
// for the client to be successfully opened.
func (c *Client) Open(path string, urls []*url.URL) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Return error if the client is already open.
	if c.opened {
		return ErrClientOpen
	}

	// Read URLs from file if no URLs are provided.
	if len(urls) == 0 {
		// Read URLs from config file. There is no guarantee
		// that the Brokers URLs in the config file are still
		// the Brokers, so we're going to double-check.
		b, err := ioutil.ReadFile(path)
		if os.IsNotExist(err) {
			// nop
		} else if err != nil {
			return err
		} else {
			if err := json.Unmarshal(b, &c.config); err != nil {
				return err
			}
			urls = c.config.Brokers
		}
	}

	if len(urls) < 1 {
		return ErrBrokerURLRequired
	}

	// Now that we have the seed URLs, actually use these to
	// get the actual Broker URLs. Do that here.
	c.config.Brokers = urls // Let's pretend they are the same

	// Create a channel for streaming messages.
	c.c = make(chan *Message, 0)

	// Open the streamer if there's an ID set.
	if c.replicaID != 0 {
		c.done = make(chan chan struct{})
		go c.streamer(c.done)
	}

	// Set open flag.
	c.opened = true

	return nil
}

// Close disconnects the client from the broker cluster.
func (c *Client) Close() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Return error if the client is already closed.
	if !c.opened {
		return ErrClientClosed
	}

	// Shutdown streamer.
	if c.done != nil {
		ch := make(chan struct{})
		c.done <- ch
		<-ch
		c.done = nil
	}

	// Close message stream.
	close(c.c)
	c.c = nil

	// Unset open flag.
	c.opened = false

	return nil
}

// Publish sends a message to the broker and returns an index or error.
func (c *Client) Publish(m *Message) (uint64, error) {
	var resp *http.Response
	var err error

	for {
		u := *c.LeaderURL()
		// Send the message to the messages endpoint.
		u.Path = "/messaging/messages"
		u.RawQuery = url.Values{
			"type":    {strconv.FormatUint(uint64(m.Type), 10)},
			"topicID": {strconv.FormatUint(m.TopicID, 10)},
		}.Encode()
		resp, err = http.Post(u.String(), "application/octet-stream", bytes.NewReader(m.Data))
		if err != nil {
			return 0, err
		}
		defer resp.Body.Close()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-200 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return 0, fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			c.SetLeaderURL(redirectURL)
			continue
		} else if resp.StatusCode != http.StatusOK {
			if errstr := resp.Header.Get("X-Broker-Error"); errstr != "" {
				return 0, errors.New(errstr)
			}
			return 0, fmt.Errorf("cannot publish(%d)", resp.StatusCode)
		} else {
			break
		}
	}

	// Parse broker index.
	index, err := strconv.ParseUint(resp.Header.Get("X-Broker-Index"), 10, 64)
	if err != nil {
		return 0, fmt.Errorf("invalid index: %s", err)
	}

	return index, nil
}

// CreateReplica creates a replica on the broker.
func (c *Client) CreateReplica(id uint64, u *url.URL) error {
	var resp *http.Response
	var err error

	for {
		u := *c.LeaderURL()
		u.Path = "/messaging/replicas"
		u.RawQuery = url.Values{
			"id":  {strconv.FormatUint(id, 10)},
			"url": {u.String()},
		}.Encode()
		resp, err = http.Post(u.String(), "application/octet-stream", nil)
		if err != nil {
			return err
		}
		defer func() { _ = resp.Body.Close() }()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-201 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			c.SetLeaderURL(redirectURL)
			continue
		} else if resp.StatusCode != http.StatusCreated {
			return errors.New(resp.Header.Get("X-Broker-Error"))
		}
		break
	}

	return nil
}

// DeleteReplica removes a replica on the broker.
func (c *Client) DeleteReplica(id uint64) error {
	var resp *http.Response
	var err error

	for {
		u := *c.LeaderURL()
		u.Path = "/messaging/replicas"
		u.RawQuery = url.Values{"id": {strconv.FormatUint(id, 10)}}.Encode()
		req, _ := http.NewRequest("DELETE", u.String(), nil)
		resp, err = http.DefaultClient.Do(req)
		if err != nil {
			return err
		}
		defer func() { _ = resp.Body.Close() }()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-204 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			c.SetLeaderURL(redirectURL)
			continue
		} else if resp.StatusCode != http.StatusNoContent {
			return errors.New(resp.Header.Get("X-Broker-Error"))
		}
		break
	}

	return nil
}

// Subscribe subscribes a replica to a topic on the broker.
func (c *Client) Subscribe(replicaID, topicID uint64) error {
	var resp *http.Response
	var err error

	for {
		u := *c.LeaderURL()
		u.Path = "/messaging/subscriptions"
		u.RawQuery = url.Values{
			"replicaID": {strconv.FormatUint(replicaID, 10)},
			"topicID":   {strconv.FormatUint(topicID, 10)},
		}.Encode()
		resp, err = http.Post(u.String(), "application/octet-stream", nil)
		if err != nil {
			return err
		}
		defer func() { _ = resp.Body.Close() }()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-201 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			c.SetLeaderURL(redirectURL)
			continue
		} else if resp.StatusCode != http.StatusCreated {
			return errors.New(resp.Header.Get("X-Broker-Error"))
		}
		break
	}

	return nil
}

// Unsubscribe unsubscribes a replica from a topic on the broker.
func (c *Client) Unsubscribe(replicaID, topicID uint64) error {
	var resp *http.Response
	var err error

	for {
		u := *c.LeaderURL()
		u.Path = "/messaging/subscriptions"
		u.RawQuery = url.Values{
			"replicaID": {strconv.FormatUint(replicaID, 10)},
			"topicID":   {strconv.FormatUint(topicID, 10)},
		}.Encode()
		req, _ := http.NewRequest("DELETE", u.String(), nil)
		resp, err = http.DefaultClient.Do(req)
		if err != nil {
			return err
		}
		defer func() { _ = resp.Body.Close() }()

		// If a temporary redirect occurs then update the leader and retry.
		// If a non-204 status is returned then an error occurred.
		if resp.StatusCode == http.StatusTemporaryRedirect {
			redirectURL, err := url.Parse(resp.Header.Get("Location"))
			if err != nil {
				return fmt.Errorf("bad redirect: %s", resp.Header.Get("Location"))
			}
			c.SetLeaderURL(redirectURL)
			continue
		} else if resp.StatusCode != http.StatusNoContent {
			return errors.New(resp.Header.Get("X-Broker-Error"))
		}
		break
	}

	return nil
}

// streamer connects to a broker server and streams the replica's messages.
func (c *Client) streamer(done chan chan struct{}) {
	for {
		// Check for the client disconnection.
		select {
		case ch := <-done:
			close(ch)
			return
		default:
		}

		// TODO: Validate that there is at least one broker URL.

		// Choose a random broker URL.
		urls := c.URLs()
		u := *urls[rand.Intn(len(urls))]

		// Connect to broker and stream.
		u.Path = "/messaging/messages"
		if err := c.streamFromURL(&u, done); err == errDone {
			return
		} else if err != nil {
			c.Logger.Print(err)
		}
	}
}

// streamFromURL connects to a broker server and streams the replica's messages.
func (c *Client) streamFromURL(u *url.URL, done chan chan struct{}) error {
	// Set the replica id on the URL and open the stream.
	u.RawQuery = url.Values{"replicaID": {strconv.FormatUint(c.replicaID, 10)}}.Encode()
	resp, err := http.Get(u.String())
	if err != nil {
		time.Sleep(c.ReconnectTimeout)
		return nil
	}
	defer func() { _ = resp.Body.Close() }()

	// Ensure that we received a 200 OK from the server before streaming.
	if resp.StatusCode != http.StatusOK {
		time.Sleep(c.ReconnectTimeout)
		c.Logger.Printf("reconnecting to broker: %s (status=%d)", u, resp.StatusCode)
		return nil
	}

	c.Logger.Printf("connected to broker: %s", u)

	// Continuously decode messages from request body in a separate goroutine.
	errNotify := make(chan error, 0)
	go func() {
		dec := NewMessageDecoder(resp.Body)
		for {
			// Decode message from the stream.
			m := &Message{}
			if err := dec.Decode(m); err != nil {
				errNotify <- err
				return
			}

			// TODO: Write broker set updates, do not passthrough to channel.

			// Write message to streaming channel.
			c.c <- m
		}
	}()

	// Check for the client disconnect or error from the stream.
	select {
	case ch := <-done:
		// Close body.
		_ = resp.Body.Close()

		// Clear message buffer.
		select {
		case <-c.c:
		default:
		}

		// Notify the close function and return marker error.
		close(ch)
		return errDone

	case err := <-errNotify:
		return err
	}
}

// marker error for the streamer.
var errDone = errors.New("done")
<|endoftext|>


<commit_before>// santiago - webhook dispatching service
// https://github.com/topfreegames/santiago
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license
// Copyright © 2016 Top Free Games <backend@tfgco.com>

package metadata

//VERSION identifies current version of the application
var VERSION = "0.2.0"
<commit_msg>Release 0.2.1<commit_after>// santiago - webhook dispatching service
// https://github.com/topfreegames/santiago
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license
// Copyright © 2016 Top Free Games <backend@tfgco.com>

package metadata

//VERSION identifies current version of the application
var VERSION = "0.2.1"
<|endoftext|>


<commit_before><commit_msg>fix experimental code<commit_after><|endoftext|>


<commit_before>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"fmt"
	"reflect"
	"sort"
	"testing"
)

func TestDirectoryStorage(t *testing.T) {
	tracker := new(FilesystemTracker)

	empty := make([]string, 0)
	folderList := tracker.ListFolders()

	if reflect.DeepEqual(empty, folderList) == false {
		t.Fatal(fmt.Sprintf("expected empty folder, found something: %v\n", folderList))
	}

	folderTemplate := []string{"A", "B", "C"}

	for _, folder := range folderTemplate {
		err := tracker.CreateFolder(folder)
		if err != nil {
			t.Fatal(err)
		}
	}

	folderList = tracker.ListFolders()

	sort.Strings(folderList)
	sort.Strings(folderTemplate)

	if reflect.DeepEqual(folderList, folderTemplate) == false {
		t.Fatal(fmt.Sprintf("Found: %v\nExpected: %v\n", folderList, folderTemplate))
	}

}
<commit_msg>adding delete folders to the test suite<commit_after>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"fmt"
	"reflect"
	"sort"
	"testing"
)

func TestDirectoryStorage(t *testing.T) {
	tracker := new(FilesystemTracker)

	empty := make([]string, 0)
	folderList := tracker.ListFolders()

	if reflect.DeepEqual(empty, folderList) == false {
		t.Fatal(fmt.Sprintf("expected empty folder, found something: %v\n", folderList))
	}

	folderTemplate := []string{"A", "B", "C"}

	for _, folder := range folderTemplate {
		err := tracker.CreateFolder(folder)
		if err != nil {
			t.Fatal(err)
		}
	}

	folderList = tracker.ListFolders()

	sort.Strings(folderList)
	sort.Strings(folderTemplate)

	if reflect.DeepEqual(folderList, folderTemplate) == false {
		t.Fatal(fmt.Sprintf("Found: %v\nExpected: %v\n", folderList, folderTemplate))
	}

	err := tracker.DeleteFolder(folderTemplate[0])
	if err != nil {
		t.Fatal(err)
	}

	folderList = tracker.ListFolders()
	sort.Strings(folderList)

	folderTemplate = folderTemplate[1:]

	if reflect.DeepEqual(folderList, folderTemplate) == false {
		t.Fatal(fmt.Sprintf("Found: %v\nExpected: %v\n", folderList, folderTemplate))
	}

}
<|endoftext|>


<commit_before>// +build production

package main
import (
	"ostential"

	"os"
	"net"
	"log"
	"fmt"
	"flag"
	"time"
 	"syscall"
	"runtime"
	"strings"
	"net/url"
	"net/http"
	"math/rand"
	"path/filepath"

	"github.com/rcrowley/goagain"

	"github.com/inconshreveable/go-update"
)

func init() {
// 	log.SetFlags(log.Lmicroseconds | log.Lshortfile)
	log.SetPrefix(fmt.Sprintf("[%d] ", os.Getpid()))
}

func upgrade_loop() {
	const dlimit = time.Hour
	delta := time.Duration(rand.Int63n(int64(dlimit)))
	for {
		select {
		case <-time.After(time.Hour * 1 + delta): // 1.5 +- 0.5 h
			if upgrade_once(true) {
				break
			}
		}
	}
}

func newer_version() string {
	// 1. https://github.com/rzab/ostent/releases/latest # redirects, NOT followed
	// 2. https://github.com/rzab/ostent/releases/vX.Y.Z # Redirect location
	// 3. return "vX.Y.Z" # basename of the location

	type redirected struct {
		error
		url url.URL
	}
	checkRedirect := func(req *http.Request, _via []*http.Request) error {
		return redirected{url: *req.URL,}
	}

	client := &http.Client{CheckRedirect: checkRedirect,}
	resp, err := client.Get("https://github.com/rzab/ostent/releases/latest")
	if err == nil {
		resp.Body.Close()
		return ""
	}
	urlerr, ok := err.(*url.Error)
	if !ok {
		fmt.Fprintln(os.Stderr, err)
		return ""
	}
	if resp != nil && resp.Body != nil {
		resp.Body.Close()
	}
	redir, ok := urlerr.Err.(redirected)
	if !ok {
		fmt.Fprintln(os.Stderr, err)
		return ""
	}
	return filepath.Base(redir.url.Path)
}

func upgrade_once(kill bool) bool {

	mach := runtime.GOARCH
	if mach == "amd64" {
		mach = "x86_64"
	} else if mach == "386" {
		mach = "i686"
	}
	new_version := newer_version()
	if new_version == "" || new_version == "v"+ ostential.VERSION {
		return false
	}
// 	url := fmt.Sprintf("https://ostrost.com"+ "/ostent/releases/%s/%s %s/newer",    ostential.VERSION, strings.Title(runtime.GOOS), mach) // before v0.1.3
	url := fmt.Sprintf("https://github.com/rzab/ostent/releases/download/%s/%s.%s", new_version,  strings.Title(runtime.GOOS), mach)

	err, _ := update.New().FromUrl(url) // , snderr
	if err != nil ||  err != nil {
		// log.Printf("Upgrade failed: %v|%v\n", err, snderr)
		return false
	}
	log.Println("Successfull UPGRADE, relaunching")
	if kill {
		syscall.Kill(os.Getpid(), syscall.SIGUSR2)
	}
	return true
}

func main() {
	var upgradelater bool
	flag.BoolVar(&upgradelater, "updatelater",  false, "Update later; Deprecated, use -upgradelater") // compability
	flag.BoolVar(&upgradelater, "upgradelater", false, "Upgrade later")

	flag.Parse()

	had_upgrade := false
	if !upgradelater && os.Getenv("GOAGAIN_PPID") == "" { // not after gone again
		log.Println("Initial check for upgrades; run with -ugradelater to disable")
		had_upgrade = upgrade_once(false)
	}

	if !had_upgrade { // start the background routine unless just had an upgrade and gonna relaunch anyway
		go ostential.Loop()
	}

	listen, err := goagain.Listener()
	if err != nil {
		listen, err = net.Listen("tcp", ostential.BindFlag.String())
		if err != nil {
			log.Fatalln(err)
		}

		if had_upgrade { // goagain
			go func() {
				time.Sleep(time.Second) // not before goagain.Wait
				syscall.Kill(os.Getpid(), syscall.SIGUSR2)
				// goagain.ForkExec(listen)
			}()
		} else {
			go upgrade_loop()
			go ostential.Serve(listen, true, nil)
		}

	} else {
		go upgrade_loop()
		go ostential.Serve(listen, true, nil)

		if err := goagain.Kill(); err != nil {
			log.Fatalln(err)
		}
	}

	if _, err := goagain.Wait(listen); err != nil { // signals before won't be catched
		log.Fatalln(err)
	}
	if err := listen.Close(); err != nil {
		log.Fatalln(err)
	}
	time.Sleep(time.Second)
}
<commit_msg>remove outdated flag alias<commit_after>// +build production

package main
import (
	"ostential"

	"os"
	"net"
	"log"
	"fmt"
	"flag"
	"time"
 	"syscall"
	"runtime"
	"strings"
	"net/url"
	"net/http"
	"math/rand"
	"path/filepath"

	"github.com/rcrowley/goagain"

	"github.com/inconshreveable/go-update"
)

func init() {
// 	log.SetFlags(log.Lmicroseconds | log.Lshortfile)
	log.SetPrefix(fmt.Sprintf("[%d] ", os.Getpid()))
}

func upgrade_loop() {
	const dlimit = time.Hour
	delta := time.Duration(rand.Int63n(int64(dlimit)))
	for {
		select {
		case <-time.After(time.Hour * 1 + delta): // 1.5 +- 0.5 h
			if upgrade_once(true) {
				break
			}
		}
	}
}

func newer_version() string {
	// 1. https://github.com/rzab/ostent/releases/latest # redirects, NOT followed
	// 2. https://github.com/rzab/ostent/releases/vX.Y.Z # Redirect location
	// 3. return "vX.Y.Z" # basename of the location

	type redirected struct {
		error
		url url.URL
	}
	checkRedirect := func(req *http.Request, _via []*http.Request) error {
		return redirected{url: *req.URL,}
	}

	client := &http.Client{CheckRedirect: checkRedirect,}
	resp, err := client.Get("https://github.com/rzab/ostent/releases/latest")
	if err == nil {
		resp.Body.Close()
		return ""
	}
	urlerr, ok := err.(*url.Error)
	if !ok {
		fmt.Fprintln(os.Stderr, err)
		return ""
	}
	if resp != nil && resp.Body != nil {
		resp.Body.Close()
	}
	redir, ok := urlerr.Err.(redirected)
	if !ok {
		fmt.Fprintln(os.Stderr, err)
		return ""
	}
	return filepath.Base(redir.url.Path)
}

func upgrade_once(kill bool) bool {

	mach := runtime.GOARCH
	if mach == "amd64" {
		mach = "x86_64"
	} else if mach == "386" {
		mach = "i686"
	}
	new_version := newer_version()
	if new_version == "" || new_version == "v"+ ostential.VERSION {
		return false
	}
// 	url := fmt.Sprintf("https://ostrost.com"+ "/ostent/releases/%s/%s %s/newer",    ostential.VERSION, strings.Title(runtime.GOOS), mach) // before v0.1.3
	url := fmt.Sprintf("https://github.com/rzab/ostent/releases/download/%s/%s.%s", new_version,  strings.Title(runtime.GOOS), mach)

	err, _ := update.New().FromUrl(url) // , snderr
	if err != nil ||  err != nil {
		// log.Printf("Upgrade failed: %v|%v\n", err, snderr)
		return false
	}
	log.Println("Successfull UPGRADE, relaunching")
	if kill {
		syscall.Kill(os.Getpid(), syscall.SIGUSR2)
	}
	return true
}

func main() {
	upgradelater := flag.Bool("upgradelater", false, "Upgrade later")

	flag.Parse()

	had_upgrade := false
	if !*upgradelater && os.Getenv("GOAGAIN_PPID") == "" { // not after gone again
		log.Println("Initial check for upgrades; run with -ugradelater to disable")
		had_upgrade = upgrade_once(false)
	}

	if !had_upgrade { // start the background routine unless just had an upgrade and gonna relaunch anyway
		go ostential.Loop()
	}

	listen, err := goagain.Listener()
	if err != nil {
		listen, err = net.Listen("tcp", ostential.BindFlag.String())
		if err != nil {
			log.Fatalln(err)
		}

		if had_upgrade { // goagain
			go func() {
				time.Sleep(time.Second) // not before goagain.Wait
				syscall.Kill(os.Getpid(), syscall.SIGUSR2)
				// goagain.ForkExec(listen)
			}()
		} else {
			go upgrade_loop()
			go ostential.Serve(listen, true, nil)
		}

	} else {
		go upgrade_loop()
		go ostential.Serve(listen, true, nil)

		if err := goagain.Kill(); err != nil {
			log.Fatalln(err)
		}
	}

	if _, err := goagain.Wait(listen); err != nil { // signals before won't be catched
		log.Fatalln(err)
	}
	if err := listen.Close(); err != nil {
		log.Fatalln(err)
	}
	time.Sleep(time.Second)
}
<|endoftext|>


<commit_before>/*
http://www.apache.org/licenses/LICENSE-2.0.txt

Copyright 2015 Intel Corporation

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 rest

import (
	"fmt"
	"net/http"
	"sort"
	"strconv"

	"github.com/julienschmidt/httprouter"

	"github.com/intelsdi-x/snap/core"
	"github.com/intelsdi-x/snap/mgmt/rest/rbody"
)

func (s *Server) getMetrics(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	mets, err := s.mm.MetricCatalog()
	if err != nil {
		respond(500, rbody.FromError(err), w)
		return
	}
	respondWithMetrics(r.Host, mets, w)
}

func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	namespace := params.ByName("namespace")

	// we land here if the request contains a trailing slash, because it matches the tree
	// lookup URL: /v1/metrics/*namespace.  If the length of the namespace param is 1, we
	// redirect the request to getMetrics.  This results in GET /v1/metrics and
	// GET /v1/metrics/ behaving the same way.
	if len(namespace) <= 1 {
		s.getMetrics(w, r, params)
		return
	}

	ns := parseNamespace(namespace)

	var (
		ver int
		err error
	)
	q := r.URL.Query()
	v := q.Get("ver")

	if ns[len(ns)-1] == "*" {
		if v == "" {
			ver = -1
		} else {
			ver, err = strconv.Atoi(v)
			if err != nil {
				respond(400, rbody.FromError(err), w)
				return
			}
		}

		mets, err := s.mm.FetchMetrics(core.NewNamespace(ns[:len(ns)-1]...), ver)
		if err != nil {
			respond(404, rbody.FromError(err), w)
			return
		}
		respondWithMetrics(r.Host, mets, w)
		return
	}

	// If no version was given, get all that fall at this namespace.
	if v == "" {
		mts, err := s.mm.GetMetricVersions(core.NewNamespace(ns...))
		if err != nil {
			respond(404, rbody.FromError(err), w)
			return
		}
		respondWithMetrics(r.Host, mts, w)
		return
	}

	// if an explicit version is given, get that single one.
	ver, err = strconv.Atoi(v)
	if err != nil {
		respond(400, rbody.FromError(err), w)
		return
	}
	mt, err := s.mm.GetMetric(core.NewNamespace(ns...), ver)
	if err != nil {
		respond(404, rbody.FromError(err), w)
		return
	}

	b := &rbody.MetricReturned{}
	mb := &rbody.Metric{
		Namespace:               mt.Namespace().String(),
		Version:                 mt.Version(),
		LastAdvertisedTimestamp: mt.LastAdvertisedTime().Unix(),
		Href: catalogedMetricURI(r.Host, mt),
	}
	rt := mt.Policy().RulesAsTable()
	policies := make([]rbody.PolicyTable, 0, len(rt))
	for _, r := range rt {
		policies = append(policies, rbody.PolicyTable{
			Name:     r.Name,
			Type:     r.Type,
			Default:  r.Default,
			Required: r.Required,
			Minimum:  r.Minimum,
			Maximum:  r.Maximum,
		})
	}
	mb.Policy = policies
	b.Metric = mb
	respond(200, b, w)
}

func respondWithMetrics(host string, mets []core.CatalogedMetric, w http.ResponseWriter) {
	b := rbody.NewMetricsReturned()

	for _, met := range mets {
		rt := met.Policy().RulesAsTable()
		policies := make([]rbody.PolicyTable, 0, len(rt))
		for _, r := range rt {
			policies = append(policies, rbody.PolicyTable{
				Name:     r.Name,
				Type:     r.Type,
				Default:  r.Default,
				Required: r.Required,
				Minimum:  r.Minimum,
				Maximum:  r.Maximum,
			})
		}
		b = append(b, rbody.Metric{
			Namespace:               met.Namespace().String(),
			Version:                 met.Version(),
			LastAdvertisedTimestamp: met.LastAdvertisedTime().Unix(),
			Policy:                  policies,
			Href:                    catalogedMetricURI(host, met),
		})
	}
	sort.Sort(b)
	respond(200, b, w)
}

func catalogedMetricURI(host string, mt core.CatalogedMetric) string {
	return fmt.Sprintf("%s://%s/v1/metrics%s?ver=%d", protocolPrefix, host, mt.Namespace().String(), mt.Version())
}
<commit_msg>Update REST API responses to include Description, Unit, Dynamic, and DynamicElements for metrics returned from the metric catalog. This includes setting dynamic elements of the metric namespace when the metric is dynamic.<commit_after>/*
http://www.apache.org/licenses/LICENSE-2.0.txt

Copyright 2015 Intel Corporation

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 rest

import (
	"fmt"
	"net/http"
	"sort"
	"strconv"

	"github.com/julienschmidt/httprouter"

	"github.com/intelsdi-x/snap/core"
	"github.com/intelsdi-x/snap/mgmt/rest/rbody"
)

func (s *Server) getMetrics(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	mets, err := s.mm.MetricCatalog()
	if err != nil {
		respond(500, rbody.FromError(err), w)
		return
	}
	respondWithMetrics(r.Host, mets, w)
}

func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	namespace := params.ByName("namespace")

	// we land here if the request contains a trailing slash, because it matches the tree
	// lookup URL: /v1/metrics/*namespace.  If the length of the namespace param is 1, we
	// redirect the request to getMetrics.  This results in GET /v1/metrics and
	// GET /v1/metrics/ behaving the same way.
	if len(namespace) <= 1 {
		s.getMetrics(w, r, params)
		return
	}

	ns := parseNamespace(namespace)

	var (
		ver int
		err error
	)
	q := r.URL.Query()
	v := q.Get("ver")

	if ns[len(ns)-1] == "*" {
		if v == "" {
			ver = -1
		} else {
			ver, err = strconv.Atoi(v)
			if err != nil {
				respond(400, rbody.FromError(err), w)
				return
			}
		}

		mets, err := s.mm.FetchMetrics(core.NewNamespace(ns[:len(ns)-1]...), ver)
		if err != nil {
			respond(404, rbody.FromError(err), w)
			return
		}
		respondWithMetrics(r.Host, mets, w)
		return
	}

	// If no version was given, get all that fall at this namespace.
	if v == "" {
		mts, err := s.mm.GetMetricVersions(core.NewNamespace(ns...))
		if err != nil {
			respond(404, rbody.FromError(err), w)
			return
		}
		respondWithMetrics(r.Host, mts, w)
		return
	}

	// if an explicit version is given, get that single one.
	ver, err = strconv.Atoi(v)
	if err != nil {
		respond(400, rbody.FromError(err), w)
		return
	}
	mt, err := s.mm.GetMetric(core.NewNamespace(ns...), ver)
	if err != nil {
		respond(404, rbody.FromError(err), w)
		return
	}

	b := &rbody.MetricReturned{}
	dyn, indexes := mt.Namespace().IsDynamic()
	var dynamicElements []rbody.DynamicElement
	if dyn {
		dynamicElements = getDynamicElements(mt.Namespace(), indexes)
	}
	mb := &rbody.Metric{
		Namespace:       mt.Namespace().String(),
		Version:         mt.Version(),
		Dynamic:         dyn,
		DynamicElements: dynamicElements,
		Description:     mt.Description(),
		Unit:            mt.Unit(),
		LastAdvertisedTimestamp: mt.LastAdvertisedTime().Unix(),
		Href: catalogedMetricURI(r.Host, mt),
	}
	rt := mt.Policy().RulesAsTable()
	policies := make([]rbody.PolicyTable, 0, len(rt))
	for _, r := range rt {
		policies = append(policies, rbody.PolicyTable{
			Name:     r.Name,
			Type:     r.Type,
			Default:  r.Default,
			Required: r.Required,
			Minimum:  r.Minimum,
			Maximum:  r.Maximum,
		})
	}
	mb.Policy = policies
	b.Metric = mb
	respond(200, b, w)
}

func respondWithMetrics(host string, mets []core.CatalogedMetric, w http.ResponseWriter) {
	b := rbody.NewMetricsReturned()

	for _, met := range mets {
		rt := met.Policy().RulesAsTable()
		policies := make([]rbody.PolicyTable, 0, len(rt))
		for _, r := range rt {
			policies = append(policies, rbody.PolicyTable{
				Name:     r.Name,
				Type:     r.Type,
				Default:  r.Default,
				Required: r.Required,
				Minimum:  r.Minimum,
				Maximum:  r.Maximum,
			})
		}
		dyn, indexes := met.Namespace().IsDynamic()
		var dynamicElements []rbody.DynamicElement
		if dyn {
			dynamicElements = getDynamicElements(met.Namespace(), indexes)
		}
		b = append(b, rbody.Metric{
			Namespace:               met.Namespace().String(),
			Version:                 met.Version(),
			LastAdvertisedTimestamp: met.LastAdvertisedTime().Unix(),
			Description:             met.Description(),
			Dynamic:                 dyn,
			DynamicElements:         dynamicElements,
			Unit:                    met.Unit(),
			Policy:                  policies,
			Href:                    catalogedMetricURI(host, met),
		})
	}
	sort.Sort(b)
	respond(200, b, w)
}

func catalogedMetricURI(host string, mt core.CatalogedMetric) string {
	return fmt.Sprintf("%s://%s/v1/metrics%s?ver=%d", protocolPrefix, host, mt.Namespace().String(), mt.Version())
}

func getDynamicElements(ns core.Namespace, indexes []int) []rbody.DynamicElement {
	elements := make([]rbody.DynamicElement, 0, len(indexes))
	for _, v := range indexes {
		e := ns.Element(v)
		elements = append(elements, rbody.DynamicElement{
			Index:       v,
			Name:        e.Name,
			Description: e.Description,
		})
	}
	return elements
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/google/go-github/github"
)

const ROUTE = "/{user:.*}/{name:.*}/"

// New returns a new http.Handler that handles github webhooks from the github API.
//
// secret is the secret provided when you register the webhook in the github UI.
// logger is the standard logger for the application
func New(address string, branch string, secret string, logger *logrus.Logger) http.Handler {
	proto := "tcp"
	addr := address
	if strings.HasPrefix(addr, "unix:") {
		proto = "unix"
		addr = addr[5:]
	}
	return &Server{
		proto:   proto,
		address: addr,
		branch:  branch,
		secret:  secret,
		logger:  logger,
	}
}

// Server handles github webhooks.
type Server struct {
	proto   string
	address string
	branch  string
	secret  string
	logger  *logrus.Logger
}

func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var (
		repo       = parseRepo(r)
		requestLog = h.logger.WithFields(newFields(r, repo))
	)
	requestLog.Debug("web hook received")

	data, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		requestLog.WithField("error", err).Error("read request body")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	if !validateSignature(requestLog, r, h.secret, data) {
		requestLog.Warn("signature verification failed")
		// return a generic NOTFOUND for auth/verification errors
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	g := GithubHeaders{
		GitHubEvent:    r.Header.Get("X-Github-Event"),
		GitHubDelivery: r.Header.Get("X-Github-Delivery"),
		HubSignature:   r.Header.Get("X-Hub-Signature"),
	}

	requestLog.Infof("Received event: %s", g.GitHubEvent)
	if g.GitHubEvent == "pull_request" {
		var e github.PullRequestEvent
		if err := json.Unmarshal(data, &e); err != nil {
			logrus.Error(err)
		} else if err := h.HandlePullRequestEvent(g, e); err != nil {
			logrus.Error(err)
		}
	} else if g.GitHubEvent == "status" {
		var e github.StatusEvent
		if err := json.Unmarshal(data, &e); err != nil {
			logrus.Error(err)
		} else if err := h.HandleStatusEvent(g, e); err != nil {
			logrus.Error(err)
		}
	} else {
		requestLog.Printf("Ignoring unknown event kind")
	}
}
<commit_msg>datakit-gh: better debug message<commit_after>package server

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/google/go-github/github"
)

const ROUTE = "/{user:.*}/{name:.*}/"

// New returns a new http.Handler that handles github webhooks from the github API.
//
// secret is the secret provided when you register the webhook in the github UI.
// logger is the standard logger for the application
func New(address string, branch string, secret string, logger *logrus.Logger) http.Handler {
	proto := "tcp"
	addr := address
	if strings.HasPrefix(addr, "unix:") {
		proto = "unix"
		addr = addr[5:]
	}
	return &Server{
		proto:   proto,
		address: addr,
		branch:  branch,
		secret:  secret,
		logger:  logger,
	}
}

// Server handles github webhooks.
type Server struct {
	proto   string
	address string
	branch  string
	secret  string
	logger  *logrus.Logger
}

func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var (
		repo       = parseRepo(r)
		requestLog = h.logger.WithFields(newFields(r, repo))
	)

	data, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		requestLog.WithField("error", err).Error("read request body")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	if !validateSignature(requestLog, r, h.secret, data) {
		requestLog.Warn("signature verification failed")
		// return a generic NOTFOUND for auth/verification errors
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	g := GithubHeaders{
		GitHubEvent:    r.Header.Get("X-Github-Event"),
		GitHubDelivery: r.Header.Get("X-Github-Delivery"),
		HubSignature:   r.Header.Get("X-Hub-Signature"),
	}

	requestLog.Infof("Received event: %s %s", g.GitHubEvent, g.GitHubDelivery)
	if g.GitHubEvent == "pull_request" {
		var e github.PullRequestEvent
		if err := json.Unmarshal(data, &e); err != nil {
			logrus.Error(err)
		} else if err := h.HandlePullRequestEvent(g, e); err != nil {
			logrus.Error(err)
		}
	} else if g.GitHubEvent == "status" {
		var e github.StatusEvent
		if err := json.Unmarshal(data, &e); err != nil {
			logrus.Error(err)
		} else if err := h.HandleStatusEvent(g, e); err != nil {
			logrus.Error(err)
		}
	} else {
		requestLog.Printf("Ignoring unknown event kind")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/hillu/go-yara/v4"

	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"sync"
)

func printMatches(item string, m []yara.MatchRule, err error) {
	if err != nil {
		log.Printf("%s: error: %s", item, err)
		return
	}
	if len(m) == 0 {
		log.Printf("%s: no matches", item)
		return
	}
	buf := &bytes.Buffer{}
	fmt.Fprintf(buf, "%s: [", item)
	for i, match := range m {
		if i > 0 {
			fmt.Fprint(buf, ", ")
		}
		fmt.Fprintf(buf, "%s:%s", match.Namespace, match.Rule)
	}
	fmt.Fprint(buf, "]")
	log.Print(buf.String())
}

func main() {
	var (
		rules       rules
		vars        variables
		processScan bool
		pids        []int
		threads     int
	)
	flag.BoolVar(&processScan, "processes", false, "scan processes instead of files")
	flag.Var(&rules, "rule", "add rules in source form: [namespace:]filename")
	flag.Var(&vars, "define", "define variable referenced n ruleset")
	flag.IntVar(&threads, "threads", 1, "use specified number of threads")
	flag.Parse()

	if len(rules) == 0 {
		log.Fatal("no rules specified")
	}

	args := flag.Args()
	if len(args) == 0 {
		log.Fatal("no files or processes specified")
	}

	if processScan {
		for _, arg := range args {
			if pid, err := strconv.Atoi(arg); err != nil {
				log.Fatalf("Could not parse %s ad number", arg)
			} else {
				pids = append(pids, pid)
			}
		}
	}

	c, err := yara.NewCompiler()
	if err != nil {
		log.Fatalf("Failed to initialize YARA compiler: %s", err)
	}
	for id, value := range vars {
		if err := c.DefineVariable(id, value); err != nil {
			log.Fatal("failed to define variable '%s': %s", id, err)
		}
	}
	for _, rule := range rules {
		f, err := os.Open(rule.filename)
		if err != nil {
			log.Fatalf("Could not open rule file %s: %s", rule.filename, err)
		}
		err = c.AddFile(f, rule.namespace)
		f.Close()
		if err != nil {
			log.Fatalf("Could not parse rule file %s: %s", rule.filename, err)
		}
	}
	r, err := c.GetRules()
	if err != nil {
		log.Fatalf("Failed to compile rules: %s", err)
	}

	wg := sync.WaitGroup{}
	wg.Add(threads)

	if processScan {
		c := make(chan int, threads)
		for i := 0; i < threads; i++ {
			s, _ := yara.NewScanner(r)
			go func(c chan int, tid int) {
				for pid := range c {
					var m yara.MatchRules
					log.Printf("<%02d> Scanning process %d...", tid, pid)
					err := s.SetCallback(&m).ScanProc(pid)
					printMatches(fmt.Sprintf("<pid %d", pid), m, err)
				}
				wg.Done()
			}(c, i)
		}
		for _, pid := range pids {
			c <- pid
		}
		close(c)
	} else {
		c := make(chan string, threads)
		for i := 0; i < threads; i++ {
			s, _ := yara.NewScanner(r)
			go func(c chan string, tid int) {
				for filename := range c {
					var m yara.MatchRules
					log.Printf("<%02d> Scanning file %s... ", tid, filename)
					err := s.SetCallback(&m).ScanFile(filename)
					printMatches(filename, m, err)
				}
				wg.Done()
			}(c, i)
		}
		for _, path := range args {
			if err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
				if info.Mode().IsRegular() {
					c <- path
				} else if info.Mode().IsDir() {
					return nil
				} else {
					log.Printf("Sipping %s", path)
				}
				return nil
			}); err != nil {
				log.Printf("walk: %s: %s", path, err)
			}
		}
		close(c)
	}
	wg.Wait()
}
<commit_msg>Make example a bit more friendly<commit_after>package main

import (
	"github.com/hillu/go-yara/v4"

	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"sync"
)

func printMatches(item string, m []yara.MatchRule, err error) {
	if err != nil {
		log.Printf("%s: error: %s", item, err)
		return
	}
	if len(m) == 0 {
		log.Printf("%s: no matches", item)
		return
	}
	buf := &bytes.Buffer{}
	fmt.Fprintf(buf, "%s: [", item)
	for i, match := range m {
		if i > 0 {
			fmt.Fprint(buf, ", ")
		}
		fmt.Fprintf(buf, "%s:%s", match.Namespace, match.Rule)
	}
	fmt.Fprint(buf, "]")
	log.Print(buf.String())
}

func main() {
	var (
		rules       rules
		vars        variables
		processScan bool
		pids        []int
		threads     int
	)
	flag.BoolVar(&processScan, "processes", false, "scan processes instead of files")
	flag.Var(&rules, "rule", "add rules in source form: [namespace:]filename")
	flag.Var(&vars, "define", "define variable referenced n ruleset")
	flag.IntVar(&threads, "threads", 1, "use specified number of threads")
	flag.Parse()

	if len(rules) == 0 {
		flag.Usage()
		log.Fatal("no rules specified")
	}

	args := flag.Args()
	if len(args) == 0 {
		flag.Usage()
		log.Fatal("no files or processes specified")
	}

	if processScan {
		for _, arg := range args {
			if pid, err := strconv.Atoi(arg); err != nil {
				log.Fatalf("Could not parse %s ad number", arg)
			} else {
				pids = append(pids, pid)
			}
		}
	}

	c, err := yara.NewCompiler()
	if err != nil {
		log.Fatalf("Failed to initialize YARA compiler: %s", err)
	}
	for id, value := range vars {
		if err := c.DefineVariable(id, value); err != nil {
			log.Fatal("failed to define variable '%s': %s", id, err)
		}
	}
	for _, rule := range rules {
		f, err := os.Open(rule.filename)
		if err != nil {
			log.Fatalf("Could not open rule file %s: %s", rule.filename, err)
		}
		err = c.AddFile(f, rule.namespace)
		f.Close()
		if err != nil {
			log.Fatalf("Could not parse rule file %s: %s", rule.filename, err)
		}
	}
	r, err := c.GetRules()
	if err != nil {
		log.Fatalf("Failed to compile rules: %s", err)
	}

	wg := sync.WaitGroup{}
	wg.Add(threads)

	if processScan {
		c := make(chan int, threads)
		for i := 0; i < threads; i++ {
			s, _ := yara.NewScanner(r)
			go func(c chan int, tid int) {
				for pid := range c {
					var m yara.MatchRules
					log.Printf("<%02d> Scanning process %d...", tid, pid)
					err := s.SetCallback(&m).ScanProc(pid)
					printMatches(fmt.Sprintf("<pid %d", pid), m, err)
				}
				wg.Done()
			}(c, i)
		}
		for _, pid := range pids {
			c <- pid
		}
		close(c)
	} else {
		c := make(chan string, threads)
		for i := 0; i < threads; i++ {
			s, _ := yara.NewScanner(r)
			go func(c chan string, tid int) {
				for filename := range c {
					var m yara.MatchRules
					log.Printf("<%02d> Scanning file %s... ", tid, filename)
					err := s.SetCallback(&m).ScanFile(filename)
					printMatches(filename, m, err)
				}
				wg.Done()
			}(c, i)
		}
		for _, path := range args {
			if err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
				if info.Mode().IsRegular() {
					c <- path
				} else if info.Mode().IsDir() {
					return nil
				} else {
					log.Printf("Sipping %s", path)
				}
				return nil
			}); err != nil {
				log.Printf("walk: %s: %s", path, err)
			}
		}
		close(c)
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sort"

	"golang.org/x/net/context"

	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/oauth2"
	"github.com/influxdata/chronograf/organizations"
)

type meLinks struct {
	Self string `json:"self"` // Self link mapping to this resource
}

type meResponse struct {
	*chronograf.User
	Links               meLinks                   `json:"links"`
	Organizations       []chronograf.Organization `json:"organizations,omitempty"`
	CurrentOrganization *chronograf.Organization  `json:"currentOrganization,omitempty"`
}

// If new user response is nil, return an empty meResponse because it
// indicates authentication is not needed
func newMeResponse(usr *chronograf.User) meResponse {
	base := "/chronograf/v1/users"
	name := "me"
	if usr != nil {
		name = PathEscape(fmt.Sprintf("%d", usr.ID))
	}

	return meResponse{
		User: usr,
		Links: meLinks{
			Self: fmt.Sprintf("%s/%s", base, name),
		},
	}
}

// TODO: This Scheme value is hard-coded temporarily since we only currently
// support OAuth2. This hard-coding should be removed whenever we add
// support for other authentication schemes.
func getScheme(ctx context.Context) (string, error) {
	return "oauth2", nil
}

func getPrincipal(ctx context.Context) (oauth2.Principal, error) {
	principal, ok := ctx.Value(oauth2.PrincipalKey).(oauth2.Principal)
	if !ok {
		return oauth2.Principal{}, fmt.Errorf("Token not found")
	}

	return principal, nil
}

func getValidPrincipal(ctx context.Context) (oauth2.Principal, error) {
	p, err := getPrincipal(ctx)
	if err != nil {
		return p, err
	}
	if p.Subject == "" {
		return oauth2.Principal{}, fmt.Errorf("Token not found")
	}
	if p.Issuer == "" {
		return oauth2.Principal{}, fmt.Errorf("Token not found")
	}
	return p, nil
}

type meRequest struct {
	// Organization is the OrganizationID
	Organization string `json:"organization"`
}

// UpdateMe changes the user's current organization on the JWT and responds
// with the same semantics as Me
func (s *Service) UpdateMe(auth oauth2.Authenticator) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		serverCtx := serverContext(ctx)
		principal, err := auth.Validate(ctx, r)
		if err != nil {
			s.Logger.Error(fmt.Sprintf("Invalid principal: %v", err))
			Error(w, http.StatusForbidden, "invalid principal", s.Logger)
			return
		}
		var req meRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			invalidJSON(w, s.Logger)
			return
		}

		// validate that the organization exists
		orgID, err := parseOrganizationID(req.Organization)
		if err != nil {
			Error(w, http.StatusInternalServerError, err.Error(), s.Logger)
			return
		}
		_, err = s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
		if err != nil {
			Error(w, http.StatusBadRequest, err.Error(), s.Logger)
			return
		}

		// validate that user belongs to organization
		ctx = context.WithValue(ctx, organizations.ContextKey, req.Organization)

		p, err := getValidPrincipal(ctx)
		if err != nil {
			invalidData(w, err, s.Logger)
			return
		}
		if p.Organization == "" {
			defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
			if err != nil {
				unknownErrorWithMessage(w, err, s.Logger)
				return
			}
			p.Organization = fmt.Sprintf("%d", defaultOrg.ID)
		}
		scheme, err := getScheme(ctx)
		if err != nil {
			invalidData(w, err, s.Logger)
			return
		}
		_, err = s.Store.Users(ctx).Get(ctx, chronograf.UserQuery{
			Name:     &p.Subject,
			Provider: &p.Issuer,
			Scheme:   &scheme,
		})
		if err == chronograf.ErrUserNotFound {
			// Since a user is not a part of this organization, we should tell them that they are Forbidden (403) from accessing this resource
			Error(w, http.StatusForbidden, err.Error(), s.Logger)
			return
		}
		if err != nil {
			Error(w, http.StatusBadRequest, err.Error(), s.Logger)
			return
		}

		// TODO: change to principal.CurrentOrganization
		principal.Organization = req.Organization

		if err := auth.Authorize(ctx, w, principal); err != nil {
			Error(w, http.StatusInternalServerError, err.Error(), s.Logger)
			return
		}

		ctx = context.WithValue(ctx, oauth2.PrincipalKey, principal)

		s.Me(w, r.WithContext(ctx))
	}
}

// Me does a findOrCreate based on the username in the context
func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	if !s.UseAuth {
		// If there's no authentication, return an empty user
		res := newMeResponse(nil)
		encodeJSON(w, http.StatusOK, res, s.Logger)
		return
	}

	p, err := getValidPrincipal(ctx)
	if err != nil {
		invalidData(w, err, s.Logger)
		return
	}
	scheme, err := getScheme(ctx)
	if err != nil {
		invalidData(w, err, s.Logger)
		return
	}

	ctx = context.WithValue(ctx, organizations.ContextKey, p.Organization)
	serverCtx := serverContext(ctx)

	if p.Organization == "" {
		defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
		if err != nil {
			unknownErrorWithMessage(w, err, s.Logger)
			return
		}
		p.Organization = fmt.Sprintf("%d", defaultOrg.ID)
	}

	usr, err := s.Store.Users(serverCtx).Get(serverCtx, chronograf.UserQuery{
		Name:     &p.Subject,
		Provider: &p.Issuer,
		Scheme:   &scheme,
	})
	if err != nil && err != chronograf.ErrUserNotFound {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}

	defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
	if err != nil {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}

	if usr != nil {
		// If the default org is private and the user has no roles, they should not have access
		if !defaultOrg.Public && len(usr.Roles) == 0 {
			Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
			return
		}
		orgID, err := parseOrganizationID(p.Organization)
		if err != nil {
			unknownErrorWithMessage(w, err, s.Logger)
			return
		}
		currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
		if err == chronograf.ErrOrganizationNotFound {
			// The intent is to force a the user to go through another auth flow
			Error(w, http.StatusForbidden, "user's current organization was not found", s.Logger)
			return
		}
		if err != nil {
			unknownErrorWithMessage(w, err, s.Logger)
			return
		}

		defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
		// If a user was added via the API, they might not yet be a member of the default organization
		// Here we check to verify that they are a user in the default organization
		if !hasRoleInDefaultOrganization(usr, defaultOrgID) {
			usr.Roles = append(usr.Roles, chronograf.Role{
				Organization: defaultOrgID,
				Name:         defaultOrg.DefaultRole,
			})
			if err := s.Store.Users(serverCtx).Update(serverCtx, usr); err != nil {
				unknownErrorWithMessage(w, err, s.Logger)
				return
			}
		}
		orgs, err := s.usersOrganizations(serverCtx, usr)
		if err != nil {
			unknownErrorWithMessage(w, err, s.Logger)
			return
		}
		res := newMeResponse(usr)
		res.Organizations = orgs
		res.CurrentOrganization = currentOrg
		encodeJSON(w, http.StatusOK, res, s.Logger)
		return
	}

	// If users must be explicitly added to the default organization, respond with 403
	// forbidden
	if !defaultOrg.Public {
		Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
		return
	}

	// Because we didnt find a user, making a new one
	user := &chronograf.User{
		Name:     p.Subject,
		Provider: p.Issuer,
		// TODO: This Scheme value is hard-coded temporarily since we only currently
		// support OAuth2. This hard-coding should be removed whenever we add
		// support for other authentication schemes.
		Scheme: scheme,
		Roles: []chronograf.Role{
			{
				Name: defaultOrg.DefaultRole,
				// This is the ID of the default organization
				Organization: fmt.Sprintf("%d", defaultOrg.ID),
			},
		},
		// TODO(desa): this needs a better name
		SuperAdmin: s.newUsersAreSuperAdmin(),
	}

	newUser, err := s.Store.Users(serverCtx).Add(serverCtx, user)
	if err != nil {
		msg := fmt.Errorf("error storing user %s: %v", user.Name, err)
		unknownErrorWithMessage(w, msg, s.Logger)
		return
	}

	orgs, err := s.usersOrganizations(serverCtx, newUser)
	if err != nil {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}
	orgID, err := parseOrganizationID(p.Organization)
	if err != nil {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}
	currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
	if err != nil {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}
	res := newMeResponse(newUser)
	res.Organizations = orgs
	res.CurrentOrganization = currentOrg
	encodeJSON(w, http.StatusOK, res, s.Logger)
}

func (s *Service) firstUser() bool {
	serverCtx := serverContext(context.Background())
	numUsers, err := s.Store.Users(serverCtx).Num(serverCtx)
	if err != nil {
		return false
	}

	return numUsers == 0
}
func (s *Service) newUsersAreSuperAdmin() bool {
	if s.firstUser() {
		return true
	}
	serverCtx := serverContext(context.Background())
	cfg, err := s.Store.Config(serverCtx).Get(serverCtx)
	if err != nil {
		return false
	}
	return cfg.Auth.SuperAdminNewUsers
}

func (s *Service) usersOrganizations(ctx context.Context, u *chronograf.User) ([]chronograf.Organization, error) {
	if u == nil {
		// TODO(desa): better error
		return nil, fmt.Errorf("user was nil")
	}

	orgIDs := map[string]bool{}
	for _, role := range u.Roles {
		orgIDs[role.Organization] = true
	}

	orgs := []chronograf.Organization{}
	for orgID, _ := range orgIDs {
		id, err := parseOrganizationID(orgID)
		org, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &id})
		if err != nil {
			return nil, err
		}
		orgs = append(orgs, *org)
	}

	sort.Slice(orgs, func(i, j int) bool {
		return orgs[i].ID < orgs[j].ID
	})

	return orgs, nil
}

func hasRoleInDefaultOrganization(u *chronograf.User, orgID string) bool {
	for _, role := range u.Roles {
		if role.Organization == orgID {
			return true
		}
	}

	return false
}
<commit_msg>Add comment about firstUser superAdmin logic<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sort"

	"golang.org/x/net/context"

	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/oauth2"
	"github.com/influxdata/chronograf/organizations"
)

type meLinks struct {
	Self string `json:"self"` // Self link mapping to this resource
}

type meResponse struct {
	*chronograf.User
	Links               meLinks                   `json:"links"`
	Organizations       []chronograf.Organization `json:"organizations,omitempty"`
	CurrentOrganization *chronograf.Organization  `json:"currentOrganization,omitempty"`
}

// If new user response is nil, return an empty meResponse because it
// indicates authentication is not needed
func newMeResponse(usr *chronograf.User) meResponse {
	base := "/chronograf/v1/users"
	name := "me"
	if usr != nil {
		name = PathEscape(fmt.Sprintf("%d", usr.ID))
	}

	return meResponse{
		User: usr,
		Links: meLinks{
			Self: fmt.Sprintf("%s/%s", base, name),
		},
	}
}

// TODO: This Scheme value is hard-coded temporarily since we only currently
// support OAuth2. This hard-coding should be removed whenever we add
// support for other authentication schemes.
func getScheme(ctx context.Context) (string, error) {
	return "oauth2", nil
}

func getPrincipal(ctx context.Context) (oauth2.Principal, error) {
	principal, ok := ctx.Value(oauth2.PrincipalKey).(oauth2.Principal)
	if !ok {
		return oauth2.Principal{}, fmt.Errorf("Token not found")
	}

	return principal, nil
}

func getValidPrincipal(ctx context.Context) (oauth2.Principal, error) {
	p, err := getPrincipal(ctx)
	if err != nil {
		return p, err
	}
	if p.Subject == "" {
		return oauth2.Principal{}, fmt.Errorf("Token not found")
	}
	if p.Issuer == "" {
		return oauth2.Principal{}, fmt.Errorf("Token not found")
	}
	return p, nil
}

type meRequest struct {
	// Organization is the OrganizationID
	Organization string `json:"organization"`
}

// UpdateMe changes the user's current organization on the JWT and responds
// with the same semantics as Me
func (s *Service) UpdateMe(auth oauth2.Authenticator) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		serverCtx := serverContext(ctx)
		principal, err := auth.Validate(ctx, r)
		if err != nil {
			s.Logger.Error(fmt.Sprintf("Invalid principal: %v", err))
			Error(w, http.StatusForbidden, "invalid principal", s.Logger)
			return
		}
		var req meRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			invalidJSON(w, s.Logger)
			return
		}

		// validate that the organization exists
		orgID, err := parseOrganizationID(req.Organization)
		if err != nil {
			Error(w, http.StatusInternalServerError, err.Error(), s.Logger)
			return
		}
		_, err = s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
		if err != nil {
			Error(w, http.StatusBadRequest, err.Error(), s.Logger)
			return
		}

		// validate that user belongs to organization
		ctx = context.WithValue(ctx, organizations.ContextKey, req.Organization)

		p, err := getValidPrincipal(ctx)
		if err != nil {
			invalidData(w, err, s.Logger)
			return
		}
		if p.Organization == "" {
			defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
			if err != nil {
				unknownErrorWithMessage(w, err, s.Logger)
				return
			}
			p.Organization = fmt.Sprintf("%d", defaultOrg.ID)
		}
		scheme, err := getScheme(ctx)
		if err != nil {
			invalidData(w, err, s.Logger)
			return
		}
		_, err = s.Store.Users(ctx).Get(ctx, chronograf.UserQuery{
			Name:     &p.Subject,
			Provider: &p.Issuer,
			Scheme:   &scheme,
		})
		if err == chronograf.ErrUserNotFound {
			// Since a user is not a part of this organization, we should tell them that they are Forbidden (403) from accessing this resource
			Error(w, http.StatusForbidden, err.Error(), s.Logger)
			return
		}
		if err != nil {
			Error(w, http.StatusBadRequest, err.Error(), s.Logger)
			return
		}

		// TODO: change to principal.CurrentOrganization
		principal.Organization = req.Organization

		if err := auth.Authorize(ctx, w, principal); err != nil {
			Error(w, http.StatusInternalServerError, err.Error(), s.Logger)
			return
		}

		ctx = context.WithValue(ctx, oauth2.PrincipalKey, principal)

		s.Me(w, r.WithContext(ctx))
	}
}

// Me does a findOrCreate based on the username in the context
func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	if !s.UseAuth {
		// If there's no authentication, return an empty user
		res := newMeResponse(nil)
		encodeJSON(w, http.StatusOK, res, s.Logger)
		return
	}

	p, err := getValidPrincipal(ctx)
	if err != nil {
		invalidData(w, err, s.Logger)
		return
	}
	scheme, err := getScheme(ctx)
	if err != nil {
		invalidData(w, err, s.Logger)
		return
	}

	ctx = context.WithValue(ctx, organizations.ContextKey, p.Organization)
	serverCtx := serverContext(ctx)

	if p.Organization == "" {
		defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
		if err != nil {
			unknownErrorWithMessage(w, err, s.Logger)
			return
		}
		p.Organization = fmt.Sprintf("%d", defaultOrg.ID)
	}

	usr, err := s.Store.Users(serverCtx).Get(serverCtx, chronograf.UserQuery{
		Name:     &p.Subject,
		Provider: &p.Issuer,
		Scheme:   &scheme,
	})
	if err != nil && err != chronograf.ErrUserNotFound {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}

	defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
	if err != nil {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}

	if usr != nil {
		// If the default org is private and the user has no roles, they should not have access
		if !defaultOrg.Public && len(usr.Roles) == 0 {
			Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
			return
		}
		orgID, err := parseOrganizationID(p.Organization)
		if err != nil {
			unknownErrorWithMessage(w, err, s.Logger)
			return
		}
		currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
		if err == chronograf.ErrOrganizationNotFound {
			// The intent is to force a the user to go through another auth flow
			Error(w, http.StatusForbidden, "user's current organization was not found", s.Logger)
			return
		}
		if err != nil {
			unknownErrorWithMessage(w, err, s.Logger)
			return
		}

		defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
		// If a user was added via the API, they might not yet be a member of the default organization
		// Here we check to verify that they are a user in the default organization
		if !hasRoleInDefaultOrganization(usr, defaultOrgID) {
			usr.Roles = append(usr.Roles, chronograf.Role{
				Organization: defaultOrgID,
				Name:         defaultOrg.DefaultRole,
			})
			if err := s.Store.Users(serverCtx).Update(serverCtx, usr); err != nil {
				unknownErrorWithMessage(w, err, s.Logger)
				return
			}
		}
		orgs, err := s.usersOrganizations(serverCtx, usr)
		if err != nil {
			unknownErrorWithMessage(w, err, s.Logger)
			return
		}
		res := newMeResponse(usr)
		res.Organizations = orgs
		res.CurrentOrganization = currentOrg
		encodeJSON(w, http.StatusOK, res, s.Logger)
		return
	}

	// If users must be explicitly added to the default organization, respond with 403
	// forbidden
	if !defaultOrg.Public {
		Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
		return
	}

	// Because we didnt find a user, making a new one
	user := &chronograf.User{
		Name:     p.Subject,
		Provider: p.Issuer,
		// TODO: This Scheme value is hard-coded temporarily since we only currently
		// support OAuth2. This hard-coding should be removed whenever we add
		// support for other authentication schemes.
		Scheme: scheme,
		Roles: []chronograf.Role{
			{
				Name: defaultOrg.DefaultRole,
				// This is the ID of the default organization
				Organization: fmt.Sprintf("%d", defaultOrg.ID),
			},
		},
		// TODO(desa): this needs a better name
		SuperAdmin: s.newUsersAreSuperAdmin(),
	}

	newUser, err := s.Store.Users(serverCtx).Add(serverCtx, user)
	if err != nil {
		msg := fmt.Errorf("error storing user %s: %v", user.Name, err)
		unknownErrorWithMessage(w, msg, s.Logger)
		return
	}

	orgs, err := s.usersOrganizations(serverCtx, newUser)
	if err != nil {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}
	orgID, err := parseOrganizationID(p.Organization)
	if err != nil {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}
	currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
	if err != nil {
		unknownErrorWithMessage(w, err, s.Logger)
		return
	}
	res := newMeResponse(newUser)
	res.Organizations = orgs
	res.CurrentOrganization = currentOrg
	encodeJSON(w, http.StatusOK, res, s.Logger)
}

func (s *Service) firstUser() bool {
	serverCtx := serverContext(context.Background())
	numUsers, err := s.Store.Users(serverCtx).Num(serverCtx)
	if err != nil {
		return false
	}

	return numUsers == 0
}
func (s *Service) newUsersAreSuperAdmin() bool {
	// It's not necessary to enforce that the first user is superAdmin here, since
	// superAdminNewUsers defaults to true, but there's nothing else in the
	// application that dictates that it must be true.
	// So for that reason, we kept this here for now. We've discussed the
	// future possibility of allowing users to override default values via CLI and
	// this case could possibly happen then.
	if s.firstUser() {
		return true
	}
	serverCtx := serverContext(context.Background())
	cfg, err := s.Store.Config(serverCtx).Get(serverCtx)
	if err != nil {
		return false
	}
	return cfg.Auth.SuperAdminNewUsers
}

func (s *Service) usersOrganizations(ctx context.Context, u *chronograf.User) ([]chronograf.Organization, error) {
	if u == nil {
		// TODO(desa): better error
		return nil, fmt.Errorf("user was nil")
	}

	orgIDs := map[string]bool{}
	for _, role := range u.Roles {
		orgIDs[role.Organization] = true
	}

	orgs := []chronograf.Organization{}
	for orgID, _ := range orgIDs {
		id, err := parseOrganizationID(orgID)
		org, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &id})
		if err != nil {
			return nil, err
		}
		orgs = append(orgs, *org)
	}

	sort.Slice(orgs, func(i, j int) bool {
		return orgs[i].ID < orgs[j].ID
	})

	return orgs, nil
}

func hasRoleInDefaultOrganization(u *chronograf.User, orgID string) bool {
	for _, role := range u.Roles {
		if role.Organization == orgID {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>// Package exif provides a go interface for exiftool.
package exif

import (
	"errors"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
)

const EXIFTOOL = "exiftool"

// Set of all writable tags
var writable = map[string]bool{}

// Set of tags exiftool reports are writable, but aren't/shouldn't be!
var stupidUnwritableWritableTags = map[string]bool{
	"PreviewImageLength": true,
  "PreviewImageStart": true,
	"ThumbnailLength": true,
	"ThumbnailOffset": true,
	"Directory": true,
	"FileName": true,
}

// This needs working out! Though this seems to be working for now...
func shellEscape(s string) string {
	// return strings.Replace(s, " ", "\\ ", -1)
	return s
}

type Exif struct {
	path     string
	data     map[string] string
	changed  map[string] bool // a poor man's set
}

// Exists returns true if "exiftool" is installed anywhere on the user's $PATH.
func Exists() bool {
	_, err := exec.LookPath(EXIFTOOL)
	return err == nil
}

// New creates an empty Exif object. To initialize with the exif data of an
// existing image use Load.
func New() *Exif {
	return &Exif{
		path:    "",
		data:    map[string] string{},
		changed: map[string] bool{},
	}
}

// Load creates a new Exif object, populated with the exif data of the file at
// the path given.
func Load(path string) *Exif {
	if !Exists() { return New() }

	if len(writable) == 0 {
		// Get the writable tags then
		out, _ := exec.Command("exiftool", "-listw").Output()
		for _, tag := range strings.Split(string(out), " ") {
			key := strings.TrimSpace(tag)
			if _, ok := stupidUnwritableWritableTags[key]; !ok {
				writable[key] = true
			}
		}
	}

	out, err := exec.Command("exiftool", "-s", path).Output()
	if err != nil {
		log.Fatal(err)
	}

	lines := strings.Split(string(out), "\n")
	exif  := New()
	exif.path = path

	for _, line := range lines[:len(lines)-1] {
		parts := strings.SplitN(line, ":", 2)
		key := strings.Replace(parts[0], " ", "", -1)
		if _, ok := writable[key]; ok {
			val := strings.TrimSpace(parts[1])
			if !strings.Contains(val, "use -b option to extract") {
				exif.data[key] = val
			}
		}
	}

	return exif
}

// Decode can be used for unnamed files, for example STDIN. It loads the exif
// data from the file given.
func Decode(r io.Reader) *Exif {
	if !Exists() { return New() }

	// Create a temporary file for exiftool to read
	tmp, _ := ioutil.TempFile("", "img-exif-")
	path := tmp.Name()
	// Make sure the file is deleted after
	defer func() { os.Remove(path) }()
	// Copy the file to it
	io.Copy(tmp, r)
	// Load normally
	return Load(path)
}

// Get returns the value for the key given. The key should be in CamelCase form.
func (e Exif) Get(key string) string {
	val, ok := e.data[key]
	if !ok { return "" }
	return val
}

// Set changes the value of the key. The key should be in CamelCase form.
func (e Exif) Set(key, val string) {
	if _, ok := writable[key]; ok {
		e.changed[key] = true
		e.data[key] = val
	}
}

// Keys returns a list of all exif data keys present.
func (e Exif) Keys() []string {
	keys := make([]string, len(e.data))
	i := 0
	for k, _ := range e.data {
		keys[i] = k
		i++
	}
	return keys
}

func (e Exif) String() string {
	if len(e.data) == 0 { return "(Exif:empty)" }
	s := ""
	for k, v := range e.data {
		s += k + " = " + v + "\n"
	}
	return s[:len(s)-1]
}

// Save will write the changed (and only the changed) exif data to the path that
// was initially given.
func (e Exif) Save() error {
	if e.path == "" {
		return errors.New("exif.Exif does not have path to save to")
	}
	args := make([]string, len(e.changed)+2)
	args[0] = "-overwrite_original"

	i := 1
	for k, _ := range e.changed {
		args[i] = "-" + k + "=" + shellEscape(e.Get(k))
		i++
	}

	args[i] = e.path
	return exec.Command("exiftool", args...).Run()
}

// Write will write to the path given all of the exif data (changed or unchanged).
func (e Exif) Write(path string) error {
	if !Exists() {
		return errors.New("exif package requires 'exiftool' to load/write exif data")
	}

	args := make([]string, len(e.data)+3)
	args[0] = "-overwrite_original"

	i := 1
	for k, v := range e.data {
		args[i] = "-" + k + "=" + shellEscape(v)
		i++
	}

	args[i] = path
	return exec.Command("exiftool", args...).Run()
}
<commit_msg>Fix chaining multiple commands together<commit_after>// Package exif provides a go interface for exiftool.
package exif

import (
	"errors"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

const EXIFTOOL = "exiftool"

// Set of all writable tags
var writable = map[string]bool{}

// Set of tags exiftool reports are writable, but aren't/shouldn't be!
var stupidUnwritableWritableTags = map[string]bool{
	"PreviewImageLength": true,
  "PreviewImageStart": true,
	"ThumbnailLength": true,
	"ThumbnailOffset": true,
	"Directory": true,
	"FileName": true,
}

// This needs working out! Though this seems to be working for now...
func shellEscape(s string) string {
	// return strings.Replace(s, " ", "\\ ", -1)
	return s
}

type Exif struct {
	path     string
	data     map[string] string
	changed  map[string] bool // a poor man's set
}

// Exists returns true if "exiftool" is installed anywhere on the user's $PATH.
func Exists() bool {
	_, err := exec.LookPath(EXIFTOOL)
	return err == nil
}

// New creates an empty Exif object. To initialize with the exif data of an
// existing image use Load.
func New() *Exif {
	return &Exif{
		path:    "",
		data:    map[string] string{},
		changed: map[string] bool{},
	}
}

func getWritableTags() {
	if len(writable) == 0 {
		// Get the writable tags then
		out, _ := exec.Command("exiftool", "-listw").Output()
		for _, tag := range strings.Split(string(out), " ") {
			key := strings.TrimSpace(tag)
			if _, ok := stupidUnwritableWritableTags[key]; !ok {
				writable[key] = true
			}
		}
	}
}

// Load creates a new Exif object, populated with the exif data of the file at
// the path given. It will silently fail if any errors are encountered.
func Load(path string) *Exif {
	if !Exists() { return New() }

	out, err := exec.Command("exiftool", "-s", path).Output()
	if err != nil {
		return New()
	}

	getWritableTags()

	lines := strings.Split(string(out), "\n")
	exif  := New()
	exif.path = path

	for _, line := range lines[:len(lines)-1] {
		parts := strings.SplitN(line, ":", 2)
		key := strings.Replace(parts[0], " ", "", -1)
		if _, ok := writable[key]; ok {
			val := strings.TrimSpace(parts[1])
			if !strings.Contains(val, "use -b option to extract") {
				exif.data[key] = val
			}
		}
	}

	return exif
}

// Decode can be used for unnamed files, for example STDIN. It loads the exif
// data from the file given.
func Decode(r io.Reader) *Exif {
	if !Exists() { return New() }

	// Create a temporary file for exiftool to read
	tmp, _ := ioutil.TempFile("", "img-exif-")
	path := tmp.Name()
	// Make sure the file is deleted after
	defer func() { os.Remove(path) }()
	// Copy the file to it
	io.Copy(tmp, r)
	// Load normally
	return Load(path)
}

// Get returns the value for the key given. The key should be in CamelCase form.
func (e Exif) Get(key string) string {
	val, ok := e.data[key]
	if !ok { return "" }
	return val
}

// Set changes the value of the key. The key should be in CamelCase form.
func (e Exif) Set(key, val string) {
	if _, ok := writable[key]; ok {
		e.changed[key] = true
		e.data[key] = val
	}
}

// Keys returns a list of all exif data keys present.
func (e Exif) Keys() []string {
	keys := make([]string, len(e.data))
	i := 0
	for k, _ := range e.data {
		keys[i] = k
		i++
	}
	return keys
}

func (e Exif) String() string {
	if len(e.data) == 0 { return "(Exif:empty)" }
	s := ""
	for k, v := range e.data {
		s += k + " = " + v + "\n"
	}
	return s[:len(s)-1]
}

// Save will write the changed (and only the changed) exif data to the path that
// was initially given.
func (e Exif) Save() error {
	if e.path == "" {
		return errors.New("exif.Exif does not have path to save to")
	}
	args := make([]string, len(e.changed)+2)
	args[0] = "-overwrite_original"

	i := 1
	for k, _ := range e.changed {
		args[i] = "-" + k + "=" + shellEscape(e.Get(k))
		i++
	}

	args[i] = e.path
	return exec.Command("exiftool", args...).Run()
}

// Write will write to the path given all of the exif data (changed or unchanged).
func (e Exif) Write(path string) error {
	if !Exists() {
		return errors.New("exif package requires 'exiftool' to load/write exif data")
	}

	args := make([]string, len(e.data)+3)
	args[0] = "-overwrite_original"

	i := 1
	for k, v := range e.data {
		args[i] = "-" + k + "=" + shellEscape(v)
		i++
	}

	args[i] = path
	return exec.Command("exiftool", args...).Run()
}
<|endoftext|>


<commit_before>package sirius

type EID string
type ExtensionConfig map[string]interface{}

type Extension interface {
	Run(Message, ExtensionConfig) (MessageAction, error)
}

type ExtensionLoader interface {
	Load(EID) (Extension, error)
}

// Read fetches a value of any type for key.
// Returns def if key is not set.
func (cfg ExtensionConfig) Read(key string, def interface{}) interface{} {
	if val, ok := cfg[key]; ok {
		return val
	}

	return def
}

// String fetches a string value for key.
// Returns def if key is not set
func (cfg ExtensionConfig) String(key string, def string) string {
	if val, ok := cfg[key]; ok {
		if s, ok := val.(string); ok {
			return s
		}
	}

	return def
}

// Integer fetches an integer value for key.
// Returns def if key is not set
func (cfg ExtensionConfig) Integer(key string, def int) int {
	if val, ok := cfg[key]; ok {
		if i, ok := val.(int); ok {
			return i
		}
	}

	return def
}

// Boolean fetches a boolean value for key.
// Returns false if key is not set
func (cfg ExtensionConfig) Boolean(key string) bool {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case bool:
			return b
		case int:
			// Require explicit 0 or 1
			if b == 0 {
				return false
			} else if b == 1 {
				return true
			}
		}
	}

	return false
}

// Float fetches a float value for key.
// Returns def if key is not set
func (cfg ExtensionConfig) Float(key string, def float64) float64 {
	if val, ok := cfg[key]; ok {
		switch f := val.(type) {
		case float32:
			return float64(f)
		case float64:
			return f
		}
	}

	return def
}

// List fetches a list value for key.
// Returns def if key is not set
func (cfg ExtensionConfig) List(key string) []string {
	if val, ok := cfg[key]; ok {
		if l, ok := val.([]string); ok {
			return l
		}
	}

	return []string{}
}
<commit_msg>Update comment with new config List default return value<commit_after>package sirius

type EID string
type ExtensionConfig map[string]interface{}

type Extension interface {
	Run(Message, ExtensionConfig) (MessageAction, error)
}

type ExtensionLoader interface {
	Load(EID) (Extension, error)
}

// Read fetches a value of any type for key.
// Returns def if key is not set.
func (cfg ExtensionConfig) Read(key string, def interface{}) interface{} {
	if val, ok := cfg[key]; ok {
		return val
	}

	return def
}

// String fetches a string value for key.
// Returns def if key is not set
func (cfg ExtensionConfig) String(key string, def string) string {
	if val, ok := cfg[key]; ok {
		if s, ok := val.(string); ok {
			return s
		}
	}

	return def
}

// Integer fetches an integer value for key.
// Returns def if key is not set
func (cfg ExtensionConfig) Integer(key string, def int) int {
	if val, ok := cfg[key]; ok {
		if i, ok := val.(int); ok {
			return i
		}
	}

	return def
}

// Boolean fetches a boolean value for key.
// Returns false if key is not set
func (cfg ExtensionConfig) Boolean(key string) bool {
	if val, ok := cfg[key]; ok {
		switch b := val.(type) {
		case bool:
			return b
		case int:
			// Require explicit 0 or 1
			if b == 0 {
				return false
			} else if b == 1 {
				return true
			}
		}
	}

	return false
}

// Float fetches a float value for key.
// Returns def if key is not set
func (cfg ExtensionConfig) Float(key string, def float64) float64 {
	if val, ok := cfg[key]; ok {
		switch f := val.(type) {
		case float32:
			return float64(f)
		case float64:
			return f
		}
	}

	return def
}

// List fetches a list value for key.
// Returns an empty list if key is not set.
func (cfg ExtensionConfig) List(key string) []string {
	if val, ok := cfg[key]; ok {
		if l, ok := val.([]string); ok {
			return l
		}
	}

	return []string{}
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package app

import (
	"fmt"
	"reflect"
	"testing"
)

func flagError(val string) error {
	return fmt.Errorf("invalid value %q for authFlow (must be one of %q, %q, or %q)", val, gcrAuthFlow, dockerConfigAuthFlow, dockerConfigURLAuthFlow)
}

func TestValidateAuthFlow(t *testing.T) {
	type FlagResult struct {
		Flow  string
		Error error
	}
	tests := []FlagResult{
		{Flow: gcrAuthFlow, Error: nil},
		{Flow: dockerConfigAuthFlow, Error: nil},
		{Flow: dockerConfigURLAuthFlow, Error: nil},
		{Flow: "bad-flow", Error: &AuthFlowFlagError{flagValue: "bad-flow"}},
		{Flow: "", Error: &AuthFlowFlagError{flagValue: ""}},
	}
	for _, tc := range tests {
		err := validateFlags(tc.Flow)
		if err != nil && tc.Error == nil {
			t.Errorf("with flow %q unexpected error %q", tc.Flow, err)
		}
		if err == nil && tc.Error != nil {
			t.Errorf("with flow %q did not get expected error %q", tc.Flow, err)
		}
		if err != nil && tc.Error != nil {
			if reflect.TypeOf(err) != reflect.TypeOf(tc.Error) {
				t.Errorf("with flow %q got unexpected error type %q (expected %q)", tc.Flow, reflect.TypeOf(err), reflect.TypeOf(tc.Error))
			}
		}
	}
}

func providerError(val string) error {
	return fmt.Errorf("unrecognized auth flow \"%s\"", val)
}

func TestProviderFromFlow(t *testing.T) {
	type ProviderResult struct {
		Flow  string
		Type  string
		Error error
	}
	tests := []ProviderResult{
		{Flow: gcrAuthFlow, Type: "ContainerRegistryProvider", Error: nil},
		{Flow: dockerConfigAuthFlow, Type: "DockerConfigKeyProvider", Error: nil},
		{Flow: dockerConfigURLAuthFlow, Type: "DockerConfigURLKeyProvider", Error: nil},
		{Flow: "bad-flow", Type: "", Error: &AuthFlowTypeError{requestedFlow: "bad-flow"}},
		{Flow: "", Type: "", Error: &AuthFlowTypeError{requestedFlow: ""}},
	}
	for _, tc := range tests {
		provider, err := providerFromFlow(tc.Flow)
		if err != nil && tc.Error == nil {
			t.Errorf("with flow %q unexpected error %q", tc.Flow, err)
		}
		if err == nil && tc.Error != nil {
			t.Errorf("with flow %q did not get expected error %q", tc.Flow, err)
		}
		if err != nil && tc.Error != nil {
			if reflect.TypeOf(err) != reflect.TypeOf(tc.Error) {
				t.Errorf("with flow %q got unexpected error type %q (expected %q)", tc.Flow, reflect.TypeOf(err), reflect.TypeOf(tc.Error))
			}
		}
		if tc.Type == "" && provider != nil {
			t.Errorf("with flow %q got unexpectedly non-nil provider %q", provider)
		}
		// The nil check is meant for test cases where provider is nil on purpose,
		// i,e, for error cases - any errors will get tested and caught above, so in
		// those cases, we don't need to run additional checks
		if provider != nil {
			providerType := reflect.TypeOf(provider).String()
			if providerType != "*gcpcredential."+tc.Type {
				t.Errorf("with flow %q unexpected provider type %q", tc.Flow, providerType)
			}
		}
	}
}
<commit_msg>Remove flagError and providerError from getcredentials_test.go.<commit_after>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package app

import (
	"reflect"
	"testing"
)

func TestValidateAuthFlow(t *testing.T) {
	type FlagResult struct {
		Flow  string
		Error error
	}
	tests := []FlagResult{
		{Flow: gcrAuthFlow, Error: nil},
		{Flow: dockerConfigAuthFlow, Error: nil},
		{Flow: dockerConfigURLAuthFlow, Error: nil},
		{Flow: "bad-flow", Error: &AuthFlowFlagError{flagValue: "bad-flow"}},
		{Flow: "", Error: &AuthFlowFlagError{flagValue: ""}},
		{Flow: "Gcrauthflow", Error: &AuthFlowFlagError{flagValue: "Gcrauthflow"}},
	}
	for _, tc := range tests {
		err := validateFlags(tc.Flow)
		if err != nil && tc.Error == nil {
			t.Errorf("with flow %q unexpected error %q", tc.Flow, err)
		}
		if err == nil && tc.Error != nil {
			t.Errorf("with flow %q did not get expected error %q", tc.Flow, err)
		}
		if err != nil && tc.Error != nil {
			if reflect.TypeOf(err) != reflect.TypeOf(tc.Error) {
				t.Errorf("with flow %q got unexpected error type %q (expected %q)", tc.Flow, reflect.TypeOf(err), reflect.TypeOf(tc.Error))
			}
		}
	}
}

func TestProviderFromFlow(t *testing.T) {
	type ProviderResult struct {
		Flow  string
		Type  string
		Error error
	}
	tests := []ProviderResult{
		{Flow: gcrAuthFlow, Type: "ContainerRegistryProvider", Error: nil},
		{Flow: dockerConfigAuthFlow, Type: "DockerConfigKeyProvider", Error: nil},
		{Flow: dockerConfigURLAuthFlow, Type: "DockerConfigURLKeyProvider", Error: nil},
		{Flow: "bad-flow", Type: "", Error: &AuthFlowTypeError{requestedFlow: "bad-flow"}},
		{Flow: "", Type: "", Error: &AuthFlowTypeError{requestedFlow: ""}},
	}
	for _, tc := range tests {
		provider, err := providerFromFlow(tc.Flow)
		if err != nil && tc.Error == nil {
			t.Errorf("with flow %q unexpected error %q", tc.Flow, err)
		}
		if err == nil && tc.Error != nil {
			t.Errorf("with flow %q did not get expected error %q", tc.Flow, err)
		}
		if err != nil && tc.Error != nil {
			if reflect.TypeOf(err) != reflect.TypeOf(tc.Error) {
				t.Errorf("with flow %q got unexpected error type %q (expected %q)", tc.Flow, reflect.TypeOf(err), reflect.TypeOf(tc.Error))
			}
		}
		if tc.Type == "" && provider != nil {
			t.Errorf("with flow %q got unexpectedly non-nil provider %q", provider)
		}
		// The nil check is meant for test cases where provider is nil on purpose,
		// i,e, for error cases - any errors will get tested and caught above, so in
		// those cases, we don't need to run additional checks
		if provider != nil {
			providerType := reflect.TypeOf(provider).String()
			if providerType != "*gcpcredential."+tc.Type {
				t.Errorf("with flow %q unexpected provider type %q", tc.Flow, providerType)
			}
		}
	}
}
<|endoftext|>


<commit_before>package dialect

import (
	"database/sql"
	"fmt"
	"time"
)

type sqlite3 struct{}

func (s *sqlite3) BinVar() string {
	return "?"
}

func (s *sqlite3) SupportLastInsertId() bool {
	return true
}

func (s *sqlite3) SqlTag(column interface{}, size int) string {
	switch column.(type) {
	case time.Time:
		return "datetime"
	case bool, sql.NullBool:
		return "bool"
	case int, int8, int16, int32, uint, uint8, uint16, uint32:
		return "integer"
	case int64, uint64, sql.NullInt64:
		return "bigint"
	case float32, float64, sql.NullFloat64:
		return "real"
	case string, sql.NullString:
		if size > 0 && size < 65532 {
			return fmt.Sprintf("varchar(%d)", size)
		} else {
			return "text"
		}
	default:
		panic("Invalid sql type for sqlite3")
	}
}

func (s *sqlite3) PrimaryKeyTag(column interface{}, size int) string {
	return "INTEGER PRIMARY KEY"
}

func (s *sqlite3) ReturningStr(key string) (str string) {
	return
}
<commit_msg>Add blob for Sqlite3<commit_after>package dialect

import (
	"database/sql"
	"fmt"
	"time"
)

type sqlite3 struct{}

func (s *sqlite3) BinVar() string {
	return "?"
}

func (s *sqlite3) SupportLastInsertId() bool {
	return true
}

func (s *sqlite3) SqlTag(column interface{}, size int) string {
	switch column.(type) {
	case time.Time:
		return "datetime"
	case bool, sql.NullBool:
		return "bool"
	case int, int8, int16, int32, uint, uint8, uint16, uint32:
		return "integer"
	case int64, uint64, sql.NullInt64:
		return "bigint"
	case float32, float64, sql.NullFloat64:
		return "real"
	case []byte:
		return "blob"
	case string, sql.NullString:
		if size > 0 && size < 65532 {
			return fmt.Sprintf("varchar(%d)", size)
		} else {
			return "text"
		}
	default:
		panic("Invalid sql type for sqlite3")
	}
}

func (s *sqlite3) PrimaryKeyTag(column interface{}, size int) string {
	return "INTEGER PRIMARY KEY"
}

func (s *sqlite3) ReturningStr(key string) (str string) {
	return
}
<|endoftext|>