<commit_before>// Copyright 2014 The go-github 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 github

import (
	"fmt"
	"time"
)

// IssueEvent represents an event that occurred around an Issue or Pull Request.
type IssueEvent struct {
	ID  *int    `json:"id,omitempty"`
	URL *string `json:"url,omitempty"`

	// The User that generated this event.
	Actor *User `json:"actor,omitempty"`

	// Event identifies the actual type of Event that occurred.  Possible
	// values are:
	//
	//     closed
	//       The issue was closed by the actor. When the commit_id is
	//       present, it identifies the commit that closed the issue using
	//       “closes / fixes #NN” syntax.
	//
	//     reopened
	//       The issue was reopened by the actor.
	//
	//     subscribed
	//       The actor subscribed to receive notifications for an issue.
	//
	//     merged
	//       The issue was merged by the actor. The commit_id attribute is the SHA1 of the HEAD commit that was merged.
	//
	//     referenced
	//       The issue was referenced from a commit message. The commit_id attribute is the commit SHA1 of where that happened.
	//
	//     mentioned
	//       The actor was @mentioned in an issue body.
	//
	//     assigned
	//       The issue was assigned to the actor.
	//
	//     head_ref_deleted
	//       The pull request’s branch was deleted.
	//
	//     head_ref_restored
	//       The pull request’s branch was restored.
	//
	//     labeled
	//       A label was added.
	//
	//     renamed
	//       The issue title was changed.
	Event *string `json:"event,omitempty"`

	// The SHA of the commit that referenced this commit, if applicable.
	CommitID *string `json:"commit_id,omitempty"`

	CreatedAt *time.Time `json:"created_at,omitempty"`
	Issue     *Issue     `json:"issue,omitempty"`

	// Only present on 'labeled' events.
	Label *Label `json:"label,omitempty"`

	// Only present on 'renamed' events.
	Rename *Rename `json:"rename,omitempty"`
}

// ListIssueEvents lists events for the specified issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue
func (s *IssuesService) ListIssueEvents(owner, repo string, number int, opt *ListOptions) ([]IssueEvent, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

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

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

	return events, resp, err
}

// ListRepositoryEvents lists events for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository
func (s *IssuesService) ListRepositoryEvents(owner, repo string, opt *ListOptions) ([]IssueEvent, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

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

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

	return events, resp, err
}

// GetEvent returns the specified issue event.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event
func (s *IssuesService) GetEvent(owner, repo string, id int) (*IssueEvent, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id)

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

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

	return event, resp, err
}

// Rename contains details for 'renamed' events.
type Rename struct {
	From *string `json:"from,omitempty"`
	To   *string `json:"to,omitempty"`
}

func (r Rename) String() string {
	return Stringify(r)
}
<commit_msg>github: document and add support for all IssueEvent types<commit_after>// Copyright 2014 The go-github 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 github

import (
	"fmt"
	"time"
)

// IssueEvent represents an event that occurred around an Issue or Pull Request.
type IssueEvent struct {
	ID  *int    `json:"id,omitempty"`
	URL *string `json:"url,omitempty"`

	// The User that generated this event.
	Actor *User `json:"actor,omitempty"`

	// Event identifies the actual type of Event that occurred.  Possible
	// values are:
	//
	//     closed
	//       The Actor closed the issue.
	//       If the issue was closed by commit message, CommitID holds the SHA1 hash of the commit.
	//
	//     merged
	//       The Actor merged into master a branch containing a commit mentioning the issue.
	//       CommitID holds the SHA1 of the merge commit.
	//
	//     referenced
	//       The Actor committed to master a commit mentioning the issue in its commit message.
	//       CommitID holds the SHA1 of the commit.
	//
	//     reopened, locked, unlocked
	//       The Actor did that to the issue.
	//
	//     renamed
	//       The Actor changed the issue title from Rename.From to Rename.To.
	//
	//     mentioned
	//       Someone unspecified @mentioned the Actor [sic] in an issue comment body.
	//
	//     assigned, unassigned
	//       The Actor assigned the issue to or removed the assignment from the Assignee.
	//
	//     labeled, unlabeled
	//       The Actor added or removed the Label from the issue.
	//
	//     milestoned, demilestoned
	//       The Actor added or removed the issue from the Milestone.
	//
	//     subscribed, unsubscribed
	//       The Actor subscribed to or unsubscribed from notifications for an issue.
	//
	//     head_ref_deleted, head_ref_restored
	//       The pull request’s branch was deleted or restored.
	//
	Event *string `json:"event,omitempty"`

	CreatedAt *time.Time `json:"created_at,omitempty"`
	Issue     *Issue     `json:"issue,omitempty"`

	// Only present on certain events; see above.
	Assignee  *User      `json:"assignee,omitempty"`
	CommitID  *string    `json:"commit_id,omitempty"`
	Milestone *Milestone `json:"milestone,omitempty"`
	Label     *Label     `json:"label,omitempty"`
	Rename    *Rename    `json:"rename,omitempty"`
}

// ListIssueEvents lists events for the specified issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue
func (s *IssuesService) ListIssueEvents(owner, repo string, number int, opt *ListOptions) ([]IssueEvent, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

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

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

	return events, resp, err
}

// ListRepositoryEvents lists events for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository
func (s *IssuesService) ListRepositoryEvents(owner, repo string, opt *ListOptions) ([]IssueEvent, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

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

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

	return events, resp, err
}

// GetEvent returns the specified issue event.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event
func (s *IssuesService) GetEvent(owner, repo string, id int) (*IssueEvent, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id)

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

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

	return event, resp, err
}

// Rename contains details for 'renamed' events.
type Rename struct {
	From *string `json:"from,omitempty"`
	To   *string `json:"to,omitempty"`
}

func (r Rename) String() string {
	return Stringify(r)
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-github 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 github

import (
	"bytes"
	"context"
	"fmt"
	"time"
)

// RepositoryCommit represents a commit in a repo.
// Note that it's wrapping a Commit, so author/committer information is in two places,
// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details".
type RepositoryCommit struct {
	SHA         *string  `json:"sha,omitempty"`
	Commit      *Commit  `json:"commit,omitempty"`
	Author      *User    `json:"author,omitempty"`
	Committer   *User    `json:"committer,omitempty"`
	Parents     []Commit `json:"parents,omitempty"`
	HTMLURL     *string  `json:"html_url,omitempty"`
	URL         *string  `json:"url,omitempty"`
	CommentsURL *string  `json:"comments_url,omitempty"`

	// Details about how many changes were made in this commit. Only filled in during GetCommit!
	Stats *CommitStats `json:"stats,omitempty"`
	// Details about which files, and how this commit touched. Only filled in during GetCommit!
	Files []CommitFile `json:"files,omitempty"`
}

func (r RepositoryCommit) String() string {
	return Stringify(r)
}

// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit or GistCommit.
type CommitStats struct {
	Additions *int `json:"additions,omitempty"`
	Deletions *int `json:"deletions,omitempty"`
	Total     *int `json:"total,omitempty"`
}

func (c CommitStats) String() string {
	return Stringify(c)
}

// CommitFile represents a file modified in a commit.
type CommitFile struct {
	SHA         *string `json:"sha,omitempty"`
	Filename    *string `json:"filename,omitempty"`
	Additions   *int    `json:"additions,omitempty"`
	Deletions   *int    `json:"deletions,omitempty"`
	Changes     *int    `json:"changes,omitempty"`
	Status      *string `json:"status,omitempty"`
	Patch       *string `json:"patch,omitempty"`
	BlobURL     *string `json:"blob_url,omitempty"`
	RawURL      *string `json:"raw_url,omitempty"`
	ContentsURL *string `json:"contents_url,omitempty"`
}

func (c CommitFile) String() string {
	return Stringify(c)
}

// CommitsComparison is the result of comparing two commits.
// See CompareCommits() for details.
type CommitsComparison struct {
	BaseCommit      *RepositoryCommit `json:"base_commit,omitempty"`
	MergeBaseCommit *RepositoryCommit `json:"merge_base_commit,omitempty"`

	// Head can be 'behind' or 'ahead'
	Status       *string `json:"status,omitempty"`
	AheadBy      *int    `json:"ahead_by,omitempty"`
	BehindBy     *int    `json:"behind_by,omitempty"`
	TotalCommits *int    `json:"total_commits,omitempty"`

	Commits []RepositoryCommit `json:"commits,omitempty"`

	Files []CommitFile `json:"files,omitempty"`

	HTMLURL      *string `json:"html_url,omitempty"`
	PermalinkURL *string `json:"permalink_url,omitempty"`
	DiffURL      *string `json:"diff_url,omitempty"`
	PatchURL     *string `json:"patch_url,omitempty"`
	URL          *string `json:"url,omitempty"` // API URL.
}

func (c CommitsComparison) String() string {
	return Stringify(c)
}

// CommitsListOptions specifies the optional parameters to the
// RepositoriesService.ListCommits method.
type CommitsListOptions struct {
	// SHA or branch to start listing Commits from.
	SHA string `url:"sha,omitempty"`

	// Path that should be touched by the returned Commits.
	Path string `url:"path,omitempty"`

	// Author of by which to filter Commits.
	Author string `url:"author,omitempty"`

	// Since when should Commits be included in the response.
	Since time.Time `url:"since,omitempty"`

	// Until when should Commits be included in the response.
	Until time.Time `url:"until,omitempty"`

	ListOptions
}

// ListCommits lists the commits of a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#list
func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opt *CommitsListOptions) ([]*RepositoryCommit, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits", owner, repo)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeGitSigningPreview)

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

	return commits, resp, nil
}

// GetCommit fetches the specified commit, including all details about it.
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-a-single-commit
// See also: https://developer.github.com/v3/git/commits/#get-a-single-commit provides the same functionality
func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string) (*RepositoryCommit, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha)

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeGitSigningPreview)

	commit := new(RepositoryCommit)
	resp, err := s.client.Do(ctx, req, commit)
	if err != nil {
		return nil, resp, err
	}

	return commit, resp, nil
}

// GetCommitRaw fetches the specified commit in raw (diff or patch) format.
func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner string, repo string, sha string, opt RawOptions) (string, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return "", nil, err
	}

	switch opt.Type {
	case Diff:
		req.Header.Set("Accept", mediaTypeV3Diff)
	case Patch:
		req.Header.Set("Accept", mediaTypeV3Patch)
	default:
		return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type)
	}

	var buf bytes.Buffer
	resp, err := s.client.Do(ctx, req, &buf)
	if err != nil {
		return "", resp, err
	}

	return buf.String(), resp, nil
}

// GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is
// supplied and no new commits have occurred, a 304 Unmodified response is returned.
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, ref, lastSHA string) (string, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, ref)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return "", nil, err
	}
	if lastSHA != "" {
		req.Header.Set("If-None-Match", `"`+lastSHA+`"`)
	}

	req.Header.Set("Accept", mediaTypeV3SHA)

	var buf bytes.Buffer
	resp, err := s.client.Do(ctx, req, &buf)
	if err != nil {
		return "", resp, err
	}

	return buf.String(), resp, nil
}

