<commit_before>/*
	Copyright (c) 2016, Percona LLC and/or its affiliates. All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package pmm

import (
	"fmt"
	"net"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/prometheus/client_golang/api/prometheus"
	"golang.org/x/net/context"
)

// CheckNetwork check connectivity between client and server.
func (a *Admin) CheckNetwork(noEmoji bool) error {
	// Check Consul health.
	consulStatus := a.ServerAlive()

	// Check QAN API health.
	qanStatus := false
	url := a.qanapi.URL(a.Config.ServerAddress, qanAPIBasePath, "ping")
	if resp, _, err := a.qanapi.Get(url); err == nil {
		if resp.StatusCode == http.StatusOK && resp.Header.Get("X-Percona-Qan-Api-Version") != "" {
			qanStatus = true
		}
	}

	// Check Prometheus API.
	promStatus := false
	promData := a.getPromTargets()
	if promData != "" {
		promStatus = true
	}

	fmt.Println("PMM Network Status\n")
	fmt.Printf("%-6s | %s\n", "Server", a.Config.ServerAddress)
	fmt.Printf("%-6s | %s\n\n", "Client", a.Config.ClientAddress)
	fmt.Println("* Client > Server")
	fmt.Printf("%-15s %-13s\n", strings.Repeat("-", 15), strings.Repeat("-", 13))
	fmt.Printf("%-15s %-13s\n", "SERVICE", "CONNECTIVITY")
	fmt.Printf("%-15s %-13s\n", strings.Repeat("-", 15), strings.Repeat("-", 13))
	fmt.Printf("%-15s %-13s\n", "Consul API", emojiStatus(noEmoji, consulStatus))
	fmt.Printf("%-15s %-13s\n", "QAN API", emojiStatus(noEmoji, qanStatus))
	fmt.Printf("%-15s %-13s\n", "Prometheus API", emojiStatus(noEmoji, promStatus))
	fmt.Println()

	a.testNetwork()
	fmt.Println()

	node, _, err := a.consulapi.Catalog().Node(a.Config.ClientName, nil)
	if err != nil || node == nil {
		fmt.Printf("No monitoring registered for this node under identifier '%s'.\n\n", a.Config.ClientName)
		return nil
	}

	if !promStatus {
		fmt.Println("Prometheus is down. Please check if PMM server container runs properly.\n")
		return nil
	}

	// Check Prometheus endpoint status.
	svcTable := []instanceStatus{}
	errStatus := false
	for _, svc := range node.Services {
		metricType := svc.Service
		if metricType == "mysql-hr" {
			metricType = "mysql"
		} else if metricType == "mysql-mr" || metricType == "mysql-lr" || metricType == "queries" {
			continue
		}

		name := "-"
		for _, tag := range svc.Tags {
			if strings.HasPrefix(tag, "alias_") {
				name = tag[6:]
				continue
			}

		}

		status := a.checkPromTargetStatus(promData, name, metricType, svc.Port)
		if !status {
			errStatus = true
		}
		row := instanceStatus{
			Type:   metricType,
			Name:   name,
			Port:   fmt.Sprintf("%d", svc.Port),
			Status: emojiStatus(noEmoji, status),
		}
		svcTable = append(svcTable, row)
	}

	fmt.Println("* Server > Client")
	if len(svcTable) == 0 {
		fmt.Println("No Prometheus endpoints found.\n")
		return nil
	}
	maxNameLen := 5
	for _, in := range svcTable {
		if len(in.Name) > maxNameLen {
			maxNameLen = len(in.Name)
		}
	}
	maxNameLen++
	linefmt := "%-8s %-" + fmt.Sprintf("%d", maxNameLen) + "s %-22s %-13s\n"
	fmt.Printf(linefmt, strings.Repeat("-", 8), strings.Repeat("-", maxNameLen), strings.Repeat("-", 22),
		strings.Repeat("-", 13))
	fmt.Printf(linefmt, "METRIC", "NAME", "PROMETHEUS ENDPOINT", "REMOTE STATE")
	fmt.Printf(linefmt, strings.Repeat("-", 8), strings.Repeat("-", maxNameLen), strings.Repeat("-", 22),
		strings.Repeat("-", 13))
	sort.Sort(sortOutput(svcTable))
	for _, i := range svcTable {
		fmt.Printf(linefmt, i.Type, i.Name, a.Config.ClientAddress+":"+i.Port, i.Status)
	}

	if errStatus {
		fmt.Println(`
For endpoints in problem state, please check if the corresponding service is running ("pmm-admin list").
If all endpoints are down here and "pmm-admin list" shows all services are up,
please check the firewall settings whether this system allows incoming connections by address:port in question.`)
	}
	fmt.Println()
	return nil
}

// getPromTargets get Prometheus targets and states.
func (a *Admin) getPromTargets() string {
	config := prometheus.Config{Address: fmt.Sprintf("http://%s/prometheus", a.Config.ServerAddress)}
	client, err := prometheus.New(config)
	if err != nil {
		return ""
	}

	// Retrieve all "up" time series.
	if res, err := prometheus.NewQueryAPI(client).Query(context.Background(), "up", time.Now()); err == nil {
		return res.String()
	}
	return ""
}

// checkPromTargetStatus check Prometheus target state by metric labels.
func (a *Admin) checkPromTargetStatus(data, alias, job string, port int) bool {
	query := fmt.Sprintf(`up{alias="%s", instance="%s:%d", job="%s"}`, alias, a.Config.ClientAddress, port, job)
	for _, row := range strings.Split(data, "\n") {
		vals := strings.Split(row, " => ")
		if vals[0] != query || len(vals) != 2 {
			continue
		}
		if string(vals[1][0]) == "1" {
			return true
		} else {
			return false
		}
	}
	return false
}

// testNetwork measure round trip duration of server connection.
func (a *Admin) testNetwork() {
	conn := &networkTransport{
		dialer: &net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		},
	}
	conn.rtp = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial:  conn.dial,
	}
	client := &http.Client{Transport: conn}

	resp, err := client.Get(fmt.Sprintf("http://%s", a.Config.ServerAddress))
	if err != nil {
		fmt.Println("Unable to measure a connection performance as it takes longer than 30 sec.")
		fmt.Println("Looks like there is a bad connectivity and it may affect your monitoring.")
		return
	}
	defer resp.Body.Close()

	fmt.Printf("%-19s | %v\n", "Connection duration", conn.connEnd.Sub(conn.connStart))
	fmt.Printf("%-19s | %v\n", "Request duration", conn.reqEnd.Sub(conn.reqStart)-conn.connEnd.Sub(conn.connStart))
	fmt.Printf("%-19s | %v\n", "Full round trip", conn.reqEnd.Sub(conn.reqStart))
}

type networkTransport struct {
	rtp       http.RoundTripper
	dialer    *net.Dialer
	connStart time.Time
	connEnd   time.Time
	reqStart  time.Time
	reqEnd    time.Time
}

func (conn *networkTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	conn.reqStart = time.Now()
	resp, err := conn.rtp.RoundTrip(r)
	conn.reqEnd = time.Now()
	return resp, err
}

func (conn *networkTransport) dial(network, addr string) (net.Conn, error) {
	conn.connStart = time.Now()
	cn, err := conn.dialer.Dial(network, addr)
	conn.connEnd = time.Now()
	return cn, err
}

// Map status to emoji or text.
func emojiStatus(noEmoji, status bool) string {
	switch true {
	case noEmoji && status:
		return "OK"
	case noEmoji && !status:
		return "PROBLEM"
	case !noEmoji && status:
		return emojiHappy
	case !noEmoji && !status:
		return emojiUnhappy
	}
	return "N/A"
}
<commit_msg>Check all prom mysql endpoints<commit_after>/*
	Copyright (c) 2016, Percona LLC and/or its affiliates. All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package pmm

import (
	"fmt"
	"net"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/prometheus/client_golang/api/prometheus"
	"golang.org/x/net/context"
)

// CheckNetwork check connectivity between client and server.
func (a *Admin) CheckNetwork(noEmoji bool) error {
	// Check Consul health.
	consulStatus := a.ServerAlive()

	// Check QAN API health.
	qanStatus := false
	url := a.qanapi.URL(a.Config.ServerAddress, qanAPIBasePath, "ping")
	if resp, _, err := a.qanapi.Get(url); err == nil {
		if resp.StatusCode == http.StatusOK && resp.Header.Get("X-Percona-Qan-Api-Version") != "" {
			qanStatus = true
		}
	}

	// Check Prometheus API.
	promStatus := false
	promData := a.getPromTargets()
	if promData != "" {
		promStatus = true
	}

	fmt.Println("PMM Network Status\n")
	fmt.Printf("%-6s | %s\n", "Server", a.Config.ServerAddress)
	fmt.Printf("%-6s | %s\n\n", "Client", a.Config.ClientAddress)
	fmt.Println("* Client > Server")
	fmt.Printf("%-15s %-13s\n", strings.Repeat("-", 15), strings.Repeat("-", 13))
	fmt.Printf("%-15s %-13s\n", "SERVICE", "CONNECTIVITY")
	fmt.Printf("%-15s %-13s\n", strings.Repeat("-", 15), strings.Repeat("-", 13))
	fmt.Printf("%-15s %-13s\n", "Consul API", emojiStatus(noEmoji, consulStatus))
	fmt.Printf("%-15s %-13s\n", "QAN API", emojiStatus(noEmoji, qanStatus))
	fmt.Printf("%-15s %-13s\n", "Prometheus API", emojiStatus(noEmoji, promStatus))
	fmt.Println()

	a.testNetwork()
	fmt.Println()

	node, _, err := a.consulapi.Catalog().Node(a.Config.ClientName, nil)
	if err != nil || node == nil {
		fmt.Printf("No monitoring registered for this node under identifier '%s'.\n\n", a.Config.ClientName)
		return nil
	}

	if !promStatus {
		fmt.Println("Prometheus is down. Please check if PMM server container runs properly.\n")
		return nil
	}

	// Check Prometheus endpoint status.
	svcTable := []instanceStatus{}
	errStatus := false
	for _, svc := range node.Services {
		metricType := svc.Service
		if metricType == "mysql-hr" || metricType == "mysql-mr" || metricType == "mysql-lr" {
			metricType = "mysql"
		} else if metricType == "queries" {
			continue
		}

		name := "-"
		for _, tag := range svc.Tags {
			if strings.HasPrefix(tag, "alias_") {
				name = tag[6:]
				continue
			}

		}

		status := a.checkPromTargetStatus(promData, name, metricType, svc.Port)
		if !status {
			errStatus = true
		}
		row := instanceStatus{
			Type:   metricType,
			Name:   name,
			Port:   fmt.Sprintf("%d", svc.Port),
			Status: emojiStatus(noEmoji, status),
		}
		svcTable = append(svcTable, row)
	}

	fmt.Println("* Server > Client")
	if len(svcTable) == 0 {
		fmt.Println("No Prometheus endpoints found.\n")
		return nil
	}
	maxNameLen := 5
	for _, in := range svcTable {
		if len(in.Name) > maxNameLen {
			maxNameLen = len(in.Name)
		}
	}
	maxNameLen++
	linefmt := "%-8s %-" + fmt.Sprintf("%d", maxNameLen) + "s %-22s %-13s\n"
	fmt.Printf(linefmt, strings.Repeat("-", 8), strings.Repeat("-", maxNameLen), strings.Repeat("-", 22),
		strings.Repeat("-", 13))
	fmt.Printf(linefmt, "METRIC", "NAME", "PROMETHEUS ENDPOINT", "REMOTE STATE")
	fmt.Printf(linefmt, strings.Repeat("-", 8), strings.Repeat("-", maxNameLen), strings.Repeat("-", 22),
		strings.Repeat("-", 13))
	sort.Sort(sortOutput(svcTable))
	for _, i := range svcTable {
		fmt.Printf(linefmt, i.Type, i.Name, a.Config.ClientAddress+":"+i.Port, i.Status)
	}

	if errStatus {
		fmt.Println(`
For endpoints in problem state, please check if the corresponding service is running ("pmm-admin list").
If all endpoints are down here and "pmm-admin list" shows all services are up,
please check the firewall settings whether this system allows incoming connections by address:port in question.`)
	}
	fmt.Println()
	return nil
}

// getPromTargets get Prometheus targets and states.
func (a *Admin) getPromTargets() string {
	config := prometheus.Config{Address: fmt.Sprintf("http://%s/prometheus", a.Config.ServerAddress)}
	client, err := prometheus.New(config)
	if err != nil {
		return ""
	}

	// Retrieve all "up" time series.
	if res, err := prometheus.NewQueryAPI(client).Query(context.Background(), "up", time.Now()); err == nil {
		return res.String()
	}
	return ""
}

// checkPromTargetStatus check Prometheus target state by metric labels.
func (a *Admin) checkPromTargetStatus(data, alias, job string, port int) bool {
	query := fmt.Sprintf(`up{alias="%s", instance="%s:%d", job="%s"}`, alias, a.Config.ClientAddress, port, job)
	for _, row := range strings.Split(data, "\n") {
		vals := strings.Split(row, " => ")
		if vals[0] != query || len(vals) != 2 {
			continue
		}
		if string(vals[1][0]) == "1" {
			return true
		} else {
			return false
		}
	}
	return false
}

// testNetwork measure round trip duration of server connection.
func (a *Admin) testNetwork() {
	conn := &networkTransport{
		dialer: &net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		},
	}
	conn.rtp = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial:  conn.dial,
	}
	client := &http.Client{Transport: conn}

	resp, err := client.Get(fmt.Sprintf("http://%s", a.Config.ServerAddress))
	if err != nil {
		fmt.Println("Unable to measure a connection performance as it takes longer than 30 sec.")
		fmt.Println("Looks like there is a bad connectivity and it may affect your monitoring.")
		return
	}
	defer resp.Body.Close()

	fmt.Printf("%-19s | %v\n", "Connection duration", conn.connEnd.Sub(conn.connStart))
	fmt.Printf("%-19s | %v\n", "Request duration", conn.reqEnd.Sub(conn.reqStart)-conn.connEnd.Sub(conn.connStart))
	fmt.Printf("%-19s | %v\n", "Full round trip", conn.reqEnd.Sub(conn.reqStart))
}

type networkTransport struct {
	rtp       http.RoundTripper
	dialer    *net.Dialer
	connStart time.Time
	connEnd   time.Time
	reqStart  time.Time
	reqEnd    time.Time
}

func (conn *networkTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	conn.reqStart = time.Now()
	resp, err := conn.rtp.RoundTrip(r)
	conn.reqEnd = time.Now()
	return resp, err
}

func (conn *networkTransport) dial(network, addr string) (net.Conn, error) {
	conn.connStart = time.Now()
	cn, err := conn.dialer.Dial(network, addr)
	conn.connEnd = time.Now()
	return cn, err
}

// Map status to emoji or text.
func emojiStatus(noEmoji, status bool) string {
	switch true {
	case noEmoji && status:
		return "OK"
	case noEmoji && !status:
		return "PROBLEM"
	case !noEmoji && status:
		return emojiHappy
	case !noEmoji && !status:
		return emojiUnhappy
	}
	return "N/A"
}
<|endoftext|>


<commit_before>package pollers

import (
	"github.com/freeformz/shh/mm"
	"github.com/freeformz/shh/utils"
	"sync"
	"time"
)

const (
	DEFAULT_POLLERS = "load,cpu,df,disk,nif,mem,ntpdate" // Default pollers
)

var (
	pollers = utils.GetEnvWithDefaultStrings("SHH_POLLERS", DEFAULT_POLLERS)
)

type Poller interface {
	Name() string
	Poll(tick time.Time)
}

func NewMultiPoller(measurements chan<- *mm.Measurement) Multi {
	mp := Multi{pollers: make(map[string]Poller), measurements: measurements, counts: make(map[string]uint64)}

	for _, poller := range pollers {
		switch poller {
		case "load":
			mp.RegisterPoller(NewLoadPoller(measurements))
		case "cpu":
			mp.RegisterPoller(NewCpuPoller(measurements))
		case "df":
			mp.RegisterPoller(NewDfPoller(measurements))
		case "disk":
			mp.RegisterPoller(NewDiskPoller(measurements))
		case "nif":
			mp.RegisterPoller(NewNetworkInterfacePoller(measurements))
		case "mem":
			mp.RegisterPoller(NewMemoryPoller(measurements))
		case "ntpdate":
			mp.RegisterPoller(NewNtpdatePoller(measurements))
		}
	}

	return mp
}

type Multi struct {
	sync.WaitGroup
	measurements chan<- *mm.Measurement
	pollers      map[string]Poller
	counts       map[string]uint64
}

func (p Multi) RegisterPoller(poller Poller) {
	p.pollers[poller.Name()] = poller
	p.counts[poller.Name()] = 0
}

func (p Multi) Poll(tick time.Time) {
	for name, poller := range p.pollers {
		count := p.counts[name]
		count += 1
		p.counts[name] = count
		p.measurements <- &mm.Measurement{tick, p.Name(), []string{"ticks", name, "count"}, count}
		p.Add(1)
		go func() {
			poller.Poll(tick)
			p.Done()
		}()
	}
	p.Wait()
}

func (p Multi) Name() string {
	return "multi_poller"
}
<commit_msg>Need to pass the poller in<commit_after>package pollers

import (
	"github.com/freeformz/shh/mm"
	"github.com/freeformz/shh/utils"
	"sync"
	"time"
)

const (
	DEFAULT_POLLERS = "load,cpu,df,disk,nif,mem,ntpdate" // Default pollers
)

var (
	pollers = utils.GetEnvWithDefaultStrings("SHH_POLLERS", DEFAULT_POLLERS)
)

type Poller interface {
	Name() string
	Poll(tick time.Time)
}

func NewMultiPoller(measurements chan<- *mm.Measurement) Multi {
	mp := Multi{pollers: make(map[string]Poller), measurements: measurements, counts: make(map[string]uint64)}

	for _, poller := range pollers {
		switch poller {
		case "load":
			mp.RegisterPoller(NewLoadPoller(measurements))
		case "cpu":
			mp.RegisterPoller(NewCpuPoller(measurements))
		case "df":
			mp.RegisterPoller(NewDfPoller(measurements))
		case "disk":
			mp.RegisterPoller(NewDiskPoller(measurements))
		case "nif":
			mp.RegisterPoller(NewNetworkInterfacePoller(measurements))
		case "mem":
			mp.RegisterPoller(NewMemoryPoller(measurements))
		case "ntpdate":
			mp.RegisterPoller(NewNtpdatePoller(measurements))
		}
	}

	return mp
}

type Multi struct {
	sync.WaitGroup
	measurements chan<- *mm.Measurement
	pollers      map[string]Poller
	counts       map[string]uint64
}

func (p Multi) RegisterPoller(poller Poller) {
	p.pollers[poller.Name()] = poller
	p.counts[poller.Name()] = 0
}

func (p Multi) Poll(tick time.Time) {
	for name, poller := range p.pollers {
		count := p.counts[name]
		count += 1
		p.counts[name] = count
		p.measurements <- &mm.Measurement{tick, p.Name(), []string{"ticks", name, "count"}, count}
		p.Add(1)
		go func(poller Poller) {
			poller.Poll(tick)
			p.Done()
		}(poller)
	}
	p.Wait()
}

func (p Multi) Name() string {
	return "multi_poller"
}
<|endoftext|>


<commit_before>package kodingcontext

import (
	"io"
	"io/ioutil"
	"os"

	"github.com/mitchellh/goamz/s3"
)

type Storage interface {
	Write(string, io.Reader) error
	Read(string) (io.Reader, error)
	Remove(string) error
	Clone(string, Storage) error
	BasePath() (string, error)
	Clean(string) error
}

var _ Storage = S3Storage{}
var _ Storage = FileStorage{}

type S3Storage struct {
	// Bucket holds the plans of terraform
	bucket *s3.Bucket
}

func NewS3Storage() *S3Storage {
	return &S3Storage{}
}

func (s S3Storage) BasePath() (string, error) {
	return "", nil
}

func (s S3Storage) Clean(path string) error {
	return nil
}

func (s S3Storage) Write(path string, file io.Reader) error {
	// TODO(cihangir): we can use bucket.PutReader here
	content, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	err = s.bucket.Put(path, content, "application/json", s3.Private)
	if err != nil {
		return err
	}

	return nil
}

func (s S3Storage) Remove(path string) error {
	if err := s.bucket.Del(path); err != nil {
		return err
	}

	return nil
}

func (s S3Storage) Read(path string) (io.Reader, error) {
	if r, err := s.bucket.GetReader(path); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func (s S3Storage) Clone(path string, target Storage) error {
	filePath := path + "/"
	// Limits the response to keys that begin with the specified prefix. You can
	// use prefixes to separate a bucket into different groupings of keys. (You
	// can think of using prefix to make groups in the same way you'd use a
	// folder in a file system.)
	prefix := filePath

	// If you don't specify the prefix parameter, then the substring starts at
	// the beginning of the key
	delim := ""

	// Specifies the key to start with when listing objects in a bucket. Amazon
	// S3 returns object keys in alphabetical order, starting with key after the
	// marker in order.
	marker := ""

	// Sets the maximum number of keys returned in the response body. You can
	// add this to your request if you want to retrieve fewer than the default
	// 1000 keys.
	max := 0

	// read all elements in a bucket, we are gonna have more than 1000 items in
	// that bucket/folder
	result, err := s.bucket.List(prefix, delim, marker, max)
	if err != nil {
		return err
	}

	// write them all to target
	for _, res := range result.Contents {
		newPath := res.Key
		r, err := s.Read(newPath)
		if err != nil {
			return err
		}

		if err := target.Write(newPath, r); err != nil {
			return err
		}
	}

	return nil
}

type FileStorage struct {
	basePath string
}

func NewFileStorage(basePath string) FileStorage {
	return FileStorage{
		basePath: basePath,
	}
}

func (f FileStorage) BasePath() (string, error) {
	if f.basePath != "" {
		return f.basePath, nil
	}

	// create dir
	// calling TempDir simultaneously will not choose the same directory.
	dir, err := ioutil.TempDir("", "storage")
	if err != nil {
		return "", err
	}

	f.basePath = dir

	return f.basePath, nil
}

func (f FileStorage) Clean(path string) error {
	return os.RemoveAll(path)
}

func (f FileStorage) Write(path string, file io.Reader) (err error) {
	tf, err := os.Create(path)
	if err != nil {
		return err
	}

	defer func() {
		// Sync commits the current contents of the file to disk
		if err = tf.Sync(); err != nil {
			return
		}

		if err = tf.Close(); err != nil {
			return
		}

	}()

	_, err = io.Copy(tf, file)
	return err
}

func (f FileStorage) Remove(path string) error {
	if err := os.RemoveAll(path); err != nil {
		return err
	}

	return nil
}

func (f FileStorage) Read(path string) (io.Reader, error) {
	if r, err := os.Open(path); err != nil {
		return nil, err
	} else {
		return r, nil
	}
func (f FileStorage) Clone(filePath string, target Storage) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	fileInfos, err := ioutil.ReadDir(fullPath)
	if err != nil {
		return err
	}

	for _, fileInfo := range fileInfos {
		fnPath := path.Join(filePath, fileInfo.Name())

		file, err := f.Read(fnPath)
		if err != nil {
			return err
		}

		fpath := path.Join(filePath, fileInfo.Name())
		if err := target.Write(fpath, file); err != nil {
			return err
		}
	}

	return nil
}

func (f FileStorage) fullPath(filePath string) (string, error) {
	dir, err := f.BasePath()
	if err != nil {
		return "", err
	}

	return path.Join(dir, filePath), nil
}
<commit_msg>Terraformer: fix logical errors on api functions<commit_after>package kodingcontext

import (
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"github.com/mitchellh/goamz/s3"
)

type Storage interface {
	Write(string, io.Reader) error
	Read(string) (io.Reader, error)
	Remove(string) error
	Clone(string, Storage) error
	BasePath() (string, error)
	Clean(string) error
}

var _ Storage = S3Storage{}
var _ Storage = FileStorage{}

type S3Storage struct {
	// Bucket holds the plans of terraform
	bucket *s3.Bucket
}

func NewS3Storage(bucket *s3.Bucket) S3Storage {
	return S3Storage{
		bucket: bucket,
	}
}

func (s S3Storage) BasePath() (string, error) {
	return "", nil
}

func (s S3Storage) Clean(path string) error {
	return nil
}

func (s S3Storage) Write(path string, file io.Reader) error {
	// TODO(cihangir): we can use bucket.PutReader here
	content, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	err = s.bucket.Put(path, content, "application/json", s3.Private)
	if err != nil {
		return err
	}

	return nil
}

func (s S3Storage) Remove(path string) error {
	if err := s.bucket.Del(path); err != nil {
		return err
	}

	return nil
}

func (s S3Storage) Read(path string) (io.Reader, error) {
	if r, err := s.bucket.GetReader(path); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func (s S3Storage) Clone(path string, target Storage) error {
	filePath := path + "/"
	// Limits the response to keys that begin with the specified prefix. You can
	// use prefixes to separate a bucket into different groupings of keys. (You
	// can think of using prefix to make groups in the same way you'd use a
	// folder in a file system.)
	prefix := filePath

	// If you don't specify the prefix parameter, then the substring starts at
	// the beginning of the key
	delim := ""

	// Specifies the key to start with when listing objects in a bucket. Amazon
	// S3 returns object keys in alphabetical order, starting with key after the
	// marker in order.
	marker := ""

	// Sets the maximum number of keys returned in the response body. You can
	// add this to your request if you want to retrieve fewer than the default
	// 1000 keys.
	max := 0

	// read all elements in a bucket, we are gonna have more than 1000 items in
	// that bucket/folder
	result, err := s.bucket.List(prefix, delim, marker, max)
	if err != nil {
		return err
	}

	// write them all to target
	for _, res := range result.Contents {
		newPath := res.Key
		r, err := s.Read(newPath)
		if err != nil {
			return err
		}

		if err := target.Write(newPath, r); err != nil {
			return err
		}
	}

	return nil
}

type FileStorage struct {
	basePath string
}

func NewFileStorage(basePath string) FileStorage {
	return FileStorage{
		basePath: basePath,
	}
}

func (f FileStorage) BasePath() (string, error) {
	if f.basePath != "" {
		return f.basePath, nil
	}

	// create dir
	// calling TempDir simultaneously will not choose the same directory.
	dir, err := ioutil.TempDir("", "storage")
	if err != nil {
		return "", err
	}

	f.basePath = dir

	return f.basePath, nil
}

func (f FileStorage) Clean(filePath string) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	return os.RemoveAll(fullPath)
}

func (f FileStorage) Write(filePath string, file io.Reader) (err error) {
	contents := strings.Split(filePath, string(os.PathSeparator))

	dirPath, err := f.fullPath(strings.Join(contents[:len(contents)-1], string(os.PathSeparator)))
	if err != nil {
		return err
	}

	if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
		return err
	}

	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	tf, err := os.Create(fullPath)
	if err != nil {
		return err
	}

	defer func() {
		// Sync commits the current contents of the file to disk
		if err = tf.Sync(); err != nil {
			return
		}

		if err = tf.Close(); err != nil {
			return
		}
	}()

	_, err = io.Copy(tf, file)
	return err
}

func (f FileStorage) Remove(filePath string) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	if err := os.RemoveAll(fullPath); err != nil {
		return err
	}

	return nil
}

func (f FileStorage) Read(filePath string) (io.Reader, error) {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return nil, err
	}

	r, err := os.Open(fullPath)
	if err != nil {
		return nil, err
	}

	return r, nil
}

func (f FileStorage) Clone(filePath string, target Storage) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	fileInfos, err := ioutil.ReadDir(fullPath)
	if err != nil {
		return err
	}

	for _, fileInfo := range fileInfos {
		fnPath := path.Join(filePath, fileInfo.Name())

		file, err := f.Read(fnPath)
		if err != nil {
			return err
		}

		fpath := path.Join(filePath, fileInfo.Name())
		if err := target.Write(fpath, file); err != nil {
			return err
		}
	}

	return nil
}

func (f FileStorage) fullPath(filePath string) (string, error) {
	dir, err := f.BasePath()
	if err != nil {
		return "", err
	}

	return path.Join(dir, filePath), nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 BVK Chaitanya
//
// This file is part of the Ascent Library.
//
// The Ascent Library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The Ascent Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with the Ascent Library.  If not, see <http://www.gnu.org/licenses/>.

//
// A Git hook script to verify what is about to be pushed.  Called by "git
// push" after it has checked the remote status, but before anything has been
// pushed.  If this script exits with a non-zero status nothing will be pushed.
//
// Information about the commits which are being pushed is supplied as lines to
// the standard input in the form:
//
//   <local ref> <local sha1> <remote ref> <remote sha1>
//

package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

func main() {
	var localRef string
	var localSha string
	var remoteRef string
	var remoteSha string

	// Scan the input parameters passed to the pre-push script.
	count, errScan := fmt.Scanf("%s %s %s %s", &localRef, &localSha, &remoteRef,
		&remoteSha)
	if errScan != nil || count != 4 {
		log.Fatalf("git input parameters are in unexpected format")
	}

	// Multiple commits can be sent in one push, so figure out the
	// uncommited commits.
	shalistCmd := exec.Command("git", "log", `--format=%H`,
		fmt.Sprintf("%s..%s", remoteSha, localSha))
	stdout, errShalist := shalistCmd.Output()
	if errShalist != nil {
		log.Fatalf("could not find uncommitted commit ids: %v", errShalist)
	}

	// Accumulate all errors and report them once.
	var errList []error
	defer func() {
		if len(errList) == 0 {
			return
		}
		fmt.Fprintln(os.Stderr)
		for _, err := range errList {
			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
		}
		fmt.Fprintln(os.Stderr)
		os.Exit(1)
	}()

	// Verify commit message format in every commit.
	for _, sha := range strings.Fields(string(stdout)) {
		commitCmd := exec.Command("git", "log", "-n", "1", sha)
		commit, errCommit := commitCmd.Output()
		if errCommit != nil {
			log.Fatalf("could not find commit message for %s: %v", sha, errCommit)
		}
		// Check the commit for compliance.
		if errs := CheckCommitMessage(sha, string(commit)); errs != nil {
			errList = append(errList, errs...)
		}
	}
}

// CheckCommitMessage verifies that commit message format includes the
// commit message template fields.
//
// See git commit template file templates/git-commit-template.txt
func CheckCommitMessage(sha, commit string) []error {
	// Regular expressions to match the fields from git commit template.
	reviewersRe := regexp.MustCompile(`^\s*Reviewers\s+:\s*\S+.*$`)
	testsRe := regexp.MustCompile(`^\s*Tests Run\s+:.*$`)
	ticketsRe := regexp.MustCompile(`^\s*Tickets Resolved\s+:.*$`)

	reviewers, tests, tickets := 0, 0, 0
	reviewersLine, testsLine, ticketsLine := -1, -1, -1
	reviewersColon, testsColon, ticketsColon := 0, 0, 0

	lines := strings.Split(commit, "\n")
	for ii, line := range lines {
		switch {
		case reviewersRe.MatchString(line):
			reviewers++
			reviewersLine = ii
			reviewersColon = strings.IndexRune(line, ':')
		case testsRe.MatchString(line):
			tests++
			testsLine = ii
			testsColon = strings.IndexRune(line, ':')
		case ticketsRe.MatchString(line):
			tickets++
			ticketsLine = ii
			ticketsColon = strings.IndexRune(line, ':')
		}
	}

	var errList []error
	if reviewers == 0 {
		err := fmt.Errorf("%s commit message doesn't have reviewers line", sha)
		errList = append(errList, err)
	}
	if reviewers > 1 {
		err := fmt.Errorf("%s commit message has multiple reviewer lines", sha)
		errList = append(errList, err)
	}

	if tests == 0 {
		err := fmt.Errorf("%s commit message doesn't have tests-run line", sha)
		errList = append(errList, err)
	}
	if tests > 1 {
		err := fmt.Errorf("%s commit message has multiple tests-run lines", sha)
		errList = append(errList, err)
	}

	if tickets == 0 {
		err := fmt.Errorf("%s commit message doesn't have tickets-resolved line",
			sha)
		errList = append(errList, err)
	}
	if tickets > 1 {
		err := fmt.Errorf("%s commit message has multiple tickets-resolved lines",
			sha)
		errList = append(errList, err)
	}

	if errList == nil {
		if reviewersColon != testsColon || testsColon != ticketsColon {
			err := fmt.Errorf("%s commit template fields are not properly aligned",
				sha)
			errList = append(errList, err)
		}

		// Commit message is found on line number 4.
		if reviewersLine == 4 {
			err := fmt.Errorf("%s a commit message must preceed the commit "+
				"template", sha)
			errList = append(errList, err)
		}

		if len(strings.TrimSpace(lines[reviewersLine-1])) != 0 {
			err := fmt.Errorf("%s must have an empty line before commit template "+
				"fields", sha)
			errList = append(errList, err)
		}

		if testsLine != reviewersLine+1 || ticketsLine != testsLine+1 {
			err := fmt.Errorf("%s commit template fields must be in order and/or "+
				"occupy only one line", sha)
			errList = append(errList, err)
		}
	}

	return errList
}
<commit_msg>Handle git-push case when local and remote repos are in sync.<commit_after>// Copyright (c) 2016 BVK Chaitanya
//
// This file is part of the Ascent Library.
//
// The Ascent Library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The Ascent Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with the Ascent Library.  If not, see <http://www.gnu.org/licenses/>.

//
// A Git hook script to verify what is about to be pushed.  Called by "git
// push" after it has checked the remote status, but before anything has been
// pushed.  If this script exits with a non-zero status nothing will be pushed.
//
// Information about the commits which are being pushed is supplied as lines to
// the standard input in the form:
//
//   <local ref> <local sha1> <remote ref> <remote sha1>
//

package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

func main() {
	var localRef string
	var localSha string
	var remoteRef string
	var remoteSha string

	// Scan the input parameters passed to the pre-push script. Stdin would be
	// empty if there are no commits to send.
	count, errScan := fmt.Scanf("%s %s %s %s", &localRef, &localSha, &remoteRef,
		&remoteSha)
	if count == 0 {
		return
	}
	if errScan != nil || count != 4 {
		log.Fatalf("git input parameters are in unexpected format")
	}

	// Multiple commits can be sent in one push, so figure out the uncommited
	// commits.
	shalistCmd := exec.Command("git", "log", `--format=%H`,
		fmt.Sprintf("%s..%s", remoteSha, localSha))
	stdout, errShalist := shalistCmd.Output()
	if errShalist != nil {
		log.Fatalf("could not find uncommitted commit ids: %v", errShalist)
	}

	// Accumulate all errors and report them once.
	var errList []error
	defer func() {
		if len(errList) == 0 {
			return
		}
		fmt.Fprintln(os.Stderr)
		for _, err := range errList {
			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
		}
		fmt.Fprintln(os.Stderr)
		os.Exit(1)
	}()

	// Verify commit message format in every commit.
	for _, sha := range strings.Fields(string(stdout)) {
		commitCmd := exec.Command("git", "log", "-n", "1", sha)
		commit, errCommit := commitCmd.Output()
		if errCommit != nil {
			log.Fatalf("could not find commit message for %s: %v", sha, errCommit)
		}
		// Check the commit for compliance.
		if errs := CheckCommitMessage(sha, string(commit)); errs != nil {
			errList = append(errList, errs...)
		}
	}
}

// CheckCommitMessage verifies that commit message format includes the commit
// message template fields.
//
// See git commit template file templates/git-commit-template.txt
func CheckCommitMessage(sha, commit string) []error {
	// Regular expressions to match the fields from git commit template.
	reviewersRe := regexp.MustCompile(`^\s*Reviewers\s+:\s*\S+.*$`)
	testsRe := regexp.MustCompile(`^\s*Tests Run\s+:.*$`)
	ticketsRe := regexp.MustCompile(`^\s*Tickets Resolved\s+:.*$`)

	reviewers, tests, tickets := 0, 0, 0
	reviewersLine, testsLine, ticketsLine := -1, -1, -1
	reviewersColon, testsColon, ticketsColon := 0, 0, 0

	lines := strings.Split(commit, "\n")
	for ii, line := range lines {
		switch {
		case reviewersRe.MatchString(line):
			reviewers++
			reviewersLine = ii
			reviewersColon = strings.IndexRune(line, ':')
		case testsRe.MatchString(line):
			tests++
			testsLine = ii
			testsColon = strings.IndexRune(line, ':')
		case ticketsRe.MatchString(line):
			tickets++
			ticketsLine = ii
			ticketsColon = strings.IndexRune(line, ':')
		}
	}

	var errList []error
	if reviewers == 0 {
		err := fmt.Errorf("%s commit message doesn't have reviewers line", sha)
		errList = append(errList, err)
	}
	if reviewers > 1 {
		err := fmt.Errorf("%s commit message has multiple reviewer lines", sha)
		errList = append(errList, err)
	}

	if tests == 0 {
		err := fmt.Errorf("%s commit message doesn't have tests-run line", sha)
		errList = append(errList, err)
	}
	if tests > 1 {
		err := fmt.Errorf("%s commit message has multiple tests-run lines", sha)
		errList = append(errList, err)
	}

	if tickets == 0 {
		err := fmt.Errorf("%s commit message doesn't have tickets-resolved line",
			sha)
		errList = append(errList, err)
	}
	if tickets > 1 {
		err := fmt.Errorf("%s commit message has multiple tickets-resolved lines",
			sha)
		errList = append(errList, err)
	}

	if errList == nil {
		if reviewersColon != testsColon || testsColon != ticketsColon {
			err := fmt.Errorf("%s commit template fields are not properly aligned",
				sha)
			errList = append(errList, err)
		}

		// Commit message is found on line number 4.
		if reviewersLine == 4 {
			err := fmt.Errorf("%s a commit message must preceed the commit "+
				"template", sha)
			errList = append(errList, err)
		}

		if len(strings.TrimSpace(lines[reviewersLine-1])) != 0 {
			err := fmt.Errorf("%s must have an empty line before commit template "+
				"fields", sha)
			errList = append(errList, err)
		}

		if testsLine != reviewersLine+1 || ticketsLine != testsLine+1 {
			err := fmt.Errorf("%s commit template fields must be in order and/or "+
				"occupy only one line", sha)
			errList = append(errList, err)
		}
	}

	return errList
}
<|endoftext|>


<commit_before>/*Package segment provides basic image segmentation and clusterring methods.*/
package segment

