<commit_before>package github

// this file was modified heavily from this https://github.com/google/go-github/blob/master/github/activity_events.go
// Portions Copyright 2013 The go-github AUTHORS. All rights reserved.

// we are using our protobuf generated events instead of the ones in their package so that we can marshal into protobuf protocol

import (
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"hash"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

const (
	// sha1Prefix is the prefix used by GitHub before the HMAC hexdigest.
	sha1Prefix = "sha1"
	// sha256Prefix and sha512Prefix are provided for future compatibility.
	sha256Prefix = "sha256"
	sha512Prefix = "sha512"
	// signatureHeader is the GitHub header key used to pass the HMAC hexdigest.
	signatureHeader = "X-Hub-Signature"
	// eventTypeHeader is the Github header key used to pass the event type.
	eventTypeHeader = "X-Github-Event"
)

var (
	// eventTypeMapping maps webhooks types to their corresponding go-github struct types.
	eventTypeMapping = map[string]string{
		"commit_comment":                        "CommitCommentEvent",
		"create":                                "CreateEvent",
		"delete":                                "DeleteEvent",
		"deployment":                            "DeploymentEvent",
		"deployment_status":                     "DeploymentStatusEvent",
		"fork":                                  "ForkEvent",
		"gollum":                                "GollumEvent",
		"integration_installation":              "IntegrationInstallationEvent",
		"integration_installation_repositories": "IntegrationInstallationRepositoriesEvent",
		"issue_comment":                         "IssueCommentEvent",
		"issues":                                "IssuesEvent",
		"member":                                "MemberEvent",
		"membership":                            "MembershipEvent",
		"page_build":                            "PageBuildEvent",
		"ping":                                  "Ping",
		"public":                                "PublicEvent",
		"pull_request_review_comment":           "PullRequestReviewCommentEvent",
		"pull_request":                          "PullRequestEvent",
		"pull_request_review":                   "PullRequestReviewEvent",
		"push":                                  "PushEvent",
		"repository":                            "RepositoryEvent",
		"release":                               "ReleaseEvent",
		"status":                                "StatusEvent",
		"team_add":                              "TeamAddEvent",
		"watch":                                 "WatchEvent",
	}
)

// genMAC generates the HMAC signature for a message provided the secret key
// and hashFunc.
func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte {
	mac := hmac.New(hashFunc, key)
	mac.Write(message)
	return mac.Sum(nil)
}

// checkMAC reports whether messageMAC is a valid HMAC tag for message.
func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool {
	expectedMAC := genMAC(message, key, hashFunc)
	return hmac.Equal(messageMAC, expectedMAC)
}

// messageMAC returns the hex-decoded HMAC tag from the signature and its
// corresponding hash function.
func messageMAC(signature string) ([]byte, func() hash.Hash, error) {
	if signature == "" {
		return nil, nil, errors.New("missing signature")
	}
	sigParts := strings.SplitN(signature, "=", 2)
	if len(sigParts) != 2 {
		return nil, nil, fmt.Errorf("error parsing signature %q", signature)
	}

	var hashFunc func() hash.Hash
	switch sigParts[0] {
	case sha1Prefix:
		hashFunc = sha1.New
	case sha256Prefix:
		hashFunc = sha256.New
	case sha512Prefix:
		hashFunc = sha512.New
	default:
		return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0])
	}

	buf, err := hex.DecodeString(sigParts[1])
	if err != nil {
		return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err)
	}
	return buf, hashFunc, nil
}

// ValidatePayload validates an incoming GitHub Webhook event request
// and returns the (JSON) payload.
// secretKey is the GitHub Webhook secret message.
//
// Example usage:
//
//     func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//       payload, err := github.ValidatePayload(r, s.webhookSecretKey)
//       if err != nil { ... }
//       // Process payload...
//     }
//
func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err error) {
	payload, err = ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	sig := r.Header.Get(signatureHeader)
	if err := validateSignature(sig, payload, secretKey); err != nil {
		return nil, err
	}
	return payload, nil
}

// validateSignature validates the signature for the given payload.
// signature is the GitHub hash signature delivered in the X-Hub-Signature header.
// payload is the JSON payload sent by GitHub Webhooks.
// secretKey is the GitHub Webhook secret message.
//
// GitHub docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
func validateSignature(signature string, payload, secretKey []byte) error {
	messageMAC, hashFunc, err := messageMAC(signature)
	if err != nil {
		return err
	}
	if !checkMAC(payload, messageMAC, secretKey, hashFunc) {
		return errors.New("payload signature check failed")
	}
	return nil
}

// WebHookType returns the event type of webhook request r.
func WebHookType(r *http.Request) string {
	return r.Header.Get(eventTypeHeader)
}

// ParseWebHook parses the event payload. For recognized event types, a
// value of the corresponding struct type will be returned (as returned
// by Event.Payload()). An error will be returned for unrecognized event
// types.
//
// Example usage:
//
//     func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//       payload, err := github.ValidatePayload(r, s.webhookSecretKey)
//       if err != nil { ... }
//       event, err := github.ParseWebHook(github.WebHookType(r), payload)
//       if err != nil { ... }
//       switch event := event.(type) {
//       case CommitCommentEvent:
//           processCommitCommentEvent(event)
//       case CreateEvent:
//           processCreateEvent(event)
//       ...
//       }
//     }
//
func ParseWebHook(messageType string, payload []byte) (interface{}, error) {
	eventType, ok := eventTypeMapping[messageType]
	if !ok {
		return nil, fmt.Errorf("unknown X-Github-Event in message: %v", messageType)
	}

	event := Event{
		Type:       &eventType,
		RawPayload: (*json.RawMessage)(&payload),
	}
	return event.Payload(), nil
}

// Event represents a GitHub event.
type Event struct {
	Type       *string          `json:"type,omitempty"`
	Public     *bool            `json:"public"`
	RawPayload *json.RawMessage `json:"payload,omitempty"`
	Repo       *Repository      `json:"repo,omitempty"`
	Actor      *User            `json:"actor,omitempty"`
	Org        *User            `json:"org,omitempty"`
	CreatedAt  *time.Time       `json:"created_at,omitempty"`
	ID         *string          `json:"id,omitempty"`
}

func (e Event) String() string {
	return Stringify(e)
}

func toTimestamp(t interface{}) string {
	switch t.(type) {
		case json.Number: {
			i, err := t.(json.Number).Int64()
			if err != nil {
				panic(fmt.Sprintf("error deserializing number: %v, error: %v", t, err))
			}
			tm := time.Unix(i, 0)
			return tm.Format("2006-01-02T15:04:05Z")
		}
		case float64: {
			tm := time.Unix(int64(t.(float64)), 0)
			return tm.Format("2006-01-02T15:04:05Z")
		}
	}
	return t.(string)
}

func applyPushEventHack(buf []byte) []byte {
	// webhooks have an issue where the pushed_at and created_at come in as
	// integers vs string like the normal API so we need to fix them before we
	// deserialize them or we get an error
	var p map[string]interface{}
	d := json.NewDecoder(strings.NewReader(string(buf)))
	d.UseNumber() // prevent numbers from getting converted
	if err := d.Decode(&p); err != nil {
		panic(err.Error())
	}
	r := p["repository"].(map[string]interface{})
	r["pushed_at"] = toTimestamp(r["pushed_at"])
	r["created_at"] = toTimestamp(r["created_at"])
	if r["organization"] != nil {
		switch v := r["organization"].(type) {
			case string: {
				// for a webhook, organization can be a string value
				// which points to the organization login instead of the
				// User object. we need to patch that
				if p["organization"] != nil {
					org := p["organization"].(map[string]interface{})
					if v == org["login"] {
						r["organization"] = org
					}
				}
			}
		}
	}
	commits := p["commits"].([]interface{})
	for _, c := range commits {
		commit := c.(map[string]interface{})
		if commit["sha"] == nil {
			if commit["id"] == nil {
				commit["sha"] = p["after"]
			} else {
				commit["sha"] = commit["id"]
			}
		}
	}
	// head_commit can have wrong id
	if p["head_commit"] != nil {
		head_commit := p["head_commit"].(map[string]interface{})
		idval := head_commit["id"]
		if  idval != nil {
			switch v := idval.(type) {
				case string: {
					head_commit["sha"] = v
					head_commit["id"] = nil
				}
			}
		}
	}
	result, err := json.Marshal(p)
	if err != nil {
		panic(err.Error())
	}
	return result
}

// Payload returns the parsed event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
func (e *Event) Payload() (payload interface{}) {
	switch *e.Type {
	case "CommitCommentEvent":
		payload = &CommitCommentEvent{}
	case "CreateEvent":
		payload = &CreateEvent{}
	case "DeleteEvent":
		payload = &DeleteEvent{}
	case "DeploymentEvent":
		payload = &DeploymentEvent{}
	case "DeploymentStatusEvent":
		payload = &DeploymentStatusEvent{}
	case "ForkEvent":
		payload = &ForkEvent{}
	case "GollumEvent":
		payload = &GollumEvent{}
	case "IntegrationInstallationEvent":
		payload = &IntegrationInstallation{}
	case "IntegrationInstallationRepositoriesEvent":
		payload = &IntegrationInstallationRepositories{}
	// case "IssueActivityEvent":
	// 	payload = &IssueActivityEvent{}
	case "IssueCommentEvent":
		payload = &IssueCommentEvent{}
	case "IssuesEvent":
		payload = &IssuesEvent{}
	case "MemberEvent":
		payload = &MemberEvent{}
	case "MembershipEvent":
		payload = &MembershipEvent{}
	case "PageBuildEvent":
		payload = &PageBuildEvent{}
	case "Ping":
		payload = &Ping{}
	case "PublicEvent":
		payload = &PublicEvent{}
	case "PullRequestEvent":
		payload = &PullRequestEvent{}
	case "PullRequestReviewEvent":
		payload = &PullRequestReviewEvent{}
	case "PullRequestReviewCommentEvent":
		payload = &PullRequestReviewCommentEvent{}
	case "PushEvent":
		*e.RawPayload = applyPushEventHack(*e.RawPayload)
		payload = &PushEvent{}
	case "ReleaseEvent":
		payload = &ReleaseEvent{}
	case "RepositoryEvent":
		payload = &RepositoryEvent{}
	case "StatusEvent":
		payload = &StatusEvent{}
	case "TeamAddEvent":
		payload = &TeamAddEvent{}
	case "WatchEvent":
		payload = &WatchEvent{}
	}
	if err := json.Unmarshal(*e.RawPayload, &payload); err != nil {
		panic(err.Error())
	}
	return payload
}
<commit_msg>Guarding type assertion for applyPushEventHack<commit_after>package github

// this file was modified heavily from this https://github.com/google/go-github/blob/master/github/activity_events.go
// Portions Copyright 2013 The go-github AUTHORS. All rights reserved.

// we are using our protobuf generated events instead of the ones in their package so that we can marshal into protobuf protocol

