<commit_before>package template

import (
	"bytes"
	"crypto/tls"
	"errors"
	"github.com/golang/glog"
	"github.com/qorio/maestro/pkg/registry"
	"github.com/qorio/maestro/pkg/zk"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

var (
	ErrNotSupportedProtocol = errors.New("protocol-not-supported")
	ErrNotConnectedToZk     = errors.New("not-connected-to-zk")
)

func ApplyTemplate(body string, context interface{}, funcs ...template.FuncMap) (string, error) {
	var t *template.Template
	var err error

	if len(funcs) > 0 {
		t, err = template.New(body).Funcs(funcs[0]).Parse(body)
		if err != nil {
			return "", err
		}
	} else {
		t, err = template.New(body).Parse(body)
		if err != nil {
			return "", err
		}
	}

	var buff bytes.Buffer
	if err := t.Execute(&buff, context); err != nil {
		return "", err
	} else {
		return buff.String(), nil
	}
}

func ParseHostPort(value string) (host, port string) {
	parts := strings.Split(value, ":")
	if len(parts) == 2 {
		return parts[0], parts[1]
	}
	return "", ""
}

func FetchUrl(urlRef string, headers map[string]string, zc ...zk.ZK) (body string, mime string, err error) {
	switch {
	case strings.Index(urlRef, "http://") == 0, strings.Index(urlRef, "https://") == 0:
		url, err := url.Parse(urlRef)
		if err != nil {
			return "", "", err
		}

		// don't check certificate for https
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
		client := &http.Client{Transport: tr}
		req, err := http.NewRequest("GET", url.String(), nil)

		for h, v := range headers {
			req.Header.Add(h, v)
		}

		resp, err := client.Do(req)
		if err != nil {
			return "", "", err
		}
		content, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			return "", "", err
		}
		return string(content), resp.Header.Get("Content-Type"), nil

	case strings.Index(urlRef, "file://") == 0:
		file := urlRef[len("file://"):]
		f, err := os.Open(file)
		if err != nil {
			return "", "", err
		}
		defer f.Close()
		if buff, err := ioutil.ReadAll(f); err != nil {
			return "", "", err
		} else {
			return string(buff), "text/plain", nil
		}

	case strings.Index(urlRef, "string://") == 0:
		content := urlRef[len("string://"):]
		return content, "text/plain", nil

	case strings.Index(urlRef, "env://") == 0:
		if len(zc) == 0 {
			return "", "", ErrNotConnectedToZk
		}
		path := urlRef[len("env://"):]
		n, err := zc[0].Get(path)
		if err != nil {
			return "", "", err
		}
		glog.Infoln("Content from environment: Path=", urlRef, "Err=", err)
		// try resolve
		_, v, err := zk.Resolve(zc[0], registry.Path(path), n.GetValueString())
		if err != nil {
			return "", "", err
		}
		return v, "text/plain", nil
	}
	return "", "", ErrNotSupportedProtocol
}

func apply_template(key, tmpl string, data interface{}, funcMap ...template.FuncMap) ([]byte, error) {
	t := template.New(key)
	if len(funcMap) > 0 {
		t = t.Funcs(funcMap[0])
	}
	t, err := t.Parse(tmpl)
	if err != nil {
		return nil, err
	}
	var buff bytes.Buffer
	err = t.Execute(&buff, data)
	return buff.Bytes(), err
}

func ExecuteTemplateUrl(zc zk.ZK, url string, authToken string, data interface{}, funcs ...template.FuncMap) ([]byte, error) {
	headers := map[string]string{
		"Authorization": "Bearer " + authToken,
	}

	config_template_text, _, err := FetchUrl(url, headers, zc)
	if err != nil {
		glog.Warningln("Error fetching template:", err)
		return nil, err
	}

	funcMap := template.FuncMap{
		"containers": func(path, service_port string) ([]interface{}, error) {
			// We support variables inside the function argument
			p, err := apply_template(path, path, data)
			if err != nil {
				return nil, err
			}
			return hostport_list_from_zk(zc, string(p), service_port)
		},
		"inline": func(url string) (string, error) {
			// We support variables inside the function argument
			u, err := apply_template(url, url, data)
			if err != nil {
				return "", err
			}
			content, _, err := FetchUrl(string(u), headers, zc)
			if err != nil {
				return "", err
			}
			return content, nil
		},
		"file": func(url string, dir ...string) (string, error) {
			// We support variables inside the function argument
			u, err := apply_template(url, url, data)
			if err != nil {
				return "", err
			}
			content, _, err := FetchUrl(string(u), headers, zc)
			if err != nil {
				return "", err
			}
			// Write to local file and return the path
			parent := os.TempDir()
			if len(dir) > 0 {
				parent = dir[0]
				// We support variables inside the function argument
				p, err := apply_template(parent, parent, data)
				if err != nil {
					return "", err
				}
				parent = string(p)
			}
			path := filepath.Join(parent, filepath.Base(string(u)))
			err = ioutil.WriteFile(path, []byte(content), 0777)
			glog.Infoln("Written", len([]byte(content)), " bytes to", path, "Err=", err)
			if err != nil {
				return "", err
			}
			return path, nil
		},
	}

	if len(funcs) > 0 {
		for k, f := range funcs[0] {
			funcMap[k] = f
		}
	}

	config_template, err := template.New(url).Funcs(funcMap).Parse(config_template_text)
	if err != nil {
		glog.Warningln("Error parsing template", url, err)
		return nil, err
	}
	var buff bytes.Buffer
	err = config_template.Execute(&buff, data)
	return buff.Bytes(), err
}

// Supports references -- if the value of the node is env:///.. then resolve the reference.
func hostport_list_from_zk(zc zk.ZK, containers_path, service_port string) ([]interface{}, error) {

	n, err := zk.Follow(zc, registry.Path(containers_path))
	if err != nil {
		return nil, err
	}

	all, err := n.VisitChildrenRecursive(func(z *zk.Node) bool {
		_, port := ParseHostPort(z.GetBasename())
		return port == service_port && z.IsLeaf()
	})
	if err != nil {
		return nil, err
	}

	list := make([]interface{}, 0)
	for _, c := range all {
		host, port := ParseHostPort(c.GetValueString())
		list = append(list, struct {
			Host string
			Port string
		}{
			Host: host,
			Port: port,
		})
	}
	return list, nil
}
<commit_msg>Add new protocol func:// to use in-memory function as data source for template<commit_after>package template

import (
	"bytes"
	"crypto/tls"
	"errors"
	"github.com/golang/glog"
	"github.com/qorio/maestro/pkg/registry"
	"github.com/qorio/maestro/pkg/zk"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

var (
	ErrNotSupportedProtocol = errors.New("protocol-not-supported")
	ErrNotConnectedToZk     = errors.New("not-connected-to-zk")
	ErrMissingTemplateFunc  = errors.New("no-template-func")
	ErrBadTemplateFunc      = errors.New("err-bad-template-func")
)

func ApplyTemplate(body string, context interface{}, funcs ...template.FuncMap) (string, error) {
	var t *template.Template
	var err error

	if len(funcs) > 0 {
		t, err = template.New(body).Funcs(funcs[0]).Parse(body)
		if err != nil {
			return "", err
		}
	} else {
		t, err = template.New(body).Parse(body)
		if err != nil {
			return "", err
		}
	}

	var buff bytes.Buffer
	if err := t.Execute(&buff, context); err != nil {
		return "", err
	} else {
		return buff.String(), nil
	}
}

func ParseHostPort(value string) (host, port string) {
	parts := strings.Split(value, ":")
	if len(parts) == 2 {
		return parts[0], parts[1]
	}
	return "", ""
}

func FetchUrl(urlRef string, headers map[string]string, zc ...zk.ZK) (body string, mime string, err error) {
	switch {
	case strings.Index(urlRef, "http://") == 0, strings.Index(urlRef, "https://") == 0:
		url, err := url.Parse(urlRef)
		if err != nil {
			return "", "", err
		}

		// don't check certificate for https
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
		client := &http.Client{Transport: tr}
		req, err := http.NewRequest("GET", url.String(), nil)

		for h, v := range headers {
			req.Header.Add(h, v)
		}

		resp, err := client.Do(req)
		if err != nil {
			return "", "", err
		}
		content, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			return "", "", err
		}
		return string(content), resp.Header.Get("Content-Type"), nil

	case strings.Index(urlRef, "file://") == 0:
		file := urlRef[len("file://"):]
		f, err := os.Open(file)
		if err != nil {
			return "", "", err
		}
		defer f.Close()
		if buff, err := ioutil.ReadAll(f); err != nil {
			return "", "", err
		} else {
			return string(buff), "text/plain", nil
		}

	case strings.Index(urlRef, "string://") == 0:
		content := urlRef[len("string://"):]
		return content, "text/plain", nil

	case strings.Index(urlRef, "env://") == 0:
		if len(zc) == 0 {
			return "", "", ErrNotConnectedToZk
		}
		path := urlRef[len("env://"):]
		n, err := zc[0].Get(path)
		if err != nil {
			return "", "", err
		}
		glog.Infoln("Content from environment: Path=", urlRef, "Err=", err)
		// try resolve
		_, v, err := zk.Resolve(zc[0], registry.Path(path), n.GetValueString())
		if err != nil {
			return "", "", err
		}
		return v, "text/plain", nil
	}
	return "", "", ErrNotSupportedProtocol
}

func apply_template(key, tmpl string, data interface{}, funcMap ...template.FuncMap) ([]byte, error) {
	t := template.New(key)
	if len(funcMap) > 0 {
		t = t.Funcs(funcMap[0])
	}
	t, err := t.Parse(tmpl)
	if err != nil {
		return nil, err
	}
	var buff bytes.Buffer
	err = t.Execute(&buff, data)
	return buff.Bytes(), err
}

func ExecuteTemplateUrl(zc zk.ZK, url string, authToken string, data interface{}, funcs ...template.FuncMap) ([]byte, error) {
	headers := map[string]string{
		"Authorization": "Bearer " + authToken,
	}

	var config_template_text string
	var err error
	switch {
	case strings.Index(url, "func://") == 0 && len(funcs) == 1:
		if f, has := funcs[0][url[len("func://"):]]; has {
			if ff, ok := f.(func() string); ok {
				config_template_text = ff()
			} else {
				glog.Warningln("Bad function:", url)
				return nil, ErrBadTemplateFunc
			}
		} else {
			glog.Warningln("Error no function:", url)
			return nil, ErrMissingTemplateFunc
		}
	default:
		config_template_text, _, err = FetchUrl(url, headers, zc)
		if err != nil {
			glog.Warningln("Error fetching template:", err)
			return nil, err
		}
	}

	funcMap := template.FuncMap{
		"containers": func(path, service_port string) ([]interface{}, error) {
			// We support variables inside the function argument
			p, err := apply_template(path, path, data)
			if err != nil {
				return nil, err
			}
			return hostport_list_from_zk(zc, string(p), service_port)
		},
		"inline": func(url string) (string, error) {
			// We support variables inside the function argument
			u, err := apply_template(url, url, data)
			if err != nil {
				return "", err
			}
			content, _, err := FetchUrl(string(u), headers, zc)
			if err != nil {
				return "", err
			}
			return content, nil
		},
		"file": func(url string, dir ...string) (string, error) {
			// We support variables inside the function argument
			u, err := apply_template(url, url, data)
			if err != nil {
				return "", err
			}
			content, _, err := FetchUrl(string(u), headers, zc)
			if err != nil {
				return "", err
			}
			// Write to local file and return the path
			parent := os.TempDir()
			if len(dir) > 0 {
				parent = dir[0]
				// We support variables inside the function argument
				p, err := apply_template(parent, parent, data)
				if err != nil {
					return "", err
				}
				parent = string(p)
			}
			path := filepath.Join(parent, filepath.Base(string(u)))
			err = ioutil.WriteFile(path, []byte(content), 0777)
			glog.Infoln("Written", len([]byte(content)), " bytes to", path, "Err=", err)
			if err != nil {
				return "", err
			}
			return path, nil
		},
	}

	if len(funcs) > 0 {
		for k, f := range funcs[0] {
			funcMap[k] = f
		}
	}

	config_template, err := template.New(url).Funcs(funcMap).Parse(config_template_text)
	if err != nil {
		glog.Warningln("Error parsing template", url, err)
		return nil, err
	}
	var buff bytes.Buffer
	err = config_template.Execute(&buff, data)
	return buff.Bytes(), err
}