import (
	"image"
	"image/color"

	"github.com/anthonynsimon/bild/clone"
	"github.com/anthonynsimon/bild/util"
)

// Threshold returns a grayscale image in which values from the param img that are
// smaller than the param level are set to black and values larger than or equal to
// it are set to white.
// Level must be of the range 0 to 255.
func Threshold(img image.Image, level uint8) *image.Gray {
	src := clone.AsRGBA(img)
	bounds := src.Bounds()

	dst := image.NewGray(bounds)

	for y := 0; y < bounds.Dy(); y++ {
		for x := 0; x < bounds.Dx(); x++ {
			srcPos := y*src.Stride + x*4
			dstPos := y*dst.Stride + x

			c := src.Pix[srcPos : srcPos+4]
			r := util.Rank(color.RGBA{c[0], c[1], c[2], c[3]})

			if uint8(r) >= level {
				dst.Pix[dstPos] = 0xFF
			} else {
				dst.Pix[dstPos] = 0x00
			}
		}
	}

	return dst
}
<commit_msg>adds handling for transparent pixels (#57)<commit_after>/*Package segment provides basic image segmentation and clusterring methods.*/
package segment

import (
	"image"
	"image/color"

	"github.com/anthonynsimon/bild/clone"
	"github.com/anthonynsimon/bild/util"
)

// Threshold returns a grayscale image in which values from the param img that are
// smaller than the param level are set to black and values larger than or equal to
// it are set to white.
// Level must be of the range 0 to 255.
func Threshold(img image.Image, level uint8) *image.Gray {
	src := clone.AsRGBA(img)
	bounds := src.Bounds()

	dst := image.NewGray(bounds)

	for y := 0; y < bounds.Dy(); y++ {
		for x := 0; x < bounds.Dx(); x++ {
			srcPos := y*src.Stride + x*4
			dstPos := y*dst.Stride + x

			c := src.Pix[srcPos : srcPos+4]
			r := util.Rank(color.RGBA{c[0], c[1], c[2], c[3]})

			// transparent pixel is always white
			if c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 {
				dst.Pix[dstPos] = 0xFF
				continue
			}

			if uint8(r) >= level {
				dst.Pix[dstPos] = 0xFF
			} else {
				dst.Pix[dstPos] = 0x00
			}
		}
	}

	return dst
}
<|endoftext|>


<commit_before>package events

import (
	"context"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/micro/micro/v3/internal/auth/namespace"
	pb "github.com/micro/micro/v3/proto/events"
	"github.com/micro/micro/v3/service/errors"
	"github.com/micro/micro/v3/service/events"
	"github.com/micro/micro/v3/service/events/util"
	"github.com/micro/micro/v3/service/logger"
)

type Stream struct{}

func (s *Stream) Publish(ctx context.Context, req *pb.PublishRequest, rsp *pb.PublishResponse) error {
	// authorize the request
	if err := namespace.AuthorizeAdmin(ctx, namespace.DefaultNamespace, "events.Stream.Publish"); err != nil {
		return err
	}

	// validate the request
	if len(req.Topic) == 0 {
		return errors.BadRequest("events.Stream.Publish", events.ErrMissingTopic.Error())
	}

	// parse options
	var opts []events.PublishOption
	if req.Timestamp > 0 {
		opts = append(opts, events.WithTimestamp(time.Unix(req.Timestamp, 0)))
	}
	if req.Metadata != nil {
		opts = append(opts, events.WithMetadata(req.Metadata))
	}

	// publish the event
	if err := events.Publish(req.Topic, req.Payload, opts...); err != nil {
		return errors.InternalServerError("events.Stream.Publish", err.Error())
	}

	// write the event to the store
	event := events.Event{
		ID:        uuid.New().String(),
		Metadata:  req.Metadata,
		Payload:   req.Payload,
		Topic:     req.Topic,
		Timestamp: time.Unix(req.Timestamp, 0),
	}
	if err := events.DefaultStore.Write(&event, events.WithTTL(time.Hour*24)); err != nil {
		logger.Errorf("Error writing event %v to store: %v", event.ID, err)
	}

	return nil
}

func (s *Stream) Consume(ctx context.Context, req *pb.ConsumeRequest, rsp pb.Stream_ConsumeStream) error {
	// authorize the request
	if err := namespace.AuthorizeAdmin(ctx, namespace.DefaultNamespace, "events.Stream.Publish"); err != nil {
		return err
	}

	// parse options
	opts := []events.ConsumeOption{}
	if req.Offset > 0 {
		opts = append(opts, events.WithOffset(time.Unix(req.Offset, 0)))
	}
	if len(req.Group) > 0 {
		opts = append(opts, events.WithGroup(req.Group))
	}
	if !req.AutoAck {
		opts = append(opts, events.WithAutoAck(req.AutoAck, time.Duration(req.AckWait)/time.Nanosecond))
	}
	if req.RetryLimit > -1 {
		opts = append(opts, events.WithRetryLimit(int(req.RetryLimit)))
	}

	// create the subscriber
	evChan, err := events.Consume(req.Topic, opts...)
	if err != nil {
		return errors.InternalServerError("events.Stream.Consume", err.Error())
	}

	type eventSent struct {
		sent  time.Time
		event events.Event
	}
	ackMap := map[string]eventSent{}
	mutex := sync.RWMutex{}
	go func() {
		for {
			req := pb.AckRequest{}
			if err := rsp.RecvMsg(&req); err != nil {
				return
			}
			mutex.RLock()
			ev, ok := ackMap[req.Id]
			mutex.RUnlock()
			if !ok {
				// not found, probably timed out after ackWait
				continue
			}
			if req.Success {
				ev.event.Ack()
			} else {
				ev.event.Nack()
			}
			mutex.Lock()
			delete(ackMap, req.Id)
			mutex.Unlock()
		}
	}()
	for {
		// Do any clean up of ackMap where we haven't got a response
		now := time.Now()
		for k, v := range ackMap {
			if v.sent.Add(2 * time.Duration(req.AckWait)).Before(now) {
				mutex.Lock()
				delete(ackMap, k)
				mutex.Unlock()
			}
		}
		ev, ok := <-evChan
		if !ok {
			rsp.Close()
			return nil
		}
		if !req.AutoAck {
			// track the acks
			mutex.Lock()
			ackMap[ev.ID] = eventSent{event: ev, sent: time.Now()}
			mutex.Unlock()
		}

		if err := rsp.Send(util.SerializeEvent(&ev)); err != nil {
			return err
		}
	}
}
<commit_msg>Fix wrong authorization check (#1696)<commit_after>package events

import (
	"context"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/micro/micro/v3/internal/auth/namespace"
	pb "github.com/micro/micro/v3/proto/events"
	"github.com/micro/micro/v3/service/errors"
	"github.com/micro/micro/v3/service/events"
	"github.com/micro/micro/v3/service/events/util"
	"github.com/micro/micro/v3/service/logger"
)

type Stream struct{}

func (s *Stream) Publish(ctx context.Context, req *pb.PublishRequest, rsp *pb.PublishResponse) error {
	// authorize the request
	if err := namespace.AuthorizeAdmin(ctx, namespace.DefaultNamespace, "events.Stream.Publish"); err != nil {
		return err
	}

	// validate the request
	if len(req.Topic) == 0 {
		return errors.BadRequest("events.Stream.Publish", events.ErrMissingTopic.Error())
	}

	// parse options
	var opts []events.PublishOption
	if req.Timestamp > 0 {
		opts = append(opts, events.WithTimestamp(time.Unix(req.Timestamp, 0)))
	}
	if req.Metadata != nil {
		opts = append(opts, events.WithMetadata(req.Metadata))
	}

	// publish the event
	if err := events.Publish(req.Topic, req.Payload, opts...); err != nil {
		return errors.InternalServerError("events.Stream.Publish", err.Error())
	}

	// write the event to the store
	event := events.Event{
		ID:        uuid.New().String(),
		Metadata:  req.Metadata,
		Payload:   req.Payload,
		Topic:     req.Topic,
		Timestamp: time.Unix(req.Timestamp, 0),
	}
	if err := events.DefaultStore.Write(&event, events.WithTTL(time.Hour*24)); err != nil {
		logger.Errorf("Error writing event %v to store: %v", event.ID, err)
	}

	return nil
}

func (s *Stream) Consume(ctx context.Context, req *pb.ConsumeRequest, rsp pb.Stream_ConsumeStream) error {
	// authorize the request
	if err := namespace.AuthorizeAdmin(ctx, namespace.DefaultNamespace, "events.Stream.Consume"); err != nil {
		return err
	}

	// parse options
	opts := []events.ConsumeOption{}
	if req.Offset > 0 {
		opts = append(opts, events.WithOffset(time.Unix(req.Offset, 0)))
	}
	if len(req.Group) > 0 {
		opts = append(opts, events.WithGroup(req.Group))
	}
	if !req.AutoAck {
		opts = append(opts, events.WithAutoAck(req.AutoAck, time.Duration(req.AckWait)/time.Nanosecond))
	}
	if req.RetryLimit > -1 {
		opts = append(opts, events.WithRetryLimit(int(req.RetryLimit)))
	}

	// create the subscriber
	evChan, err := events.Consume(req.Topic, opts...)
	if err != nil {
		return errors.InternalServerError("events.Stream.Consume", err.Error())
	}

	type eventSent struct {
		sent  time.Time
		event events.Event
	}
	ackMap := map[string]eventSent{}
	mutex := sync.RWMutex{}
	go func() {
		for {
			req := pb.AckRequest{}
			if err := rsp.RecvMsg(&req); err != nil {
				return
			}
			mutex.RLock()
			ev, ok := ackMap[req.Id]
			mutex.RUnlock()
			if !ok {
				// not found, probably timed out after ackWait
				continue
			}
			if req.Success {
				ev.event.Ack()
			} else {
				ev.event.Nack()
			}
			mutex.Lock()
			delete(ackMap, req.Id)
			mutex.Unlock()
		}
	}()
	for {
		// Do any clean up of ackMap where we haven't got a response
		now := time.Now()
		for k, v := range ackMap {
			if v.sent.Add(2 * time.Duration(req.AckWait)).Before(now) {
				mutex.Lock()
				delete(ackMap, k)
				mutex.Unlock()
			}
		}
		ev, ok := <-evChan
		if !ok {
			rsp.Close()
			return nil
		}
		if !req.AutoAck {
			// track the acks
			mutex.Lock()
			ackMap[ev.ID] = eventSent{event: ev, sent: time.Now()}
			mutex.Unlock()
		}

		if err := rsp.Send(util.SerializeEvent(&ev)); err != nil {
			return err
		}
	}
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"strings"

	"github.com/astaxie/beego/orm"
	"github.com/l2x/wolffy/server/config"
	_ "github.com/mattn/go-sqlite3"
)

var (
	DB orm.Ormer
)

func InitModels() error {
	dbPath := fmt.Sprintf("%s/%s", strings.TrimRight(config.DBPath, "/"), "data.db")

	orm.Debug = true
	orm.RegisterDriver("sqlite3", orm.DR_Sqlite)
	orm.RegisterDataBase("default", "sqlite3", dbPath)

	orm.RegisterModel(ClusterModel)
	orm.RegisterModel(DeployModel)
	orm.RegisterModel(DeployHistoryModel)
	orm.RegisterModel(MachineModel)
	orm.RegisterModel(ProjectModel)
	orm.RegisterModel(ProjectClusterModel)

	err := orm.RunSyncdb("default", false, true)
	if err != nil {
		return err
	}

	DB = orm.NewOrm()

	return nil
}
<commit_msg>update<commit_after>package models

import (
	"fmt"
	"strings"

	"github.com/astaxie/beego/orm"
	"github.com/l2x/wolffy/server/config"
	_ "github.com/mattn/go-sqlite3"
)

var (
	DB orm.Ormer
)

func InitModels() error {
	dbPath := fmt.Sprintf("%s/%s", strings.TrimRight(config.DBPath, "/"), "data.db")

	orm.Debug = true
	orm.RegisterDriver("sqlite3", orm.DR_Sqlite)
	orm.RegisterDataBase("default", "sqlite3", dbPath)

	orm.RegisterModel(ClusterModel)
	orm.RegisterModel(DeployModel)
	orm.RegisterModel(DeployHistoryModel)
	orm.RegisterModel(MachineModel)
	orm.RegisterModel(ProjectModel)
	orm.RegisterModel(ProjectClusterModel)
	orm.RegisterModel(UserModel)

	err := orm.RunSyncdb("default", false, true)
	if err != nil {
		return err
	}

	DB = orm.NewOrm()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"math"
	"time"
)

const (
	// 6.67e-11 m^3kg^-1s^-2
	GravitationalConst = 6.67e-11
	// Play GC in m^3kg^-1ms^-2
	GravConst = 5e-8
)

type Simulation struct {
	Sun                 *PlanetaryBody
	Planets             []*PlanetaryBody
	AggregatePopulation int
}

func NewSimulation() *Simulation {
	return &Simulation{
		Sun:                 NewSun(),
		Planets:             []*PlanetaryBody{},
		AggregatePopulation: 0,
	}
}

func (s *Simulation) Update(elapsed time.Duration) {
	s.NBodyUpdate(elapsed)
	var popSum = 0
	for _, p := range s.Planets {
		popSum += p.GetPopulation()
		p.Update(elapsed)
		dist = float64(p.Pos().DistanceTo(s.Sun.Pos()))
		switch {
		case dist < 10:
			p.SetState(TooClose)
		case dist > 15:
			p.SetState(TooFar)
		default:
			p.SetState(Fertile)
		}
	}
	s.SetPopulation(popSum)
}

func (s *Simulation) NBodyUpdate(elapsed time.Duration) {
	var dist float64
	for _, p := range s.Planets {
		// First, we must handle the sun...
		dist = float64(s.Sun.Pos().DistanceTo(p.Pos()))
		var force = s.Sun.Pos().Sub(p.Pos()).Scale(s.Sun.Mass * p.Mass).Scale(float32(math.Pow(dist, -3)))
		for _, p2 := range s.Planets {
			if p == p2 {
				continue
			}
			dist = float64(p2.Pos().DistanceTo(p.Pos()))
			force = force.Add(p2.Pos().Sub(p.Pos()).Scale(p2.Mass * p.Mass).Scale(float32(math.Pow(dist, -3))))
		}
		// Don't forget the gravitational constant and p's mass.
		var accel = force.Scale(GravConst).Scale(1 / p.Mass)
		p.CalcNewVelocity(accel, elapsed)
	}
}

func (s *Simulation) AddPlanet(x, y float32) {
	s.Planets = append(s.Planets, NewPlanet(x, y))
}

func (s *Simulation) SetPopulation(population int) {
	s.AggregatePopulation = population
}

func (s *Simulation) GetPopulation() int {
	return s.AggregatePopulation
}
<commit_msg>Fix build.<commit_after>package main

import (
	"math"
	"time"
)

const (
	// 6.67e-11 m^3kg^-1s^-2
	GravitationalConst = 6.67e-11
	// Play GC in m^3kg^-1ms^-2
	GravConst = 5e-8
)

type Simulation struct {
	Sun                 *PlanetaryBody
	Planets             []*PlanetaryBody
	AggregatePopulation int
}

func NewSimulation() *Simulation {
	return &Simulation{
		Sun:                 NewSun(),
		Planets:             []*PlanetaryBody{},
		AggregatePopulation: 0,
	}
}

func (s *Simulation) Update(elapsed time.Duration) {
	var (
		dist   float32
		popSum = 0
	)
	s.NBodyUpdate(elapsed)
	for _, p := range s.Planets {
		popSum += p.GetPopulation()
		p.Update(elapsed)
		dist = p.Pos().DistanceTo(s.Sun.Pos())
		switch {
		case dist < 10:
			p.SetState(TooClose)
		case dist > 15:
			p.SetState(TooFar)
		default:
			p.SetState(Fertile)
		}
	}
	s.SetPopulation(popSum)
}

func (s *Simulation) NBodyUpdate(elapsed time.Duration) {
	var dist float64
	for _, p := range s.Planets {
		// First, we must handle the sun...
		dist = float64(s.Sun.Pos().DistanceTo(p.Pos()))
		var force = s.Sun.Pos().Sub(p.Pos()).Scale(s.Sun.Mass * p.Mass).Scale(float32(math.Pow(dist, -3)))
		for _, p2 := range s.Planets {
			if p == p2 {
				continue
			}
			dist = float64(p2.Pos().DistanceTo(p.Pos()))
			force = force.Add(p2.Pos().Sub(p.Pos()).Scale(p2.Mass * p.Mass).Scale(float32(math.Pow(dist, -3))))
		}
		// Don't forget the gravitational constant and p's mass.
		var accel = force.Scale(GravConst).Scale(1 / p.Mass)
		p.CalcNewVelocity(accel, elapsed)
	}
}

func (s *Simulation) AddPlanet(x, y float32) {
	s.Planets = append(s.Planets, NewPlanet(x, y))
}

func (s *Simulation) SetPopulation(population int) {
	s.AggregatePopulation = population
}

func (s *Simulation) GetPopulation() int {
	return s.AggregatePopulation
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package fs

import (
	"sync"
	"testing"
	"time"

	xtime "github.com/m3db/m3x/time"

	"github.com/fortytw2/leaktest"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/require"
)

func TestSeekerManagerCacheShardIndices(t *testing.T) {
	defer leaktest.CheckTimeout(t, 1*time.Minute)()

	shards := []uint32{2, 5, 9, 478, 1023}
	m := NewSeekerManager(nil, NewOptions(), NewBlockRetrieverOptions().FetchConcurrency()).(*seekerManager)
	var byTimes []*seekersByTime
	m.openAnyUnopenSeekersFn = func(byTime *seekersByTime) error {
		byTimes = append(byTimes, byTime)
		return nil
	}

	require.NoError(t, m.CacheShardIndices(shards))

	// Assert captured byTime objects match expectations
	require.Equal(t, len(shards), len(byTimes))
	for idx, shard := range shards {
		byTimes[idx].shard = shard
	}

	// Assert seeksByShardIdx match expectations
	shardSet := make(map[uint32]struct{}, len(shards))
	for _, shard := range shards {
		shardSet[shard] = struct{}{}
	}
	for shard, byTime := range m.seekersByShardIdx {
		_, exists := shardSet[uint32(shard)]
		if !exists {
			require.False(t, byTime.accessed)
		} else {
			require.True(t, byTime.accessed)
			require.Equal(t, uint32(shard), byTime.shard)
		}
	}
}

// TestSeekerManagerBorrowOpenSeekersLazy tests that the Borrow() method will
// open seekers lazily if they're not already open.
func TestSeekerManagerBorrowOpenSeekersLazy(t *testing.T) {
	defer leaktest.CheckTimeout(t, 1*time.Minute)()

	ctrl := gomock.NewController(t)

	shards := []uint32{2, 5, 9, 478, 1023}
	m := NewSeekerManager(nil, NewOptions(), NewBlockRetrieverOptions().FetchConcurrency()).(*seekerManager)
	m.newOpenSeekerFn = func(
		shard uint32,
		blockStart time.Time,
	) (FileSetSeeker, error) {
		mock := NewMockFileSetSeeker(ctrl)
		mock.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
		mock.EXPECT().ConcurrentClone().Return(mock, nil)
		for i := 0; i < NewBlockRetrieverOptions().FetchConcurrency(); i++ {
			mock.EXPECT().Close().Return(nil)
			mock.EXPECT().ConcurrentIDBloomFilter().Return(nil)
		}
		return mock, nil
	}

	metadata := testNs1Metadata(t)
	require.NoError(t, m.Open(metadata))
	for _, shard := range shards {
		seeker, err := m.Borrow(shard, time.Time{})
		require.NoError(t, err)
		byTime := m.seekersByTime(shard)
		byTime.RLock()
		seekers := byTime.seekers[xtime.ToUnixNano(time.Time{})]
		require.Equal(t, NewBlockRetrieverOptions().FetchConcurrency(), len(seekers.seekers))
		byTime.RUnlock()
		require.NoError(t, m.Return(shard, time.Time{}, seeker))
	}

	require.NoError(t, m.Close())
}

// TestSeekerManagerOpenCloseLoop tests the openCloseLoop of the SeekerManager
// by making sure that it makes the right decisions with regards to cleaning
// up resources based on their state.
func TestSeekerManagerOpenCloseLoop(t *testing.T) {
	defer leaktest.CheckTimeout(t, 1*time.Minute)()

	ctrl := gomock.NewController(t)

	shards := []uint32{2, 5, 9, 478, 1023}
	m := NewSeekerManager(nil, NewOptions(), NewBlockRetrieverOptions().FetchConcurrency()).(*seekerManager)
	clockOpts := m.opts.ClockOptions()
	now := clockOpts.NowFn()()
	startNano := xtime.ToUnixNano(now)

	fakeTime := now
	fakeTimeLock := sync.Mutex{}
	// Setup a function that will allow us to dynamically modify the clock in
	// a concurrency-safe way
	newNowFn := func() time.Time {
		fakeTimeLock.Lock()
		defer fakeTimeLock.Unlock()
		return fakeTime
	}
	clockOpts = clockOpts.SetNowFn(newNowFn)
	m.opts = m.opts.SetClockOptions(clockOpts)

	// Initialize some seekers for a time period
	m.openAnyUnopenSeekersFn = func(byTime *seekersByTime) error {
		byTime.Lock()
		defer byTime.Unlock()

		// Don't overwrite if called again
		if len(byTime.seekers) != 0 {
			return nil
		}

		// Don't re-open if they should have expired
		fakeTimeLock.Lock()
		defer fakeTimeLock.Unlock()
		if !fakeTime.Equal(now) {
			return nil
		}

		mock := NewMockFileSetSeeker(ctrl)
		mock.EXPECT().Close().Return(nil)
		mocks := []borrowableSeeker{}
		mocks = append(mocks, borrowableSeeker{seeker: mock})
		byTime.seekers[startNano] = seekersAndBloom{
			seekers:     mocks,
			bloomFilter: nil,
		}
		return nil
	}

	// Force all the seekers to be opened
	require.NoError(t, m.CacheShardIndices(shards))

	// Notified everytime the openCloseLoop ticks
	tickCh := make(chan struct{})
	cleanupCh := make(chan struct{})
	m.sleepFn = func(_ time.Duration) {
		tickCh <- struct{}{}
	}

	metadata := testNs1Metadata(t)
	seekers := []ConcurrentFileSetSeeker{}

	require.NoError(t, m.Open(metadata))
	// Steps is a series of steps for the test. It is guaranteed that at least
	// one (not exactly one!) tick of the openCloseLoop will occur between every step.
	steps := []struct {
		title string
		step  func()
	}{
		{
			title: "Make sure it didn't clean up the seekers which are still in retention",
			step: func() {
				m.RLock()
				for _, shard := range shards {
					require.Equal(t, 1, len(m.seekersByTime(shard).seekers[startNano].seekers))
				}
				m.RUnlock()
			},
		},
		{
			title: "Borrow a seeker from each shard and then modify the clock such that they're out of retention",
			step: func() {
				for _, shard := range shards {
					seeker, err := m.Borrow(shard, now)
					require.NoError(t, err)
					require.NotNil(t, seeker)
					seekers = append(seekers, seeker)
				}

				fakeTimeLock.Lock()
				fakeTime = fakeTime.Add(10 * metadata.Options().RetentionOptions().RetentionPeriod())
				fakeTimeLock.Unlock()
			},
		},
		{
			title: "Make sure the seeker manager cant be closed while seekers are borrowed",
			step: func() {
				require.Equal(t, errCantCloseSeekerManagerWhileSeekersAreBorrowed, m.Close())
			},
		},
		{
			title: "Make sure that none of the seekers were cleaned up during the openCloseLoop tick (because they're still borrowed)",
			step: func() {
				m.RLock()
				for _, shard := range shards {
					require.Equal(t, 1, len(m.seekersByTime(shard).seekers[startNano].seekers))
				}
				m.RUnlock()
			},
		},
		{
			title: "Return the borrowed seekers",
			step: func() {
				for i, seeker := range seekers {
					require.NoError(t, m.Return(shards[i], now, seeker))
				}
			},
		},
		{
			title: "Make sure that the returned seekers were cleaned up during the openCloseLoop tick",
			step: func() {
				m.RLock()
				for _, shard := range shards {
					byTime := m.seekersByTime(shard)
					byTime.RLock()
					_, ok := byTime.seekers[startNano]
					byTime.RUnlock()
					require.False(t, ok)
				}
				m.RUnlock()
			},
		},
	}

	for _, step := range steps {
		// Wait for two notifications between steps to guarantee that the entirety
		// of the openCloseLoop is executed at least once
		<-tickCh
		<-tickCh
		step.step()
	}

	// Background goroutine that will pull notifications off the tickCh so that
	// the openCloseLoop is not blocked when we call Close()
	go func() {
		for {
			select {
			case <-tickCh:
				continue
			case <-cleanupCh:
				return
			}
		}
	}()

	// Restore previous interval once the openCloseLoop ends
	require.NoError(t, m.Close())
	// Make sure there are no goroutines still trying to write into the tickCh
	// to prevent the test itself from interfering with the goroutine leak test
	close(cleanupCh)
}
<commit_msg>Shorten sleep to make test less flaky (#508)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package fs

import (
	"sync"
	"testing"
	"time"

	xtime "github.com/m3db/m3x/time"

	"github.com/fortytw2/leaktest"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/require"
)

func TestSeekerManagerCacheShardIndices(t *testing.T) {
	defer leaktest.CheckTimeout(t, 1*time.Minute)()

	shards := []uint32{2, 5, 9, 478, 1023}
	m := NewSeekerManager(nil, NewOptions(), NewBlockRetrieverOptions().FetchConcurrency()).(*seekerManager)
	var byTimes []*seekersByTime
	m.openAnyUnopenSeekersFn = func(byTime *seekersByTime) error {
		byTimes = append(byTimes, byTime)
		return nil
	}

	require.NoError(t, m.CacheShardIndices(shards))

	// Assert captured byTime objects match expectations
	require.Equal(t, len(shards), len(byTimes))
	for idx, shard := range shards {
		byTimes[idx].shard = shard
	}

	// Assert seeksByShardIdx match expectations
	shardSet := make(map[uint32]struct{}, len(shards))
	for _, shard := range shards {
		shardSet[shard] = struct{}{}
	}
	for shard, byTime := range m.seekersByShardIdx {
		_, exists := shardSet[uint32(shard)]
		if !exists {
			require.False(t, byTime.accessed)
		} else {
			require.True(t, byTime.accessed)
			require.Equal(t, uint32(shard), byTime.shard)
		}
	}
}

// TestSeekerManagerBorrowOpenSeekersLazy tests that the Borrow() method will
// open seekers lazily if they're not already open.
func TestSeekerManagerBorrowOpenSeekersLazy(t *testing.T) {
	defer leaktest.CheckTimeout(t, 1*time.Minute)()

	ctrl := gomock.NewController(t)

	shards := []uint32{2, 5, 9, 478, 1023}
	m := NewSeekerManager(nil, NewOptions(), NewBlockRetrieverOptions().FetchConcurrency()).(*seekerManager)
	m.newOpenSeekerFn = func(
		shard uint32,
		blockStart time.Time,
	) (FileSetSeeker, error) {
		mock := NewMockFileSetSeeker(ctrl)
		mock.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
		mock.EXPECT().ConcurrentClone().Return(mock, nil)
		for i := 0; i < NewBlockRetrieverOptions().FetchConcurrency(); i++ {
			mock.EXPECT().Close().Return(nil)
			mock.EXPECT().ConcurrentIDBloomFilter().Return(nil)
		}
		return mock, nil
	}
	m.sleepFn = func(_ time.Duration) {
		time.Sleep(time.Millisecond)
	}

	metadata := testNs1Metadata(t)
	require.NoError(t, m.Open(metadata))
	for _, shard := range shards {
		seeker, err := m.Borrow(shard, time.Time{})
		require.NoError(t, err)
		byTime := m.seekersByTime(shard)
		byTime.RLock()
		seekers := byTime.seekers[xtime.ToUnixNano(time.Time{})]
		require.Equal(t, NewBlockRetrieverOptions().FetchConcurrency(), len(seekers.seekers))
		byTime.RUnlock()
		require.NoError(t, m.Return(shard, time.Time{}, seeker))
	}

	require.NoError(t, m.Close())
}

// TestSeekerManagerOpenCloseLoop tests the openCloseLoop of the SeekerManager
// by making sure that it makes the right decisions with regards to cleaning
// up resources based on their state.
func TestSeekerManagerOpenCloseLoop(t *testing.T) {
	defer leaktest.CheckTimeout(t, 1*time.Minute)()

	ctrl := gomock.NewController(t)

	shards := []uint32{2, 5, 9, 478, 1023}
	m := NewSeekerManager(nil, NewOptions(), NewBlockRetrieverOptions().FetchConcurrency()).(*seekerManager)
	clockOpts := m.opts.ClockOptions()
	now := clockOpts.NowFn()()
	startNano := xtime.ToUnixNano(now)

	fakeTime := now
	fakeTimeLock := sync.Mutex{}
	// Setup a function that will allow us to dynamically modify the clock in
	// a concurrency-safe way
	newNowFn := func() time.Time {
		fakeTimeLock.Lock()
		defer fakeTimeLock.Unlock()
		return fakeTime
	}
	clockOpts = clockOpts.SetNowFn(newNowFn)
	m.opts = m.opts.SetClockOptions(clockOpts)

	// Initialize some seekers for a time period
	m.openAnyUnopenSeekersFn = func(byTime *seekersByTime) error {
		byTime.Lock()
		defer byTime.Unlock()

		// Don't overwrite if called again
		if len(byTime.seekers) != 0 {
			return nil
		}

		// Don't re-open if they should have expired
		fakeTimeLock.Lock()
		defer fakeTimeLock.Unlock()
		if !fakeTime.Equal(now) {
			return nil
		}

		mock := NewMockFileSetSeeker(ctrl)
		mock.EXPECT().Close().Return(nil)
		mocks := []borrowableSeeker{}
		mocks = append(mocks, borrowableSeeker{seeker: mock})
		byTime.seekers[startNano] = seekersAndBloom{
			seekers:     mocks,
			bloomFilter: nil,
		}
		return nil
	}

	// Force all the seekers to be opened
	require.NoError(t, m.CacheShardIndices(shards))

	// Notified everytime the openCloseLoop ticks
	tickCh := make(chan struct{})
	cleanupCh := make(chan struct{})
	m.sleepFn = func(_ time.Duration) {
		tickCh <- struct{}{}
	}

	metadata := testNs1Metadata(t)
	seekers := []ConcurrentFileSetSeeker{}

	require.NoError(t, m.Open(metadata))
	// Steps is a series of steps for the test. It is guaranteed that at least
	// one (not exactly one!) tick of the openCloseLoop will occur between every step.
	steps := []struct {
		title string
		step  func()
	}{
		{
			title: "Make sure it didn't clean up the seekers which are still in retention",
			step: func() {
				m.RLock()
				for _, shard := range shards {
					require.Equal(t, 1, len(m.seekersByTime(shard).seekers[startNano].seekers))
				}
				m.RUnlock()
			},
		},
		{
			title: "Borrow a seeker from each shard and then modify the clock such that they're out of retention",
			step: func() {
				for _, shard := range shards {
					seeker, err := m.Borrow(shard, now)
					require.NoError(t, err)
					require.NotNil(t, seeker)
					seekers = append(seekers, seeker)
				}

				fakeTimeLock.Lock()
				fakeTime = fakeTime.Add(10 * metadata.Options().RetentionOptions().RetentionPeriod())
				fakeTimeLock.Unlock()
			},
		},
		{
			title: "Make sure the seeker manager cant be closed while seekers are borrowed",
			step: func() {
				require.Equal(t, errCantCloseSeekerManagerWhileSeekersAreBorrowed, m.Close())
			},
		},
		{
			title: "Make sure that none of the seekers were cleaned up during the openCloseLoop tick (because they're still borrowed)",
			step: func() {
				m.RLock()
				for _, shard := range shards {
					require.Equal(t, 1, len(m.seekersByTime(shard).seekers[startNano].seekers))
				}
				m.RUnlock()
			},
		},
		{
			title: "Return the borrowed seekers",
			step: func() {
				for i, seeker := range seekers {
					require.NoError(t, m.Return(shards[i], now, seeker))
				}
			},
		},
		{
			title: "Make sure that the returned seekers were cleaned up during the openCloseLoop tick",
			step: func() {
				m.RLock()
				for _, shard := range shards {
					byTime := m.seekersByTime(shard)
					byTime.RLock()
					_, ok := byTime.seekers[startNano]
					byTime.RUnlock()
					require.False(t, ok)
				}
				m.RUnlock()
			},
		},
	}

	for _, step := range steps {
		// Wait for two notifications between steps to guarantee that the entirety
		// of the openCloseLoop is executed at least once
		<-tickCh
		<-tickCh
		step.step()
	}

	// Background goroutine that will pull notifications off the tickCh so that
	// the openCloseLoop is not blocked when we call Close()
	go func() {
		for {
			select {
			case <-tickCh:
				continue
			case <-cleanupCh:
				return
			}
		}
	}()

	// Restore previous interval once the openCloseLoop ends
	require.NoError(t, m.Close())
	// Make sure there are no goroutines still trying to write into the tickCh
	// to prevent the test itself from interfering with the goroutine leak test
	close(cleanupCh)
}
<|endoftext|>


<commit_before>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package xds

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/peer"

	"istio.io/istio/pilot/pkg/features"
	"istio.io/pkg/env"
)

var authPlaintext = env.RegisterBoolVar("XDS_AUTH_PLAINTEXT", false,
	"Authenticate plain text requests - used if Istiod is behind a gateway handling TLS").Get()

// authenticate authenticates the ADS request using the configured authenticators.
// Returns the validated principals or an error.
// If no authenticators are configured, or if the request is on a non-secure
// stream ( 15010 ) - returns an empty list of principals and no errors.
func (s *DiscoveryServer) authenticate(ctx context.Context) ([]string, error) {
	if !features.XDSAuth {
		return nil, nil
	}

	// Authenticate - currently just checks that request has a certificate signed with the our key.
	// Protected by flag to avoid breaking upgrades - should be enabled in multi-cluster/meshexpansion where
	// XDS is exposed.
	peerInfo, ok := peer.FromContext(ctx)
	if !ok {
		return nil, errors.New("invalid context")
	}
	// Not a TLS connection, we will not perform authentication
	// TODO: add a flag to prevent unauthenticated requests ( 15010 )
	// request not over TLS on the insecure port
	if _, ok := peerInfo.AuthInfo.(credentials.TLSInfo); !ok && !authPlaintext {
		return nil, nil
	}
	authFailMsgs := []string{}
	for _, authn := range s.Authenticators {
		u, err := authn.Authenticate(ctx)
		// If one authenticator passes, return
		if u != nil && u.Identities != nil && err == nil {
			return u.Identities, nil
		}
		authFailMsgs = append(authFailMsgs, fmt.Sprintf("Authenticator %s: %v", authn.AuthenticatorType(), err))
	}

	log.Errorf("Failed to authenticate client from %s: %s", peerInfo.Addr.String(), strings.Join(authFailMsgs, "; "))
	return nil, errors.New("authentication failure")
}
<commit_msg>Export XDS_AUTH_PLAINTEXT variable (#33449)<commit_after>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package xds

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/peer"

	"istio.io/istio/pilot/pkg/features"
	"istio.io/pkg/env"
)

var AuthPlaintext = env.RegisterBoolVar("XDS_AUTH_PLAINTEXT", false,
	"Authenticate plain text requests - used if Istiod is behind a gateway handling TLS").Get()

// authenticate authenticates the ADS request using the configured authenticators.
// Returns the validated principals or an error.
// If no authenticators are configured, or if the request is on a non-secure
// stream ( 15010 ) - returns an empty list of principals and no errors.
func (s *DiscoveryServer) authenticate(ctx context.Context) ([]string, error) {
	if !features.XDSAuth {
		return nil, nil
	}

	// Authenticate - currently just checks that request has a certificate signed with the our key.
	// Protected by flag to avoid breaking upgrades - should be enabled in multi-cluster/meshexpansion where
	// XDS is exposed.
	peerInfo, ok := peer.FromContext(ctx)
	if !ok {
		return nil, errors.New("invalid context")
	}
	// Not a TLS connection, we will not perform authentication
	// TODO: add a flag to prevent unauthenticated requests ( 15010 )
	// request not over TLS on the insecure port
	if _, ok := peerInfo.AuthInfo.(credentials.TLSInfo); !ok && !AuthPlaintext {
		return nil, nil
	}
	authFailMsgs := []string{}
	for _, authn := range s.Authenticators {
		u, err := authn.Authenticate(ctx)
		// If one authenticator passes, return
		if u != nil && u.Identities != nil && err == nil {
			return u.Identities, nil
		}
		authFailMsgs = append(authFailMsgs, fmt.Sprintf("Authenticator %s: %v", authn.AuthenticatorType(), err))
	}

	log.Errorf("Failed to authenticate client from %s: %s", peerInfo.Addr.String(), strings.Join(authFailMsgs, "; "))
	return nil, errors.New("authentication failure")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"time"

	"github.com/PonyvilleFM/aura/bot"
	"github.com/PonyvilleFM/aura/commands/source"
	"github.com/bwmarrin/discordgo"
	_ "github.com/joho/godotenv/autoload"
)

type aerial struct {
	cs *bot.CommandSet
	s  *discordgo.Session
}

const (
	djonHelp  = ``
	djoffHelp = ``
	setupHelp = ``
)

func (a *aerial) Handle(s *discordgo.Session, m *discordgo.MessageCreate) {
	err := a.cs.Run(s, m.Message)
	if err != nil {
		log.Println(err)
	}
}

var (
	username          = os.Getenv("USERNAME")
	password          = os.Getenv("PASSWORD")
	youtubeSpamRoomID = os.Getenv("DISCORD_YOUTUBESPAM_ROOMID")
	gClientID         = os.Getenv("GOOGLE_CLIENT_ID")
	gClientSecret     = os.Getenv("GOOGLE_CLIENT_SECRET")

	musicLinkRegex = regexp.MustCompile(`(.*)((http(s?):\/\/(www\.)?soundcloud.com\/.*)|(http(s?):\/\/(www\.)?youtube.com\/.*)|(http(s?):\/\/(www\.)?youtu.be\/.*))(.*)|(.*)http(s?):\/\/(www\.)?mixcloud.com\/.*`)
)

func main() {
	flag.Parse()
	dg, err := discordgo.New(username, password)
	if err != nil {
		log.Fatal(err)
	}

	a := &aerial{
		cs: bot.NewCommandSet(),
		s:  dg,
	}

	a.cs.Prefix = ";"
	a.cs.AddCmd("np", "shows what's up next on Ponyville FM", bot.NoPermissions, np)
	a.cs.AddCmd("dj", "shows which DJ is up next on Ponyville FM", bot.NoPermissions, dj)
	a.cs.AddCmd("stats", "shows radio station statistics for Ponyville FM", bot.NoPermissions, stats)
	a.cs.AddCmd("schedule", "shows the future radio schedule for Ponyville FM", bot.NoPermissions, schedule)
	a.cs.AddCmd("printerfact", "shows useful facts about printers", bot.NoPermissions, printerFact)
	a.cs.AddCmd("hipster", "hip me up fam", bot.NoPermissions, hipster)
	a.cs.AddCmd("source", "Source code information", bot.NoPermissions, source.Source)

	dg.AddHandler(a.Handle)
	dg.AddHandler(pesterLink)
	dg.AddHandler(messageCreate)
	dg.AddHandler(imageMeEvent)

	err = dg.Open()
	if err != nil {
		log.Fatal(err)
	}

	log.Println("ready")

	<-make(chan struct{})
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	// Print message to stdout.
	fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content)
}
<commit_msg>aerial: use a discord bot account<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"time"

	"github.com/PonyvilleFM/aura/bot"
	"github.com/PonyvilleFM/aura/commands/source"
	"github.com/bwmarrin/discordgo"
	_ "github.com/joho/godotenv/autoload"
)

type aerial struct {
	cs *bot.CommandSet
	s  *discordgo.Session
}

const (
	djonHelp  = ``
	djoffHelp = ``
	setupHelp = ``
)

func (a *aerial) Handle(s *discordgo.Session, m *discordgo.MessageCreate) {
	err := a.cs.Run(s, m.Message)
	if err != nil {
		log.Println(err)
	}
}

var (
	botToken          = os.Getenv("TOKEN")
	youtubeSpamRoomID = os.Getenv("DISCORD_YOUTUBESPAM_ROOMID")
	gClientID         = os.Getenv("GOOGLE_CLIENT_ID")
	gClientSecret     = os.Getenv("GOOGLE_CLIENT_SECRET")

	musicLinkRegex = regexp.MustCompile(`(.*)((http(s?):\/\/(www\.)?soundcloud.com\/.*)|(http(s?):\/\/(www\.)?youtube.com\/.*)|(http(s?):\/\/(www\.)?youtu.be\/.*))(.*)|(.*)http(s?):\/\/(www\.)?mixcloud.com\/.*`)
)

func main() {
	flag.Parse()
	dg, err := discordgo.New(botToken)
	if err != nil {
		log.Fatal(err)
	}

	a := &aerial{
		cs: bot.NewCommandSet(),
		s:  dg,
	}

	a.cs.Prefix = ";"
	a.cs.AddCmd("np", "shows what's up next on Ponyville FM", bot.NoPermissions, np)
	a.cs.AddCmd("dj", "shows which DJ is up next on Ponyville FM", bot.NoPermissions, dj)
	a.cs.AddCmd("stats", "shows radio station statistics for Ponyville FM", bot.NoPermissions, stats)
	a.cs.AddCmd("schedule", "shows the future radio schedule for Ponyville FM", bot.NoPermissions, schedule)
	a.cs.AddCmd("printerfact", "shows useful facts about printers", bot.NoPermissions, printerFact)
	a.cs.AddCmd("hipster", "hip me up fam", bot.NoPermissions, hipster)
	a.cs.AddCmd("source", "Source code information", bot.NoPermissions, source.Source)

	dg.AddHandler(a.Handle)
	dg.AddHandler(pesterLink)
	dg.AddHandler(messageCreate)
	dg.AddHandler(imageMeEvent)

	err = dg.Open()
	if err != nil {
		log.Fatal(err)
	}

	log.Println("ready")

	<-make(chan struct{})
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	// Print message to stdout.
	fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content)
}
<|endoftext|>