// CompareCommits compares a range of commits with each other.
// todo: support media formats - https://github.com/google/go-github/issues/6
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/index.html#compare-two-commits
func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string) (*CommitsComparison, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head)

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

	comp := new(CommitsComparison)
	resp, err := s.client.Do(ctx, req, comp)
	if err != nil {
		return nil, resp, err
	}

	return comp, resp, nil
}
<commit_msg>Remove unnecessary index.html from a #compare-two-commits url (#955)<commit_after>// Copyright 2013 The go-github 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 github

import (
	"bytes"
	"context"
	"fmt"
	"time"
)

// RepositoryCommit represents a commit in a repo.
// Note that it's wrapping a Commit, so author/committer information is in two places,
// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details".
type RepositoryCommit struct {
	SHA         *string  `json:"sha,omitempty"`
	Commit      *Commit  `json:"commit,omitempty"`
	Author      *User    `json:"author,omitempty"`
	Committer   *User    `json:"committer,omitempty"`
	Parents     []Commit `json:"parents,omitempty"`
	HTMLURL     *string  `json:"html_url,omitempty"`
	URL         *string  `json:"url,omitempty"`
	CommentsURL *string  `json:"comments_url,omitempty"`

	// Details about how many changes were made in this commit. Only filled in during GetCommit!
	Stats *CommitStats `json:"stats,omitempty"`
	// Details about which files, and how this commit touched. Only filled in during GetCommit!
	Files []CommitFile `json:"files,omitempty"`
}

func (r RepositoryCommit) String() string {
	return Stringify(r)
}

// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit or GistCommit.
type CommitStats struct {
	Additions *int `json:"additions,omitempty"`
	Deletions *int `json:"deletions,omitempty"`
	Total     *int `json:"total,omitempty"`
}

func (c CommitStats) String() string {
	return Stringify(c)
}

// CommitFile represents a file modified in a commit.
type CommitFile struct {
	SHA         *string `json:"sha,omitempty"`
	Filename    *string `json:"filename,omitempty"`
	Additions   *int    `json:"additions,omitempty"`
	Deletions   *int    `json:"deletions,omitempty"`
	Changes     *int    `json:"changes,omitempty"`
	Status      *string `json:"status,omitempty"`
	Patch       *string `json:"patch,omitempty"`
	BlobURL     *string `json:"blob_url,omitempty"`
	RawURL      *string `json:"raw_url,omitempty"`
	ContentsURL *string `json:"contents_url,omitempty"`
}

func (c CommitFile) String() string {
	return Stringify(c)
}

// CommitsComparison is the result of comparing two commits.
// See CompareCommits() for details.
type CommitsComparison struct {
	BaseCommit      *RepositoryCommit `json:"base_commit,omitempty"`
	MergeBaseCommit *RepositoryCommit `json:"merge_base_commit,omitempty"`

	// Head can be 'behind' or 'ahead'
	Status       *string `json:"status,omitempty"`
	AheadBy      *int    `json:"ahead_by,omitempty"`
	BehindBy     *int    `json:"behind_by,omitempty"`
	TotalCommits *int    `json:"total_commits,omitempty"`

	Commits []RepositoryCommit `json:"commits,omitempty"`

	Files []CommitFile `json:"files,omitempty"`

	HTMLURL      *string `json:"html_url,omitempty"`
	PermalinkURL *string `json:"permalink_url,omitempty"`
	DiffURL      *string `json:"diff_url,omitempty"`
	PatchURL     *string `json:"patch_url,omitempty"`
	URL          *string `json:"url,omitempty"` // API URL.
}

func (c CommitsComparison) String() string {
	return Stringify(c)
}

// CommitsListOptions specifies the optional parameters to the
// RepositoriesService.ListCommits method.
type CommitsListOptions struct {
	// SHA or branch to start listing Commits from.
	SHA string `url:"sha,omitempty"`

	// Path that should be touched by the returned Commits.
	Path string `url:"path,omitempty"`

	// Author of by which to filter Commits.
	Author string `url:"author,omitempty"`

	// Since when should Commits be included in the response.
	Since time.Time `url:"since,omitempty"`

	// Until when should Commits be included in the response.
	Until time.Time `url:"until,omitempty"`

	ListOptions
}

// ListCommits lists the commits of a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#list
func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opt *CommitsListOptions) ([]*RepositoryCommit, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits", owner, repo)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeGitSigningPreview)

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

	return commits, resp, nil
}

// GetCommit fetches the specified commit, including all details about it.
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-a-single-commit
// See also: https://developer.github.com/v3/git/commits/#get-a-single-commit provides the same functionality
func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string) (*RepositoryCommit, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha)

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeGitSigningPreview)

	commit := new(RepositoryCommit)
	resp, err := s.client.Do(ctx, req, commit)
	if err != nil {
		return nil, resp, err
	}

	return commit, resp, nil
}

// GetCommitRaw fetches the specified commit in raw (diff or patch) format.
func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner string, repo string, sha string, opt RawOptions) (string, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return "", nil, err
	}

	switch opt.Type {
	case Diff:
		req.Header.Set("Accept", mediaTypeV3Diff)
	case Patch:
		req.Header.Set("Accept", mediaTypeV3Patch)
	default:
		return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type)
	}

	var buf bytes.Buffer
	resp, err := s.client.Do(ctx, req, &buf)
	if err != nil {
		return "", resp, err
	}

	return buf.String(), resp, nil
}

// GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is
// supplied and no new commits have occurred, a 304 Unmodified response is returned.
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, ref, lastSHA string) (string, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, ref)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return "", nil, err
	}
	if lastSHA != "" {
		req.Header.Set("If-None-Match", `"`+lastSHA+`"`)
	}

	req.Header.Set("Accept", mediaTypeV3SHA)

	var buf bytes.Buffer
	resp, err := s.client.Do(ctx, req, &buf)
	if err != nil {
		return "", resp, err
	}

	return buf.String(), resp, nil
}

// CompareCommits compares a range of commits with each other.
// todo: support media formats - https://github.com/google/go-github/issues/6
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#compare-two-commits
func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string) (*CommitsComparison, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head)

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

	comp := new(CommitsComparison)
	resp, err := s.client.Do(ctx, req, comp)
	if err != nil {
		return nil, resp, err
	}

	return comp, resp, nil
}
<|endoftext|>


<commit_before>package v2

import (
	"context"
	"math"
	"net"
	"net/http"
	"net/url"
	"strconv"

	"github.com/go-graphite/carbonzipper/limiter"
	"github.com/go-graphite/carbonzipper/zipper/errors"
	"github.com/go-graphite/carbonzipper/zipper/helper"
	"github.com/go-graphite/carbonzipper/zipper/httpHeaders"
	"github.com/go-graphite/carbonzipper/zipper/metadata"
	"github.com/go-graphite/carbonzipper/zipper/types"
	protov2 "github.com/go-graphite/protocol/carbonapi_v2_pb"
	protov3 "github.com/go-graphite/protocol/carbonapi_v3_pb"

	"go.uber.org/zap"
)

const (
	format = "protobuf"
)

func init() {
	aliases := []string{"carbonapi_v2_pb", "proto_v2_pb", "v2_pb", "pb", "pb3", "protobuf", "protobuf3"}
	metadata.Metadata.Lock()
	for _, name := range aliases {
		metadata.Metadata.SupportedProtocols[name] = struct{}{}
		metadata.Metadata.ProtocolInits[name] = New
		metadata.Metadata.ProtocolInitsWithLimiter[name] = NewWithLimiter
	}
	defer metadata.Metadata.Unlock()
}

// RoundRobin is used to connect to backends inside clientGroups, implements ServerClient interface
type ClientProtoV2Group struct {
	groupName string
	servers   []string

	client *http.Client

	counter             uint64
	maxIdleConnsPerHost int

	limiter              *limiter.ServerLimiter
	logger               *zap.Logger
	timeout              types.Timeouts
	maxTries             int
	maxMetricsPerRequest int

	httpQuery *helper.HttpQuery
}

func NewWithLimiter(logger *zap.Logger, config types.BackendV2, limiter *limiter.ServerLimiter) (types.ServerClient, *errors.Errors) {
	logger = logger.With(zap.String("type", "protoV2Group"), zap.String("name", config.GroupName))

	httpClient := &http.Client{
		Transport: &http.Transport{
			MaxIdleConnsPerHost: *config.MaxIdleConnsPerHost,
			DialContext: (&net.Dialer{
				Timeout:   config.Timeouts.Connect,
				KeepAlive: *config.KeepAliveInterval,
				DualStack: true,
			}).DialContext,
		},
	}

	httpQuery := helper.NewHttpQuery(logger, config.GroupName, config.Servers, *config.MaxTries, limiter, httpClient, httpHeaders.ContentTypeCarbonAPIv2PB)

	c := &ClientProtoV2Group{
		groupName:            config.GroupName,
		servers:              config.Servers,
		timeout:              *config.Timeouts,
		maxTries:             *config.MaxTries,
		maxMetricsPerRequest: config.MaxGlobs,

		client:  httpClient,
		limiter: limiter,
		logger:  logger,

		httpQuery: httpQuery,
	}
	return c, nil
}

func New(logger *zap.Logger, config types.BackendV2) (types.ServerClient, *errors.Errors) {
	if config.ConcurrencyLimit == nil {
		return nil, errors.Fatal("concurency limit is not set")
	}
	if len(config.Servers) == 0 {
		return nil, errors.Fatal("no servers specified")
	}
	limiter := limiter.NewServerLimiter([]string{config.GroupName}, *config.ConcurrencyLimit)

	return NewWithLimiter(logger, config, limiter)
}

func (c ClientProtoV2Group) MaxMetricsPerRequest() int {
	return c.maxMetricsPerRequest
}

func (c ClientProtoV2Group) Name() string {
	return c.groupName
}

func (c ClientProtoV2Group) Backends() []string {
	return c.servers
}

func (c *ClientProtoV2Group) Fetch(ctx context.Context, request *protov3.MultiFetchRequest) (*protov3.MultiFetchResponse, *types.Stats, *errors.Errors) {
	stats := &types.Stats{}
	rewrite, _ := url.Parse("http://127.0.0.1/render/")

	pathExprToTargets := make(map[string][]string)
	for _, m := range request.Metrics {
		targets := pathExprToTargets[m.PathExpression]
		pathExprToTargets[m.PathExpression] = append(targets, m.Name)
	}

	var r protov3.MultiFetchResponse
	for pathExpr, targets := range pathExprToTargets {
		v := url.Values{
			"target": targets,
			"format": []string{format},
			"from":   []string{strconv.Itoa(int(request.Metrics[0].StartTime))},
			"until":  []string{strconv.Itoa(int(request.Metrics[0].StopTime))},
		}
		rewrite.RawQuery = v.Encode()
		res, err := c.httpQuery.DoQuery(ctx, rewrite.RequestURI(), nil)
		if err == nil {
			err = &errors.Errors{}
		}
		if err.HaveFatalErrors {
			err.HaveFatalErrors = false
			return nil, stats, err
		}

		var metrics protov2.MultiFetchResponse
		err.AddFatal(metrics.Unmarshal(res.Response))
		if err.HaveFatalErrors {
			return nil, stats, err
		}

		for _, m := range metrics.Metrics {
			for i, v := range m.IsAbsent {
				if v {
					m.Values[i] = math.NaN()
				}
			}
			r.Metrics = append(r.Metrics, protov3.FetchResponse{
				Name:              m.Name,
				PathExpression:    pathExpr,
				ConsolidationFunc: "Average",
				StopTime:          int64(m.StopTime),
				StartTime:         int64(m.StartTime),
				StepTime:          int64(m.StepTime),
				Values:            m.Values,
				XFilesFactor:      0.0,
			})
		}
	}

	return &r, stats, nil
}