// Supports references -- if the value of the node is env:///.. then resolve the reference.
func hostport_list_from_zk(zc zk.ZK, containers_path, service_port string) ([]interface{}, error) {

	n, err := zk.Follow(zc, registry.Path(containers_path))
	if err != nil {
		return nil, err
	}

	all, err := n.VisitChildrenRecursive(func(z *zk.Node) bool {
		_, port := ParseHostPort(z.GetBasename())
		return port == service_port && z.IsLeaf()
	})
	if err != nil {
		return nil, err
	}

	list := make([]interface{}, 0)
	for _, c := range all {
		host, port := ParseHostPort(c.GetValueString())
		list = append(list, struct {
			Host string
			Port string
		}{
			Host: host,
			Port: port,
		})
	}
	return list, nil
}
<|endoftext|>


<commit_before>package epgo

import (
	"fmt"
	"strings"

	// CaltechLibrary Packages
	"github.com/caltechlibrary/dataset"
)

// GetGrantNumbersByFunder returns a JSON list of unique Group names in index
func (api *EPrintsAPI) GetGrantNumbersByFunder(direction int) ([]string, error) {
	c, err := dataset.Open(api.Dataset)
	failCheck(err, fmt.Sprintf("GetGrantNumbers() %s, %s", api.Dataset, err))
	defer c.Close()

	sl, err := c.Select("funder")
	if err != nil {
		return nil, err
	}
	sl.Sort(direction)

	// Note: Aggregate the local group names
	grantNumbersByFunder := []string{}
	lastKey := ""
	curKey := ""
	for _, id := range sl.List() {
		kys := strings.Split(id, indexDelimiter)
		funderName := first(kys)
		grantNo := second(kys)
		//NOTE: Since some ageny funded pubs lack Grant No, skip those (They will be listed under funder)
		if len(grantNo) > 0 && len(funderName) > 0 {
			curKey = fmt.Sprintf("%s%s%s", funderName, indexDelimiter, grantNo)

			if strings.Compare(curKey, lastKey) != 0 {
				lastKey = curKey
				grantNumbersByFunder = append(grantNumbersByFunder, curKey)
			}
		}
	}
	return grantNumbersByFunder, nil
}

// GetGrantNumberPublications returns a list of EPrint records with funderName
func (api *EPrintsAPI) GetGrantNumberPublications(funderName string, grantNumber string, start, count, direction int) ([]*Record, error) {
	c, err := dataset.Open(api.Dataset)
	failCheck(err, fmt.Sprintf("GetGrantNumberPublications() %s, %s", api.Dataset, err))
	defer c.Close()

	// Note: Filter for funderName/Grant Number, passing matching eprintIDs to getRecordList()
	ids, err := api.GetIDsBySelectList("funder", direction, func(s string) bool {
		parts := strings.Split(s, indexDelimiter)
		if strings.Compare(funderName, first(parts)) == 0 && strings.Compare(grantNumber, second(parts)) == 0 {
			return true
		}
		return false
	})
	if err != nil {
		return nil, err
	}
	return getRecordList(c, ids, start, count, func(rec *Record) bool {
		if rec.IsPublished == "pub" {
			return true
		}
		return false
	})
}

// GetGrantNumberArticles returns a list of EPrint records with funderName
func (api *EPrintsAPI) GetGrantNumberArticles(funderName string, grantNumber string, start, count, direction int) ([]*Record, error) {
	c, err := dataset.Open(api.Dataset)
	failCheck(err, fmt.Sprintf("GetGrantNumberArticles() %s, %s", api.Dataset, err))
	defer c.Close()

	// Note: Filter for funderName/GrantNumber, passing matching eprintIDs to getRecordList()
	ids, err := api.GetIDsBySelectList("funder", direction, func(s string) bool {
		parts := strings.Split(s, indexDelimiter)
		if strings.Compare(funderName, first(parts)) == 0 && strings.Compare(grantNumber, second(parts)) == 0 {
			return true
		}
		return false
	})
	if err != nil {
		return nil, err
	}
	return getRecordList(c, ids, start, count, func(rec *Record) bool {
		if rec.IsPublished == "pub" && rec.Type == "article" {
			return true
		}
		return false
	})
}
<commit_msg>Quick save<commit_after>package epgo

import (
	"fmt"
	"strings"

	// CaltechLibrary Packages
	"github.com/caltechlibrary/dataset"
)

// GetGrantNumbersByFunder returns a JSON list of unique Group names in index
func (api *EPrintsAPI) GetGrantNumbersByFunder(direction int) ([]string, error) {
	c, err := dataset.Open(api.Dataset)
	failCheck(err, fmt.Sprintf("GetGrantNumbers() %s, %s", api.Dataset, err))
	defer c.Close()

	sl, err := c.Select("funder")
	if err != nil {
		return nil, err
	}
	sl.Sort(direction)

	// Note: Aggregate the local group names
	grantNumbersByFunder := []string{}
	lastKey := ""
	curKey := ""
	for _, id := range sl.List() {
		kys := strings.Split(id, indexDelimiter)
		funderName := first(kys)
		grantNo := second(kys)
		//NOTE: Since some ageny funded pubs lack Grant No, skip those (They will be listed under funder)
		if len(grantNo) > 0 && len(funderName) > 0 {
			curKey = fmt.Sprintf("%s%s%s", funderName, indexDelimiter, grantNo)

			if curKey != lastKey {
				lastKey = curKey
				grantNumbersByFunder = append(grantNumbersByFunder, curKey)
			}
		}
	}
	return grantNumbersByFunder, nil
}

// GetGrantNumberPublications returns a list of EPrint records with funderName
func (api *EPrintsAPI) GetGrantNumberPublications(funderName string, grantNumber string, start, count, direction int) ([]*Record, error) {
	c, err := dataset.Open(api.Dataset)
	failCheck(err, fmt.Sprintf("GetGrantNumberPublications() %s, %s", api.Dataset, err))
	defer c.Close()

	// Note: Filter for funderName/Grant Number, passing matching eprintIDs to getRecordList()
	ids, err := api.GetIDsBySelectList("funder", direction, func(s string) bool {
		parts := strings.Split(s, indexDelimiter)
		if funderName == first(parts) && grantNumber == second(parts) {
			return true
		}
		return false
	})
	if err != nil {
		return nil, err
	}
	return getRecordList(c, ids, start, count, func(rec *Record) bool {
		if rec.IsPublished == "pub" {
			return true
		}
		return false
	})
}

// GetGrantNumberArticles returns a list of EPrint records with funderName
func (api *EPrintsAPI) GetGrantNumberArticles(funderName string, grantNumber string, start, count, direction int) ([]*Record, error) {
	c, err := dataset.Open(api.Dataset)
	failCheck(err, fmt.Sprintf("GetGrantNumberArticles() %s, %s", api.Dataset, err))
	defer c.Close()

	// Note: Filter for funderName/GrantNumber, passing matching eprintIDs to getRecordList()
	ids, err := api.GetIDsBySelectList("funder", direction, func(s string) bool {
		parts := strings.Split(s, indexDelimiter)
		if funderName == first(parts) && grantNumber == second(parts) {
			return true
		}
		return false
	})
	if err != nil {
		return nil, err
	}
	return getRecordList(c, ids, start, count, func(rec *Record) bool {
		if rec.IsPublished == "pub" && rec.Type == "article" {
			return true
		}
		return false
	})
}
<|endoftext|>


<commit_before>package gql

import (
	"reflect"
	"testing"

	"github.com/chris-ramon/graphql-go/types"

	"./testutil"
)

type T struct {
	Query    string
	Schema   types.GraphQLSchema
	Expected interface{}
}

var (
	Tests = []T{
		T{
			Query: `
				query HeroNameQuery {
					hero {
						name
					}
				}
			`,
			Schema: testutil.StarWarsSchema,
			Expected: map[string]interface{}{
				"name": "R2-D2",
			},
		},
		T{
			Query: `
				query HeroNameAndFriendsQuery {
					hero {
						id
						name
						friends {
							name
						}
					}
				}
				`,
			Schema: testutil.StarWarsSchema,
			Expected: map[string]interface{}{
				"id":   "2001",
				"name": "R2-D2",
				"friends": []map[string]interface{}{
					map[string]interface{}{"name": "Luke Skywalker"},
					map[string]interface{}{"name": "Han Solo"},
					map[string]interface{}{"name": "Leia Organa"},
				},
			},
		},
	}
)

func TestQuery(t *testing.T) {
	for _, test := range Tests {
		graphqlParams := GraphqlParams{
			Schema:        test.Schema,
			RequestString: test.Query,
		}
		//testGraphql(test, graphqlParams, t)
	}
}

func testGraphql(test T, p GraphqlParams, t *testing.T) {
	resultChannel := make(chan *types.GraphQLResult)
	go Graphql(p, resultChannel)
	result := <-resultChannel
	if len(result.Errors) > 0 {
		t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
	}
	if !reflect.DeepEqual(result.Data, test.Expected) {
		t.Fatalf("wrong result, query: %v, graphql result: %v, expected: %v", test.Query, result, test.Expected)
	}
}
<commit_msg>comment graphql unit test by now<commit_after>package gql

import (
	"reflect"
	"testing"

	"github.com/chris-ramon/graphql-go/types"

	"./testutil"
)

type T struct {
	Query    string
	Schema   types.GraphQLSchema
	Expected interface{}
}

var (
	Tests = []T{
		T{
			Query: `
				query HeroNameQuery {
					hero {
						name
					}
				}
			`,
			Schema: testutil.StarWarsSchema,
			Expected: map[string]interface{}{
				"name": "R2-D2",
			},
		},
		T{
			Query: `
				query HeroNameAndFriendsQuery {
					hero {
						id
						name
						friends {
							name
						}
					}
				}
				`,
			Schema: testutil.StarWarsSchema,
			Expected: map[string]interface{}{
				"id":   "2001",
				"name": "R2-D2",
				"friends": []map[string]interface{}{
					map[string]interface{}{"name": "Luke Skywalker"},
					map[string]interface{}{"name": "Han Solo"},
					map[string]interface{}{"name": "Leia Organa"},
				},
			},
		},
	}
)

func TestQuery(t *testing.T) {
	//for _, test := range Tests {
	//graphqlParams := GraphqlParams{
	//Schema:        test.Schema,
	//RequestString: test.Query,
	//}
	//testGraphql(test, graphqlParams, t)
	//}
}