<commit_before>package cmd

import (
	"context"
	"fmt"
	"os"

	"github.com/AlecAivazis/survey/v2"
	"github.com/AlecAivazis/survey/v2/terminal"
	"github.com/spf13/cobra"

	fn "knative.dev/kn-plugin-func"
	"knative.dev/kn-plugin-func/utils"
)

func NewConfigLabelsCmd(loaderSaver functionLoaderSaver) *cobra.Command {
	var configLabelsCmd = &cobra.Command{
		Use:   "labels",
		Short: "List and manage configured labels for a function",
		Long: `List and manage configured labels for a function

Prints configured labels for a function project present in
the current directory or from the directory specified with --path.
`,
		SuggestFor: []string{"albels", "abels", "label"},
		PreRunE:    bindEnv("path"),
		RunE: func(cmd *cobra.Command, args []string) (err error) {
			function, err := initConfigCommand(args, loaderSaver)
			if err != nil {
				return
			}

			listLabels(function)

			return
		},
	}

	var configLabelsAddCmd = &cobra.Command{
		Use:   "add",
		Short: "Add labels to the function configuration",
		Long: `Add labels to the function configuration

Interactive prompt to add labels to the function project in the current
directory or from the directory specified with --path.

The label can be set directly from a value or from an environment variable on
the local machine.
`,
		SuggestFor: []string{"ad", "create", "insert", "append"},
		PreRunE:    bindEnv("path"),
		RunE: func(cmd *cobra.Command, args []string) (err error) {
			function, err := initConfigCommand(args, loaderSaver)
			if err != nil {
				return
			}

			return runAddLabelsPrompt(cmd.Context(), function, loaderSaver)
		},
	}

	var configLabelsRemoveCmd = &cobra.Command{
		Use:   "remove",
		Short: "Remove labels from the function configuration",
		Long: `Remove labels from the function configuration

Interactive prompt to remove labels from the function project in the current
directory or from the directory specified with --path.
`,
		SuggestFor: []string{"del", "delete", "rmeove"},
		PreRunE:    bindEnv("path"),
		RunE: func(cmd *cobra.Command, args []string) (err error) {
			function, err := initConfigCommand(args, loaderSaver)
			if err != nil {
				return
			}

			return runRemoveLabelsPrompt(function, loaderSaver)
		},
	}

	configLabelsCmd.AddCommand(configLabelsAddCmd)
	configLabelsAddCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)")
	configLabelsCmd.AddCommand(configLabelsRemoveCmd)
	configLabelsRemoveCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)")

	return configLabelsCmd
}