import (
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"hash"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

const (
	// sha1Prefix is the prefix used by GitHub before the HMAC hexdigest.
	sha1Prefix = "sha1"
	// sha256Prefix and sha512Prefix are provided for future compatibility.
	sha256Prefix = "sha256"
	sha512Prefix = "sha512"
	// signatureHeader is the GitHub header key used to pass the HMAC hexdigest.
	signatureHeader = "X-Hub-Signature"
	// eventTypeHeader is the Github header key used to pass the event type.
	eventTypeHeader = "X-Github-Event"
)

var (
	// eventTypeMapping maps webhooks types to their corresponding go-github struct types.
	eventTypeMapping = map[string]string{
		"commit_comment":                        "CommitCommentEvent",
		"create":                                "CreateEvent",
		"delete":                                "DeleteEvent",
		"deployment":                            "DeploymentEvent",
		"deployment_status":                     "DeploymentStatusEvent",
		"fork":                                  "ForkEvent",
		"gollum":                                "GollumEvent",
		"integration_installation":              "IntegrationInstallationEvent",
		"integration_installation_repositories": "IntegrationInstallationRepositoriesEvent",
		"issue_comment":                         "IssueCommentEvent",
		"issues":                                "IssuesEvent",
		"member":                                "MemberEvent",
		"membership":                            "MembershipEvent",
		"page_build":                            "PageBuildEvent",
		"ping":                                  "Ping",
		"public":                                "PublicEvent",
		"pull_request_review_comment":           "PullRequestReviewCommentEvent",
		"pull_request":                          "PullRequestEvent",
		"pull_request_review":                   "PullRequestReviewEvent",
		"push":                                  "PushEvent",
		"repository":                            "RepositoryEvent",
		"release":                               "ReleaseEvent",
		"status":                                "StatusEvent",
		"team_add":                              "TeamAddEvent",
		"watch":                                 "WatchEvent",
	}
)

// genMAC generates the HMAC signature for a message provided the secret key
// and hashFunc.
func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte {
	mac := hmac.New(hashFunc, key)
	mac.Write(message)
	return mac.Sum(nil)
}

// checkMAC reports whether messageMAC is a valid HMAC tag for message.
func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool {
	expectedMAC := genMAC(message, key, hashFunc)
	return hmac.Equal(messageMAC, expectedMAC)
}

// messageMAC returns the hex-decoded HMAC tag from the signature and its
// corresponding hash function.
func messageMAC(signature string) ([]byte, func() hash.Hash, error) {
	if signature == "" {
		return nil, nil, errors.New("missing signature")
	}
	sigParts := strings.SplitN(signature, "=", 2)
	if len(sigParts) != 2 {
		return nil, nil, fmt.Errorf("error parsing signature %q", signature)
	}

	var hashFunc func() hash.Hash
	switch sigParts[0] {
	case sha1Prefix:
		hashFunc = sha1.New
	case sha256Prefix:
		hashFunc = sha256.New
	case sha512Prefix:
		hashFunc = sha512.New
	default:
		return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0])
	}

	buf, err := hex.DecodeString(sigParts[1])
	if err != nil {
		return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err)
	}
	return buf, hashFunc, nil
}

// ValidatePayload validates an incoming GitHub Webhook event request
// and returns the (JSON) payload.
// secretKey is the GitHub Webhook secret message.
//
// Example usage:
//
//     func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//       payload, err := github.ValidatePayload(r, s.webhookSecretKey)
//       if err != nil { ... }
//       // Process payload...
//     }
//
func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err error) {
	payload, err = ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	sig := r.Header.Get(signatureHeader)
	if err := validateSignature(sig, payload, secretKey); err != nil {
		return nil, err
	}
	return payload, nil
}

// validateSignature validates the signature for the given payload.
// signature is the GitHub hash signature delivered in the X-Hub-Signature header.
// payload is the JSON payload sent by GitHub Webhooks.
// secretKey is the GitHub Webhook secret message.
//
// GitHub docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
func validateSignature(signature string, payload, secretKey []byte) error {
	messageMAC, hashFunc, err := messageMAC(signature)
	if err != nil {
		return err
	}
	if !checkMAC(payload, messageMAC, secretKey, hashFunc) {
		return errors.New("payload signature check failed")
	}
	return nil
}

// WebHookType returns the event type of webhook request r.
func WebHookType(r *http.Request) string {
	return r.Header.Get(eventTypeHeader)
}

// ParseWebHook parses the event payload. For recognized event types, a
// value of the corresponding struct type will be returned (as returned
// by Event.Payload()). An error will be returned for unrecognized event
// types.
//
// Example usage:
//
//     func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//       payload, err := github.ValidatePayload(r, s.webhookSecretKey)
//       if err != nil { ... }
//       event, err := github.ParseWebHook(github.WebHookType(r), payload)
//       if err != nil { ... }
//       switch event := event.(type) {
//       case CommitCommentEvent:
//           processCommitCommentEvent(event)
//       case CreateEvent:
//           processCreateEvent(event)
//       ...
//       }
//     }
//
func ParseWebHook(messageType string, payload []byte) (interface{}, error) {
	eventType, ok := eventTypeMapping[messageType]
	if !ok {
		return nil, fmt.Errorf("unknown X-Github-Event in message: %v", messageType)
	}

	event := Event{
		Type:       &eventType,
		RawPayload: (*json.RawMessage)(&payload),
	}
	return event.Payload(), nil
}

// Event represents a GitHub event.
type Event struct {
	Type       *string          `json:"type,omitempty"`
	Public     *bool            `json:"public"`
	RawPayload *json.RawMessage `json:"payload,omitempty"`
	Repo       *Repository      `json:"repo,omitempty"`
	Actor      *User            `json:"actor,omitempty"`
	Org        *User            `json:"org,omitempty"`
	CreatedAt  *time.Time       `json:"created_at,omitempty"`
	ID         *string          `json:"id,omitempty"`
}

func (e Event) String() string {
	return Stringify(e)
}

func toTimestamp(t interface{}) string {
	switch t.(type) {
		case json.Number: {
			i, err := t.(json.Number).Int64()
			if err != nil {
				panic(fmt.Sprintf("error deserializing number: %v, error: %v", t, err))
			}
			tm := time.Unix(i, 0)
			return tm.Format("2006-01-02T15:04:05Z")
		}
		case float64: {
			tm := time.Unix(int64(t.(float64)), 0)
			return tm.Format("2006-01-02T15:04:05Z")
		}
	}
	return t.(string)
}

func applyPushEventHack(buf []byte) []byte {
	// webhooks have an issue where the pushed_at and created_at come in as
	// integers vs string like the normal API so we need to fix them before we
	// deserialize them or we get an error
	var p map[string]interface{}
	d := json.NewDecoder(strings.NewReader(string(buf)))
	d.UseNumber() // prevent numbers from getting converted
	if err := d.Decode(&p); err != nil {
		panic(err.Error())
	}
	if r, ok := p["repository"].(map[string]interface{}); ok {
		r["pushed_at"] = toTimestamp(r["pushed_at"])
		r["created_at"] = toTimestamp(r["created_at"])
		if r["organization"] != nil {
			switch v := r["organization"].(type) {
			case string: {
				// for a webhook, organization can be a string value
				// which points to the organization login instead of the
				// User object. we need to patch that
				if p["organization"] != nil {
					if org, ok := p["organization"].(map[string]interface{}); ok {
						if v == org["login"] {
							r["organization"] = org
						}
					}
				}
			}
			}
		}
	}
	if commits, ok := p["commits"].([]interface{}); ok {
		for _, c := range commits {
			if commit, ok := c.(map[string]interface{}); ok {
				if commit["sha"] == nil {
					if commit["id"] == nil {
						commit["sha"] = p["after"]
					} else {
						commit["sha"] = commit["id"]
					}
				}
			}
		}
	}
	// head_commit can have wrong id
	if p["head_commit"] != nil {
		if head_commit, ok := p["head_commit"].(map[string]interface{}); ok {
			idval := head_commit["id"]
			if idval != nil {
				switch v := idval.(type) {
				case string: {
					head_commit["sha"] = v
					head_commit["id"] = nil
				}
				}
			}
		}
	}
	result, err := json.Marshal(p)
	if err != nil {
		panic(err.Error())
	}
	return result
}

// Payload returns the parsed event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
func (e *Event) Payload() (payload interface{}) {
	switch *e.Type {
	case "CommitCommentEvent":
		payload = &CommitCommentEvent{}
	case "CreateEvent":
		payload = &CreateEvent{}
	case "DeleteEvent":
		payload = &DeleteEvent{}
	case "DeploymentEvent":
		payload = &DeploymentEvent{}
	case "DeploymentStatusEvent":
		payload = &DeploymentStatusEvent{}
	case "ForkEvent":
		payload = &ForkEvent{}
	case "GollumEvent":
		payload = &GollumEvent{}
	case "IntegrationInstallationEvent":
		payload = &IntegrationInstallation{}
	case "IntegrationInstallationRepositoriesEvent":
		payload = &IntegrationInstallationRepositories{}
	// case "IssueActivityEvent":
	// 	payload = &IssueActivityEvent{}
	case "IssueCommentEvent":
		payload = &IssueCommentEvent{}
	case "IssuesEvent":
		payload = &IssuesEvent{}
	case "MemberEvent":
		payload = &MemberEvent{}
	case "MembershipEvent":
		payload = &MembershipEvent{}
	case "PageBuildEvent":
		payload = &PageBuildEvent{}
	case "Ping":
		payload = &Ping{}
	case "PublicEvent":
		payload = &PublicEvent{}
	case "PullRequestEvent":
		payload = &PullRequestEvent{}
	case "PullRequestReviewEvent":
		payload = &PullRequestReviewEvent{}
	case "PullRequestReviewCommentEvent":
		payload = &PullRequestReviewCommentEvent{}
	case "PushEvent":
		*e.RawPayload = applyPushEventHack(*e.RawPayload)
		payload = &PushEvent{}
	case "ReleaseEvent":
		payload = &ReleaseEvent{}
	case "RepositoryEvent":
		payload = &RepositoryEvent{}
	case "StatusEvent":
		payload = &StatusEvent{}
	case "TeamAddEvent":
		payload = &TeamAddEvent{}
	case "WatchEvent":
		payload = &WatchEvent{}
	}
	if err := json.Unmarshal(*e.RawPayload, &payload); err != nil {
		panic(err.Error())
	}
	return payload
}
<|endoftext|>


<commit_before>package github

import (
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/google/go-github/github"
	"github.com/lufia/taskfs/fs"
	"golang.org/x/oauth2"
)

type Comment struct {
	seq     int
	comment *github.IssueComment
}

func NewComment(seq int, comment *github.IssueComment) *Comment {
	return &Comment{
		seq:     seq,
		comment: comment,
	}
}

func (p *Comment) Key() string {
	return fmt.Sprintf("%d", p.seq)
}

func (p *Comment) Message() string {
	return *p.comment.Body
}

func (p *Comment) Creation() time.Time {
	return *p.comment.CreatedAt
}

func (p *Comment) LastMod() time.Time {
	return *p.comment.UpdatedAt
}

type Issue struct {
	issue *github.Issue
	svc   *Service
}

func (p *Issue) Key() string {
	return fmt.Sprintf("%d", *p.issue.ID)
}

func (p *Issue) Subject() string {
	return *p.issue.Title
}

func (p *Issue) Message() string {
	return *p.issue.Body
}

func (p *Issue) PermaLink() string {
	return *p.issue.HTMLURL
}

func (p *Issue) Creation() time.Time {
	return *p.issue.CreatedAt
}

func (p *Issue) LastMod() time.Time {
	return *p.issue.UpdatedAt
}

func (p *Issue) Comments() (a []fs.Comment, err error) {
	var buf []*github.IssueComment
	page := 0
	for {
		var b []*github.IssueComment
		b, page, err = p.fetchComments(page)
		if err != nil {
			return
		}
		buf = append(buf, b...)
		if page == 0 {
			break
		}
	}
	a = make([]fs.Comment, len(buf))
	for i, v := range buf {
		a[i] = NewComment(i+1, v)
	}
	return a, nil
}

func (p *Issue) fetchComments(page int) ([]*github.IssueComment, int, error) {
	owner := *p.issue.Repository.Owner.Login
	if org := p.issue.Repository.Organization; org != nil {
		owner = *org.Login
	}
	repo := *p.issue.Repository.Name
	n := *p.issue.Number
	var opt github.IssueListCommentsOptions
	opt.Page = page
	b, resp, err := p.svc.c.Issues.ListComments(owner, repo, n, &opt)
	if err != nil {
		return nil, 0, err
	}
	return b, resp.NextPage, nil
}

type Config struct {
	BaseURL string
	Token   string
}

func (c *Config) authorizedClient() *http.Client {
	if c.Token == "" {
		return nil
	}
	token := &oauth2.Token{
		AccessToken: c.Token,
	}
	s := oauth2.StaticTokenSource(token)
	return oauth2.NewClient(oauth2.NoContext, s)
}

type Service struct {
	c *github.Client
}