func (c *ClientProtoV2Group) Find(ctx context.Context, request *protov3.MultiGlobRequest) (*protov3.MultiGlobResponse, *types.Stats, *errors.Errors) {
	logger := c.logger.With(zap.String("type", "find"), zap.Strings("request", request.Metrics))
	stats := &types.Stats{}
	rewrite, _ := url.Parse("http://127.0.0.1/metrics/find/")

	var r protov3.MultiGlobResponse
	r.Metrics = make([]protov3.GlobResponse, 0)
	var e errors.Errors
	for _, query := range request.Metrics {
		v := url.Values{
			"query":  []string{query},
			"format": []string{format},
		}
		rewrite.RawQuery = v.Encode()
		res, err := c.httpQuery.DoQuery(ctx, rewrite.RequestURI(), nil)
		if err != nil {
			e.Merge(err)
			continue
		}
		var globs protov2.GlobResponse
		marshalErr := globs.Unmarshal(res.Response)
		if marshalErr != nil {
			e.Add(marshalErr)
			continue
		}
		stats.Servers = append(stats.Servers, res.Server)
		matches := make([]protov3.GlobMatch, 0, len(globs.Matches))
		for _, m := range globs.Matches {
			matches = append(matches, protov3.GlobMatch{
				Path:   m.Path,
				IsLeaf: m.IsLeaf,
			})
		}
		r.Metrics = append(r.Metrics, protov3.GlobResponse{
			Name:    globs.Name,
			Matches: matches,
		})
	}

	if len(e.Errors) != 0 {
		logger.Error("errors occurred while getting results",
			zap.Any("errors", e.Errors),
		)
	}

	if len(r.Metrics) == 0 {
		return nil, stats, errors.FromErr(types.ErrNoResponseFetched)
	}
	return &r, stats, nil
}

func (c *ClientProtoV2Group) Info(ctx context.Context, request *protov3.MultiMetricsInfoRequest) (*protov3.ZipperInfoResponse, *types.Stats, *errors.Errors) {
	logger := c.logger.With(zap.String("type", "info"))
	stats := &types.Stats{}
	rewrite, _ := url.Parse("http://127.0.0.1/info/")

	var r protov3.ZipperInfoResponse
	var e errors.Errors
	r.Info = make(map[string]protov3.MultiMetricsInfoResponse)
	data := protov3.MultiMetricsInfoResponse{}
	server := c.groupName
	if len(c.servers) == 1 {
		server = c.servers[0]
	}

	for _, query := range request.Names {
		v := url.Values{
			"target": []string{query},
			"format": []string{format},
		}
		rewrite.RawQuery = v.Encode()
		res, e2 := c.httpQuery.DoQuery(ctx, rewrite.RequestURI(), nil)
		if e2 != nil {
			e.Merge(e2)
			continue
		}

		var info protov2.InfoResponse
		err := info.Unmarshal(res.Response)
		if err != nil {
			e.Add(err)
			continue
		}
		stats.Servers = append(stats.Servers, res.Server)

		if info.AggregationMethod == "" {
			info.AggregationMethod = "average"
		}
		infoV3 := protov3.MetricsInfoResponse{
			Name:              info.Name,
			ConsolidationFunc: info.AggregationMethod,
			XFilesFactor:      info.XFilesFactor,
			MaxRetention:      int64(info.MaxRetention),
		}

		for _, r := range info.Retentions {
			newR := protov3.Retention{
				SecondsPerPoint: int64(r.SecondsPerPoint),
				NumberOfPoints:  int64(r.NumberOfPoints),
			}
			infoV3.Retentions = append(infoV3.Retentions, newR)
		}

		data.Metrics = append(data.Metrics, infoV3)
	}
	r.Info[server] = data

	if len(e.Errors) != 0 {
		logger.Error("errors occurred while getting results",
			zap.Any("errors", e.Errors),
		)
	}

	if len(r.Info[server].Metrics) == 0 {
		return nil, stats, errors.FromErr(types.ErrNoResponseFetched)
	}

	logger.Debug("got client response",
		zap.Any("r", r),
	)

	return &r, stats, nil
}

func (c *ClientProtoV2Group) List(ctx context.Context) (*protov3.ListMetricsResponse, *types.Stats, *errors.Errors) {
	return nil, nil, errors.FromErr(types.ErrNotImplementedYet)
}
func (c *ClientProtoV2Group) Stats(ctx context.Context) (*protov3.MetricDetailsResponse, *types.Stats, *errors.Errors) {
	return nil, nil, errors.FromErr(types.ErrNotImplementedYet)
}

func (c *ClientProtoV2Group) ProbeTLDs(ctx context.Context) ([]string, *errors.Errors) {
	logger := c.logger.With(zap.String("function", "prober"))
	req := &protov3.MultiGlobRequest{
		Metrics: []string{"*"},
	}

	logger.Debug("doing request",
		zap.Strings("request", req.Metrics),
	)

	res, _, err := c.Find(ctx, req)
	if err != nil {
		return nil, err
	}

	var tlds []string
	for _, m := range res.Metrics {
		for _, v := range m.Matches {
			tlds = append(tlds, v.Path)
		}
	}

	logger.Debug("will return data",
		zap.Strings("tlds", tlds),
	)

	return tlds, nil
}
<commit_msg>Correctly batch metric fetching in protocol v2<commit_after>package v2

import (
	"context"
	"math"
	"net"
	"net/http"
	"net/url"
	"strconv"

	"github.com/go-graphite/carbonzipper/limiter"
	"github.com/go-graphite/carbonzipper/zipper/errors"
	"github.com/go-graphite/carbonzipper/zipper/helper"
	"github.com/go-graphite/carbonzipper/zipper/httpHeaders"
	"github.com/go-graphite/carbonzipper/zipper/metadata"
	"github.com/go-graphite/carbonzipper/zipper/types"
	protov2 "github.com/go-graphite/protocol/carbonapi_v2_pb"
	protov3 "github.com/go-graphite/protocol/carbonapi_v3_pb"

	"go.uber.org/zap"
)

const (
	format = "protobuf"
)

func init() {
	aliases := []string{"carbonapi_v2_pb", "proto_v2_pb", "v2_pb", "pb", "pb3", "protobuf", "protobuf3"}
	metadata.Metadata.Lock()
	for _, name := range aliases {
		metadata.Metadata.SupportedProtocols[name] = struct{}{}
		metadata.Metadata.ProtocolInits[name] = New
		metadata.Metadata.ProtocolInitsWithLimiter[name] = NewWithLimiter
	}
	defer metadata.Metadata.Unlock()
}

// RoundRobin is used to connect to backends inside clientGroups, implements ServerClient interface
type ClientProtoV2Group struct {
	groupName string
	servers   []string

	client *http.Client

	counter             uint64
	maxIdleConnsPerHost int

	limiter              *limiter.ServerLimiter
	logger               *zap.Logger
	timeout              types.Timeouts
	maxTries             int
	maxMetricsPerRequest int

	httpQuery *helper.HttpQuery
}

func NewWithLimiter(logger *zap.Logger, config types.BackendV2, limiter *limiter.ServerLimiter) (types.ServerClient, *errors.Errors) {
	logger = logger.With(zap.String("type", "protoV2Group"), zap.String("name", config.GroupName))

	httpClient := &http.Client{
		Transport: &http.Transport{
			MaxIdleConnsPerHost: *config.MaxIdleConnsPerHost,
			DialContext: (&net.Dialer{
				Timeout:   config.Timeouts.Connect,
				KeepAlive: *config.KeepAliveInterval,
				DualStack: true,
			}).DialContext,
		},
	}

	httpQuery := helper.NewHttpQuery(logger, config.GroupName, config.Servers, *config.MaxTries, limiter, httpClient, httpHeaders.ContentTypeCarbonAPIv2PB)

	c := &ClientProtoV2Group{
		groupName:            config.GroupName,
		servers:              config.Servers,
		timeout:              *config.Timeouts,
		maxTries:             *config.MaxTries,
		maxMetricsPerRequest: config.MaxGlobs,

		client:  httpClient,
		limiter: limiter,
		logger:  logger,

		httpQuery: httpQuery,
	}
	return c, nil
}

func New(logger *zap.Logger, config types.BackendV2) (types.ServerClient, *errors.Errors) {
	if config.ConcurrencyLimit == nil {
		return nil, errors.Fatal("concurency limit is not set")
	}
	if len(config.Servers) == 0 {
		return nil, errors.Fatal("no servers specified")
	}
	limiter := limiter.NewServerLimiter([]string{config.GroupName}, *config.ConcurrencyLimit)

	return NewWithLimiter(logger, config, limiter)
}

func (c ClientProtoV2Group) MaxMetricsPerRequest() int {
	return c.maxMetricsPerRequest
}

func (c ClientProtoV2Group) Name() string {
	return c.groupName
}

func (c ClientProtoV2Group) Backends() []string {
	return c.servers
}

type queryBatch struct {
	pathExpression string
	from           int64
	until          int64
}

func (c *ClientProtoV2Group) Fetch(ctx context.Context, request *protov3.MultiFetchRequest) (*protov3.MultiFetchResponse, *types.Stats, *errors.Errors) {
	stats := &types.Stats{}
	rewrite, _ := url.Parse("http://127.0.0.1/render/")

	batches := make(map[queryBatch][]string)
	for _, m := range request.Metrics {
		b := queryBatch{
			pathExpression: m.PathExpression,
			from:           m.StartTime,
			until:          m.StopTime,
		}

		batches[b] = append(batches[b], m.Name)
	}

	var r protov3.MultiFetchResponse
	for batch, targets := range batches {
		v := url.Values{
			"target": targets,
			"format": []string{format},
			"from":   []string{strconv.Itoa(int(batch.from))},
			"until":  []string{strconv.Itoa(int(batch.until))},
		}
		rewrite.RawQuery = v.Encode()
		res, err := c.httpQuery.DoQuery(ctx, rewrite.RequestURI(), nil)
		if err == nil {
			err = &errors.Errors{}
		}
		if err.HaveFatalErrors {
			err.HaveFatalErrors = false
			return nil, stats, err
		}

		var metrics protov2.MultiFetchResponse
		err.AddFatal(metrics.Unmarshal(res.Response))
		if err.HaveFatalErrors {
			return nil, stats, err
		}

		for _, m := range metrics.Metrics {
			for i, v := range m.IsAbsent {
				if v {
					m.Values[i] = math.NaN()
				}
			}
			r.Metrics = append(r.Metrics, protov3.FetchResponse{
				Name:              m.Name,
				PathExpression:    batch.pathExpression,
				ConsolidationFunc: "Average",
				StopTime:          int64(m.StopTime),
				StartTime:         int64(m.StartTime),
				StepTime:          int64(m.StepTime),
				Values:            m.Values,
				XFilesFactor:      0.0,
				RequestStartTime:  batch.from,
				RequestStopTime:   batch.until,
			})
		}
	}

	return &r, stats, nil
}