func listLabels(f fn.Function) {
	if len(f.Labels) == 0 {
		fmt.Println("There aren't any configured labels")
		return
	}

	fmt.Println("Configured labels:")
	for _, e := range f.Labels {
		fmt.Println(" - ", e.String())
	}
}

func runAddLabelsPrompt(ctx context.Context, f fn.Function, saver functionSaver) (err error) {

	insertToIndex := 0

	// SECTION - if there are some labels already set, choose the position of the new entry
	if len(f.Labels) > 0 {
		options := []string{}
		for _, e := range f.Labels {
			options = append(options, fmt.Sprintf("Insert before:  %s", e.String()))
		}
		options = append(options, "Insert here.")

		selectedLabel := ""
		prompt := &survey.Select{
			Message: "Where do you want to add the label?",
			Options: options,
			Default: options[len(options)-1],
		}
		err = survey.AskOne(prompt, &selectedLabel)
		if err != nil {
			if err == terminal.InterruptErr {
				return nil
			}
			return
		}

		for i, option := range options {
			if option == selectedLabel {
				insertToIndex = i
				break
			}
		}
	}

	// SECTION - select the type of label to be added
	selectedOption := ""
	const (
		optionLabelValue = "Label with a specified value"
		optionLabelLocal = "Value from a local environment variable"
	)
	options := []string{optionLabelValue, optionLabelLocal}

	err = survey.AskOne(&survey.Select{
		Message: "What type of label do you want to add?",
		Options: options,
	}, &selectedOption)
	if err != nil {
		if err == terminal.InterruptErr {
			return nil
		}
		return
	}

	newPair := fn.Label{}

	switch selectedOption {
	// SECTION - add new label with the specified value
	case optionLabelValue:
		qs := []*survey.Question{
			{
				Name:   "key",
				Prompt: &survey.Input{Message: "Please specify the label key:"},
				Validate: func(val interface{}) error {
					return utils.ValidateLabelKey(val.(string))
				},
			},
			{
				Name:   "value",
				Prompt: &survey.Input{Message: "Please specify the label value:"},
				Validate: func(val interface{}) error {
					return utils.ValidateLabelValue(val.(string))
				}},
		}
		answers := struct {
			Key   string
			Value string
		}{}

		err = survey.Ask(qs, &answers)
		if err != nil {
			if err == terminal.InterruptErr {
				return nil
			}
			return
		}

		newPair.Key = &answers.Key
		newPair.Value = &answers.Value

	// SECTION - add new label with value from a local environment variable
	case optionLabelLocal:
		qs := []*survey.Question{
			{
				Name:   "key",
				Prompt: &survey.Input{Message: "Please specify the label key:"},
				Validate: func(val interface{}) error {
					return utils.ValidateLabelKey(val.(string))
				},
			},
			{
				Name:   "value",
				Prompt: &survey.Input{Message: "Please specify the local environment variable:"},
				Validate: func(val interface{}) error {
					return utils.ValidateLabelValue(val.(string))
				},
			},
		}
		answers := struct {
			Key   string
			Value string
		}{}

		err = survey.Ask(qs, &answers)
		if err != nil {
			if err == terminal.InterruptErr {
				return nil
			}
			return
		}

		if _, ok := os.LookupEnv(answers.Value); !ok {
			fmt.Printf("Warning: specified local environment variable %q is not set\n", answers.Value)
		}

		value := fmt.Sprintf("{{ env:%s }}", answers.Value)
		newPair.Key = &answers.Key
		newPair.Value = &value
	}

	// we have all necessary information -> let's insert the label to the selected position in the list
	if insertToIndex == len(f.Labels) {
		f.Labels = append(f.Labels, newPair)
	} else {
		f.Labels = append(f.Labels[:insertToIndex+1], f.Labels[insertToIndex:]...)
		f.Labels[insertToIndex] = newPair
	}

	err = saver.Save(f)
	if err == nil {
		fmt.Println("Label entry was added to the function configuration")
	}

	return
}

