<commit_before>// Copyright 2016 The Go 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 gt

import (
	"bytes"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"time"
)

type vcsNewer func(h *HttpHandler) VcsHandle

type HttpHandler struct {
	runner
	httpAddr string
	vcsAddr  string
	vcsName  string
	pkg      string
	l        net.Listener
	g        *GopathTest
	newer    vcsNewer

	handles map[string]VcsHandle
}

func (h *HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	out := w

	const templ = `<meta name="go-import" content="%s %s %s">
`
	p := strings.TrimPrefix(r.URL.Path, "/")
	var handle VcsHandle
	for _, try := range h.handles {
		if strings.HasPrefix(p, try.pkg()) {
			handle = try
			break
		}
	}
	if handle == nil {
		http.Error(w, "repo not found", http.StatusNotFound)
		return
	}

	fmt.Fprintf(out, templ, h.httpAddr+"/"+handle.pkg(), h.vcsName, h.vcsAddr+handle.pkg()+"/.git")
}

func (h *HttpHandler) Close() error {
	return h.l.Close()
}
func (h *HttpHandler) HttpAddr() string {
	return h.httpAddr
}

// Setup returns type with Remove function that can be defer'ed.
func (h *HttpHandler) Setup() VcsHandle {
	vcs := h.newer(h)
	vcs.create()
	h.g.onClean(vcs.remove)

	h.handles[vcs.pkg()] = vcs
	return vcs
}

func NewHttpHandler(g *GopathTest, vcsName string) *HttpHandler {
	// Test if git is installed. If it is, enable the git test.
	// If enabled, start the http server and accept git server registrations.
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil
	}

	h := &HttpHandler{
		runner: runner{
			cwd: g.Current(),
			t:   g,
		},
		pkg:      g.pkg,
		vcsName:  vcsName,
		httpAddr: l.Addr().String(),
		l:        l,
		g:        g,

		handles: make(map[string]VcsHandle, 6),
	}
	go func() {
		err = http.Serve(l, h)
		if err != nil {
			fmt.Printf("Error serving HTTP server %v\n", err)
			os.Exit(1)
		}
	}()

	execPath, _ := exec.LookPath(vcsName)
	if len(execPath) == 0 {
		g.Skip("unsupported vcs")
	}
	h.execPath = execPath
	switch vcsName {
	default:
		panic("unknown vcs type")
	case "git":
		port := h.freePort()
		h.vcsAddr = fmt.Sprintf("git://localhost:%d/", port)

		h.runAsync(" Ready ", "daemon",
			"--listen=localhost", fmt.Sprintf("--port=%d", port),
			"--export-all", "--verbose", "--informative-errors",
			"--base-path="+g.Path(""), h.cwd,
		)
		fmt.Printf("base-path %q, serve %q\n", g.Path(""), h.cwd)

		h.newer = func(h *HttpHandler) VcsHandle {
			return &gitVcsHandle{
				vcsCommon: vcsCommon{
					runner: runner{
						execPath: execPath,
						cwd:      h.g.Current(),
						t:        h.g,
					},
					h:          h,
					importPath: h.g.pkg,
				},
			}
		}
	}
	return h
}

type vcsCommon struct {
	runner
	importPath string

	h *HttpHandler
}

func (vcs *vcsCommon) pkg() string {
	return vcs.importPath
}

type VcsHandle interface {
	remove()
	pkg() string
	create()
	Commit() (rev string, commitTime string)
}

type gitVcsHandle struct {
	vcsCommon
}

func (vcs *gitVcsHandle) remove() {
	delete(vcs.h.handles, vcs.pkg())
}
func (vcs *gitVcsHandle) create() {
	vcs.run("init")
	vcs.run("config", "user.name", "tests")
	vcs.run("config", "user.email", "tests@govendor.io")
}

func (vcs *gitVcsHandle) Commit() (rev string, commitTime string) {
	vcs.run("add", "-A")
	vcs.run("commit", "-a", "-m", "msg")
	out := vcs.run("show", "--pretty=format:%H@%ai", "-s")

	line := strings.TrimSpace(string(out))
	ss := strings.Split(line, "@")
	rev = ss[0]
	tm, err := time.Parse("2006-01-02 15:04:05 -0700", ss[1])
	if err != nil {
		panic("Failed to parse time: " + ss[1] + " : " + err.Error())
	}

	return rev, tm.UTC().Format(time.RFC3339)
}

type runner struct {
	execPath string
	cwd      string
	t        *GopathTest
}

func (r *runner) run(args ...string) []byte {
	cmd := exec.Command(r.execPath, args...)
	cmd.Dir = r.cwd
	out, err := cmd.CombinedOutput()
	if err != nil {
		r.t.Fatalf("Failed to run %q %q: %v", r.execPath, args, err)
	}
	return out
}

func (r *runner) freePort() int {
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		r.t.Fatalf("Failed to find free port %v", err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	l.Close()
	if runtime.GOOS == "windows" {
		time.Sleep(time.Millisecond * 300) // Wait for OS to release port.
	}
	return port
}

// Prevents a race condition in runAsync.
type safeBuf struct {
	sync.Mutex
	buf bytes.Buffer
}

func (s *safeBuf) Write(b []byte) (int, error) {
	s.Lock()
	defer s.Unlock()
	return s.buf.Write(b)
}
func (s *safeBuf) String() string {
	s.Lock()
	defer s.Unlock()
	return s.buf.String()
}

func (r *runner) runAsync(checkFor string, args ...string) *exec.Cmd {
	cmd := exec.Command(r.execPath, args...)
	cmd.Dir = r.t.Current()

	var buf *safeBuf
	var bufErr *safeBuf
	if checkFor != "" {
		buf = &safeBuf{}
		bufErr = &safeBuf{}
		cmd.Stdout = buf
		cmd.Stderr = bufErr
	}
	err := cmd.Start()
	if err != nil {
		r.t.Fatalf("Failed to start %q %q: %v", r.execPath, args)
	}
	r.t.onClean(func() {
		if cmd.Process == nil {
			return
		}
		cmd.Process.Signal(os.Interrupt)

		done := make(chan struct{}, 3)
		go func() {
			cmd.Process.Wait()
			done <- struct{}{}
		}()
		select {
		case <-time.After(time.Millisecond * 300):
			cmd.Process.Kill()
		case <-done:
		}

		r.t.Logf("%q StdOut: %s\n", cmd.Path, buf.String())
		r.t.Logf("%q StdErr: %s\n", cmd.Path, bufErr.String())
	})
	if checkFor != "" {
		for i := 0; i < 100; i++ {
			if cmd.ProcessState != nil && cmd.ProcessState.Exited() {
				r.t.Fatalf("unexpected stop %q %q\n%s\n%s\n", r.execPath, args, buf.String(), bufErr.String())
			}
			if strings.Contains(buf.String(), checkFor) {
				return cmd
			}
			if strings.Contains(bufErr.String(), checkFor) {
				return cmd
			}
			time.Sleep(time.Millisecond * 10)
		}
		r.t.Fatalf("failed to read expected output %q from %q %q\n%s\n", checkFor, r.execPath, args, bufErr.String())
	}
	return cmd
}
<commit_msg>internal/gt: don't run git tests on windows for now<commit_after>// Copyright 2016 The Go 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 gt

import (
	"bytes"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"time"
)

type vcsNewer func(h *HttpHandler) VcsHandle

type HttpHandler struct {
	runner
	httpAddr string
	vcsAddr  string
	vcsName  string
	pkg      string
	l        net.Listener
	g        *GopathTest
	newer    vcsNewer

	handles map[string]VcsHandle
}

func (h *HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	out := w

	const templ = `<meta name="go-import" content="%s %s %s">
`
	p := strings.TrimPrefix(r.URL.Path, "/")
	var handle VcsHandle
	for _, try := range h.handles {
		if strings.HasPrefix(p, try.pkg()) {
			handle = try
			break
		}
	}
	if handle == nil {
		http.Error(w, "repo not found", http.StatusNotFound)
		return
	}

	fmt.Fprintf(out, templ, h.httpAddr+"/"+handle.pkg(), h.vcsName, h.vcsAddr+handle.pkg()+"/.git")
}

func (h *HttpHandler) Close() error {
	return h.l.Close()
}
func (h *HttpHandler) HttpAddr() string {
	return h.httpAddr
}

// Setup returns type with Remove function that can be defer'ed.
func (h *HttpHandler) Setup() VcsHandle {
	vcs := h.newer(h)
	vcs.create()
	h.g.onClean(vcs.remove)

	h.handles[vcs.pkg()] = vcs
	return vcs
}

func NewHttpHandler(g *GopathTest, vcsName string) *HttpHandler {
	if runtime.GOOS == "windows" {
		g.Skip("ports in the import path currently don't work on windows"
	}
	// Test if git is installed. If it is, enable the git test.
	// If enabled, start the http server and accept git server registrations.
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil
	}

	h := &HttpHandler{
		runner: runner{
			cwd: g.Current(),
			t:   g,
		},
		pkg:      g.pkg,
		vcsName:  vcsName,
		httpAddr: l.Addr().String(),
		l:        l,
		g:        g,

		handles: make(map[string]VcsHandle, 6),
	}
	go func() {
		err = http.Serve(l, h)
		if err != nil {
			fmt.Printf("Error serving HTTP server %v\n", err)
			os.Exit(1)
		}
	}()

	execPath, _ := exec.LookPath(vcsName)
	if len(execPath) == 0 {
		g.Skip("unsupported vcs")
	}
	h.execPath = execPath
	switch vcsName {
	default:
		panic("unknown vcs type")
	case "git":
		port := h.freePort()
		h.vcsAddr = fmt.Sprintf("git://localhost:%d/", port)

		h.runAsync(" Ready ", "daemon",
			"--listen=localhost", fmt.Sprintf("--port=%d", port),
			"--export-all", "--verbose", "--informative-errors",
			"--base-path="+g.Path(""), h.cwd,
		)
		fmt.Printf("base-path %q, serve %q\n", g.Path(""), h.cwd)

		h.newer = func(h *HttpHandler) VcsHandle {
			return &gitVcsHandle{
				vcsCommon: vcsCommon{
					runner: runner{
						execPath: execPath,
						cwd:      h.g.Current(),
						t:        h.g,
					},
					h:          h,
					importPath: h.g.pkg,
				},
			}
		}
	}
	return h
}

type vcsCommon struct {
	runner
	importPath string

	h *HttpHandler
}

func (vcs *vcsCommon) pkg() string {
	return vcs.importPath
}

type VcsHandle interface {
	remove()
	pkg() string
	create()
	Commit() (rev string, commitTime string)
}

type gitVcsHandle struct {
	vcsCommon
}

func (vcs *gitVcsHandle) remove() {
	delete(vcs.h.handles, vcs.pkg())
}
func (vcs *gitVcsHandle) create() {
	vcs.run("init")
	vcs.run("config", "user.name", "tests")
	vcs.run("config", "user.email", "tests@govendor.io")
}

func (vcs *gitVcsHandle) Commit() (rev string, commitTime string) {
	vcs.run("add", "-A")
	vcs.run("commit", "-a", "-m", "msg")
	out := vcs.run("show", "--pretty=format:%H@%ai", "-s")

	line := strings.TrimSpace(string(out))
	ss := strings.Split(line, "@")
	rev = ss[0]
	tm, err := time.Parse("2006-01-02 15:04:05 -0700", ss[1])
	if err != nil {
		panic("Failed to parse time: " + ss[1] + " : " + err.Error())
	}

	return rev, tm.UTC().Format(time.RFC3339)
}

type runner struct {
	execPath string
	cwd      string
	t        *GopathTest
}

func (r *runner) run(args ...string) []byte {
	cmd := exec.Command(r.execPath, args...)
	cmd.Dir = r.cwd
	out, err := cmd.CombinedOutput()
	if err != nil {
		r.t.Fatalf("Failed to run %q %q: %v", r.execPath, args, err)
	}
	return out
}

func (r *runner) freePort() int {
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		r.t.Fatalf("Failed to find free port %v", err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	l.Close()
	if runtime.GOOS == "windows" {
		time.Sleep(time.Millisecond * 300) // Wait for OS to release port.
	}
	return port
}

// Prevents a race condition in runAsync.
type safeBuf struct {
	sync.Mutex
	buf bytes.Buffer
}

func (s *safeBuf) Write(b []byte) (int, error) {
	s.Lock()
	defer s.Unlock()
	return s.buf.Write(b)
}
func (s *safeBuf) String() string {
	s.Lock()
	defer s.Unlock()
	return s.buf.String()
}

func (r *runner) runAsync(checkFor string, args ...string) *exec.Cmd {
	cmd := exec.Command(r.execPath, args...)
	cmd.Dir = r.t.Current()

	var buf *safeBuf
	var bufErr *safeBuf
	if checkFor != "" {
		buf = &safeBuf{}
		bufErr = &safeBuf{}
		cmd.Stdout = buf
		cmd.Stderr = bufErr
	}
	err := cmd.Start()
	if err != nil {
		r.t.Fatalf("Failed to start %q %q: %v", r.execPath, args)
	}
	r.t.onClean(func() {
		if cmd.Process == nil {
			return
		}
		cmd.Process.Signal(os.Interrupt)

		done := make(chan struct{}, 3)
		go func() {
			cmd.Process.Wait()
			done <- struct{}{}
		}()
		select {
		case <-time.After(time.Millisecond * 300):
			cmd.Process.Kill()
		case <-done:
		}

		r.t.Logf("%q StdOut: %s\n", cmd.Path, buf.String())
		r.t.Logf("%q StdErr: %s\n", cmd.Path, bufErr.String())
	})
	if checkFor != "" {
		for i := 0; i < 100; i++ {
			if cmd.ProcessState != nil && cmd.ProcessState.Exited() {
				r.t.Fatalf("unexpected stop %q %q\n%s\n%s\n", r.execPath, args, buf.String(), bufErr.String())
			}
			if strings.Contains(buf.String(), checkFor) {
				return cmd
			}
			if strings.Contains(bufErr.String(), checkFor) {
				return cmd
			}
			time.Sleep(time.Millisecond * 10)
		}
		r.t.Fatalf("failed to read expected output %q from %q %q\n%s\n", checkFor, r.execPath, args, bufErr.String())
	}
	return cmd
}
<|endoftext|>


<commit_before>// Package github implements the OAuth2 protocol for authenticating users through Github.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package github

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/markbates/goth"
	"golang.org/x/oauth2"
)

// These vars define the Authentication, Token, and API URLS for GitHub. If
// using GitHub enterprise you should change these values before calling New.
//
// Examples:
//	github.AuthURL = "https://github.acme.com/login/oauth/authorize
//	github.TokenURL = "https://github.acme.com/login/oauth/access_token
//	github.ProfileURL = "https://github.acme.com/api/v3/user
var (
	AuthURL    = "https://github.com/login/oauth/authorize"
	TokenURL   = "https://github.com/login/oauth/access_token"
	ProfileURL = "https://api.github.com/user"
	EmailURL   = "https://api.github.com/user/emails"
)

// New creates a new Github provider, and sets up important connection details.
// You should always call `github.New` to get a new Provider. Never try to create
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
	p := &Provider{
		ClientKey:   clientKey,
		Secret:      secret,
		CallbackURL: callbackURL,
	}
	p.config = newConfig(p, scopes)
	return p
}