func NewService(config *Config) (*Service, error) {
	var client *http.Client
	if config.Token != "" {
		client = config.authorizedClient()
	}
	c := github.NewClient(client)
	if config.BaseURL != "" {
		u, err := url.Parse(config.BaseURL)
		if err != nil {
			return nil, err
		}
		c.BaseURL = u
	}
	return &Service{c: c}, nil
}

func (p *Service) Name() string {
	return "github"
}

func (p *Service) List() ([]fs.Task, error) {
	var a []fs.Task
	var opt github.IssueListOptions
	for {
		b, resp, err := p.c.Issues.List(true, &opt)
		if err != nil {
			return nil, err
		}
		a = p.appendIssues(a, b)
		if resp.NextPage == 0 {
			break
		}
		opt.Page = resp.NextPage
	}
	return a, nil
}

func (p *Service) appendIssues(a []fs.Task, b []*github.Issue) []fs.Task {
	for _, v := range b {
		a = append(a, &Issue{issue: v, svc: p})
	}
	return a
}
<commit_msg>change taskdir name: ID to repo@organization#num<commit_after>package github

import (
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/google/go-github/github"
	"github.com/lufia/taskfs/fs"
	"golang.org/x/oauth2"
)

type Comment struct {
	seq     int
	comment *github.IssueComment
}

func NewComment(seq int, comment *github.IssueComment) *Comment {
	return &Comment{
		seq:     seq,
		comment: comment,
	}
}

func (p *Comment) Key() string {
	return fmt.Sprintf("%d", p.seq)
}

func (p *Comment) Message() string {
	return *p.comment.Body
}

func (p *Comment) Creation() time.Time {
	return *p.comment.CreatedAt
}

func (p *Comment) LastMod() time.Time {
	return *p.comment.UpdatedAt
}

type Issue struct {
	issue *github.Issue
	svc   *Service
}

func (p *Issue) Key() string {
	owner := p.repositoryOwner()
	repo := p.repositoryName()
	return fmt.Sprintf("%s@%s#%d", repo, owner, p.Number())
}

func (p *Issue) Subject() string {
	return *p.issue.Title
}

func (p *Issue) Message() string {
	return *p.issue.Body
}

func (p *Issue) PermaLink() string {
	return *p.issue.HTMLURL
}

func (p *Issue) Creation() time.Time {
	return *p.issue.CreatedAt
}

func (p *Issue) LastMod() time.Time {
	return *p.issue.UpdatedAt
}

func (p *Issue) Comments() (a []fs.Comment, err error) {
	var buf []*github.IssueComment
	page := 0
	for {
		var b []*github.IssueComment
		b, page, err = p.fetchComments(page)
		if err != nil {
			return
		}
		buf = append(buf, b...)
		if page == 0 {
			break
		}
	}
	a = make([]fs.Comment, len(buf))
	for i, v := range buf {
		a[i] = NewComment(i+1, v)
	}
	return a, nil
}

func (p *Issue) repositoryOwner() string {
	owner := *p.issue.Repository.Owner.Login
	if org := p.issue.Repository.Organization; org != nil {
		owner = *org.Login
	}
	return owner
}

func (p *Issue) repositoryName() string {
	return *p.issue.Repository.Name
}

func (p *Issue) Number() int {
	return *p.issue.Number
}

func (p *Issue) fetchComments(page int) ([]*github.IssueComment, int, error) {
	owner := p.repositoryOwner()
	repo := p.repositoryName()
	n := p.Number()
	var opt github.IssueListCommentsOptions
	opt.Page = page
	b, resp, err := p.svc.c.Issues.ListComments(owner, repo, n, &opt)
	if err != nil {
		return nil, 0, err
	}
	return b, resp.NextPage, nil
}

type Config struct {
	BaseURL string
	Token   string
}

func (c *Config) authorizedClient() *http.Client {
	if c.Token == "" {
		return nil
	}
	token := &oauth2.Token{
		AccessToken: c.Token,
	}
	s := oauth2.StaticTokenSource(token)
	return oauth2.NewClient(oauth2.NoContext, s)
}

type Service struct {
	c *github.Client
}

func NewService(config *Config) (*Service, error) {
	var client *http.Client
	if config.Token != "" {
		client = config.authorizedClient()
	}
	c := github.NewClient(client)
	if config.BaseURL != "" {
		u, err := url.Parse(config.BaseURL)
		if err != nil {
			return nil, err
		}
		c.BaseURL = u
	}
	return &Service{c: c}, nil
}

func (p *Service) Name() string {
	return "github"
}

func (p *Service) List() ([]fs.Task, error) {
	var a []fs.Task
	var opt github.IssueListOptions
	for {
		b, resp, err := p.c.Issues.List(true, &opt)
		if err != nil {
			return nil, err
		}
		a = p.appendIssues(a, b)
		if resp.NextPage == 0 {
			break
		}
		opt.Page = resp.NextPage
	}
	return a, nil
}

func (p *Service) appendIssues(a []fs.Task, b []*github.Issue) []fs.Task {
	for _, v := range b {
		a = append(a, &Issue{issue: v, svc: p})
	}
	return a
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Couchbase, Inc.
// 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 indexer

import (
	"errors"
	"github.com/couchbase/indexing/secondary/common"
	"github.com/couchbase/indexing/secondary/manager"
)

//ClustMgrAgent provides the mechanism to talk to Index Coordinator
type ClustMgrAgent interface {
}

type clustMgrAgent struct {
	supvCmdch  MsgChannel //supervisor sends commands on this channel
	supvRespch MsgChannel //channel to send any message to supervisor

	mgr *manager.IndexManager //handle to index manager

	createNotifyCh <-chan interface{} //listen to create ddl from manager
	dropNotifyCh   <-chan interface{} //listen to drop ddl from manager

}

func NewClustMgrAgent(supvCmdch MsgChannel, supvRespch MsgChannel) (
	ClustMgrAgent, Message) {

	//Init the clustMgrAgent struct
	c := &clustMgrAgent{
		supvCmdch:  supvCmdch,
		supvRespch: supvRespch,
	}

	mgr, err := manager.NewIndexManager(GOMETA_REQUEST_ADDR,
		GOMETA_LEADER_ADDR, INDEX_MANAGER_CONFIG)

	if err != nil {
		common.Errorf("ClustMgrAgent::NewClustMgrAgent Error In Init %v", err)
		return nil, &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_AGENT_INIT,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}

	}

	c.mgr = mgr

	c.createNotifyCh, err = mgr.StartListenIndexCreate("ClustMgrAgent")
	if err != nil {
		common.Errorf("ClustMgrAgent::NewClustMgrAgent Error In "+
			"StartListenIndexCreate %v", err)
		return nil, &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_AGENT_INIT,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	}

	c.dropNotifyCh, err = mgr.StartListenIndexDelete("ClustMgrAgent")
	if err != nil {
		common.Errorf("ClustMgrAgent::NewClustMgrAgent Error In "+
			"StartListenIndexDelete %v", err)
		return nil, &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_AGENT_INIT,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	}
	//start clustMgrAgent loop which listens to commands from its supervisor
	go c.run()

	go c.listenIndexManagerMsgs()

	return c, &MsgSuccess{}

}

//run starts the clustmgrAgent loop which listens to messages
//from it supervisor(indexer)
func (c *clustMgrAgent) run() {

	//main ClustMgrAgent loop

	defer c.mgr.Close()

	defer c.panicHandler()

loop:
	for {
		select {

		case cmd, ok := <-c.supvCmdch:
			if ok {
				if cmd.GetMsgType() == CLUST_MGR_AGENT_SHUTDOWN {
					common.Infof("ClusterMgrAgent: Shutting Down")
					c.supvCmdch <- &MsgSuccess{}
					break loop
				}
				c.handleSupvervisorCommands(cmd)
			} else {
				//supervisor channel closed. exit
				break loop
			}

		}
	}
}

func (c *clustMgrAgent) handleSupvervisorCommands(cmd Message) {

	switch cmd.GetMsgType() {

	case CBQ_CREATE_INDEX_DDL:
		c.handleCreateIndex(cmd)

	case CBQ_DROP_INDEX_DDL:
		c.handleDropIndex(cmd)

	default:
		common.Errorf("ClusterMgrAgent::handleSupvervisorCommands Unknown Message %v", cmd)
	}

}

func (c *clustMgrAgent) listenIndexManagerMsgs() {

	for {
		select {

		case data, ok := <-c.createNotifyCh:
			if ok {
				//unmarshal and send to indexer
				common.Debugf("clustMgrAgent::listenIndexManagerMsgs Notification " +
					"Received for Create Index")
				idxDefn, err := manager.UnmarshallIndexDefn(data.([]byte))
				if err == nil {

					pc := common.NewKeyPartitionContainer()

					//Add one partition for now
					endpt := []common.Endpoint{INDEXER_MAINT_DATA_PORT_ENDPOINT}
					partnDefn := common.KeyPartitionDefn{Id: common.PartitionId(1),
						Endpts: endpt}
					pc.AddPartition(common.PartitionId(1), partnDefn)

					idxInst := common.IndexInst{InstId: common.IndexInstId(idxDefn.DefnId),
						Defn:  *idxDefn,
						State: common.INDEX_STATE_INITIAL,
						Pc:    pc,
					}

					c.supvRespch <- &MsgCreateIndex{mType: CLUST_MGR_CREATE_INDEX_DDL,
						indexInst: idxInst}
				}
			} else {

				common.Errorf("clustMgrAgent::listenIndexManagerMsgs Unexpected " +
					"Close Received from Index Manager.")
				c.supvRespch <- &MsgError{
					err: Error{code: ERROR_INDEX_MANAGER_CHANNEL_CLOSE,
						severity: FATAL,
						category: CLUSTER_MGR}}
			}

		case data, ok := <-c.dropNotifyCh:
			//unmarshal and send to indexer
			if ok {
				common.Debugf("clustMgrAgent::listenIndexManagerMsgs Notification " +
					"Received for Drop Index")
				idxKey := data.(string)
				idxDefn, err := c.mgr.GetIndexDefnByName(idxKey)
				if err == nil {
					c.supvRespch <- &MsgDropIndex{mType: CLUST_MGR_DROP_INDEX_DDL,
						indexInstId: common.IndexInstId(idxDefn.DefnId)}
				} else {
					common.Errorf("clustMgrAgent::listenIndexManagerMsgs Unable to find"+
						"Index. Key %v. Error %v", idxKey, err)
				}

			} else {
				common.Errorf("clustMgrAgent::listenIndexManagerMsgs Unexpected " +
					"Close Received from Index Manager.")
				c.supvRespch <- &MsgError{
					err: Error{code: ERROR_INDEX_MANAGER_CHANNEL_CLOSE,
						severity: FATAL,
						category: CLUSTER_MGR}}
			}

		}
	}

}

func (c *clustMgrAgent) handleCreateIndex(cmd Message) {

	idxInst := cmd.(*MsgCreateIndex).GetIndexInst()

	err := c.mgr.HandleCreateIndexDDL(&idxInst.Defn)
	if err != nil {
		common.Errorf("ClustMgrAgent::handleCreateIndex Error In Create Index %v", err)
		c.supvCmdch <- &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_CREATE_FAIL,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	} else {
		c.supvCmdch <- &MsgSuccess{}
	}
}

func (c *clustMgrAgent) handleDropIndex(cmd Message) {

	idxInstId := cmd.(*MsgDropIndex).GetIndexInstId()

	//TODO DefnId and InstId are same for now
	idxDefn, err := c.mgr.GetIndexDefnById(common.IndexDefnId(idxInstId))
	if err != nil {
		common.Errorf("ClustMgrAgent::handleDropIndex Unable to find Index Id %v Err %v", err)
		c.supvCmdch <- &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_DROP_FAIL,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	}

	err = c.mgr.HandleDeleteIndexDDL(idxDefn.Bucket + "/" + idxDefn.Name)
	if err != nil {
		common.Errorf("ClustMgrAgent::handleDropIndex Error In Drop Index %v", err)
		c.supvCmdch <- &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_DROP_FAIL,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	} else {
		c.supvCmdch <- &MsgSuccess{}
	}

}