func runRemoveLabelsPrompt(f fn.Function, saver functionSaver) (err error) {
	if len(f.Labels) == 0 {
		fmt.Println("There aren't any configured labels")
		return
	}

	options := []string{}
	for _, e := range f.Labels {
		options = append(options, e.String())
	}

	selectedLabel := ""
	prompt := &survey.Select{
		Message: "Which labels do you want to remove?",
		Options: options,
	}
	err = survey.AskOne(prompt, &selectedLabel)
	if err != nil {
		if err == terminal.InterruptErr {
			return nil
		}
		return
	}

	var newLabels fn.Labels
	removed := false
	for i, e := range f.Labels {
		if e.String() == selectedLabel {
			newLabels = append(f.Labels[:i], f.Labels[i+1:]...)
			removed = true
			break
		}
	}

	if removed {
		f.Labels = newLabels
		err = saver.Save(f)
		if err == nil {
			fmt.Println("Label was removed from the function configuration")
		}
	}

	return
}
<commit_msg>fix: `config labels` panic (#493)<commit_after>package cmd

import (
	"context"
	"fmt"
	"os"

	"github.com/AlecAivazis/survey/v2"
	"github.com/AlecAivazis/survey/v2/terminal"
	"github.com/spf13/cobra"

	fn "knative.dev/kn-plugin-func"
	"knative.dev/kn-plugin-func/utils"
)

func NewConfigLabelsCmd(loaderSaver functionLoaderSaver) *cobra.Command {
	var configLabelsCmd = &cobra.Command{
		Use:   "labels",
		Short: "List and manage configured labels for a function",
		Long: `List and manage configured labels for a function

Prints configured labels for a function project present in
the current directory or from the directory specified with --path.
`,
		SuggestFor: []string{"albels", "abels", "label"},
		PreRunE:    bindEnv("path"),
		RunE: func(cmd *cobra.Command, args []string) (err error) {
			function, err := initConfigCommand(args, loaderSaver)
			if err != nil {
				return
			}

			listLabels(function)

			return
		},
	}

	var configLabelsAddCmd = &cobra.Command{
		Use:   "add",
		Short: "Add labels to the function configuration",
		Long: `Add labels to the function configuration

Interactive prompt to add labels to the function project in the current
directory or from the directory specified with --path.

The label can be set directly from a value or from an environment variable on
the local machine.
`,
		SuggestFor: []string{"ad", "create", "insert", "append"},
		PreRunE:    bindEnv("path"),
		RunE: func(cmd *cobra.Command, args []string) (err error) {
			function, err := initConfigCommand(args, loaderSaver)
			if err != nil {
				return
			}

			return runAddLabelsPrompt(cmd.Context(), function, loaderSaver)
		},
	}

	var configLabelsRemoveCmd = &cobra.Command{
		Use:   "remove",
		Short: "Remove labels from the function configuration",
		Long: `Remove labels from the function configuration

Interactive prompt to remove labels from the function project in the current
directory or from the directory specified with --path.
`,
		SuggestFor: []string{"del", "delete", "rmeove"},
		PreRunE:    bindEnv("path"),
		RunE: func(cmd *cobra.Command, args []string) (err error) {
			function, err := initConfigCommand(args, loaderSaver)
			if err != nil {
				return
			}

			return runRemoveLabelsPrompt(function, loaderSaver)
		},
	}

	configLabelsCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)")
	configLabelsCmd.AddCommand(configLabelsAddCmd)
	configLabelsAddCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)")
	configLabelsCmd.AddCommand(configLabelsRemoveCmd)
	configLabelsRemoveCmd.Flags().StringP("path", "p", cwd(), "Path to the project directory (Env: $FUNC_PATH)")

	return configLabelsCmd
}