// Provider is the implementation of `goth.Provider` for accessing Github.
type Provider struct {
	ClientKey   string
	Secret      string
	CallbackURL string
	config      *oauth2.Config
}

// Name is the name used to retrieve this provider later.
func (p *Provider) Name() string {
	return "github"
}

// Debug is a no-op for the github package.
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Github for an authentication end-point.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
	url := p.config.AuthCodeURL(state)
	session := &Session{
		AuthURL: url,
	}
	return session, nil
}

// FetchUser will go to Github and access basic information about the user.
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
	sess := session.(*Session)
	user := goth.User{
		AccessToken: sess.AccessToken,
		Provider:    p.Name(),
	}

	response, err := http.Get(ProfileURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
	if err != nil {
		if response != nil {
			response.Body.Close()
		}
		return user, err
	}
	defer response.Body.Close()

	bits, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return user, err
	}

	err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
	if err != nil {
		return user, err
	}

	err = userFromReader(bytes.NewReader(bits), &user)
	if err != nil {
		return user, err
	}

	if user.Email == "" {
		for _, scope := range p.config.Scopes {
			if strings.TrimSpace(scope) == "user" || strings.TrimSpace(scope) == "user:email" {
				user.Email, err = getPrivateMail(p, sess)
				if err != nil {
					return user, err
				}
				break
			}
		}
	}
	return user, err
}

func userFromReader(reader io.Reader, user *goth.User) error {
	u := struct {
		ID       int    `json:"id"`
		Email    string `json:"email"`
		Bio      string `json:"bio"`
		Name     string `json:"name"`
		Login    string `json:"login"`
		Picture  string `json:"avatar_url"`
		Location string `json:"location"`
	}{}

	err := json.NewDecoder(reader).Decode(&u)
	if err != nil {
		return err
	}

	user.Name = u.Name
	user.NickName = u.Login
	user.Email = u.Email
	user.Description = u.Bio
	user.AvatarURL = u.Picture
	user.UserID = strconv.Itoa(u.ID)
	user.Location = u.Location

	return err
}

func getPrivateMail(p *Provider, sess *Session) (email string, err error) {
	response, err := http.Get(EmailURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
	defer response.Body.Close()
	if err != nil {
		if response != nil {
			response.Body.Close()
		}
		return email, err
	}
	var mailList = []struct {
		Email    string `json:"email"`
		Primary  bool   `json:"primary"`
		Verified bool   `json:"verified"`
	}{}
	err = json.NewDecoder(response.Body).Decode(&mailList)
	if err != nil {
		return email, err
	}
	for _, v := range mailList {
		if v.Primary && v.Verified {
			return v.Email, nil
		}
	}
	// can't get primary email - shouldn't be possible
	return
}

func newConfig(provider *Provider, scopes []string) *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     provider.ClientKey,
		ClientSecret: provider.Secret,
		RedirectURL:  provider.CallbackURL,
		Endpoint: oauth2.Endpoint{
			AuthURL:  AuthURL,
			TokenURL: TokenURL,
		},
		Scopes: []string{},
	}

	for _, scope := range scopes {
		c.Scopes = append(c.Scopes, scope)
	}

	return c
}

//RefreshToken refresh token is not provided by github
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
	return nil, errors.New("Refresh token is not provided by github")
}

//RefreshTokenAvailable refresh token is not provided by github
func (p *Provider) RefreshTokenAvailable() bool {
	return false
}
<commit_msg>Handle non-200 responses from GitHub API endpoints<commit_after>// Package github implements the OAuth2 protocol for authenticating users through Github.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package github

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/markbates/goth"
	"golang.org/x/oauth2"
)

// These vars define the Authentication, Token, and API URLS for GitHub. If
// using GitHub enterprise you should change these values before calling New.
//
// Examples:
//	github.AuthURL = "https://github.acme.com/login/oauth/authorize
//	github.TokenURL = "https://github.acme.com/login/oauth/access_token
//	github.ProfileURL = "https://github.acme.com/api/v3/user
//	github.EmailURL = "https://github.acme.com/api/v3/user/emails
var (
	AuthURL    = "https://github.com/login/oauth/authorize"
	TokenURL   = "https://github.com/login/oauth/access_token"
	ProfileURL = "https://api.github.com/user"
	EmailURL   = "https://api.github.com/user/emails"
)

// New creates a new Github provider, and sets up important connection details.
// You should always call `github.New` to get a new Provider. Never try to create
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
	p := &Provider{
		ClientKey:   clientKey,
		Secret:      secret,
		CallbackURL: callbackURL,
	}
	p.config = newConfig(p, scopes)
	return p
}

// Provider is the implementation of `goth.Provider` for accessing Github.
type Provider struct {
	ClientKey   string
	Secret      string
	CallbackURL string
	config      *oauth2.Config
}

// Name is the name used to retrieve this provider later.
func (p *Provider) Name() string {
	return "github"
}

// Debug is a no-op for the github package.
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Github for an authentication end-point.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
	url := p.config.AuthCodeURL(state)
	session := &Session{
		AuthURL: url,
	}
	return session, nil
}

// FetchUser will go to Github and access basic information about the user.
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
	sess := session.(*Session)
	user := goth.User{
		AccessToken: sess.AccessToken,
		Provider:    p.Name(),
	}

	response, err := http.Get(ProfileURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
	if err != nil {
		if response != nil {
			response.Body.Close()
		}
		return user, err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		return user, fmt.Errorf("GitHub API responded with a %d trying to fetch user information", response.StatusCode)
	}

	bits, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return user, err
	}

	err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
	if err != nil {
		return user, err
	}

	err = userFromReader(bytes.NewReader(bits), &user)
	if err != nil {
		return user, err
	}

	if user.Email == "" {
		for _, scope := range p.config.Scopes {
			if strings.TrimSpace(scope) == "user" || strings.TrimSpace(scope) == "user:email" {
				user.Email, err = getPrivateMail(p, sess)
				if err != nil {
					return user, err
				}
				break
			}
		}
	}
	return user, err
}

func userFromReader(reader io.Reader, user *goth.User) error {
	u := struct {
		ID       int    `json:"id"`
		Email    string `json:"email"`
		Bio      string `json:"bio"`
		Name     string `json:"name"`
		Login    string `json:"login"`
		Picture  string `json:"avatar_url"`
		Location string `json:"location"`
	}{}

	err := json.NewDecoder(reader).Decode(&u)
	if err != nil {
		return err
	}

	user.Name = u.Name
	user.NickName = u.Login
	user.Email = u.Email
	user.Description = u.Bio
	user.AvatarURL = u.Picture
	user.UserID = strconv.Itoa(u.ID)
	user.Location = u.Location

	return err
}