//panicHandler handles the panic from index manager
func (c *clustMgrAgent) panicHandler() {

	//panic recovery
	if rc := recover(); rc != nil {
		var err error
		switch x := rc.(type) {
		case string:
			err = errors.New(x)
		case error:
			err = x
		default:
			err = errors.New("Unknown panic")
		}

		//panic, propagate to supervisor
		msg := &MsgError{
			err: Error{code: ERROR_INDEX_MANAGER_PANIC,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
		c.supvRespch <- msg
	}

}
<commit_msg>fix build issue - fix indexer/cluster_manager_agent.go due to index manager API changes<commit_after>// Copyright (c) 2014 Couchbase, Inc.
// 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 indexer

import (
	"errors"
	"strconv"
	"github.com/couchbase/indexing/secondary/common"
	"github.com/couchbase/indexing/secondary/manager"
)

//ClustMgrAgent provides the mechanism to talk to Index Coordinator
type ClustMgrAgent interface {
}

type clustMgrAgent struct {
	supvCmdch  MsgChannel //supervisor sends commands on this channel
	supvRespch MsgChannel //channel to send any message to supervisor

	mgr *manager.IndexManager //handle to index manager

	createNotifyCh <-chan interface{} //listen to create ddl from manager
	dropNotifyCh   <-chan interface{} //listen to drop ddl from manager

}

func NewClustMgrAgent(supvCmdch MsgChannel, supvRespch MsgChannel) (
	ClustMgrAgent, Message) {

	//Init the clustMgrAgent struct
	c := &clustMgrAgent{
		supvCmdch:  supvCmdch,
		supvRespch: supvRespch,
	}

	mgr, err := manager.NewIndexManager(GOMETA_REQUEST_ADDR,
		GOMETA_LEADER_ADDR, INDEX_MANAGER_CONFIG)

	if err != nil {
		common.Errorf("ClustMgrAgent::NewClustMgrAgent Error In Init %v", err)
		return nil, &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_AGENT_INIT,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}

	}

	c.mgr = mgr

	c.createNotifyCh, err = mgr.StartListenIndexCreate("ClustMgrAgent")
	if err != nil {
		common.Errorf("ClustMgrAgent::NewClustMgrAgent Error In "+
			"StartListenIndexCreate %v", err)
		return nil, &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_AGENT_INIT,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	}

	c.dropNotifyCh, err = mgr.StartListenIndexDelete("ClustMgrAgent")
	if err != nil {
		common.Errorf("ClustMgrAgent::NewClustMgrAgent Error In "+
			"StartListenIndexDelete %v", err)
		return nil, &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_AGENT_INIT,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	}
	//start clustMgrAgent loop which listens to commands from its supervisor
	go c.run()

	go c.listenIndexManagerMsgs()

	return c, &MsgSuccess{}

}

//run starts the clustmgrAgent loop which listens to messages
//from it supervisor(indexer)
func (c *clustMgrAgent) run() {

	//main ClustMgrAgent loop

	defer c.mgr.Close()

	defer c.panicHandler()

loop:
	for {
		select {

		case cmd, ok := <-c.supvCmdch:
			if ok {
				if cmd.GetMsgType() == CLUST_MGR_AGENT_SHUTDOWN {
					common.Infof("ClusterMgrAgent: Shutting Down")
					c.supvCmdch <- &MsgSuccess{}
					break loop
				}
				c.handleSupvervisorCommands(cmd)
			} else {
				//supervisor channel closed. exit
				break loop
			}

		}
	}
}

func (c *clustMgrAgent) handleSupvervisorCommands(cmd Message) {

	switch cmd.GetMsgType() {

	case CBQ_CREATE_INDEX_DDL:
		c.handleCreateIndex(cmd)

	case CBQ_DROP_INDEX_DDL:
		c.handleDropIndex(cmd)

	default:
		common.Errorf("ClusterMgrAgent::handleSupvervisorCommands Unknown Message %v", cmd)
	}

}

func (c *clustMgrAgent) listenIndexManagerMsgs() {

	for {
		select {

		case data, ok := <-c.createNotifyCh:
			if ok {
				//unmarshal and send to indexer
				common.Debugf("clustMgrAgent::listenIndexManagerMsgs Notification " +
					"Received for Create Index")
				idxDefn, err := manager.UnmarshallIndexDefn(data.([]byte))
				if err == nil {

					pc := common.NewKeyPartitionContainer()

					//Add one partition for now
					endpt := []common.Endpoint{INDEXER_MAINT_DATA_PORT_ENDPOINT}
					partnDefn := common.KeyPartitionDefn{Id: common.PartitionId(1),
						Endpts: endpt}
					pc.AddPartition(common.PartitionId(1), partnDefn)

					idxInst := common.IndexInst{InstId: common.IndexInstId(idxDefn.DefnId),
						Defn:  *idxDefn,
						State: common.INDEX_STATE_INITIAL,
						Pc:    pc,
					}

					c.supvRespch <- &MsgCreateIndex{mType: CLUST_MGR_CREATE_INDEX_DDL,
						indexInst: idxInst}
				}
			} else {

				common.Errorf("clustMgrAgent::listenIndexManagerMsgs Unexpected " +
					"Close Received from Index Manager.")
				c.supvRespch <- &MsgError{
					err: Error{code: ERROR_INDEX_MANAGER_CHANNEL_CLOSE,
						severity: FATAL,
						category: CLUSTER_MGR}}
			}

		case data, ok := <-c.dropNotifyCh:
			//unmarshal and send to indexer
			if ok {
				common.Debugf("clustMgrAgent::listenIndexManagerMsgs Notification " +
					"Received for Drop Index")
				idxKey := data.(string)
				id, err := strconv.ParseUint(idxKey, 10, 64)
				if err != nil {
					idxDefn, err := c.mgr.GetIndexDefnById(common.IndexDefnId(id))
					if err == nil {
						c.supvRespch <- &MsgDropIndex{mType: CLUST_MGR_DROP_INDEX_DDL,
							indexInstId: common.IndexInstId(idxDefn.DefnId)}
					} else {
						common.Errorf("clustMgrAgent::listenIndexManagerMsgs Unable to find"+
							"Index. Key %v. Error %v", idxKey, err)
					}
				}

			} else {
				common.Errorf("clustMgrAgent::listenIndexManagerMsgs Unexpected " +
					"Close Received from Index Manager.")
				c.supvRespch <- &MsgError{
					err: Error{code: ERROR_INDEX_MANAGER_CHANNEL_CLOSE,
						severity: FATAL,
						category: CLUSTER_MGR}}
			}

		}
	}

}

func (c *clustMgrAgent) handleCreateIndex(cmd Message) {

	idxInst := cmd.(*MsgCreateIndex).GetIndexInst()

	err := c.mgr.HandleCreateIndexDDL(&idxInst.Defn)
	if err != nil {
		common.Errorf("ClustMgrAgent::handleCreateIndex Error In Create Index %v", err)
		c.supvCmdch <- &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_CREATE_FAIL,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	} else {
		c.supvCmdch <- &MsgSuccess{}
	}
}

func (c *clustMgrAgent) handleDropIndex(cmd Message) {

	idxInstId := cmd.(*MsgDropIndex).GetIndexInstId()

	//TODO DefnId and InstId are same for now
	idxDefn, err := c.mgr.GetIndexDefnById(common.IndexDefnId(idxInstId))
	if err != nil {
		common.Errorf("ClustMgrAgent::handleDropIndex Unable to find Index Id %v Err %v", err)
		c.supvCmdch <- &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_DROP_FAIL,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	}

	err = c.mgr.HandleDeleteIndexDDL(idxDefn.Bucket, idxDefn.Name)
	if err != nil {
		common.Errorf("ClustMgrAgent::handleDropIndex Error In Drop Index %v", err)
		c.supvCmdch <- &MsgError{
			err: Error{code: ERROR_CLUSTER_MGR_DROP_FAIL,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
	} else {
		c.supvCmdch <- &MsgSuccess{}
	}

}

//panicHandler handles the panic from index manager
func (c *clustMgrAgent) panicHandler() {

	//panic recovery
	if rc := recover(); rc != nil {
		var err error
		switch x := rc.(type) {
		case string:
			err = errors.New(x)
		case error:
			err = x
		default:
			err = errors.New("Unknown panic")
		}

		//panic, propagate to supervisor
		msg := &MsgError{
			err: Error{code: ERROR_INDEX_MANAGER_PANIC,
				severity: FATAL,
				category: CLUSTER_MGR,
				cause:    err}}
		c.supvRespch <- msg
	}

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"path"
	"path/filepath"
	"text/template"
)

type data map[string]interface{}

var templates = make(map[string]*template.Template)

func init() {
	for name, html := range pagesHTML {
		t := template.New(name)
		template.Must(t.Parse(html))
		template.Must(t.Parse(baseHTML))
		templates[name] = t
	}
}

func render(w http.ResponseWriter, name string, d data) {
	w.Header().Add("Content-Type", "text/html")

	t, ok := templates[name]
	if !ok {
		renderError(w, http.StatusNotFound, fmt.Errorf("Page not found: %s", name))
		return
	}

	err := t.ExecuteTemplate(w, "root", d)
	if err != nil {
		renderError(w, http.StatusInternalServerError, err)
	}
}

func renderError(w http.ResponseWriter, status int, e error) {
	w.WriteHeader(status)
	err := templates["error"].ExecuteTemplate(w, "root", data{
		"Title": fmt.Sprintf("Error %d", status),
		"Error": e,
	})

	if err != nil {
		log.Println(err)
	}
}

type CommandCenter struct {
	server
	tld       string
	autoStart bool
	apps      map[string]App
	servers   []Server
}

func NewCommandCenter(c *Config) *CommandCenter {
	cc := &CommandCenter{tld: c.Tld}
	cc.name = "bam"
	cc.apps = make(map[string]App)
	cc.servers = []Server{cc}
	cc.loadApps(c)
	cc.autoStart = c.AutoStart
	return cc
}

func (cc *CommandCenter) List() []Server {
	return cc.servers
}

func (cc *CommandCenter) Start() error {
	port, _ := FreePort()
	cc.port = port
	if cc.autoStart {
		go func() {
			cc.startApps()
		}()
	}
	return http.ListenAndServe(fmt.Sprintf(":%d", cc.port), cc.createHandler())
}

func (cc *CommandCenter) startApps() {
	for _, app := range cc.apps {
		log.Printf("Starting app %s\n", app.Name())
		go app.Start()
	}
}

func (cc *CommandCenter) createHandler() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("/", cc.index)
	mux.HandleFunc("/start", cc.start)
	mux.HandleFunc("/stop", cc.stop)
	return mux
}

func (cc *CommandCenter) index(w http.ResponseWriter, r *http.Request) {
	render(w, "index", data{
		"Title": "BAM!",
		"Tld":   cc.tld,
		"Apps":  cc.apps,
	})
}

func (cc *CommandCenter) start(w http.ResponseWriter, r *http.Request) {
	cc.action(w, r, func(a App) error {
		log.Printf("Starting app %s\n", a.Name())
		return a.Start()
	})
}

func (cc *CommandCenter) stop(w http.ResponseWriter, r *http.Request) {
	cc.action(w, r, func(a App) error {
		log.Printf("Stopping app %s\n", a.Name())
		return a.Stop()
	})
}

func (cc *CommandCenter) action(w http.ResponseWriter, r *http.Request, action func(a App) error) {
	name := r.URL.Query().Get("app")
	app, _ := cc.apps[name] // FIXME not found
	action(app)
	http.Redirect(w, r, "/", 302)
}

func (cc *CommandCenter) register(a App) {
	if _, ok := cc.apps[a.Name()]; ok {
		return
	}
	cc.apps[a.Name()] = a
	cc.servers = append(cc.servers, a)
}

func (cc *CommandCenter) loadApps(c *Config) {
	cc.loadAliasApps(c.Aliases)
	cc.loadProcessApps(c.AppsDir)
	cc.loadWebServerApps(c.AppsDir)
}

func (cc *CommandCenter) loadAliasApps(aliases map[string]int) {
	for name, port := range aliases {
		cc.register(NewAliasApp(name, port))
	}
}

func (cc *CommandCenter) loadProcessApps(dir string) {
	procfiles, _ := filepath.Glob(fmt.Sprintf("%s/*/Procfile", dir))
	for _, p := range procfiles {
		app, err := NewProcessApp(p)
		if err != nil {
			log.Printf("Unable to load application %s. Error: %s\n", p, err.Error())
		} else {
			cc.register(app)
		}
	}
}

func (cc *CommandCenter) loadWebServerApps(dir string) {
	pages, _ := filepath.Glob(fmt.Sprintf("%s/*/index.html", dir))
	for _, p := range pages {
		cc.register(NewWebServerApp(path.Dir(p)))
	}
}

const baseHTML = `
{{ define "root" }}

    <title>{{.Title}}</title>

{{.Title}}

			{{ template "body" . }}

{{ end }}
`

var pagesHTML = map[string]string{
	"index": `
	{{ define "body" }}
		{{$tld := .Tld}}

			{{range .Apps}}
				{{ if .Running}}

				{{ else }}

				{{ end }}

					{{.Name}}

					{{ if .Running}}
						Stop
					{{ else }}
						Start
					{{ end }}

			{{end}}

	{{ end }}`,
	"error": `
	{{ define "body" }}
		{{.Error}}
	{{ end }}`,
}
<commit_msg>better error handling<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"path"
	"path/filepath"
	"text/template"
)

type data map[string]interface{}

var templates = make(map[string]*template.Template)

func init() {
	for name, html := range pagesHTML {
		t := template.New(name)
		template.Must(t.Parse(html))
		template.Must(t.Parse(baseHTML))
		templates[name] = t
	}
}

func render(w http.ResponseWriter, name string, d data) {
	w.Header().Add("Content-Type", "text/html")

	t, ok := templates[name]
	if !ok {
		renderError(w, http.StatusNotFound, fmt.Errorf("Page not found: %s", name))
		return
	}

	err := t.ExecuteTemplate(w, "root", d)
	if err != nil {
		renderError(w, http.StatusInternalServerError, err)
	}
}

func renderError(w http.ResponseWriter, status int, e error) {
	w.WriteHeader(status)
	err := templates["error"].ExecuteTemplate(w, "root", data{
		"Title": fmt.Sprintf("Error %d", status),
		"Error": e,
	})

	if err != nil {
		log.Println(err)
	}
}

type CommandCenter struct {
	server
	tld       string
	autoStart bool
	apps      map[string]App
	servers   []Server
}

func NewCommandCenter(c *Config) *CommandCenter {
	cc := &CommandCenter{tld: c.Tld}
	cc.name = "bam"
	cc.apps = make(map[string]App)
	cc.servers = []Server{cc}
	cc.loadApps(c)
	cc.autoStart = c.AutoStart
	return cc
}

func (cc *CommandCenter) List() []Server {
	return cc.servers
}

func (cc *CommandCenter) Start() error {
	port, err := FreePort()
	if err != nil {
		return err
	}

	cc.port = port
	if cc.autoStart {
		go func() {
			cc.startApps()
		}()
	}
	return http.ListenAndServe(fmt.Sprintf(":%d", cc.port), cc.createHandler())
}

func (cc *CommandCenter) startApps() {
	for _, app := range cc.apps {
		go func(a App) {
			log.Printf("Starting app %s\n", app.Name())
			err := app.Start()
			if err != nil {
				log.Printf("Failed to start %s: %s\n", app.Name(), err)
			}
		}(app)
	}
}

func (cc *CommandCenter) createHandler() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("/", cc.index)
	mux.HandleFunc("/start", cc.start)
	mux.HandleFunc("/stop", cc.stop)
	return mux
}