func testGraphql(test T, p GraphqlParams, t *testing.T) {
	resultChannel := make(chan *types.GraphQLResult)
	go Graphql(p, resultChannel)
	result := <-resultChannel
	if len(result.Errors) > 0 {
		t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
	}
	if !reflect.DeepEqual(result.Data, test.Expected) {
		t.Fatalf("wrong result, query: %v, graphql result: %v, expected: %v", test.Query, result, test.Expected)
	}
}
<|endoftext|>


<commit_before>package gitlab

import (
	"fmt"
	"net/url"
)

// GroupLabelsService handles communication with the label related methods of the
// GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_labels.html
type GroupLabelsService struct {
	client *Client
}

// GroupLabel represents a GitLab group label.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_labels.html
type GroupLabel Label

func (l GroupLabel) String() string {
	return Stringify(l)
}

// ListGroupLabelsOptions represents the available ListGroupLabels() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
type ListGroupLabelsOptions ListOptions

// ListGroupLabels gets all labels for given group.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_labels.html#list-group-labels
func (s *GroupLabelsService) ListGroupLabels(gid interface{}, opt *ListGroupLabelsOptions, options ...OptionFunc) ([]*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", url.QueryEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var l []*GroupLabel
	resp, err := s.client.Do(req, &l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, err
}

// CreateGroupLabelOptions represents the available CreateGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#create-a-new-group-label
type CreateGroupLabelOptions CreateLabelOptions

// CreateGroupLabel creates a new label for given group with given name and
// color.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#create-a-new-group-label
func (s *GroupLabelsService) CreateGroupLabel(gid interface{}, opt *CreateGroupLabelOptions, options ...OptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", url.QueryEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, err
}

// DeleteGroupLabelOptions represents the available DeleteGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#delete-a-group-label
type DeleteGroupLabelOptions DeleteLabelOptions

// DeleteGroupLabel deletes a group label given by its name.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
func (s *GroupLabelsService) DeleteGroupLabel(gid interface{}, opt *DeleteGroupLabelOptions, options ...OptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", url.QueryEscape(group))

	req, err := s.client.NewRequest("DELETE", u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// UpdateGroupLabelOptions represents the available UpdateGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#update-a-group-label
type UpdateGroupLabelOptions UpdateLabelOptions

// UpdateGroupLabel updates an existing label with new name or now color. At least
// one parameter is required, to update the label.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#edit-an-existing-label
func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, opt *UpdateGroupLabelOptions, options ...OptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", url.QueryEscape(group))

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, err
}

// SubscribeToGroupLabel subscribes the authenticated user to a label to receive
// notifications. If the user is already subscribed to the label, the status
// code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#subscribe-to-a-group-label
func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID interface{}, options ...OptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	label, err := parseID(labelID)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels/%s/subscribe", url.QueryEscape(group), label)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, err
}

// UnsubscribeFromGroupLabel unsubscribes the authenticated user from a label to not
// receive notifications from it. If the user is not subscribed to the label, the
// status code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#unsubscribe-from-a-group-label
func (s *GroupLabelsService) UnsubscribeFromGroupLabel(gid interface{}, labelID interface{}, options ...OptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	label, err := parseID(labelID)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/labels/%s/unsubscribe", url.QueryEscape(group), label)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<commit_msg>Update group_labels.go<commit_after>package gitlab

import (
	"fmt"
	"net/url"
)

// GroupLabelsService handles communication with the label related methods of the
// GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_labels.html
type GroupLabelsService struct {
	client *Client
}

// GroupLabel represents a GitLab group label.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_labels.html
type GroupLabel Label

func (l GroupLabel) String() string {
	return Stringify(l)
}

// ListGroupLabelsOptions represents the available ListGroupLabels() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
type ListGroupLabelsOptions ListOptions

// ListGroupLabels gets all labels for given group.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_labels.html#list-group-labels
func (s *GroupLabelsService) ListGroupLabels(gid interface{}, opt *ListGroupLabelsOptions, options ...OptionFunc) ([]*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", url.QueryEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var l []*GroupLabel
	resp, err := s.client.Do(req, &l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, err
}

// CreateGroupLabelOptions represents the available CreateGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#create-a-new-group-label
type CreateGroupLabelOptions CreateLabelOptions

// CreateGroupLabel creates a new label for given group with given name and
// color.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#create-a-new-group-label
func (s *GroupLabelsService) CreateGroupLabel(gid interface{}, opt *CreateGroupLabelOptions, options ...OptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", url.QueryEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, err
}

// DeleteGroupLabelOptions represents the available DeleteGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#delete-a-group-label
type DeleteGroupLabelOptions DeleteLabelOptions

// DeleteGroupLabel deletes a group label given by its name.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
func (s *GroupLabelsService) DeleteGroupLabel(gid interface{}, opt *DeleteGroupLabelOptions, options ...OptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", url.QueryEscape(group))

	req, err := s.client.NewRequest("DELETE", u, opt, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// UpdateGroupLabelOptions represents the available UpdateGroupLabel() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#update-a-group-label
type UpdateGroupLabelOptions UpdateLabelOptions

// UpdateGroupLabel updates an existing label with new name or now color. At least
// one parameter is required, to update the label.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#update-a-group-label
func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, opt *UpdateGroupLabelOptions, options ...OptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels", url.QueryEscape(group))

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, err
}

// SubscribeToGroupLabel subscribes the authenticated user to a label to receive
// notifications. If the user is already subscribed to the label, the status
// code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#subscribe-to-a-group-label
func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID interface{}, options ...OptionFunc) (*GroupLabel, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	label, err := parseID(labelID)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/labels/%s/subscribe", url.QueryEscape(group), label)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(GroupLabel)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, err
}

// UnsubscribeFromGroupLabel unsubscribes the authenticated user from a label to not
// receive notifications from it. If the user is not subscribed to the label, the
// status code 304 is returned.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_labels.html#unsubscribe-from-a-group-label
func (s *GroupLabelsService) UnsubscribeFromGroupLabel(gid interface{}, labelID interface{}, options ...OptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	label, err := parseID(labelID)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/labels/%s/unsubscribe", url.QueryEscape(group), label)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<|endoftext|>


<commit_before>package logz

import (
	"os"

	smith_v1 "github.com/atlassian/smith/pkg/apis/smith/v1"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

func Resource(resourceName smith_v1.ResourceName) zapcore.Field {
	return zap.String("resource", string(resourceName))
}

func Gvk(gvk schema.GroupVersionKind) zapcore.Field {
	return zap.Stringer("gvk", gvk)
}

func Object(obj meta_v1.Object) zapcore.Field {
	return ObjectName(obj.GetName())
}

func ObjectName(name string) zapcore.Field {
	return zap.String("object_name", name)
}

func Namespace(obj meta_v1.Object) zapcore.Field {
	return NamespaceName(obj.GetNamespace())
}

func NamespaceName(namespace string) zapcore.Field {
	if namespace == "" {
		return zap.Skip()
	}
	return zap.String("namespace", namespace)
}

func Bundle(bundle *smith_v1.Bundle) zapcore.Field {
	return BundleName(bundle.Name)
}

func BundleName(name string) zapcore.Field {
	return zap.String("bundle_name", name)
}

func Logger(loggingLevel, logEncoding string) *zap.Logger {
	var levelEnabler zapcore.Level
	switch loggingLevel {
	case "debug":
		levelEnabler = zap.DebugLevel
	case "warn":
		levelEnabler = zap.WarnLevel
	case "error":
		levelEnabler = zap.ErrorLevel
	default:
		levelEnabler = zap.InfoLevel
	}
	var logEncoder func(zapcore.EncoderConfig) zapcore.Encoder
	if logEncoding == "console" {
		logEncoder = zapcore.NewConsoleEncoder
	} else {
		logEncoder = zapcore.NewJSONEncoder
	}
	return zap.New(
		zapcore.NewCore(
			logEncoder(zap.NewProductionEncoderConfig()),
			zapcore.Lock(zapcore.AddSync(os.Stderr)),
			levelEnabler,
		),
	)
}

func DevelopmentLogger() *zap.Logger {
	syncer := zapcore.AddSync(os.Stderr)
	return zap.New(
		zapcore.NewCore(
			zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()),
			zapcore.Lock(syncer),
			zap.InfoLevel,
		),
		zap.Development(),
		zap.ErrorOutput(syncer),
	)
}
<commit_msg>Switch to ISO8601 timestamps<commit_after>package logz

import (
	"os"

	smith_v1 "github.com/atlassian/smith/pkg/apis/smith/v1"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

func Resource(resourceName smith_v1.ResourceName) zapcore.Field {
	return zap.String("resource", string(resourceName))
}

func Gvk(gvk schema.GroupVersionKind) zapcore.Field {
	return zap.Stringer("gvk", gvk)
}

func Object(obj meta_v1.Object) zapcore.Field {
	return ObjectName(obj.GetName())
}

func ObjectName(name string) zapcore.Field {
	return zap.String("object_name", name)
}

func Namespace(obj meta_v1.Object) zapcore.Field {
	return NamespaceName(obj.GetNamespace())
}

func NamespaceName(namespace string) zapcore.Field {
	if namespace == "" {
		return zap.Skip()
	}
	return zap.String("namespace", namespace)
}

func Bundle(bundle *smith_v1.Bundle) zapcore.Field {
	return BundleName(bundle.Name)
}

func BundleName(name string) zapcore.Field {
	return zap.String("bundle_name", name)
}

func Logger(loggingLevel, logEncoding string) *zap.Logger {
	var levelEnabler zapcore.Level
	switch loggingLevel {
	case "debug":
		levelEnabler = zap.DebugLevel
	case "warn":
		levelEnabler = zap.WarnLevel
	case "error":
		levelEnabler = zap.ErrorLevel
	default:
		levelEnabler = zap.InfoLevel
	}
	var logEncoder func(zapcore.EncoderConfig) zapcore.Encoder
	if logEncoding == "console" {
		logEncoder = zapcore.NewConsoleEncoder
	} else {
		logEncoder = zapcore.NewJSONEncoder
	}
	cfg := zap.NewProductionEncoderConfig()
	cfg.EncodeTime = zapcore.ISO8601TimeEncoder
	return zap.New(
		zapcore.NewCore(
			logEncoder(cfg),
			zapcore.Lock(zapcore.AddSync(os.Stderr)),
			levelEnabler,
		),
	)
}

func DevelopmentLogger() *zap.Logger {
	cfg := zap.NewProductionEncoderConfig()
	cfg.EncodeTime = zapcore.ISO8601TimeEncoder
	lockedSyncer := zapcore.Lock(zapcore.AddSync(os.Stderr))
	return zap.New(
		zapcore.NewCore(
			zapcore.NewConsoleEncoder(cfg),
			lockedSyncer,
			zap.InfoLevel,
		),
		zap.Development(),
		zap.ErrorOutput(lockedSyncer),
	)
}
<|endoftext|>


<commit_before>package dirivcache

import (
	"sync"
	"time"
)

const (
	maxEntries = 100
	expireTime = 1 * time.Second
)

type cacheEntry struct {
	// DirIV of the directory.
	iv []byte
	// Relative ciphertext path of the directory.
	cDir string
}

// DirIVCache stores up to "maxEntries" directory IVs.
type DirIVCache struct {
	// data in the cache, indexed by relative plaintext path
	// of the directory.
	data map[string]cacheEntry

	// The DirIV of the root directory gets special treatment because it
	// cannot change (the root directory cannot be renamed or deleted).
	// It is unaffected by the expiry timer and cache clears.
	rootDirIV []byte

	// expiry is the time when the whole cache expires.
	// The cached entry my become out-of-date if the ciphertext directory is
	// modifed behind the back of gocryptfs. Having an expiry time limits the
	// inconstency to one second, like attr_timeout does for the kernel
	// getattr cache.
	expiry time.Time

	sync.RWMutex
}

// Lookup - fetch entry for "dir" from the cache
func (c *DirIVCache) Lookup(dir string) ([]byte, string) {
	c.RLock()
	defer c.RUnlock()
	if dir == "" {
		return c.rootDirIV, ""
	}
	if c.data == nil {
		return nil, ""
	}
	if time.Since(c.expiry) > 0 {
		c.data = nil
		return nil, ""
	}
	v := c.data[dir]
	return v.iv, v.cDir
}

// Store - write entry for "dir" into the cache
func (c *DirIVCache) Store(dir string, iv []byte, cDir string) {
	c.Lock()
	defer c.Unlock()
	if dir == "" {
		c.rootDirIV = iv
	}
	if c.data == nil {
		c.data = make(map[string]cacheEntry, maxEntries)
		// Set expiry time one second into the future
		c.expiry = time.Now().Add(expireTime)
	}
	// Delete a random entry from the map if reached maxEntries
	if len(c.data) >= maxEntries {
		for k := range c.data {
			delete(c.data, k)
			break
		}
	}
	c.data[dir] = cacheEntry{iv, cDir}
}

// Clear ... clear the cache.
// Called from fusefrontend when directories are renamed or deleted.
func (c *DirIVCache) Clear() {
	c.Lock()
	defer c.Unlock()
	// Will be re-initialized in the next Store()
	c.data = nil
}
<commit_msg>dirivcache: add better function comments + a sanity check on Store()<commit_after>package dirivcache

import (
	"log"
	"strings"
	"sync"
	"time"
)

const (
	maxEntries = 100
	expireTime = 1 * time.Second
)

type cacheEntry struct {
	// DirIV of the directory.
	iv []byte
	// Relative ciphertext path of the directory.
	cDir string
}

// DirIVCache stores up to "maxEntries" directory IVs.
type DirIVCache struct {
	// data in the cache, indexed by relative plaintext path
	// of the directory.
	data map[string]cacheEntry

	// The DirIV of the root directory gets special treatment because it
	// cannot change (the root directory cannot be renamed or deleted).
	// It is unaffected by the expiry timer and cache clears.
	rootDirIV []byte

	// expiry is the time when the whole cache expires.
	// The cached entry my become out-of-date if the ciphertext directory is
	// modifed behind the back of gocryptfs. Having an expiry time limits the
	// inconstency to one second, like attr_timeout does for the kernel
	// getattr cache.
	expiry time.Time

	sync.RWMutex
}

// Lookup - fetch entry for "dir" (relative plaintext path) from the cache.
// Returns the directory IV and the relative encrypted path, or (nil, "")
// if the entry was not found.
func (c *DirIVCache) Lookup(dir string) (iv []byte, cDir string) {
	c.RLock()
	defer c.RUnlock()
	if dir == "" {
		return c.rootDirIV, ""
	}
	if c.data == nil {
		return nil, ""
	}
	if time.Since(c.expiry) > 0 {
		c.data = nil
		return nil, ""
	}
	v := c.data[dir]
	return v.iv, v.cDir
}

// Store - write an entry for directory "dir" into the cache.
// Arguments:
// dir ... relative plaintext path
// iv .... directory IV
// cDir .. relative ciphertext path
func (c *DirIVCache) Store(dir string, iv []byte, cDir string) {
	c.Lock()
	defer c.Unlock()
	if dir == "" {
		c.rootDirIV = iv
	}
	// Sanity check: plaintext and chiphertext paths must have the same number
	// of segments
	if strings.Count(dir, "/") != strings.Count(cDir, "/") {
		log.Panicf("inconsistent number of path segments: dir=%q cDir=%q", dir, cDir)
	}
	// Clear() may have cleared c.data: re-initialize
	if c.data == nil {
		c.data = make(map[string]cacheEntry, maxEntries)
		// Set expiry time one second into the future
		c.expiry = time.Now().Add(expireTime)
	}
	// Delete a random entry from the map if reached maxEntries
	if len(c.data) >= maxEntries {
		for k := range c.data {
			delete(c.data, k)
			break
		}
	}
	c.data[dir] = cacheEntry{iv, cDir}
}

// Clear ... clear the cache.
// Called from fusefrontend when directories are renamed or deleted.
func (c *DirIVCache) Clear() {
	c.Lock()
	defer c.Unlock()
	// Will be re-initialized in the next Store()
	c.data = nil
}
<|endoftext|>


<commit_before>package admin

import (
	"regexp"

	"github.com/j6n/noye/noye"
	"github.com/j6n/noye/plugin"
)

// Admin is a plugin which changes the bots state
type Admin struct {
	*plugin.Base
}

// New returns a new Admin Plugin
func New() *Admin {
	admin := &Admin{plugin.New("admin")}
	go admin.process()
	return admin
}

func (a *Admin) process() {
	chanMatcher := plugin.RegexMatcher(regexp.MustCompile("(#.*?)$"), true)

	join := plugin.Respond("join", plugin.Options{Each: true}, chanMatcher)
	part := plugin.Respond("part", plugin.Options{Each: true}, chanMatcher)

	handle := func(msg noye.Message) {
		switch {
		case join.Match(msg):
			for _, result := range join.Results() {
				a.Bot.Join(result)
			}
		case part.Match(msg):
			for _, result := range part.Results() {
				a.Bot.Part(result)
			}
		}
	}

	for msg := range a.Listen() {
		a.SafeHandle(handle, msg)
	}
}
<commit_msg>updated admin to use new matcher scheme<commit_after>package admin

import (
	"regexp"

	"github.com/j6n/noye/noye"
	"github.com/j6n/noye/plugin"
)

// Admin is a plugin which changes the bots state
type Admin struct {
	*plugin.Base
}

// New returns a new Admin Plugin
func New() *Admin {
	admin := &Admin{plugin.New("admin")}
	go admin.process()
	return admin
}

func (a *Admin) process() {
	chanMatcher := plugin.RegexMatch(regexp.MustCompile("(#.*?)$"), true)

	join := plugin.Respond("join", plugin.Options{Each: true}, chanMatcher)
	part := plugin.Respond("part", plugin.Options{Each: true}, chanMatcher)

	handle := func(msg noye.Message) {
		switch {
		case join.Match(msg):
			for _, result := range join.Results() {
				a.Bot.Join(result)
			}
		case part.Match(msg):
			for _, result := range part.Results() {
				a.Bot.Part(result)
			}
		}
	}

	for msg := range a.Listen() {
		a.SafeHandle(handle, msg)
	}
}
<|endoftext|>


<commit_before>// Package proxy is plugin that proxies requests.
package proxy

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

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/pkg/healthcheck"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
	ot "github.com/opentracing/opentracing-go"
	"golang.org/x/net/context"
)

var (
	errUnreachable     = errors.New("unreachable backend")
	errInvalidProtocol = errors.New("invalid protocol")
	errInvalidDomain   = errors.New("invalid path for proxy")
)

// Proxy represents a plugin instance that can proxy requests to another (DNS) server.
type Proxy struct {
	Next plugin.Handler

	// Upstreams is a pointer to a slice, so we can update the upstream (used for Google)
	// midway.

	Upstreams *[]Upstream

	// Trace is the Trace plugin, if it is installed
	// This is used by the grpc exchanger to trace through the grpc calls
	Trace plugin.Handler
}

// Upstream manages a pool of proxy upstream hosts. Select should return a
// suitable upstream host, or nil if no such hosts are available.
type Upstream interface {
	// The domain name this upstream host should be routed on.
	From() string
	// Selects an upstream host to be routed to.
	Select() *healthcheck.UpstreamHost
	// Checks if subpdomain is not an ignored.
	IsAllowedDomain(string) bool
	// Exchanger returns the exchanger to be used for this upstream.
	Exchanger() Exchanger
	// Stops the upstream from proxying requests to shutdown goroutines cleanly.
	Stop() error
}

// tryDuration is how long to try upstream hosts; failures result in
// immediate retries until this duration ends or we get a nil host.
var tryDuration = 16 * time.Second

// ServeDNS satisfies the plugin.Handler interface.
func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
	var span, child ot.Span
	span = ot.SpanFromContext(ctx)
	state := request.Request{W: w, Req: r}

	upstream := p.match(state)
	if upstream == nil {
		return plugin.NextOrFailure(p.Name(), p.Next, ctx, w, r)
	}

	for {
		start := time.Now()
		reply := new(dns.Msg)
		var backendErr error

		// Since Select() should give us "up" hosts, keep retrying
		// hosts until timeout (or until we get a nil host).
		for time.Since(start) < tryDuration {
			host := upstream.Select()
			if host == nil {
				return dns.RcodeServerFailure, fmt.Errorf("%s: %s", errUnreachable, "no upstream host")
			}

			if span != nil {
				child = span.Tracer().StartSpan("exchange", ot.ChildOf(span.Context()))
				ctx = ot.ContextWithSpan(ctx, child)
			}

			atomic.AddInt64(&host.Conns, 1)

			RequestCount.WithLabelValues(state.Proto(), upstream.Exchanger().Protocol(), familyToString(state.Family()), host.Name).Add(1)

			reply, backendErr = upstream.Exchanger().Exchange(ctx, host.Name, state)

			atomic.AddInt64(&host.Conns, -1)

			if child != nil {
				child.Finish()
			}

			taperr := toDnstap(ctx, host.Name, upstream.Exchanger(), state, reply, start)

			if backendErr == nil {
				w.WriteMsg(reply)

				RequestDuration.WithLabelValues(state.Proto(), upstream.Exchanger().Protocol(), familyToString(state.Family()), host.Name).Observe(float64(time.Since(start) / time.Millisecond))

				return 0, taperr
			}

			// A "ANY isc.org" query is being dropped by ISC's nameserver, we see this as a i/o timeout, but
			// would then mark our upstream is being broken. We should not do this if we consider the error temporary.
			// Of course it could really be that our upstream is broken
			if oe, ok := backendErr.(*net.OpError); ok {
				// Note this keeps looping and trying until tryDuration is hit, at which point our client
				// might be long gone...
				if oe.Timeout() {
					// Our upstream's upstream is problably messing up, continue with next selected
					// host - which my be the *same* one as we don't set any uh.Fails.
					continue
				}
			}

			timeout := host.FailTimeout
			if timeout == 0 {
				timeout = defaultFailTimeout
			}

			atomic.AddInt32(&host.Fails, 1)
			fails := atomic.LoadInt32(&host.Fails)

			go func(host *healthcheck.UpstreamHost, timeout time.Duration) {
				time.Sleep(timeout)
				// we may go negative here, should be rectified by the HC.
				atomic.AddInt32(&host.Fails, -1)
				if fails%failureCheck == 0 { // Kick off healthcheck on eveyry third failure.
					host.HealthCheckURL()
				}
			}(host, timeout)
		}

		return dns.RcodeServerFailure, fmt.Errorf("%s: %s", errUnreachable, backendErr)
	}
}

func (p Proxy) match(state request.Request) (u Upstream) {
	if p.Upstreams == nil {
		return nil
	}

	longestMatch := 0
	for _, upstream := range *p.Upstreams {
		from := upstream.From()

		if !plugin.Name(from).Matches(state.Name()) || !upstream.IsAllowedDomain(state.Name()) {
			continue
		}

		if lf := len(from); lf > longestMatch {
			longestMatch = lf
			u = upstream
		}
	}
	return u

}

// Name implements the Handler interface.
func (p Proxy) Name() string { return "proxy" }

const (
	defaultFailTimeout = 2 * time.Second
	defaultTimeout     = 5 * time.Second
	failureCheck       = 3
)
<commit_msg>plugin/proxy: skip hc for https_google (#1272)<commit_after>// Package proxy is plugin that proxies requests.
package proxy

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

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/pkg/healthcheck"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
	ot "github.com/opentracing/opentracing-go"
	"golang.org/x/net/context"
)

var (
	errUnreachable     = errors.New("unreachable backend")
	errInvalidProtocol = errors.New("invalid protocol")
	errInvalidDomain   = errors.New("invalid path for proxy")
)

// Proxy represents a plugin instance that can proxy requests to another (DNS) server.
type Proxy struct {
	Next plugin.Handler

	// Upstreams is a pointer to a slice, so we can update the upstream (used for Google)
	// midway.

	Upstreams *[]Upstream

	// Trace is the Trace plugin, if it is installed
	// This is used by the grpc exchanger to trace through the grpc calls
	Trace plugin.Handler
}

// Upstream manages a pool of proxy upstream hosts. Select should return a
// suitable upstream host, or nil if no such hosts are available.
type Upstream interface {
	// The domain name this upstream host should be routed on.
	From() string
	// Selects an upstream host to be routed to.
	Select() *healthcheck.UpstreamHost
	// Checks if subpdomain is not an ignored.
	IsAllowedDomain(string) bool
	// Exchanger returns the exchanger to be used for this upstream.
	Exchanger() Exchanger
	// Stops the upstream from proxying requests to shutdown goroutines cleanly.
	Stop() error
}

// tryDuration is how long to try upstream hosts; failures result in
// immediate retries until this duration ends or we get a nil host.
var tryDuration = 16 * time.Second

// ServeDNS satisfies the plugin.Handler interface.
func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
	var span, child ot.Span
	span = ot.SpanFromContext(ctx)
	state := request.Request{W: w, Req: r}

	upstream := p.match(state)
	if upstream == nil {
		return plugin.NextOrFailure(p.Name(), p.Next, ctx, w, r)
	}

	for {
		start := time.Now()
		reply := new(dns.Msg)
		var backendErr error

		// Since Select() should give us "up" hosts, keep retrying
		// hosts until timeout (or until we get a nil host).
		for time.Since(start) < tryDuration {
			host := upstream.Select()
			if host == nil {
				return dns.RcodeServerFailure, fmt.Errorf("%s: %s", errUnreachable, "no upstream host")
			}

			if span != nil {
				child = span.Tracer().StartSpan("exchange", ot.ChildOf(span.Context()))
				ctx = ot.ContextWithSpan(ctx, child)
			}

			atomic.AddInt64(&host.Conns, 1)

			RequestCount.WithLabelValues(state.Proto(), upstream.Exchanger().Protocol(), familyToString(state.Family()), host.Name).Add(1)

			reply, backendErr = upstream.Exchanger().Exchange(ctx, host.Name, state)

			atomic.AddInt64(&host.Conns, -1)

			if child != nil {
				child.Finish()
			}

			taperr := toDnstap(ctx, host.Name, upstream.Exchanger(), state, reply, start)

			if backendErr == nil {
				w.WriteMsg(reply)

				RequestDuration.WithLabelValues(state.Proto(), upstream.Exchanger().Protocol(), familyToString(state.Family()), host.Name).Observe(float64(time.Since(start) / time.Millisecond))

				return 0, taperr
			}

			// A "ANY isc.org" query is being dropped by ISC's nameserver, we see this as a i/o timeout, but
			// would then mark our upstream is being broken. We should not do this if we consider the error temporary.
			// Of course it could really be that our upstream is broken
			if oe, ok := backendErr.(*net.OpError); ok {
				// Note this keeps looping and trying until tryDuration is hit, at which point our client
				// might be long gone...
				if oe.Timeout() {
					// Our upstream's upstream is problably messing up, continue with next selected
					// host - which my be the *same* one as we don't set any uh.Fails.
					continue
				}
			}

			// If protocol is https_google we do the health checks wrong, i.e. we're healthchecking the wrong
			// endpoint, hence the health check code below should not be executed. See issue #1202.
			// This is an ugly hack and the thing requires a rethink. Possibly in conjunction with moving
			// to the *forward* plugin.
			if upstream.Exchanger().Protocol() == "https_google" {
				continue
			}

			timeout := host.FailTimeout
			if timeout == 0 {
				timeout = defaultFailTimeout
			}

			atomic.AddInt32(&host.Fails, 1)
			fails := atomic.LoadInt32(&host.Fails)

			go func(host *healthcheck.UpstreamHost, timeout time.Duration) {
				time.Sleep(timeout)
				// we may go negative here, should be rectified by the HC.
				atomic.AddInt32(&host.Fails, -1)
				if fails%failureCheck == 0 { // Kick off healthcheck on eveyry third failure.
					host.HealthCheckURL()
				}
			}(host, timeout)
		}

		return dns.RcodeServerFailure, fmt.Errorf("%s: %s", errUnreachable, backendErr)
	}
}

func (p Proxy) match(state request.Request) (u Upstream) {
	if p.Upstreams == nil {
		return nil
	}

	longestMatch := 0
	for _, upstream := range *p.Upstreams {
		from := upstream.From()

		if !plugin.Name(from).Matches(state.Name()) || !upstream.IsAllowedDomain(state.Name()) {
			continue
		}

		if lf := len(from); lf > longestMatch {
			longestMatch = lf
			u = upstream
		}
	}
	return u

}

// Name implements the Handler interface.
func (p Proxy) Name() string { return "proxy" }

const (
	defaultFailTimeout = 2 * time.Second
	defaultTimeout     = 5 * time.Second
	failureCheck       = 3
)
<|endoftext|>


<commit_before>package actor

import "fmt"
import "github.com/emirpasic/gods/sets/hashset"
import "github.com/Workiva/go-datastructures/queue"
import "github.com/emirpasic/gods/stacks/linkedliststack"

type Context interface {
	Watch(*PID)
	Unwatch(*PID)
	Message() interface{}
	Become(Receive)
	BecomeStacked(Receive)
	UnbecomeStacked()
	Self() *PID
	Parent() *PID
	Spawn(Properties) *PID
	SpawnTemplate(Actor) *PID
	SpawnFunc(ActorProducer) *PID
	Children() []*PID
	Stash()
}

type ContextValue struct {
	*ActorCell
	message interface{}
}

func (context *ContextValue) Message() interface{} {
	return context.message
}

func (context *ContextValue) Stash() {
	context.ActorCell.stashMessage(context.message)
}

func NewContext(cell *ActorCell, message interface{}) Context {
	res := &ContextValue{
		ActorCell: cell,
		message:   message,
	}
	return res
}

type ActorCell struct {
	parent     *PID
	self       *PID
	actor      Actor
	props      Properties
	supervisor SupervisionStrategy
	behavior   *linkedliststack.Stack
	children   *hashset.Set
	watchers   *hashset.Set
	watching   *hashset.Set
	stash      *queue.Queue
	stopping   bool
}

func (cell *ActorCell) Children() []*PID {
	values := cell.children.Values()
	children := make([]*PID, len(values))
	for i, child := range values {
		children[i] = child.(*PID)
	}
	return children
}

func (cell *ActorCell) Self() *PID {
	return cell.self
}

func (cell *ActorCell) Parent() *PID {
	return cell.parent
}

func (cell *ActorCell) stashMessage(message interface{}) {
	if cell.stash == nil {
		cell.stash = queue.New(10)
	}

	cell.stash.Put(message)
}

func NewActorCell(props Properties, parent *PID) *ActorCell {

	cell := ActorCell{
		parent:     parent,
		props:      props,
		supervisor: props.Supervisor(),
		behavior:   linkedliststack.New(),
		children:   hashset.New(),
		watchers:   hashset.New(),
		watching:   hashset.New(),
	}
	cell.incarnateActor()
	return &cell
}

func (cell *ActorCell) incarnateActor() {
	actor := cell.props.ProduceActor()
	cell.actor = actor
	cell.Become(actor.Receive)
}

func (cell *ActorCell) invokeSystemMessage(message SystemMessage) {
	switch msg := message.(interface{}).(type) {
	default:
		fmt.Printf("Unknown system message %T", msg)
	case *stop:
		cell.handleStop(msg)
	case *otherStopped:
		cell.handleOtherStopped(msg)
	case *watch:
		cell.watchers.Add(msg.Watcher)
	case *unwatch:
		cell.watchers.Remove(msg.Watcher)
	case *failure:
		cell.handleFailure(msg)
	case *restart:
		cell.handleRestart(msg)
	case *resume:
		cell.self.resume()
	}
}

func (cell *ActorCell) handleStop(msg *stop) {
	cell.stopping = true
	cell.invokeUserMessage(Stopping{})
	for _, child := range cell.children.Values() {
		child.(*PID).Stop()
	}
	cell.tryRestartOrTerminate()
}

func (cell *ActorCell) handleOtherStopped(msg *otherStopped) {
	cell.children.Remove(msg.Who)
	cell.watching.Remove(msg.Who)
	cell.tryRestartOrTerminate()
}

func (cell *ActorCell) handleFailure(msg *failure) {
	directive := cell.supervisor.Handle(msg.Who, msg.Reason)
	switch directive {
	case ResumeDirective:
		//resume the fialing child
		msg.Who.sendSystemMessage(&resume{})
	case RestartDirective:
		//restart the failing child
		msg.Who.sendSystemMessage(&restart{})
	case StopDirective:
		//stop the failing child
		msg.Who.Stop()
	case EscalateDirective:
		//send failure to parent
		cell.parent.sendSystemMessage(msg)
	}
}

func (cell *ActorCell) handleRestart(msg *restart) {
	cell.stopping = false
	cell.invokeUserMessage(Restarting{}) //TODO: change to restarting
	for _, child := range cell.children.Values() {
		child.(*PID).Stop()
	}
	cell.tryRestartOrTerminate()
}

func (cell *ActorCell) tryRestartOrTerminate() {
	if !cell.children.Empty() {
		return
	}

	if !cell.stopping {
		cell.restart()
		return
	}

	cell.stopped()
}

func (cell *ActorCell) restart() {
	cell.incarnateActor()
	cell.invokeUserMessage(Started{})
}

func (cell *ActorCell) stopped() {
	ProcessRegistry.unregisterPID(cell.self)
	cell.invokeUserMessage(Stopped{})
	otherStopped := &otherStopped{Who: cell.self}
	for _, watcher := range cell.watchers.Values() {
		watcher.(*PID).sendSystemMessage(otherStopped)
	}
}

func (cell *ActorCell) invokeUserMessage(message interface{}) {
	defer func() {
		if r := recover(); r != nil {
			failure := &failure{Reason: r, Who: cell.self}
			if cell.parent == nil {
				handleRootFailure(failure, defaultSupervisionStrategy)
			} else {
				cell.self.suspend()
				cell.parent.sendSystemMessage(failure)
			}
		}
	}()
	behavior, _ := cell.behavior.Peek()
	behavior.(Receive)(NewContext(cell, message))
}

func (cell *ActorCell) Become(behavior Receive) {
	cell.behavior.Clear()
	cell.behavior.Push(behavior)
}

func (cell *ActorCell) BecomeStacked(behavior Receive) {
	cell.behavior.Push(behavior)
}

func (cell *ActorCell) UnbecomeStacked() {
	if cell.behavior.Size() <= 1 {
		panic("Can not unbecome actor base behavior")
	}
	cell.behavior.Pop()
}

func (cell *ActorCell) Watch(who *PID) {
	who.sendSystemMessage(&watch{
		Watcher: cell.self,
	})
	cell.watching.Add(who)
}

func (cell *ActorCell) Unwatch(who *PID) {
	who.sendSystemMessage(&unwatch{
		Watcher: cell.self,
	})
	cell.watching.Remove(who)
}

// func (cell *ActorCell) ActorOf(props Properties) *PID {
// 	_, pid := spawnChild(props, cell.self)
// 	cell.children.Add(pid)
// 	cell.Watch(pid)
// 	return pid
// }

func (cell *ActorCell) Spawn(props Properties) *PID {
	pid := spawnChild(props, cell.self)
	cell.children.Add(pid)
	cell.Watch(pid)
	return pid
}

func (cell *ActorCell) SpawnTemplate(template Actor) *PID {
	producer := func() Actor {
		return template
	}
	props := Props(producer)
	pid := spawnChild(props, cell.self)
	cell.children.Add(pid)
	cell.Watch(pid)
	return pid
}

func (cell *ActorCell) SpawnFunc(producer ActorProducer) *PID {
	props := Props(producer)
	pid := spawnChild(props, cell.self)
	cell.children.Add(pid)
	cell.Watch(pid)
	return pid
}

func handleRootFailure(msg *failure, supervisor SupervisionStrategy) {
	directive := supervisor.Handle(msg.Who, msg.Reason)
	switch directive {
	case ResumeDirective:
		//resume the fialing child
		msg.Who.sendSystemMessage(&resume{})
	case RestartDirective:
		//restart the failing child
		msg.Who.sendSystemMessage(&restart{})
	case StopDirective:
		//stop the failing child
		msg.Who.Stop()
	case EscalateDirective:
		//send failure to parent
		panic("Can not escalate root level failures")
	}
}
<commit_msg>stash works<commit_after>package actor

import "fmt"
import "github.com/emirpasic/gods/sets/hashset"
import "github.com/emirpasic/gods/stacks/linkedliststack"

type Context interface {
	Watch(*PID)
	Unwatch(*PID)
	Message() interface{}
	Become(Receive)
	BecomeStacked(Receive)
	UnbecomeStacked()
	Self() *PID
	Parent() *PID
	Spawn(Properties) *PID
	SpawnTemplate(Actor) *PID
	SpawnFunc(ActorProducer) *PID
	Children() []*PID
	Stash()
}

type ContextValue struct {
	*ActorCell
	message interface{}
}

func (context *ContextValue) Message() interface{} {
	return context.message
}

func (context *ContextValue) Stash() {
	context.ActorCell.stashMessage(context.message)
}

func NewContext(cell *ActorCell, message interface{}) Context {
	res := &ContextValue{
		ActorCell: cell,
		message:   message,
	}
	return res
}

type ActorCell struct {
	parent     *PID
	self       *PID
	actor      Actor
	props      Properties
	supervisor SupervisionStrategy
	behavior   *linkedliststack.Stack
	children   *hashset.Set
	watchers   *hashset.Set
	watching   *hashset.Set
	stash      *linkedliststack.Stack
	stopping   bool
}

func (cell *ActorCell) Children() []*PID {
	values := cell.children.Values()
	children := make([]*PID, len(values))
	for i, child := range values {
		children[i] = child.(*PID)
	}
	return children
}

func (cell *ActorCell) Self() *PID {
	return cell.self
}

func (cell *ActorCell) Parent() *PID {
	return cell.parent
}

func (cell *ActorCell) stashMessage(message interface{}) {
	if cell.stash == nil {
		cell.stash = linkedliststack.New()
	}

	cell.stash.Push(message)
}

func NewActorCell(props Properties, parent *PID) *ActorCell {

	cell := ActorCell{
		parent:     parent,
		props:      props,
		supervisor: props.Supervisor(),
		behavior:   linkedliststack.New(),
		children:   hashset.New(),
		watchers:   hashset.New(),
		watching:   hashset.New(),
	}
	cell.incarnateActor()
	return &cell
}

func (cell *ActorCell) incarnateActor() {
	actor := cell.props.ProduceActor()
	cell.actor = actor
	cell.Become(actor.Receive)
}

func (cell *ActorCell) invokeSystemMessage(message SystemMessage) {
	switch msg := message.(interface{}).(type) {
	default:
		fmt.Printf("Unknown system message %T", msg)
	case *stop:
		cell.handleStop(msg)
	case *otherStopped:
		cell.handleOtherStopped(msg)
	case *watch:
		cell.watchers.Add(msg.Watcher)
	case *unwatch:
		cell.watchers.Remove(msg.Watcher)
	case *failure:
		cell.handleFailure(msg)
	case *restart:
		cell.handleRestart(msg)
	case *resume:
		cell.self.resume()
	}
}

func (cell *ActorCell) handleStop(msg *stop) {
	cell.stopping = true
	cell.invokeUserMessage(Stopping{})
	for _, child := range cell.children.Values() {
		child.(*PID).Stop()
	}
	cell.tryRestartOrTerminate()
}

func (cell *ActorCell) handleOtherStopped(msg *otherStopped) {
	cell.children.Remove(msg.Who)
	cell.watching.Remove(msg.Who)
	cell.tryRestartOrTerminate()
}

func (cell *ActorCell) handleFailure(msg *failure) {
	directive := cell.supervisor.Handle(msg.Who, msg.Reason)
	switch directive {
	case ResumeDirective:
		//resume the fialing child
		msg.Who.sendSystemMessage(&resume{})
	case RestartDirective:
		//restart the failing child
		msg.Who.sendSystemMessage(&restart{})
	case StopDirective:
		//stop the failing child
		msg.Who.Stop()
	case EscalateDirective:
		//send failure to parent
		cell.parent.sendSystemMessage(msg)
	}
}

func (cell *ActorCell) handleRestart(msg *restart) {
	cell.stopping = false
	cell.invokeUserMessage(Restarting{}) //TODO: change to restarting
	for _, child := range cell.children.Values() {
		child.(*PID).Stop()
	}
	cell.tryRestartOrTerminate()
}

func (cell *ActorCell) tryRestartOrTerminate() {
	if !cell.children.Empty() {
		return
	}

	if !cell.stopping {
		cell.restart()
		return
	}

	cell.stopped()
}

func (cell *ActorCell) restart() {
	cell.incarnateActor()
	cell.invokeUserMessage(Started{})
	if cell.stash != nil {
		for !cell.stash.Empty() {
			msg, _ := cell.stash.Pop()
			cell.invokeUserMessage(msg)
		}
	}
}

func (cell *ActorCell) stopped() {
	ProcessRegistry.unregisterPID(cell.self)
	cell.invokeUserMessage(Stopped{})
	otherStopped := &otherStopped{Who: cell.self}
	for _, watcher := range cell.watchers.Values() {
		watcher.(*PID).sendSystemMessage(otherStopped)
	}
}

func (cell *ActorCell) invokeUserMessage(message interface{}) {
	defer func() {
		if r := recover(); r != nil {
			failure := &failure{Reason: r, Who: cell.self}
			if cell.parent == nil {
				handleRootFailure(failure, defaultSupervisionStrategy)
			} else {
				cell.self.suspend()
				cell.parent.sendSystemMessage(failure)
			}
		}
	}()
	behavior, _ := cell.behavior.Peek()
	behavior.(Receive)(NewContext(cell, message))
}

func (cell *ActorCell) Become(behavior Receive) {
	cell.behavior.Clear()
	cell.behavior.Push(behavior)
}

func (cell *ActorCell) BecomeStacked(behavior Receive) {
	cell.behavior.Push(behavior)
}

func (cell *ActorCell) UnbecomeStacked() {
	if cell.behavior.Size() <= 1 {
		panic("Can not unbecome actor base behavior")
	}
	cell.behavior.Pop()
}

func (cell *ActorCell) Watch(who *PID) {
	who.sendSystemMessage(&watch{
		Watcher: cell.self,
	})
	cell.watching.Add(who)
}

func (cell *ActorCell) Unwatch(who *PID) {
	who.sendSystemMessage(&unwatch{
		Watcher: cell.self,
	})
	cell.watching.Remove(who)
}

// func (cell *ActorCell) ActorOf(props Properties) *PID {
// 	_, pid := spawnChild(props, cell.self)
// 	cell.children.Add(pid)
// 	cell.Watch(pid)
// 	return pid
// }

func (cell *ActorCell) Spawn(props Properties) *PID {
	pid := spawnChild(props, cell.self)
	cell.children.Add(pid)
	cell.Watch(pid)
	return pid
}

func (cell *ActorCell) SpawnTemplate(template Actor) *PID {
	producer := func() Actor {
		return template
	}
	props := Props(producer)
	pid := spawnChild(props, cell.self)
	cell.children.Add(pid)
	cell.Watch(pid)
	return pid
}

func (cell *ActorCell) SpawnFunc(producer ActorProducer) *PID {
	props := Props(producer)
	pid := spawnChild(props, cell.self)
	cell.children.Add(pid)
	cell.Watch(pid)
	return pid
}

func handleRootFailure(msg *failure, supervisor SupervisionStrategy) {
	directive := supervisor.Handle(msg.Who, msg.Reason)
	switch directive {
	case ResumeDirective:
		//resume the fialing child
		msg.Who.sendSystemMessage(&resume{})
	case RestartDirective:
		//restart the failing child
		msg.Who.sendSystemMessage(&restart{})
	case StopDirective:
		//stop the failing child
		msg.Who.Stop()
	case EscalateDirective:
		//send failure to parent
		panic("Can not escalate root level failures")
	}
}
<|endoftext|>


<commit_before>package api

// Define consts for all the lifecycle events.
const (
	EventLifecycleCertificateCreated                = "certificate-created"
	EventLifecycleCertificateDeleted                = "certificate-deleted"
	EventLifecycleCertificateUpdated                = "certificate-updated"
	EventLifecycleClusterCertificateUpdated         = "cluster-certificate-updated"
	EventLifecycleClusterDisabled                   = "cluster-disabled"
	EventLifecycleClusterEnabled                    = "cluster-enabled"
	EventLifecycleClusterGroupCreated               = "cluster-group-created"
	EventLifecycleClusterGroupDeleted               = "cluster-group-deleted"
	EventLifecycleClusterGroupRenamed               = "cluster-group-renamed"
	EventLifecycleClusterGroupUpdated               = "cluster-group-updated"
	EventLifecycleClusterMemberAdded                = "cluster-member-added"
	EventLifecycleClusterMemberRemoved              = "cluster-member-removed"
	EventLifecycleClusterMemberRenamed              = "cluster-member-renamed"
	EventLifecycleClusterMemberUpdated              = "cluster-member-updated"
	EventLifecycleClusterTokenCreated               = "cluster-token-created"
	EventLifecycleConfigUpdated                     = "config-updated"
	EventLifecycleImageAliasCreated                 = "image-alias-created"
	EventLifecycleImageAliasDeleted                 = "image-alias-deleted"
	EventLifecycleImageAliasRenamed                 = "image-alias-renamed"
	EventLifecycleImageAliasUpdated                 = "image-alias-updated"
	EventLifecycleImageCreated                      = "image-created"
	EventLifecycleImageDeleted                      = "image-deleted"
	EventLifecycleImageRefreshed                    = "image-refreshed"
	EventLifecycleImageRetrieved                    = "image-retrieved"
	EventLifecycleImageSecretCreated                = "image-secret-created"
	EventLifecycleImageUpdated                      = "image-updated"
	EventLifecycleInstanceBackupCreated             = "instance-backup-created"
	EventLifecycleInstanceBackupDeleted             = "instance-backup-deleted"
	EventLifecycleInstanceBackupRenamed             = "instance-backup-renamed"
	EventLifecycleInstanceBackupRetrieved           = "instance-backup-retrieved"
	EventLifecycleInstanceConsole                   = "instance-console"
	EventLifecycleInstanceConsoleReset              = "instance-console-reset"
	EventLifecycleInstanceConsoleRetrieved          = "instance-console-retrieved"
	EventLifecycleInstanceCreated                   = "instance-created"
	EventLifecycleInstanceDeleted                   = "instance-deleted"
	EventLifecycleInstanceExec                      = "instance-exec"
	EventLifecycleInstanceFileDeleted               = "instance-file-deleted"
	EventLifecycleInstanceFilePushed                = "instance-file-pushed"
	EventLifecycleInstanceFileRetrieved             = "instance-file-retrieved"
	EventLifecycleInstanceLogDeleted                = "instance-log-deleted"
	EventLifecycleInstanceLogRetrieved              = "instance-log-retrieved"
	EventLifecycleInstanceMetadataRetrieved         = "instance-metadata-retrieved"
	EventLifecycleInstanceMetadataTemplateCreated   = "instance-metadata-template-created"
	EventLifecycleInstanceMetadataTemplateDeleted   = "instance-metadata-template-deleted"
	EventLifecycleInstanceMetadataTemplateRetrieved = "instance-metadata-template-retrieved"
	EventLifecycleInstanceMetadataUpdated           = "instance-metadata-updated"
	EventLifecycleInstancePaused                    = "instance-paused"
	EventLifecycleInstanceRenamed                   = "instance-renamed"
	EventLifecycleInstanceRestarted                 = "instance-restarted"
	EventLifecycleInstanceRestored                  = "instance-restored"
	EventLifecycleInstanceResumed                   = "instance-resumed"
	EventLifecycleInstanceShutdown                  = "instance-shutdown"
	EventLifecycleInstanceSnapshotCreated           = "instance-snapshot-created"
	EventLifecycleInstanceSnapshotDeleted           = "instance-snapshot-deleted"
	EventLifecycleInstanceSnapshotRenamed           = "instance-snapshot-renamed"
	EventLifecycleInstanceSnapshotUpdated           = "instance-snapshot-updated"
	EventLifecycleInstanceStarted                   = "instance-started"
	EventLifecycleInstanceStopped                   = "instance-stopped"
	EventLifecycleInstanceUpdated                   = "instance-updated"
	EventLifecycleNetworkACLCreated                 = "network-acl-created"
	EventLifecycleNetworkACLDeleted                 = "network-acl-deleted"
	EventLifecycleNetworkACLRenamed                 = "network-acl-renamed"
	EventLifecycleNetworkACLUpdated                 = "network-acl-updated"
	EventLifecycleNetworkCreated                    = "network-created"
	EventLifecycleNetworkDeleted                    = "network-deleted"
	EventLifecycleNetworkForwardCreated             = "network-forward-created"
	EventLifecycleNetworkForwardDeleted             = "network-forward-deleted"
	EventLifecycleNetworkForwardUpdated             = "network-forward-updated"
	EventLifecycleNetworkPeerCreated                = "network-peer-created"
	EventLifecycleNetworkPeerDeleted                = "network-peer-deleted"
	EventLifecycleNetworkPeerUpdated                = "network-peer-updated"
	EventLifecycleNetworkRenamed                    = "network-renamed"
	EventLifecycleNetworkUpdated                    = "network-updated"
	EventLifecycleNetworkZoneCreated                = "network-zone-created"
	EventLifecycleNetworkZoneDeleted                = "network-zone-deleted"
	EventLifecycleNetworkZoneRecordCreated          = "network-zone-record-created"
	EventLifecycleNetworkZoneRecordDeleted          = "network-zone-record-deleted"
	EventLifecycleNetworkZoneRecordUpdated          = "network-zone-record-updated"
	EventLifecycleNetworkZoneUpdated                = "network-zone-updated"
	EventLifecycleOperationCancelled                = "operation-cancelled"
	EventLifecycleProfileCreated                    = "profile-created"
	EventLifecycleProfileDeleted                    = "profile-deleted"
	EventLifecycleProfileRenamed                    = "profile-renamed"
	EventLifecycleProfileUpdated                    = "profile-updated"
	EventLifecycleProjectCreated                    = "project-created"
	EventLifecycleProjectDeleted                    = "project-deleted"
	EventLifecycleProjectRenamed                    = "project-renamed"
	EventLifecycleProjectUpdated                    = "project-updated"
	EventLifecycleStoragePoolCreated                = "storage-pool-created"
	EventLifecycleStoragePoolDeleted                = "storage-pool-deleted"
	EventLifecycleStoragePoolUpdated                = "storage-pool-updated"
	EventLifecycleStorageVolumCreated               = "storage-volume-created"
	EventLifecycleStorageVolumeBackupCreated        = "storage-volume-backup-created"
	EventLifecycleStorageVolumeBackupDeleted        = "storage-volume-backup-deleted"
	EventLifecycleStorageVolumeBackupRenamed        = "storage-volume-backup-renamed"
	EventLifecycleStorageVolumeBackupRetrieved      = "storage-volume-backup-retrieved"
	EventLifecycleStorageVolumeDeleted              = "storage-volume-deleted"
	EventLifecycleStorageVolumeRenamed              = "storage-volume-renamed"
	EventLifecycleStorageVolumeRestored             = "storage-volume-restored"
	EventLifecycleStorageVolumeSnapshotCreated      = "storage-volume-snapshot-created"
	EventLifecycleStorageVolumeSnapshotDeleted      = "storage-volume-snapshot-deleted"
	EventLifecycleStorageVolumeSnapshotRenamed      = "storage-volume-snapshot-renamed"
	EventLifecycleStorageVolumeSnapshotUpdated      = "storage-volume-snapshot-updated"
	EventLifecycleStorageVolumeUpdated              = "storage-volume-updated"
	EventLifecycleWarningAcknowledged               = "warning-acknowledged"
	EventLifecycleWarningDeleted                    = "warning-deleted"
	EventLifecycleWarningReset                      = "warning-reset"
)
<commit_msg>shared/api/event/lifecycle: Adds network load balancer lifecycle constants<commit_after>package api

// Define consts for all the lifecycle events.
const (
	EventLifecycleCertificateCreated                = "certificate-created"
	EventLifecycleCertificateDeleted                = "certificate-deleted"
	EventLifecycleCertificateUpdated                = "certificate-updated"
	EventLifecycleClusterCertificateUpdated         = "cluster-certificate-updated"
	EventLifecycleClusterDisabled                   = "cluster-disabled"
	EventLifecycleClusterEnabled                    = "cluster-enabled"
	EventLifecycleClusterGroupCreated               = "cluster-group-created"
	EventLifecycleClusterGroupDeleted               = "cluster-group-deleted"
	EventLifecycleClusterGroupRenamed               = "cluster-group-renamed"
	EventLifecycleClusterGroupUpdated               = "cluster-group-updated"
	EventLifecycleClusterMemberAdded                = "cluster-member-added"
	EventLifecycleClusterMemberRemoved              = "cluster-member-removed"
	EventLifecycleClusterMemberRenamed              = "cluster-member-renamed"
	EventLifecycleClusterMemberUpdated              = "cluster-member-updated"
	EventLifecycleClusterTokenCreated               = "cluster-token-created"
	EventLifecycleConfigUpdated                     = "config-updated"
	EventLifecycleImageAliasCreated                 = "image-alias-created"
	EventLifecycleImageAliasDeleted                 = "image-alias-deleted"
	EventLifecycleImageAliasRenamed                 = "image-alias-renamed"
	EventLifecycleImageAliasUpdated                 = "image-alias-updated"
	EventLifecycleImageCreated                      = "image-created"
	EventLifecycleImageDeleted                      = "image-deleted"
	EventLifecycleImageRefreshed                    = "image-refreshed"
	EventLifecycleImageRetrieved                    = "image-retrieved"
	EventLifecycleImageSecretCreated                = "image-secret-created"
	EventLifecycleImageUpdated                      = "image-updated"
	EventLifecycleInstanceBackupCreated             = "instance-backup-created"
	EventLifecycleInstanceBackupDeleted             = "instance-backup-deleted"
	EventLifecycleInstanceBackupRenamed             = "instance-backup-renamed"
	EventLifecycleInstanceBackupRetrieved           = "instance-backup-retrieved"
	EventLifecycleInstanceConsole                   = "instance-console"
	EventLifecycleInstanceConsoleReset              = "instance-console-reset"
	EventLifecycleInstanceConsoleRetrieved          = "instance-console-retrieved"
	EventLifecycleInstanceCreated                   = "instance-created"
	EventLifecycleInstanceDeleted                   = "instance-deleted"
	EventLifecycleInstanceExec                      = "instance-exec"
	EventLifecycleInstanceFileDeleted               = "instance-file-deleted"
	EventLifecycleInstanceFilePushed                = "instance-file-pushed"
	EventLifecycleInstanceFileRetrieved             = "instance-file-retrieved"
	EventLifecycleInstanceLogDeleted                = "instance-log-deleted"
	EventLifecycleInstanceLogRetrieved              = "instance-log-retrieved"
	EventLifecycleInstanceMetadataRetrieved         = "instance-metadata-retrieved"
	EventLifecycleInstanceMetadataTemplateCreated   = "instance-metadata-template-created"
	EventLifecycleInstanceMetadataTemplateDeleted   = "instance-metadata-template-deleted"
	EventLifecycleInstanceMetadataTemplateRetrieved = "instance-metadata-template-retrieved"
	EventLifecycleInstanceMetadataUpdated           = "instance-metadata-updated"
	EventLifecycleInstancePaused                    = "instance-paused"
	EventLifecycleInstanceRenamed                   = "instance-renamed"
	EventLifecycleInstanceRestarted                 = "instance-restarted"
	EventLifecycleInstanceRestored                  = "instance-restored"
	EventLifecycleInstanceResumed                   = "instance-resumed"
	EventLifecycleInstanceShutdown                  = "instance-shutdown"
	EventLifecycleInstanceSnapshotCreated           = "instance-snapshot-created"
	EventLifecycleInstanceSnapshotDeleted           = "instance-snapshot-deleted"
	EventLifecycleInstanceSnapshotRenamed           = "instance-snapshot-renamed"
	EventLifecycleInstanceSnapshotUpdated           = "instance-snapshot-updated"
	EventLifecycleInstanceStarted                   = "instance-started"
	EventLifecycleInstanceStopped                   = "instance-stopped"
	EventLifecycleInstanceUpdated                   = "instance-updated"
	EventLifecycleNetworkACLCreated                 = "network-acl-created"
	EventLifecycleNetworkACLDeleted                 = "network-acl-deleted"
	EventLifecycleNetworkACLRenamed                 = "network-acl-renamed"
	EventLifecycleNetworkACLUpdated                 = "network-acl-updated"
	EventLifecycleNetworkCreated                    = "network-created"
	EventLifecycleNetworkDeleted                    = "network-deleted"
	EventLifecycleNetworkForwardCreated             = "network-forward-created"
	EventLifecycleNetworkForwardDeleted             = "network-forward-deleted"
	EventLifecycleNetworkForwardUpdated             = "network-forward-updated"
	EventLifecycleNetworkLoadBalancerCreated        = "network-load-balancer-created"
	EventLifecycleNetworkLoadBalancerDeleted        = "network-load-balancer-deleted"
	EventLifecycleNetworkLoadBalancerUpdated        = "network-load-balancer-updated"
	EventLifecycleNetworkPeerCreated                = "network-peer-created"
	EventLifecycleNetworkPeerDeleted                = "network-peer-deleted"
	EventLifecycleNetworkPeerUpdated                = "network-peer-updated"
	EventLifecycleNetworkRenamed                    = "network-renamed"
	EventLifecycleNetworkUpdated                    = "network-updated"
	EventLifecycleNetworkZoneCreated                = "network-zone-created"
	EventLifecycleNetworkZoneDeleted                = "network-zone-deleted"
	EventLifecycleNetworkZoneRecordCreated          = "network-zone-record-created"
	EventLifecycleNetworkZoneRecordDeleted          = "network-zone-record-deleted"
	EventLifecycleNetworkZoneRecordUpdated          = "network-zone-record-updated"
	EventLifecycleNetworkZoneUpdated                = "network-zone-updated"
	EventLifecycleOperationCancelled                = "operation-cancelled"
	EventLifecycleProfileCreated                    = "profile-created"
	EventLifecycleProfileDeleted                    = "profile-deleted"
	EventLifecycleProfileRenamed                    = "profile-renamed"
	EventLifecycleProfileUpdated                    = "profile-updated"
	EventLifecycleProjectCreated                    = "project-created"
	EventLifecycleProjectDeleted                    = "project-deleted"
	EventLifecycleProjectRenamed                    = "project-renamed"
	EventLifecycleProjectUpdated                    = "project-updated"
	EventLifecycleStoragePoolCreated                = "storage-pool-created"
	EventLifecycleStoragePoolDeleted                = "storage-pool-deleted"
	EventLifecycleStoragePoolUpdated                = "storage-pool-updated"
	EventLifecycleStorageVolumCreated               = "storage-volume-created"
	EventLifecycleStorageVolumeBackupCreated        = "storage-volume-backup-created"
	EventLifecycleStorageVolumeBackupDeleted        = "storage-volume-backup-deleted"
	EventLifecycleStorageVolumeBackupRenamed        = "storage-volume-backup-renamed"
	EventLifecycleStorageVolumeBackupRetrieved      = "storage-volume-backup-retrieved"
	EventLifecycleStorageVolumeDeleted              = "storage-volume-deleted"
	EventLifecycleStorageVolumeRenamed              = "storage-volume-renamed"
	EventLifecycleStorageVolumeRestored             = "storage-volume-restored"
	EventLifecycleStorageVolumeSnapshotCreated      = "storage-volume-snapshot-created"
	EventLifecycleStorageVolumeSnapshotDeleted      = "storage-volume-snapshot-deleted"
	EventLifecycleStorageVolumeSnapshotRenamed      = "storage-volume-snapshot-renamed"
	EventLifecycleStorageVolumeSnapshotUpdated      = "storage-volume-snapshot-updated"
	EventLifecycleStorageVolumeUpdated              = "storage-volume-updated"
	EventLifecycleWarningAcknowledged               = "warning-acknowledged"
	EventLifecycleWarningDeleted                    = "warning-deleted"
	EventLifecycleWarningReset                      = "warning-reset"
)
<|endoftext|>


<commit_before>package extra

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"unicode"

	"github.com/go-xorm/xorm"

	seabird "github.com/belak/go-seabird"
	irc "gopkg.in/irc.v3"
)

// NOAAStation is a simple cache which will store a user's last-requested
// NOAA station.
type NOAAStation struct {
	ID      int64
	Nick    string `xorm:"unique"`
	Station string
}

type noaaPlugin struct {
	db *xorm.Engine
}

func init() {
	seabird.RegisterPlugin("noaa", newMetarPlugin)
}

func newMetarPlugin(b *seabird.Bot, cm *seabird.CommandMux, db *xorm.Engine) error {
	p := &noaaPlugin{db: db}

	// Ensure DB tables are up to date
	err := p.db.Sync(NOAAStation{})
	if err != nil {
		return err
	}

	cm.Event("metar", p.metarCallback, &seabird.HelpInfo{
		Usage:       "<station>",
		Description: "Gives METAR report for given station",
	})
	cm.Event("taf", p.tafCallback, &seabird.HelpInfo{
		Usage:       "<station>",
		Description: "Gives TAF report for given station",
	})

	return nil
}

func (p *noaaPlugin) getStation(b *seabird.Bot, m *irc.Message) (string, error) {
	l := m.Trailing()

	target := &NOAAStation{Nick: m.Prefix.Name}

	// If it's an empty string, check the cache
	if l == "" {
		found, err := p.db.Get(target)
		if err != nil || !found {
			return "", fmt.Errorf("Could not find a location for %q", m.Prefix.Name)
		}
		return target.Station, nil
	}

	newStation := &NOAAStation{
		Nick:    m.Prefix.Name,
		Station: m.Trailing(),
	}

	_, err := p.db.Transaction(func(s *xorm.Session) (interface{}, error) {
		found, _ := s.Get(target)
		if !found {
			return s.Insert(newStation)
		}

		return s.ID(target.ID).Update(newStation)
	})

	return newStation.Station, err
}

func (p *noaaPlugin) metarCallback(b *seabird.Bot, m *irc.Message) {
	station, err := p.getStation(b, m)
	if err != nil {
		b.MentionReply(m, "%s", err.Error())
		return
	}

	r, err := NOAALookup("http://tgftp.nws.noaa.gov/data/observations/metar/stations/%s.TXT", station)
	if err != nil {
		b.MentionReply(m, "Error: %s", err)
		return
	}

	b.MentionReply(m, "%s", r)
}

func (p *noaaPlugin) tafCallback(b *seabird.Bot, m *irc.Message) {
	station, err := p.getStation(b, m)
	if err != nil {
		b.MentionReply(m, "%s", err.Error())
		return
	}

	r, err := NOAALookup("http://tgftp.nws.noaa.gov/data/forecasts/taf/stations/%s.TXT", station)
	if err != nil {
		b.MentionReply(m, "Error: %s", err)
		return
	}

	b.MentionReply(m, "%s", r)
}

// NOAALookup takes the given formatted url and an airport code and tries to
// look up the raw data. The first line is skipped, as that is generally the
// date and the rest of the lines are joined together with a maximum of one
// space between them.
func NOAALookup(urlFormat, code string) (string, error) {
	code = strings.ToUpper(code)

	for _, letter := range code {
		if !unicode.IsDigit(letter) && !unicode.IsLetter(letter) {
			return "", errors.New("Not a valid airport code")
		}
	}

	resp, err := http.Get(fmt.Sprintf(urlFormat, code))
	if err != nil {
		return "", errors.New("NOAA appears to be down")
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "", errors.New("Station does not exist")
	}

	out := &bytes.Buffer{}
	in := bufio.NewReader(resp.Body)
	first := false
	for {
		line, err := in.ReadString('\n')
		if err == io.EOF {
			break
		} else if err != nil {
			return "", errors.New("No results")
		}

		// We skip the first line as it contains the date.
		if !first {
			first = true
			continue
		}

		out.WriteString(" " + strings.TrimSpace(line))
	}

	return strings.TrimSpace(out.String()), nil
}
<commit_msg>Normalize inputs to db<commit_after>package extra

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"unicode"

	"github.com/go-xorm/xorm"

	seabird "github.com/belak/go-seabird"
	irc "gopkg.in/irc.v3"
)