func getPrivateMail(p *Provider, sess *Session) (email string, err error) {
	response, err := http.Get(EmailURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
	if err != nil {
		if response != nil {
			response.Body.Close()
		}
		return email, err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		return email, fmt.Errorf("GitHub API responded with a %d trying to fetch user email", response.StatusCode)
	}

	var mailList = []struct {
		Email    string `json:"email"`
		Primary  bool   `json:"primary"`
		Verified bool   `json:"verified"`
	}{}
	err = json.NewDecoder(response.Body).Decode(&mailList)
	if err != nil {
		return email, err
	}
	for _, v := range mailList {
		if v.Primary && v.Verified {
			return v.Email, nil
		}
	}
	// can't get primary email - shouldn't be possible
	return
}

func newConfig(provider *Provider, scopes []string) *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     provider.ClientKey,
		ClientSecret: provider.Secret,
		RedirectURL:  provider.CallbackURL,
		Endpoint: oauth2.Endpoint{
			AuthURL:  AuthURL,
			TokenURL: TokenURL,
		},
		Scopes: []string{},
	}

	for _, scope := range scopes {
		c.Scopes = append(c.Scopes, scope)
	}

	return c
}

//RefreshToken refresh token is not provided by github
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
	return nil, errors.New("Refresh token is not provided by github")
}

//RefreshTokenAvailable refresh token is not provided by github
func (p *Provider) RefreshTokenAvailable() bool {
	return false
}
<|endoftext|>


<commit_before>// Copyright 2012 The Walk 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 declarative

import (
	"github.com/kumakichi/walk"
)

type MainWindow struct {
	AssignTo         **walk.MainWindow
	Name             string
	Enabled          Property
	Visible          Property
	Font             Font
	MinSize          Size
	MaxSize          Size
	ContextMenuItems []MenuItem
	OnKeyDown        walk.KeyEventHandler
	OnKeyPress       walk.KeyEventHandler
	OnKeyUp          walk.KeyEventHandler
	OnMouseDown      walk.MouseEventHandler
	OnMouseMove      walk.MouseEventHandler
	OnMouseUp        walk.MouseEventHandler
	OnSizeChanged    walk.EventHandler
	Title            string
	Size             Size
	DataBinder       DataBinder
	Layout           Layout
	Children         []Widget
	MenuItems        []MenuItem
	ToolBarItems     []MenuItem
	ShowStatusBar    bool
}

func (mw MainWindow) Create() error {
	w, err := walk.NewMainWindow()
	if err != nil {
		return err
	}

	tlwi := topLevelWindowInfo{
		Name:             mw.Name,
		Font:             mw.Font,
		ToolTipText:      "",
		MinSize:          mw.MinSize,
		MaxSize:          mw.MaxSize,
		ContextMenuItems: mw.ContextMenuItems,
		OnKeyDown:        mw.OnKeyDown,
		OnKeyPress:       mw.OnKeyPress,
		OnKeyUp:          mw.OnKeyUp,
		OnMouseDown:      mw.OnMouseDown,
		OnMouseMove:      mw.OnMouseMove,
		OnMouseUp:        mw.OnMouseUp,
		OnSizeChanged:    mw.OnSizeChanged,
		DataBinder:       mw.DataBinder,
		Layout:           mw.Layout,
		Children:         mw.Children,
	}

	builder := NewBuilder(nil)

	w.SetSuspended(true)
	builder.Defer(func() error {
		w.SetSuspended(false)
		return nil
	})

	builder.deferBuildMenuActions(w.Menu(), mw.MenuItems)
	builder.deferBuildActions(w.ToolBar().Actions(), mw.ToolBarItems)

	return builder.InitWidget(tlwi, w, func() error {
		if err := w.SetTitle(mw.Title); err != nil {
			return err
		}

		if err := w.SetSize(mw.Size.toW()); err != nil {
			return err
		}

		imageList, err := walk.NewImageList(walk.Size{16, 16}, 0)
		if err != nil {
			return err
		}
		w.ToolBar().SetImageList(imageList)

		if mw.AssignTo != nil {
			*mw.AssignTo = w
		}

		if mw.ShowStatusBar == true {
			w.StatusBar().SetVisible(true)
		}

		builder.Defer(func() error {
			w.Show()

			return nil
		})

		return nil
	})
}

func (mw MainWindow) Run() (int, error) {
	var w *walk.MainWindow

	if mw.AssignTo == nil {
		mw.AssignTo = &w
	}

	if err := mw.Create(); err != nil {
		return 0, err
	}

	return (*mw.AssignTo).Run(), nil
}
<commit_msg>add CreateHidden()<commit_after>// Copyright 2012 The Walk 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 declarative

import (
	"github.com/kumakichi/walk"
)

type MainWindow struct {
	AssignTo         **walk.MainWindow
	Name             string
	Enabled          Property
	Visible          Property
	Font             Font
	MinSize          Size
	MaxSize          Size
	ContextMenuItems []MenuItem
	OnKeyDown        walk.KeyEventHandler
	OnKeyPress       walk.KeyEventHandler
	OnKeyUp          walk.KeyEventHandler
	OnMouseDown      walk.MouseEventHandler
	OnMouseMove      walk.MouseEventHandler
	OnMouseUp        walk.MouseEventHandler
	OnSizeChanged    walk.EventHandler
	Title            string
	Size             Size
	DataBinder       DataBinder
	Layout           Layout
	Children         []Widget
	MenuItems        []MenuItem
	ToolBarItems     []MenuItem
	ShowStatusBar    bool
}

func (mw MainWindow) Create() error {
	w, err := walk.NewMainWindow()
	if err != nil {
		return err
	}

	tlwi := topLevelWindowInfo{
		Name:             mw.Name,
		Font:             mw.Font,
		ToolTipText:      "",
		MinSize:          mw.MinSize,
		MaxSize:          mw.MaxSize,
		ContextMenuItems: mw.ContextMenuItems,
		OnKeyDown:        mw.OnKeyDown,
		OnKeyPress:       mw.OnKeyPress,
		OnKeyUp:          mw.OnKeyUp,
		OnMouseDown:      mw.OnMouseDown,
		OnMouseMove:      mw.OnMouseMove,
		OnMouseUp:        mw.OnMouseUp,
		OnSizeChanged:    mw.OnSizeChanged,
		DataBinder:       mw.DataBinder,
		Layout:           mw.Layout,
		Children:         mw.Children,
	}

	builder := NewBuilder(nil)

	w.SetSuspended(true)
	builder.Defer(func() error {
		w.SetSuspended(false)
		return nil
	})

	builder.deferBuildMenuActions(w.Menu(), mw.MenuItems)
	builder.deferBuildActions(w.ToolBar().Actions(), mw.ToolBarItems)

	return builder.InitWidget(tlwi, w, func() error {
		if err := w.SetTitle(mw.Title); err != nil {
			return err
		}

		if err := w.SetSize(mw.Size.toW()); err != nil {
			return err
		}

		imageList, err := walk.NewImageList(walk.Size{16, 16}, 0)
		if err != nil {
			return err
		}
		w.ToolBar().SetImageList(imageList)

		if mw.AssignTo != nil {
			*mw.AssignTo = w
		}

		if mw.ShowStatusBar == true {
			w.StatusBar().SetVisible(true)
		}

		builder.Defer(func() error {
			w.Show()

			return nil
		})

		return nil
	})
}

func (mw MainWindow) CreateHidden() error {
	w, err := walk.NewMainWindow()
	if err != nil {
		return err
	}

	tlwi := topLevelWindowInfo{
		Name:             mw.Name,
		Font:             mw.Font,
		ToolTipText:      "",
		MinSize:          mw.MinSize,
		MaxSize:          mw.MaxSize,
		ContextMenuItems: mw.ContextMenuItems,
		OnKeyDown:        mw.OnKeyDown,
		OnKeyPress:       mw.OnKeyPress,
		OnKeyUp:          mw.OnKeyUp,
		OnMouseDown:      mw.OnMouseDown,
		OnMouseMove:      mw.OnMouseMove,
		OnMouseUp:        mw.OnMouseUp,
		OnSizeChanged:    mw.OnSizeChanged,
		DataBinder:       mw.DataBinder,
		Layout:           mw.Layout,
		Children:         mw.Children,
	}

	builder := NewBuilder(nil)

	w.SetSuspended(true)
	builder.Defer(func() error {
		w.SetSuspended(false)
		return nil
	})

	builder.deferBuildMenuActions(w.Menu(), mw.MenuItems)
	builder.deferBuildActions(w.ToolBar().Actions(), mw.ToolBarItems)

	return builder.InitWidget(tlwi, w, func() error {
		if err := w.SetTitle(mw.Title); err != nil {
			return err
		}

		if err := w.SetSize(mw.Size.toW()); err != nil {
			return err
		}

		imageList, err := walk.NewImageList(walk.Size{16, 16}, 0)
		if err != nil {
			return err
		}
		w.ToolBar().SetImageList(imageList)

		if mw.AssignTo != nil {
			*mw.AssignTo = w
		}

		return nil
	})
}

func (mw MainWindow) Run() (int, error) {
	var w *walk.MainWindow

	if mw.AssignTo == nil {
		mw.AssignTo = &w
	}

	if err := mw.Create(); err != nil {
		return 0, err
	}

	return (*mw.AssignTo).Run(), nil
}
<|endoftext|>


<commit_before>package engine

import (
	bd "adexchange/engine/baidu/mobads_api"
	"fmt"
	"github.com/astaxie/beego"
	//"google.golang.org/grpc"
	//"log"
	//	"github.com/golang/protobuf/proto"

	//	"github.com/franela/goreq"
	//	"time"
	//	"bytes"
	//	"github.com/golang/protobuf/proto"
	"adexchange/lib"
	m "adexchange/models"
	"github.com/franela/goreq"
	"github.com/golang/protobuf/proto"
	"strings"
	"time"
)

var (
	OS_MAP = map[int]bd.Device_Os{
		0: bd.Device_ANDROID,
		1: bd.Device_IOS,
		2: bd.Device_IOS,
		3: bd.Device_IOS,
	}
)

//func invokeBD2(demand *Demand) {

//	address := "http://220.181.163.105/api"

//	conn, err := grpc.Dial(address)
//	if err != nil {
//		log.Fatalf("failed to connect: %v", err)
//	}
//	defer conn.Close()

//	// Set up a connection to the server.
////	c := bd.NewBDServiceClient(conn)
////
////	r, err := c.RequestAd(context.Background(), &bd.BidRequest{})
////
////	if err != nil {
////		log.Fatalf("could not get add from server: %v", err)
////	}
////	log.Printf("Greeting: %s", r.ErrorCode)

//	beego.Debug("invoke BD..XXXXXXXXXXXXXXXXXXXXXXXXXXX")

////	beego.Debug(r)

//}