func listLabels(f fn.Function) {
	if len(f.Labels) == 0 {
		fmt.Println("There aren't any configured labels")
		return
	}

	fmt.Println("Configured labels:")
	for _, e := range f.Labels {
		fmt.Println(" - ", e.String())
	}
}

func runAddLabelsPrompt(ctx context.Context, f fn.Function, saver functionSaver) (err error) {

	insertToIndex := 0

	// SECTION - if there are some labels already set, choose the position of the new entry
	if len(f.Labels) > 0 {
		options := []string{}
		for _, e := range f.Labels {
			options = append(options, fmt.Sprintf("Insert before:  %s", e.String()))
		}
		options = append(options, "Insert here.")

		selectedLabel := ""
		prompt := &survey.Select{
			Message: "Where do you want to add the label?",
			Options: options,
			Default: options[len(options)-1],
		}
		err = survey.AskOne(prompt, &selectedLabel)
		if err != nil {
			if err == terminal.InterruptErr {
				return nil
			}
			return
		}

		for i, option := range options {
			if option == selectedLabel {
				insertToIndex = i
				break
			}
		}
	}

	// SECTION - select the type of label to be added
	selectedOption := ""
	const (
		optionLabelValue = "Label with a specified value"
		optionLabelLocal = "Value from a local environment variable"
	)
	options := []string{optionLabelValue, optionLabelLocal}

	err = survey.AskOne(&survey.Select{
		Message: "What type of label do you want to add?",
		Options: options,
	}, &selectedOption)
	if err != nil {
		if err == terminal.InterruptErr {
			return nil
		}
		return
	}

	newPair := fn.Label{}

	switch selectedOption {
	// SECTION - add new label with the specified value
	case optionLabelValue:
		qs := []*survey.Question{
			{
				Name:   "key",
				Prompt: &survey.Input{Message: "Please specify the label key:"},
				Validate: func(val interface{}) error {
					return utils.ValidateLabelKey(val.(string))
				},
			},
			{
				Name:   "value",
				Prompt: &survey.Input{Message: "Please specify the label value:"},
				Validate: func(val interface{}) error {
					return utils.ValidateLabelValue(val.(string))
				}},
		}
		answers := struct {
			Key   string
			Value string
		}{}

		err = survey.Ask(qs, &answers)
		if err != nil {
			if err == terminal.InterruptErr {
				return nil
			}
			return
		}

		newPair.Key = &answers.Key
		newPair.Value = &answers.Value

	// SECTION - add new label with value from a local environment variable
	case optionLabelLocal:
		qs := []*survey.Question{
			{
				Name:   "key",
				Prompt: &survey.Input{Message: "Please specify the label key:"},
				Validate: func(val interface{}) error {
					return utils.ValidateLabelKey(val.(string))
				},
			},
			{
				Name:   "value",
				Prompt: &survey.Input{Message: "Please specify the local environment variable:"},
				Validate: func(val interface{}) error {
					return utils.ValidateLabelValue(val.(string))
				},
			},
		}
		answers := struct {
			Key   string
			Value string
		}{}

		err = survey.Ask(qs, &answers)
		if err != nil {
			if err == terminal.InterruptErr {
				return nil
			}
			return
		}

		if _, ok := os.LookupEnv(answers.Value); !ok {
			fmt.Printf("Warning: specified local environment variable %q is not set\n", answers.Value)
		}

		value := fmt.Sprintf("{{ env:%s }}", answers.Value)
		newPair.Key = &answers.Key
		newPair.Value = &value
	}

	// we have all necessary information -> let's insert the label to the selected position in the list
	if insertToIndex == len(f.Labels) {
		f.Labels = append(f.Labels, newPair)
	} else {
		f.Labels = append(f.Labels[:insertToIndex+1], f.Labels[insertToIndex:]...)
		f.Labels[insertToIndex] = newPair
	}

	err = saver.Save(f)
	if err == nil {
		fmt.Println("Label entry was added to the function configuration")
	}

	return
}

func runRemoveLabelsPrompt(f fn.Function, saver functionSaver) (err error) {
	if len(f.Labels) == 0 {
		fmt.Println("There aren't any configured labels")
		return
	}

	options := []string{}
	for _, e := range f.Labels {
		options = append(options, e.String())
	}

	selectedLabel := ""
	prompt := &survey.Select{
		Message: "Which labels do you want to remove?",
		Options: options,
	}
	err = survey.AskOne(prompt, &selectedLabel)
	if err != nil {
		if err == terminal.InterruptErr {
			return nil
		}
		return
	}

	var newLabels fn.Labels
	removed := false
	for i, e := range f.Labels {
		if e.String() == selectedLabel {
			newLabels = append(f.Labels[:i], f.Labels[i+1:]...)
			removed = true
			break
		}
	}

	if removed {
		f.Labels = newLabels
		err = saver.Save(f)
		if err == nil {
			fmt.Println("Label was removed from the function configuration")
		}
	}

	return
}
<|endoftext|>