// NOAAStation is a simple cache which will store a user's last-requested
// NOAA station.
type NOAAStation struct {
	ID      int64
	Nick    string `xorm:"unique"`
	Station string
}

type noaaPlugin struct {
	db *xorm.Engine
}

func init() {
	seabird.RegisterPlugin("noaa", newMetarPlugin)
}

func newMetarPlugin(b *seabird.Bot, cm *seabird.CommandMux, db *xorm.Engine) error {
	p := &noaaPlugin{db: db}

	// Ensure DB tables are up to date
	err := p.db.Sync(NOAAStation{})
	if err != nil {
		return err
	}

	cm.Event("metar", p.metarCallback, &seabird.HelpInfo{
		Usage:       "<station>",
		Description: "Gives METAR report for given station",
	})
	cm.Event("taf", p.tafCallback, &seabird.HelpInfo{
		Usage:       "<station>",
		Description: "Gives TAF report for given station",
	})

	return nil
}

func (p *noaaPlugin) getStation(b *seabird.Bot, m *irc.Message) (string, error) {
	l := m.Trailing()

	target := &NOAAStation{Nick: m.Prefix.Name}

	// If it's an empty string, check the cache
	if l == "" {
		found, err := p.db.Get(target)
		if err != nil || !found {
			return "", fmt.Errorf("Could not find a location for %q", m.Prefix.Name)
		}
		return target.Station, nil
	}

	newStation := &NOAAStation{
		Nick:    m.Prefix.Name,
		Station: strings.ToUpper(l),
	}

	_, err := p.db.Transaction(func(s *xorm.Session) (interface{}, error) {
		found, _ := s.Get(target)
		if !found {
			return s.Insert(newStation)
		}

		return s.ID(target.ID).Update(newStation)
	})

	return newStation.Station, err
}