func invokeBD(demand *Demand) {

	// current baidu api version is 4.0
	// TODO move this to conf file
	apiVersion := &bd.Version{
		Major: pUint32(4),
		Minor: pUint32(0),
	}
	//	appVersion := &bd.Version{
	//		Major: pUint32(4),
	//		Minor: pUint32(0),
	//	}

	/*  App   (required)*/

	// TODO put appid in secret_key 字段???
	var appId string = demand.AdSecretKey // required

	// below are optional
	//	var appBundleId string = "com.xxxxx"	// required
	//	var appName string = "";
	//	appCategories := []uint32{uint32(1)}			// required
	//	userPermissionType := bd.App_UserPermission_ACCESS_FINE_LOCATION
	//	userPermissionStatus := bd.App_UserPermission_UNKNOWN
	app := &bd.App{
		Id: &appId,

		// optional.  because there's no categories, so don't provide static info
		//		StaticInfo: &bd.App_StaticInfo{
		//			BundleId: &appBundleId,		// required
		//			Name: &appName,
		//			Categories: appCategories,	// required
		//		},
		//		Version: appVersion,
		//		UserPermission: []*bd.App_UserPermission{
		//			&bd.App_UserPermission{
		//				Type: &userPermissionType,		// required
		//				Status: &userPermissionStatus,					// required
		//			},
		//		},
	}

	/*  Device  (required)*/
	stringArr := strings.Split(demand.AdRequest.Osv, ".")

	devOsVersion := &bd.Version{
		Major: pUint32(lib.ConvertStrToInt(stringArr[0])),
		Minor: pUint32(lib.ConvertStrToInt(stringArr[1])),
	}
	devModel := demand.AdRequest.Device // IPhone5s
	var devVendor string                // Apple
	if demand.AdRequest.Os == 1 {
		devVendor = "Apple"
	} else {
		devVendor = "Google"
	}

	devUdid := bd.Device_UdId{}

	if demand.AdRequest.Idfa != "" {
		devUdid.Idfa = &demand.AdRequest.Idfa
	}
	if demand.AdRequest.Imei != "" {
		devUdid.Imei = &demand.AdRequest.Imei
	}
	if demand.AdRequest.Wma != "" {
		devUdid.Mac = &demand.AdRequest.Wma
	}

	devType := bd.Device_PHONE
	devOs := OS_MAP[demand.AdRequest.Os]
	dev := &bd.Device{
		Type:      &devType,     // required. Mobile, Tablet, TV
		Os:        &devOs,       // required. android or IOS
		OsVersion: devOsVersion, // required. OS version
		Vendor:    &devVendor,   // required.
		Model:     &devModel,    // required.
		Udid:      &devUdid,     // required. ios: idfa, mac, android: imei, mac, tv: imei, mac, idfv
	}

	/*  Network  (required) */
	nt := &bd.Network{
		Ipv4: &demand.AdRequest.Ip,
	}

	/* Adslot  (required) */
	adSpaceId := demand.AdspaceKey
	adWidth := demand.AdRequest.Width
	adHeight := demand.AdRequest.Height
	adSize := bd.Size{
		Width:  pUint32(lib.ConvertStrToInt(adWidth)),  // required
		Height: pUint32(lib.ConvertStrToInt(adHeight)), // required
	}
	//	adType := bd.AdSlot_StaticInfo_BANNER
	//	adStaticInfo := bd.AdSlot_StaticInfo{
	//		Type: &adType,
	//	}

	var requestId string = demand.AdRequest.Bid
	req := bd.BidRequest{
		RequestId:  &requestId,
		ApiVersion: apiVersion,
		App:        app,
		Device:     dev,
		Network:    nt,
		Adslots: []*bd.AdSlot{
			&bd.AdSlot{
				Id:   &adSpaceId, // required.
				Size: &adSize,    // required
				//				StaticInfo: &adStaticInfo,
			},
		},
	}

	beego.Debug("baidu request: ", req.String())

	data, err := proto.Marshal(&req)

	if err != nil {
		generateErrorResp(lib.ERROR_BD_MARSHAL_REQ, "failed to marshal bd request", err, demand)
	} else {
		adResponse := new(m.AdResponse)
		adResponse.Bid = demand.AdRequest.Bid
		adResponse.SetDemandAdspaceKey(demand.AdspaceKey)
		adResponse.SetResponseTime(time.Now().Unix())

		resp, err := goreq.Request{
			Method:  "POST",
			Uri:     demand.URL,
			Timeout: time.Duration(demand.Timeout) * time.Millisecond,
			Body:    data,
		}.Do()

		if serr, ok := err.(*goreq.Error); ok {
			beego.Critical(err.Error())
			if serr.Timeout() {
				generateErrorResp(lib.ERROR_TIMEOUT_ERROR, "failed to send request to baidu", err, demand)

			} else {
				generateErrorResp(lib.ERROR_BD_SERVER, "failed to send request to baidu", err, demand)

			}

		} else {
			bidResp := &bd.BidResponse{}
			respStr, err := resp.Body.ToString()

			defer resp.Body.Close()

			if err != nil {
				generateErrorResp(lib.ERROR_BD_FAILED_RES, "failed to get bd response body", err, demand)
			} else {

				err = proto.Unmarshal([]byte(respStr), bidResp)

				if err != nil {
					generateErrorResp(lib.ERROR_BD_MARSHAL_RES, "failed to unmarshal response body", err, demand)
				} else {
					beego.Debug("baidu response: ", bidResp.String())
					mapBDResponse(bidResp, adResponse)
					beego.Debug("map to pmp response successfully.")
					demand.Result <- adResponse
				}
			}
		}
	}
}

func pUint32(v int) *uint32 {
	p := new(uint32)
	*p = uint32(v)
	return p
}

func mapBDResponse(bdResp *bd.BidResponse, adResponse *m.AdResponse) {
	beego.Debug("start mapping.....")
	//	adResponse.StatusCode, _ = strconv.Atoi(strconv.FormatUint(*bdResp.ErrorCode,10))
	//	beego.Debug("error code: ", *bdResp.ErrorCode)
	adResponse.SetResponseTime(time.Now().Unix())

	//	if adResponse.StatusCode == 0 {
	adUnit := new(m.AdUnit)
	adResponse.Adunit = adUnit
	if len(bdResp.GetAds()) > 0 {

		ad := bdResp.GetAds()[0]
		adMeta := ad.MaterialMeta
		//			adUnit.Cid = *ad.AdslotId
		adUnit.ClickUrl = *adMeta.ClickUrl
		//todo hardcode 3 for MH, only support picture ad
		adUnit.Cid = fmt.Sprint(ad.AdId)
		//adUnit.CreativeType = 3
		adUnit.ClickUrl = *adMeta.ClickUrl
		adUnit.CreativeUrls = []string{*adMeta.MediaUrl}

		//adUnit.ImpTrackingUrls = []string{*adMeta.MediaUrl}
		adUnit.ImpTrackingUrls = adMeta.WinNoticeUrl
		//			 baidu doens't need the tracking url
		adUnit.ClkTrackingUrls = nil
		adUnit.AdWidth = int(*adMeta.MediaWidth)
		adUnit.AdHeight = int(*adMeta.MediaHeight)
		adResponse.StatusCode = lib.STATUS_SUCCESS
	} else {
		// no ads returned from baidu
		adResponse.StatusCode = lib.ERROR_NOAD
	}
	//	}
}

func generateErrorResp(errorCode int, message string, err error, demand *Demand) {
	beego.Critical(err.Error())
	adResponse := generateErrorResponse(demand.AdRequest, demand.AdspaceKey, errorCode)
	demand.Result <- adResponse
}
<commit_msg>fix index of bound issues<commit_after>package engine

import (
	bd "adexchange/engine/baidu/mobads_api"
	"fmt"
	"github.com/astaxie/beego"
	//"google.golang.org/grpc"
	//"log"
	//	"github.com/golang/protobuf/proto"

	//	"github.com/franela/goreq"
	//	"time"
	//	"bytes"
	//	"github.com/golang/protobuf/proto"
	"adexchange/lib"
	m "adexchange/models"
	"github.com/franela/goreq"
	"github.com/golang/protobuf/proto"
	"strings"
	"time"
)

var (
	OS_MAP = map[int]bd.Device_Os{
		0: bd.Device_ANDROID,
		1: bd.Device_IOS,
		2: bd.Device_IOS,
		3: bd.Device_IOS,
	}
)

//func invokeBD2(demand *Demand) {

//	address := "http://220.181.163.105/api"

//	conn, err := grpc.Dial(address)
//	if err != nil {
//		log.Fatalf("failed to connect: %v", err)
//	}
//	defer conn.Close()

//	// Set up a connection to the server.
////	c := bd.NewBDServiceClient(conn)
////
////	r, err := c.RequestAd(context.Background(), &bd.BidRequest{})
////
////	if err != nil {
////		log.Fatalf("could not get add from server: %v", err)
////	}
////	log.Printf("Greeting: %s", r.ErrorCode)

//	beego.Debug("invoke BD..XXXXXXXXXXXXXXXXXXXXXXXXXXX")

////	beego.Debug(r)

//}