func (c *ClientProtoV2Group) Find(ctx context.Context, request *protov3.MultiGlobRequest) (*protov3.MultiGlobResponse, *types.Stats, *errors.Errors) {
	logger := c.logger.With(zap.String("type", "find"), zap.Strings("request", request.Metrics))
	stats := &types.Stats{}
	rewrite, _ := url.Parse("http://127.0.0.1/metrics/find/")

	var r protov3.MultiGlobResponse
	r.Metrics = make([]protov3.GlobResponse, 0)
	var e errors.Errors
	for _, query := range request.Metrics {
		v := url.Values{
			"query":  []string{query},
			"format": []string{format},
		}
		rewrite.RawQuery = v.Encode()
		res, err := c.httpQuery.DoQuery(ctx, rewrite.RequestURI(), nil)
		if err != nil {
			e.Merge(err)
			continue
		}
		var globs protov2.GlobResponse
		marshalErr := globs.Unmarshal(res.Response)
		if marshalErr != nil {
			e.Add(marshalErr)
			continue
		}
		stats.Servers = append(stats.Servers, res.Server)
		matches := make([]protov3.GlobMatch, 0, len(globs.Matches))
		for _, m := range globs.Matches {
			matches = append(matches, protov3.GlobMatch{
				Path:   m.Path,
				IsLeaf: m.IsLeaf,
			})
		}
		r.Metrics = append(r.Metrics, protov3.GlobResponse{
			Name:    globs.Name,
			Matches: matches,
		})
	}

	if len(e.Errors) != 0 {
		logger.Error("errors occurred while getting results",
			zap.Any("errors", e.Errors),
		)
	}

	if len(r.Metrics) == 0 {
		return nil, stats, errors.FromErr(types.ErrNoResponseFetched)
	}
	return &r, stats, nil
}

func (c *ClientProtoV2Group) Info(ctx context.Context, request *protov3.MultiMetricsInfoRequest) (*protov3.ZipperInfoResponse, *types.Stats, *errors.Errors) {
	logger := c.logger.With(zap.String("type", "info"))
	stats := &types.Stats{}
	rewrite, _ := url.Parse("http://127.0.0.1/info/")

	var r protov3.ZipperInfoResponse
	var e errors.Errors
	r.Info = make(map[string]protov3.MultiMetricsInfoResponse)
	data := protov3.MultiMetricsInfoResponse{}
	server := c.groupName
	if len(c.servers) == 1 {
		server = c.servers[0]
	}

	for _, query := range request.Names {
		v := url.Values{
			"target": []string{query},
			"format": []string{format},
		}
		rewrite.RawQuery = v.Encode()
		res, e2 := c.httpQuery.DoQuery(ctx, rewrite.RequestURI(), nil)
		if e2 != nil {
			e.Merge(e2)
			continue
		}

		var info protov2.InfoResponse
		err := info.Unmarshal(res.Response)
		if err != nil {
			e.Add(err)
			continue
		}
		stats.Servers = append(stats.Servers, res.Server)

		if info.AggregationMethod == "" {
			info.AggregationMethod = "average"
		}
		infoV3 := protov3.MetricsInfoResponse{
			Name:              info.Name,
			ConsolidationFunc: info.AggregationMethod,
			XFilesFactor:      info.XFilesFactor,
			MaxRetention:      int64(info.MaxRetention),
		}

		for _, r := range info.Retentions {
			newR := protov3.Retention{
				SecondsPerPoint: int64(r.SecondsPerPoint),
				NumberOfPoints:  int64(r.NumberOfPoints),
			}
			infoV3.Retentions = append(infoV3.Retentions, newR)
		}

		data.Metrics = append(data.Metrics, infoV3)
	}
	r.Info[server] = data

	if len(e.Errors) != 0 {
		logger.Error("errors occurred while getting results",
			zap.Any("errors", e.Errors),
		)
	}

	if len(r.Info[server].Metrics) == 0 {
		return nil, stats, errors.FromErr(types.ErrNoResponseFetched)
	}

	logger.Debug("got client response",
		zap.Any("r", r),
	)

	return &r, stats, nil
}

func (c *ClientProtoV2Group) List(ctx context.Context) (*protov3.ListMetricsResponse, *types.Stats, *errors.Errors) {
	return nil, nil, errors.FromErr(types.ErrNotImplementedYet)
}
func (c *ClientProtoV2Group) Stats(ctx context.Context) (*protov3.MetricDetailsResponse, *types.Stats, *errors.Errors) {
	return nil, nil, errors.FromErr(types.ErrNotImplementedYet)
}

func (c *ClientProtoV2Group) ProbeTLDs(ctx context.Context) ([]string, *errors.Errors) {
	logger := c.logger.With(zap.String("function", "prober"))
	req := &protov3.MultiGlobRequest{
		Metrics: []string{"*"},
	}

	logger.Debug("doing request",
		zap.Strings("request", req.Metrics),
	)

	res, _, err := c.Find(ctx, req)
	if err != nil {
		return nil, err
	}

	var tlds []string
	for _, m := range res.Metrics {
		for _, v := range m.Matches {
			tlds = append(tlds, v.Path)
		}
	}

	logger.Debug("will return data",
		zap.Strings("tlds", tlds),
	)

	return tlds, nil
}
<|endoftext|>


<commit_before>package oci

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"github.com/go-ini/ini"
)

func testConfig(accessConfFile *os.File) map[string]interface{} {
	return map[string]interface{}{

		"availability_domain": "aaaa:PHX-AD-3",
		"access_cfg_file":     accessConfFile.Name(),

		// Image
		"base_image_ocid": "ocd1...",
		"image_name":      "HelloWorld",

		// Networking
		"subnet_ocid": "ocd1...",

		// Comm
		"ssh_username":   "opc",
		"use_private_ip": false,
		"metadata": map[string]string{
			"key": "value",
		},
		"defined_tags": map[string]map[string]interface{}{
			"namespace": {"key": "value"},
		},

		// Instance Details
		"instance_name": "hello-world",
		"instance_tags": map[string]string{
			"key": "value",
		},
		"create_vnic_details": map[string]interface{}{
			"nsg_ids": []string{"ocd1..."},
		},
		"shape":     "VM.Standard1.1",
		"disk_size": 60,
	}
}

func TestConfig(t *testing.T) {
	// Shared set-up and deferred deletion

	cfg, keyFile, err := baseTestConfigWithTmpKeyFile()
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(keyFile.Name())

	cfgFile, err := writeTestConfig(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(cfgFile.Name())

	// Temporarily set $HOME to temp directory to bypass default
	// access config loading.

	tmpHome, err := ioutil.TempDir("", "packer_config_test")
	if err != nil {
		t.Fatalf("Unexpected error when creating temporary directory: %+v", err)
	}
	defer os.Remove(tmpHome)

	home := os.Getenv("HOME")
	os.Setenv("HOME", tmpHome)
	defer os.Setenv("HOME", home)

	// Config tests
	t.Run("BaseConfig", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}
	})

	t.Run("BaseImageFilterWithoutOCID", func(t *testing.T) {
		raw := testConfig(cfgFile)
		raw["base_image_ocid"] = ""
		raw["base_image_filter"] = map[string]interface{}{
			"display_name": "hello_world",
		}

		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}
	})

	t.Run("BaseImageFilterDefault", func(t *testing.T) {
		raw := testConfig(cfgFile)

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		if *c.BaseImageFilter.Shape != raw["shape"] {
			t.Fatalf("Default base_image_filter shape %v does not equal config shape %v",
				*c.BaseImageFilter.Shape, raw["shape"])
		}
	})

	t.Run("NoAccessConfig", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "access_cfg_file")

		var c Config
		errs := c.Prepare(raw)

		expectedErrors := []string{
			"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
		}

		s := errs.Error()
		for _, expected := range expectedErrors {
			if !strings.Contains(s, expected) {
				t.Errorf("Expected %q to contain '%s'", s, expected)
			}
		}
	})

	t.Run("AccessConfigTemplateOnly", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "access_cfg_file")
		raw["user_ocid"] = "ocid1..."
		raw["tenancy_ocid"] = "ocid1..."
		raw["fingerprint"] = "00:00..."
		raw["key_file"] = keyFile.Name()

		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("err: %+v", errs)
		}

	})

	t.Run("TenancyReadFromAccessCfgFile", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		tenancy, err := c.configProvider.TenancyOCID()
		if err != nil {
			t.Fatalf("Unexpected error getting tenancy ocid: %v", err)
		}

		expected := "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
		if tenancy != expected {
			t.Errorf("Expected tenancy: %s, got %s.", expected, tenancy)
		}

	})

	t.Run("RegionNotDefaultedToPHXWhenSetInOCISettings", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		region, err := c.configProvider.Region()
		if err != nil {
			t.Fatalf("Unexpected error getting region: %v", err)
		}

		expected := "us-ashburn-1"
		if region != expected {
			t.Errorf("Expected region: %s, got %s.", expected, region)
		}

	})

	// Test the correct errors are produced when required template keys are
	// omitted.
	requiredKeys := []string{"availability_domain", "base_image_ocid", "shape", "subnet_ocid"}
	for _, k := range requiredKeys {
		t.Run(k+"_required", func(t *testing.T) {
			raw := testConfig(cfgFile)
			delete(raw, k)

			var c Config
			errs := c.Prepare(raw)

			if !strings.Contains(errs.Error(), k) {
				t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
			}
		})
	}

	t.Run("ImageNameDefaultedIfEmpty", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "image_name")

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		if !strings.Contains(c.ImageName, "packer-") {
			t.Errorf("got default ImageName %q, want image name 'packer-{{timestamp}}'", c.ImageName)
		}
	})

	t.Run("user_ocid_overridden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["user_ocid"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		user, _ := c.configProvider.UserOCID()
		if user != expected {
			t.Errorf("Expected ConfigProvider.UserOCID: %s, got %s", expected, user)
		}
	})

	t.Run("tenancy_ocid_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["tenancy_ocid"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		tenancy, _ := c.configProvider.TenancyOCID()
		if tenancy != expected {
			t.Errorf("Expected ConfigProvider.TenancyOCID: %s, got %s", expected, tenancy)
		}
	})

	t.Run("region_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["region"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		region, _ := c.configProvider.Region()
		if region != expected {
			t.Errorf("Expected ConfigProvider.Region: %s, got %s", expected, region)
		}
	})

	t.Run("fingerprint_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["fingerprint"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration: %+v", errs)
		}

		fingerprint, _ := c.configProvider.KeyFingerprint()
		if fingerprint != expected {
			t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
		}
	})

	// Test the correct errors are produced when certain template keys
	// are present alongside use_instance_principals key.
	invalidKeys := []string{
		"access_cfg_file",
		"access_cfg_file_account",
		"user_ocid",
		"tenancy_ocid",
		"region",
		"fingerprint",
		"key_file",
		"pass_phrase",
	}
	for _, k := range invalidKeys {
		t.Run(k+"_mixed_with_use_instance_principals", func(t *testing.T) {
			raw := testConfig(cfgFile)
			raw["use_instance_principals"] = "true"
			raw[k] = "some_random_value"

			var c Config

			c.configProvider = instancePrincipalConfigurationProviderMock{}

			errs := c.Prepare(raw)

			if !strings.Contains(errs.Error(), k) {
				t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
			}
		})
	}
}