func (cc *CommandCenter) index(w http.ResponseWriter, r *http.Request) {
	render(w, "index", data{
		"Title": "BAM!",
		"Tld":   cc.tld,
		"Apps":  cc.apps,
	})
}

func (cc *CommandCenter) start(w http.ResponseWriter, r *http.Request) {
	cc.action(w, r, func(a App) error {
		log.Printf("Starting app %s\n", a.Name())
		return a.Start()
	})
}

func (cc *CommandCenter) stop(w http.ResponseWriter, r *http.Request) {
	cc.action(w, r, func(a App) error {
		log.Printf("Stopping app %s\n", a.Name())
		return a.Stop()
	})
}

func (cc *CommandCenter) action(w http.ResponseWriter, r *http.Request, action func(a App) error) {
	name := r.URL.Query().Get("app")
	app, found := cc.apps[name]
	if !found {
		renderError(w, http.StatusNotFound, fmt.Errorf("Application not found: %s", name))
		return
	}

	err := action(app)
	if err != nil {
		renderError(w, http.StatusInternalServerError, err)
	} else {
		http.Redirect(w, r, "/", http.StatusFound)
	}
}

func (cc *CommandCenter) register(a App) {
	if _, ok := cc.apps[a.Name()]; ok {
		return
	}
	cc.apps[a.Name()] = a
	cc.servers = append(cc.servers, a)
}

func (cc *CommandCenter) loadApps(c *Config) {
	cc.loadAliasApps(c.Aliases)
	cc.loadProcessApps(c.AppsDir)
	cc.loadWebServerApps(c.AppsDir)
}

func (cc *CommandCenter) loadAliasApps(aliases map[string]int) {
	for name, port := range aliases {
		cc.register(NewAliasApp(name, port))
	}
}

func (cc *CommandCenter) loadProcessApps(dir string) {
	procfiles, err := filepath.Glob(fmt.Sprintf("%s/*/Procfile", dir))
	if err != nil {
		log.Printf("An error occurred while searching for Procfiles at directory %s: %s\n", dir, err)
		return
	}

	for _, p := range procfiles {
		app, err := NewProcessApp(p)
		if err != nil {
			log.Printf("Unable to load application %s. Error: %s\n", p, err)
		} else {
			cc.register(app)
		}
	}
}

func (cc *CommandCenter) loadWebServerApps(dir string) {
	pages, err := filepath.Glob(fmt.Sprintf("%s/*/index.html", dir))
	if err != nil {
		log.Printf("An error occurred while searching for index.html at directory %s: %s\n", dir, err)
		return
	}

	for _, p := range pages {
		cc.register(NewWebServerApp(path.Dir(p)))
	}
}

const baseHTML = `
{{ define "root" }}

    <title>{{.Title}}</title>

{{.Title}}

			{{ template "body" . }}

{{ end }}
`

var pagesHTML = map[string]string{
	"index": `
	{{ define "body" }}
		{{$tld := .Tld}}

			{{range .Apps}}
				{{ if .Running}}

				{{ else }}

				{{ end }}

					{{.Name}}

					{{ if .Running}}
						Stop
					{{ else }}
						Start
					{{ end }}

			{{end}}

	{{ end }}`,
	"error": `
	{{ define "body" }}
		{{.Error}}
	{{ end }}`,
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"fmt"
	"os"
	"text/tabwriter"

	circleci "github.com/mtchavez/circlecigo"
	"github.com/urfave/cli"
)

// BuildCmd - build project and interact with builds
var BuildCmd = cli.Command{
	Name:        "build",
	Usage:       "build project and interact with builds",
	Flags:       buildFlags,
	Action:      buildProjectAction,
	Subcommands: buildSubcommands,
}

var buildFlags = []cli.Flag{
	userFlag,
	projectFlag,
}

var buildSubcommands = []cli.Command{
	{
		Name:   "cancel",
		Usage:  "cancel a specific build",
		Action: buildCancelAction,
		Flags:  append(buildFlags, buildNumFlag),
	},
}

func buildProjectAction(context *cli.Context) error {
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	build, resp := client.NewBuild(context.String("user"), context.String("project"), nil)
	if resp.Success() {
		padding := 1
		writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)
		fmt.Fprintln(writer, "Build\tBranch\tQueued\tURL")
		fmt.Fprintln(writer, fmt.Sprintf("%d\t%s\t%s\t%s\t", build.BuildNum, build.Branch, build.QueuedAt, build.BuildURL))
		writer.Flush()
		return nil
	}
	failed := errors.New("Failed to build")
	return failed
}

func buildCancelAction(context *cli.Context) error {
	token := context.GlobalString("token")
	buildNum := context.Int("num")
	client := circleci.NewClient(token)
	build, resp := client.CancelBuild(context.String("user"), context.String("project"), buildNum)
	if resp.Success() {
		padding := 1
		writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)
		fmt.Fprintln(writer, "Build\tBranch\tStatus\tURL")
		fmt.Fprintln(writer, fmt.Sprintf("%d\t%s\t%s\t%s\t", build.BuildNum, build.Branch, build.Status, build.BuildURL))
		writer.Flush()
		return nil
	}
	failed := errors.New("Failed to cancel build")
	return failed
}
<commit_msg>Add retry build command<commit_after>package commands

import (
	"errors"
	"fmt"
	"os"
	"text/tabwriter"

	circleci "github.com/mtchavez/circlecigo"
	"github.com/urfave/cli"
)

// BuildCmd - build project and interact with builds
var BuildCmd = cli.Command{
	Name:        "build",
	Usage:       "build project and interact with builds",
	Flags:       buildFlags,
	Action:      buildProjectAction,
	Subcommands: buildSubcommands,
}

var buildFlags = []cli.Flag{
	userFlag,
	projectFlag,
}

var buildSubcommands = []cli.Command{
	{
		Name:   "cancel",
		Usage:  "cancel a specific build",
		Action: buildCancelAction,
		Flags:  append(buildFlags, buildNumFlag),
	},
	{
		Name:   "retry",
		Usage:  "retry a specific build",
		Action: buildRetryAction,
		Flags:  append(buildFlags, buildNumFlag),
	},
}

func buildProjectAction(context *cli.Context) error {
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	build, resp := client.NewBuild(context.String("user"), context.String("project"), nil)
	if resp.Success() {
		padding := 1
		writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)
		fmt.Fprintln(writer, "Build\tBranch\tQueued\tURL")
		fmt.Fprintln(writer, fmt.Sprintf("%d\t%s\t%s\t%s\t", build.BuildNum, build.Branch, build.QueuedAt, build.BuildURL))
		writer.Flush()
		return nil
	}
	failed := errors.New("Failed to build")
	return failed
}

func buildCancelAction(context *cli.Context) error {
	token := context.GlobalString("token")
	buildNum := context.Int("num")
	client := circleci.NewClient(token)
	build, resp := client.CancelBuild(context.String("user"), context.String("project"), buildNum)
	if resp.Success() {
		padding := 1
		writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)
		fmt.Fprintln(writer, "Build\tBranch\tStatus\tURL")
		fmt.Fprintln(writer, fmt.Sprintf("%d\t%s\t%s\t%s\t", build.BuildNum, build.Branch, build.Status, build.BuildURL))
		writer.Flush()
		return nil
	}
	failed := errors.New("Failed to cancel build")
	return failed
}

func buildRetryAction(context *cli.Context) error {
	token := context.GlobalString("token")
	buildNum := context.Int("num")
	client := circleci.NewClient(token)
	build, resp := client.RetryBuild(context.String("user"), context.String("project"), buildNum)
	if resp.Success() {
		padding := 1
		writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)
		fmt.Fprintln(writer, "Build\tBranch\tStatus\tURL")
		fmt.Fprintln(writer, fmt.Sprintf("%d\t%s\t%s\t%s\t", build.BuildNum, build.Branch, build.Status, build.BuildURL))
		writer.Flush()
		return nil
	}
	failed := errors.New("Failed to retry build")
	return failed
}
<|endoftext|>


<commit_before>package serve

import (
	"errors"
	"fmt"
	"net/http"

	"github.com/cloudflare/cfssl/api"
	"github.com/cloudflare/cfssl/bundler"
	"github.com/cloudflare/cfssl/cli"
	"github.com/cloudflare/cfssl/cli/sign"
	"github.com/cloudflare/cfssl/log"
	"github.com/cloudflare/cfssl/ubiquity"
)