func invokeBD(demand *Demand) {

	// current baidu api version is 4.0
	// TODO move this to conf file
	apiVersion := &bd.Version{
		Major: pUint32(4),
		Minor: pUint32(0),
	}
	//	appVersion := &bd.Version{
	//		Major: pUint32(4),
	//		Minor: pUint32(0),
	//	}

	/*  App   (required)*/

	// TODO put appid in secret_key 字段???
	var appId string = demand.AdSecretKey // required

	// below are optional
	//	var appBundleId string = "com.xxxxx"	// required
	//	var appName string = "";
	//	appCategories := []uint32{uint32(1)}			// required
	//	userPermissionType := bd.App_UserPermission_ACCESS_FINE_LOCATION
	//	userPermissionStatus := bd.App_UserPermission_UNKNOWN
	app := &bd.App{
		Id: &appId,

		// optional.  because there's no categories, so don't provide static info
		//		StaticInfo: &bd.App_StaticInfo{
		//			BundleId: &appBundleId,		// required
		//			Name: &appName,
		//			Categories: appCategories,	// required
		//		},
		//		Version: appVersion,
		//		UserPermission: []*bd.App_UserPermission{
		//			&bd.App_UserPermission{
		//				Type: &userPermissionType,		// required
		//				Status: &userPermissionStatus,					// required
		//			},
		//		},
	}

	/*  Device  (required)*/
	stringArr := strings.Split(demand.AdRequest.Osv, ".")

	var strOsvMinor string
	if len(stringArr) > 1 {
		strOsvMinor = stringArr[1]
	}
	devOsVersion := &bd.Version{
		Major: pUint32(lib.ConvertStrToInt(stringArr[0])),
		Minor: pUint32(lib.ConvertStrToInt(strOsvMinor)),
	}
	devModel := demand.AdRequest.Device // IPhone5s
	var devVendor string                // Apple
	if demand.AdRequest.Os == 1 {
		devVendor = "Apple"
	} else {
		devVendor = "Google"
	}

	devUdid := bd.Device_UdId{}

	if demand.AdRequest.Idfa != "" {
		devUdid.Idfa = &demand.AdRequest.Idfa
	}
	if demand.AdRequest.Imei != "" {
		devUdid.Imei = &demand.AdRequest.Imei
	}
	if demand.AdRequest.Wma != "" {
		devUdid.Mac = &demand.AdRequest.Wma
	}

	devType := bd.Device_PHONE
	devOs := OS_MAP[demand.AdRequest.Os]
	dev := &bd.Device{
		Type:      &devType,     // required. Mobile, Tablet, TV
		Os:        &devOs,       // required. android or IOS
		OsVersion: devOsVersion, // required. OS version
		Vendor:    &devVendor,   // required.
		Model:     &devModel,    // required.
		Udid:      &devUdid,     // required. ios: idfa, mac, android: imei, mac, tv: imei, mac, idfv
	}

	/*  Network  (required) */
	nt := &bd.Network{
		Ipv4: &demand.AdRequest.Ip,
	}

	/* Adslot  (required) */
	adSpaceId := demand.AdspaceKey
	adWidth := demand.AdRequest.Width
	adHeight := demand.AdRequest.Height
	adSize := bd.Size{
		Width:  pUint32(lib.ConvertStrToInt(adWidth)),  // required
		Height: pUint32(lib.ConvertStrToInt(adHeight)), // required
	}
	//	adType := bd.AdSlot_StaticInfo_BANNER
	//	adStaticInfo := bd.AdSlot_StaticInfo{
	//		Type: &adType,
	//	}

	var requestId string = demand.AdRequest.Bid
	req := bd.BidRequest{
		RequestId:  &requestId,
		ApiVersion: apiVersion,
		App:        app,
		Device:     dev,
		Network:    nt,
		Adslots: []*bd.AdSlot{
			&bd.AdSlot{
				Id:   &adSpaceId, // required.
				Size: &adSize,    // required
				//				StaticInfo: &adStaticInfo,
			},
		},
	}

	beego.Debug("baidu request: ", req.String())

	data, err := proto.Marshal(&req)

	if err != nil {
		generateErrorResp(lib.ERROR_BD_MARSHAL_REQ, "failed to marshal bd request", err, demand)
	} else {
		adResponse := new(m.AdResponse)
		adResponse.Bid = demand.AdRequest.Bid
		adResponse.SetDemandAdspaceKey(demand.AdspaceKey)
		adResponse.SetResponseTime(time.Now().Unix())

		resp, err := goreq.Request{
			Method:  "POST",
			Uri:     demand.URL,
			Timeout: time.Duration(demand.Timeout) * time.Millisecond,
			Body:    data,
		}.Do()

		if serr, ok := err.(*goreq.Error); ok {
			beego.Critical(err.Error())
			if serr.Timeout() {
				generateErrorResp(lib.ERROR_TIMEOUT_ERROR, "failed to send request to baidu", err, demand)

			} else {
				generateErrorResp(lib.ERROR_BD_SERVER, "failed to send request to baidu", err, demand)

			}

		} else {
			bidResp := &bd.BidResponse{}
			respStr, err := resp.Body.ToString()

			defer resp.Body.Close()

			if err != nil {
				generateErrorResp(lib.ERROR_BD_FAILED_RES, "failed to get bd response body", err, demand)
			} else {

				err = proto.Unmarshal([]byte(respStr), bidResp)

				if err != nil {
					generateErrorResp(lib.ERROR_BD_MARSHAL_RES, "failed to unmarshal response body", err, demand)
				} else {
					beego.Debug("baidu response: ", bidResp.String())
					mapBDResponse(bidResp, adResponse)
					beego.Debug("map to pmp response successfully.")
					demand.Result <- adResponse
				}
			}
		}
	}
}

func pUint32(v int) *uint32 {
	p := new(uint32)
	*p = uint32(v)
	return p
}

func mapBDResponse(bdResp *bd.BidResponse, adResponse *m.AdResponse) {
	beego.Debug("start mapping.....")
	//	adResponse.StatusCode, _ = strconv.Atoi(strconv.FormatUint(*bdResp.ErrorCode,10))
	//	beego.Debug("error code: ", *bdResp.ErrorCode)
	adResponse.SetResponseTime(time.Now().Unix())

	//	if adResponse.StatusCode == 0 {
	adUnit := new(m.AdUnit)
	adResponse.Adunit = adUnit
	if len(bdResp.GetAds()) > 0 {

		ad := bdResp.GetAds()[0]
		adMeta := ad.MaterialMeta
		//			adUnit.Cid = *ad.AdslotId
		adUnit.ClickUrl = *adMeta.ClickUrl
		//todo hardcode 3 for MH, only support picture ad
		adUnit.Cid = fmt.Sprint(ad.AdId)
		//adUnit.CreativeType = 3
		adUnit.ClickUrl = *adMeta.ClickUrl
		adUnit.CreativeUrls = []string{*adMeta.MediaUrl}

		//adUnit.ImpTrackingUrls = []string{*adMeta.MediaUrl}
		adUnit.ImpTrackingUrls = adMeta.WinNoticeUrl
		//			 baidu doens't need the tracking url
		adUnit.ClkTrackingUrls = nil
		adUnit.AdWidth = int(*adMeta.MediaWidth)
		adUnit.AdHeight = int(*adMeta.MediaHeight)
		adResponse.StatusCode = lib.STATUS_SUCCESS
	} else {
		// no ads returned from baidu
		adResponse.StatusCode = lib.ERROR_NOAD
	}
	//	}
}

func generateErrorResp(errorCode int, message string, err error, demand *Demand) {
	beego.Critical(err.Error())
	adResponse := generateErrorResponse(demand.AdRequest, demand.AdspaceKey, errorCode)
	demand.Result <- adResponse
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"bufio"
	"bytes"
	"github.com/labstack/echo/engine/test"
	"github.com/labstack/gommon/log"
	fast "github.com/valyala/fasthttp"
	"net"
	"net/url"
	"testing"
)

type fakeAddr struct {
	addr string
	net.Addr
}

func (a fakeAddr) String() string {
	return a.addr
}

func TestRequest(t *testing.T) {
	var ctx fast.RequestCtx

	url, _ := url.Parse("https://github.com/labstack/echo")
	ctx.Init(&fast.Request{}, fakeAddr{addr: "127.0.0.1"}, nil)
	ctx.Request.Read(bufio.NewReader(bytes.NewBufferString(test.MultipartRequest)))
	ctx.Request.SetRequestURI(url.String())

	test.RequestTest(t, NewRequest(&ctx, log.New("echo")))
}
<commit_msg>Fixed broken build<commit_after>package fasthttp

import (
	"bufio"
	"bytes"
	"net"
	"net/url"
	"testing"

	"github.com/labstack/echo/engine/test"
	"github.com/labstack/gommon/log"
	fast "github.com/valyala/fasthttp"
)

type fakeAddr struct {
	addr string
	net.Addr
}

func (a fakeAddr) String() string {
	return a.addr
}

func TestRequest(t *testing.T) {
	var ctx fast.RequestCtx

	url, _ := url.Parse("http://github.com/labstack/echo")
	ctx.Init(&fast.Request{}, fakeAddr{addr: "127.0.0.1"}, nil)
	ctx.Request.Read(bufio.NewReader(bytes.NewBufferString(test.MultipartRequest)))
	ctx.Request.SetRequestURI(url.String())

	test.RequestTest(t, NewRequest(&ctx, log.New("echo")))
}
<|endoftext|>


<commit_before>package entities

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/Vladimiroff/vec2d"
)

type Mission struct {
	Color      Color
	Source     embeddedPlanet
	Target     embeddedPlanet
	Type       string
	StartTime  int64
	TravelTime time.Duration // in ms.
	Player     string
	ShipCount  int32
	areaSet    string
}

// Just an internal type, used to embed source and target in Mission
type embeddedPlanet struct {
	Name     string
	Owner    string
	Position *vec2d.Vector
}

// Database key.
func (m *Mission) Key() string {
	return fmt.Sprintf("mission.%d_%s", m.StartTime, m.Source.Name)
}

// We plan to tweak the missions' speed based on some game logic.
// For now, 10 seems like a fair choice.
func (m *Mission) GetSpeed() int64 {
	return 10
}

// Returns the sorted set by X or Y where this entity has to be put in
func (m *Mission) AreaSet() string {
	return m.areaSet
}

// Changes its areaset based on axis and direction and updates the db
func (m *Mission) ChangeAreaSet(axis rune, direction int8) {
	areaParts := strings.Split(m.areaSet, ":")
	x, _ := strconv.ParseInt(areaParts[1], 10, 64)
	y, _ := strconv.ParseInt(areaParts[2], 10, 64)

	if axis == 'X' {
		x += int64(direction)
	} else if axis == 'Y' {
		y += int64(direction)
	}

	m.areaSet = fmt.Sprintf("area:%d:%d", x, y)
	RemoveFromArea(m.Key(), m.areaSet)
}

// Returns all transfer points this mission will ever cross
func (m *Mission) TransferPoints() AreaTransferPoints {
	result := make(AreaTransferPoints, 0, 10)

	fillAxises := func(startPoint, endPoint float64) (container []int64) {
		startAxis := RoundCoordinateTo(startPoint)
		endAxis := RoundCoordinateTo(endPoint)
		axises := []int64{startAxis, endAxis}
		if endAxis < startAxis {
			axises = []int64{endAxis, startAxis}
		}

		for i := axises[0] + 1; i < axises[1]; i += 1 {
			container = append(container, i*AREA_SIZE)
		}
		return
	}

	axisDirection := func(xA, xB float64) int8 {
		if xB > xA {
			return 1
		} else if xB == xA {
			return 0
		} else {
			return -1
		}
	}

	xAxises := fillAxises(m.Source.Position.X, m.Target.Position.X)
	yAxises := fillAxises(m.Source.Position.Y, m.Target.Position.Y)

	missionVectorEquation := NewCartesianEquation(m.Source.Position, m.Target.Position)

	direction := []int8{
		axisDirection(m.Source.Position.X, m.Target.Position.X),
		axisDirection(m.Source.Position.Y, m.Target.Position.Y),
	}

	for _, axis := range xAxises {
		crossPoint := vec2d.New(float64(axis), missionVectorEquation.GetYByX(float64(axis)))
		transferPoint := &AreaTransferPoint{
			TravelTime:     calculateTravelTime(m.Source.Position, crossPoint, m.GetSpeed()),
			Direction:      direction[0],
			CoordinateAxis: 'X',
		}
		result = append(result, transferPoint)
	}

	for _, axis := range yAxises {
		crossPoint := vec2d.New(missionVectorEquation.GetXByY(float64(axis)), float64(axis))
		transferPoint := &AreaTransferPoint{
			TravelTime:     calculateTravelTime(m.Source.Position, crossPoint, m.GetSpeed()),
			Direction:      direction[1],
			CoordinateAxis: 'Y',
		}
		result = append(result, transferPoint)
	}

	sort.Sort(result)
	return result
}

// Calculates the travel time in milliseconds between two planets with given speed.
// Traveling is implemented like a simple time.Sleep from our side.
func calculateTravelTime(source, target *vec2d.Vector, speed int64) time.Duration {
	distance := vec2d.GetDistance(source, target)
	return time.Duration(distance / float64(speed) * 100)
}