// BaseTestConfig creates the base (DEFAULT) config including a temporary key
// file.
// NOTE: Caller is responsible for removing temporary key file.
func baseTestConfigWithTmpKeyFile() (*ini.File, *os.File, error) {
	keyFile, err := generateRSAKeyFile()
	if err != nil {
		return nil, keyFile, err
	}
	// Build ini
	cfg := ini.Empty()
	section, _ := cfg.NewSection("DEFAULT")
	section.NewKey("region", "us-ashburn-1")
	section.NewKey("tenancy", "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	section.NewKey("user", "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	section.NewKey("fingerprint", "70:04:5z:b3:19:ab:90:75:a4:1f:50:d4:c7:c3:33:20")
	section.NewKey("key_file", keyFile.Name())

	return cfg, keyFile, nil
}

// WriteTestConfig writes a ini.File to a temporary file for use in unit tests.
// NOTE: Caller is responsible for removing temporary file.
func writeTestConfig(cfg *ini.File) (*os.File, error) {
	confFile, err := ioutil.TempFile("", "config_file")
	if err != nil {
		return nil, err
	}

	if _, err := confFile.Write([]byte("[DEFAULT]\n")); err != nil {
		os.Remove(confFile.Name())
		return nil, err
	}

	if _, err := cfg.WriteTo(confFile); err != nil {
		os.Remove(confFile.Name())
		return nil, err
	}
	return confFile, nil
}

// generateRSAKeyFile generates an RSA key file for use in unit tests.
// NOTE: The caller is responsible for deleting the temporary file.
func generateRSAKeyFile() (*os.File, error) {
	// Create temporary file for the key
	f, err := ioutil.TempFile("", "key")
	if err != nil {
		return nil, err
	}

	// Generate key
	priv, err := rsa.GenerateKey(rand.Reader, 2014)
	if err != nil {
		return nil, err
	}

	// ASN.1 DER encoded form
	privDer := x509.MarshalPKCS1PrivateKey(priv)
	privBlk := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   privDer,
	}

	// Write the key out
	if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil {
		return nil, err
	}

	return f, nil
}
<commit_msg>test (builder/oracle-oci): add config test for LaunchMode<commit_after>package oci

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"github.com/go-ini/ini"
)

func testConfig(accessConfFile *os.File) map[string]interface{} {
	return map[string]interface{}{

		"availability_domain": "aaaa:PHX-AD-3",
		"access_cfg_file":     accessConfFile.Name(),

		// Image
		"base_image_ocid": "ocd1...",
		"image_name":      "HelloWorld",

		// Networking
		"subnet_ocid": "ocd1...",

		// Comm
		"ssh_username":   "opc",
		"use_private_ip": false,
		"metadata": map[string]string{
			"key": "value",
		},
		"defined_tags": map[string]map[string]interface{}{
			"namespace": {"key": "value"},
		},

		// Instance Details
		"instance_name": "hello-world",
		"instance_tags": map[string]string{
			"key": "value",
		},
		"create_vnic_details": map[string]interface{}{
			"nsg_ids": []string{"ocd1..."},
		},
		"shape":     "VM.Standard1.1",
		"disk_size": 60,
	}
}

func TestConfig(t *testing.T) {
	// Shared set-up and deferred deletion

	cfg, keyFile, err := baseTestConfigWithTmpKeyFile()
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(keyFile.Name())

	cfgFile, err := writeTestConfig(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(cfgFile.Name())

	// Temporarily set $HOME to temp directory to bypass default
	// access config loading.

	tmpHome, err := ioutil.TempDir("", "packer_config_test")
	if err != nil {
		t.Fatalf("Unexpected error when creating temporary directory: %+v", err)
	}
	defer os.Remove(tmpHome)

	home := os.Getenv("HOME")
	os.Setenv("HOME", tmpHome)
	defer os.Setenv("HOME", home)

	// Config tests
	t.Run("BaseConfig", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}
	})

	t.Run("BaseImageFilterWithoutOCID", func(t *testing.T) {
		raw := testConfig(cfgFile)
		raw["base_image_ocid"] = ""
		raw["base_image_filter"] = map[string]interface{}{
			"display_name": "hello_world",
		}

		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}
	})

	t.Run("BaseImageFilterDefault", func(t *testing.T) {
		raw := testConfig(cfgFile)

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		if *c.BaseImageFilter.Shape != raw["shape"] {
			t.Fatalf("Default base_image_filter shape %v does not equal config shape %v",
				*c.BaseImageFilter.Shape, raw["shape"])
		}
	})

	t.Run("LaunchMode", func(t *testing.T) {
		raw := testConfig(cfgFile)
		raw["image_launch_mode"] = "NATIVE"

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}
	})

	t.Run("NoAccessConfig", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "access_cfg_file")

		var c Config
		errs := c.Prepare(raw)

		expectedErrors := []string{
			"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
		}

		s := errs.Error()
		for _, expected := range expectedErrors {
			if !strings.Contains(s, expected) {
				t.Errorf("Expected %q to contain '%s'", s, expected)
			}
		}
	})

	t.Run("AccessConfigTemplateOnly", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "access_cfg_file")
		raw["user_ocid"] = "ocid1..."
		raw["tenancy_ocid"] = "ocid1..."
		raw["fingerprint"] = "00:00..."
		raw["key_file"] = keyFile.Name()

		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("err: %+v", errs)
		}

	})

	t.Run("TenancyReadFromAccessCfgFile", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		tenancy, err := c.configProvider.TenancyOCID()
		if err != nil {
			t.Fatalf("Unexpected error getting tenancy ocid: %v", err)
		}

		expected := "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
		if tenancy != expected {
			t.Errorf("Expected tenancy: %s, got %s.", expected, tenancy)
		}

	})

	t.Run("RegionNotDefaultedToPHXWhenSetInOCISettings", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		region, err := c.configProvider.Region()
		if err != nil {
			t.Fatalf("Unexpected error getting region: %v", err)
		}

		expected := "us-ashburn-1"
		if region != expected {
			t.Errorf("Expected region: %s, got %s.", expected, region)
		}

	})

	// Test the correct errors are produced when required template keys are
	// omitted.
	requiredKeys := []string{"availability_domain", "base_image_ocid", "shape", "subnet_ocid"}
	for _, k := range requiredKeys {
		t.Run(k+"_required", func(t *testing.T) {
			raw := testConfig(cfgFile)
			delete(raw, k)

			var c Config
			errs := c.Prepare(raw)

			if !strings.Contains(errs.Error(), k) {
				t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
			}
		})
	}

	t.Run("ImageNameDefaultedIfEmpty", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "image_name")

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		if !strings.Contains(c.ImageName, "packer-") {
			t.Errorf("got default ImageName %q, want image name 'packer-{{timestamp}}'", c.ImageName)
		}
	})

	t.Run("user_ocid_overridden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["user_ocid"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		user, _ := c.configProvider.UserOCID()
		if user != expected {
			t.Errorf("Expected ConfigProvider.UserOCID: %s, got %s", expected, user)
		}
	})

	t.Run("tenancy_ocid_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["tenancy_ocid"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		tenancy, _ := c.configProvider.TenancyOCID()
		if tenancy != expected {
			t.Errorf("Expected ConfigProvider.TenancyOCID: %s, got %s", expected, tenancy)
		}
	})

	t.Run("region_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["region"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		region, _ := c.configProvider.Region()
		if region != expected {
			t.Errorf("Expected ConfigProvider.Region: %s, got %s", expected, region)
		}
	})

	t.Run("fingerprint_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["fingerprint"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration: %+v", errs)
		}

		fingerprint, _ := c.configProvider.KeyFingerprint()
		if fingerprint != expected {
			t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
		}
	})

	// Test the correct errors are produced when certain template keys
	// are present alongside use_instance_principals key.
	invalidKeys := []string{
		"access_cfg_file",
		"access_cfg_file_account",
		"user_ocid",
		"tenancy_ocid",
		"region",
		"fingerprint",
		"key_file",
		"pass_phrase",
	}
	for _, k := range invalidKeys {
		t.Run(k+"_mixed_with_use_instance_principals", func(t *testing.T) {
			raw := testConfig(cfgFile)
			raw["use_instance_principals"] = "true"
			raw[k] = "some_random_value"

			var c Config

			c.configProvider = instancePrincipalConfigurationProviderMock{}

			errs := c.Prepare(raw)

			if !strings.Contains(errs.Error(), k) {
				t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
			}
		})
	}
}