// Usage text of 'cfssl serve'
var serverUsageText = `cfssl serve -- set up a HTTP server handles CF SSL requests

Usage of serve:
        cfssl serve [-address address] [-ca cert] [-ca-bundle bundle] \
                    [-ca-key key] [-int-bundle bundle] [-port port] [-metadata file] \
                    [-remote remote_host] [-config config]

Flags:
`

// Flags used by 'cfssl serve'
var serverFlags = []string{"address", "port", "ca", "ca-key", "ca-bundle", "int-bundle", "int-dir", "metadata", "remote", "config"}

// registerHandlers instantiates various handlers and associate them to corresponding endpoints.
func registerHandlers(c cli.Config) error {
	log.Info("Setting up info endpoint")
	infoHandler, err := api.NewInfoHandlerFromPEM([]string{c.CAFile})
	if err != nil {
		log.Warningf("endpoint '/api/v1/cfssl/info' is disabled: %v", err)
	} else {
		http.Handle("/api/v1/cfssl/info", infoHandler)
	}

	log.Info("Setting up signer endpoint")
	s, err := sign.SignerFromConfig(c)
	if err != nil {
		log.Warningf("endpoint '/api/v1/cfssl/sign' is disabled: %v", err)
	} else {
		signHandler := api.NewSignHandlerFromSigner(s)
		http.Handle("/api/v1/cfssl/sign", signHandler)
	}

	log.Info("Setting up new cert endpoint")
	if err != nil {
		log.Errorf("endpoint '/api/v1/cfssl/newcert' is disabled")
	} else {
		newCertGenerator := api.NewCertGeneratorHandlerFromSigner(api.CSRValidate, s)
		http.Handle("/api/v1/cfssl/newcert", newCertGenerator)
	}

	log.Info("Setting up bundler endpoint")
	bundleHandler, err := api.NewBundleHandler(c.CABundleFile, c.IntBundleFile)
	if err != nil {
		log.Warningf("endpoint '/api/v1/cfssl/bundle' is disabled: %v", err)
	} else {
		http.Handle("/api/v1/cfssl/bundle", bundleHandler)
	}

	log.Info("Setting up CSR endpoint")
	generatorHandler, err := api.NewGeneratorHandler(api.CSRValidate)
	if err != nil {
		log.Errorf("Failed to set up CSR endpoint: %v", err)
		return err
	}
	http.Handle("/api/v1/cfssl/newkey", generatorHandler)

	log.Info("Setting up initial CA endpoint")
	http.Handle("/api/v1/cfssl/init_ca", api.NewInitCAHandler())

	log.Info("Handler set up complete.")
	return nil
}

// serverMain is the command line entry point to the API server. It sets up a
// new HTTP server to handle sign, bundle, and validate requests.
func serverMain(args []string, c cli.Config) error {
	// serve doesn't support arguments.
	if len(args) > 0 {
		return errors.New("argument is provided but not defined; please refer to the usage by flag -h")
	}

	bundler.IntermediateStash = c.IntDir
	ubiquity.LoadPlatforms(c.Metadata)

	err := registerHandlers(c)
	if err != nil {
		return err
	}

	addr := fmt.Sprintf("%s:%d", c.Address, c.Port)
	log.Info("Now listening on ", addr)
	return http.ListenAndServe(addr, nil)
}

// CLIServer assembles the definition of Command 'serve'
var Command = &cli.Command{UsageText: serverUsageText, Flags: serverFlags, Main: serverMain}
<commit_msg>Switch from sign to authsign when appropriate.<commit_after>package serve

import (
	"errors"
	"fmt"
	"net/http"

	"github.com/cloudflare/cfssl/api"
	"github.com/cloudflare/cfssl/bundler"
	"github.com/cloudflare/cfssl/cli"
	"github.com/cloudflare/cfssl/cli/sign"
	"github.com/cloudflare/cfssl/log"
	"github.com/cloudflare/cfssl/ubiquity"
)

// Usage text of 'cfssl serve'
var serverUsageText = `cfssl serve -- set up a HTTP server handles CF SSL requests

Usage of serve:
        cfssl serve [-address address] [-ca cert] [-ca-bundle bundle] \
                    [-ca-key key] [-int-bundle bundle] [-port port] [-metadata file] \
                    [-remote remote_host] [-config config]

Flags:
`

// Flags used by 'cfssl serve'
var serverFlags = []string{"address", "port", "ca", "ca-key", "ca-bundle", "int-bundle", "int-dir", "metadata", "remote", "config"}

// registerHandlers instantiates various handlers and associate them to corresponding endpoints.
func registerHandlers(c cli.Config) error {
	log.Info("Setting up info endpoint")
	infoHandler, err := api.NewInfoHandlerFromPEM([]string{c.CAFile})
	if err != nil {
		log.Warningf("endpoint '/api/v1/cfssl/info' is disabled: %v", err)
	} else {
		http.Handle("/api/v1/cfssl/info", infoHandler)
	}

	log.Info("Setting up signer endpoint")
	s, err := sign.SignerFromConfig(c)
	if err != nil {
		log.Warningf("endpoint '/api/v1/cfssl/sign' is disabled: %v", err)
	} else {
		if signHandler, err := api.NewAuthSignHandler(s); err == nil {
			log.Info("Assigning handler to /authsign")
			http.Handle("/api/v1/cfssl/authsign", signHandler)
		} else {
			log.Info("Assigning handler to /sign")
			signHandler := api.NewSignHandlerFromSigner(s)
			http.Handle("/api/v1/cfssl/sign", signHandler)
		}
	}

	log.Info("Setting up new cert endpoint")
	if err != nil {
		log.Errorf("endpoint '/api/v1/cfssl/newcert' is disabled")
	} else {
		newCertGenerator := api.NewCertGeneratorHandlerFromSigner(api.CSRValidate, s)
		http.Handle("/api/v1/cfssl/newcert", newCertGenerator)
	}

	log.Info("Setting up bundler endpoint")
	bundleHandler, err := api.NewBundleHandler(c.CABundleFile, c.IntBundleFile)
	if err != nil {
		log.Warningf("endpoint '/api/v1/cfssl/bundle' is disabled: %v", err)
	} else {
		http.Handle("/api/v1/cfssl/bundle", bundleHandler)
	}

	log.Info("Setting up CSR endpoint")
	generatorHandler, err := api.NewGeneratorHandler(api.CSRValidate)
	if err != nil {
		log.Errorf("Failed to set up CSR endpoint: %v", err)
		return err
	}
	http.Handle("/api/v1/cfssl/newkey", generatorHandler)

	log.Info("Setting up initial CA endpoint")
	http.Handle("/api/v1/cfssl/init_ca", api.NewInitCAHandler())

	log.Info("Handler set up complete.")
	return nil
}

// serverMain is the command line entry point to the API server. It sets up a
// new HTTP server to handle sign, bundle, and validate requests.
func serverMain(args []string, c cli.Config) error {
	// serve doesn't support arguments.
	if len(args) > 0 {
		return errors.New("argument is provided but not defined; please refer to the usage by flag -h")
	}

	bundler.IntermediateStash = c.IntDir
	ubiquity.LoadPlatforms(c.Metadata)

	err := registerHandlers(c)
	if err != nil {
		return err
	}

	addr := fmt.Sprintf("%s:%d", c.Address, c.Port)
	log.Info("Now listening on ", addr)
	return http.ListenAndServe(addr, nil)
}

// CLIServer assembles the definition of Command 'serve'
var Command = &cli.Command{UsageText: serverUsageText, Flags: serverFlags, Main: serverMain}
<|endoftext|>


<commit_before>/* This file is part of VoltDB.
 * Copyright (C) 2008-2018 VoltDB Inc.
 *
 * 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 VoltDB.  If not, see <http://www.gnu.org/licenses/>.
 */

package voltdbclient

import (
	"database/sql/driver"
	"encoding/binary"
	"encoding/json"
	"errors"
	"strconv"

	"sync"

	"github.com/spaolacci/murmur3"
)

var spool = sync.Pool{
	New: func() interface{} {
		return make([]byte, 8)
	},
}

type hashinator interface {
	getConfigurationType() string

	/**
	 * Given the type of the targeting partition parameter and an object,
	 * coerce the object to the correct type and hash it.
	 */
	getHashedPartitionForParameter(partitionParameterType int, partitionValue driver.Value) (hashedPartition int, err error)
}

type hashinatorElastic struct {
	// sorted array of token2partition pair
	tp Token2PartitionSlice
}

func newHashinatorElastic(hashConfigFormat int, cooked bool, hashConfig []byte) (h *hashinatorElastic, err error) {
	if hashConfigFormat != JSONFormat {
		return nil, errors.New("Only support JSON format hashconfig.")
	}

	if cooked {
		hashConfig, err = fromGzip(hashConfig)
		if err != nil {
			return nil, err
		}
	}

	h = &hashinatorElastic{}

	// unmarshall json
	if err = h.unmarshalJSONConfig(hashConfig); err != nil {
		return nil, err
	}

	return h, nil
}

func (h *hashinatorElastic) getConfigurationType() string {
	return Elastic
}

func (h *hashinatorElastic) getHashedPartitionForParameter(partitionParameterType int, v driver.Value) (hashedPartition int, err error) {
	if v == nil {
		return 0, nil
	}
	var value uint64
	switch v.(type) {
	case nullValue:
		return 0, nil
	case []byte:
		v1, _ := murmur3.Sum128(v.([]byte))
		hash := int(int(v1) >> 32)
		return SearchToken2Partitions(h.tp, hash), nil
	case string:
		v1, _ := murmur3.Sum128([]byte(v.(string)))
		hash := int(int(v1) >> 32)
		return SearchToken2Partitions(h.tp, hash), nil
	case byte:
		value = uint64(v.(byte))
	case int8:
		value = uint64(v.(int8))
	case int16:
		value = uint64(v.(int16))
	case int32:
		value = uint64(v.(int32))
	case int64:
		value = uint64(v.(int64))
	}
	buf := spool.Get().([]byte)
	defer spool.Put(buf)
	binary.LittleEndian.PutUint64(buf, value)
	return h.checkPertition(&buf)
}

func (h *hashinatorElastic) checkPertition(v *[]byte) (int, error) {
	v1, _ := murmur3.Sum128(*v)
	hash := int(int(v1) >> 32)
	return SearchToken2Partitions(h.tp, hash), nil
}

// until go 1.7, go lang won't support non-string type keys for (un-)marshal
// https://github.com/golang/go/commit/ffbd31e9f79ad8b6aaeceac1397678e237581064
// need to one more loop for the conversation
func (h *hashinatorElastic) unmarshalJSONConfig(bytes []byte) (err error) {

	// Unmarshal the string-keyed map
	sk := make(map[string]int)
	err = json.Unmarshal(bytes, &sk)
	if err != nil {
		return
	}

	h.tp = make(Token2PartitionSlice, 128)
	// Copy the values

	var ki int
	for k, v := range sk {
		ki, err = strconv.Atoi(k)
		if err != nil {
			return
		}
		h.tp = append(h.tp, token2Partition{ki, v})
	}
	h.tp.Sort()

	return
}
<commit_msg>Update hashinator.go<commit_after>/* This file is part of VoltDB.
 * Copyright (C) 2008-2018 VoltDB Inc.
 *
 * 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 VoltDB.  If not, see <http://www.gnu.org/licenses/>.
 */

package voltdbclient

import (
	"database/sql/driver"
	"encoding/binary"
	"encoding/json"
	"errors"
	"strconv"

	"sync"

	"github.com/spaolacci/murmur3"
)

var spool = sync.Pool{
	New: func() interface{} {
		return make([]byte, 8)
	},
}

type hashinator interface {
	getConfigurationType() string

	/**
	 * Given the type of the targeting partition parameter and an object,
	 * coerce the object to the correct type and hash it.
	 */
	getHashedPartitionForParameter(partitionParameterType int, partitionValue driver.Value) (hashedPartition int, err error)
}