// When the missionary is done traveling (a.k.a. sleeping) calls this in order
// to calculate the outcome of the battle/suppliemnt/spying on target planet.

// EndAttackMission: We have to check if the target planet is owned by the attacker.
// If that's true we simply increment the ship count on that planet. If not we do the
// math and decrease the count ship on the attacked planet. We should check if the attacker
// should own that planet, which comes with all the changing colors and owner stuff.
func (m *Mission) EndAttackMission(target *Planet) (excessShips int32) {
	if target.Owner == m.Player {
		m.Target.Owner = target.Owner
		m.Type = "Supply"
		return m.EndSupplyMission(target)
	} else {
		if m.ShipCount < target.ShipCount {
			target.SetShipCount(target.ShipCount - m.ShipCount)
		} else {
			if target.IsHome {
				target.SetShipCount(0)
				excessShips = m.ShipCount - target.ShipCount
			} else {
				target.SetShipCount(m.ShipCount - target.ShipCount)
				target.Owner = m.Player
				target.Color = m.Color
			}
		}
	}
	return
}

// End Supply Mission: We simply increase the ship count and we're done :P
// If however the owner of the target planet has changed we change the mission type
// to attack.
func (m *Mission) EndSupplyMission(target *Planet) int32 {
	if target.Owner != m.Target.Owner {
		m.Type = "Attack"
		return m.EndAttackMission(target)
	}

	target.SetShipCount(target.ShipCount + m.ShipCount)
	return 0
}

// End Spy Mission: Create a spy report for that planet and find a way to notify the logged in
// instances of the user who sent this mission.
func (m *Mission) EndSpyMission(target *Planet) int32 {
	if target.Owner == m.Player {
		m.Target.Owner = target.Owner
		return m.EndSupplyMission(target)
	}
	CreateSpyReport(target, m)
	m.ShipCount -= 1
	return 0
}
<commit_msg>Transfer planet owners from the leaderboard if needed<commit_after>package entities

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
	"warcluster/leaderboard"

	"github.com/Vladimiroff/vec2d"
)

type Mission struct {
	Color      Color
	Source     embeddedPlanet
	Target     embeddedPlanet
	Type       string
	StartTime  int64
	TravelTime time.Duration // in ms.
	Player     string
	ShipCount  int32
	areaSet    string
}

// Just an internal type, used to embed source and target in Mission
type embeddedPlanet struct {
	Name     string
	Owner    string
	Position *vec2d.Vector
}

// Database key.
func (m *Mission) Key() string {
	return fmt.Sprintf("mission.%d_%s", m.StartTime, m.Source.Name)
}

// We plan to tweak the missions' speed based on some game logic.
// For now, 10 seems like a fair choice.
func (m *Mission) GetSpeed() int64 {
	return 10
}

// Returns the sorted set by X or Y where this entity has to be put in
func (m *Mission) AreaSet() string {
	return m.areaSet
}

// Changes its areaset based on axis and direction and updates the db
func (m *Mission) ChangeAreaSet(axis rune, direction int8) {
	areaParts := strings.Split(m.areaSet, ":")
	x, _ := strconv.ParseInt(areaParts[1], 10, 64)
	y, _ := strconv.ParseInt(areaParts[2], 10, 64)

	if axis == 'X' {
		x += int64(direction)
	} else if axis == 'Y' {
		y += int64(direction)
	}

	m.areaSet = fmt.Sprintf("area:%d:%d", x, y)
	RemoveFromArea(m.Key(), m.areaSet)
}

// Returns all transfer points this mission will ever cross
func (m *Mission) TransferPoints() AreaTransferPoints {
	result := make(AreaTransferPoints, 0, 10)

	fillAxises := func(startPoint, endPoint float64) (container []int64) {
		startAxis := RoundCoordinateTo(startPoint)
		endAxis := RoundCoordinateTo(endPoint)
		axises := []int64{startAxis, endAxis}
		if endAxis < startAxis {
			axises = []int64{endAxis, startAxis}
		}

		for i := axises[0] + 1; i < axises[1]; i += 1 {
			container = append(container, i*AREA_SIZE)
		}
		return
	}

	axisDirection := func(xA, xB float64) int8 {
		if xB > xA {
			return 1
		} else if xB == xA {
			return 0
		} else {
			return -1
		}
	}

	xAxises := fillAxises(m.Source.Position.X, m.Target.Position.X)
	yAxises := fillAxises(m.Source.Position.Y, m.Target.Position.Y)

	missionVectorEquation := NewCartesianEquation(m.Source.Position, m.Target.Position)

	direction := []int8{
		axisDirection(m.Source.Position.X, m.Target.Position.X),
		axisDirection(m.Source.Position.Y, m.Target.Position.Y),
	}

	for _, axis := range xAxises {
		crossPoint := vec2d.New(float64(axis), missionVectorEquation.GetYByX(float64(axis)))
		transferPoint := &AreaTransferPoint{
			TravelTime:     calculateTravelTime(m.Source.Position, crossPoint, m.GetSpeed()),
			Direction:      direction[0],
			CoordinateAxis: 'X',
		}
		result = append(result, transferPoint)
	}

	for _, axis := range yAxises {
		crossPoint := vec2d.New(missionVectorEquation.GetXByY(float64(axis)), float64(axis))
		transferPoint := &AreaTransferPoint{
			TravelTime:     calculateTravelTime(m.Source.Position, crossPoint, m.GetSpeed()),
			Direction:      direction[1],
			CoordinateAxis: 'Y',
		}
		result = append(result, transferPoint)
	}

	sort.Sort(result)
	return result
}

// Calculates the travel time in milliseconds between two planets with given speed.
// Traveling is implemented like a simple time.Sleep from our side.
func calculateTravelTime(source, target *vec2d.Vector, speed int64) time.Duration {
	distance := vec2d.GetDistance(source, target)
	return time.Duration(distance / float64(speed) * 100)
}

// When the missionary is done traveling (a.k.a. sleeping) calls this in order
// to calculate the outcome of the battle/suppliemnt/spying on target planet.

// EndAttackMission: We have to check if the target planet is owned by the attacker.
// If that's true we simply increment the ship count on that planet. If not we do the
// math and decrease the count ship on the attacked planet. We should check if the attacker
// should own that planet, which comes with all the changing colors and owner stuff.
func (m *Mission) EndAttackMission(target *Planet) (excessShips int32) {
	if target.Owner == m.Player {
		m.Target.Owner = target.Owner
		m.Type = "Supply"
		return m.EndSupplyMission(target)
	} else {
		if m.ShipCount < target.ShipCount {
			target.SetShipCount(target.ShipCount - m.ShipCount)
		} else {
			if target.IsHome {
				target.SetShipCount(0)
				excessShips = m.ShipCount - target.ShipCount
			} else {
				leaderboard.Board.Transfer(target.Owner, m.Player)
				target.SetShipCount(m.ShipCount - target.ShipCount)
				target.Owner = m.Player
				target.Color = m.Color
			}
		}
	}
	return
}

// End Supply Mission: We simply increase the ship count and we're done :P
// If however the owner of the target planet has changed we change the mission type
// to attack.
func (m *Mission) EndSupplyMission(target *Planet) int32 {
	if target.Owner != m.Target.Owner {
		m.Type = "Attack"
		return m.EndAttackMission(target)
	}

	target.SetShipCount(target.ShipCount + m.ShipCount)
	return 0
}

// End Spy Mission: Create a spy report for that planet and find a way to notify the logged in
// instances of the user who sent this mission.
func (m *Mission) EndSpyMission(target *Planet) int32 {
	if target.Owner == m.Player {
		m.Target.Owner = target.Owner
		return m.EndSupplyMission(target)
	}
	CreateSpyReport(target, m)
	m.ShipCount -= 1
	return 0
}
<|endoftext|>


<commit_before>package v2

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

	etcdErr "github.com/coreos/etcd/error"
	"github.com/coreos/etcd/log"
	"github.com/coreos/etcd/store"
	"github.com/coreos/raft"
	"github.com/gorilla/mux"
)

func GetHandler(w http.ResponseWriter, req *http.Request, s Server) error {
	var err error
	var event *store.Event

	vars := mux.Vars(req)
	key := "/" + vars["key"]

	// Help client to redirect the request to the current leader
	if req.FormValue("consistent") == "true" && s.State() != raft.Leader {
		leader := s.Leader()
		hostname, _ := s.ClientURL(leader)
		url := hostname + req.URL.Path
		log.Debugf("Redirect consistent get to %s", url)
		http.Redirect(w, req, url, http.StatusTemporaryRedirect)
		return nil
	}

	recursive := (req.FormValue("recursive") == "true")
	sorted := (req.FormValue("sorted") == "true")

	if req.FormValue("wait") == "true" { // watch
		// Create a command to watch from a given index (default 0).
		var sinceIndex uint64 = 0

		waitIndex := req.FormValue("waitIndex")
		if waitIndex != "" {
			sinceIndex, err = strconv.ParseUint(string(req.FormValue("waitIndex")), 10, 64)
			if err != nil {
				return etcdErr.NewError(etcdErr.EcodeIndexNaN, "Watch From Index", s.Store().Index())
			}
		}

		// Start the watcher on the store.
		eventChan, err := s.Store().Watch(key, recursive, sinceIndex)
		if err != nil {
			return etcdErr.NewError(500, key, s.Store().Index())
		}

		cn, _ := w.(http.CloseNotifier)
		closeChan := cn.CloseNotify()

		select {
		case <-closeChan:
			return nil
		case event = <-eventChan:
		}

	} else { //get
		// Retrieve the key from the store.
		event, err = s.Store().Get(key, recursive, sorted)
		if err != nil {
			return err
		}
	}

	w.Header().Set("Content-Type", "application/json")
	w.Header().Add("X-Etcd-Index", fmt.Sprint(s.Store().Index()))
	w.Header().Add("X-Raft-Index", fmt.Sprint(s.CommitIndex()))
	w.Header().Add("X-Raft-Term", fmt.Sprint(s.Term()))
	w.WriteHeader(http.StatusOK)
	b, _ := json.Marshal(event)

	w.Write(b)

	return nil
}
<commit_msg>fix redirect url should include rawquery<commit_after>package v2

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	etcdErr "github.com/coreos/etcd/error"
	"github.com/coreos/etcd/log"
	"github.com/coreos/etcd/store"
	"github.com/coreos/raft"
	"github.com/gorilla/mux"
)