// BaseTestConfig creates the base (DEFAULT) config including a temporary key
// file.
// NOTE: Caller is responsible for removing temporary key file.
func baseTestConfigWithTmpKeyFile() (*ini.File, *os.File, error) {
	keyFile, err := generateRSAKeyFile()
	if err != nil {
		return nil, keyFile, err
	}
	// Build ini
	cfg := ini.Empty()
	section, _ := cfg.NewSection("DEFAULT")
	section.NewKey("region", "us-ashburn-1")
	section.NewKey("tenancy", "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	section.NewKey("user", "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	section.NewKey("fingerprint", "70:04:5z:b3:19:ab:90:75:a4:1f:50:d4:c7:c3:33:20")
	section.NewKey("key_file", keyFile.Name())

	return cfg, keyFile, nil
}

// WriteTestConfig writes a ini.File to a temporary file for use in unit tests.
// NOTE: Caller is responsible for removing temporary file.
func writeTestConfig(cfg *ini.File) (*os.File, error) {
	confFile, err := ioutil.TempFile("", "config_file")
	if err != nil {
		return nil, err
	}

	if _, err := confFile.Write([]byte("[DEFAULT]\n")); err != nil {
		os.Remove(confFile.Name())
		return nil, err
	}

	if _, err := cfg.WriteTo(confFile); err != nil {
		os.Remove(confFile.Name())
		return nil, err
	}
	return confFile, nil
}

// generateRSAKeyFile generates an RSA key file for use in unit tests.
// NOTE: The caller is responsible for deleting the temporary file.
func generateRSAKeyFile() (*os.File, error) {
	// Create temporary file for the key
	f, err := ioutil.TempFile("", "key")
	if err != nil {
		return nil, err
	}

	// Generate key
	priv, err := rsa.GenerateKey(rand.Reader, 2014)
	if err != nil {
		return nil, err
	}

	// ASN.1 DER encoded form
	privDer := x509.MarshalPKCS1PrivateKey(priv)
	privBlk := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   privDer,
	}

	// Write the key out
	if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil {
		return nil, err
	}

	return f, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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 audio

import (
	"fmt"
	"io"
	"sync"
)

// TODO: In JavaScript, mixing should be done by WebAudio for performance.
type mixedPlayersStream struct {
	context *Context
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func (s *mixedPlayersStream) Read(b []byte) (int, error) {
	s.context.Lock()
	defer s.context.Unlock()

	l := len(b) / 4 * 4
	if len(s.context.players) == 0 {
		copy(b, make([]byte, l))
		return l, nil
	}
	closed := []*Player{}
	bb := make([]byte, max(1024, l))
	ll := l
	for p := range s.context.players {
		n, err := p.src.Read(bb)
		if 0 < n {
			p.buf = append(p.buf, bb[:n]...)
		}
		if err == io.EOF {
			closed = append(closed, p)
			continue
		}
		if err != nil {
			return 0, err
		}
		ll = min(len(p.buf)/4*4, ll)
	}
	for _, p := range closed {
		if len(p.buf) < ll {
			p.buf = append(p.buf, make([]byte, ll-len(p.buf))...)
		}
	}
	for i := 0; i < ll/2; i++ {
		x := 0
		for p := range s.context.players {
			x += int(int16(p.buf[2*i]) | (int16(p.buf[2*i+1]) << 8))
		}
		if x > (1<<15)-1 {
			x = (1 << 15) - 1
		}
		if x < -(1 << 15) {
			x = -(1 << 15)
		}
		b[2*i] = byte(x)
		b[2*i+1] = byte(x >> 8)
	}
	for p := range s.context.players {
		p.buf = p.buf[ll:]
	}
	for _, p := range closed {
		delete(s.context.players, p)
	}
	return ll, nil
}

// TODO: Enable to specify the format like Mono8?

type Context struct {
	sampleRate int
	stream     *mixedPlayersStream
	players    map[*Player]struct{}
	sync.Mutex
}

func NewContext(sampleRate int) *Context {
	// TODO: Panic if one context exists.
	c := &Context{
		sampleRate: sampleRate,
		players:    map[*Player]struct{}{},
	}
	c.stream = &mixedPlayersStream{c}
	if err := startPlaying(c.stream, c.sampleRate); err != nil {
		panic(fmt.Sprintf("audio: NewContext error: %v", err))
	}
	return c
}

type Player struct {
	context *Context
	src     io.ReadSeeker
	buf     []byte
}

// NewPlayer creates a new player with the given data to the given channel.
// The given data is queued to the end of the buffer.
// This may not be played immediately when data already exists in the buffer.
//
// src's format must be linear PCM (16bits, 2 channel stereo, little endian)
// without a header (e.g. RIFF header).
//
// TODO: Pass sample rate and num of channels.
func (c *Context) NewPlayer(src io.ReadSeeker) (*Player, error) {
	c.Lock()
	defer c.Unlock()

	p := &Player{
		context: c,
		src:     src,
		buf:     []byte{},
	}
	return p, nil
}

func (p *Player) Play() error {
	p.context.Lock()
	defer p.context.Unlock()

	p.context.players[p] = struct{}{}
	return nil
}

// TODO: IsPlaying
// TODO: Stop
// TODO: Seek

func (p *Player) Pause() error {
	p.context.Lock()
	defer p.context.Unlock()

	delete(p.context.players, p)
	return nil
}
<commit_msg>audio: mixedPlayerStream.Read now may return 0 byte result<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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 audio

import (
	"fmt"
	"io"
	"sync"
)

// TODO: In JavaScript, mixing should be done by WebAudio for performance.
type mixedPlayersStream struct {
	context *Context
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func (s *mixedPlayersStream) Read(b []byte) (int, error) {
	s.context.Lock()
	defer s.context.Unlock()

	if len(s.context.players) == 0 {
		return 0, nil
	}
	closed := []*Player{}
	l := len(b) / 4 * 4
	bb := make([]byte, l)
	ll := l
	for p := range s.context.players {
		n, err := p.src.Read(bb)
		if 0 < n {
			p.buf = append(p.buf, bb[:n]...)
		}
		if err == io.EOF {
			closed = append(closed, p)
			continue
		}
		if err != nil {
			return 0, err
		}
		ll = min(len(p.buf)/4*4, ll)
	}
	for _, p := range closed {
		if len(p.buf) < ll {
			p.buf = append(p.buf, make([]byte, ll-len(p.buf))...)
		}
	}
	for i := 0; i < ll/2; i++ {
		x := 0
		for p := range s.context.players {
			x += int(int16(p.buf[2*i]) | (int16(p.buf[2*i+1]) << 8))
		}
		if x > (1<<15)-1 {
			x = (1 << 15) - 1
		}
		if x < -(1 << 15) {
			x = -(1 << 15)
		}
		b[2*i] = byte(x)
		b[2*i+1] = byte(x >> 8)
	}
	for p := range s.context.players {
		p.buf = p.buf[ll:]
	}
	for _, p := range closed {
		delete(s.context.players, p)
	}
	return ll, nil
}

// TODO: Enable to specify the format like Mono8?

type Context struct {
	sampleRate int
	stream     *mixedPlayersStream
	players    map[*Player]struct{}
	sync.Mutex
}

func NewContext(sampleRate int) *Context {
	// TODO: Panic if one context exists.
	c := &Context{
		sampleRate: sampleRate,
		players:    map[*Player]struct{}{},
	}
	c.stream = &mixedPlayersStream{c}
	if err := startPlaying(c.stream, c.sampleRate); err != nil {
		panic(fmt.Sprintf("audio: NewContext error: %v", err))
	}
	return c
}

type Player struct {
	context *Context
	src     io.ReadSeeker
	buf     []byte
}

// NewPlayer creates a new player with the given data to the given channel.
// The given data is queued to the end of the buffer.
// This may not be played immediately when data already exists in the buffer.
//
// src's format must be linear PCM (16bits, 2 channel stereo, little endian)
// without a header (e.g. RIFF header).
//
// TODO: Pass sample rate and num of channels.
func (c *Context) NewPlayer(src io.ReadSeeker) (*Player, error) {
	c.Lock()
	defer c.Unlock()

	p := &Player{
		context: c,
		src:     src,
		buf:     []byte{},
	}
	return p, nil
}

func (p *Player) Play() error {
	p.context.Lock()
	defer p.context.Unlock()

	p.context.players[p] = struct{}{}
	return nil
}

// TODO: IsPlaying
// TODO: Stop
// TODO: Seek

func (p *Player) Pause() error {
	p.context.Lock()
	defer p.context.Unlock()

	delete(p.context.players, p)
	return nil
}
<|endoftext|>


<commit_before>package azure

import (
	"fmt"
	"os"
	"sync"

	"github.com/Azure/azure-sdk-for-go/management"
	"github.com/Azure/azure-sdk-for-go/management/hostedservice"
	"github.com/Azure/azure-sdk-for-go/management/networksecuritygroup"
	"github.com/Azure/azure-sdk-for-go/management/osimage"
	"github.com/Azure/azure-sdk-for-go/management/sql"
	"github.com/Azure/azure-sdk-for-go/management/storageservice"
	"github.com/Azure/azure-sdk-for-go/management/virtualmachine"
	"github.com/Azure/azure-sdk-for-go/management/virtualmachinedisk"
	"github.com/Azure/azure-sdk-for-go/management/virtualmachineimage"
	"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
	"github.com/Azure/azure-sdk-for-go/storage"
)

// Config is the configuration structure used to instantiate a
// new Azure management client.
type Config struct {
	SettingsFile   string
	SubscriptionID string
	Certificate    []byte
	ManagementURL  string
}

// Client contains all the handles required for managing Azure services.
type Client struct {
	mgmtClient management.Client

	hostedServiceClient hostedservice.HostedServiceClient

	secGroupClient networksecuritygroup.SecurityGroupClient

	osImageClient osimage.OSImageClient

	sqlClient sql.SqlDatabaseClient

	storageServiceClient storageservice.StorageServiceClient

	vmClient virtualmachine.VirtualMachineClient

	vmDiskClient virtualmachinedisk.DiskClient

	vmImageClient virtualmachineimage.Client

	// unfortunately; because of how Azure's network API works; doing networking operations
	// concurrently is very hazardous, and we need a mutex to guard the VirtualNetworkClient.
	vnetClient virtualnetwork.VirtualNetworkClient
	mutex      *sync.Mutex
}

// getStorageClientForStorageService is helper method which returns the
// storage.Client associated to the given storage service name.
func (c Client) getStorageClientForStorageService(serviceName string) (storage.Client, error) {
	var storageClient storage.Client

	keys, err := c.storageServiceClient.GetStorageServiceKeys(serviceName)
	if err != nil {
		return storageClient, fmt.Errorf("Failed getting Storage Service keys for %s: %s", serviceName, err)
	}

	storageClient, err = storage.NewBasicClient(serviceName, keys.PrimaryKey)
	if err != nil {
		return storageClient, fmt.Errorf("Failed creating Storage Service client for %s: %s", serviceName, err)
	}

	return storageClient, err
}

// getStorageServiceBlobClient is a helper method which returns the
// storage.BlobStorageClient associated to the given storage service name.
func (c Client) getStorageServiceBlobClient(serviceName string) (storage.BlobStorageClient, error) {
	storageClient, err := c.getStorageClientForStorageService(serviceName)
	if err != nil {
		return storage.BlobStorageClient{}, err
	}

	return storageClient.GetBlobService(), nil
}

// getStorageServiceQueueClient is a helper method which returns the
// storage.QueueServiceClient associated to the given storage service name.
func (c Client) getStorageServiceQueueClient(serviceName string) (storage.QueueServiceClient, error) {
	storageClient, err := c.getStorageClientForStorageService(serviceName)
	if err != nil {
		return storage.QueueServiceClient{}, err
	}

	return storageClient.GetQueueService(), err
}

// NewClientFromSettingsFile returns a new Azure management
// client created using a publish settings file.
func (c *Config) NewClientFromSettingsFile() (*Client, error) {
	if _, err := os.Stat(c.SettingsFile); os.IsNotExist(err) {
		return nil, fmt.Errorf("Publish Settings file %q does not exist!", c.SettingsFile)
	}

	mc, err := management.ClientFromPublishSettingsFile(c.SettingsFile, c.SubscriptionID)
	if err != nil {
		return nil, nil
	}

	return &Client{
		mgmtClient:           mc,
		hostedServiceClient:  hostedservice.NewClient(mc),
		secGroupClient:       networksecuritygroup.NewClient(mc),
		osImageClient:        osimage.NewClient(mc),
		sqlClient:            sql.NewClient(mc),
		storageServiceClient: storageservice.NewClient(mc),
		vmClient:             virtualmachine.NewClient(mc),
		vmDiskClient:         virtualmachinedisk.NewClient(mc),
		vmImageClient:        virtualmachineimage.NewClient(mc),
		vnetClient:           virtualnetwork.NewClient(mc),
		mutex:                &sync.Mutex{},
	}, nil
}

// NewClient returns a new Azure management client created
// using a subscription ID and certificate.
func (c *Config) NewClient() (*Client, error) {
	mc, err := management.NewClient(c.SubscriptionID, c.Certificate)
	if err != nil {
		return nil, nil
	}

	return &Client{
		mgmtClient:           mc,
		hostedServiceClient:  hostedservice.NewClient(mc),
		secGroupClient:       networksecuritygroup.NewClient(mc),
		osImageClient:        osimage.NewClient(mc),
		sqlClient:            sql.NewClient(mc),
		storageServiceClient: storageservice.NewClient(mc),
		vmClient:             virtualmachine.NewClient(mc),
		vmDiskClient:         virtualmachinedisk.NewClient(mc),
		vmImageClient:        virtualmachineimage.NewClient(mc),
		vnetClient:           virtualnetwork.NewClient(mc),
		mutex:                &sync.Mutex{},
	}, nil
}
<commit_msg>provider/azure: Fix SQL client name to match upstream<commit_after>package azure

import (
	"fmt"
	"os"
	"sync"

	"github.com/Azure/azure-sdk-for-go/management"
	"github.com/Azure/azure-sdk-for-go/management/hostedservice"
	"github.com/Azure/azure-sdk-for-go/management/networksecuritygroup"
	"github.com/Azure/azure-sdk-for-go/management/osimage"
	"github.com/Azure/azure-sdk-for-go/management/sql"
	"github.com/Azure/azure-sdk-for-go/management/storageservice"
	"github.com/Azure/azure-sdk-for-go/management/virtualmachine"
	"github.com/Azure/azure-sdk-for-go/management/virtualmachinedisk"
	"github.com/Azure/azure-sdk-for-go/management/virtualmachineimage"
	"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
	"github.com/Azure/azure-sdk-for-go/storage"
)

// Config is the configuration structure used to instantiate a
// new Azure management client.
type Config struct {
	SettingsFile   string
	SubscriptionID string
	Certificate    []byte
	ManagementURL  string
}

// Client contains all the handles required for managing Azure services.
type Client struct {
	mgmtClient management.Client

	hostedServiceClient hostedservice.HostedServiceClient

	secGroupClient networksecuritygroup.SecurityGroupClient

	osImageClient osimage.OSImageClient

	sqlClient sql.SQLDatabaseClient

	storageServiceClient storageservice.StorageServiceClient

	vmClient virtualmachine.VirtualMachineClient

	vmDiskClient virtualmachinedisk.DiskClient

	vmImageClient virtualmachineimage.Client

	// unfortunately; because of how Azure's network API works; doing networking operations
	// concurrently is very hazardous, and we need a mutex to guard the VirtualNetworkClient.
	vnetClient virtualnetwork.VirtualNetworkClient
	mutex      *sync.Mutex
}

// getStorageClientForStorageService is helper method which returns the
// storage.Client associated to the given storage service name.
func (c Client) getStorageClientForStorageService(serviceName string) (storage.Client, error) {
	var storageClient storage.Client

	keys, err := c.storageServiceClient.GetStorageServiceKeys(serviceName)
	if err != nil {
		return storageClient, fmt.Errorf("Failed getting Storage Service keys for %s: %s", serviceName, err)
	}

	storageClient, err = storage.NewBasicClient(serviceName, keys.PrimaryKey)
	if err != nil {
		return storageClient, fmt.Errorf("Failed creating Storage Service client for %s: %s", serviceName, err)
	}

	return storageClient, err
}

// getStorageServiceBlobClient is a helper method which returns the
// storage.BlobStorageClient associated to the given storage service name.
func (c Client) getStorageServiceBlobClient(serviceName string) (storage.BlobStorageClient, error) {
	storageClient, err := c.getStorageClientForStorageService(serviceName)
	if err != nil {
		return storage.BlobStorageClient{}, err
	}

	return storageClient.GetBlobService(), nil
}

// getStorageServiceQueueClient is a helper method which returns the
// storage.QueueServiceClient associated to the given storage service name.
func (c Client) getStorageServiceQueueClient(serviceName string) (storage.QueueServiceClient, error) {
	storageClient, err := c.getStorageClientForStorageService(serviceName)
	if err != nil {
		return storage.QueueServiceClient{}, err
	}

	return storageClient.GetQueueService(), err
}

// NewClientFromSettingsFile returns a new Azure management
// client created using a publish settings file.
func (c *Config) NewClientFromSettingsFile() (*Client, error) {
	if _, err := os.Stat(c.SettingsFile); os.IsNotExist(err) {
		return nil, fmt.Errorf("Publish Settings file %q does not exist!", c.SettingsFile)
	}

	mc, err := management.ClientFromPublishSettingsFile(c.SettingsFile, c.SubscriptionID)
	if err != nil {
		return nil, nil
	}

	return &Client{
		mgmtClient:           mc,
		hostedServiceClient:  hostedservice.NewClient(mc),
		secGroupClient:       networksecuritygroup.NewClient(mc),
		osImageClient:        osimage.NewClient(mc),
		sqlClient:            sql.NewClient(mc),
		storageServiceClient: storageservice.NewClient(mc),
		vmClient:             virtualmachine.NewClient(mc),
		vmDiskClient:         virtualmachinedisk.NewClient(mc),
		vmImageClient:        virtualmachineimage.NewClient(mc),
		vnetClient:           virtualnetwork.NewClient(mc),
		mutex:                &sync.Mutex{},
	}, nil
}

// NewClient returns a new Azure management client created
// using a subscription ID and certificate.
func (c *Config) NewClient() (*Client, error) {
	mc, err := management.NewClient(c.SubscriptionID, c.Certificate)
	if err != nil {
		return nil, nil
	}

	return &Client{
		mgmtClient:           mc,
		hostedServiceClient:  hostedservice.NewClient(mc),
		secGroupClient:       networksecuritygroup.NewClient(mc),
		osImageClient:        osimage.NewClient(mc),
		sqlClient:            sql.NewClient(mc),
		storageServiceClient: storageservice.NewClient(mc),
		vmClient:             virtualmachine.NewClient(mc),
		vmDiskClient:         virtualmachinedisk.NewClient(mc),
		vmImageClient:        virtualmachineimage.NewClient(mc),
		vnetClient:           virtualnetwork.NewClient(mc),
		mutex:                &sync.Mutex{},
	}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"
)

// HelpForConfig outputs usage information for the bigv config command.
func (cmds *CommandSet) HelpForConfig() {
	fmt.Println("go-bigv config")
	fmt.Println()
	fmt.Println("Usage:")
	fmt.Println("    go-bigv config")
	fmt.Println("        Outputs the current values of all variables and what source they were derived from")
	fmt.Println()
	fmt.Println("    go-bigv config set <variable> <value>")
	fmt.Println("        Sets a variable by writing to your bigv config (usually ~/.go-bigv)")
	fmt.Println()
	fmt.Println("    go-bigv config unset <variable>")
	fmt.Println("        Unsets a variable by removing data from bigv config (usually ~/.go-bigv)")
	fmt.Println()
	fmt.Println("Available variables:")
	fmt.Println("    endpoint - the BigV endpoint to connect to. https://uk0.bigv.io is the default")
	fmt.Println("    auth-endpoint - the endpoint to authenticate to. https://auth.bytemark.co.uk is the default.")
	fmt.Println("    debug-level - the default debug level. Set to 0 unless you like lots of output")
	fmt.Println()
}

// Config provides the bigv config command, which sets variables in the user's config. See HelpForConfig for usage information.
// It's slightly more user friendly than echo "value" > ~/.go-bigv/
func (cmds *CommandSet) Config(args []string) ExitCode {
	if len(args) == 0 {
		vars, err := cmds.config.GetAll()
		if err != nil {
			return processError(err)
		}
		for _, v := range vars {
			fmt.Printf("%s\t: '%s' (%s)\r\n", v.Name, v.Value, v.Source)
		}
		return E_SUCCESS
	} else if len(args) == 1 {
		cmds.HelpForConfig()
		return E_SUCCESS
	}

	switch strings.ToLower(args[0]) {
	case "set":
		variable := strings.ToLower(args[1])

		oldVar, err := cmds.config.GetV(variable)
		if err != nil {
			if e, ok := err.(*ConfigReadError); ok {
				fmt.Fprintf(os.Stderr, "Couldn't read the old value of %s - %v\r\n", e.Name, e.Err)
			} else {
				fmt.Fprintf(os.Stderr, "Couldn't read the old value of %s - %v\r\n", variable, err)
			}
			return E_CANT_READ_CONFIG
		}

		if len(args) == 2 {
			fmt.Printf("%s: '%s' (%s)\r\n", oldVar.Name, oldVar.Value, oldVar.Source)
			return E_SUCCESS
		}

		// TODO(telyn): consider validating input for the set command
		// TODO(telyn): This should possibly return errors.
		err = cmds.config.SetPersistent(variable, args[2], "CMD set")
		if err != nil {
			if e, ok := err.(*ConfigReadError); ok {
				fmt.Fprintf(os.Stderr, "Couldn't set %s - %v\r\n", e.Name, e.Err)
			} else {
				fmt.Fprintf(os.Stderr, "Couldn't set %s - %v\r\n", variable, err)
			}
			return E_CANT_WRITE_CONFIG
		}

		if oldVar.Source == "config" && !cmds.config.Silent() {
			fmt.Printf("%s has been changed.\r\nOld value: %s\r\nNew value: %s\r\n", variable, oldVar.Value, args[1])
		} else if !cmds.config.Silent() {
			fmt.Printf("%s has been set. \r\nNew value: %s\r\n", variable, args[1])
		}

	case "unset":
		// TODO(telyn): write this...
	default:
		fmt.Printf("Unrecognised command %s\r\n", args[0])
		cmds.HelpForConfig()
	}
	return E_SUCCESS
}
<commit_msg>Implement unset<commit_after>package main

import (
	"fmt"
	"os"
	"strings"
)

// HelpForConfig outputs usage information for the bigv config command.
func (cmds *CommandSet) HelpForConfig() {
	fmt.Println("go-bigv config")
	fmt.Println()
	fmt.Println("Usage:")
	fmt.Println("    go-bigv config")
	fmt.Println("        Outputs the current values of all variables and what source they were derived from")
	fmt.Println()
	fmt.Println("    go-bigv config set <variable> <value>")
	fmt.Println("        Sets a variable by writing to your bigv config (usually ~/.go-bigv)")
	fmt.Println()
	fmt.Println("    go-bigv config unset <variable>")
	fmt.Println("        Unsets a variable by removing data from bigv config (usually ~/.go-bigv)")
	fmt.Println()
	fmt.Println("Available variables:")
	fmt.Println("    endpoint - the BigV endpoint to connect to. https://uk0.bigv.io is the default")
	fmt.Println("    auth-endpoint - the endpoint to authenticate to. https://auth.bytemark.co.uk is the default.")
	fmt.Println("    debug-level - the default debug level. Set to 0 unless you like lots of output")
	fmt.Println()
}

// Config provides the bigv config command, which sets variables in the user's config. See HelpForConfig for usage information.
// It's slightly more user friendly than echo "value" > ~/.go-bigv/
func (cmds *CommandSet) Config(args []string) ExitCode {
	if len(args) == 0 {
		vars, err := cmds.config.GetAll()
		if err != nil {
			return processError(err)
		}
		for _, v := range vars {
			fmt.Printf("%s\t: '%s' (%s)\r\n", v.Name, v.Value, v.Source)
		}
		return E_SUCCESS
	} else if len(args) == 1 {
		cmds.HelpForConfig()
		return E_SUCCESS
	}

	switch strings.ToLower(args[0]) {
	case "set":
		variable := strings.ToLower(args[1])

		oldVar, err := cmds.config.GetV(variable)
		if err != nil {
			if e, ok := err.(*ConfigReadError); ok {
				fmt.Fprintf(os.Stderr, "Couldn't read the old value of %s - %v\r\n", e.Name, e.Err)
			} else {
				fmt.Fprintf(os.Stderr, "Couldn't read the old value of %s - %v\r\n", variable, err)
			}
			return E_CANT_READ_CONFIG
		}

		if len(args) == 2 {
			fmt.Printf("%s: '%s' (%s)\r\n", oldVar.Name, oldVar.Value, oldVar.Source)
			return E_SUCCESS
		}

		// TODO(telyn): consider validating input for the set command
		// TODO(telyn): This should possibly return errors.
		err = cmds.config.SetPersistent(variable, args[2], "CMD set")
		if err != nil {
			if e, ok := err.(*ConfigReadError); ok {
				fmt.Fprintf(os.Stderr, "Couldn't set %s - %v\r\n", e.Name, e.Err)
			} else {
				fmt.Fprintf(os.Stderr, "Couldn't set %s - %v\r\n", variable, err)
			}
			return E_CANT_WRITE_CONFIG
		}

		if oldVar.Source == "config" && !cmds.config.Silent() {
			fmt.Printf("%s has been changed.\r\nOld value: %s\r\nNew value: %s\r\n", variable, oldVar.Value, args[1])
		} else if !cmds.config.Silent() {
			fmt.Printf("%s has been set. \r\nNew value: %s\r\n", variable, args[1])
		}

	case "unset":
		variable := strings.ToLower(args[0])
		err := cmds.config.Unset(variable)
		return processError(err)
	default:
		fmt.Printf("Unrecognised command %s\r\n", args[0])
		cmds.HelpForConfig()
	}
	return E_SUCCESS
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof" // register in DefaultServerMux
	"os"
	"time"

	"crypto/tls"

	"github.com/grpc-ecosystem/go-grpc-middleware"
	"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
	"github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/improbable-eng/grpc-web/go/grpcweb"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/grpc-proxy/proxy"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
	"golang.org/x/net/context"
	_ "golang.org/x/net/trace" // register in DefaultServerMux
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

var (
	flagBindAddr    = pflag.String("server_bind_address", "0.0.0.0", "address to bind the server to")
	flagHttpPort    = pflag.Int("server_http_debug_port", 8080, "TCP port to listen on for HTTP1.1 debug calls.")
	flagHttpTlsPort = pflag.Int("server_http_tls_port", 8443, "TCP port to listen on for HTTPS (gRPC, gRPC-Web).")

	runHttpServer = pflag.Bool("run_http_server", true, "whether to run HTTP server")
	runTlsServer  = pflag.Bool("run_tls_server", true, "whether to run TLS server")

	flagHttpMaxWriteTimeout = pflag.Duration("server_http_max_write_timeout", 10*time.Second, "HTTP server config, max write duration.")
	flagHttpMaxReadTimeout  = pflag.Duration("server_http_max_read_timeout", 10*time.Second, "HTTP server config, max read duration.")
)

func main() {
	pflag.Parse()

	logrus.SetOutput(os.Stdout)

	logEntry := logrus.NewEntry(logrus.StandardLogger())

	grpcServer := buildGrpcProxyServer(logEntry)
	errChan := make(chan error)

	// gRPC-Web compatibility layer with CORS configured to accept on every
	wrappedGrpc := grpcweb.WrapServer(grpcServer, grpcweb.WithCorsForRegisteredEndpointsOnly(false))

	if !*runHttpServer && !*runTlsServer {
		logrus.Fatalf("Both run_http_server and run_tls_server are set to false. At least one must be enabled for grpcweb proxy to function correctly.")
	}

	if *runHttpServer {
		// Debug server.
		debugServer := buildServer(wrappedGrpc)
		http.Handle("/metrics", promhttp.Handler())
		debugListener := buildListenerOrFail("http", *flagHttpPort)
		serveServer(debugServer, debugListener, "http", errChan)
	}

	if *runTlsServer {
		// Debug server.
		servingServer := buildServer(wrappedGrpc)
		servingListener := buildListenerOrFail("http", *flagHttpTlsPort)
		servingListener = tls.NewListener(servingListener, buildServerTlsOrFail())
		serveServer(servingServer, servingListener, "http_tls", errChan)
	}

	<-errChan
	// TODO(mwitkow): Add graceful shutdown.
}

func buildServer(wrappedGrpc *grpcweb.WrappedGrpcServer) *http.Server {
	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			wrappedGrpc.ServeHTTP(resp, req)
		}),
	}
}

func serveServer(server *http.Server, listener net.Listener, name string, errChan chan error) {
	go func() {
		logrus.Infof("listening for %s on: %v", name, listener.Addr().String())
		if err := server.Serve(listener); err != nil {
			errChan <- fmt.Errorf("%s server error: %v", name, err)
		}
	}()
}

func buildGrpcProxyServer(logger *logrus.Entry) *grpc.Server {
	// gRPC-wide changes.
	grpc.EnableTracing = true
	grpc_logrus.ReplaceGrpcLogger(logger)

	// gRPC proxy logic.
	backendConn := dialBackendOrFail()
	director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
		md, _ := metadata.FromIncomingContext(ctx)
		outCtx, _ := context.WithCancel(ctx)
    	mdCopy := md.Copy()
    	delete(mdCopy, "user-agent")
		outCtx = metadata.NewOutgoingContext(outCtx, mdCopy)
		return outCtx, backendConn, nil
	}
	// Server with logging and monitoring enabled.
	return grpc.NewServer(
		grpc.CustomCodec(proxy.Codec()), // needed for proxy to function.
		grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
		grpc_middleware.WithUnaryServerChain(
			grpc_logrus.UnaryServerInterceptor(logger),
			grpc_prometheus.UnaryServerInterceptor,
		),
		grpc_middleware.WithStreamServerChain(
			grpc_logrus.StreamServerInterceptor(logger),
			grpc_prometheus.StreamServerInterceptor,
		),
	)
}