func (p *noaaPlugin) metarCallback(b *seabird.Bot, m *irc.Message) {
	station, err := p.getStation(b, m)
	if err != nil {
		b.MentionReply(m, "%s", err.Error())
		return
	}

	r, err := NOAALookup("http://tgftp.nws.noaa.gov/data/observations/metar/stations/%s.TXT", station)
	if err != nil {
		b.MentionReply(m, "Error: %s", err)
		return
	}

	b.MentionReply(m, "%s", r)
}

func (p *noaaPlugin) tafCallback(b *seabird.Bot, m *irc.Message) {
	station, err := p.getStation(b, m)
	if err != nil {
		b.MentionReply(m, "%s", err.Error())
		return
	}

	r, err := NOAALookup("http://tgftp.nws.noaa.gov/data/forecasts/taf/stations/%s.TXT", station)
	if err != nil {
		b.MentionReply(m, "Error: %s", err)
		return
	}

	b.MentionReply(m, "%s", r)
}

// NOAALookup takes the given formatted url and an airport code and tries to
// look up the raw data. The first line is skipped, as that is generally the
// date and the rest of the lines are joined together with a maximum of one
// space between them.
func NOAALookup(urlFormat, code string) (string, error) {
	code = strings.ToUpper(code)

	for _, letter := range code {
		if !unicode.IsDigit(letter) && !unicode.IsLetter(letter) {
			return "", errors.New("Not a valid airport code")
		}
	}

	resp, err := http.Get(fmt.Sprintf(urlFormat, code))
	if err != nil {
		return "", errors.New("NOAA appears to be down")
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "", errors.New("Station does not exist")
	}

	out := &bytes.Buffer{}
	in := bufio.NewReader(resp.Body)
	first := false
	for {
		line, err := in.ReadString('\n')
		if err == io.EOF {
			break
		} else if err != nil {
			return "", errors.New("No results")
		}

		// We skip the first line as it contains the date.
		if !first {
			first = true
			continue
		}

		out.WriteString(" " + strings.TrimSpace(line))
	}

	return strings.TrimSpace(out.String()), nil
}
<|endoftext|>