type hashinatorElastic struct {
	// sorted array of token2partition pair
	tp Token2PartitionSlice
}

func newHashinatorElastic(hashConfigFormat int, cooked bool, hashConfig []byte) (h *hashinatorElastic, err error) {
	if hashConfigFormat != JSONFormat {
		return nil, errors.New("Only support JSON format hashconfig.")
	}

	if cooked {
		hashConfig, err = fromGzip(hashConfig)
		if err != nil {
			return nil, err
		}
	}

	h = &hashinatorElastic{}

	// unmarshall json
	if err = h.unmarshalJSONConfig(hashConfig); err != nil {
		return nil, err
	}

	return h, nil
}

func (h *hashinatorElastic) getConfigurationType() string {
	return Elastic
}

func (h *hashinatorElastic) getHashedPartitionForParameter(partitionParameterType int, v driver.Value) (hashedPartition int, err error) {
	if v == nil {
		return 0, nil
	}
	var value uint64
	switch v.(type) {
	case nullValue:
		return 0, nil
	case []byte:
		v1, _ := murmur3.Sum128(v.([]byte))
		hash := int(v1) >> 32
		return SearchToken2Partitions(h.tp, hash), nil
	case string:
		v1, _ := murmur3.Sum128([]byte(v.(string)))
		hash := int(v1) >> 32
		return SearchToken2Partitions(h.tp, hash), nil
	case byte:
		value = uint64(v.(byte))
	case int8:
		value = uint64(v.(int8))
	case int16:
		value = uint64(v.(int16))
	case int32:
		value = uint64(v.(int32))
	case int64:
		value = uint64(v.(int64))
	}
	buf := spool.Get().([]byte)
	defer spool.Put(buf)
	binary.LittleEndian.PutUint64(buf, value)
	return h.checkPertition(&buf)
}

func (h *hashinatorElastic) checkPertition(v *[]byte) (int, error) {
	v1, _ := murmur3.Sum128(*v)
	hash := int(v1) >> 32
	return SearchToken2Partitions(h.tp, hash), nil
}

// until go 1.7, go lang won't support non-string type keys for (un-)marshal
// https://github.com/golang/go/commit/ffbd31e9f79ad8b6aaeceac1397678e237581064
// need to one more loop for the conversation
func (h *hashinatorElastic) unmarshalJSONConfig(bytes []byte) (err error) {

	// Unmarshal the string-keyed map
	sk := make(map[string]int)
	err = json.Unmarshal(bytes, &sk)
	if err != nil {
		return
	}

	h.tp = make(Token2PartitionSlice, 128)
	// Copy the values

	var ki int
	for k, v := range sk {
		ki, err = strconv.Atoi(k)
		if err != nil {
			return
		}
		h.tp = append(h.tp, token2Partition{ki, v})
	}
	h.tp.Sort()

	return
}
<|endoftext|>


<commit_before>// Copyright ©2017 The go-lsst Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bench

import (
	"fmt"

	"github.com/go-lsst/ncs/drivers/m702"
)

const (
	ParamManualOverride = "0.08.005" // 0:sw, 1:manual-override
	ParamCmdReady       = "0.08.015"
	ParamHome           = "2.02.017"
	ParamModePos        = "2.02.011"
	ParamRPMs           = "0.20.022"
	ParamWritePos       = "3.70.000"
	ParamReadPos        = "0.18.002"
	ParamTemp0          = "0.07.004"
	ParamTemp1          = "0.07.005"
	ParamTemp2          = "0.07.006"
	ParamTemp3          = "0.07.034"

	ParamHWSafety = "0.08.040" // 0:OK, 1:HW-Safety ON
)

var (
	ErrMotorOffline     = FcsError{1, "fcs: motor OFFLINE"}
	ErrMotorHWLock      = FcsError{2, "fcs: motor HW-safety enabled"}
	ErrMotorManual      = FcsError{3, "fcs: motor manual-mode enabled"}
	ErrOpNotSupported   = FcsError{20, "fcs: operation not supported"}
	ErrInvalidReq       = FcsError{102, "fcs: invalid request"}
	ErrInvalidMotorName = FcsError{200, "fcs: invalid motor name"}
)

type FcsError struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

func (e FcsError) Error() string {
	return fmt.Sprintf("[%03d]: %s", e.Code, e.Msg)
}

func (e FcsError) String() string {
	return e.Error()
}

type Bench struct {
	Motor struct {
		X Motor
		Z Motor
	}
}

// Motor is the interface to the FCS testbench motors
type Motor interface {
	//State() m702.FSM
	ReadParam(p *m702.Parameter) error
	WriteParam(p m702.Parameter) error
}

func NewMotorFrom(m m702.Motor) Motor {
	return &m
}

type MotorInfos struct {
	Motor  string            `json:"motor"` // x,z
	Online bool              `json:"online"`
	Status string            `json:"status"` // N/A,manual,hw-safety,ready
	Mode   string            `json:"mode"`   // N/A,ready,home,position
	RPMs   int               `json:"rpms"`
	Angle  int               `json:"angle"`
	Temps  [4]float64        `json:"temps"`
	Histos map[string]string `json:"histos"`
	Webcam string            `json:"webcam"`
}
<commit_msg>bench: define params for master/slave monitoring<commit_after>// Copyright ©2017 The go-lsst Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bench

import (
	"fmt"

	"github.com/go-lsst/ncs/drivers/m702"
)

const (
	ParamManualOverride = "0.08.005" // 0:sw, 1:manual-override
	ParamCmdReady       = "0.08.015"
	ParamHome           = "2.02.017"
	ParamModePos        = "2.02.011"
	ParamRPMs           = "0.20.022"
	ParamWritePos       = "3.70.000"
	ParamReadPos        = "0.18.002"
	ParamTemp0          = "0.07.004"
	ParamTemp1          = "0.07.005"
	ParamTemp2          = "0.07.006"
	ParamTemp3          = "0.07.034"

	ParamHWSafety = "0.08.040" // 0:OK, 1:HW-Safety ON

	// Number of messages exchanged b/w master and slave.
	// This number needs to be >0 on the slave to indicate the connection
	// is correctly established.
	ParamMasterSlaveExchange = "4.10.004"

	// Ratio load (in %) on the master/slave axis.
	ParamMasterSlaveLoadRatio = "4.020"
)

var (
	ErrMotorOffline     = FcsError{1, "fcs: motor OFFLINE"}
	ErrMotorHWLock      = FcsError{2, "fcs: motor HW-safety enabled"}
	ErrMotorManual      = FcsError{3, "fcs: motor manual-mode enabled"}
	ErrOpNotSupported   = FcsError{20, "fcs: operation not supported"}
	ErrInvalidReq       = FcsError{102, "fcs: invalid request"}
	ErrInvalidMotorName = FcsError{200, "fcs: invalid motor name"}
)

type FcsError struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

func (e FcsError) Error() string {
	return fmt.Sprintf("[%03d]: %s", e.Code, e.Msg)
}

func (e FcsError) String() string {
	return e.Error()
}

type Bench struct {
	Motor struct {
		X Motor
		Z Motor
	}
}

// Motor is the interface to the FCS testbench motors
type Motor interface {
	//State() m702.FSM
	ReadParam(p *m702.Parameter) error
	WriteParam(p m702.Parameter) error
}

func NewMotorFrom(m m702.Motor) Motor {
	return &m
}

type MotorInfos struct {
	Motor  string            `json:"motor"` // x,z
	Online bool              `json:"online"`
	Status string            `json:"status"` // N/A,manual,hw-safety,ready
	Mode   string            `json:"mode"`   // N/A,ready,home,position
	RPMs   int               `json:"rpms"`
	Angle  int               `json:"angle"`
	Temps  [4]float64        `json:"temps"`
	Histos map[string]string `json:"histos"`
	Webcam string            `json:"webcam"`
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/fzzbt/radix/redis"
	"log"
	"os"
	"runtime"
	"runtime/pprof"
	"strings"
	"time"
)

var connections *int = flag.Int("c", 50, "number of connections")
var requests *int = flag.Int("n", 10000, "number of request")
var dsize *int = flag.Int("d", 3, "data size")
var cpuprof *string = flag.String("cpuprof", "", "filename for cpuprof")
var gomaxprocs *int = flag.Int("p", 8, "GOMAXPROCS value")

// handleReplyError prints an error message for the given reply.
func handleReplyError(rep *redis.Reply) {
	if rep.Err != nil {
		log.Println("redis: " + rep.Err.Error())
	} else {
		log.Println("redis: unexpected reply type")
	}
}

// benchmark benchmarks the given function.
func benchmark(data string, handle func(string, *redis.Client, chan struct{})) time.Duration {
	c, err := redis.NewClient(redis.Configuration{
		Database: 8,
		Path:     "/tmp/redis.sock",
		PoolCapacity: *connections,
	})

	if err != nil {
		log.Fatalf("NewClient failed: %s\n", err)
	}

	defer c.Close()

	rep := c.Flushdb()
	if rep.Err != nil {
		handleReplyError(rep)
		os.Exit(1)
	}

	ch := make(chan struct{})
	start := time.Now()

	for i := 0; i < *connections; i++ {
		go handle(data, c, ch)
	}

	for i := 0; i < *requests; i++ {
		ch <- struct{}{}
	}

	dur := time.Now().Sub(start)
	return dur
}

func run(name string, handle func(string, *redis.Client, chan struct{}), data string) {
	fmt.Printf("===== %s =====\n", strings.ToUpper(name))
	duration := benchmark(data, handle)
	rps := float64(*requests) / duration.Seconds()
	fmt.Println("Requests per second: ", rps)
	fmt.Println("Duration: ", duration)
	fmt.Println()
}

func main() {
	var data string

	flag.Parse()
	runtime.GOMAXPROCS(*gomaxprocs)

	if *cpuprof != "" {
		f, err := os.Create(*cpuprof)
		if err != nil {
			log.Fatalln(err)
		}

		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	for i := 0; i < *dsize; i++ {
		data += "x"
	}

	fmt.Printf(
		"Connections: %d, Requests: %d, Payload: %d bytes, GOMAXPROCS: %d\n\n",
		*connections,
		*requests,
		*dsize,
		*gomaxprocs)

	args := flag.Args()
	if len(args) == 0 {
		// run all tests by default
		for i, name := range testNames {
			run(name, testHandles[i], data)
		}
	} else {
		for i, name := range testNames {
			for _, arg := range args {
				if arg == name {
					run(name, testHandles[i], data)
				}
			}
		}
	}
}
<commit_msg>change default data size to 2.<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/fzzbt/radix/redis"
	"log"
	"os"
	"runtime"
	"runtime/pprof"
	"strings"
	"time"
)

var connections *int = flag.Int("c", 50, "number of connections")
var requests *int = flag.Int("n", 10000, "number of request")
var dsize *int = flag.Int("d", 2, "data size")
var cpuprof *string = flag.String("cpuprof", "", "filename for cpuprof")
var gomaxprocs *int = flag.Int("p", 8, "GOMAXPROCS value")

// handleReplyError prints an error message for the given reply.
func handleReplyError(rep *redis.Reply) {
	if rep.Err != nil {
		log.Println("redis: " + rep.Err.Error())
	} else {
		log.Println("redis: unexpected reply type")
	}
}

// benchmark benchmarks the given function.
func benchmark(data string, handle func(string, *redis.Client, chan struct{})) time.Duration {
	c, err := redis.NewClient(redis.Configuration{
		Database: 8,
		PoolCapacity: *connections,
	})

	if err != nil {
		log.Fatalf("NewClient failed: %s\n", err)
	}

	defer c.Close()

	rep := c.Flushdb()
	if rep.Err != nil {
		handleReplyError(rep)
		os.Exit(1)
	}

	ch := make(chan struct{})
	start := time.Now()

	for i := 0; i < *connections; i++ {
		go handle(data, c, ch)
	}

	for i := 0; i < *requests; i++ {
		ch <- struct{}{}
	}

	dur := time.Now().Sub(start)
	return dur
}