func buildListenerOrFail(name string, port int) net.Listener {
	addr := fmt.Sprintf("%s:%d", *flagBindAddr, port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("failed listening for '%v' on %v: %v", name, port, err)
	}
	return conntrack.NewListener(listener,
		conntrack.TrackWithName(name),
		conntrack.TrackWithTcpKeepAlive(20*time.Second),
		conntrack.TrackWithTracing(),
	)
}
<commit_msg>Fix indenting<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof" // register in DefaultServerMux
	"os"
	"time"

	"crypto/tls"

	"github.com/grpc-ecosystem/go-grpc-middleware"
	"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
	"github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/improbable-eng/grpc-web/go/grpcweb"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/grpc-proxy/proxy"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
	"golang.org/x/net/context"
	_ "golang.org/x/net/trace" // register in DefaultServerMux
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

var (
	flagBindAddr    = pflag.String("server_bind_address", "0.0.0.0", "address to bind the server to")
	flagHttpPort    = pflag.Int("server_http_debug_port", 8080, "TCP port to listen on for HTTP1.1 debug calls.")
	flagHttpTlsPort = pflag.Int("server_http_tls_port", 8443, "TCP port to listen on for HTTPS (gRPC, gRPC-Web).")

	runHttpServer = pflag.Bool("run_http_server", true, "whether to run HTTP server")
	runTlsServer  = pflag.Bool("run_tls_server", true, "whether to run TLS server")

	flagHttpMaxWriteTimeout = pflag.Duration("server_http_max_write_timeout", 10*time.Second, "HTTP server config, max write duration.")
	flagHttpMaxReadTimeout  = pflag.Duration("server_http_max_read_timeout", 10*time.Second, "HTTP server config, max read duration.")
)