func GetHandler(w http.ResponseWriter, req *http.Request, s Server) error {
	var err error
	var event *store.Event

	vars := mux.Vars(req)
	key := "/" + vars["key"]

	// Help client to redirect the request to the current leader
	if req.FormValue("consistent") == "true" && s.State() != raft.Leader {
		leader := s.Leader()
		hostname, _ := s.ClientURL(leader)

		url, err := url.Parse(hostname)
		if err != nil {
			log.Warn("Redirect cannot parse hostName ", hostname)
			return err
		}
		url.RawQuery = req.URL.RawQuery
		url.Path = req.URL.Path

		log.Debugf("Redirect consistent get to %s", url.String())
		http.Redirect(w, req, url.String(), http.StatusTemporaryRedirect)
		return nil
	}

	recursive := (req.FormValue("recursive") == "true")
	sorted := (req.FormValue("sorted") == "true")

	if req.FormValue("wait") == "true" { // watch
		// Create a command to watch from a given index (default 0).
		var sinceIndex uint64 = 0

		waitIndex := req.FormValue("waitIndex")
		if waitIndex != "" {
			sinceIndex, err = strconv.ParseUint(string(req.FormValue("waitIndex")), 10, 64)
			if err != nil {
				return etcdErr.NewError(etcdErr.EcodeIndexNaN, "Watch From Index", s.Store().Index())
			}
		}

		// Start the watcher on the store.
		eventChan, err := s.Store().Watch(key, recursive, sinceIndex)
		if err != nil {
			return etcdErr.NewError(500, key, s.Store().Index())
		}

		cn, _ := w.(http.CloseNotifier)
		closeChan := cn.CloseNotify()

		select {
		case <-closeChan:
			return nil
		case event = <-eventChan:
		}

	} else { //get
		// Retrieve the key from the store.
		event, err = s.Store().Get(key, recursive, sorted)
		if err != nil {
			return err
		}
	}

	w.Header().Set("Content-Type", "application/json")
	w.Header().Add("X-Etcd-Index", fmt.Sprint(s.Store().Index()))
	w.Header().Add("X-Raft-Index", fmt.Sprint(s.CommitIndex()))
	w.Header().Add("X-Raft-Term", fmt.Sprint(s.Term()))
	w.WriteHeader(http.StatusOK)
	b, _ := json.Marshal(event)

	w.Write(b)

	return nil
}
<|endoftext|>


<commit_before>package ec2_test

import (
	"crypto/rand"
	"fmt"
	"io"
	amzec2 "launchpad.net/goamz/ec2"
	. "launchpad.net/gocheck"
	"launchpad.net/juju/go/environs"
	"launchpad.net/juju/go/environs/ec2"
	"launchpad.net/juju/go/environs/jujutest"
	"launchpad.net/juju/go/testing"
	"strings"
	"time"
)

// amazonConfig holds the environments configuration
// for running the amazon EC2 integration tests.
//
// This is missing keys for security reasons; set the following environment variables
// to make the Amazon testing work:
//  access-key: $AWS_ACCESS_KEY_ID
//  secret-key: $AWS_SECRET_ACCESS_KEY
var amazonConfig = fmt.Sprintf(`
environments:
  sample-%s:
    type: ec2
    control-bucket: 'juju-test-%s'
    juju-origin: distro
`, uniqueName, uniqueName)

// uniqueName is generated afresh for every test, so that
// we are not polluted by previous test state.
var uniqueName = randomName()

func randomName() string {
	buf := make([]byte, 8)
	_, err := io.ReadFull(rand.Reader, buf)
	if err != nil {
		panic(fmt.Sprintf("error from crypto rand: %v", err))
	}
	return fmt.Sprintf("%x", buf)
}

func registerAmazonTests() {
	envs, err := environs.ReadEnvironsBytes([]byte(amazonConfig))
	if err != nil {
		panic(fmt.Errorf("cannot parse amazon tests config data: %v", err))
	}
	for _, name := range envs.Names() {
		Suite(&LiveTests{
			LiveTests: jujutest.LiveTests{
				Environs:         envs,
				Name:             name,
				ConsistencyDelay: 5 * time.Second,
				CanOpenState:     true,
			},
		})
	}
}

// LiveTests contains tests that can be run against the Amazon servers.
// Each test runs using the same ec2 connection.
type LiveTests struct {
	testing.LoggingSuite
	jujutest.LiveTests
}

func (t *LiveTests) SetUpTest(c *C) {
	t.LoggingSuite.SetUpTest(c)
	t.LiveTests.SetUpTest(c)
}

func (t *LiveTests) TearDownTest(c *C) {
	t.LiveTests.TearDownTest(c)
	t.LoggingSuite.TearDownTest(c)
}

func (t *LiveTests) TestInstanceDNSName(c *C) {
	inst, err := t.Env.StartInstance(30, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)
	defer t.Env.StopInstances([]environs.Instance{inst})
	dns, err := inst.WaitDNSName()
	c.Check(err, IsNil)
	c.Check(dns, Not(Equals), "")

	insts, err := t.Env.Instances([]string{inst.Id()})
	c.Assert(err, IsNil)
	c.Assert(len(insts), Equals, 1)

	ec2inst := ec2.InstanceEC2(insts[0])
	c.Check(ec2inst.DNSName, Equals, dns)
}

func (t *LiveTests) TestInstanceGroups(c *C) {
	ec2conn := ec2.EnvironEC2(t.Env)

	groups := amzec2.SecurityGroupNames(
		ec2.GroupName(t.Env),
		ec2.MachineGroupName(t.Env, 98),
		ec2.MachineGroupName(t.Env, 99),
	)
	info := make([]amzec2.SecurityGroupInfo, len(groups))

	// Create a group with the same name as the juju group
	// but with different permissions, to check that it's deleted
	// and recreated correctly.
	oldJujuGroup := createGroup(c, ec2conn, groups[0].Name, "old juju group")

	// Add two permissions: one is required and should be left alone;
	// the other is not and should be deleted.
	// N.B. this is unfortunately sensitive to the actual set of permissions used.
	_, err := ec2conn.AuthorizeSecurityGroup(oldJujuGroup,
		[]amzec2.IPPerm{
			{
				Protocol:  "tcp",
				FromPort:  22,
				ToPort:    22,
				SourceIPs: []string{"0.0.0.0/0"},
			},
			{
				Protocol:  "udp",
				FromPort:  4321,
				ToPort:    4322,
				SourceIPs: []string{"3.4.5.6/32"},
			},
		})
	c.Assert(err, IsNil)

	inst0, err := t.Env.StartInstance(98, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)
	defer t.Env.StopInstances([]environs.Instance{inst0})

	// Create a same-named group for the second instance
	// before starting it, to check that it's reused correctly.
	oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group")

	inst1, err := t.Env.StartInstance(99, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)
	defer t.Env.StopInstances([]environs.Instance{inst1})

	groupsResp, err := ec2conn.SecurityGroups(groups, nil)
	c.Assert(err, IsNil)
	c.Assert(groupsResp.Groups, HasLen, len(groups))

	// For each group, check that it exists and record its id.
	for i, group := range groups {
		found := false
		for _, g := range groupsResp.Groups {
			if g.Name == group.Name {
				groups[i].Id = g.Id
				info[i] = g
				found = true
				break
			}
		}
		if !found {
			c.Fatalf("group %q not found", group.Name)
		}
	}

	// The old juju group should have been reused.
	c.Check(groups[0].Id, Equals, oldJujuGroup.Id)

	// Check that it authorizes the correct ports and there
	// are no extra permissions (in particular we are checking
	// that the unneeded permission that we added earlier
	// has been deleted).
	perms := info[0].IPPerms
	c.Assert(perms, HasLen, 1)
	checkPortAllowed(c, perms, 22)

	// The old machine group should have been reused also.
	c.Check(groups[2].Id, Equals, oldMachineGroup.Id)

	// Check that each instance is part of the correct groups.
	resp, err := ec2conn.Instances([]string{inst0.Id(), inst1.Id()}, nil)
	c.Assert(err, IsNil)
	c.Assert(resp.Reservations, HasLen, 2)
	for _, r := range resp.Reservations {
		c.Assert(r.Instances, HasLen, 1)
		// each instance must be part of the general juju group.
		msg := Commentf("reservation %#v", r)
		c.Assert(hasSecurityGroup(r, groups[0]), Equals, true, msg)
		inst := r.Instances[0]
		switch inst.InstanceId {
		case inst0.Id():
			c.Assert(hasSecurityGroup(r, groups[1]), Equals, true, msg)
			c.Assert(hasSecurityGroup(r, groups[2]), Equals, false, msg)
		case inst1.Id():
			c.Assert(hasSecurityGroup(r, groups[2]), Equals, true, msg)
			c.Assert(hasSecurityGroup(r, groups[1]), Equals, false, msg)
		default:
			c.Errorf("unknown instance found: %v", inst)
		}
	}
}

func (t *LiveTests) TestDestroy(c *C) {
	s := t.Env.Storage()
	err := s.Put("foo", strings.NewReader("foo"), 3)
	c.Assert(err, IsNil)
	err = s.Put("bar", strings.NewReader("bar"), 3)
	c.Assert(err, IsNil)

	// Check that bucket exists, so we can be sure
	// we have checked correctly that it's been destroyed.
	names, err := s.List("")
	c.Assert(err, IsNil)
	c.Assert(len(names) >= 2, Equals, true)

	t.Destroy(c)

	for i := 0; i < 5; i++ {
		names, err = s.List("")
		if err != nil {
			break
		}
	}
	var notFoundError *environs.NotFoundError
	c.Assert(err, FitsTypeOf, notFoundError)
}

func checkPortAllowed(c *C, perms []amzec2.IPPerm, port int) {
	for _, perm := range perms {
		if perm.FromPort == port {
			c.Check(perm.Protocol, Equals, "tcp")
			c.Check(perm.ToPort, Equals, port)
			c.Check(perm.SourceIPs, DeepEquals, []string{"0.0.0.0/0"})
			c.Check(perm.SourceGroups, HasLen, 0)
			return
		}
	}
	c.Errorf("ip port permission not found for %d in %#v", port, perms)
}

func (t *LiveTests) TestStopInstances(c *C) {
	// It would be nice if this test was in jujutest, but
	// there's no way for jujutest to fabricate a valid-looking
	// instance id.
	inst0, err := t.Env.StartInstance(40, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)

	inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa")

	inst2, err := t.Env.StartInstance(41, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)

	err = t.Env.StopInstances([]environs.Instance{inst0, inst1, inst2})
	c.Check(err, IsNil)

	var insts []environs.Instance

	// We need the retry logic here because we are waiting
	// for Instances to return an error, and it will not retry
	// if it succeeds.
	gone := false
	for i := 0; i < 5; i++ {
		insts, err = t.Env.Instances([]string{inst0.Id(), inst2.Id()})
		if err == environs.ErrPartialInstances {
			// instances not gone yet.
			time.Sleep(1e9)
			continue
		}
		if err == environs.ErrNoInstances {
			gone = true
			break
		}
		c.Fatalf("error getting instances: %v", err)
	}
	if !gone {
		c.Errorf("after termination, instances remaining: %v", insts)
	}
}