func run(name string, handle func(string, *redis.Client, chan struct{}), data string) {
	fmt.Printf("===== %s =====\n", strings.ToUpper(name))
	duration := benchmark(data, handle)
	rps := float64(*requests) / duration.Seconds()
	fmt.Println("Requests per second: ", rps)
	fmt.Println("Duration: ", duration)
	fmt.Println()
}

func main() {
	var data string

	flag.Parse()
	runtime.GOMAXPROCS(*gomaxprocs)

	if *cpuprof != "" {
		f, err := os.Create(*cpuprof)
		if err != nil {
			log.Fatalln(err)
		}

		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	for i := 0; i < *dsize; i++ {
		data += "x"
	}

	fmt.Printf(
		"Connections: %d, Requests: %d, Payload: %d bytes, GOMAXPROCS: %d\n\n",
		*connections,
		*requests,
		*dsize,
		*gomaxprocs)

	args := flag.Args()
	if len(args) == 0 {
		// run all tests by default
		for i, name := range testNames {
			run(name, testHandles[i], data)
		}
	} else {
		for i, name := range testNames {
			for _, arg := range args {
				if arg == name {
					run(name, testHandles[i], data)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015-2017 Matt Holt and caddy contributors
// Copyright 2017 Kirill Danshin and gramework contributors

package gramework

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
)

const challengeBasePath = "/.well-known/acme-challenge"

// HTTPChallengeHandler proxies challenge requests to ACME client if the
// request path starts with challengeBasePath. It returns true if it
// handled the request and no more needs to be done; it returns false
// if this call was a no-op and the request still needs handling.
func HTTPChallengeHandler(w http.ResponseWriter, r *http.Request, listenHost, altPort string) bool {
	if !strings.HasPrefix(r.URL.Path, challengeBasePath) {
		return false
	}
	if DisableHTTPChallenge {
		return false
	}
	if !namesObtaining.Has(r.Host) {
		return false
	}

	scheme := "http"
	if r.TLS != nil {
		scheme = "https"
	}

	if listenHost == "" {
		listenHost = "localhost"
	}

	upstream, err := url.Parse(fmt.Sprintf("%s://%s:%s", scheme, listenHost, altPort))
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Printf("[ERROR] ACME proxy handler: %v", err)
		return true
	}

	proxy := httputil.NewSingleHostReverseProxy(upstream)
	proxy.Transport = &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	proxy.ServeHTTP(w, r)

	return true
}
<commit_msg>nettls: more on integration<commit_after>// Copyright 2015-2017 Matt Holt and caddy contributors
// Copyright 2017 Kirill Danshin and gramework contributors

package gramework

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	"github.com/valyala/fasthttp"
)

const challengeBasePath = "/.well-known/acme-challenge"

// HTTPChallengeHandler proxies challenge requests to ACME client if the
// request path starts with challengeBasePath. It returns true if it
// handled the request and no more needs to be done; it returns false
// if this call was a no-op and the request still needs handling.
func HTTPChallengeHandler(ctx *Context, listenHost, altPort string) bool {
	if !strings.HasPrefix(string(ctx.URI().Path()), challengeBasePath) {
		return false
	}
	if DisableHTTPChallenge {
		return false
	}
	if !namesObtaining.Has(string(ctx.Host())) {
		return false
	}

	scheme := "http"
	if ctx.IsTLS() {
		scheme = "https"
	}

	if listenHost == "" {
		listenHost = "localhost"
	}

	upstream, err := url.Parse(fmt.Sprintf("%s://%s:%s", scheme, listenHost, altPort))
	if err != nil {
		ctx.Err500()
		log.Printf("[ERROR] ACME proxy handler: %v", err)
		return true
	}

	proxy := httputil.NewSingleHostReverseProxy(upstream)
	proxy.Transport = &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	ReverseProxyHandler(ctx, &fasthttp.HostClient{
		Addr: upstream.String(),
	}, upstream)

	return true
}

func ReverseProxyHandler(ctx *Context, proxyClient *fasthttp.HostClient, upstream *url.URL) {
	req := &ctx.Request
	resp := &ctx.Response
	prepareRequest(req, upstream)
	if err := proxyClient.Do(req, resp); err != nil {
		ctx.Logger.Errorf("error when proxying the request: %s", err)
	}
	postprocessResponse(resp)
}

func prepareRequest(req *fasthttp.Request, upstream *url.URL) {
	// do not proxy "Connection" header.
	req.Header.Del("Connection")
	req.SetHost(upstream.Host)
	// strip other unneeded headers.

	// alter other request params before sending them to upstream host
}

func postprocessResponse(resp *fasthttp.Response) {
	// do not proxy "Connection" header
	resp.Header.Del("Connection")

	// strip other unneeded headers

	// alter other response data if needed
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"strings"

	"github.com/github/hub/github"
	"github.com/github/hub/ui"
	"github.com/github/hub/utils"
)

var (
	cmdIssue = &Command{
		Run:   issue,
		Usage: "issue [-a <ASSIGNEE>]",
		Long: `List open issues for the current GitHub project.

## Options:
	-a, --assignee <ASSIGNEE>
		Display only issues assigned to <ASSIGNEE>
`,
	}

	cmdCreateIssue = &Command{
		Key:   "create",
		Run:   createIssue,
		Usage: "issue create [-m <MESSAGE>|-f <FILE>] [-l <LABELS>]",
		Long: `File an issue for the current GitHub project.

## Options:
	-m, --message <MESSAGE>
		Use the first line of <MESSAGE> as issue title, and the rest as issue description.

	-f, --file <FILE>
		Read the issue title and description from <FILE>.

	-l, --labels <LABELS>
		Add a comma-separated list of labels to this issue.
`,
	}

	flagIssueAssignee,
	flagIssueMessage,
	flagIssueFile string

	flagIssueLabels listFlag
)

func init() {
	cmdCreateIssue.Flag.StringVarP(&flagIssueMessage, "message", "m", "", "MESSAGE")
	cmdCreateIssue.Flag.StringVarP(&flagIssueFile, "file", "f", "", "FILE")
	cmdCreateIssue.Flag.VarP(&flagIssueLabels, "label", "l", "LABEL")

	cmdIssue.Flag.StringVarP(&flagIssueAssignee, "assignee", "a", "", "ASSIGNEE")

	cmdIssue.Use(cmdCreateIssue)
	CmdRunner.Use(cmdIssue)
}

/*
  $ hub issue
*/
func issue(cmd *Command, args *Args) {
	runInLocalRepo(func(localRepo *github.GitHubRepo, project *github.Project, gh *github.Client) {
		if args.Noop {
			ui.Printf("Would request list of issues for %s\n", project)
		} else {
			issues, err := gh.Issues(project)
			utils.Check(err)
			for _, issue := range issues {
				var url string
				// use the pull request URL if we have one
				if issue.PullRequest.HTMLURL != "" {
					url = issue.PullRequest.HTMLURL
				} else {
					url = issue.HTMLURL
				}

				if flagIssueAssignee == "" ||
					strings.EqualFold(issue.Assignee.Login, flagIssueAssignee) {
					// "nobody" should have more than 1 million github issues
					ui.Printf("% 7d] %s ( %s )\n", issue.Number, issue.Title, url)
				}
			}
		}
	})
}

func createIssue(cmd *Command, args *Args) {
	runInLocalRepo(func(localRepo *github.GitHubRepo, project *github.Project, gh *github.Client) {
		if args.Noop {
			ui.Printf("Would create an issue for %s\n", project)
		} else {
			title, body, err := getTitleAndBodyFromFlags(flagIssueMessage, flagIssueFile)
			utils.Check(err)

			if title == "" {
				title, body, err = writeIssueTitleAndBody(project)
				utils.Check(err)
			}

			issue, err := gh.CreateIssue(project, title, body, flagIssueLabels)
			utils.Check(err)

			ui.Println(issue.HTMLURL)
		}
	})
}

func writeIssueTitleAndBody(project *github.Project) (string, string, error) {
	message := `
# Creating issue for %s.
#
# Write a message for this issue. The first block of
# text is the title and the rest is the description.
`
	message = fmt.Sprintf(message, project.Name)

	editor, err := github.NewEditor("ISSUE", "issue", message)
	if err != nil {
		return "", "", err
	}

	defer editor.DeleteFile()

	return editor.EditTitleAndBody()
}
<commit_msg>Support issue template<commit_after>package commands

import (
	"fmt"
	"strings"

	"github.com/github/hub/github"
	"github.com/github/hub/ui"
	"github.com/github/hub/utils"
)

var (
	cmdIssue = &Command{
		Run:   issue,
		Usage: "issue [-a <ASSIGNEE>]",
		Long: `List open issues for the current GitHub project.

## Options:
	-a, --assignee <ASSIGNEE>
		Display only issues assigned to <ASSIGNEE>
`,
	}

	cmdCreateIssue = &Command{
		Key:   "create",
		Run:   createIssue,
		Usage: "issue create [-m <MESSAGE>|-f <FILE>] [-l <LABELS>]",
		Long: `File an issue for the current GitHub project.

## Options:
	-m, --message <MESSAGE>
		Use the first line of <MESSAGE> as issue title, and the rest as issue description.

	-f, --file <FILE>
		Read the issue title and description from <FILE>.

	-l, --labels <LABELS>
		Add a comma-separated list of labels to this issue.
`,
	}

	flagIssueAssignee,
	flagIssueMessage,
	flagIssueFile string

	flagIssueLabels listFlag
)

func init() {
	cmdCreateIssue.Flag.StringVarP(&flagIssueMessage, "message", "m", "", "MESSAGE")
	cmdCreateIssue.Flag.StringVarP(&flagIssueFile, "file", "f", "", "FILE")
	cmdCreateIssue.Flag.VarP(&flagIssueLabels, "label", "l", "LABEL")

	cmdIssue.Flag.StringVarP(&flagIssueAssignee, "assignee", "a", "", "ASSIGNEE")

	cmdIssue.Use(cmdCreateIssue)
	CmdRunner.Use(cmdIssue)
}

/*
  $ hub issue
*/
func issue(cmd *Command, args *Args) {
	runInLocalRepo(func(localRepo *github.GitHubRepo, project *github.Project, gh *github.Client) {
		if args.Noop {
			ui.Printf("Would request list of issues for %s\n", project)
		} else {
			issues, err := gh.Issues(project)
			utils.Check(err)
			for _, issue := range issues {
				var url string
				// use the pull request URL if we have one
				if issue.PullRequest.HTMLURL != "" {
					url = issue.PullRequest.HTMLURL
				} else {
					url = issue.HTMLURL
				}

				if flagIssueAssignee == "" ||
					strings.EqualFold(issue.Assignee.Login, flagIssueAssignee) {
					// "nobody" should have more than 1 million github issues
					ui.Printf("% 7d] %s ( %s )\n", issue.Number, issue.Title, url)
				}
			}
		}
	})
}

func createIssue(cmd *Command, args *Args) {
	runInLocalRepo(func(localRepo *github.GitHubRepo, project *github.Project, gh *github.Client) {
		if args.Noop {
			ui.Printf("Would create an issue for %s\n", project)
		} else {
			title, body, err := getTitleAndBodyFromFlags(flagIssueMessage, flagIssueFile)
			utils.Check(err)

			if title == "" {
				title, body, err = writeIssueTitleAndBody(project)
				utils.Check(err)
			}

			issue, err := gh.CreateIssue(project, title, body, flagIssueLabels)
			utils.Check(err)

			ui.Println(issue.HTMLURL)
		}
	})
}

func writeIssueTitleAndBody(project *github.Project) (string, string, error) {
	message := `%s
# Creating issue for %s.
#
# Write a message for this issue. The first block of
# text is the title and the rest is the description.
`

	if template := github.GetIssueTempalte(); template == "" {
		message = fmt.Sprintf(message, "", project.Name)
	} else {
		message = fmt.Sprintf(message, template, project.Name)
	}

	editor, err := github.NewEditor("ISSUE", "issue", message)
	if err != nil {
		return "", "", err
	}

	defer editor.DeleteFile()

	return editor.EditTitleAndBody()
}
<|endoftext|>