func main() {
	pflag.Parse()

	logrus.SetOutput(os.Stdout)

	logEntry := logrus.NewEntry(logrus.StandardLogger())

	grpcServer := buildGrpcProxyServer(logEntry)
	errChan := make(chan error)

	// gRPC-Web compatibility layer with CORS configured to accept on every
	wrappedGrpc := grpcweb.WrapServer(grpcServer, grpcweb.WithCorsForRegisteredEndpointsOnly(false))

	if !*runHttpServer && !*runTlsServer {
		logrus.Fatalf("Both run_http_server and run_tls_server are set to false. At least one must be enabled for grpcweb proxy to function correctly.")
	}

	if *runHttpServer {
		// Debug server.
		debugServer := buildServer(wrappedGrpc)
		http.Handle("/metrics", promhttp.Handler())
		debugListener := buildListenerOrFail("http", *flagHttpPort)
		serveServer(debugServer, debugListener, "http", errChan)
	}

	if *runTlsServer {
		// Debug server.
		servingServer := buildServer(wrappedGrpc)
		servingListener := buildListenerOrFail("http", *flagHttpTlsPort)
		servingListener = tls.NewListener(servingListener, buildServerTlsOrFail())
		serveServer(servingServer, servingListener, "http_tls", errChan)
	}

	<-errChan
	// TODO(mwitkow): Add graceful shutdown.
}

func buildServer(wrappedGrpc *grpcweb.WrappedGrpcServer) *http.Server {
	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			wrappedGrpc.ServeHTTP(resp, req)
		}),
	}
}

func serveServer(server *http.Server, listener net.Listener, name string, errChan chan error) {
	go func() {
		logrus.Infof("listening for %s on: %v", name, listener.Addr().String())
		if err := server.Serve(listener); err != nil {
			errChan <- fmt.Errorf("%s server error: %v", name, err)
		}
	}()
}

func buildGrpcProxyServer(logger *logrus.Entry) *grpc.Server {
	// gRPC-wide changes.
	grpc.EnableTracing = true
	grpc_logrus.ReplaceGrpcLogger(logger)

	// gRPC proxy logic.
	backendConn := dialBackendOrFail()
	director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
		md, _ := metadata.FromIncomingContext(ctx)
		outCtx, _ := context.WithCancel(ctx)
		mdCopy := md.Copy()
		delete(mdCopy, "user-agent")
		outCtx = metadata.NewOutgoingContext(outCtx, mdCopy)
		return outCtx, backendConn, nil
	}
	// Server with logging and monitoring enabled.
	return grpc.NewServer(
		grpc.CustomCodec(proxy.Codec()), // needed for proxy to function.
		grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
		grpc_middleware.WithUnaryServerChain(
			grpc_logrus.UnaryServerInterceptor(logger),
			grpc_prometheus.UnaryServerInterceptor,
		),
		grpc_middleware.WithStreamServerChain(
			grpc_logrus.StreamServerInterceptor(logger),
			grpc_prometheus.StreamServerInterceptor,
		),
	)
}

func buildListenerOrFail(name string, port int) net.Listener {
	addr := fmt.Sprintf("%s:%d", *flagBindAddr, port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("failed listening for '%v' on %v: %v", name, port, err)
	}
	return conntrack.NewListener(listener,
		conntrack.TrackWithName(name),
		conntrack.TrackWithTcpKeepAlive(20*time.Second),
		conntrack.TrackWithTracing(),
	)
}
<|endoftext|>