// createGroup creates a new EC2 group and returns it. If it already exists,
// it revokes all its permissions and returns the existing group.
func createGroup(c *C, ec2conn *amzec2.EC2, name, descr string) amzec2.SecurityGroup {
	resp, err := ec2conn.CreateSecurityGroup(name, descr)
	if err == nil {
		return resp.SecurityGroup
	}
	if err.(*amzec2.Error).Code != "InvalidGroup.Duplicate" {
		c.Fatalf("cannot make group %q: %v", name, err)
	}

	// Found duplicate group, so revoke its permissions and return it.
	gresp, err := ec2conn.SecurityGroups(amzec2.SecurityGroupNames(name), nil)
	c.Assert(err, IsNil)

	gi := gresp.Groups[0]
	if len(gi.IPPerms) > 0 {
		_, err = ec2conn.RevokeSecurityGroup(gi.SecurityGroup, gi.IPPerms)
		c.Assert(err, IsNil)
	}
	return gi.SecurityGroup
}

func hasSecurityGroup(r amzec2.Reservation, g amzec2.SecurityGroup) bool {
	for _, rg := range r.SecurityGroups {
		if rg.Id == g.Id {
			return true
		}
	}
	return false
}
<commit_msg>environs/ec2: sleep in test<commit_after>package ec2_test

import (
	"crypto/rand"
	"fmt"
	"io"
	amzec2 "launchpad.net/goamz/ec2"
	. "launchpad.net/gocheck"
	"launchpad.net/juju/go/environs"
	"launchpad.net/juju/go/environs/ec2"
	"launchpad.net/juju/go/environs/jujutest"
	"launchpad.net/juju/go/testing"
	"strings"
	"time"
)

// amazonConfig holds the environments configuration
// for running the amazon EC2 integration tests.
//
// This is missing keys for security reasons; set the following environment variables
// to make the Amazon testing work:
//  access-key: $AWS_ACCESS_KEY_ID
//  secret-key: $AWS_SECRET_ACCESS_KEY
var amazonConfig = fmt.Sprintf(`
environments:
  sample-%s:
    type: ec2
    control-bucket: 'juju-test-%s'
    juju-origin: distro
`, uniqueName, uniqueName)

// uniqueName is generated afresh for every test, so that
// we are not polluted by previous test state.
var uniqueName = randomName()

func randomName() string {
	buf := make([]byte, 8)
	_, err := io.ReadFull(rand.Reader, buf)
	if err != nil {
		panic(fmt.Sprintf("error from crypto rand: %v", err))
	}
	return fmt.Sprintf("%x", buf)
}

func registerAmazonTests() {
	envs, err := environs.ReadEnvironsBytes([]byte(amazonConfig))
	if err != nil {
		panic(fmt.Errorf("cannot parse amazon tests config data: %v", err))
	}
	for _, name := range envs.Names() {
		Suite(&LiveTests{
			LiveTests: jujutest.LiveTests{
				Environs:         envs,
				Name:             name,
				ConsistencyDelay: 5 * time.Second,
				CanOpenState:     true,
			},
		})
	}
}

// LiveTests contains tests that can be run against the Amazon servers.
// Each test runs using the same ec2 connection.
type LiveTests struct {
	testing.LoggingSuite
	jujutest.LiveTests
}

func (t *LiveTests) SetUpTest(c *C) {
	t.LoggingSuite.SetUpTest(c)
	t.LiveTests.SetUpTest(c)
}

func (t *LiveTests) TearDownTest(c *C) {
	t.LiveTests.TearDownTest(c)
	t.LoggingSuite.TearDownTest(c)
}

func (t *LiveTests) TestInstanceDNSName(c *C) {
	inst, err := t.Env.StartInstance(30, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)
	defer t.Env.StopInstances([]environs.Instance{inst})
	dns, err := inst.WaitDNSName()
	c.Check(err, IsNil)
	c.Check(dns, Not(Equals), "")

	insts, err := t.Env.Instances([]string{inst.Id()})
	c.Assert(err, IsNil)
	c.Assert(len(insts), Equals, 1)

	ec2inst := ec2.InstanceEC2(insts[0])
	c.Check(ec2inst.DNSName, Equals, dns)
}

func (t *LiveTests) TestInstanceGroups(c *C) {
	ec2conn := ec2.EnvironEC2(t.Env)

	groups := amzec2.SecurityGroupNames(
		ec2.GroupName(t.Env),
		ec2.MachineGroupName(t.Env, 98),
		ec2.MachineGroupName(t.Env, 99),
	)
	info := make([]amzec2.SecurityGroupInfo, len(groups))

	// Create a group with the same name as the juju group
	// but with different permissions, to check that it's deleted
	// and recreated correctly.
	oldJujuGroup := createGroup(c, ec2conn, groups[0].Name, "old juju group")

	// Add two permissions: one is required and should be left alone;
	// the other is not and should be deleted.
	// N.B. this is unfortunately sensitive to the actual set of permissions used.
	_, err := ec2conn.AuthorizeSecurityGroup(oldJujuGroup,
		[]amzec2.IPPerm{
			{
				Protocol:  "tcp",
				FromPort:  22,
				ToPort:    22,
				SourceIPs: []string{"0.0.0.0/0"},
			},
			{
				Protocol:  "udp",
				FromPort:  4321,
				ToPort:    4322,
				SourceIPs: []string{"3.4.5.6/32"},
			},
		})
	c.Assert(err, IsNil)

	inst0, err := t.Env.StartInstance(98, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)
	defer t.Env.StopInstances([]environs.Instance{inst0})

	// Create a same-named group for the second instance
	// before starting it, to check that it's reused correctly.
	oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group")

	inst1, err := t.Env.StartInstance(99, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)
	defer t.Env.StopInstances([]environs.Instance{inst1})

	groupsResp, err := ec2conn.SecurityGroups(groups, nil)
	c.Assert(err, IsNil)
	c.Assert(groupsResp.Groups, HasLen, len(groups))

	// For each group, check that it exists and record its id.
	for i, group := range groups {
		found := false
		for _, g := range groupsResp.Groups {
			if g.Name == group.Name {
				groups[i].Id = g.Id
				info[i] = g
				found = true
				break
			}
		}
		if !found {
			c.Fatalf("group %q not found", group.Name)
		}
	}

	// The old juju group should have been reused.
	c.Check(groups[0].Id, Equals, oldJujuGroup.Id)

	// Check that it authorizes the correct ports and there
	// are no extra permissions (in particular we are checking
	// that the unneeded permission that we added earlier
	// has been deleted).
	perms := info[0].IPPerms
	c.Assert(perms, HasLen, 1)
	checkPortAllowed(c, perms, 22)

	// The old machine group should have been reused also.
	c.Check(groups[2].Id, Equals, oldMachineGroup.Id)

	// Check that each instance is part of the correct groups.
	resp, err := ec2conn.Instances([]string{inst0.Id(), inst1.Id()}, nil)
	c.Assert(err, IsNil)
	c.Assert(resp.Reservations, HasLen, 2)
	for _, r := range resp.Reservations {
		c.Assert(r.Instances, HasLen, 1)
		// each instance must be part of the general juju group.
		msg := Commentf("reservation %#v", r)
		c.Assert(hasSecurityGroup(r, groups[0]), Equals, true, msg)
		inst := r.Instances[0]
		switch inst.InstanceId {
		case inst0.Id():
			c.Assert(hasSecurityGroup(r, groups[1]), Equals, true, msg)
			c.Assert(hasSecurityGroup(r, groups[2]), Equals, false, msg)
		case inst1.Id():
			c.Assert(hasSecurityGroup(r, groups[2]), Equals, true, msg)
			c.Assert(hasSecurityGroup(r, groups[1]), Equals, false, msg)
		default:
			c.Errorf("unknown instance found: %v", inst)
		}
	}
}

func (t *LiveTests) TestDestroy(c *C) {
	s := t.Env.Storage()
	err := s.Put("foo", strings.NewReader("foo"), 3)
	c.Assert(err, IsNil)
	err = s.Put("bar", strings.NewReader("bar"), 3)
	c.Assert(err, IsNil)

	// Check that bucket exists, so we can be sure
	// we have checked correctly that it's been destroyed.
	names, err := s.List("")
	c.Assert(err, IsNil)
	c.Assert(len(names) >= 2, Equals, true)

	t.Destroy(c)

	for i := 0; i < 5; i++ {
		names, err = s.List("")
		if err != nil {
			break
		}
		time.Sleep(1e9)
	}
	var notFoundError *environs.NotFoundError
	c.Assert(err, FitsTypeOf, notFoundError)
}

func checkPortAllowed(c *C, perms []amzec2.IPPerm, port int) {
	for _, perm := range perms {
		if perm.FromPort == port {
			c.Check(perm.Protocol, Equals, "tcp")
			c.Check(perm.ToPort, Equals, port)
			c.Check(perm.SourceIPs, DeepEquals, []string{"0.0.0.0/0"})
			c.Check(perm.SourceGroups, HasLen, 0)
			return
		}
	}
	c.Errorf("ip port permission not found for %d in %#v", port, perms)
}

func (t *LiveTests) TestStopInstances(c *C) {
	// It would be nice if this test was in jujutest, but
	// there's no way for jujutest to fabricate a valid-looking
	// instance id.
	inst0, err := t.Env.StartInstance(40, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)

	inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa")

	inst2, err := t.Env.StartInstance(41, jujutest.InvalidStateInfo)
	c.Assert(err, IsNil)

	err = t.Env.StopInstances([]environs.Instance{inst0, inst1, inst2})
	c.Check(err, IsNil)

	var insts []environs.Instance

	// We need the retry logic here because we are waiting
	// for Instances to return an error, and it will not retry
	// if it succeeds.
	gone := false
	for i := 0; i < 5; i++ {
		insts, err = t.Env.Instances([]string{inst0.Id(), inst2.Id()})
		if err == environs.ErrPartialInstances {
			// instances not gone yet.
			time.Sleep(1e9)
			continue
		}
		if err == environs.ErrNoInstances {
			gone = true
			break
		}
		c.Fatalf("error getting instances: %v", err)
	}
	if !gone {
		c.Errorf("after termination, instances remaining: %v", insts)
	}
}

// createGroup creates a new EC2 group and returns it. If it already exists,
// it revokes all its permissions and returns the existing group.
func createGroup(c *C, ec2conn *amzec2.EC2, name, descr string) amzec2.SecurityGroup {
	resp, err := ec2conn.CreateSecurityGroup(name, descr)
	if err == nil {
		return resp.SecurityGroup
	}
	if err.(*amzec2.Error).Code != "InvalidGroup.Duplicate" {
		c.Fatalf("cannot make group %q: %v", name, err)
	}

	// Found duplicate group, so revoke its permissions and return it.
	gresp, err := ec2conn.SecurityGroups(amzec2.SecurityGroupNames(name), nil)
	c.Assert(err, IsNil)

	gi := gresp.Groups[0]
	if len(gi.IPPerms) > 0 {
		_, err = ec2conn.RevokeSecurityGroup(gi.SecurityGroup, gi.IPPerms)
		c.Assert(err, IsNil)
	}
	return gi.SecurityGroup
}

func hasSecurityGroup(r amzec2.Reservation, g amzec2.SecurityGroup) bool {
	for _, rg := range r.SecurityGroups {
		if rg.Id == g.Id {
			return true
		}
	}
	return false
}
<|endoftext|>